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: */ 13e16c824a 2008-02-13 drh: static void sendText(const char *z, int n){ 13e16c824a 2008-02-13 drh: if( enableOutput && n ){ 13e16c824a 2008-02-13 drh: if( n<0 ) n = strlen(z); 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: } 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: } 13e16c824a 2008-02-13 drh: if( enableOutput ){ 13e16c824a 2008-02-13 drh: int size; 13e16c824a 2008-02-13 drh: char *zOut; 13e16c824a 2008-02-13 drh: if( pConvert ){ 13e16c824a 2008-02-13 drh: zOut = htmlize((char*)argv[1], argl[1]); 13e16c824a 2008-02-13 drh: size = strlen(zOut); 13e16c824a 2008-02-13 drh: }else{ 13e16c824a 2008-02-13 drh: zOut = (char*)argv[1]; 13e16c824a 2008-02-13 drh: size = argl[1]; 13e16c824a 2008-02-13 drh: } 13e16c824a 2008-02-13 drh: sendText(zOut, size); 13e16c824a 2008-02-13 drh: if( pConvert ){ 13e16c824a 2008-02-13 drh: free(zOut); 13e16c824a 2008-02-13 drh: } 13e16c824a 2008-02-13 drh: } 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 ){ 13e16c824a 2008-02-13 drh: return Th_WrongNumArgs(interp, "puts 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: } 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}, 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: } 13e16c824a 2008-02-13 drh: } 13e16c824a 2008-02-13 drh: 13e16c824a 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(); 3ad9a5e210 2008-02-13 drh: Th_SetVar(interp, zName, -1, zValue, strlen(zValue)); 3ad9a5e210 2008-02-13 drh: } 3ad9a5e210 2008-02-13 drh: 3ad9a5e210 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; 3ad9a5e210 2008-02-13 drh: if( z[0]==':' && z[1]==':' && isalpha(z[2]) ){ 3ad9a5e210 2008-02-13 drh: z += 3; 3ad9a5e210 2008-02-13 drh: i = 3; 3ad9a5e210 2008-02-13 drh: }else if( isalpha(z[0]) ){ 3ad9a5e210 2008-02-13 drh: z ++; 3ad9a5e210 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: } 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. 13e16c824a 2008-02-13 drh: ** The TH1 scripts are contained within <th1>...</th1>. This routine 13e16c824a 2008-02-13 drh: ** processes the template and writes the results on either 13e16c824a 2008-02-13 drh: ** 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 ){ 3ad9a5e210 2008-02-13 drh: sendText(z, i); 3ad9a5e210 2008-02-13 drh: rc = Th_GetVar(interp, &z[i+1], n); 3ad9a5e210 2008-02-13 drh: z += i+1+n; 3ad9a5e210 2008-02-13 drh: i = 0; 3ad9a5e210 2008-02-13 drh: zResult = Th_GetResult(interp, &n); 3ad9a5e210 2008-02-13 drh: sendText(zResult, n); 3ad9a5e210 2008-02-13 drh: }else if( z[i]=='<' && isBeginScriptTag(&z[i]) ){ 13e16c824a 2008-02-13 drh: sendText(z, i); 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 ){ 13e16c824a 2008-02-13 drh: sendText("<hr><p><font color=\"red\"><b>ERROR: ", -1); 3ad9a5e210 2008-02-13 drh: zResult = Th_GetResult(interp, &n); 3ad9a5e210 2008-02-13 drh: sendText(zResult, n); 13e16c824a 2008-02-13 drh: sendText("</b></font></p>", -1); 13e16c824a 2008-02-13 drh: }else{ 13e16c824a 2008-02-13 drh: sendText(z, i); 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: }