13e16c824a 2008-02-13 drh: /* 13e16c824a 2008-02-13 drh: ** Copyright (c) 2008 D. Richard Hipp 13e16c824a 2008-02-13 drh: ** 13e16c824a 2008-02-13 drh: ** This program is free software; you can redistribute it and/or 13e16c824a 2008-02-13 drh: ** modify it under the terms of the GNU General Public 13e16c824a 2008-02-13 drh: ** License version 2 as published by the Free Software Foundation. 13e16c824a 2008-02-13 drh: ** 13e16c824a 2008-02-13 drh: ** This program is distributed in the hope that it will be useful, 13e16c824a 2008-02-13 drh: ** but WITHOUT ANY WARRANTY; without even the implied warranty of 13e16c824a 2008-02-13 drh: ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13e16c824a 2008-02-13 drh: ** General Public License for more details. 13e16c824a 2008-02-13 drh: ** 13e16c824a 2008-02-13 drh: ** You should have received a copy of the GNU General Public 13e16c824a 2008-02-13 drh: ** License along with this library; if not, write to the 13e16c824a 2008-02-13 drh: ** Free Software Foundation, Inc., 59 Temple Place - Suite 330, 13e16c824a 2008-02-13 drh: ** Boston, MA 02111-1307, USA. 13e16c824a 2008-02-13 drh: ** 13e16c824a 2008-02-13 drh: ** Author contact information: 13e16c824a 2008-02-13 drh: ** drh@hwaci.com 13e16c824a 2008-02-13 drh: ** http://www.hwaci.com/drh/ 13e16c824a 2008-02-13 drh: ** 13e16c824a 2008-02-13 drh: ******************************************************************************* 13e16c824a 2008-02-13 drh: ** 13e16c824a 2008-02-13 drh: ** This file contains an interface between the TH scripting language 13e16c824a 2008-02-13 drh: ** (an independent project) and fossil. 13e16c824a 2008-02-13 drh: */ 13e16c824a 2008-02-13 drh: #include "config.h" 13e16c824a 2008-02-13 drh: #include "th_main.h" 13e16c824a 2008-02-13 drh: 13e16c824a 2008-02-13 drh: /* 13e16c824a 2008-02-13 drh: ** Global variable counting the number of outstanding calls to malloc() 13e16c824a 2008-02-13 drh: ** made by the th1 implementation. This is used to catch memory leaks 13e16c824a 2008-02-13 drh: ** in the interpreter. Obviously, it also means th1 is not threadsafe. 13e16c824a 2008-02-13 drh: */ 13e16c824a 2008-02-13 drh: static int nOutstandingMalloc = 0; 13e16c824a 2008-02-13 drh: 13e16c824a 2008-02-13 drh: /* 13e16c824a 2008-02-13 drh: ** A pointer to the single TH interpreter used within fossil. 13e16c824a 2008-02-13 drh: */ 13e16c824a 2008-02-13 drh: static Th_Interp *interp = 0; 13e16c824a 2008-02-13 drh: 13e16c824a 2008-02-13 drh: 13e16c824a 2008-02-13 drh: /* 13e16c824a 2008-02-13 drh: ** Implementations of malloc() and free() to pass to the interpreter. 13e16c824a 2008-02-13 drh: */ 13e16c824a 2008-02-13 drh: static void *xMalloc(unsigned int n){ 13e16c824a 2008-02-13 drh: void *p = malloc(n); 13e16c824a 2008-02-13 drh: if( p ){ 13e16c824a 2008-02-13 drh: nOutstandingMalloc++; 13e16c824a 2008-02-13 drh: } 13e16c824a 2008-02-13 drh: return p; 13e16c824a 2008-02-13 drh: } 13e16c824a 2008-02-13 drh: static void xFree(void *p){ 13e16c824a 2008-02-13 drh: if( p ){ 13e16c824a 2008-02-13 drh: nOutstandingMalloc--; 13e16c824a 2008-02-13 drh: } 13e16c824a 2008-02-13 drh: free(p); 13e16c824a 2008-02-13 drh: } 13e16c824a 2008-02-13 drh: static Th_Vtab vtab = { xMalloc, xFree }; 13e16c824a 2008-02-13 drh: 13e16c824a 2008-02-13 drh: 13e16c824a 2008-02-13 drh: /* 13e16c824a 2008-02-13 drh: ** True if output is enabled. False if disabled. 13e16c824a 2008-02-13 drh: */ 13e16c824a 2008-02-13 drh: static int enableOutput = 1; 13e16c824a 2008-02-13 drh: 13e16c824a 2008-02-13 drh: /* 13e16c824a 2008-02-13 drh: ** TH command: enable_output BOOLEAN 13e16c824a 2008-02-13 drh: ** 13e16c824a 2008-02-13 drh: ** Enable or disable the puts and hputs commands. 13e16c824a 2008-02-13 drh: */ 13e16c824a 2008-02-13 drh: static int enableOutputCmd( 13e16c824a 2008-02-13 drh: Th_Interp *interp, 13e16c824a 2008-02-13 drh: void *p, 13e16c824a 2008-02-13 drh: int argc, 13e16c824a 2008-02-13 drh: const unsigned char **argv, 13e16c824a 2008-02-13 drh: int *argl 13e16c824a 2008-02-13 drh: ){ 13e16c824a 2008-02-13 drh: if( argc!=2 ){ 13e16c824a 2008-02-13 drh: return Th_WrongNumArgs(interp, "enable_output BOOLEAN"); 13e16c824a 2008-02-13 drh: } 13e16c824a 2008-02-13 drh: return Th_ToInt(interp, argv[1], argl[1], &enableOutput); 13e16c824a 2008-02-13 drh: } 13e16c824a 2008-02-13 drh: 13e16c824a 2008-02-13 drh: /* 13e16c824a 2008-02-13 drh: ** Send text to the appropriate output: Either to the console 13e16c824a 2008-02-13 drh: ** or to the CGI reply buffer. 13e16c824a 2008-02-13 drh: */ ffe92f1a2f 2008-02-13 drh: static void sendText(const char *z, int n, int encode){ 13e16c824a 2008-02-13 drh: if( enableOutput && n ){ 13e16c824a 2008-02-13 drh: if( n<0 ) n = strlen(z); ffe92f1a2f 2008-02-13 drh: if( encode ){ ffe92f1a2f 2008-02-13 drh: z = htmlize(z, n); ffe92f1a2f 2008-02-13 drh: n = strlen(z); ffe92f1a2f 2008-02-13 drh: } 13e16c824a 2008-02-13 drh: if( g.cgiPanic ){ 13e16c824a 2008-02-13 drh: cgi_append_content(z, n); 13e16c824a 2008-02-13 drh: }else{ 13e16c824a 2008-02-13 drh: fwrite(z, 1, n, stdout); 13e16c824a 2008-02-13 drh: } ffe92f1a2f 2008-02-13 drh: if( encode ) free((char*)z); 13e16c824a 2008-02-13 drh: } 13e16c824a 2008-02-13 drh: } 13e16c824a 2008-02-13 drh: 13e16c824a 2008-02-13 drh: /* 13e16c824a 2008-02-13 drh: ** TH command: puts STRING 13e16c824a 2008-02-13 drh: ** TH command: html STRING 13e16c824a 2008-02-13 drh: ** 13e16c824a 2008-02-13 drh: ** Output STRING as HTML (html) or unchanged (puts). 13e16c824a 2008-02-13 drh: */ 13e16c824a 2008-02-13 drh: static int putsCmd( 13e16c824a 2008-02-13 drh: Th_Interp *interp, 13e16c824a 2008-02-13 drh: void *pConvert, 13e16c824a 2008-02-13 drh: int argc, 13e16c824a 2008-02-13 drh: const unsigned char **argv, 13e16c824a 2008-02-13 drh: int *argl 13e16c824a 2008-02-13 drh: ){ 13e16c824a 2008-02-13 drh: if( argc!=2 ){ 13e16c824a 2008-02-13 drh: return Th_WrongNumArgs(interp, "puts STRING"); 13e16c824a 2008-02-13 drh: } ffe92f1a2f 2008-02-13 drh: sendText((char*)argv[1], argl[1], pConvert!=0); 13e16c824a 2008-02-13 drh: return TH_OK; 13e16c824a 2008-02-13 drh: } 13e16c824a 2008-02-13 drh: 13e16c824a 2008-02-13 drh: /* 13e16c824a 2008-02-13 drh: ** TH command: wiki STRING 13e16c824a 2008-02-13 drh: ** 13e16c824a 2008-02-13 drh: ** Render the input string as wiki. 13e16c824a 2008-02-13 drh: */ 13e16c824a 2008-02-13 drh: static int wikiCmd( 13e16c824a 2008-02-13 drh: Th_Interp *interp, 13e16c824a 2008-02-13 drh: void *p, 13e16c824a 2008-02-13 drh: int argc, 13e16c824a 2008-02-13 drh: const unsigned char **argv, 13e16c824a 2008-02-13 drh: int *argl 13e16c824a 2008-02-13 drh: ){ 13e16c824a 2008-02-13 drh: if( argc!=2 ){ ffe92f1a2f 2008-02-13 drh: return Th_WrongNumArgs(interp, "wiki STRING"); 13e16c824a 2008-02-13 drh: } 13e16c824a 2008-02-13 drh: if( enableOutput ){ 13e16c824a 2008-02-13 drh: Blob src; 13e16c824a 2008-02-13 drh: blob_init(&src, (char*)argv[1], argl[1]); 13e16c824a 2008-02-13 drh: wiki_convert(&src, 0, WIKI_INLINE); 13e16c824a 2008-02-13 drh: blob_reset(&src); 13e16c824a 2008-02-13 drh: } ffe92f1a2f 2008-02-13 drh: return TH_OK; ffe92f1a2f 2008-02-13 drh: } ffe92f1a2f 2008-02-13 drh: ffe92f1a2f 2008-02-13 drh: /* ffe92f1a2f 2008-02-13 drh: ** TH command: hascap STRING ffe92f1a2f 2008-02-13 drh: ** ffe92f1a2f 2008-02-13 drh: ** Return true if the user has all of the capabilities listed in STRING. ffe92f1a2f 2008-02-13 drh: */ ffe92f1a2f 2008-02-13 drh: static int hascapCmd( ffe92f1a2f 2008-02-13 drh: Th_Interp *interp, ffe92f1a2f 2008-02-13 drh: void *p, ffe92f1a2f 2008-02-13 drh: int argc, ffe92f1a2f 2008-02-13 drh: const unsigned char **argv, ffe92f1a2f 2008-02-13 drh: int *argl ffe92f1a2f 2008-02-13 drh: ){ ffe92f1a2f 2008-02-13 drh: if( argc!=2 ){ ffe92f1a2f 2008-02-13 drh: return Th_WrongNumArgs(interp, "hascap STRING"); ffe92f1a2f 2008-02-13 drh: } ffe92f1a2f 2008-02-13 drh: Th_SetResultInt(interp, login_has_capability((char*)argv[1],argl[1])); 13e16c824a 2008-02-13 drh: return TH_OK; 13e16c824a 2008-02-13 drh: } 13e16c824a 2008-02-13 drh: 13e16c824a 2008-02-13 drh: /* 13e16c824a 2008-02-13 drh: ** Make sure the interpreter has been initialized. 13e16c824a 2008-02-13 drh: */ 13e16c824a 2008-02-13 drh: static void initializeInterp(void){ 13e16c824a 2008-02-13 drh: static struct _Command { 13e16c824a 2008-02-13 drh: const char *zName; 13e16c824a 2008-02-13 drh: Th_CommandProc xProc; 13e16c824a 2008-02-13 drh: void *pContext; 13e16c824a 2008-02-13 drh: } aCommand[] = { 13e16c824a 2008-02-13 drh: {"enable_output", enableOutputCmd, 0}, ffe92f1a2f 2008-02-13 drh: {"hascap", hascapCmd, 0}, 13e16c824a 2008-02-13 drh: {"html", putsCmd, 0}, 13e16c824a 2008-02-13 drh: {"puts", putsCmd, (void*)1}, 13e16c824a 2008-02-13 drh: {"wiki", wikiCmd, 0}, 13e16c824a 2008-02-13 drh: }; 13e16c824a 2008-02-13 drh: if( interp==0 ){ 13e16c824a 2008-02-13 drh: int i; 13e16c824a 2008-02-13 drh: interp = Th_CreateInterp(&vtab); 13e16c824a 2008-02-13 drh: th_register_language(interp); /* Basic scripting commands. */ 13e16c824a 2008-02-13 drh: for(i=0; i<sizeof(aCommand)/sizeof(aCommand[0]); i++){ 13e16c824a 2008-02-13 drh: Th_CreateCommand(interp, aCommand[i].zName, aCommand[i].xProc, 13e16c824a 2008-02-13 drh: aCommand[i].pContext, 0); 13e16c824a 2008-02-13 drh: } 13e16c824a 2008-02-13 drh: } 3ad9a5e210 2008-02-13 drh: } 3ad9a5e210 2008-02-13 drh: 3ad9a5e210 2008-02-13 drh: /* 3ad9a5e210 2008-02-13 drh: ** Initialize a variable in the interpreter. 3ad9a5e210 2008-02-13 drh: */ 3ad9a5e210 2008-02-13 drh: void Th_InitVar(const char *zName, const char *zValue){ 3ad9a5e210 2008-02-13 drh: initializeInterp(); ffe92f1a2f 2008-02-13 drh: Th_SetVar(interp, (uchar*)zName, -1, (uchar*)zValue, strlen(zValue)); 13e16c824a 2008-02-13 drh: } 13e16c824a 2008-02-13 drh: 13e16c824a 2008-02-13 drh: /* 13e16c824a 2008-02-13 drh: ** Return true if the string begins with the TH1 begin-script 13e16c824a 2008-02-13 drh: ** tag: <th1>. 13e16c824a 2008-02-13 drh: */ 13e16c824a 2008-02-13 drh: static int isBeginScriptTag(const char *z){ 13e16c824a 2008-02-13 drh: return z[0]=='<' 13e16c824a 2008-02-13 drh: && (z[1]=='t' || z[1]=='T') 13e16c824a 2008-02-13 drh: && (z[2]=='h' || z[2]=='H') 13e16c824a 2008-02-13 drh: && z[3]=='1' 13e16c824a 2008-02-13 drh: && z[4]=='>'; 13e16c824a 2008-02-13 drh: } 13e16c824a 2008-02-13 drh: 13e16c824a 2008-02-13 drh: /* 13e16c824a 2008-02-13 drh: ** Return true if the string begins with the TH1 end-script 13e16c824a 2008-02-13 drh: ** tag: </th1>. 13e16c824a 2008-02-13 drh: */ 13e16c824a 2008-02-13 drh: static int isEndScriptTag(const char *z){ 13e16c824a 2008-02-13 drh: return z[0]=='<' 13e16c824a 2008-02-13 drh: && z[1]=='/' 13e16c824a 2008-02-13 drh: && (z[2]=='t' || z[2]=='T') 13e16c824a 2008-02-13 drh: && (z[3]=='h' || z[3]=='H') 13e16c824a 2008-02-13 drh: && z[4]=='1' 13e16c824a 2008-02-13 drh: && z[5]=='>'; 13e16c824a 2008-02-13 drh: } 13e16c824a 2008-02-13 drh: 13e16c824a 2008-02-13 drh: /* 3ad9a5e210 2008-02-13 drh: ** If string z[0...] contains a valid variable name, return 3ad9a5e210 2008-02-13 drh: ** the number of characters in that name. Otherwise, return 0. 3ad9a5e210 2008-02-13 drh: */ 3ad9a5e210 2008-02-13 drh: static int validVarName(const char *z){ 3ad9a5e210 2008-02-13 drh: int i = 0; ffe92f1a2f 2008-02-13 drh: int inBracket = 0; ffe92f1a2f 2008-02-13 drh: if( z[0]=='<' ){ ffe92f1a2f 2008-02-13 drh: inBracket = 1; ffe92f1a2f 2008-02-13 drh: z++; ffe92f1a2f 2008-02-13 drh: } 3ad9a5e210 2008-02-13 drh: if( z[0]==':' && z[1]==':' && isalpha(z[2]) ){ 3ad9a5e210 2008-02-13 drh: z += 3; ffe92f1a2f 2008-02-13 drh: i += 3; 3ad9a5e210 2008-02-13 drh: }else if( isalpha(z[0]) ){ 3ad9a5e210 2008-02-13 drh: z ++; ffe92f1a2f 2008-02-13 drh: i += 1; 3ad9a5e210 2008-02-13 drh: }else{ 3ad9a5e210 2008-02-13 drh: return 0; 3ad9a5e210 2008-02-13 drh: } 3ad9a5e210 2008-02-13 drh: while( isalnum(z[0]) || z[0]=='_' ){ 3ad9a5e210 2008-02-13 drh: z++; 3ad9a5e210 2008-02-13 drh: i++; 3ad9a5e210 2008-02-13 drh: } ffe92f1a2f 2008-02-13 drh: if( inBracket ){ ffe92f1a2f 2008-02-13 drh: if( z[0]!='>' ) return 0; ffe92f1a2f 2008-02-13 drh: i += 2; ffe92f1a2f 2008-02-13 drh: } 3ad9a5e210 2008-02-13 drh: return i; 3ad9a5e210 2008-02-13 drh: } 3ad9a5e210 2008-02-13 drh: 3ad9a5e210 2008-02-13 drh: /* 13e16c824a 2008-02-13 drh: ** The z[] input contains text mixed with TH1 scripts. ffe92f1a2f 2008-02-13 drh: ** The TH1 scripts are contained within <th1>...</th1>. ffe92f1a2f 2008-02-13 drh: ** TH1 variables are $aaa or $<aaa>. The first form of ffe92f1a2f 2008-02-13 drh: ** variable is literal. The second is run through htmlize ffe92f1a2f 2008-02-13 drh: ** before being inserted. ffe92f1a2f 2008-02-13 drh: ** ffe92f1a2f 2008-02-13 drh: ** This routine processes the template and writes the results ffe92f1a2f 2008-02-13 drh: ** on either stdout or into CGI. 13e16c824a 2008-02-13 drh: */ 13e16c824a 2008-02-13 drh: int Th_Render(const char *z){ 13e16c824a 2008-02-13 drh: int i = 0; 3ad9a5e210 2008-02-13 drh: int n; 13e16c824a 2008-02-13 drh: int rc = TH_OK; 3ad9a5e210 2008-02-13 drh: uchar *zResult; 13e16c824a 2008-02-13 drh: initializeInterp(); 13e16c824a 2008-02-13 drh: while( z[i] ){ 3ad9a5e210 2008-02-13 drh: if( z[i]=='$' && (n = validVarName(&z[i+1]))>0 ){ ffe92f1a2f 2008-02-13 drh: const char *zVar; ffe92f1a2f 2008-02-13 drh: int nVar; ffe92f1a2f 2008-02-13 drh: sendText(z, i, 0); ffe92f1a2f 2008-02-13 drh: if( z[i+1]=='<' ){ ffe92f1a2f 2008-02-13 drh: /* Variables of the form $<aaa> */ ffe92f1a2f 2008-02-13 drh: zVar = &z[i+2]; ffe92f1a2f 2008-02-13 drh: nVar = n-2; ffe92f1a2f 2008-02-13 drh: }else{ ffe92f1a2f 2008-02-13 drh: /* Variables of the form $aaa */ ffe92f1a2f 2008-02-13 drh: zVar = &z[i+1]; ffe92f1a2f 2008-02-13 drh: nVar = n; ffe92f1a2f 2008-02-13 drh: } ffe92f1a2f 2008-02-13 drh: rc = Th_GetVar(interp, (uchar*)zVar, nVar); 3ad9a5e210 2008-02-13 drh: z += i+1+n; 3ad9a5e210 2008-02-13 drh: i = 0; ffe92f1a2f 2008-02-13 drh: zResult = (uchar*)Th_GetResult(interp, &n); ffe92f1a2f 2008-02-13 drh: sendText((char*)zResult, n, n>nVar); 3ad9a5e210 2008-02-13 drh: }else if( z[i]=='<' && isBeginScriptTag(&z[i]) ){ ffe92f1a2f 2008-02-13 drh: sendText(z, i, 0); 13e16c824a 2008-02-13 drh: z += i+5; 13e16c824a 2008-02-13 drh: for(i=0; z[i] && (z[i]!='<' || !isEndScriptTag(&z[i])); i++){} 13e16c824a 2008-02-13 drh: rc = Th_Eval(interp, 0, (const uchar*)z, i); 13e16c824a 2008-02-13 drh: if( rc!=SBS_OK ) break; 13e16c824a 2008-02-13 drh: z += i; 13e16c824a 2008-02-13 drh: if( z[0] ){ z += 6; } 13e16c824a 2008-02-13 drh: i = 0; 13e16c824a 2008-02-13 drh: }else{ 13e16c824a 2008-02-13 drh: i++; 13e16c824a 2008-02-13 drh: } 13e16c824a 2008-02-13 drh: } 13e16c824a 2008-02-13 drh: if( rc==TH_ERROR ){ ffe92f1a2f 2008-02-13 drh: sendText("<hr><p><font color=\"red\"><b>ERROR: ", -1, 0); ffe92f1a2f 2008-02-13 drh: zResult = (uchar*)Th_GetResult(interp, &n); ffe92f1a2f 2008-02-13 drh: sendText((char*)zResult, n, 1); ffe92f1a2f 2008-02-13 drh: sendText("</b></font></p>", -1, 0); 13e16c824a 2008-02-13 drh: }else{ ffe92f1a2f 2008-02-13 drh: sendText(z, i, 0); 13e16c824a 2008-02-13 drh: } 13e16c824a 2008-02-13 drh: return rc; 13e16c824a 2008-02-13 drh: } 13e16c824a 2008-02-13 drh: 13e16c824a 2008-02-13 drh: /* 13e16c824a 2008-02-13 drh: ** COMMAND: test-th-render 13e16c824a 2008-02-13 drh: */ 13e16c824a 2008-02-13 drh: void test_th_render(void){ 13e16c824a 2008-02-13 drh: Blob in; 13e16c824a 2008-02-13 drh: if( g.argc<3 ){ 13e16c824a 2008-02-13 drh: usage("FILE"); 13e16c824a 2008-02-13 drh: } 13e16c824a 2008-02-13 drh: blob_zero(&in); 13e16c824a 2008-02-13 drh: blob_read_from_file(&in, g.argv[2]); 13e16c824a 2008-02-13 drh: Th_Render(blob_str(&in)); 13e16c824a 2008-02-13 drh: }