Fossil SCM
Initial work on a pikchr CLI command which integrates TH1 for programmatic pikchr generation. DO NOT MERGE: the required changes to TH1 need more testing to ensure they do not introduce fallout in the skin output. Also, output redirection is still very imcomplete and TH1 error reporting does not yet do the right thing.
Commit
8a4304eae23e796fa88fe60269a3a8157dafbbc66ce62f380826cfcd829c7748
Parent
931f782ff3f02ea…
2 files changed
+92
+91
-40
+92
| --- src/pikchrshow.c | ||
| +++ src/pikchrshow.c | ||
| @@ -165,5 +165,97 @@ | ||
| 165 | 165 | } |
| 166 | 166 | builtin_emit_fossil_js_apis("page.pikchrshow", 0); |
| 167 | 167 | builtin_fulfill_js_requests(); |
| 168 | 168 | style_footer(); |
| 169 | 169 | } |
| 170 | + | |
| 171 | +static void pikchr_th_init(u32 fThInit){ | |
| 172 | + Th_FossilInit(fThInit); | |
| 173 | +} | |
| 174 | + | |
| 175 | +/* | |
| 176 | +** COMMAND: pikchr | |
| 177 | +** | |
| 178 | +** Usage: %fossil pikchr [options] ?INFILE? ?OUTFILE? | |
| 179 | +** | |
| 180 | +** Options: | |
| 181 | +** | |
| 182 | +** -div On success, adds a DIV wrapper around the | |
| 183 | +** resulting SVG output which limits its max-width. | |
| 184 | +** | |
| 185 | +** -th Process the input using TH1 before passing it to pikchr. | |
| 186 | +** | |
| 187 | +** -th-novar Disable $var and $<var> TH1 processing. Only applies | |
| 188 | +** with the -th flag. | |
| 189 | +** | |
| 190 | +** -th-trace Trace TH1 execution (for debugging purposes) | |
| 191 | +** | |
| 192 | +** TH1 Caveats: the built-in TH1 commands make some assumptions about | |
| 193 | +** HTML escaping and output which do not apply via this | |
| 194 | +** command. e.g. some commands will output directly to stdout, rather | |
| 195 | +** than the output buffer this command requires. Improvements in that | |
| 196 | +** regard are under consideration/construction. | |
| 197 | +*/ | |
| 198 | +void pikchr_cmd(void){ | |
| 199 | + Blob bIn = empty_blob; | |
| 200 | + Blob bOut = empty_blob; | |
| 201 | + const char * zInfile = "-"; | |
| 202 | + const char * zOutfile = "-"; | |
| 203 | + const int fWithDiv = find_option("div",0,0)!=0; | |
| 204 | + const int fTh1 = find_option("th",0,0)!=0; | |
| 205 | + const int fNoVar = find_option("th-novar",0,0)!=0; | |
| 206 | + int isErr = 0; | |
| 207 | + u32 fThInit = TH_INIT_DEFAULT; | |
| 208 | + | |
| 209 | + Th_InitTraceLog()/*processes -th-trace flag*/; | |
| 210 | + verify_all_options(); | |
| 211 | + if(g.argc>4){ | |
| 212 | + usage("?INFILE? ?OUTFILE?"); | |
| 213 | + } | |
| 214 | + if(g.argc>2){ | |
| 215 | + zInfile = g.argv[2]; | |
| 216 | + } | |
| 217 | + if(g.argc>3){ | |
| 218 | + zOutfile = g.argv[3]; | |
| 219 | + } | |
| 220 | + blob_read_from_file(&bIn, zInfile, ExtFILE); | |
| 221 | + if(fTh1){ | |
| 222 | + Blob out = empty_blob; | |
| 223 | + Blob * oldOut; | |
| 224 | + db_find_and_open_repository(OPEN_ANY_SCHEMA | OPEN_OK_NOT_FOUND, 0) | |
| 225 | + /* ^^^ needed for certain functions to work */; | |
| 226 | + oldOut = Th_SetOutputBlob(&out); | |
| 227 | + pikchr_th_init(fThInit); | |
| 228 | + isErr = Th_RenderToBlob(blob_str(&bIn), &out, | |
| 229 | + fNoVar ? TH_R2B_NO_VARS : 0); | |
| 230 | + blob_reset(&bIn); | |
| 231 | + bIn = out; | |
| 232 | + Th_SetOutputBlob(oldOut); | |
| 233 | + /*fossil_print("th'd:\n%b\n", &bIn);*/ | |
| 234 | + } | |
| 235 | + if(!isErr){ | |
| 236 | + int w = 0, h = 0; | |
| 237 | + const char * zContent = blob_str(&bIn); | |
| 238 | + char *zOut = pikchr(zContent, "pikchr", 0, &w, &h); | |
| 239 | + if( w>0 && h>0 ){ | |
| 240 | + if(fWithDiv){ | |
| 241 | + blob_appendf(&bOut,"<div style='max-width:%dpx;'>\n", w); | |
| 242 | + } | |
| 243 | + blob_append(&bOut, zOut, -1); | |
| 244 | + if(fWithDiv){ | |
| 245 | + blob_append(&bOut,"</div>\n", 7); | |
| 246 | + } | |
| 247 | + }else{ | |
| 248 | + isErr = 1; | |
| 249 | + blob_append(&bOut, zOut, -1); | |
| 250 | + } | |
| 251 | + fossil_free(zOut); | |
| 252 | + } | |
| 253 | + if(isErr){ | |
| 254 | + fossil_fatal("ERROR: %b", &bOut); | |
| 255 | + }else{ | |
| 256 | + blob_write_to_file(&bOut, zOutfile); | |
| 257 | + } | |
| 258 | + Th_PrintTraceLog(); | |
| 259 | + blob_reset(&bIn); | |
| 260 | + blob_reset(&bOut); | |
| 261 | +} | |
| 170 | 262 |
| --- src/pikchrshow.c | |
| +++ src/pikchrshow.c | |
| @@ -165,5 +165,97 @@ | |
| 165 | } |
| 166 | builtin_emit_fossil_js_apis("page.pikchrshow", 0); |
| 167 | builtin_fulfill_js_requests(); |
| 168 | style_footer(); |
| 169 | } |
| 170 |
| --- src/pikchrshow.c | |
| +++ src/pikchrshow.c | |
| @@ -165,5 +165,97 @@ | |
| 165 | } |
| 166 | builtin_emit_fossil_js_apis("page.pikchrshow", 0); |
| 167 | builtin_fulfill_js_requests(); |
| 168 | style_footer(); |
| 169 | } |
| 170 | |
| 171 | static void pikchr_th_init(u32 fThInit){ |
| 172 | Th_FossilInit(fThInit); |
| 173 | } |
| 174 | |
| 175 | /* |
| 176 | ** COMMAND: pikchr |
| 177 | ** |
| 178 | ** Usage: %fossil pikchr [options] ?INFILE? ?OUTFILE? |
| 179 | ** |
| 180 | ** Options: |
| 181 | ** |
| 182 | ** -div On success, adds a DIV wrapper around the |
| 183 | ** resulting SVG output which limits its max-width. |
| 184 | ** |
| 185 | ** -th Process the input using TH1 before passing it to pikchr. |
| 186 | ** |
| 187 | ** -th-novar Disable $var and $<var> TH1 processing. Only applies |
| 188 | ** with the -th flag. |
| 189 | ** |
| 190 | ** -th-trace Trace TH1 execution (for debugging purposes) |
| 191 | ** |
| 192 | ** TH1 Caveats: the built-in TH1 commands make some assumptions about |
| 193 | ** HTML escaping and output which do not apply via this |
| 194 | ** command. e.g. some commands will output directly to stdout, rather |
| 195 | ** than the output buffer this command requires. Improvements in that |
| 196 | ** regard are under consideration/construction. |
| 197 | */ |
| 198 | void pikchr_cmd(void){ |
| 199 | Blob bIn = empty_blob; |
| 200 | Blob bOut = empty_blob; |
| 201 | const char * zInfile = "-"; |
| 202 | const char * zOutfile = "-"; |
| 203 | const int fWithDiv = find_option("div",0,0)!=0; |
| 204 | const int fTh1 = find_option("th",0,0)!=0; |
| 205 | const int fNoVar = find_option("th-novar",0,0)!=0; |
| 206 | int isErr = 0; |
| 207 | u32 fThInit = TH_INIT_DEFAULT; |
| 208 | |
| 209 | Th_InitTraceLog()/*processes -th-trace flag*/; |
| 210 | verify_all_options(); |
| 211 | if(g.argc>4){ |
| 212 | usage("?INFILE? ?OUTFILE?"); |
| 213 | } |
| 214 | if(g.argc>2){ |
| 215 | zInfile = g.argv[2]; |
| 216 | } |
| 217 | if(g.argc>3){ |
| 218 | zOutfile = g.argv[3]; |
| 219 | } |
| 220 | blob_read_from_file(&bIn, zInfile, ExtFILE); |
| 221 | if(fTh1){ |
| 222 | Blob out = empty_blob; |
| 223 | Blob * oldOut; |
| 224 | db_find_and_open_repository(OPEN_ANY_SCHEMA | OPEN_OK_NOT_FOUND, 0) |
| 225 | /* ^^^ needed for certain functions to work */; |
| 226 | oldOut = Th_SetOutputBlob(&out); |
| 227 | pikchr_th_init(fThInit); |
| 228 | isErr = Th_RenderToBlob(blob_str(&bIn), &out, |
| 229 | fNoVar ? TH_R2B_NO_VARS : 0); |
| 230 | blob_reset(&bIn); |
| 231 | bIn = out; |
| 232 | Th_SetOutputBlob(oldOut); |
| 233 | /*fossil_print("th'd:\n%b\n", &bIn);*/ |
| 234 | } |
| 235 | if(!isErr){ |
| 236 | int w = 0, h = 0; |
| 237 | const char * zContent = blob_str(&bIn); |
| 238 | char *zOut = pikchr(zContent, "pikchr", 0, &w, &h); |
| 239 | if( w>0 && h>0 ){ |
| 240 | if(fWithDiv){ |
| 241 | blob_appendf(&bOut,"<div style='max-width:%dpx;'>\n", w); |
| 242 | } |
| 243 | blob_append(&bOut, zOut, -1); |
| 244 | if(fWithDiv){ |
| 245 | blob_append(&bOut,"</div>\n", 7); |
| 246 | } |
| 247 | }else{ |
| 248 | isErr = 1; |
| 249 | blob_append(&bOut, zOut, -1); |
| 250 | } |
| 251 | fossil_free(zOut); |
| 252 | } |
| 253 | if(isErr){ |
| 254 | fossil_fatal("ERROR: %b", &bOut); |
| 255 | }else{ |
| 256 | blob_write_to_file(&bOut, zOutfile); |
| 257 | } |
| 258 | Th_PrintTraceLog(); |
| 259 | blob_reset(&bIn); |
| 260 | blob_reset(&bOut); |
| 261 | } |
| 262 |
+91
-40
| --- src/th_main.c | ||
| +++ src/th_main.c | ||
| @@ -309,41 +309,65 @@ | ||
| 309 | 309 | } |
| 310 | 310 | } |
| 311 | 311 | return zRc; |
| 312 | 312 | } |
| 313 | 313 | |
| 314 | +static Blob * pThOut = 0; | |
| 314 | 315 | /* |
| 315 | -** Send text to the appropriate output: Either to the console | |
| 316 | -** or to the CGI reply buffer. Escape all characters with special | |
| 317 | -** meaning to HTML if the encode parameter is true. | |
| 316 | +** Sets the th1-internal output-redirection blob and returns the | |
| 317 | +** previous value. That blob is used by certain output-generation | |
| 318 | +** routines to emit its output. | |
| 318 | 319 | */ |
| 319 | -static void sendText(const char *z, int n, int encode){ | |
| 320 | +Blob * Th_SetOutputBlob(Blob * pOut){ | |
| 321 | + Blob * tmp = pThOut; | |
| 322 | + pThOut = pOut; | |
| 323 | + return tmp; | |
| 324 | +} | |
| 325 | + | |
| 326 | +/* | |
| 327 | +** Send text to the appropriate output: If pOut is not NULL, it is | |
| 328 | +** appended there, else to the console or to the CGI reply buffer. | |
| 329 | +** Escape all characters with special meaning to HTML if the encode | |
| 330 | +** parameter is true. | |
| 331 | +** | |
| 332 | +** If pOut is NULL and the global pThOut is not then that blob | |
| 333 | +** is used for output. | |
| 334 | +*/ | |
| 335 | +static void sendText(Blob * pOut, const char *z, int n, int encode){ | |
| 336 | + if(0==pOut && pThOut!=0){ | |
| 337 | + pOut = pThOut; | |
| 338 | + } | |
| 320 | 339 | if( enableOutput && n ){ |
| 321 | 340 | if( n<0 ) n = strlen(z); |
| 322 | 341 | if( encode ){ |
| 323 | 342 | z = htmlize(z, n); |
| 324 | 343 | n = strlen(z); |
| 325 | 344 | } |
| 326 | - if( g.cgiOutput ){ | |
| 345 | + if(pOut!=0){ | |
| 346 | + blob_append(pOut, z, n); | |
| 347 | + }else if( g.cgiOutput ){ | |
| 327 | 348 | cgi_append_content(z, n); |
| 328 | 349 | }else{ |
| 329 | 350 | fwrite(z, 1, n, stdout); |
| 330 | 351 | fflush(stdout); |
| 331 | 352 | } |
| 332 | 353 | if( encode ) free((char*)z); |
| 333 | 354 | } |
| 334 | 355 | } |
| 335 | 356 | |
| 336 | -static void sendError(const char *z, int n, int forceCgi){ | |
| 357 | +/* | |
| 358 | +** error-reporting counterpart of sendText(). | |
| 359 | +*/ | |
| 360 | +static void sendError(Blob * pOut, const char *z, int n, int forceCgi){ | |
| 337 | 361 | int savedEnable = enableOutput; |
| 338 | 362 | enableOutput = 1; |
| 339 | 363 | if( forceCgi || g.cgiOutput ){ |
| 340 | - sendText("<hr /><p class=\"thmainError\">", -1, 0); | |
| 364 | + sendText(pOut, "<hr /><p class=\"thmainError\">", -1, 0); | |
| 341 | 365 | } |
| 342 | - sendText("ERROR: ", -1, 0); | |
| 343 | - sendText((char*)z, n, 1); | |
| 344 | - sendText(forceCgi || g.cgiOutput ? "</p>" : "\n", -1, 0); | |
| 366 | + sendText(pOut,"ERROR: ", -1, 0); | |
| 367 | + sendText(pOut,(char*)z, n, 1); | |
| 368 | + sendText(pOut,forceCgi || g.cgiOutput ? "</p>" : "\n", -1, 0); | |
| 345 | 369 | enableOutput = savedEnable; |
| 346 | 370 | } |
| 347 | 371 | |
| 348 | 372 | /* |
| 349 | 373 | ** Convert name to an rid. This function was copied from name_to_typed_rid() |
| @@ -450,11 +474,11 @@ | ||
| 450 | 474 | int *argl |
| 451 | 475 | ){ |
| 452 | 476 | if( argc!=2 ){ |
| 453 | 477 | return Th_WrongNumArgs(interp, "puts STRING"); |
| 454 | 478 | } |
| 455 | - sendText((char*)argv[1], argl[1], *(unsigned int*)pConvert); | |
| 479 | + sendText(0,(char*)argv[1], argl[1], *(unsigned int*)pConvert); | |
| 456 | 480 | return TH_OK; |
| 457 | 481 | } |
| 458 | 482 | |
| 459 | 483 | /* |
| 460 | 484 | ** TH1 command: redirect URL ?withMethod? |
| @@ -996,11 +1020,11 @@ | ||
| 996 | 1020 | blob_init(&name, (char*)argv[1], argl[1]); |
| 997 | 1021 | zValue = Th_Fetch(blob_str(&name), &nValue); |
| 998 | 1022 | zH = htmlize(blob_buffer(&name), blob_size(&name)); |
| 999 | 1023 | z = mprintf("<select id=\"%s\" name=\"%s\" size=\"%d\">", zH, zH, height); |
| 1000 | 1024 | free(zH); |
| 1001 | - sendText(z, -1, 0); | |
| 1025 | + sendText(0,z, -1, 0); | |
| 1002 | 1026 | free(z); |
| 1003 | 1027 | blob_reset(&name); |
| 1004 | 1028 | for(i=0; i<nElem; i++){ |
| 1005 | 1029 | zH = htmlize((char*)azElem[i], aszElem[i]); |
| 1006 | 1030 | if( zValue && aszElem[i]==nValue |
| @@ -1009,14 +1033,14 @@ | ||
| 1009 | 1033 | zH, zH); |
| 1010 | 1034 | }else{ |
| 1011 | 1035 | z = mprintf("<option value=\"%s\">%s</option>", zH, zH); |
| 1012 | 1036 | } |
| 1013 | 1037 | free(zH); |
| 1014 | - sendText(z, -1, 0); | |
| 1038 | + sendText(0,z, -1, 0); | |
| 1015 | 1039 | free(z); |
| 1016 | 1040 | } |
| 1017 | - sendText("</select>", -1, 0); | |
| 1041 | + sendText(0,"</select>", -1, 0); | |
| 1018 | 1042 | Th_Free(interp, azElem); |
| 1019 | 1043 | } |
| 1020 | 1044 | return TH_OK; |
| 1021 | 1045 | } |
| 1022 | 1046 | |
| @@ -1060,11 +1084,11 @@ | ||
| 1060 | 1084 | if( Th_ToInt(interp, argv[4], argl[4], ©length) ) return TH_ERROR; |
| 1061 | 1085 | } |
| 1062 | 1086 | zResult = style_copy_button( |
| 1063 | 1087 | /*bOutputCGI==*/0, /*TARGETID==*/(char*)argv[1], |
| 1064 | 1088 | flipped, copylength, "%h", /*TEXT==*/(char*)argv[3]); |
| 1065 | - sendText(zResult, -1, 0); | |
| 1089 | + sendText(0,zResult, -1, 0); | |
| 1066 | 1090 | free(zResult); |
| 1067 | 1091 | } |
| 1068 | 1092 | return TH_OK; |
| 1069 | 1093 | } |
| 1070 | 1094 | |
| @@ -2221,11 +2245,11 @@ | ||
| 2221 | 2245 | if( g.th1Setup ){ |
| 2222 | 2246 | rc = Th_Eval(g.interp, 0, g.th1Setup, -1); |
| 2223 | 2247 | if( rc==TH_ERROR ){ |
| 2224 | 2248 | int nResult = 0; |
| 2225 | 2249 | char *zResult = (char*)Th_GetResult(g.interp, &nResult); |
| 2226 | - sendError(zResult, nResult, 0); | |
| 2250 | + sendError(0,zResult, nResult, 0); | |
| 2227 | 2251 | } |
| 2228 | 2252 | } |
| 2229 | 2253 | if( g.thTrace ){ |
| 2230 | 2254 | Th_Trace("th1-setup {%h} => %h<br />\n", g.th1Setup, |
| 2231 | 2255 | Th_ReturnCodeName(rc, 0)); |
| @@ -2446,11 +2470,11 @@ | ||
| 2446 | 2470 | /* |
| 2447 | 2471 | ** Make sure that the TH1 script error was not caused by a "missing" |
| 2448 | 2472 | ** command hook handler as that is not actually an error condition. |
| 2449 | 2473 | */ |
| 2450 | 2474 | if( memcmp(zResult, NO_COMMAND_HOOK_ERROR, nResult)!=0 ){ |
| 2451 | - sendError(zResult, nResult, 0); | |
| 2475 | + sendError(0,zResult, nResult, 0); | |
| 2452 | 2476 | }else{ |
| 2453 | 2477 | /* |
| 2454 | 2478 | ** There is no command hook handler "installed". This situation |
| 2455 | 2479 | ** is NOT actually an error. |
| 2456 | 2480 | */ |
| @@ -2533,11 +2557,11 @@ | ||
| 2533 | 2557 | /* |
| 2534 | 2558 | ** Make sure that the TH1 script error was not caused by a "missing" |
| 2535 | 2559 | ** webpage hook handler as that is not actually an error condition. |
| 2536 | 2560 | */ |
| 2537 | 2561 | if( memcmp(zResult, NO_WEBPAGE_HOOK_ERROR, nResult)!=0 ){ |
| 2538 | - sendError(zResult, nResult, 1); | |
| 2562 | + sendError(0,zResult, nResult, 1); | |
| 2539 | 2563 | }else{ |
| 2540 | 2564 | /* |
| 2541 | 2565 | ** There is no webpage hook handler "installed". This situation |
| 2542 | 2566 | ** is NOT actually an error. |
| 2543 | 2567 | */ |
| @@ -2610,32 +2634,42 @@ | ||
| 2610 | 2634 | return db_get_boolean("th1-docs", 0); |
| 2611 | 2635 | } |
| 2612 | 2636 | #endif |
| 2613 | 2637 | |
| 2614 | 2638 | |
| 2639 | +#if INTERFACE | |
| 2640 | +/* | |
| 2641 | +** Flags for use with Th_RenderToBlob. These must not overlap with | |
| 2642 | +** TH_INIT_MASK. | |
| 2643 | +*/ | |
| 2644 | +#define TH_R2B_MASK ((u32)0xff0000) | |
| 2645 | +#define TH_R2B_NO_VARS ((u32)0x010000) /* Disables eval of $vars and $<vars> */ | |
| 2646 | +#endif | |
| 2647 | + | |
| 2615 | 2648 | /* |
| 2616 | -** The z[] input contains text mixed with TH1 scripts. | |
| 2617 | -** The TH1 scripts are contained within <th1>...</th1>. | |
| 2618 | -** TH1 variables are $aaa or $<aaa>. The first form of | |
| 2619 | -** variable is literal. The second is run through htmlize | |
| 2620 | -** before being inserted. | |
| 2621 | -** | |
| 2622 | -** This routine processes the template and writes the results | |
| 2623 | -** on either stdout or into CGI. | |
| 2649 | +** If pOut is NULL, this works identically to Th_Render(), else it | |
| 2650 | +** works just like that function but appends any TH1-generated output | |
| 2651 | +** to the given blob. A bitmask of TH_R2B_xxx and/or TH_INIT_xxx flags | |
| 2652 | +** may be passed as the 3rd argument, or 0 for default options. | |
| 2624 | 2653 | */ |
| 2625 | -int Th_Render(const char *z){ | |
| 2654 | +int Th_RenderToBlob(const char *z, Blob * pOut, u32 mFlags){ | |
| 2626 | 2655 | int i = 0; |
| 2627 | 2656 | int n; |
| 2628 | 2657 | int rc = TH_OK; |
| 2629 | 2658 | char *zResult; |
| 2630 | - Th_FossilInit(TH_INIT_DEFAULT); | |
| 2659 | + Blob * const origOut = pThOut; | |
| 2660 | + | |
| 2661 | + assert(0==(TH_R2B_MASK & TH_INIT_MASK) && "init/r2b mask conflict"); | |
| 2662 | + pThOut = pOut; | |
| 2663 | + Th_FossilInit(mFlags & TH_INIT_MASK); | |
| 2631 | 2664 | while( z[i] ){ |
| 2632 | - if( z[i]=='$' && (n = validVarName(&z[i+1]))>0 ){ | |
| 2665 | + if( !(TH_R2B_NO_VARS & mFlags) | |
| 2666 | + && z[i]=='$' && (n = validVarName(&z[i+1]))>0 ){ | |
| 2633 | 2667 | const char *zVar; |
| 2634 | 2668 | int nVar; |
| 2635 | 2669 | int encode = 1; |
| 2636 | - sendText(z, i, 0); | |
| 2670 | + sendText(pOut,z, i, 0); | |
| 2637 | 2671 | if( z[i+1]=='<' ){ |
| 2638 | 2672 | /* Variables of the form $<aaa> are html escaped */ |
| 2639 | 2673 | zVar = &z[i+2]; |
| 2640 | 2674 | nVar = n-2; |
| 2641 | 2675 | }else{ |
| @@ -2646,13 +2680,13 @@ | ||
| 2646 | 2680 | } |
| 2647 | 2681 | rc = Th_GetVar(g.interp, (char*)zVar, nVar); |
| 2648 | 2682 | z += i+1+n; |
| 2649 | 2683 | i = 0; |
| 2650 | 2684 | zResult = (char*)Th_GetResult(g.interp, &n); |
| 2651 | - sendText((char*)zResult, n, encode); | |
| 2685 | + sendText(pOut,(char*)zResult, n, encode); | |
| 2652 | 2686 | }else if( z[i]=='<' && isBeginScriptTag(&z[i]) ){ |
| 2653 | - sendText(z, i, 0); | |
| 2687 | + sendText(pOut,z, i, 0); | |
| 2654 | 2688 | z += i+5; |
| 2655 | 2689 | for(i=0; z[i] && (z[i]!='<' || !isEndScriptTag(&z[i])); i++){} |
| 2656 | 2690 | if( g.thTrace ){ |
| 2657 | 2691 | Th_Trace("render_eval {<pre>%#h</pre>}<br />\n", i, z); |
| 2658 | 2692 | } |
| @@ -2671,16 +2705,33 @@ | ||
| 2671 | 2705 | i++; |
| 2672 | 2706 | } |
| 2673 | 2707 | } |
| 2674 | 2708 | if( rc==TH_ERROR ){ |
| 2675 | 2709 | zResult = (char*)Th_GetResult(g.interp, &n); |
| 2676 | - sendError(zResult, n, 1); | |
| 2710 | + sendError(pOut,zResult, n, 1); | |
| 2677 | 2711 | }else{ |
| 2678 | - sendText(z, i, 0); | |
| 2712 | + sendText(pOut,z, i, 0); | |
| 2679 | 2713 | } |
| 2714 | + pThOut = origOut; | |
| 2680 | 2715 | return rc; |
| 2681 | 2716 | } |
| 2717 | + | |
| 2718 | +/* | |
| 2719 | +** The z[] input contains text mixed with TH1 scripts. | |
| 2720 | +** The TH1 scripts are contained within <th1>...</th1>. | |
| 2721 | +** TH1 variables are $aaa or $<aaa>. The first form of | |
| 2722 | +** variable is literal. The second is run through htmlize | |
| 2723 | +** before being inserted. | |
| 2724 | +** | |
| 2725 | +** This routine processes the template and writes the results on | |
| 2726 | +** either stdout, into CGI, or to an internal blob which was set up | |
| 2727 | +** via a recursive call to this routine. | |
| 2728 | +*/ | |
| 2729 | +int Th_Render(const char *z){ | |
| 2730 | + return Th_RenderToBlob(z, pThOut, 0); | |
| 2731 | +} | |
| 2732 | + | |
| 2682 | 2733 | |
| 2683 | 2734 | /* |
| 2684 | 2735 | ** COMMAND: test-th-render |
| 2685 | 2736 | ** |
| 2686 | 2737 | ** Usage: %fossil test-th-render FILE |
| @@ -2899,17 +2950,17 @@ | ||
| 2899 | 2950 | fossil_fatal("Unknown TH1 hook %s", g.argv[2]); |
| 2900 | 2951 | } |
| 2901 | 2952 | if( g.interp ){ |
| 2902 | 2953 | zResult = (char*)Th_GetResult(g.interp, &nResult); |
| 2903 | 2954 | } |
| 2904 | - sendText("RESULT (", -1, 0); | |
| 2905 | - sendText(Th_ReturnCodeName(rc, 0), -1, 0); | |
| 2906 | - sendText(")", -1, 0); | |
| 2955 | + sendText(0,"RESULT (", -1, 0); | |
| 2956 | + sendText(0,Th_ReturnCodeName(rc, 0), -1, 0); | |
| 2957 | + sendText(0,")", -1, 0); | |
| 2907 | 2958 | if( zResult && nResult>0 ){ |
| 2908 | - sendText(": ", -1, 0); | |
| 2909 | - sendText(zResult, nResult, 0); | |
| 2959 | + sendText(0,": ", -1, 0); | |
| 2960 | + sendText(0,zResult, nResult, 0); | |
| 2910 | 2961 | } |
| 2911 | - sendText("\n", -1, 0); | |
| 2962 | + sendText(0,"\n", -1, 0); | |
| 2912 | 2963 | Th_PrintTraceLog(); |
| 2913 | 2964 | if( forceCgi ) cgi_reply(); |
| 2914 | 2965 | } |
| 2915 | 2966 | #endif |
| 2916 | 2967 |
| --- src/th_main.c | |
| +++ src/th_main.c | |
| @@ -309,41 +309,65 @@ | |
| 309 | } |
| 310 | } |
| 311 | return zRc; |
| 312 | } |
| 313 | |
| 314 | /* |
| 315 | ** Send text to the appropriate output: Either to the console |
| 316 | ** or to the CGI reply buffer. Escape all characters with special |
| 317 | ** meaning to HTML if the encode parameter is true. |
| 318 | */ |
| 319 | static void sendText(const char *z, int n, int encode){ |
| 320 | if( enableOutput && n ){ |
| 321 | if( n<0 ) n = strlen(z); |
| 322 | if( encode ){ |
| 323 | z = htmlize(z, n); |
| 324 | n = strlen(z); |
| 325 | } |
| 326 | if( g.cgiOutput ){ |
| 327 | cgi_append_content(z, n); |
| 328 | }else{ |
| 329 | fwrite(z, 1, n, stdout); |
| 330 | fflush(stdout); |
| 331 | } |
| 332 | if( encode ) free((char*)z); |
| 333 | } |
| 334 | } |
| 335 | |
| 336 | static void sendError(const char *z, int n, int forceCgi){ |
| 337 | int savedEnable = enableOutput; |
| 338 | enableOutput = 1; |
| 339 | if( forceCgi || g.cgiOutput ){ |
| 340 | sendText("<hr /><p class=\"thmainError\">", -1, 0); |
| 341 | } |
| 342 | sendText("ERROR: ", -1, 0); |
| 343 | sendText((char*)z, n, 1); |
| 344 | sendText(forceCgi || g.cgiOutput ? "</p>" : "\n", -1, 0); |
| 345 | enableOutput = savedEnable; |
| 346 | } |
| 347 | |
| 348 | /* |
| 349 | ** Convert name to an rid. This function was copied from name_to_typed_rid() |
| @@ -450,11 +474,11 @@ | |
| 450 | int *argl |
| 451 | ){ |
| 452 | if( argc!=2 ){ |
| 453 | return Th_WrongNumArgs(interp, "puts STRING"); |
| 454 | } |
| 455 | sendText((char*)argv[1], argl[1], *(unsigned int*)pConvert); |
| 456 | return TH_OK; |
| 457 | } |
| 458 | |
| 459 | /* |
| 460 | ** TH1 command: redirect URL ?withMethod? |
| @@ -996,11 +1020,11 @@ | |
| 996 | blob_init(&name, (char*)argv[1], argl[1]); |
| 997 | zValue = Th_Fetch(blob_str(&name), &nValue); |
| 998 | zH = htmlize(blob_buffer(&name), blob_size(&name)); |
| 999 | z = mprintf("<select id=\"%s\" name=\"%s\" size=\"%d\">", zH, zH, height); |
| 1000 | free(zH); |
| 1001 | sendText(z, -1, 0); |
| 1002 | free(z); |
| 1003 | blob_reset(&name); |
| 1004 | for(i=0; i<nElem; i++){ |
| 1005 | zH = htmlize((char*)azElem[i], aszElem[i]); |
| 1006 | if( zValue && aszElem[i]==nValue |
| @@ -1009,14 +1033,14 @@ | |
| 1009 | zH, zH); |
| 1010 | }else{ |
| 1011 | z = mprintf("<option value=\"%s\">%s</option>", zH, zH); |
| 1012 | } |
| 1013 | free(zH); |
| 1014 | sendText(z, -1, 0); |
| 1015 | free(z); |
| 1016 | } |
| 1017 | sendText("</select>", -1, 0); |
| 1018 | Th_Free(interp, azElem); |
| 1019 | } |
| 1020 | return TH_OK; |
| 1021 | } |
| 1022 | |
| @@ -1060,11 +1084,11 @@ | |
| 1060 | if( Th_ToInt(interp, argv[4], argl[4], ©length) ) return TH_ERROR; |
| 1061 | } |
| 1062 | zResult = style_copy_button( |
| 1063 | /*bOutputCGI==*/0, /*TARGETID==*/(char*)argv[1], |
| 1064 | flipped, copylength, "%h", /*TEXT==*/(char*)argv[3]); |
| 1065 | sendText(zResult, -1, 0); |
| 1066 | free(zResult); |
| 1067 | } |
| 1068 | return TH_OK; |
| 1069 | } |
| 1070 | |
| @@ -2221,11 +2245,11 @@ | |
| 2221 | if( g.th1Setup ){ |
| 2222 | rc = Th_Eval(g.interp, 0, g.th1Setup, -1); |
| 2223 | if( rc==TH_ERROR ){ |
| 2224 | int nResult = 0; |
| 2225 | char *zResult = (char*)Th_GetResult(g.interp, &nResult); |
| 2226 | sendError(zResult, nResult, 0); |
| 2227 | } |
| 2228 | } |
| 2229 | if( g.thTrace ){ |
| 2230 | Th_Trace("th1-setup {%h} => %h<br />\n", g.th1Setup, |
| 2231 | Th_ReturnCodeName(rc, 0)); |
| @@ -2446,11 +2470,11 @@ | |
| 2446 | /* |
| 2447 | ** Make sure that the TH1 script error was not caused by a "missing" |
| 2448 | ** command hook handler as that is not actually an error condition. |
| 2449 | */ |
| 2450 | if( memcmp(zResult, NO_COMMAND_HOOK_ERROR, nResult)!=0 ){ |
| 2451 | sendError(zResult, nResult, 0); |
| 2452 | }else{ |
| 2453 | /* |
| 2454 | ** There is no command hook handler "installed". This situation |
| 2455 | ** is NOT actually an error. |
| 2456 | */ |
| @@ -2533,11 +2557,11 @@ | |
| 2533 | /* |
| 2534 | ** Make sure that the TH1 script error was not caused by a "missing" |
| 2535 | ** webpage hook handler as that is not actually an error condition. |
| 2536 | */ |
| 2537 | if( memcmp(zResult, NO_WEBPAGE_HOOK_ERROR, nResult)!=0 ){ |
| 2538 | sendError(zResult, nResult, 1); |
| 2539 | }else{ |
| 2540 | /* |
| 2541 | ** There is no webpage hook handler "installed". This situation |
| 2542 | ** is NOT actually an error. |
| 2543 | */ |
| @@ -2610,32 +2634,42 @@ | |
| 2610 | return db_get_boolean("th1-docs", 0); |
| 2611 | } |
| 2612 | #endif |
| 2613 | |
| 2614 | |
| 2615 | /* |
| 2616 | ** The z[] input contains text mixed with TH1 scripts. |
| 2617 | ** The TH1 scripts are contained within <th1>...</th1>. |
| 2618 | ** TH1 variables are $aaa or $<aaa>. The first form of |
| 2619 | ** variable is literal. The second is run through htmlize |
| 2620 | ** before being inserted. |
| 2621 | ** |
| 2622 | ** This routine processes the template and writes the results |
| 2623 | ** on either stdout or into CGI. |
| 2624 | */ |
| 2625 | int Th_Render(const char *z){ |
| 2626 | int i = 0; |
| 2627 | int n; |
| 2628 | int rc = TH_OK; |
| 2629 | char *zResult; |
| 2630 | Th_FossilInit(TH_INIT_DEFAULT); |
| 2631 | while( z[i] ){ |
| 2632 | if( z[i]=='$' && (n = validVarName(&z[i+1]))>0 ){ |
| 2633 | const char *zVar; |
| 2634 | int nVar; |
| 2635 | int encode = 1; |
| 2636 | sendText(z, i, 0); |
| 2637 | if( z[i+1]=='<' ){ |
| 2638 | /* Variables of the form $<aaa> are html escaped */ |
| 2639 | zVar = &z[i+2]; |
| 2640 | nVar = n-2; |
| 2641 | }else{ |
| @@ -2646,13 +2680,13 @@ | |
| 2646 | } |
| 2647 | rc = Th_GetVar(g.interp, (char*)zVar, nVar); |
| 2648 | z += i+1+n; |
| 2649 | i = 0; |
| 2650 | zResult = (char*)Th_GetResult(g.interp, &n); |
| 2651 | sendText((char*)zResult, n, encode); |
| 2652 | }else if( z[i]=='<' && isBeginScriptTag(&z[i]) ){ |
| 2653 | sendText(z, i, 0); |
| 2654 | z += i+5; |
| 2655 | for(i=0; z[i] && (z[i]!='<' || !isEndScriptTag(&z[i])); i++){} |
| 2656 | if( g.thTrace ){ |
| 2657 | Th_Trace("render_eval {<pre>%#h</pre>}<br />\n", i, z); |
| 2658 | } |
| @@ -2671,16 +2705,33 @@ | |
| 2671 | i++; |
| 2672 | } |
| 2673 | } |
| 2674 | if( rc==TH_ERROR ){ |
| 2675 | zResult = (char*)Th_GetResult(g.interp, &n); |
| 2676 | sendError(zResult, n, 1); |
| 2677 | }else{ |
| 2678 | sendText(z, i, 0); |
| 2679 | } |
| 2680 | return rc; |
| 2681 | } |
| 2682 | |
| 2683 | /* |
| 2684 | ** COMMAND: test-th-render |
| 2685 | ** |
| 2686 | ** Usage: %fossil test-th-render FILE |
| @@ -2899,17 +2950,17 @@ | |
| 2899 | fossil_fatal("Unknown TH1 hook %s", g.argv[2]); |
| 2900 | } |
| 2901 | if( g.interp ){ |
| 2902 | zResult = (char*)Th_GetResult(g.interp, &nResult); |
| 2903 | } |
| 2904 | sendText("RESULT (", -1, 0); |
| 2905 | sendText(Th_ReturnCodeName(rc, 0), -1, 0); |
| 2906 | sendText(")", -1, 0); |
| 2907 | if( zResult && nResult>0 ){ |
| 2908 | sendText(": ", -1, 0); |
| 2909 | sendText(zResult, nResult, 0); |
| 2910 | } |
| 2911 | sendText("\n", -1, 0); |
| 2912 | Th_PrintTraceLog(); |
| 2913 | if( forceCgi ) cgi_reply(); |
| 2914 | } |
| 2915 | #endif |
| 2916 |
| --- src/th_main.c | |
| +++ src/th_main.c | |
| @@ -309,41 +309,65 @@ | |
| 309 | } |
| 310 | } |
| 311 | return zRc; |
| 312 | } |
| 313 | |
| 314 | static Blob * pThOut = 0; |
| 315 | /* |
| 316 | ** Sets the th1-internal output-redirection blob and returns the |
| 317 | ** previous value. That blob is used by certain output-generation |
| 318 | ** routines to emit its output. |
| 319 | */ |
| 320 | Blob * Th_SetOutputBlob(Blob * pOut){ |
| 321 | Blob * tmp = pThOut; |
| 322 | pThOut = pOut; |
| 323 | return tmp; |
| 324 | } |
| 325 | |
| 326 | /* |
| 327 | ** Send text to the appropriate output: If pOut is not NULL, it is |
| 328 | ** appended there, else to the console or to the CGI reply buffer. |
| 329 | ** Escape all characters with special meaning to HTML if the encode |
| 330 | ** parameter is true. |
| 331 | ** |
| 332 | ** If pOut is NULL and the global pThOut is not then that blob |
| 333 | ** is used for output. |
| 334 | */ |
| 335 | static void sendText(Blob * pOut, const char *z, int n, int encode){ |
| 336 | if(0==pOut && pThOut!=0){ |
| 337 | pOut = pThOut; |
| 338 | } |
| 339 | if( enableOutput && n ){ |
| 340 | if( n<0 ) n = strlen(z); |
| 341 | if( encode ){ |
| 342 | z = htmlize(z, n); |
| 343 | n = strlen(z); |
| 344 | } |
| 345 | if(pOut!=0){ |
| 346 | blob_append(pOut, z, n); |
| 347 | }else if( g.cgiOutput ){ |
| 348 | cgi_append_content(z, n); |
| 349 | }else{ |
| 350 | fwrite(z, 1, n, stdout); |
| 351 | fflush(stdout); |
| 352 | } |
| 353 | if( encode ) free((char*)z); |
| 354 | } |
| 355 | } |
| 356 | |
| 357 | /* |
| 358 | ** error-reporting counterpart of sendText(). |
| 359 | */ |
| 360 | static void sendError(Blob * pOut, const char *z, int n, int forceCgi){ |
| 361 | int savedEnable = enableOutput; |
| 362 | enableOutput = 1; |
| 363 | if( forceCgi || g.cgiOutput ){ |
| 364 | sendText(pOut, "<hr /><p class=\"thmainError\">", -1, 0); |
| 365 | } |
| 366 | sendText(pOut,"ERROR: ", -1, 0); |
| 367 | sendText(pOut,(char*)z, n, 1); |
| 368 | sendText(pOut,forceCgi || g.cgiOutput ? "</p>" : "\n", -1, 0); |
| 369 | enableOutput = savedEnable; |
| 370 | } |
| 371 | |
| 372 | /* |
| 373 | ** Convert name to an rid. This function was copied from name_to_typed_rid() |
| @@ -450,11 +474,11 @@ | |
| 474 | int *argl |
| 475 | ){ |
| 476 | if( argc!=2 ){ |
| 477 | return Th_WrongNumArgs(interp, "puts STRING"); |
| 478 | } |
| 479 | sendText(0,(char*)argv[1], argl[1], *(unsigned int*)pConvert); |
| 480 | return TH_OK; |
| 481 | } |
| 482 | |
| 483 | /* |
| 484 | ** TH1 command: redirect URL ?withMethod? |
| @@ -996,11 +1020,11 @@ | |
| 1020 | blob_init(&name, (char*)argv[1], argl[1]); |
| 1021 | zValue = Th_Fetch(blob_str(&name), &nValue); |
| 1022 | zH = htmlize(blob_buffer(&name), blob_size(&name)); |
| 1023 | z = mprintf("<select id=\"%s\" name=\"%s\" size=\"%d\">", zH, zH, height); |
| 1024 | free(zH); |
| 1025 | sendText(0,z, -1, 0); |
| 1026 | free(z); |
| 1027 | blob_reset(&name); |
| 1028 | for(i=0; i<nElem; i++){ |
| 1029 | zH = htmlize((char*)azElem[i], aszElem[i]); |
| 1030 | if( zValue && aszElem[i]==nValue |
| @@ -1009,14 +1033,14 @@ | |
| 1033 | zH, zH); |
| 1034 | }else{ |
| 1035 | z = mprintf("<option value=\"%s\">%s</option>", zH, zH); |
| 1036 | } |
| 1037 | free(zH); |
| 1038 | sendText(0,z, -1, 0); |
| 1039 | free(z); |
| 1040 | } |
| 1041 | sendText(0,"</select>", -1, 0); |
| 1042 | Th_Free(interp, azElem); |
| 1043 | } |
| 1044 | return TH_OK; |
| 1045 | } |
| 1046 | |
| @@ -1060,11 +1084,11 @@ | |
| 1084 | if( Th_ToInt(interp, argv[4], argl[4], ©length) ) return TH_ERROR; |
| 1085 | } |
| 1086 | zResult = style_copy_button( |
| 1087 | /*bOutputCGI==*/0, /*TARGETID==*/(char*)argv[1], |
| 1088 | flipped, copylength, "%h", /*TEXT==*/(char*)argv[3]); |
| 1089 | sendText(0,zResult, -1, 0); |
| 1090 | free(zResult); |
| 1091 | } |
| 1092 | return TH_OK; |
| 1093 | } |
| 1094 | |
| @@ -2221,11 +2245,11 @@ | |
| 2245 | if( g.th1Setup ){ |
| 2246 | rc = Th_Eval(g.interp, 0, g.th1Setup, -1); |
| 2247 | if( rc==TH_ERROR ){ |
| 2248 | int nResult = 0; |
| 2249 | char *zResult = (char*)Th_GetResult(g.interp, &nResult); |
| 2250 | sendError(0,zResult, nResult, 0); |
| 2251 | } |
| 2252 | } |
| 2253 | if( g.thTrace ){ |
| 2254 | Th_Trace("th1-setup {%h} => %h<br />\n", g.th1Setup, |
| 2255 | Th_ReturnCodeName(rc, 0)); |
| @@ -2446,11 +2470,11 @@ | |
| 2470 | /* |
| 2471 | ** Make sure that the TH1 script error was not caused by a "missing" |
| 2472 | ** command hook handler as that is not actually an error condition. |
| 2473 | */ |
| 2474 | if( memcmp(zResult, NO_COMMAND_HOOK_ERROR, nResult)!=0 ){ |
| 2475 | sendError(0,zResult, nResult, 0); |
| 2476 | }else{ |
| 2477 | /* |
| 2478 | ** There is no command hook handler "installed". This situation |
| 2479 | ** is NOT actually an error. |
| 2480 | */ |
| @@ -2533,11 +2557,11 @@ | |
| 2557 | /* |
| 2558 | ** Make sure that the TH1 script error was not caused by a "missing" |
| 2559 | ** webpage hook handler as that is not actually an error condition. |
| 2560 | */ |
| 2561 | if( memcmp(zResult, NO_WEBPAGE_HOOK_ERROR, nResult)!=0 ){ |
| 2562 | sendError(0,zResult, nResult, 1); |
| 2563 | }else{ |
| 2564 | /* |
| 2565 | ** There is no webpage hook handler "installed". This situation |
| 2566 | ** is NOT actually an error. |
| 2567 | */ |
| @@ -2610,32 +2634,42 @@ | |
| 2634 | return db_get_boolean("th1-docs", 0); |
| 2635 | } |
| 2636 | #endif |
| 2637 | |
| 2638 | |
| 2639 | #if INTERFACE |
| 2640 | /* |
| 2641 | ** Flags for use with Th_RenderToBlob. These must not overlap with |
| 2642 | ** TH_INIT_MASK. |
| 2643 | */ |
| 2644 | #define TH_R2B_MASK ((u32)0xff0000) |
| 2645 | #define TH_R2B_NO_VARS ((u32)0x010000) /* Disables eval of $vars and $<vars> */ |
| 2646 | #endif |
| 2647 | |
| 2648 | /* |
| 2649 | ** If pOut is NULL, this works identically to Th_Render(), else it |
| 2650 | ** works just like that function but appends any TH1-generated output |
| 2651 | ** to the given blob. A bitmask of TH_R2B_xxx and/or TH_INIT_xxx flags |
| 2652 | ** may be passed as the 3rd argument, or 0 for default options. |
| 2653 | */ |
| 2654 | int Th_RenderToBlob(const char *z, Blob * pOut, u32 mFlags){ |
| 2655 | int i = 0; |
| 2656 | int n; |
| 2657 | int rc = TH_OK; |
| 2658 | char *zResult; |
| 2659 | Blob * const origOut = pThOut; |
| 2660 | |
| 2661 | assert(0==(TH_R2B_MASK & TH_INIT_MASK) && "init/r2b mask conflict"); |
| 2662 | pThOut = pOut; |
| 2663 | Th_FossilInit(mFlags & TH_INIT_MASK); |
| 2664 | while( z[i] ){ |
| 2665 | if( !(TH_R2B_NO_VARS & mFlags) |
| 2666 | && z[i]=='$' && (n = validVarName(&z[i+1]))>0 ){ |
| 2667 | const char *zVar; |
| 2668 | int nVar; |
| 2669 | int encode = 1; |
| 2670 | sendText(pOut,z, i, 0); |
| 2671 | if( z[i+1]=='<' ){ |
| 2672 | /* Variables of the form $<aaa> are html escaped */ |
| 2673 | zVar = &z[i+2]; |
| 2674 | nVar = n-2; |
| 2675 | }else{ |
| @@ -2646,13 +2680,13 @@ | |
| 2680 | } |
| 2681 | rc = Th_GetVar(g.interp, (char*)zVar, nVar); |
| 2682 | z += i+1+n; |
| 2683 | i = 0; |
| 2684 | zResult = (char*)Th_GetResult(g.interp, &n); |
| 2685 | sendText(pOut,(char*)zResult, n, encode); |
| 2686 | }else if( z[i]=='<' && isBeginScriptTag(&z[i]) ){ |
| 2687 | sendText(pOut,z, i, 0); |
| 2688 | z += i+5; |
| 2689 | for(i=0; z[i] && (z[i]!='<' || !isEndScriptTag(&z[i])); i++){} |
| 2690 | if( g.thTrace ){ |
| 2691 | Th_Trace("render_eval {<pre>%#h</pre>}<br />\n", i, z); |
| 2692 | } |
| @@ -2671,16 +2705,33 @@ | |
| 2705 | i++; |
| 2706 | } |
| 2707 | } |
| 2708 | if( rc==TH_ERROR ){ |
| 2709 | zResult = (char*)Th_GetResult(g.interp, &n); |
| 2710 | sendError(pOut,zResult, n, 1); |
| 2711 | }else{ |
| 2712 | sendText(pOut,z, i, 0); |
| 2713 | } |
| 2714 | pThOut = origOut; |
| 2715 | return rc; |
| 2716 | } |
| 2717 | |
| 2718 | /* |
| 2719 | ** The z[] input contains text mixed with TH1 scripts. |
| 2720 | ** The TH1 scripts are contained within <th1>...</th1>. |
| 2721 | ** TH1 variables are $aaa or $<aaa>. The first form of |
| 2722 | ** variable is literal. The second is run through htmlize |
| 2723 | ** before being inserted. |
| 2724 | ** |
| 2725 | ** This routine processes the template and writes the results on |
| 2726 | ** either stdout, into CGI, or to an internal blob which was set up |
| 2727 | ** via a recursive call to this routine. |
| 2728 | */ |
| 2729 | int Th_Render(const char *z){ |
| 2730 | return Th_RenderToBlob(z, pThOut, 0); |
| 2731 | } |
| 2732 | |
| 2733 | |
| 2734 | /* |
| 2735 | ** COMMAND: test-th-render |
| 2736 | ** |
| 2737 | ** Usage: %fossil test-th-render FILE |
| @@ -2899,17 +2950,17 @@ | |
| 2950 | fossil_fatal("Unknown TH1 hook %s", g.argv[2]); |
| 2951 | } |
| 2952 | if( g.interp ){ |
| 2953 | zResult = (char*)Th_GetResult(g.interp, &nResult); |
| 2954 | } |
| 2955 | sendText(0,"RESULT (", -1, 0); |
| 2956 | sendText(0,Th_ReturnCodeName(rc, 0), -1, 0); |
| 2957 | sendText(0,")", -1, 0); |
| 2958 | if( zResult && nResult>0 ){ |
| 2959 | sendText(0,": ", -1, 0); |
| 2960 | sendText(0,zResult, nResult, 0); |
| 2961 | } |
| 2962 | sendText(0,"\n", -1, 0); |
| 2963 | Th_PrintTraceLog(); |
| 2964 | if( forceCgi ) cgi_reply(); |
| 2965 | } |
| 2966 | #endif |
| 2967 |