Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | 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. |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | pikchr-th |
| Files: | files | file ages | folders |
| SHA3-256: |
8a4304eae23e796fa88fe60269a3a815 |
| User & Date: | stephan 2020-09-13 16:35:10.218 |
Context
|
2020-09-13
| ||
| 18:26 | Found a workaround for the th1 puts escaping, but am still wondering whether it's a viable long-term solution. ... (check-in: 18abb60d42 user: stephan tags: pikchr-th) | |
| 16:35 | 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. ... (check-in: 8a4304eae2 user: stephan tags: pikchr-th) | |
| 11:31 | Update pikchr.c to a version that avoids using the " " entity. ... (check-in: 931f782ff3 user: drh tags: trunk) | |
Changes
Changes to src/pikchrshow.c.
| ︙ | ︙ | |||
163 164 165 166 167 168 169 |
builtin_emit_fossil_js_apis("dom", "fetch", "copybutton",
"popupwidget", 0);
}
builtin_emit_fossil_js_apis("page.pikchrshow", 0);
builtin_fulfill_js_requests();
style_footer();
}
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 |
builtin_emit_fossil_js_apis("dom", "fetch", "copybutton",
"popupwidget", 0);
}
builtin_emit_fossil_js_apis("page.pikchrshow", 0);
builtin_fulfill_js_requests();
style_footer();
}
static void pikchr_th_init(u32 fThInit){
Th_FossilInit(fThInit);
}
/*
** COMMAND: pikchr
**
** Usage: %fossil pikchr [options] ?INFILE? ?OUTFILE?
**
** Options:
**
** -div On success, adds a DIV wrapper around the
** resulting SVG output which limits its max-width.
**
** -th Process the input using TH1 before passing it to pikchr.
**
** -th-novar Disable $var and $<var> TH1 processing. Only applies
** with the -th flag.
**
** -th-trace Trace TH1 execution (for debugging purposes)
**
** TH1 Caveats: the built-in TH1 commands make some assumptions about
** HTML escaping and output which do not apply via this
** command. e.g. some commands will output directly to stdout, rather
** than the output buffer this command requires. Improvements in that
** regard are under consideration/construction.
*/
void pikchr_cmd(void){
Blob bIn = empty_blob;
Blob bOut = empty_blob;
const char * zInfile = "-";
const char * zOutfile = "-";
const int fWithDiv = find_option("div",0,0)!=0;
const int fTh1 = find_option("th",0,0)!=0;
const int fNoVar = find_option("th-novar",0,0)!=0;
int isErr = 0;
u32 fThInit = TH_INIT_DEFAULT;
Th_InitTraceLog()/*processes -th-trace flag*/;
verify_all_options();
if(g.argc>4){
usage("?INFILE? ?OUTFILE?");
}
if(g.argc>2){
zInfile = g.argv[2];
}
if(g.argc>3){
zOutfile = g.argv[3];
}
blob_read_from_file(&bIn, zInfile, ExtFILE);
if(fTh1){
Blob out = empty_blob;
Blob * oldOut;
db_find_and_open_repository(OPEN_ANY_SCHEMA | OPEN_OK_NOT_FOUND, 0)
/* ^^^ needed for certain functions to work */;
oldOut = Th_SetOutputBlob(&out);
pikchr_th_init(fThInit);
isErr = Th_RenderToBlob(blob_str(&bIn), &out,
fNoVar ? TH_R2B_NO_VARS : 0);
blob_reset(&bIn);
bIn = out;
Th_SetOutputBlob(oldOut);
/*fossil_print("th'd:\n%b\n", &bIn);*/
}
if(!isErr){
int w = 0, h = 0;
const char * zContent = blob_str(&bIn);
char *zOut = pikchr(zContent, "pikchr", 0, &w, &h);
if( w>0 && h>0 ){
if(fWithDiv){
blob_appendf(&bOut,"<div style='max-width:%dpx;'>\n", w);
}
blob_append(&bOut, zOut, -1);
if(fWithDiv){
blob_append(&bOut,"</div>\n", 7);
}
}else{
isErr = 1;
blob_append(&bOut, zOut, -1);
}
fossil_free(zOut);
}
if(isErr){
fossil_fatal("ERROR: %b", &bOut);
}else{
blob_write_to_file(&bOut, zOutfile);
}
Th_PrintTraceLog();
blob_reset(&bIn);
blob_reset(&bOut);
}
|
Changes to src/th_main.c.
| ︙ | ︙ | |||
307 308 309 310 311 312 313 314 |
default: {
sqlite3_snprintf(sizeof(zRc), zRc, "TH1 return code %d", rc);
}
}
return zRc;
}
/*
| > > > > > > > > > > > > | > | | > > > | > > > > > | > > > | | | | | | 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 |
default: {
sqlite3_snprintf(sizeof(zRc), zRc, "TH1 return code %d", rc);
}
}
return zRc;
}
static Blob * pThOut = 0;
/*
** Sets the th1-internal output-redirection blob and returns the
** previous value. That blob is used by certain output-generation
** routines to emit its output.
*/
Blob * Th_SetOutputBlob(Blob * pOut){
Blob * tmp = pThOut;
pThOut = pOut;
return tmp;
}
/*
** Send text to the appropriate output: If pOut is not NULL, it is
** appended there, else to the console or to the CGI reply buffer.
** Escape all characters with special meaning to HTML if the encode
** parameter is true.
**
** If pOut is NULL and the global pThOut is not then that blob
** is used for output.
*/
static void sendText(Blob * pOut, const char *z, int n, int encode){
if(0==pOut && pThOut!=0){
pOut = pThOut;
}
if( enableOutput && n ){
if( n<0 ) n = strlen(z);
if( encode ){
z = htmlize(z, n);
n = strlen(z);
}
if(pOut!=0){
blob_append(pOut, z, n);
}else if( g.cgiOutput ){
cgi_append_content(z, n);
}else{
fwrite(z, 1, n, stdout);
fflush(stdout);
}
if( encode ) free((char*)z);
}
}
/*
** error-reporting counterpart of sendText().
*/
static void sendError(Blob * pOut, const char *z, int n, int forceCgi){
int savedEnable = enableOutput;
enableOutput = 1;
if( forceCgi || g.cgiOutput ){
sendText(pOut, "<hr /><p class=\"thmainError\">", -1, 0);
}
sendText(pOut,"ERROR: ", -1, 0);
sendText(pOut,(char*)z, n, 1);
sendText(pOut,forceCgi || g.cgiOutput ? "</p>" : "\n", -1, 0);
enableOutput = savedEnable;
}
/*
** Convert name to an rid. This function was copied from name_to_typed_rid()
** in name.c; however, it has been modified to report TH1 script errors instead
** of "fatal errors".
|
| ︙ | ︙ | |||
448 449 450 451 452 453 454 |
int argc,
const char **argv,
int *argl
){
if( argc!=2 ){
return Th_WrongNumArgs(interp, "puts STRING");
}
| | | 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 |
int argc,
const char **argv,
int *argl
){
if( argc!=2 ){
return Th_WrongNumArgs(interp, "puts STRING");
}
sendText(0,(char*)argv[1], argl[1], *(unsigned int*)pConvert);
return TH_OK;
}
/*
** TH1 command: redirect URL ?withMethod?
**
** Issues an HTTP redirect to the specified URL and then exits the process.
|
| ︙ | ︙ | |||
994 995 996 997 998 999 1000 |
if( Th_ToInt(interp, argv[3], argl[3], &height) ) return TH_ERROR;
Th_SplitList(interp, argv[2], argl[2], &azElem, &aszElem, &nElem);
blob_init(&name, (char*)argv[1], argl[1]);
zValue = Th_Fetch(blob_str(&name), &nValue);
zH = htmlize(blob_buffer(&name), blob_size(&name));
z = mprintf("<select id=\"%s\" name=\"%s\" size=\"%d\">", zH, zH, height);
free(zH);
| | | | | 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 |
if( Th_ToInt(interp, argv[3], argl[3], &height) ) return TH_ERROR;
Th_SplitList(interp, argv[2], argl[2], &azElem, &aszElem, &nElem);
blob_init(&name, (char*)argv[1], argl[1]);
zValue = Th_Fetch(blob_str(&name), &nValue);
zH = htmlize(blob_buffer(&name), blob_size(&name));
z = mprintf("<select id=\"%s\" name=\"%s\" size=\"%d\">", zH, zH, height);
free(zH);
sendText(0,z, -1, 0);
free(z);
blob_reset(&name);
for(i=0; i<nElem; i++){
zH = htmlize((char*)azElem[i], aszElem[i]);
if( zValue && aszElem[i]==nValue
&& memcmp(zValue, azElem[i], nValue)==0 ){
z = mprintf("<option value=\"%s\" selected=\"selected\">%s</option>",
zH, zH);
}else{
z = mprintf("<option value=\"%s\">%s</option>", zH, zH);
}
free(zH);
sendText(0,z, -1, 0);
free(z);
}
sendText(0,"</select>", -1, 0);
Th_Free(interp, azElem);
}
return TH_OK;
}
/*
** TH1 command: copybtn TARGETID FLIPPED TEXT ?COPYLENGTH?
|
| ︙ | ︙ | |||
1058 1059 1060 1061 1062 1063 1064 |
if( Th_ToInt(interp, argv[2], argl[2], &flipped) ) return TH_ERROR;
if( argc==5 ){
if( Th_ToInt(interp, argv[4], argl[4], ©length) ) return TH_ERROR;
}
zResult = style_copy_button(
/*bOutputCGI==*/0, /*TARGETID==*/(char*)argv[1],
flipped, copylength, "%h", /*TEXT==*/(char*)argv[3]);
| | | 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 |
if( Th_ToInt(interp, argv[2], argl[2], &flipped) ) return TH_ERROR;
if( argc==5 ){
if( Th_ToInt(interp, argv[4], argl[4], ©length) ) return TH_ERROR;
}
zResult = style_copy_button(
/*bOutputCGI==*/0, /*TARGETID==*/(char*)argv[1],
flipped, copylength, "%h", /*TEXT==*/(char*)argv[3]);
sendText(0,zResult, -1, 0);
free(zResult);
}
return TH_OK;
}
/*
** TH1 command: linecount STRING MAX MIN
|
| ︙ | ︙ | |||
2219 2220 2221 2222 2223 2224 2225 |
g.th1Setup = db_get("th1-setup", 0); /* Grab TH1 setup script. */
}
if( g.th1Setup ){
rc = Th_Eval(g.interp, 0, g.th1Setup, -1);
if( rc==TH_ERROR ){
int nResult = 0;
char *zResult = (char*)Th_GetResult(g.interp, &nResult);
| | | 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 |
g.th1Setup = db_get("th1-setup", 0); /* Grab TH1 setup script. */
}
if( g.th1Setup ){
rc = Th_Eval(g.interp, 0, g.th1Setup, -1);
if( rc==TH_ERROR ){
int nResult = 0;
char *zResult = (char*)Th_GetResult(g.interp, &nResult);
sendError(0,zResult, nResult, 0);
}
}
if( g.thTrace ){
Th_Trace("th1-setup {%h} => %h<br />\n", g.th1Setup,
Th_ReturnCodeName(rc, 0));
}
}
|
| ︙ | ︙ | |||
2444 2445 2446 2447 2448 2449 2450 |
int nResult = 0;
char *zResult = (char*)Th_GetResult(g.interp, &nResult);
/*
** Make sure that the TH1 script error was not caused by a "missing"
** command hook handler as that is not actually an error condition.
*/
if( memcmp(zResult, NO_COMMAND_HOOK_ERROR, nResult)!=0 ){
| | | 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 |
int nResult = 0;
char *zResult = (char*)Th_GetResult(g.interp, &nResult);
/*
** Make sure that the TH1 script error was not caused by a "missing"
** command hook handler as that is not actually an error condition.
*/
if( memcmp(zResult, NO_COMMAND_HOOK_ERROR, nResult)!=0 ){
sendError(0,zResult, nResult, 0);
}else{
/*
** There is no command hook handler "installed". This situation
** is NOT actually an error.
*/
rc = TH_OK;
}
|
| ︙ | ︙ | |||
2531 2532 2533 2534 2535 2536 2537 |
int nResult = 0;
char *zResult = (char*)Th_GetResult(g.interp, &nResult);
/*
** Make sure that the TH1 script error was not caused by a "missing"
** webpage hook handler as that is not actually an error condition.
*/
if( memcmp(zResult, NO_WEBPAGE_HOOK_ERROR, nResult)!=0 ){
| | | 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 |
int nResult = 0;
char *zResult = (char*)Th_GetResult(g.interp, &nResult);
/*
** Make sure that the TH1 script error was not caused by a "missing"
** webpage hook handler as that is not actually an error condition.
*/
if( memcmp(zResult, NO_WEBPAGE_HOOK_ERROR, nResult)!=0 ){
sendError(0,zResult, nResult, 1);
}else{
/*
** There is no webpage hook handler "installed". This situation
** is NOT actually an error.
*/
rc = TH_OK;
}
|
| ︙ | ︙ | |||
2608 2609 2610 2611 2612 2613 2614 2615 |
return 1;
}
return db_get_boolean("th1-docs", 0);
}
#endif
/*
| > | < < < | | > > | | > > > > > | > > > > | > | | | | | 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 |
return 1;
}
return db_get_boolean("th1-docs", 0);
}
#endif
#if INTERFACE
/*
** Flags for use with Th_RenderToBlob. These must not overlap with
** TH_INIT_MASK.
*/
#define TH_R2B_MASK ((u32)0xff0000)
#define TH_R2B_NO_VARS ((u32)0x010000) /* Disables eval of $vars and $<vars> */
#endif
/*
** If pOut is NULL, this works identically to Th_Render(), else it
** works just like that function but appends any TH1-generated output
** to the given blob. A bitmask of TH_R2B_xxx and/or TH_INIT_xxx flags
** may be passed as the 3rd argument, or 0 for default options.
*/
int Th_RenderToBlob(const char *z, Blob * pOut, u32 mFlags){
int i = 0;
int n;
int rc = TH_OK;
char *zResult;
Blob * const origOut = pThOut;
assert(0==(TH_R2B_MASK & TH_INIT_MASK) && "init/r2b mask conflict");
pThOut = pOut;
Th_FossilInit(mFlags & TH_INIT_MASK);
while( z[i] ){
if( !(TH_R2B_NO_VARS & mFlags)
&& z[i]=='$' && (n = validVarName(&z[i+1]))>0 ){
const char *zVar;
int nVar;
int encode = 1;
sendText(pOut,z, i, 0);
if( z[i+1]=='<' ){
/* Variables of the form $<aaa> are html escaped */
zVar = &z[i+2];
nVar = n-2;
}else{
/* Variables of the form $aaa are output raw */
zVar = &z[i+1];
nVar = n;
encode = 0;
}
rc = Th_GetVar(g.interp, (char*)zVar, nVar);
z += i+1+n;
i = 0;
zResult = (char*)Th_GetResult(g.interp, &n);
sendText(pOut,(char*)zResult, n, encode);
}else if( z[i]=='<' && isBeginScriptTag(&z[i]) ){
sendText(pOut,z, i, 0);
z += i+5;
for(i=0; z[i] && (z[i]!='<' || !isEndScriptTag(&z[i])); i++){}
if( g.thTrace ){
Th_Trace("render_eval {<pre>%#h</pre>}<br />\n", i, z);
}
rc = Th_Eval(g.interp, 0, (const char*)z, i);
if( g.thTrace ){
|
| ︙ | ︙ | |||
2669 2670 2671 2672 2673 2674 2675 |
i = 0;
}else{
i++;
}
}
if( rc==TH_ERROR ){
zResult = (char*)Th_GetResult(g.interp, &n);
| | | > > > > > > > > > > > > > > > > > | 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 |
i = 0;
}else{
i++;
}
}
if( rc==TH_ERROR ){
zResult = (char*)Th_GetResult(g.interp, &n);
sendError(pOut,zResult, n, 1);
}else{
sendText(pOut,z, i, 0);
}
pThOut = origOut;
return rc;
}
/*
** The z[] input contains text mixed with TH1 scripts.
** The TH1 scripts are contained within <th1>...</th1>.
** TH1 variables are $aaa or $<aaa>. The first form of
** variable is literal. The second is run through htmlize
** before being inserted.
**
** This routine processes the template and writes the results on
** either stdout, into CGI, or to an internal blob which was set up
** via a recursive call to this routine.
*/
int Th_Render(const char *z){
return Th_RenderToBlob(z, pThOut, 0);
}
/*
** COMMAND: test-th-render
**
** Usage: %fossil test-th-render FILE
**
** Read the content of the file named "FILE" as if it were a header or
|
| ︙ | ︙ | |||
2897 2898 2899 2900 2901 2902 2903 |
rc = Th_WebpageNotify(g.argv[3], (unsigned int)atoi(g.argv[4]));
}else{
fossil_fatal("Unknown TH1 hook %s", g.argv[2]);
}
if( g.interp ){
zResult = (char*)Th_GetResult(g.interp, &nResult);
}
| | | | | | | | 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 |
rc = Th_WebpageNotify(g.argv[3], (unsigned int)atoi(g.argv[4]));
}else{
fossil_fatal("Unknown TH1 hook %s", g.argv[2]);
}
if( g.interp ){
zResult = (char*)Th_GetResult(g.interp, &nResult);
}
sendText(0,"RESULT (", -1, 0);
sendText(0,Th_ReturnCodeName(rc, 0), -1, 0);
sendText(0,")", -1, 0);
if( zResult && nResult>0 ){
sendText(0,": ", -1, 0);
sendText(0,zResult, nResult, 0);
}
sendText(0,"\n", -1, 0);
Th_PrintTraceLog();
if( forceCgi ) cgi_reply();
}
#endif
|