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.

stephan 2020-09-13 16:35 UTC trunk
Commit 8a4304eae23e796fa88fe60269a3a8157dafbbc66ce62f380826cfcd829c7748
2 files changed +92 +91 -40
--- src/pikchrshow.c
+++ src/pikchrshow.c
@@ -165,5 +165,97 @@
165165
}
166166
builtin_emit_fossil_js_apis("page.pikchrshow", 0);
167167
builtin_fulfill_js_requests();
168168
style_footer();
169169
}
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
+}
170262
--- 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 @@
309309
}
310310
}
311311
return zRc;
312312
}
313313
314
+static Blob * pThOut = 0;
314315
/*
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.
318319
*/
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
+ }
320339
if( enableOutput && n ){
321340
if( n<0 ) n = strlen(z);
322341
if( encode ){
323342
z = htmlize(z, n);
324343
n = strlen(z);
325344
}
326
- if( g.cgiOutput ){
345
+ if(pOut!=0){
346
+ blob_append(pOut, z, n);
347
+ }else if( g.cgiOutput ){
327348
cgi_append_content(z, n);
328349
}else{
329350
fwrite(z, 1, n, stdout);
330351
fflush(stdout);
331352
}
332353
if( encode ) free((char*)z);
333354
}
334355
}
335356
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){
337361
int savedEnable = enableOutput;
338362
enableOutput = 1;
339363
if( forceCgi || g.cgiOutput ){
340
- sendText("<hr /><p class=\"thmainError\">", -1, 0);
364
+ sendText(pOut, "<hr /><p class=\"thmainError\">", -1, 0);
341365
}
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);
345369
enableOutput = savedEnable;
346370
}
347371
348372
/*
349373
** Convert name to an rid. This function was copied from name_to_typed_rid()
@@ -450,11 +474,11 @@
450474
int *argl
451475
){
452476
if( argc!=2 ){
453477
return Th_WrongNumArgs(interp, "puts STRING");
454478
}
455
- sendText((char*)argv[1], argl[1], *(unsigned int*)pConvert);
479
+ sendText(0,(char*)argv[1], argl[1], *(unsigned int*)pConvert);
456480
return TH_OK;
457481
}
458482
459483
/*
460484
** TH1 command: redirect URL ?withMethod?
@@ -996,11 +1020,11 @@
9961020
blob_init(&name, (char*)argv[1], argl[1]);
9971021
zValue = Th_Fetch(blob_str(&name), &nValue);
9981022
zH = htmlize(blob_buffer(&name), blob_size(&name));
9991023
z = mprintf("<select id=\"%s\" name=\"%s\" size=\"%d\">", zH, zH, height);
10001024
free(zH);
1001
- sendText(z, -1, 0);
1025
+ sendText(0,z, -1, 0);
10021026
free(z);
10031027
blob_reset(&name);
10041028
for(i=0; i<nElem; i++){
10051029
zH = htmlize((char*)azElem[i], aszElem[i]);
10061030
if( zValue && aszElem[i]==nValue
@@ -1009,14 +1033,14 @@
10091033
zH, zH);
10101034
}else{
10111035
z = mprintf("<option value=\"%s\">%s</option>", zH, zH);
10121036
}
10131037
free(zH);
1014
- sendText(z, -1, 0);
1038
+ sendText(0,z, -1, 0);
10151039
free(z);
10161040
}
1017
- sendText("</select>", -1, 0);
1041
+ sendText(0,"</select>", -1, 0);
10181042
Th_Free(interp, azElem);
10191043
}
10201044
return TH_OK;
10211045
}
10221046
@@ -1060,11 +1084,11 @@
10601084
if( Th_ToInt(interp, argv[4], argl[4], &copylength) ) return TH_ERROR;
10611085
}
10621086
zResult = style_copy_button(
10631087
/*bOutputCGI==*/0, /*TARGETID==*/(char*)argv[1],
10641088
flipped, copylength, "%h", /*TEXT==*/(char*)argv[3]);
1065
- sendText(zResult, -1, 0);
1089
+ sendText(0,zResult, -1, 0);
10661090
free(zResult);
10671091
}
10681092
return TH_OK;
10691093
}
10701094
@@ -2221,11 +2245,11 @@
22212245
if( g.th1Setup ){
22222246
rc = Th_Eval(g.interp, 0, g.th1Setup, -1);
22232247
if( rc==TH_ERROR ){
22242248
int nResult = 0;
22252249
char *zResult = (char*)Th_GetResult(g.interp, &nResult);
2226
- sendError(zResult, nResult, 0);
2250
+ sendError(0,zResult, nResult, 0);
22272251
}
22282252
}
22292253
if( g.thTrace ){
22302254
Th_Trace("th1-setup {%h} => %h<br />\n", g.th1Setup,
22312255
Th_ReturnCodeName(rc, 0));
@@ -2446,11 +2470,11 @@
24462470
/*
24472471
** Make sure that the TH1 script error was not caused by a "missing"
24482472
** command hook handler as that is not actually an error condition.
24492473
*/
24502474
if( memcmp(zResult, NO_COMMAND_HOOK_ERROR, nResult)!=0 ){
2451
- sendError(zResult, nResult, 0);
2475
+ sendError(0,zResult, nResult, 0);
24522476
}else{
24532477
/*
24542478
** There is no command hook handler "installed". This situation
24552479
** is NOT actually an error.
24562480
*/
@@ -2533,11 +2557,11 @@
25332557
/*
25342558
** Make sure that the TH1 script error was not caused by a "missing"
25352559
** webpage hook handler as that is not actually an error condition.
25362560
*/
25372561
if( memcmp(zResult, NO_WEBPAGE_HOOK_ERROR, nResult)!=0 ){
2538
- sendError(zResult, nResult, 1);
2562
+ sendError(0,zResult, nResult, 1);
25392563
}else{
25402564
/*
25412565
** There is no webpage hook handler "installed". This situation
25422566
** is NOT actually an error.
25432567
*/
@@ -2610,32 +2634,42 @@
26102634
return db_get_boolean("th1-docs", 0);
26112635
}
26122636
#endif
26132637
26142638
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
+
26152648
/*
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.
26242653
*/
2625
-int Th_Render(const char *z){
2654
+int Th_RenderToBlob(const char *z, Blob * pOut, u32 mFlags){
26262655
int i = 0;
26272656
int n;
26282657
int rc = TH_OK;
26292658
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);
26312664
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 ){
26332667
const char *zVar;
26342668
int nVar;
26352669
int encode = 1;
2636
- sendText(z, i, 0);
2670
+ sendText(pOut,z, i, 0);
26372671
if( z[i+1]=='<' ){
26382672
/* Variables of the form $<aaa> are html escaped */
26392673
zVar = &z[i+2];
26402674
nVar = n-2;
26412675
}else{
@@ -2646,13 +2680,13 @@
26462680
}
26472681
rc = Th_GetVar(g.interp, (char*)zVar, nVar);
26482682
z += i+1+n;
26492683
i = 0;
26502684
zResult = (char*)Th_GetResult(g.interp, &n);
2651
- sendText((char*)zResult, n, encode);
2685
+ sendText(pOut,(char*)zResult, n, encode);
26522686
}else if( z[i]=='<' && isBeginScriptTag(&z[i]) ){
2653
- sendText(z, i, 0);
2687
+ sendText(pOut,z, i, 0);
26542688
z += i+5;
26552689
for(i=0; z[i] && (z[i]!='<' || !isEndScriptTag(&z[i])); i++){}
26562690
if( g.thTrace ){
26572691
Th_Trace("render_eval {<pre>%#h</pre>}<br />\n", i, z);
26582692
}
@@ -2671,16 +2705,33 @@
26712705
i++;
26722706
}
26732707
}
26742708
if( rc==TH_ERROR ){
26752709
zResult = (char*)Th_GetResult(g.interp, &n);
2676
- sendError(zResult, n, 1);
2710
+ sendError(pOut,zResult, n, 1);
26772711
}else{
2678
- sendText(z, i, 0);
2712
+ sendText(pOut,z, i, 0);
26792713
}
2714
+ pThOut = origOut;
26802715
return rc;
26812716
}
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
+
26822733
26832734
/*
26842735
** COMMAND: test-th-render
26852736
**
26862737
** Usage: %fossil test-th-render FILE
@@ -2899,17 +2950,17 @@
28992950
fossil_fatal("Unknown TH1 hook %s", g.argv[2]);
29002951
}
29012952
if( g.interp ){
29022953
zResult = (char*)Th_GetResult(g.interp, &nResult);
29032954
}
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);
29072958
if( zResult && nResult>0 ){
2908
- sendText(": ", -1, 0);
2909
- sendText(zResult, nResult, 0);
2959
+ sendText(0,": ", -1, 0);
2960
+ sendText(0,zResult, nResult, 0);
29102961
}
2911
- sendText("\n", -1, 0);
2962
+ sendText(0,"\n", -1, 0);
29122963
Th_PrintTraceLog();
29132964
if( forceCgi ) cgi_reply();
29142965
}
29152966
#endif
29162967
--- 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], &copylength) ) 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], &copylength) ) 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

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button