1 /*
2 ** Copyright (c) 2007 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 ** drh@hwaci.com
14 ** http://www.hwaci.com/drh/
15 **
16 *******************************************************************************
17 **
18 ** This file contains code used to format and print comments or other
19 ** text on a TTY.
20 */
21 #include "config.h"
22 #include "comformat.h"
23 #include <assert.h>
24
25 #if INTERFACE
26 #define COMMENT_PRINT_NONE ((u32)0x00000000) /* No flags = non-legacy. */
27 #define COMMENT_PRINT_LEGACY ((u32)0x00000001) /* Use legacy algorithm. */
28 #define COMMENT_PRINT_TRIM_CRLF ((u32)0x00000002) /* Trim leading CR/LF. */
29 #define COMMENT_PRINT_TRIM_SPACE ((u32)0x00000004) /* Trim leading/trailing. */
30 #define COMMENT_PRINT_WORD_BREAK ((u32)0x00000008) /* Break lines on words. */
31 #define COMMENT_PRINT_ORIG_BREAK ((u32)0x00000010) /* Break before original. */
32 #define COMMENT_PRINT_DEFAULT (COMMENT_PRINT_LEGACY) /* Defaults. */
33 #define COMMENT_PRINT_UNSET (-1) /* Not initialized. */
34 #endif
35
36 /*
37 ** This is the previous value used by most external callers when they
38 ** needed to specify a default maximum line length to be used with the
39 ** comment_print() function.
40 */
41 #ifndef COMMENT_LEGACY_LINE_LENGTH
42 # define COMMENT_LEGACY_LINE_LENGTH (78)
43 #endif
44
45 /*
46 ** This is the number of spaces to print when a tab character is seen.
47 */
48 #ifndef COMMENT_TAB_WIDTH
49 # define COMMENT_TAB_WIDTH (8)
50 #endif
51
52 /*
53 ** This function sets the maximum number of characters to print per line
54 ** based on the detected terminal line width, if available; otherwise, it
55 ** uses the legacy default terminal line width minus the amount to indent.
56 **
57 ** Zero is returned to indicate any failure. One is returned to indicate
58 ** the successful detection of the terminal line width. Negative one is
59 ** returned to indicate the terminal line width is using the hard-coded
60 ** legacy default value.
61 */
comment_set_maxchars(int indent,int * pMaxChars)62 static int comment_set_maxchars(
63 int indent,
64 int *pMaxChars
65 ){
66 struct TerminalSize ts;
67 if ( !terminal_get_size(&ts) ){
68 return 0;
69 }
70
71 if( ts.nColumns ){
72 *pMaxChars = ts.nColumns - indent;
73 return 1;
74 }else{
75 /*
76 ** Fallback to using more-or-less the "legacy semantics" of hard-coding
77 ** the maximum line length to a value reasonable for the vast majority
78 ** of supported systems.
79 */
80 *pMaxChars = COMMENT_LEGACY_LINE_LENGTH - indent;
81 return -1;
82 }
83 }
84
85 /*
86 ** This function checks the current line being printed against the original
87 ** comment text. Upon matching, it updates the provided character and line
88 ** counts, if applicable. The caller needs to emit a new line, if desired.
89 */
comment_check_orig(const char * zOrigText,const char * zLine,int * pCharCnt,int * pLineCnt)90 static int comment_check_orig(
91 const char *zOrigText, /* [in] Original comment text ONLY, may be NULL. */
92 const char *zLine, /* [in] The comment line to print. */
93 int *pCharCnt, /* [in/out] Pointer to the line character count. */
94 int *pLineCnt /* [in/out] Pointer to the total line count. */
95 ){
96 if( zOrigText && fossil_strcmp(zLine, zOrigText)==0 ){
97 if( pCharCnt ) *pCharCnt = 0;
98 if( pLineCnt ) (*pLineCnt)++;
99 return 1;
100 }
101 return 0;
102 }
103
104 /*
105 ** This function scans the specified comment line starting just after the
106 ** initial index and returns the index of the next spacing character -OR-
107 ** zero if such a character cannot be found. For the purposes of this
108 ** algorithm, the NUL character is treated the same as a spacing character.
109 */
comment_next_space(const char * zLine,int index,int * distUTF8)110 static int comment_next_space(
111 const char *zLine, /* [in] The comment line being printed. */
112 int index, /* [in] The current character index being handled. */
113 int *distUTF8 /* [out] Distance to next space in UTF-8 sequences. */
114 ){
115 int nextIndex = index + 1;
116 int fNonASCII=0;
117 for(;;){
118 char c = zLine[nextIndex];
119 if( (c&0x80)==0x80 ) fNonASCII=1;
120 if( c==0 || fossil_isspace(c) ){
121 if( distUTF8 ){
122 if( fNonASCII!=0 ){
123 *distUTF8 = strlen_utf8(&zLine[index], nextIndex-index);
124 }else{
125 *distUTF8 = nextIndex-index;
126 }
127 }
128 return nextIndex;
129 }
130 nextIndex++;
131 }
132 return 0; /* NOT REACHED */
133 }
134
135 /*
136 ** Count the number of UTF-8 sequences in a string. Incomplete, ill-formed and
137 ** overlong sequences are counted as one sequence. The invalid lead bytes 0xC0
138 ** to 0xC1 and 0xF5 to 0xF7 are allowed to initiate (ill-formed) 2- and 4-byte
139 ** sequences, respectively, the other invalid lead bytes 0xF8 to 0xFF are
140 ** treated as invalid 1-byte sequences (as lone trail bytes).
141 ** Combining characters and East Asian Wide and Fullwidth characters are counted
142 ** as one, so this function does not calculate the effective "display width".
143 */
strlen_utf8(const char * zString,int lengthBytes)144 int strlen_utf8(const char *zString, int lengthBytes){
145 int i; /* Counted bytes. */
146 int lengthUTF8; /* Counted UTF-8 sequences. */
147 #if 0
148 assert( lengthBytes>=0 );
149 #endif
150 for(i=0, lengthUTF8=0; i<lengthBytes; i++, lengthUTF8++){
151 char c = zString[i];
152 int cchUTF8=1; /* Code units consumed. */
153 int maxUTF8=1; /* Expected sequence length. */
154 if( (c&0xe0)==0xc0 )maxUTF8=2; /* UTF-8 lead byte 110vvvvv */
155 else if( (c&0xf0)==0xe0 )maxUTF8=3; /* UTF-8 lead byte 1110vvvv */
156 else if( (c&0xf8)==0xf0 )maxUTF8=4; /* UTF-8 lead byte 11110vvv */
157 while( cchUTF8<maxUTF8 &&
158 i<lengthBytes-1 &&
159 (zString[i+1]&0xc0)==0x80 ){ /* UTF-8 trail byte 10vvvvvv */
160 cchUTF8++;
161 i++;
162 }
163 }
164 return lengthUTF8;
165 }
166
167 /*
168 ** This function is called when printing a logical comment line to calculate
169 ** the necessary indenting. The caller needs to emit the indenting spaces.
170 */
comment_calc_indent(const char * zLine,int indent,int trimCrLf,int trimSpace,int * piIndex)171 static void comment_calc_indent(
172 const char *zLine, /* [in] The comment line being printed. */
173 int indent, /* [in] Number of spaces to indent, zero for none. */
174 int trimCrLf, /* [in] Non-zero to trim leading/trailing CR/LF. */
175 int trimSpace, /* [in] Non-zero to trim leading/trailing spaces. */
176 int *piIndex /* [in/out] Pointer to first non-space character. */
177 ){
178 if( zLine && piIndex ){
179 int index = *piIndex;
180 if( trimCrLf ){
181 while( zLine[index]=='\r' || zLine[index]=='\n' ){ index++; }
182 }
183 if( trimSpace ){
184 while( fossil_isspace(zLine[index]) ){ index++; }
185 }
186 *piIndex = index;
187 }
188 }
189
190 /*
191 ** This function prints one logical line of a comment, stopping when it hits
192 ** a new line -OR- runs out of space on the logical line.
193 */
comment_print_line(const char * zOrigText,const char * zLine,int origIndent,int indent,int lineChars,int trimCrLf,int trimSpace,int wordBreak,int origBreak,int * pLineCnt,const char ** pzLine)194 static void comment_print_line(
195 const char *zOrigText, /* [in] Original comment text ONLY, may be NULL. */
196 const char *zLine, /* [in] The comment line to print. */
197 int origIndent, /* [in] Number of spaces to indent before the original
198 ** comment. */
199 int indent, /* [in] Number of spaces to indent, before the line
200 ** to print. */
201 int lineChars, /* [in] Maximum number of characters to print. */
202 int trimCrLf, /* [in] Non-zero to trim leading/trailing CR/LF. */
203 int trimSpace, /* [in] Non-zero to trim leading/trailing spaces. */
204 int wordBreak, /* [in] Non-zero to try breaking on word boundaries. */
205 int origBreak, /* [in] Non-zero to break before original comment. */
206 int *pLineCnt, /* [in/out] Pointer to the total line count. */
207 const char **pzLine /* [out] Pointer to the end of the logical line. */
208 ){
209 int index = 0, charCnt = 0, lineCnt = 0, maxChars, i;
210 char zBuf[400]; int iBuf=0; /* Output buffer and counter. */
211 int cchUTF8, maxUTF8; /* Helper variables to count UTF-8 sequences. */
212 if( !zLine ) return;
213 if( lineChars<=0 ) return;
214 #if 0
215 assert( indent<sizeof(zBuf)-5 ); /* See following comments to explain */
216 assert( origIndent<sizeof(zBuf)-5 ); /* these limits. */
217 #endif
218 if( indent>sizeof(zBuf)-6 ){
219 /* Limit initial indent to fit output buffer. */
220 indent = sizeof(zBuf)-6;
221 }
222 comment_calc_indent(zLine, indent, trimCrLf, trimSpace, &index);
223 if( indent>0 ){
224 for(i=0; i<indent; i++){
225 zBuf[iBuf++] = ' ';
226 }
227 }
228 if( origIndent>sizeof(zBuf)-6 ){
229 /* Limit line indent to fit output buffer. */
230 origIndent = sizeof(zBuf)-6;
231 }
232 maxChars = lineChars;
233 for(;;){
234 int useChars = 1;
235 char c = zLine[index];
236 /* Flush the output buffer if there's no space left for at least one more
237 ** (potentially 4-byte) UTF-8 sequence, one level of indentation spaces,
238 ** a new line, and a terminating NULL. */
239 if( iBuf>sizeof(zBuf)-origIndent-6 ){
240 zBuf[iBuf]=0;
241 iBuf=0;
242 fossil_print("%s", zBuf);
243 }
244 if( c==0 ){
245 break;
246 }else{
247 if( origBreak && index>0 ){
248 const char *zCurrent = &zLine[index];
249 if( comment_check_orig(zOrigText, zCurrent, &charCnt, &lineCnt) ){
250 zBuf[iBuf++] = '\n';
251 comment_calc_indent(zLine, origIndent, trimCrLf, trimSpace, &index);
252 for( i=0; i<origIndent; i++ ){
253 zBuf[iBuf++] = ' ';
254 }
255 maxChars = lineChars;
256 }
257 }
258 index++;
259 }
260 if( c=='\n' ){
261 lineCnt++;
262 charCnt = 0;
263 useChars = 0;
264 }else if( c=='\t' ){
265 int distUTF8;
266 int nextIndex = comment_next_space(zLine, index, &distUTF8);
267 if( nextIndex<=0 || distUTF8>maxChars ){
268 break;
269 }
270 charCnt++;
271 useChars = COMMENT_TAB_WIDTH;
272 if( maxChars<useChars ){
273 zBuf[iBuf++] = ' ';
274 break;
275 }
276 }else if( wordBreak && fossil_isspace(c) ){
277 int distUTF8;
278 int nextIndex = comment_next_space(zLine, index, &distUTF8);
279 if( nextIndex<=0 || distUTF8>maxChars ){
280 break;
281 }
282 charCnt++;
283 }else{
284 charCnt++;
285 }
286 assert( c!='\n' || charCnt==0 );
287 zBuf[iBuf++] = c;
288 /* Skip over UTF-8 sequences, see comment on strlen_utf8() for details. */
289 cchUTF8=1; /* Code units consumed. */
290 maxUTF8=1; /* Expected sequence length. */
291 if( (c&0xe0)==0xc0 )maxUTF8=2; /* UTF-8 lead byte 110vvvvv */
292 else if( (c&0xf0)==0xe0 )maxUTF8=3; /* UTF-8 lead byte 1110vvvv */
293 else if( (c&0xf8)==0xf0 )maxUTF8=4; /* UTF-8 lead byte 11110vvv */
294 while( cchUTF8<maxUTF8 &&
295 (zLine[index]&0xc0)==0x80 ){ /* UTF-8 trail byte 10vvvvvv */
296 cchUTF8++;
297 zBuf[iBuf++] = zLine[index++];
298 }
299 maxChars -= useChars;
300 if( maxChars<=0 ) break;
301 if( c=='\n' ) break;
302 }
303 if( charCnt>0 ){
304 zBuf[iBuf++] = '\n';
305 lineCnt++;
306 }
307 /* Flush the remaining output buffer. */
308 if( iBuf>0 ){
309 zBuf[iBuf]=0;
310 iBuf=0;
311 fossil_print("%s", zBuf);
312 }
313 if( pLineCnt ){
314 *pLineCnt += lineCnt;
315 }
316 if( pzLine ){
317 *pzLine = zLine + index;
318 }
319 }
320
321 /*
322 ** This is the legacy comment printing algorithm. It is being retained
323 ** for backward compatibility.
324 **
325 ** Given a comment string, format that string for printing on a TTY.
326 ** Assume that the output cursors is indent spaces from the left margin
327 ** and that a single line can contain no more than 'width' characters.
328 ** Indent all subsequent lines by 'indent'.
329 **
330 ** Returns the number of new lines emitted.
331 */
comment_print_legacy(const char * zText,int indent,int width)332 static int comment_print_legacy(
333 const char *zText, /* The comment text to be printed. */
334 int indent, /* Number of spaces to indent each non-initial line. */
335 int width /* Maximum number of characters per line. */
336 ){
337 int maxChars = width - indent;
338 int si, sk, i, k, kc;
339 int doIndent = 0;
340 char *zBuf;
341 char zBuffer[400];
342 int lineCnt = 0;
343 int cchUTF8, maxUTF8; /* Helper variables to count UTF-8 sequences. */
344
345 if( width<0 ){
346 comment_set_maxchars(indent, &maxChars);
347 }
348 if( zText==0 ) zText = "(NULL)";
349 if( maxChars<=0 ){
350 maxChars = strlen(zText);
351 }
352 /* Ensure the buffer can hold the longest-possible UTF-8 sequences. */
353 if( maxChars >= (sizeof(zBuffer)/4-1) ){
354 zBuf = fossil_malloc(maxChars*4+1);
355 }else{
356 zBuf = zBuffer;
357 }
358 for(;;){
359 while( fossil_isspace(zText[0]) ){ zText++; }
360 if( zText[0]==0 ){
361 if( doIndent==0 ){
362 fossil_print("\n");
363 lineCnt = 1;
364 }
365 if( zBuf!=zBuffer) fossil_free(zBuf);
366 return lineCnt;
367 }
368 for(sk=si=i=k=kc=0; zText[i] && kc<maxChars; i++){
369 char c = zText[i];
370 kc++; /* Count complete UTF-8 sequences. */
371 /* Skip over UTF-8 sequences, see comment on strlen_utf8() for details. */
372 cchUTF8=1; /* Code units consumed. */
373 maxUTF8=1; /* Expected sequence length. */
374 if( (c&0xe0)==0xc0 )maxUTF8=2; /* UTF-8 lead byte 110vvvvv */
375 else if( (c&0xf0)==0xe0 )maxUTF8=3; /* UTF-8 lead byte 1110vvvv */
376 else if( (c&0xf8)==0xf0 )maxUTF8=4; /* UTF-8 lead byte 11110vvv */
377 if( maxUTF8>1 ){
378 zBuf[k++] = c;
379 while( cchUTF8<maxUTF8 &&
380 (zText[i+1]&0xc0)==0x80 ){ /* UTF-8 trail byte 10vvvvvv */
381 cchUTF8++;
382 zBuf[k++] = zText[++i];
383 }
384 }
385 else if( fossil_isspace(c) ){
386 si = i;
387 sk = k;
388 if( k==0 || zBuf[k-1]!=' ' ){
389 zBuf[k++] = ' ';
390 }
391 }else{
392 zBuf[k] = c;
393 if( c=='-' && k>0 && fossil_isalpha(zBuf[k-1]) ){
394 si = i+1;
395 sk = k+1;
396 }
397 k++;
398 }
399 }
400 if( doIndent ){
401 fossil_print("%*s", indent, "");
402 }
403 doIndent = 1;
404 if( sk>0 && zText[i] ){
405 zText += si;
406 zBuf[sk] = 0;
407 }else{
408 zText += i;
409 zBuf[k] = 0;
410 }
411 fossil_print("%s\n", zBuf);
412 lineCnt++;
413 }
414 }
415
416 /*
417 ** This is the comment printing function. The comment printing algorithm
418 ** contained within it attempts to preserve the formatting present within
419 ** the comment string itself while honoring line width limitations. There
420 ** are several flags that modify the default behavior of this function:
421 **
422 ** COMMENT_PRINT_LEGACY: Forces use of the legacy comment printing
423 ** algorithm. For backward compatibility,
424 ** this is the default.
425 **
426 ** COMMENT_PRINT_TRIM_CRLF: Trims leading and trailing carriage-returns
427 ** and line-feeds where they do not materially
428 ** impact pre-existing formatting (i.e. at the
429 ** start of the comment string -AND- right
430 ** before line indentation). This flag does
431 ** not apply to the legacy comment printing
432 ** algorithm. This flag may be combined with
433 ** COMMENT_PRINT_TRIM_SPACE.
434 **
435 ** COMMENT_PRINT_TRIM_SPACE: Trims leading and trailing spaces where they
436 ** do not materially impact the pre-existing
437 ** formatting (i.e. at the start of the comment
438 ** string -AND- right before line indentation).
439 ** This flag does not apply to the legacy
440 ** comment printing algorithm. This flag may
441 ** be combined with COMMENT_PRINT_TRIM_CRLF.
442 **
443 ** COMMENT_PRINT_WORD_BREAK: Attempts to break lines on word boundaries
444 ** while honoring the logical line length.
445 ** If this flag is not specified, honoring the
446 ** logical line length may result in breaking
447 ** lines in the middle of words. This flag
448 ** does not apply to the legacy comment
449 ** printing algorithm.
450 **
451 ** COMMENT_PRINT_ORIG_BREAK: Looks for the original comment text within
452 ** the text being printed. Upon matching, a
453 ** new line will be emitted, thus preserving
454 ** more of the pre-existing formatting.
455 **
456 ** Given a comment string, format that string for printing on a TTY.
457 ** Assume that the output cursors is indent spaces from the left margin
458 ** and that a single line can contain no more than 'width' characters.
459 ** Indent all subsequent lines by 'indent'.
460 **
461 ** Returns the number of new lines emitted.
462 */
comment_print(const char * zText,const char * zOrigText,int indent,int width,int flags)463 int comment_print(
464 const char *zText, /* The comment text to be printed. */
465 const char *zOrigText, /* Original comment text ONLY, may be NULL. */
466 int indent, /* Spaces to indent each non-initial line. */
467 int width, /* Maximum number of characters per line. */
468 int flags /* Zero or more "COMMENT_PRINT_*" flags. */
469 ){
470 int maxChars = width - indent;
471 int legacy = flags & COMMENT_PRINT_LEGACY;
472 int trimCrLf = flags & COMMENT_PRINT_TRIM_CRLF;
473 int trimSpace = flags & COMMENT_PRINT_TRIM_SPACE;
474 int wordBreak = flags & COMMENT_PRINT_WORD_BREAK;
475 int origBreak = flags & COMMENT_PRINT_ORIG_BREAK;
476 int lineCnt = 0;
477 const char *zLine;
478
479 if( legacy ){
480 return comment_print_legacy(zText, indent, width);
481 }
482 if( width<0 ){
483 comment_set_maxchars(indent, &maxChars);
484 }
485 if( zText==0 ) zText = "(NULL)";
486 if( maxChars<=0 ){
487 maxChars = strlen(zText);
488 }
489 if( trimSpace ){
490 while( fossil_isspace(zText[0]) ){ zText++; }
491 }
492 if( zText[0]==0 ){
493 fossil_print("\n");
494 lineCnt++;
495 return lineCnt;
496 }
497 zLine = zText;
498 for(;;){
499 comment_print_line(zOrigText, zLine, indent, zLine>zText ? indent : 0,
500 maxChars, trimCrLf, trimSpace, wordBreak, origBreak,
501 &lineCnt, &zLine);
502 if( !zLine || !zLine[0] ) break;
503 }
504 return lineCnt;
505 }
506
507 /*
508 ** Return the "COMMENT_PRINT_*" flags specified by the following sources,
509 ** evaluated in the following cascading order:
510 **
511 ** 1. The global --comfmtflags (alias --comment-format) command-line option.
512 ** 2. The local (per-repository) "comment-format" setting.
513 ** 3. The global (all-repositories) "comment-format" setting.
514 ** 4. The default value COMMENT_PRINT_DEFAULT.
515 */
get_comment_format()516 int get_comment_format(){
517 int comFmtFlags;
518 /* The global command-line option is present, or the value has been cached. */
519 if( g.comFmtFlags!=COMMENT_PRINT_UNSET ){
520 comFmtFlags = g.comFmtFlags;
521 return comFmtFlags;
522 }
523 /* Load the local (per-repository) or global (all-repositories) value, and use
524 ** g.comFmtFlags as a cache. */
525 comFmtFlags = db_get_int("comment-format", COMMENT_PRINT_UNSET);
526 if( comFmtFlags!=COMMENT_PRINT_UNSET ){
527 g.comFmtFlags = comFmtFlags;
528 return comFmtFlags;
529 }
530 /* Fallback to the default value. */
531 comFmtFlags = COMMENT_PRINT_DEFAULT;
532 return comFmtFlags;
533 }
534
535 /*
536 **
537 ** COMMAND: test-comment-format
538 **
539 ** Usage: %fossil test-comment-format ?OPTIONS? PREFIX TEXT ?ORIGTEXT?
540 **
541 ** Test comment formatting and printing. Use for testing only.
542 **
543 ** Options:
544 ** --file The comment text is really just a file name to
545 ** read it from.
546 ** --decode Decode the text using the same method used when
547 ** handling the value of a C-card from a manifest.
548 ** --legacy Use the legacy comment printing algorithm.
549 ** --trimcrlf Enable trimming of leading/trailing CR/LF.
550 ** --trimspace Enable trimming of leading/trailing spaces.
551 ** --wordbreak Attempt to break lines on word boundaries.
552 ** --origbreak Attempt to break when the original comment text
553 ** is detected.
554 ** --indent Number of spaces to indent (default (-1) is to
555 ** auto-detect). Zero means no indent.
556 ** -W|--width NUM Width of lines (default (-1) is to auto-detect).
557 ** Zero means no limit.
558 */
test_comment_format(void)559 void test_comment_format(void){
560 const char *zWidth;
561 const char *zIndent;
562 const char *zPrefix;
563 char *zText;
564 char *zOrigText;
565 int indent, width;
566 int fromFile = find_option("file", 0, 0)!=0;
567 int decode = find_option("decode", 0, 0)!=0;
568 int flags = COMMENT_PRINT_NONE;
569 if( find_option("legacy", 0, 0) ){
570 flags |= COMMENT_PRINT_LEGACY;
571 }
572 if( find_option("trimcrlf", 0, 0) ){
573 flags |= COMMENT_PRINT_TRIM_CRLF;
574 }
575 if( find_option("trimspace", 0, 0) ){
576 flags |= COMMENT_PRINT_TRIM_SPACE;
577 }
578 if( find_option("wordbreak", 0, 0) ){
579 flags |= COMMENT_PRINT_WORD_BREAK;
580 }
581 if( find_option("origbreak", 0, 0) ){
582 flags |= COMMENT_PRINT_ORIG_BREAK;
583 }
584 zWidth = find_option("width","W",1);
585 if( zWidth ){
586 width = atoi(zWidth);
587 }else{
588 width = -1; /* automatic */
589 }
590 zIndent = find_option("indent",0,1);
591 if( zIndent ){
592 indent = atoi(zIndent);
593 }else{
594 indent = -1; /* automatic */
595 }
596 if( g.argc!=4 && g.argc!=5 ){
597 usage("?OPTIONS? PREFIX TEXT ?ORIGTEXT?");
598 }
599 zPrefix = g.argv[2];
600 zText = g.argv[3];
601 if( g.argc==5 ){
602 zOrigText = g.argv[4];
603 }else{
604 zOrigText = 0;
605 }
606 if( fromFile ){
607 Blob fileData;
608 blob_read_from_file(&fileData, zText, ExtFILE);
609 zText = mprintf("%s", blob_str(&fileData));
610 blob_reset(&fileData);
611 if( zOrigText ){
612 blob_read_from_file(&fileData, zOrigText, ExtFILE);
613 zOrigText = mprintf("%s", blob_str(&fileData));
614 blob_reset(&fileData);
615 }
616 }
617 if( decode ){
618 zText = mprintf(fromFile?"%z":"%s" /*works-like:"%s"*/, zText);
619 defossilize(zText);
620 if( zOrigText ){
621 zOrigText = mprintf(fromFile?"%z":"%s" /*works-like:"%s"*/, zOrigText);
622 defossilize(zOrigText);
623 }
624 }
625 if( indent<0 ){
626 indent = strlen(zPrefix);
627 }
628 if( zPrefix && *zPrefix ){
629 fossil_print("%s", zPrefix);
630 }
631 fossil_print("(%d lines output)\n",
632 comment_print(zText, zOrigText, indent, width, flags));
633 if( zOrigText && zOrigText!=g.argv[4] ) fossil_free(zOrigText);
634 if( zText && zText!=g.argv[3] ) fossil_free(zText);
635 }
636