Fossil SCM

fossil-scm / src / pikchrshow.c
Blame History Raw 575 lines
1
/*
2
** Copyright (c) 2020 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
**
8
** This program is distributed in the hope that it will be useful,
9
** but without any warranty; without even the implied warranty of
10
** merchantability or fitness for a particular purpose.
11
**
12
** Author contact information:
13
** [email protected]
14
** http://www.hwaci.com/drh/
15
**
16
*******************************************************************************
17
**
18
** This file contains fossil-specific code related to pikchr.
19
*/
20
#include "config.h"
21
#include <assert.h>
22
#include <ctype.h>
23
#include "pikchrshow.h"
24
25
#if INTERFACE
26
/* These are described in pikchr_process()'s docs. */
27
/* The first two must match the values from pikchr.c */
28
#define PIKCHR_PROCESS_PLAINTEXT_ERRORS 0x0001
29
#define PIKCHR_PROCESS_DARK_MODE 0x0002
30
/* end of flags supported directly by pikchr() */
31
#define PIKCHR_PROCESS_PASSTHROUGH 0x0003 /* Pass through these flags */
32
#define PIKCHR_PROCESS_NONCE 0x0010
33
#define PIKCHR_PROCESS_ERR_PRE 0x0020
34
#define PIKCHR_PROCESS_SRC 0x0040
35
#define PIKCHR_PROCESS_DIV 0x0080
36
#define PIKCHR_PROCESS_DIV_INDENT 0x0100
37
#define PIKCHR_PROCESS_DIV_CENTER 0x0200
38
#define PIKCHR_PROCESS_DIV_FLOAT_LEFT 0x0400
39
#define PIKCHR_PROCESS_DIV_FLOAT_RIGHT 0x0800
40
#define PIKCHR_PROCESS_DIV_TOGGLE 0x1000
41
#define PIKCHR_PROCESS_DIV_SOURCE 0x2000
42
#define PIKCHR_PROCESS_DIV_SOURCE_INLINE 0x4000
43
#endif
44
45
/*
46
** Processes a pikchr script. zIn is the NUL-terminated input
47
** script. pikFlags may be a bitmask of any of the PIKCHR_PROCESS_xxx
48
** flags documented below. Output is sent to pOut,
49
**
50
** Returns 0 on success, or non-zero if pikchr processing failed.
51
** In either case, the error message (if any) from pikchr will be
52
** appended to pOut.
53
**
54
** pikFlags flag descriptions:
55
**
56
** - PIKCHR_PROCESS_DIV: if set, the SVG result is wrapped in a DIV
57
** element which specifies a max-width style value based on the SVG's
58
** calculated size. This flag has multiple mutually exclusive forms:
59
**
60
** - PIKCHR_PROCESS_DIV uses default element alignment.
61
** - PIKCHR_PROCESS_DIV_INDENT indents the div.
62
** - PIKCHR_PROCESS_DIV_CENTER centers the div.
63
** - PIKCHR_PROCESS_DIV_FLOAT_LEFT floats the div left.
64
** - PIKCHR_PROCESS_DIV_FLOAT_RIGHT floats the div right.
65
**
66
** If more than one is specified, which one is used is undefined. Those
67
** flags may be OR'd with one or both of the following:
68
**
69
** - PIKCHR_PROCESS_DIV_TOGGLE: adds the 'toggle' CSS class to the
70
** outer DIV so that event-handler code can install different
71
** toggling behaviour than the default. Default is ctrl-click, but
72
** this flag enables single-click toggling for the element.
73
**
74
** - PIKCHR_PROCESS_DIV_SOURCE: adds the 'source' CSS class to the
75
** outer DIV, which is a hint to the client-side renderer (see
76
** fossil.pikchr.js) that the pikchr should initially be rendered
77
** in source code form mode (the default is to hide the source and
78
** show the SVG).
79
**
80
** - PIKCHR_PROCESS_DIV_SOURCE_INLINE: adds the 'source-inline' CSS
81
** class to the outer wrapper. This modifier changes how the
82
** 'source' CSS class gets applied: with this flag, the source view
83
** should be rendered "inline" (same position as the graphic), else
84
** it is to be left-aligned.
85
**
86
** - PIKCHR_PROCESS_NONCE: if set, the resulting SVG/DIV are wrapped
87
** in "safe nonce" comments, which are a fossil-internal mechanism
88
** which prevents the wiki/markdown processors from re-processing this
89
** output. This is necessary when calling this routine in the context
90
** of wiki/embedded doc processing, but not (e.g.) when fetching
91
** an image for /pikchrpage.
92
**
93
** - PIKCHR_PROCESS_SRC: if set, a new PRE.pikchr-src element is
94
** injected adjacent to the SVG element which contains the
95
** HTML-escaped content of the input script. If
96
** PIKCHR_PROCESS_DIV_SOURCE or PIKCHR_PROCESS_DIV_SOURCE_INLINE is
97
** set, this flag is automatically implied.
98
**
99
** - PIKCHR_PROCESS_ERR_PRE: if set and pikchr() fails, the resulting
100
** error report is wrapped in a PRE element, else it is retained
101
** as-is (intended only for console output).
102
*/
103
int pikchr_process(const char *zIn, int pikFlags, Blob * pOut){
104
int isErr = 0;
105
int w = 0, h = 0;
106
char *zOut;
107
const char *zNonce = (PIKCHR_PROCESS_NONCE & pikFlags)
108
? safe_html_nonce(1) : 0;
109
110
if(!(PIKCHR_PROCESS_DIV & pikFlags)
111
/* If any DIV_xxx flags are set, set DIV */
112
&& (PIKCHR_PROCESS_DIV_INDENT
113
| PIKCHR_PROCESS_DIV_CENTER
114
| PIKCHR_PROCESS_DIV_FLOAT_RIGHT
115
| PIKCHR_PROCESS_DIV_FLOAT_LEFT
116
| PIKCHR_PROCESS_DIV_SOURCE
117
| PIKCHR_PROCESS_DIV_SOURCE_INLINE
118
| PIKCHR_PROCESS_DIV_TOGGLE
119
) & pikFlags){
120
pikFlags |= PIKCHR_PROCESS_DIV;
121
}
122
if(zNonce){
123
blob_appendf(pOut, "%s\n", zNonce);
124
}
125
zOut = pikchr(zIn, "pikchr",
126
0x01 | (pikFlags&PIKCHR_PROCESS_PASSTHROUGH),
127
&w, &h);
128
if( w>0 && h>0 ){
129
const char * zClassToggle = "";
130
const char * zClassSource = "";
131
const char * zWrapperClass = "";
132
if(PIKCHR_PROCESS_DIV & pikFlags){
133
if(PIKCHR_PROCESS_DIV_CENTER & pikFlags){
134
zWrapperClass = " center";
135
}else if(PIKCHR_PROCESS_DIV_INDENT & pikFlags){
136
zWrapperClass = " indent";
137
}else if(PIKCHR_PROCESS_DIV_FLOAT_LEFT & pikFlags){
138
zWrapperClass = " float-left";
139
}else if(PIKCHR_PROCESS_DIV_FLOAT_RIGHT & pikFlags){
140
zWrapperClass = " float-right";
141
}
142
if(PIKCHR_PROCESS_DIV_TOGGLE & pikFlags){
143
zClassToggle = " toggle";
144
}
145
if(PIKCHR_PROCESS_DIV_SOURCE_INLINE & pikFlags){
146
if(PIKCHR_PROCESS_DIV_SOURCE & pikFlags){
147
zClassSource = " source source-inline";
148
}else{
149
zClassSource = " source-inline";
150
}
151
pikFlags |= PIKCHR_PROCESS_SRC;
152
}else if(PIKCHR_PROCESS_DIV_SOURCE & pikFlags){
153
zClassSource = " source";
154
pikFlags |= PIKCHR_PROCESS_SRC;
155
}
156
blob_appendf(pOut,"<div class='pikchr-wrapper"
157
"%s%s%s'>"
158
"<div class=\"pikchr-svg\" "
159
"style=\"max-width:%dpx\">\n",
160
zWrapperClass/*safe-for-%s*/,
161
zClassToggle/*safe-for-%s*/,
162
zClassSource/*safe-for-%s*/, w);
163
}
164
blob_append(pOut, zOut, -1);
165
if(PIKCHR_PROCESS_DIV & pikFlags){
166
blob_append(pOut, "</div>\n", 7);
167
}
168
if(PIKCHR_PROCESS_SRC & pikFlags){
169
static int counter = 0;
170
++counter;
171
blob_appendf(pOut, "<div class='pikchr-src'>"
172
"<pre id='pikchr-src-%d'>%h</pre>"
173
"<span class='hidden'>"
174
"<a href='%R/pikchrshow?fromSession' "
175
"class='pikchr-src-pikchrshow' target='_new-%d' "
176
"data-pikchrid='pikchr-src-%d' "
177
"title='Open this pikchr in /pikchrshow'"
178
">&rarr; /pikchrshow</a></span>"
179
"</div>\n",
180
counter, zIn, counter, counter);
181
}
182
if(PIKCHR_PROCESS_DIV & pikFlags){
183
blob_append(pOut, "</div>\n", 7);
184
}
185
}else{
186
isErr = 2;
187
if(PIKCHR_PROCESS_ERR_PRE & pikFlags){
188
blob_append(pOut, "<pre class='error'>\n", 20);
189
}
190
blob_appendf(pOut, "%h", zOut);
191
if(PIKCHR_PROCESS_ERR_PRE & pikFlags){
192
blob_append(pOut, "\n</pre>\n", 8);
193
}
194
}
195
fossil_free(zOut);
196
if(zNonce){
197
blob_appendf(pOut, "%s\n", zNonce);
198
}
199
return isErr;
200
}
201
202
/*
203
** Legacy impl of /pikchrshow. pikchrshow_page() will delegate to
204
** this one if the "legacy" or "ajax" request arguments are set.
205
**
206
** A pikchr code editor and previewer, allowing users to experiment
207
** with pikchr code or prototype it for use in copy/pasting into forum
208
** posts, wiki pages, or embedded docs. This version of pikchrshow
209
** uses JavaScript to send pikchr code to the server for
210
** processing. The newer /pikchrshow applications runs pikchr on the
211
** client machine, without the need for back-and-forth network
212
** traffic.
213
*/
214
void pikchrshowcs_page(void){
215
const char *zContent = 0;
216
int isDark; /* true if the current skin is "dark" */
217
int pikFlags =
218
PIKCHR_PROCESS_DIV
219
| PIKCHR_PROCESS_SRC
220
| PIKCHR_PROCESS_ERR_PRE;
221
222
login_check_credentials();
223
if( !g.perm.RdWiki && !g.perm.Read && !g.perm.RdForum ){
224
cgi_redirectf("%R/login?g=pikchrshowcs");
225
}
226
if(P("wasm")){
227
pikchrshow_page();
228
return;
229
}
230
zContent = PD("content",P("p"));
231
if(P("ajax")!=0){
232
/* Called from the JS-side preview updater.
233
TODO: respond with JSON instead.*/
234
cgi_set_content_type("text/html");
235
if(zContent && *zContent){
236
Blob out = empty_blob;
237
const int isErr =
238
pikchr_process(zContent, pikFlags, &out);
239
if(isErr){
240
cgi_printf_header("x-pikchrshow-is-error: %d\r\n", isErr);
241
}
242
CX("%b", &out);
243
blob_reset(&out);
244
}else{
245
CX("<pre>No content! Nothing to render</pre>");
246
}
247
return;
248
}/*ajax response*/
249
style_emit_noscript_for_js_page();
250
isDark = skin_detail_boolean("white-foreground");
251
if(!zContent){
252
zContent = "arrow right 200% \"Markdown\" \"Source\"\n"
253
"box rad 10px \"Markdown\" \"Formatter\" \"(markdown.c)\" fit\n"
254
"arrow right 200% \"HTML+SVG\" \"Output\"\n"
255
"arrow <-> down from last box.s\n"
256
"box same \"Pikchr\" \"Formatter\" \"(pikchr.c)\" fit\n";
257
}
258
style_header("PikchrShow Client/Server");
259
CX("<style>"); {
260
CX("div.content { padding-top: 0.5em }\n");
261
CX("#sbs-wrapper {"
262
"display: flex; flex-direction: column;"
263
"}\n");
264
CX("#sbs-wrapper > * {"
265
"margin: 0 0.25em 0.5em 0; flex: 1 10 auto;"
266
"align-self: stretch;"
267
"}\n");
268
CX("#sbs-wrapper textarea {"
269
"max-width: initial; flex: 1 1 auto;"
270
"}\n");
271
CX("#pikchrshow-output, #pikchrshow-form"
272
"{display: flex; flex-direction: column; align-items: stretch;}");
273
CX("#pikchrshow-form > * {margin: 0.25em 0}\n");
274
CX("#pikchrshow-output {flex: 5 1 auto; padding: 0}\n");
275
CX("#pikchrshow-output > pre, "
276
"#pikchrshow-output > pre > div, "
277
"#pikchrshow-output > pre > div > pre "
278
"{margin: 0; padding: 0}\n");
279
CX("#pikchrshow-output.error > pre "
280
/* Server-side error report */
281
"{padding: 0.5em}\n");
282
CX("#pikchrshow-controls {" /* where the buttons live */
283
"display: flex; flex-direction: row; "
284
"align-items: center; flex-wrap: wrap;"
285
"}\n");
286
CX("#pikchrshow-controls > * {"
287
"display: inline; margin: 0 0.25em 0.5em 0;"
288
"}\n");
289
CX("#pikchrshow-output-wrapper label {"
290
"cursor: pointer;"
291
"}\n");
292
CX("body.pikchrshow .input-with-label > * {"
293
"margin: 0 0.2em;"
294
"}\n");
295
CX("body.pikchrshow .input-with-label > label {"
296
"cursor: pointer;"
297
"}\n");
298
CX("#pikchrshow-output.dark-mode svg {"
299
/* Flip the colors to approximate a dark theme look */
300
"filter: invert(1) hue-rotate(180deg);"
301
"}\n");
302
CX("#pikchrshow-output-wrapper {"
303
"padding: 0.25em 0.5em; border-radius: 0.25em;"
304
"border-width: 1px;"/*some skins disable fieldset borders*/
305
"}\n");
306
CX("#pikchrshow-output-wrapper > legend > *:not(.copy-button){"
307
"margin-right: 0.5em; vertical-align: middle;"
308
"}\n");
309
CX("body.pikchrshow .v-align-middle{"
310
"vertical-align: middle"
311
"}\n");
312
CX(".dragover {border: 3px dotted rgba(0,255,0,0.6)}\n");
313
} CX("</style>");
314
CX("<div>Input pikchr code and tap Preview (or Shift-Enter) to render "
315
"it. <a href='?wasm'>Switch to WASM mode</a>.</div>");
316
CX("<div id='sbs-wrapper'>"); {
317
CX("<div id='pikchrshow-form'>"); {
318
CX("<textarea id='content' name='content' rows='15'>"
319
"%s</textarea>",zContent/*safe-for-%s*/);
320
CX("<div id='pikchrshow-controls'>"); {
321
CX("<button id='pikchr-submit-preview'>Preview</button>");
322
CX("<div class='input-with-label'>"); {
323
CX("<button id='pikchr-stash'>Stash</button>");
324
CX("<button id='pikchr-unstash'>Unstash</button>");
325
CX("<button id='pikchr-clear-stash'>Clear stash</button>");
326
CX("<span>Stores/restores a single pikchr script to/from "
327
"browser-local storage from/to the editor.</span>"
328
/* gets turned into a help-buttonlet */);
329
} CX("</div>"/*stash controls*/);
330
style_labeled_checkbox("flipcolors-wrapper", "flipcolors",
331
"Dark mode?",
332
"1", isDark, 0);
333
} CX("</div>"/*#pikchrshow-controls*/);
334
}
335
CX("</div>"/*#pikchrshow-form*/);
336
CX("<fieldset id='pikchrshow-output-wrapper'>"); {
337
CX("<legend></legend>"
338
/* Reminder: Firefox does not properly flexbox a LEGEND
339
element, always flowing it in column mode. */);
340
CX("<div id='pikchrshow-output'>");
341
if(*zContent){
342
Blob out = empty_blob;
343
pikchr_process(zContent, pikFlags, &out);
344
CX("%b", &out);
345
blob_reset(&out);
346
} CX("</div>"/*#pikchrshow-output*/);
347
} CX("</fieldset>"/*#pikchrshow-output-wrapper*/);
348
} CX("</div>"/*sbs-wrapper*/);
349
builtin_fossil_js_bundle_or("fetch", "copybutton", "popupwidget",
350
"storage", "pikchr", NULL);
351
builtin_request_js("fossil.page.pikchrshow.js");
352
builtin_fulfill_js_requests();
353
style_finish_page();
354
}
355
356
/*
357
** WEBPAGE: pikchrshow
358
**
359
** A pikchr code editor and previewer, allowing users to experiment
360
** with pikchr code or prototype it for use in copy/pasting into forum
361
** posts, wiki pages, or embedded docs. This version of pikchrshow
362
** uses WebAssembly to run entirely in the client browser, without a
363
** need for back-and-forth client/server traffic to perform the
364
** rendering. The "legacy" version of this application, which sends
365
** all input to the server for rendering, can be accessed by adding
366
** the "legacy" URL argument.
367
**
368
** It optionally accepts a p=pikchr-script-code URL parameter or POST
369
** value to pre-populate the editor with that code.
370
*/
371
void pikchrshow_page(void){
372
const char *zContent = 0;
373
374
if(P("legacy") || P("ajax")){
375
pikchrshowcs_page();
376
return;
377
}
378
login_check_credentials();
379
if( !g.perm.RdWiki && !g.perm.Read && !g.perm.RdForum ){
380
cgi_redirectf("%R/login?g=pikchrshow");
381
}
382
style_emit_noscript_for_js_page();
383
style_header("PikchrShow");
384
zContent = PD("content",P("p"));
385
if(!zContent){
386
zContent = "arrow right 200% \"Markdown\" \"Source\"\n"
387
"box rad 10px \"Markdown\" \"Formatter\" \"(markdown.c)\" fit\n"
388
"arrow right 200% \"HTML+SVG\" \"Output\"\n"
389
"arrow <-> down from last box.s\n"
390
"box same \"Pikchr\" \"Formatter\" \"(pikchr.c)\" fit\n";
391
}
392
/* Wasm load/init progress widget... */
393
CX("<div class='emscripten'>"); {
394
CX("<figure id='module-spinner'>");
395
CX("<div class='spinner'></div>");
396
CX("<div class='center'><strong>Initializing app...</strong></div>");
397
CX("<div class='center'>");
398
CX("On a slow internet connection this may take a moment. If this ");
399
CX("message displays for \"a long time\", initialization may have ");
400
CX("failed and the JavaScript console may contain clues as to why. ");
401
CX("</div>");
402
CX("<div><a href='?legacy'>Switch to legacy mode</a></div>");
403
CX("</figure>");
404
CX("<div class='emscripten' id='module-status'>Downloading...</div>");
405
CX("<progress value='0' max='100' id='module-progress' hidden='1'>"
406
"</progress>");
407
} CX("</div><!-- .emscripten -->");
408
/* Main view... */
409
CX("<div id='view-split' class='app-view initially-hidden'>"); {
410
CX("<fieldset class='options collapsible'>"); {
411
CX("<legend><button class='fieldset-toggle'>Options</button></legend>");
412
CX("<div>");
413
CX("<span class='labeled-input'>");
414
CX("<input type='checkbox' id='opt-cb-sbs' ");
415
CX("data-csstgt='#main-wrapper' ");
416
CX("data-cssclass='side-by-side' ");
417
CX("data-config='sideBySide'>");
418
CX("<label for='opt-cb-sbs'>Side-by-side</label>");
419
CX("</span>");
420
CX("<span class='labeled-input'>");
421
CX("<input type='checkbox' id='opt-cb-swapio' ");
422
CX("data-csstgt='#main-wrapper' ");
423
CX("data-cssclass='swapio' ");
424
CX("data-config='swapInOut'>");
425
CX("<label for='opt-cb-swapio'>Swap in/out</label>");
426
CX("</span>");
427
CX("<span class='labeled-input'>");
428
CX("<input type='checkbox' id='opt-cb-autofit' ");
429
CX("data-config='renderAutofit'>");
430
CX("<label for='opt-cb-autofit' "
431
"title='Attempt to scale SVG to fit viewport. "
432
"Whether it will work depends in part on the size "
433
"and shape of the image and the viewport.'"
434
">Auto-fit SVG</label>");
435
CX("</span>");
436
CX("<span class='labeled-input'>");
437
CX("<input type='checkbox' id='opt-cb-autorender' ");
438
CX("data-csstgt='#main-wrapper' ");
439
CX("data-cssclass='auto-render' ");
440
CX("data-config='renderWhileTyping'>");
441
CX("<label for='opt-cb-autorender'>Render while typing</label>");
442
CX("</span>");
443
CX("<span class='labeled-input'>");
444
CX("<a href='?legacy'>Legacy mode</a>");
445
CX("</span>");
446
CX("</div><!-- options wrapper -->");
447
} CX("</fieldset>");
448
CX("<div id='main-wrapper' class=''>"); {
449
CX("<fieldset class='zone-wrapper input'>"); {
450
CX("<legend><div class='button-bar'>");
451
CX("<button id='btn-render' "
452
"title='Ctrl-Enter/Shift-Enter'>Render</button>");
453
CX("<button id='btn-clear'>Clear Input</button>");
454
CX("</div></legend>");
455
CX("<div><textarea id='input'");
456
CX("placeholder='Pikchr input. Ctrl-enter/shift-enter runs it.'>");
457
CX("/**\n");
458
CX(" Use ctrl-enter or shift-enter to execute\n");
459
CX(" pikchr code. If only a subset is currently\n");
460
CX(" selected, only that part is evaluated.\n*/\n");
461
CX("%s</textarea></div>",zContent/*safe-for-%s*/);
462
} CX("</fieldset><!-- .zone-wrapper.input -->");
463
CX("<fieldset class='zone-wrapper output'>"); {
464
CX("<legend><div class='button-bar'>");
465
CX("<button id='btn-render-mode'>Render Mode</button> ");
466
CX("<span style='white-space:nowrap'>"
467
"<button id='preview-copy-button' "
468
"title='Tap to copy to clipboard.'><span></span></button>"
469
"<label for='preview-copy-button' "
470
"title='Tap to copy to clipboard.'></label>"
471
"</span>");
472
CX("</div></legend>");
473
CX("<div id='pikchr-output-wrapper'>");
474
CX("<div id='pikchr-output'></div>");
475
CX("<textarea class='hidden' id='pikchr-output-text'></textarea>");
476
CX("</div>");
477
} CX("</fieldset> <!-- .zone-wrapper.output -->");
478
} CX("</div><!-- #main-wrapper -->");
479
} CX("</div><!-- #view-split -->");
480
builtin_fossil_js_bundle_or("dom", "storage", "copybutton", NULL);
481
builtin_request_js("fossil.page.pikchrshowasm.js");
482
builtin_fulfill_js_requests();
483
style_finish_page();
484
}
485
486
487
/*
488
** COMMAND: pikchr*
489
**
490
** Usage: %fossil pikchr [options] ?INFILE? ?OUTFILE?
491
**
492
** Accepts a pikchr script as input and outputs the rendered script as
493
** an SVG graphic. The INFILE and OUTFILE options default to stdin
494
** resp. stdout, and the names "-" can be used as aliases for those
495
** streams.
496
**
497
** Options:
498
** -div On success, add a DIV wrapper around the
499
** resulting SVG output which limits its max-width to
500
** its computed maximum ideal size
501
**
502
** -div-indent Like -div but indent the div
503
**
504
** -div-center Like -div but center the div
505
**
506
** -div-left Like -div but float the div left
507
**
508
** -div-right Like -div but float the div right
509
**
510
** -div-toggle Set the 'toggle' CSS class on the div (used by the
511
** JavaScript-side post-processor)
512
**
513
** -div-source Set the 'source' CSS class on the div, which tells
514
** CSS to hide the SVG and reveal the source by default.
515
**
516
** -src Store the input pikchr's source code in the output as
517
** a separate element adjacent to the SVG one. Implied
518
** by -div-source.
519
**
520
** -dark Change pikchr colors to assume a dark-mode theme.
521
**
522
**
523
** The -div-indent/center/left/right flags may not be combined.
524
*/
525
void pikchr_cmd(void){
526
Blob bIn = empty_blob;
527
Blob bOut = empty_blob;
528
const char * zInfile = "-";
529
const char * zOutfile = "-";
530
int isErr = 0;
531
int pikFlags = find_option("src",0,0)!=0
532
? PIKCHR_PROCESS_SRC : 0;
533
534
if(find_option("div",0,0)!=0){
535
pikFlags |= PIKCHR_PROCESS_DIV;
536
}else if(find_option("div-indent",0,0)!=0){
537
pikFlags |= PIKCHR_PROCESS_DIV_INDENT;
538
}else if(find_option("div-center",0,0)!=0){
539
pikFlags |= PIKCHR_PROCESS_DIV_CENTER;
540
}else if(find_option("div-float-left",0,0)!=0){
541
pikFlags |= PIKCHR_PROCESS_DIV_FLOAT_LEFT;
542
}else if(find_option("div-float-right",0,0)!=0){
543
pikFlags |= PIKCHR_PROCESS_DIV_FLOAT_RIGHT;
544
}
545
if(find_option("div-toggle",0,0)!=0){
546
pikFlags |= PIKCHR_PROCESS_DIV_TOGGLE;
547
}
548
if(find_option("div-source",0,0)!=0){
549
pikFlags |= PIKCHR_PROCESS_DIV_SOURCE | PIKCHR_PROCESS_SRC;
550
}
551
if(find_option("dark",0,0)!=0){
552
pikFlags |= PIKCHR_PROCESS_DARK_MODE;
553
}
554
555
verify_all_options();
556
if(g.argc>4){
557
usage("?INFILE? ?OUTFILE?");
558
}
559
if(g.argc>2){
560
zInfile = g.argv[2];
561
}
562
if(g.argc>3){
563
zOutfile = g.argv[3];
564
}
565
blob_read_from_file(&bIn, zInfile, ExtFILE);
566
isErr = pikchr_process(blob_str(&bIn), pikFlags, &bOut);
567
if(isErr){
568
fossil_fatal("pikchr ERROR: %b", &bOut);
569
}else{
570
blob_write_to_file(&bOut, zOutfile);
571
}
572
blob_reset(&bIn);
573
blob_reset(&bOut);
574
}
575

Keyboard Shortcuts

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