PageRenderTime 48ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/src/ssfossil/fossil/src/th_main.c

https://github.com/paulfitz/coopy
C | 530 lines | 371 code | 25 blank | 134 comment | 73 complexity | 0341a29b557d1b97a801f335ecdea086 MD5 | raw file
  1. /*
  2. ** Copyright (c) 2008 D. Richard Hipp
  3. **
  4. ** This program is free software; you can redistribute it and/or
  5. ** modify it under the terms of the Simplified BSD License (also
  6. ** known as the "2-Clause License" or "FreeBSD License".)
  7. ** This program is distributed in the hope that it will be useful,
  8. ** but without any warranty; without even the implied warranty of
  9. ** merchantability or fitness for a particular purpose.
  10. **
  11. ** Author contact information:
  12. ** drh@hwaci.com
  13. ** http://www.hwaci.com/drh/
  14. **
  15. *******************************************************************************
  16. **
  17. ** This file contains an interface between the TH scripting language
  18. ** (an independent project) and fossil.
  19. */
  20. #include "config.h"
  21. #include "th_main.h"
  22. /*
  23. ** Global variable counting the number of outstanding calls to malloc()
  24. ** made by the th1 implementation. This is used to catch memory leaks
  25. ** in the interpreter. Obviously, it also means th1 is not threadsafe.
  26. */
  27. static int nOutstandingMalloc = 0;
  28. /*
  29. ** Implementations of malloc() and free() to pass to the interpreter.
  30. */
  31. static void *xMalloc(unsigned int n){
  32. void *p = malloc(n);
  33. if( p ){
  34. nOutstandingMalloc++;
  35. }
  36. return p;
  37. }
  38. static void xFree(void *p){
  39. if( p ){
  40. nOutstandingMalloc--;
  41. }
  42. free(p);
  43. }
  44. static Th_Vtab vtab = { xMalloc, xFree };
  45. /*
  46. ** Generate a TH1 trace message if debugging is enabled.
  47. */
  48. void Th_Trace(const char *zFormat, ...){
  49. va_list ap;
  50. va_start(ap, zFormat);
  51. blob_vappendf(&g.thLog, zFormat, ap);
  52. va_end(ap);
  53. }
  54. /*
  55. ** True if output is enabled. False if disabled.
  56. */
  57. static int enableOutput = 1;
  58. /*
  59. ** TH command: enable_output BOOLEAN
  60. **
  61. ** Enable or disable the puts and hputs commands.
  62. */
  63. static int enableOutputCmd(
  64. Th_Interp *interp,
  65. void *p,
  66. int argc,
  67. const char **argv,
  68. int *argl
  69. ){
  70. if( argc!=2 ){
  71. return Th_WrongNumArgs(interp, "enable_output BOOLEAN");
  72. }
  73. return Th_ToInt(interp, argv[1], argl[1], &enableOutput);
  74. }
  75. /*
  76. ** Send text to the appropriate output: Either to the console
  77. ** or to the CGI reply buffer.
  78. */
  79. static void sendText(const char *z, int n, int encode){
  80. if( enableOutput && n ){
  81. if( n<0 ) n = strlen(z);
  82. if( encode ){
  83. z = htmlize(z, n);
  84. n = strlen(z);
  85. }
  86. if( g.cgiOutput ){
  87. cgi_append_content(z, n);
  88. }else{
  89. fwrite(z, 1, n, stdout);
  90. }
  91. if( encode ) free((char*)z);
  92. }
  93. }
  94. /*
  95. ** TH command: puts STRING
  96. ** TH command: html STRING
  97. **
  98. ** Output STRING as HTML (html) or unchanged (puts).
  99. */
  100. static int putsCmd(
  101. Th_Interp *interp,
  102. void *pConvert,
  103. int argc,
  104. const char **argv,
  105. int *argl
  106. ){
  107. if( argc!=2 ){
  108. return Th_WrongNumArgs(interp, "puts STRING");
  109. }
  110. sendText((char*)argv[1], argl[1], pConvert!=0);
  111. return TH_OK;
  112. }
  113. /*
  114. ** TH command: wiki STRING
  115. **
  116. ** Render the input string as wiki.
  117. */
  118. static int wikiCmd(
  119. Th_Interp *interp,
  120. void *p,
  121. int argc,
  122. const char **argv,
  123. int *argl
  124. ){
  125. if( argc!=2 ){
  126. return Th_WrongNumArgs(interp, "wiki STRING");
  127. }
  128. if( enableOutput ){
  129. Blob src;
  130. blob_init(&src, (char*)argv[1], argl[1]);
  131. wiki_convert(&src, 0, WIKI_INLINE);
  132. blob_reset(&src);
  133. }
  134. return TH_OK;
  135. }
  136. /*
  137. ** TH command: htmlize STRING
  138. **
  139. ** Escape all characters of STRING which have special meaning in HTML.
  140. ** Return a new string result.
  141. */
  142. static int htmlizeCmd(
  143. Th_Interp *interp,
  144. void *p,
  145. int argc,
  146. const char **argv,
  147. int *argl
  148. ){
  149. char *zOut;
  150. if( argc!=2 ){
  151. return Th_WrongNumArgs(interp, "htmlize STRING");
  152. }
  153. zOut = htmlize((char*)argv[1], argl[1]);
  154. Th_SetResult(interp, zOut, -1);
  155. free(zOut);
  156. return TH_OK;
  157. }
  158. /*
  159. ** TH command: date
  160. **
  161. ** Return a string which is the current time and date.
  162. */
  163. static int dateCmd(
  164. Th_Interp *interp,
  165. void *p,
  166. int argc,
  167. const char **argv,
  168. int *argl
  169. ){
  170. char *zOut = db_text("??", "SELECT datetime('now')");
  171. Th_SetResult(interp, zOut, -1);
  172. free(zOut);
  173. return TH_OK;
  174. }
  175. /*
  176. ** TH command: hascap STRING
  177. **
  178. ** Return true if the user has all of the capabilities listed in STRING.
  179. */
  180. static int hascapCmd(
  181. Th_Interp *interp,
  182. void *p,
  183. int argc,
  184. const char **argv,
  185. int *argl
  186. ){
  187. int rc;
  188. if( argc!=2 ){
  189. return Th_WrongNumArgs(interp, "hascap STRING");
  190. }
  191. rc = login_has_capability((char*)argv[1],argl[1]);
  192. if( g.thTrace ){
  193. Th_Trace("[hascap %.*h] => %d<br />\n", argl[1], argv[1], rc);
  194. }
  195. Th_SetResultInt(interp, rc);
  196. return TH_OK;
  197. }
  198. /*
  199. ** TH command: anycap STRING
  200. **
  201. ** Return true if the user has any one of the capabilities listed in STRING.
  202. */
  203. static int anycapCmd(
  204. Th_Interp *interp,
  205. void *p,
  206. int argc,
  207. const char **argv,
  208. int *argl
  209. ){
  210. int rc = 0;
  211. int i;
  212. if( argc!=2 ){
  213. return Th_WrongNumArgs(interp, "anycap STRING");
  214. }
  215. for(i=0; rc==0 && i<argl[1]; i++){
  216. rc = login_has_capability((char*)&argv[1][i],1);
  217. }
  218. if( g.thTrace ){
  219. Th_Trace("[hascap %.*h] => %d<br />\n", argl[1], argv[1], rc);
  220. }
  221. Th_SetResultInt(interp, rc);
  222. return TH_OK;
  223. }
  224. /*
  225. ** TH1 command: combobox NAME TEXT-LIST NUMLINES
  226. **
  227. ** Generate an HTML combobox. NAME is both the name of the
  228. ** CGI parameter and the name of a variable that contains the
  229. ** currently selected value. TEXT-LIST is a list of possible
  230. ** values for the combobox. NUMLINES is 1 for a true combobox.
  231. ** If NUMLINES is greater than one then the display is a listbox
  232. ** with the number of lines given.
  233. */
  234. static int comboboxCmd(
  235. Th_Interp *interp,
  236. void *p,
  237. int argc,
  238. const char **argv,
  239. int *argl
  240. ){
  241. if( argc!=4 ){
  242. return Th_WrongNumArgs(interp, "combobox NAME TEXT-LIST NUMLINES");
  243. }
  244. if( enableOutput ){
  245. int height;
  246. Blob name;
  247. int nValue;
  248. const char *zValue;
  249. char *z, *zH;
  250. int nElem;
  251. int *aszElem;
  252. char **azElem;
  253. int i;
  254. if( Th_ToInt(interp, argv[3], argl[3], &height) ) return TH_ERROR;
  255. Th_SplitList(interp, argv[2], argl[2], &azElem, &aszElem, &nElem);
  256. blob_init(&name, (char*)argv[1], argl[1]);
  257. zValue = Th_Fetch(blob_str(&name), &nValue);
  258. z = mprintf("<select name=\"%z\" size=\"%d\">",
  259. htmlize(blob_buffer(&name), blob_size(&name)), height);
  260. sendText(z, -1, 0);
  261. free(z);
  262. blob_reset(&name);
  263. for(i=0; i<nElem; i++){
  264. zH = htmlize((char*)azElem[i], aszElem[i]);
  265. if( zValue && aszElem[i]==nValue
  266. && memcmp(zValue, azElem[i], nValue)==0 ){
  267. z = mprintf("<option value=\"%s\" selected=\"selected\">%s</option>",
  268. zH, zH);
  269. }else{
  270. z = mprintf("<option value=\"%s\">%s</option>", zH, zH);
  271. }
  272. free(zH);
  273. sendText(z, -1, 0);
  274. free(z);
  275. }
  276. sendText("</select>", -1, 0);
  277. Th_Free(interp, azElem);
  278. }
  279. return TH_OK;
  280. }
  281. /*
  282. ** TH1 command: linecount STRING MAX MIN
  283. **
  284. ** Return one more than the number of \n characters in STRING. But
  285. ** never return less than MIN or more than MAX.
  286. */
  287. static int linecntCmd(
  288. Th_Interp *interp,
  289. void *p,
  290. int argc,
  291. const char **argv,
  292. int *argl
  293. ){
  294. const char *z;
  295. int size, n, i;
  296. int iMin, iMax;
  297. if( argc!=4 ){
  298. return Th_WrongNumArgs(interp, "linecount STRING MAX MIN");
  299. }
  300. if( Th_ToInt(interp, argv[2], argl[2], &iMax) ) return TH_ERROR;
  301. if( Th_ToInt(interp, argv[3], argl[3], &iMin) ) return TH_ERROR;
  302. z = argv[1];
  303. size = argl[1];
  304. for(n=1, i=0; i<size; i++){
  305. if( z[i]=='\n' ){
  306. n++;
  307. if( n>=iMax ) break;
  308. }
  309. }
  310. if( n<iMin ) n = iMin;
  311. if( n>iMax ) n = iMax;
  312. Th_SetResultInt(interp, n);
  313. return TH_OK;
  314. }
  315. /*
  316. ** Make sure the interpreter has been initialized. Initialize it if
  317. ** it has not been already.
  318. **
  319. ** The interpreter is stored in the g.interp global variable.
  320. */
  321. void Th_FossilInit(void){
  322. static struct _Command {
  323. const char *zName;
  324. Th_CommandProc xProc;
  325. void *pContext;
  326. } aCommand[] = {
  327. {"anycap", anycapCmd, 0},
  328. {"combobox", comboboxCmd, 0},
  329. {"enable_output", enableOutputCmd, 0},
  330. {"linecount", linecntCmd, 0},
  331. {"hascap", hascapCmd, 0},
  332. {"htmlize", htmlizeCmd, 0},
  333. {"date", dateCmd, 0},
  334. {"html", putsCmd, 0},
  335. {"puts", putsCmd, (void*)1},
  336. {"wiki", wikiCmd, 0},
  337. };
  338. if( g.interp==0 ){
  339. int i;
  340. g.interp = Th_CreateInterp(&vtab);
  341. th_register_language(g.interp); /* Basic scripting commands. */
  342. for(i=0; i<sizeof(aCommand)/sizeof(aCommand[0]); i++){
  343. Th_CreateCommand(g.interp, aCommand[i].zName, aCommand[i].xProc,
  344. aCommand[i].pContext, 0);
  345. }
  346. }
  347. }
  348. /*
  349. ** Store a string value in a variable in the interpreter.
  350. */
  351. void Th_Store(const char *zName, const char *zValue){
  352. Th_FossilInit();
  353. if( zValue ){
  354. if( g.thTrace ){
  355. Th_Trace("set %h {%h}<br />\n", zName, zValue);
  356. }
  357. Th_SetVar(g.interp, zName, -1, zValue, strlen(zValue));
  358. }
  359. }
  360. /*
  361. ** Unset a variable.
  362. */
  363. void Th_Unstore(const char *zName){
  364. if( g.interp ){
  365. Th_UnsetVar(g.interp, (char*)zName, -1);
  366. }
  367. }
  368. /*
  369. ** Retrieve a string value from the interpreter. If no such
  370. ** variable exists, return NULL.
  371. */
  372. char *Th_Fetch(const char *zName, int *pSize){
  373. int rc;
  374. Th_FossilInit();
  375. rc = Th_GetVar(g.interp, (char*)zName, -1);
  376. if( rc==TH_OK ){
  377. return (char*)Th_GetResult(g.interp, pSize);
  378. }else{
  379. return 0;
  380. }
  381. }
  382. /*
  383. ** Return true if the string begins with the TH1 begin-script
  384. ** tag: <th1>.
  385. */
  386. static int isBeginScriptTag(const char *z){
  387. return z[0]=='<'
  388. && (z[1]=='t' || z[1]=='T')
  389. && (z[2]=='h' || z[2]=='H')
  390. && z[3]=='1'
  391. && z[4]=='>';
  392. }
  393. /*
  394. ** Return true if the string begins with the TH1 end-script
  395. ** tag: </th1>.
  396. */
  397. static int isEndScriptTag(const char *z){
  398. return z[0]=='<'
  399. && z[1]=='/'
  400. && (z[2]=='t' || z[2]=='T')
  401. && (z[3]=='h' || z[3]=='H')
  402. && z[4]=='1'
  403. && z[5]=='>';
  404. }
  405. /*
  406. ** If string z[0...] contains a valid variable name, return
  407. ** the number of characters in that name. Otherwise, return 0.
  408. */
  409. static int validVarName(const char *z){
  410. int i = 0;
  411. int inBracket = 0;
  412. if( z[0]=='<' ){
  413. inBracket = 1;
  414. z++;
  415. }
  416. if( z[0]==':' && z[1]==':' && isalpha(z[2]) ){
  417. z += 3;
  418. i += 3;
  419. }else if( isalpha(z[0]) ){
  420. z ++;
  421. i += 1;
  422. }else{
  423. return 0;
  424. }
  425. while( isalnum(z[0]) || z[0]=='_' ){
  426. z++;
  427. i++;
  428. }
  429. if( inBracket ){
  430. if( z[0]!='>' ) return 0;
  431. i += 2;
  432. }
  433. return i;
  434. }
  435. /*
  436. ** The z[] input contains text mixed with TH1 scripts.
  437. ** The TH1 scripts are contained within <th1>...</th1>.
  438. ** TH1 variables are $aaa or $<aaa>. The first form of
  439. ** variable is literal. The second is run through htmlize
  440. ** before being inserted.
  441. **
  442. ** This routine processes the template and writes the results
  443. ** on either stdout or into CGI.
  444. */
  445. int Th_Render(const char *z){
  446. int i = 0;
  447. int n;
  448. int rc = TH_OK;
  449. char *zResult;
  450. Th_FossilInit();
  451. while( z[i] ){
  452. if( z[i]=='$' && (n = validVarName(&z[i+1]))>0 ){
  453. const char *zVar;
  454. int nVar;
  455. sendText(z, i, 0);
  456. if( z[i+1]=='<' ){
  457. /* Variables of the form $<aaa> */
  458. zVar = &z[i+2];
  459. nVar = n-2;
  460. }else{
  461. /* Variables of the form $aaa */
  462. zVar = &z[i+1];
  463. nVar = n;
  464. }
  465. rc = Th_GetVar(g.interp, (char*)zVar, nVar);
  466. z += i+1+n;
  467. i = 0;
  468. zResult = (char*)Th_GetResult(g.interp, &n);
  469. sendText((char*)zResult, n, n>nVar);
  470. }else if( z[i]=='<' && isBeginScriptTag(&z[i]) ){
  471. sendText(z, i, 0);
  472. z += i+5;
  473. for(i=0; z[i] && (z[i]!='<' || !isEndScriptTag(&z[i])); i++){}
  474. rc = Th_Eval(g.interp, 0, (const char*)z, i);
  475. if( rc!=TH_OK ) break;
  476. z += i;
  477. if( z[0] ){ z += 6; }
  478. i = 0;
  479. }else{
  480. i++;
  481. }
  482. }
  483. if( rc==TH_ERROR ){
  484. sendText("<hr><p><font color=\"red\"><b>ERROR: ", -1, 0);
  485. zResult = (char*)Th_GetResult(g.interp, &n);
  486. sendText((char*)zResult, n, 1);
  487. sendText("</b></font></p>", -1, 0);
  488. }else{
  489. sendText(z, i, 0);
  490. }
  491. return rc;
  492. }
  493. /*
  494. ** COMMAND: test-th-render
  495. */
  496. void test_th_render(void){
  497. Blob in;
  498. if( g.argc<3 ){
  499. usage("FILE");
  500. }
  501. blob_zero(&in);
  502. blob_read_from_file(&in, g.argv[2]);
  503. Th_Render(blob_str(&in));
  504. }