Fossil SCM

fossil-scm / src / printf.c
Blame History Raw 1310 lines
1
/*
2
** Copyright (c) 2006 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 implementations of routines for formatting output
19
** (ex: mprintf()) and for output to the console.
20
*/
21
#include "config.h"
22
#include "printf.h"
23
#if defined(_WIN32)
24
# include <io.h>
25
# include <fcntl.h>
26
#endif
27
#include <time.h>
28
29
/* Two custom conversions are used to show a prefix of artifact hashes:
30
**
31
** %!S Prefix of a length appropriate for URLs
32
** %S Prefix of a length appropriate for human display
33
**
34
** The following macros help determine those lengths. FOSSIL_HASH_DIGITS
35
** is the default number of digits to display to humans. This value can
36
** be overridden using the hash-digits setting. FOSSIL_HASH_DIGITS_URL
37
** is the minimum number of digits to be used in URLs. The number used
38
** will always be at least 6 more than the number used for human output,
39
** or HNAME_MAX, whichever is least.
40
*/
41
#ifndef FOSSIL_HASH_DIGITS
42
# define FOSSIL_HASH_DIGITS 10 /* For %S (human display) */
43
#endif
44
#ifndef FOSSIL_HASH_DIGITS_URL
45
# define FOSSIL_HASH_DIGITS_URL 16 /* For %!S (embedded in URLs) */
46
#endif
47
48
/*
49
** Return the number of artifact hash digits to display. The number is for
50
** human output if the bForUrl is false and is destined for a URL if
51
** bForUrl is false.
52
*/
53
int hash_digits(int bForUrl){
54
static int nDigitHuman = 0;
55
static int nDigitUrl = 0;
56
if( nDigitHuman==0 ){
57
nDigitHuman = db_get_int("hash-digits", FOSSIL_HASH_DIGITS);
58
if( nDigitHuman < 6 ) nDigitHuman = 6;
59
if( nDigitHuman > HNAME_MAX ) nDigitHuman = HNAME_MAX;
60
nDigitUrl = nDigitHuman + 6;
61
if( nDigitUrl < FOSSIL_HASH_DIGITS_URL ) nDigitUrl = FOSSIL_HASH_DIGITS_URL;
62
if( nDigitUrl > HNAME_MAX ) nDigitUrl = HNAME_MAX;
63
}
64
return bForUrl ? nDigitUrl : nDigitHuman;
65
}
66
67
/*
68
** Return the number of characters in a %S output.
69
*/
70
int length_of_S_display(void){
71
return hash_digits(0);
72
}
73
74
/*
75
** Conversion types fall into various categories as defined by the
76
** following enumeration.
77
*/
78
#define etRADIX 1 /* Integer types. %d, %x, %o, and so forth */
79
#define etFLOAT 2 /* Floating point. %f */
80
#define etEXP 3 /* Exponential notation. %e and %E */
81
#define etGENERIC 4 /* Floating or exponential, depending on exponent. %g */
82
#define etSIZE 5 /* Return number of characters processed so far. %n */
83
#define etSTRING 6 /* Strings. %s */
84
#define etDYNSTRING 7 /* Dynamically allocated strings. %z */
85
#define etPERCENT 8 /* Percent symbol. %% */
86
#define etCHARX 9 /* Characters. %c */
87
#define etERROR 10 /* Used to indicate no such conversion type */
88
/* The rest are extensions, not normally found in printf() */
89
#define etBLOB 11 /* Blob objects. %b */
90
#define etBLOBSQL 12 /* Blob objects quoted for SQL. %B */
91
#define etSQLESCAPE 13 /* Strings with '\'' doubled. %q */
92
#define etSQLESCAPE2 14 /* Strings with '\'' doubled and enclosed in '',
93
NULL pointers replaced by SQL NULL. %Q */
94
#define etSQLESCAPE3 15 /* Double '"' characters within an identifier. %w */
95
#define etPOINTER 16 /* The %p conversion */
96
#define etHTMLIZE 17 /* Make text safe for HTML */
97
#define etHTTPIZE 18 /* Make text safe for HTTP. "/" encoded as %2f */
98
#define etURLIZE 19 /* Make text safe for HTTP. "/" not encoded */
99
#define etFOSSILIZE 20 /* The fossil header encoding format. */
100
#define etPATH 21 /* Path type */
101
#define etWIKISTR 22 /* Timeline comment text rendered from a char*: %W */
102
#define etSTRINGID 23 /* String with length limit for a hash prefix: %S */
103
#define etROOT 24 /* String value of g.zTop: %R */
104
#define etJSONSTR 25 /* String encoded as a JSON string literal: %j
105
Use %!j to include double-quotes around it. */
106
#define etSHELLESC 26 /* Escape a filename for use in a shell command: %$
107
See blob_append_escaped_arg() for details
108
"%$" -> adds "./" prefix if necessary.
109
"%!$" -> omits the "./" prefix. */
110
#define etHEX 27 /* Encode a string as hexadecimal */
111
112
113
/*
114
** An "etByte" is an 8-bit unsigned value.
115
*/
116
typedef unsigned char etByte;
117
118
/*
119
** Each builtin conversion character (ex: the 'd' in "%d") is described
120
** by an instance of the following structure
121
*/
122
typedef struct et_info { /* Information about each format field */
123
char fmttype; /* The format field code letter */
124
etByte base; /* The base for radix conversion */
125
etByte flags; /* One or more of FLAG_ constants below */
126
etByte type; /* Conversion paradigm */
127
etByte charset; /* Offset into aDigits[] of the digits string */
128
etByte prefix; /* Offset into aPrefix[] of the prefix string */
129
} et_info;
130
131
/*
132
** Allowed values for et_info.flags
133
*/
134
#define FLAG_SIGNED 1 /* True if the value to convert is signed */
135
#define FLAG_INTERN 2 /* True if for internal use only */
136
#define FLAG_STRING 4 /* Allow infinity precision */
137
138
139
/*
140
** The following table is searched linearly, so it is good to put the
141
** most frequently used conversion types first.
142
**
143
** NB: When modifying this table is it vital that you also update the fmtchr[]
144
** variable to match!!!
145
*/
146
static const char aDigits[] = "0123456789ABCDEF0123456789abcdef";
147
static const char aPrefix[] = "-x0\000X0";
148
static const char fmtchr[] = "dsgzqQbBWhRtTwFSjcouxXfeEGin%p/$H";
149
static const et_info fmtinfo[] = {
150
{ 'd', 10, 1, etRADIX, 0, 0 },
151
{ 's', 0, 4, etSTRING, 0, 0 },
152
{ 'g', 0, 1, etGENERIC, 30, 0 },
153
{ 'z', 0, 6, etDYNSTRING, 0, 0 },
154
{ 'q', 0, 4, etSQLESCAPE, 0, 0 },
155
{ 'Q', 0, 4, etSQLESCAPE2, 0, 0 },
156
{ 'b', 0, 2, etBLOB, 0, 0 },
157
{ 'B', 0, 2, etBLOBSQL, 0, 0 },
158
{ 'W', 0, 2, etWIKISTR, 0, 0 },
159
{ 'h', 0, 4, etHTMLIZE, 0, 0 },
160
{ 'R', 0, 0, etROOT, 0, 0 },
161
{ 't', 0, 4, etHTTPIZE, 0, 0 }, /* "/" -> "%2F" */
162
{ 'T', 0, 4, etURLIZE, 0, 0 }, /* "/" unchanged */
163
{ 'w', 0, 4, etSQLESCAPE3, 0, 0 },
164
{ 'F', 0, 4, etFOSSILIZE, 0, 0 },
165
{ 'S', 0, 4, etSTRINGID, 0, 0 },
166
{ 'j', 0, 0, etJSONSTR, 0, 0 },
167
{ 'c', 0, 0, etCHARX, 0, 0 },
168
{ 'o', 8, 0, etRADIX, 0, 2 },
169
{ 'u', 10, 0, etRADIX, 0, 0 },
170
{ 'x', 16, 0, etRADIX, 16, 1 },
171
{ 'X', 16, 0, etRADIX, 0, 4 },
172
{ 'f', 0, 1, etFLOAT, 0, 0 },
173
{ 'e', 0, 1, etEXP, 30, 0 },
174
{ 'E', 0, 1, etEXP, 14, 0 },
175
{ 'G', 0, 1, etGENERIC, 14, 0 },
176
{ 'i', 10, 1, etRADIX, 0, 0 },
177
{ 'n', 0, 0, etSIZE, 0, 0 },
178
{ '%', 0, 0, etPERCENT, 0, 0 },
179
{ 'p', 16, 0, etPOINTER, 0, 1 },
180
{ '/', 0, 0, etPATH, 0, 0 },
181
{ '$', 0, 0, etSHELLESC, 0, 0 },
182
{ 'H', 0, 0, etHEX, 0, 0 },
183
{ etERROR, 0,0,0,0,0} /* Must be last */
184
};
185
#define etNINFO count(fmtinfo)
186
187
/*
188
** Verify that the fmtchr[] and fmtinfo[] arrays are in agreement.
189
**
190
** This routine is a defense against programming errors.
191
*/
192
void fossil_printf_selfcheck(void){
193
int i;
194
for(i=0; fmtchr[i]; i++){
195
assert( fmtchr[i]==fmtinfo[i].fmttype );
196
}
197
}
198
199
200
/*
201
** "*val" is a double such that 0.1 <= *val < 10.0
202
** Return the ascii code for the leading digit of *val, then
203
** multiply "*val" by 10.0 to renormalize.
204
**
205
** Example:
206
** input: *val = 3.14159
207
** output: *val = 1.4159 function return = '3'
208
**
209
** The counter *cnt is incremented each time. After counter exceeds
210
** 16 (the number of significant digits in a 64-bit float) '0' is
211
** always returned.
212
*/
213
static int et_getdigit(long double *val, int *cnt){
214
int digit;
215
long double d;
216
if( (*cnt)++ >= 16 ) return '0';
217
digit = (int)*val;
218
d = digit;
219
digit += '0';
220
*val = (*val - d)*10.0;
221
return digit;
222
}
223
224
/*
225
** Size of temporary conversion buffer.
226
*/
227
#define etBUFSIZE 500
228
229
/*
230
** Find the length of a string as long as that length does not
231
** exceed N bytes. If no zero terminator is seen in the first
232
** N bytes then return N. If N is negative, then this routine
233
** is an alias for strlen().
234
*/
235
#if _XOPEN_SOURCE >= 700 || _POSIX_C_SOURCE >= 200809L
236
# define StrNLen32(Z,N) (int)strnlen(Z,N)
237
#else
238
static int StrNLen32(const char *z, int N){
239
int n = 0;
240
while( (N-- != 0) && *(z++)!=0 ){ n++; }
241
return n;
242
}
243
#endif
244
245
/*
246
** SETTING: timeline-plaintext boolean default=off
247
**
248
** If enabled, no wiki-formatting is done for timeline comment messages.
249
** Hyperlinks are activated, but they show up on screen using the
250
** complete input text, not just the display text. No other formatting
251
** is done.
252
*/
253
/*
254
** SETTING: timeline-hard-newlines boolean default=off
255
**
256
** If enabled, the timeline honors newline characters in check-in comments.
257
** In other words, newlines are converted into <br> for HTML display.
258
** The default behavior, when this setting is off, is that newlines are
259
** treated like any other whitespace character.
260
*/
261
262
/*
263
** Return an appropriate set of flags for wiki_convert() for displaying
264
** comments on a timeline. These flag settings are determined by
265
** configuration parameters.
266
**
267
** The altForm2 argument is true for "%!W" (with the "!" alternate-form-2
268
** flags) and is false for plain "%W". The ! flag indicates that the
269
** formatting is for display of a check-in comment on the timeline. Such
270
** comments used to be rendered differently, but ever since 2020, they
271
** have been rendered identically, so the ! flag does not make any different
272
** in the output any more.
273
*/
274
int wiki_convert_flags(int altForm2){
275
static int wikiFlags = 0;
276
(void)altForm2;
277
if( wikiFlags==0 ){
278
wikiFlags = WIKI_INLINE | WIKI_NOBADLINKS;
279
if( db_get_boolean("timeline-plaintext", 0) ){
280
wikiFlags |= WIKI_LINKSONLY;
281
}
282
if( db_get_boolean("timeline-hard-newlines", 0) ){
283
wikiFlags |= WIKI_NEWLINE;
284
}
285
}
286
return wikiFlags;
287
}
288
289
290
291
/*
292
** The root program. All variations call this core.
293
**
294
** INPUTS:
295
** pBlob This is the blob where the output will be built.
296
**
297
** fmt This is the format string, as in the usual print.
298
**
299
** ap This is a pointer to a list of arguments. Same as in
300
** vfprint.
301
**
302
** OUTPUTS:
303
** The return value is the total number of characters sent to
304
** the function "func". Returns -1 on error.
305
**
306
** Note that the order in which automatic variables are declared below
307
** seems to make a big difference in determining how fast this beast
308
** will run.
309
*/
310
int vxprintf(
311
Blob *pBlob, /* Append output to this blob */
312
const char *fmt, /* Format string */
313
va_list ap /* arguments */
314
){
315
int c; /* Next character in the format string */
316
char *bufpt; /* Pointer to the conversion buffer */
317
int precision; /* Precision of the current field */
318
int length; /* Length of the field */
319
int idx; /* A general purpose loop counter */
320
int count; /* Total number of characters output */
321
int width; /* Width of the current field */
322
etByte flag_leftjustify; /* True if "-" flag is present */
323
etByte flag_plussign; /* True if "+" flag is present */
324
etByte flag_blanksign; /* True if " " flag is present */
325
etByte flag_alternateform; /* True if "#" flag is present */
326
etByte flag_altform2; /* True if "!" flag is present */
327
etByte flag_zeropad; /* True if field width constant starts with zero */
328
etByte flag_long; /* True if "l" flag is present */
329
etByte flag_longlong; /* True if the "ll" flag is present */
330
etByte done; /* Loop termination flag */
331
etByte cThousand; /* Thousands separator for %d and %u */
332
u64 longvalue; /* Value for integer types */
333
long double realvalue; /* Value for real types */
334
const et_info *infop; /* Pointer to the appropriate info structure */
335
char buf[etBUFSIZE]; /* Conversion buffer */
336
char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */
337
etByte errorflag = 0; /* True if an error is encountered */
338
etByte xtype; /* Conversion paradigm */
339
char *zExtra; /* Extra memory used for etTCLESCAPE conversions */
340
static const char spaces[] =
341
" ";
342
#define etSPACESIZE (sizeof(spaces)-1)
343
int exp, e2; /* exponent of real numbers */
344
double rounder; /* Used for rounding floating point values */
345
etByte flag_dp; /* True if decimal point should be shown */
346
etByte flag_rtz; /* True if trailing zeros should be removed */
347
etByte flag_exp; /* True to force display of the exponent */
348
int nsd; /* Number of significant digits returned */
349
char *zFmtLookup;
350
351
count = length = 0;
352
bufpt = 0;
353
for(; (c=(*fmt))!=0; ++fmt){
354
if( c!='%' ){
355
bufpt = (char *)fmt;
356
#if HAVE_STRCHRNUL
357
fmt = strchrnul(fmt, '%');
358
#else
359
do{ fmt++; }while( *fmt && *fmt != '%' );
360
#endif
361
blob_append(pBlob, bufpt, (int)(fmt - bufpt));
362
if( *fmt==0 ) break;
363
}
364
if( (c=(*++fmt))==0 ){
365
errorflag = 1;
366
blob_append(pBlob,"%",1);
367
count++;
368
break;
369
}
370
/* Find out what flags are present */
371
flag_leftjustify = flag_plussign = flag_blanksign = cThousand =
372
flag_alternateform = flag_altform2 = flag_zeropad = 0;
373
done = 0;
374
do{
375
switch( c ){
376
case '-': flag_leftjustify = 1; break;
377
case '+': flag_plussign = 1; break;
378
case ' ': flag_blanksign = 1; break;
379
case '#': flag_alternateform = 1; break;
380
case '!': flag_altform2 = 1; break;
381
case '0': flag_zeropad = 1; break;
382
case ',': cThousand = ','; break;
383
default: done = 1; break;
384
}
385
}while( !done && (c=(*++fmt))!=0 );
386
/* Get the field width */
387
width = 0;
388
if( c=='*' ){
389
width = va_arg(ap,int);
390
if( width<0 ){
391
flag_leftjustify = 1;
392
width = -width;
393
}
394
c = *++fmt;
395
}else{
396
while( c>='0' && c<='9' ){
397
width = width*10 + c - '0';
398
c = *++fmt;
399
}
400
}
401
if( width > etBUFSIZE-10 ){
402
width = etBUFSIZE-10;
403
}
404
/* Get the precision */
405
if( c=='.' ){
406
precision = 0;
407
c = *++fmt;
408
if( c=='*' ){
409
precision = va_arg(ap,int);
410
if( precision<0 ) precision = -precision;
411
c = *++fmt;
412
}else{
413
while( c>='0' && c<='9' ){
414
precision = precision*10 + c - '0';
415
c = *++fmt;
416
}
417
}
418
}else{
419
precision = -1;
420
}
421
/* Get the conversion type modifier */
422
if( c=='l' ){
423
flag_long = 1;
424
c = *++fmt;
425
if( c=='l' ){
426
flag_longlong = 1;
427
c = *++fmt;
428
}else{
429
flag_longlong = 0;
430
}
431
}else{
432
flag_long = flag_longlong = 0;
433
}
434
/* Fetch the info entry for the field */
435
zFmtLookup = strchr(fmtchr,c);
436
if( zFmtLookup ){
437
infop = &fmtinfo[zFmtLookup-fmtchr];
438
xtype = infop->type;
439
}else{
440
infop = 0;
441
xtype = etERROR;
442
}
443
zExtra = 0;
444
445
/* Limit the precision to prevent overflowing buf[] during conversion */
446
if( precision>etBUFSIZE-40 && (infop->flags & FLAG_STRING)==0 ){
447
precision = etBUFSIZE-40;
448
}
449
450
/*
451
** At this point, variables are initialized as follows:
452
**
453
** flag_alternateform TRUE if a '#' is present.
454
** flag_altform2 TRUE if a '!' is present.
455
** flag_plussign TRUE if a '+' is present.
456
** flag_leftjustify TRUE if a '-' is present or if the
457
** field width was negative.
458
** flag_zeropad TRUE if the width began with 0.
459
** flag_long TRUE if the letter 'l' (ell) prefixed
460
** the conversion character.
461
** flag_longlong TRUE if the letters 'll' (ell ell) prefixed
462
** the conversion character.
463
** flag_blanksign TRUE if a ' ' is present.
464
** width The specified field width. This is
465
** always non-negative. Zero is the default.
466
** precision The specified precision. The default
467
** is -1.
468
** xtype The class of the conversion.
469
** infop Pointer to the appropriate info struct.
470
*/
471
switch( xtype ){
472
case etPOINTER:
473
flag_longlong = sizeof(char*)==sizeof(i64);
474
flag_long = sizeof(char*)==sizeof(long int);
475
/* Fall through into the next case */
476
case etRADIX:
477
if( infop->flags & FLAG_SIGNED ){
478
i64 v;
479
if( flag_longlong ) v = va_arg(ap,i64);
480
else if( flag_long ) v = va_arg(ap,long int);
481
else v = va_arg(ap,int);
482
if( v<0 ){
483
longvalue = -v;
484
prefix = '-';
485
}else{
486
longvalue = v;
487
if( flag_plussign ) prefix = '+';
488
else if( flag_blanksign ) prefix = ' ';
489
else prefix = 0;
490
}
491
}else{
492
if( flag_longlong ) longvalue = va_arg(ap,u64);
493
else if( flag_long ) longvalue = va_arg(ap,unsigned long int);
494
else longvalue = va_arg(ap,unsigned int);
495
prefix = 0;
496
}
497
if( longvalue==0 ) flag_alternateform = 0;
498
if( flag_zeropad && precision<width-(prefix!=0) ){
499
precision = width-(prefix!=0);
500
}
501
bufpt = &buf[etBUFSIZE-1];
502
{
503
register const char *cset; /* Use registers for speed */
504
register int base;
505
cset = &aDigits[infop->charset];
506
base = infop->base;
507
do{ /* Convert to ascii */
508
*(--bufpt) = cset[longvalue%base];
509
longvalue = longvalue/base;
510
}while( longvalue>0 );
511
}
512
length = &buf[etBUFSIZE-1]-bufpt;
513
while( precision>length ){
514
*(--bufpt) = '0'; /* Zero pad */
515
length++;
516
}
517
if( cThousand ){
518
int nn = (length - 1)/3; /* Number of "," to insert */
519
int ix = (length - 1)%3 + 1;
520
bufpt -= nn;
521
for(idx=0; nn>0; idx++){
522
bufpt[idx] = bufpt[idx+nn];
523
ix--;
524
if( ix==0 ){
525
bufpt[++idx] = cThousand;
526
nn--;
527
ix = 3;
528
}
529
}
530
}
531
if( prefix ) *(--bufpt) = prefix; /* Add sign */
532
if( flag_alternateform && infop->prefix ){ /* Add "0" or "0x" */
533
const char *pre;
534
char x;
535
pre = &aPrefix[infop->prefix];
536
if( *bufpt!=pre[0] ){
537
for(; (x=(*pre))!=0; pre++) *(--bufpt) = x;
538
}
539
}
540
length = &buf[etBUFSIZE-1]-bufpt;
541
break;
542
case etFLOAT:
543
case etEXP:
544
case etGENERIC:
545
realvalue = va_arg(ap,double);
546
if( precision<0 ) precision = 6; /* Set default precision */
547
if( precision>etBUFSIZE/2-10 ) precision = etBUFSIZE/2-10;
548
if( realvalue<0.0 ){
549
realvalue = -realvalue;
550
prefix = '-';
551
}else{
552
if( flag_plussign ) prefix = '+';
553
else if( flag_blanksign ) prefix = ' ';
554
else prefix = 0;
555
}
556
if( xtype==etGENERIC && precision>0 ) precision--;
557
#if 0
558
/* Rounding works like BSD when the constant 0.4999 is used. Wierd! */
559
for(idx=precision, rounder=0.4999; idx>0; idx--, rounder*=0.1);
560
#else
561
/* It makes more sense to use 0.5 */
562
for(idx=precision, rounder=0.5; idx>0; idx--, rounder*=0.1);
563
#endif
564
if( xtype==etFLOAT ) realvalue += rounder;
565
/* Normalize realvalue to within 10.0 > realvalue >= 1.0 */
566
exp = 0;
567
if( realvalue>0.0 ){
568
while( realvalue>=1e32 && exp<=350 ){ realvalue *= 1e-32; exp+=32; }
569
while( realvalue>=1e8 && exp<=350 ){ realvalue *= 1e-8; exp+=8; }
570
while( realvalue>=10.0 && exp<=350 ){ realvalue *= 0.1; exp++; }
571
while( realvalue<1e-8 && exp>=-350 ){ realvalue *= 1e8; exp-=8; }
572
while( realvalue<1.0 && exp>=-350 ){ realvalue *= 10.0; exp--; }
573
if( exp>350 || exp<-350 ){
574
bufpt = "NaN";
575
length = 3;
576
break;
577
}
578
}
579
bufpt = buf;
580
/*
581
** If the field type is etGENERIC, then convert to either etEXP
582
** or etFLOAT, as appropriate.
583
*/
584
flag_exp = xtype==etEXP;
585
if( xtype!=etFLOAT ){
586
realvalue += rounder;
587
if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; }
588
}
589
if( xtype==etGENERIC ){
590
flag_rtz = !flag_alternateform;
591
if( exp<-4 || exp>precision ){
592
xtype = etEXP;
593
}else{
594
precision = precision - exp;
595
xtype = etFLOAT;
596
}
597
}else{
598
flag_rtz = 0;
599
}
600
if( xtype==etEXP ){
601
e2 = 0;
602
}else{
603
e2 = exp;
604
}
605
nsd = 0;
606
flag_dp = (precision>0) | flag_alternateform | flag_altform2;
607
/* The sign in front of the number */
608
if( prefix ){
609
*(bufpt++) = prefix;
610
}
611
/* Digits prior to the decimal point */
612
if( e2<0 ){
613
*(bufpt++) = '0';
614
}else{
615
for(; e2>=0; e2--){
616
*(bufpt++) = et_getdigit(&realvalue,&nsd);
617
}
618
}
619
/* The decimal point */
620
if( flag_dp ){
621
*(bufpt++) = '.';
622
}
623
/* "0" digits after the decimal point but before the first
624
** significant digit of the number */
625
for(e2++; e2<0 && precision>0; precision--, e2++){
626
*(bufpt++) = '0';
627
}
628
/* Significant digits after the decimal point */
629
while( (precision--)>0 ){
630
*(bufpt++) = et_getdigit(&realvalue,&nsd);
631
}
632
/* Remove trailing zeros and the "." if no digits follow the "." */
633
if( flag_rtz && flag_dp ){
634
while( bufpt[-1]=='0' ) *(--bufpt) = 0;
635
assert( bufpt>buf );
636
if( bufpt[-1]=='.' ){
637
if( flag_altform2 ){
638
*(bufpt++) = '0';
639
}else{
640
*(--bufpt) = 0;
641
}
642
}
643
}
644
/* Add the "eNNN" suffix */
645
if( flag_exp || (xtype==etEXP && exp) ){
646
*(bufpt++) = aDigits[infop->charset];
647
if( exp<0 ){
648
*(bufpt++) = '-'; exp = -exp;
649
}else{
650
*(bufpt++) = '+';
651
}
652
if( exp>=100 ){
653
*(bufpt++) = (exp/100)+'0'; /* 100's digit */
654
exp %= 100;
655
}
656
*(bufpt++) = exp/10+'0'; /* 10's digit */
657
*(bufpt++) = exp%10+'0'; /* 1's digit */
658
}
659
*bufpt = 0;
660
661
/* The converted number is in buf[] and zero terminated. Output it.
662
** Note that the number is in the usual order, not reversed as with
663
** integer conversions. */
664
length = bufpt-buf;
665
bufpt = buf;
666
667
/* Special case: Add leading zeros if the flag_zeropad flag is
668
** set and we are not left justified */
669
if( flag_zeropad && !flag_leftjustify && length < width){
670
int i;
671
int nPad = width - length;
672
for(i=width; i>=nPad; i--){
673
bufpt[i] = bufpt[i-nPad];
674
}
675
i = prefix!=0;
676
while( nPad-- ) bufpt[i++] = '0';
677
length = width;
678
}
679
break;
680
case etSIZE:
681
*(va_arg(ap,int*)) = count;
682
length = width = 0;
683
break;
684
case etPERCENT:
685
buf[0] = '%';
686
bufpt = buf;
687
length = 1;
688
break;
689
case etCHARX:
690
c = buf[0] = va_arg(ap,int);
691
if( precision>=0 ){
692
for(idx=1; idx<precision; idx++) buf[idx] = c;
693
length = precision;
694
}else{
695
length =1;
696
}
697
bufpt = buf;
698
break;
699
case etPATH: {
700
int i;
701
int limit = flag_alternateform ? va_arg(ap,int) : -1;
702
char *e = va_arg(ap,char*);
703
if( e==0 ){e="";}
704
length = StrNLen32(e, limit);
705
zExtra = bufpt = fossil_malloc(length+1);
706
for( i=0; i<length; i++ ){
707
if( e[i]=='\\' ){
708
bufpt[i]='/';
709
}else{
710
bufpt[i]=e[i];
711
}
712
}
713
bufpt[length]='\0';
714
break;
715
}
716
case etROOT: {
717
bufpt = g.zTop ? g.zTop : "";
718
length = (int)strlen(bufpt);
719
break;
720
}
721
case etSTRINGID:
722
case etSTRING:
723
case etDYNSTRING: {
724
int limit = flag_alternateform ? va_arg(ap,int) : -1;
725
bufpt = va_arg(ap,char*);
726
if( bufpt==0 ){
727
bufpt = "";
728
}else if( xtype==etDYNSTRING ){
729
zExtra = bufpt;
730
}else if( xtype==etSTRINGID ){
731
precision = hash_digits(flag_altform2);
732
}
733
length = StrNLen32(bufpt, limit);
734
if( precision>=0 && precision<length ) length = precision;
735
break;
736
}
737
case etBLOB: {
738
int limit = flag_alternateform ? va_arg(ap, int) : -1;
739
Blob *pBlob = va_arg(ap, Blob*);
740
bufpt = blob_buffer(pBlob);
741
length = blob_size(pBlob);
742
if( limit>=0 && limit<length ) length = limit;
743
break;
744
}
745
case etBLOBSQL: {
746
int limit = flag_alternateform ? va_arg(ap, int) : -1;
747
Blob *pBlob = va_arg(ap, Blob*);
748
char *zOrig = blob_buffer(pBlob);
749
int i, j, n, cnt;
750
n = blob_size(pBlob);
751
if( limit>=0 && limit<n ) n = limit;
752
for(cnt=i=0; i<n; i++){ if( zOrig[i]=='\'' ) cnt++; }
753
if( n+cnt+2 > etBUFSIZE ){
754
bufpt = zExtra = fossil_malloc( n + cnt + 2 );
755
}else{
756
bufpt = buf;
757
}
758
bufpt[0] = '\'';
759
for(i=0, j=1; i<n; i++, j++){
760
if( zOrig[i]=='\'' ){ bufpt[j++] = '\''; }
761
bufpt[j] = zOrig[i];
762
}
763
bufpt[j++] = '\'';
764
length = j;
765
assert( length==n+cnt+2 );
766
break;
767
}
768
case etSQLESCAPE:
769
case etSQLESCAPE2:
770
case etSQLESCAPE3: {
771
int i, j, n, ch, isnull;
772
int needQuote;
773
int limit = flag_alternateform ? va_arg(ap,int) : -1;
774
char q = ((xtype==etSQLESCAPE3)?'"':'\''); /* Quote characters */
775
char *escarg = va_arg(ap,char*);
776
isnull = escarg==0;
777
if( isnull ) escarg = (xtype==etSQLESCAPE2 ? "NULL" : "(NULL)");
778
if( limit<0 ) limit = strlen(escarg);
779
if( precision>=0 && precision<limit ) limit = precision;
780
for(i=n=0; i<limit; i++){
781
if( escarg[i]==q ) n++;
782
}
783
needQuote = !isnull && xtype==etSQLESCAPE2;
784
n += i + 1 + needQuote*2;
785
if( n>etBUFSIZE ){
786
bufpt = zExtra = fossil_malloc( n );
787
}else{
788
bufpt = buf;
789
}
790
j = 0;
791
if( needQuote ) bufpt[j++] = q;
792
for(i=0; i<limit; i++){
793
bufpt[j++] = ch = escarg[i];
794
if( ch==q ) bufpt[j++] = ch;
795
}
796
if( needQuote ) bufpt[j++] = q;
797
bufpt[j] = 0;
798
length = j;
799
break;
800
}
801
case etHTMLIZE: {
802
int limit = flag_alternateform ? va_arg(ap,int) : -1;
803
char *zMem = va_arg(ap,char*);
804
if( zMem==0 ) zMem = "";
805
zExtra = bufpt = htmlize(zMem, limit);
806
length = strlen(bufpt);
807
if( precision>=0 && precision<length ) length = precision;
808
break;
809
}
810
case etHTTPIZE: {
811
int limit = flag_alternateform ? va_arg(ap,int) : -1;
812
char *zMem = va_arg(ap,char*);
813
if( zMem==0 ) zMem = "";
814
zExtra = bufpt = httpize(zMem, limit);
815
length = strlen(bufpt);
816
if( precision>=0 && precision<length ) length = precision;
817
break;
818
}
819
case etURLIZE: {
820
int limit = flag_alternateform ? va_arg(ap,int) : -1;
821
char *zMem = va_arg(ap,char*);
822
if( zMem==0 ) zMem = "";
823
zExtra = bufpt = urlize(zMem, limit);
824
length = strlen(bufpt);
825
if( precision>=0 && precision<length ) length = precision;
826
break;
827
}
828
case etFOSSILIZE: {
829
int limit = flag_alternateform ? va_arg(ap,int) : -1;
830
char *zMem = va_arg(ap,char*);
831
if( zMem==0 ) zMem = "";
832
zExtra = bufpt = fossilize(zMem, limit);
833
length = strlen(bufpt);
834
if( precision>=0 && precision<length ) length = precision;
835
break;
836
}
837
case etJSONSTR: {
838
int limit = flag_alternateform ? va_arg(ap,int) : -1;
839
char *zMem = va_arg(ap,char*);
840
if( limit!=0 ){
841
/* Ignore the limit flag, if set, for JSON string
842
** output. This block exists to squelch the associated
843
** "unused variable" compiler warning. */
844
}
845
if( zMem==0 ) zMem = "";
846
zExtra = bufpt =
847
encode_json_string_literal(zMem, flag_altform2, &length);
848
if( precision>=0 && precision<length ) length = precision;
849
break;
850
}
851
case etWIKISTR: {
852
int limit = flag_alternateform ? va_arg(ap,int) : -1;
853
char *zWiki = va_arg(ap, char*);
854
Blob wiki;
855
blob_init(&wiki, zWiki, limit);
856
wiki_convert(&wiki, pBlob, wiki_convert_flags(flag_altform2));
857
blob_reset(&wiki);
858
length = width = 0;
859
break;
860
}
861
case etSHELLESC: {
862
char *zArg = va_arg(ap, char*);
863
blob_append_escaped_arg(pBlob, zArg, !flag_altform2);
864
length = width = 0;
865
break;
866
}
867
case etHEX: {
868
char *zArg = va_arg(ap, char*);
869
int szArg = (int)strlen(zArg);
870
int szBlob = blob_size(pBlob);
871
u8 *aBuf;
872
blob_resize(pBlob, szBlob+szArg*2+1);
873
aBuf = (u8*)&blob_buffer(pBlob)[szBlob];
874
encode16((const u8*)zArg, aBuf, szArg);
875
length = width = 0;
876
break;
877
}
878
case etERROR:
879
buf[0] = '%';
880
buf[1] = c;
881
errorflag = 0;
882
idx = 1+(c!=0);
883
blob_append(pBlob,"%",idx);
884
count += idx;
885
if( c==0 ) fmt--;
886
break;
887
}/* End switch over the format type */
888
/*
889
** The text of the conversion is pointed to by "bufpt" and is
890
** "length" characters long. The field width is "width". Do
891
** the output.
892
*/
893
if( !flag_leftjustify ){
894
register int nspace;
895
nspace = width-length;
896
if( nspace>0 ){
897
count += nspace;
898
while( nspace>=(int)etSPACESIZE ){
899
blob_append(pBlob,spaces,etSPACESIZE);
900
nspace -= etSPACESIZE;
901
}
902
if( nspace>0 ) blob_append(pBlob,spaces,nspace);
903
}
904
}
905
if( length>0 ){
906
blob_append(pBlob,bufpt,length);
907
count += length;
908
}
909
if( flag_leftjustify ){
910
register int nspace;
911
nspace = width-length;
912
if( nspace>0 ){
913
count += nspace;
914
while( nspace>=(int)etSPACESIZE ){
915
blob_append(pBlob,spaces,etSPACESIZE);
916
nspace -= etSPACESIZE;
917
}
918
if( nspace>0 ) blob_append(pBlob,spaces,nspace);
919
}
920
}
921
if( zExtra ){
922
fossil_free(zExtra);
923
}
924
}/* End for loop over the format string */
925
return errorflag ? -1 : count;
926
} /* End of function */
927
928
/*
929
** Print into memory obtained from fossil_malloc().
930
*/
931
char *mprintf(const char *zFormat, ...){
932
va_list ap;
933
char *z;
934
va_start(ap,zFormat);
935
z = vmprintf(zFormat, ap);
936
va_end(ap);
937
return z;
938
}
939
char *vmprintf(const char *zFormat, va_list ap){
940
Blob blob = empty_blob;
941
blob_vappendf(&blob, zFormat, ap);
942
blob_materialize(&blob);
943
return blob.aData;
944
}
945
946
/*
947
** Record an error message in the global g.zErrMsg variable.
948
**
949
** If there is already another error message, only overwrite it if
950
** the current error has a higher priority.
951
*/
952
void fossil_error(int iPriority, const char *zFormat, ...){
953
va_list ap;
954
if( iPriority<=0 ){
955
return;
956
}
957
if( g.zErrMsg ){
958
if( g.iErrPriority>=iPriority ){
959
return;
960
}
961
free(g.zErrMsg);
962
}
963
va_start(ap, zFormat);
964
g.zErrMsg = vmprintf(zFormat, ap);
965
va_end(ap);
966
g.iErrPriority = iPriority;
967
}
968
void fossil_error_reset(void){
969
free(g.zErrMsg);
970
g.zErrMsg = 0;
971
g.iErrPriority = 0;
972
}
973
974
/* True if the last character standard output cursor is setting at
975
** the beginning of a blank link. False if a \r has been to move the
976
** cursor to the beginning of the line or if not at the beginning of
977
** a line.
978
** was a \n
979
*/
980
static int stdoutAtBOL = 1;
981
982
/*
983
** Write to standard output or standard error.
984
**
985
** On windows, transform the output into the current terminal encoding
986
** if the output is going to the screen. If output is redirected into
987
** a file, no translation occurs. Switch output mode to binary to
988
** properly process line-endings, make sure to switch the mode back to
989
** text when done.
990
** No translation ever occurs on unix.
991
*/
992
void fossil_puts(const char *z, int toStdErr, int n){
993
FILE* out = (toStdErr ? stderr : stdout);
994
if( n==0 ) return;
995
assert( toStdErr==0 || toStdErr==1 );
996
if( toStdErr==0 ) stdoutAtBOL = (z[n-1]=='\n');
997
#if defined(_WIN32)
998
if( fossil_utf8_to_console(z, n, toStdErr) >= 0 ){
999
return;
1000
}
1001
fflush(out);
1002
_setmode(_fileno(out), _O_BINARY);
1003
#endif
1004
fwrite(z, 1, n, out);
1005
#if defined(_WIN32)
1006
fflush(out);
1007
_setmode(_fileno(out), _O_TEXT);
1008
#endif
1009
}
1010
1011
/*
1012
** Force the standard output cursor to move to the beginning
1013
** of a line, if it is not there already.
1014
*/
1015
int fossil_force_newline(void){
1016
if( g.cgiOutput==0 && stdoutAtBOL==0 ){
1017
fossil_puts("\n", 0, 1);
1018
return 1;
1019
}
1020
return 0;
1021
}
1022
1023
/*
1024
** Indicate that the cursor has moved to the start of a line by means
1025
** other than writing to standard output.
1026
*/
1027
void fossil_new_line_started(void){
1028
stdoutAtBOL = 1;
1029
}
1030
1031
/*
1032
** Write output for user consumption. If g.cgiOutput is enabled, then
1033
** send the output as part of the CGI reply. If g.cgiOutput is false,
1034
** then write on standard output.
1035
*/
1036
void fossil_print(const char *zFormat, ...){
1037
va_list ap;
1038
va_start(ap, zFormat);
1039
if( g.cgiOutput ){
1040
cgi_vprintf(zFormat, ap);
1041
}else{
1042
vxprintf(0, zFormat, ap);
1043
}
1044
va_end(ap);
1045
}
1046
void fossil_vprint(const char *zFormat, va_list ap){
1047
if( g.cgiOutput ){
1048
cgi_vprintf(zFormat, ap);
1049
}else{
1050
vxprintf(0, zFormat, ap);
1051
}
1052
}
1053
1054
/*
1055
** Print a trace message on standard error.
1056
*/
1057
void fossil_trace(const char *zFormat, ...){
1058
va_list ap;
1059
Blob b;
1060
va_start(ap, zFormat);
1061
b = empty_blob;
1062
vxprintf(&b, zFormat, ap);
1063
fossil_puts(blob_buffer(&b), 1, blob_size(&b));
1064
blob_reset(&b);
1065
va_end(ap);
1066
}
1067
1068
/*
1069
** Write a message to the error log, if the error log filename is
1070
** defined.
1071
**
1072
** If the message format begins with 'X', then omit that X from the
1073
** beginning of the message and add much more CGI context.
1074
*/
1075
void fossil_errorlog(const char *zFormat, ...){
1076
struct tm *pNow;
1077
time_t now;
1078
FILE *out;
1079
const char *z;
1080
int i;
1081
int bDetail = 0;
1082
int bBrief = 0;
1083
va_list ap;
1084
static const char *const azEnv[] = { "HTTP_HOST", "HTTP_REFERER",
1085
"HTTP_USER_AGENT",
1086
"PATH_INFO", "QUERY_STRING", "REMOTE_ADDR", "REQUEST_METHOD",
1087
"REQUEST_URI", "SCRIPT_NAME" };
1088
if( g.zErrlog==0 ) return;
1089
if( g.zErrlog[0]=='-' && g.zErrlog[1]==0 ){
1090
out = stderr;
1091
}else{
1092
out = fossil_fopen(g.zErrlog, "a");
1093
if( out==0 ) return;
1094
}
1095
now = time(0);
1096
pNow = gmtime(&now);
1097
fprintf(out, "------------- %04d-%02d-%02d %02d:%02d:%02d UTC ------------\n",
1098
pNow->tm_year+1900, pNow->tm_mon+1, pNow->tm_mday,
1099
pNow->tm_hour, pNow->tm_min, pNow->tm_sec);
1100
va_start(ap, zFormat);
1101
if( zFormat[0]=='X' ){
1102
bDetail = 1;
1103
zFormat++;
1104
}else if( strncmp(zFormat,"SMTP:",5)==0 ){
1105
bBrief = 1;
1106
}
1107
vfprintf(out, zFormat, ap);
1108
fprintf(out, " (pid %d)\n", (int)getpid());
1109
va_end(ap);
1110
if( g.zPhase!=0 ) fprintf(out, "while in %s\n", g.zPhase);
1111
if( bBrief ){
1112
/* Say nothing more */
1113
}else if( bDetail ){
1114
cgi_print_all(1,3,out);
1115
}else{
1116
for(i=0; i<count(azEnv); i++){
1117
char *p;
1118
if( (p = fossil_getenv(azEnv[i]))!=0 && p[0]!=0 ){
1119
fprintf(out, "%s=%s\n", azEnv[i], p);
1120
fossil_path_free(p);
1121
}else if( (z = P(azEnv[i]))!=0 && z[0]!=0 ){
1122
fprintf(out, "%s=%s\n", azEnv[i], z);
1123
}
1124
}
1125
}
1126
if( out!=stderr ) fclose(out);
1127
}
1128
1129
/*
1130
** The following variable becomes true while processing a fatal error
1131
** or a panic. If additional "recursive-fatal" errors occur while
1132
** shutting down, the recursive errors are silently ignored.
1133
*/
1134
static int mainInFatalError = 0;
1135
1136
/*
1137
** Write error message output
1138
*/
1139
static int fossil_print_error(int rc, const char *z){
1140
#ifdef FOSSIL_ENABLE_JSON
1141
if( g.json.isJsonMode!=0 ){
1142
/*
1143
** Avoid calling into the JSON support subsystem if it
1144
** has not yet been initialized, e.g. early SQLite log
1145
** messages, etc.
1146
*/
1147
if( !json_is_bootstrapped_early() ) json_bootstrap_early();
1148
json_err( 0, z, 1 );
1149
if( g.isHTTP && !g.json.preserveRc ){
1150
rc = 0 /* avoid HTTP 500 */;
1151
}
1152
if( g.cgiOutput==1 ){
1153
g.cgiOutput = 2;
1154
cgi_reply();
1155
}
1156
}
1157
else
1158
#endif
1159
if( g.cgiOutput==1 && g.db ){
1160
g.cgiOutput = 2;
1161
cgi_reset_content();
1162
cgi_set_content_type("text/html");
1163
if( g.zLogin!=0 ){
1164
style_set_current_feature("error");
1165
}
1166
style_header("Bad Request");
1167
etag_cancel();
1168
if( g.zLogin==0 ){
1169
/* Do not give unnecessary clues about a malfunction to robots */
1170
@ <p>Something did not work right.</p>
1171
@ <p>%h(z)</p>
1172
}else{
1173
@ <p class="generalError">%h(z)</p>
1174
cgi_set_status(400, "Bad Request");
1175
}
1176
style_finish_page();
1177
cgi_reply();
1178
}else if( !g.fQuiet ){
1179
fossil_force_newline();
1180
fossil_trace("%s\n", z);
1181
}
1182
return rc;
1183
}
1184
1185
/*
1186
** Print an error message, rollback all databases, and quit. These
1187
** routines never return and produce a non-zero process exit status.
1188
**
1189
** The main difference between fossil_fatal() and fossil_panic() is that
1190
** fossil_panic() makes an entry in the error log whereas fossil_fatal()
1191
** does not. On POSIX platforms, if there is not an error log, then both
1192
** routines work similarly with respect to user-visible effects. Hence,
1193
** the routines are interchangeable for commands and only act differently
1194
** when processing web pages. On the Windows platform, fossil_panic()
1195
** also displays a pop-up stating that an error has occurred and allowing
1196
** just-in-time debugging to commence. On all platforms, fossil_panic()
1197
** ends execution with a SIGABRT signal, bypassing atexit processing.
1198
** This signal can also produce a core dump on POSIX platforms.
1199
**
1200
** Use fossil_fatal() for malformed inputs that should be reported back
1201
** to the user, but which do not represent a configuration problem or bug.
1202
**
1203
** Use fossil_panic() for any kind of error that should be brought to the
1204
** attention of the system administrator or Fossil developers. It should
1205
** be avoided for ordinary usage, parameter, OOM and I/O errors.
1206
*/
1207
NORETURN void fossil_panic(const char *zFormat, ...){
1208
va_list ap;
1209
int rc = 1;
1210
char z[1000];
1211
static int once = 0;
1212
1213
if( once ) exit(1);
1214
once = 1;
1215
mainInFatalError = 1;
1216
/* db_force_rollback(); */
1217
va_start(ap, zFormat);
1218
sqlite3_vsnprintf(sizeof(z),z,zFormat, ap);
1219
va_end(ap);
1220
if( g.fAnyTrace ){
1221
fprintf(stderr, "/***** panic on %d *****/\n", getpid());
1222
}
1223
fossil_errorlog("panic: %s", z);
1224
rc = fossil_print_error(rc, z);
1225
abort();
1226
exit(rc);
1227
}
1228
NORETURN void fossil_fatal(const char *zFormat, ...){
1229
static int once = 0;
1230
va_list ap;
1231
char *z;
1232
int rc = 1;
1233
if( once ) exit(1);
1234
once = 1;
1235
mainInFatalError = 1;
1236
va_start(ap, zFormat);
1237
z = vmprintf(zFormat, ap);
1238
va_end(ap);
1239
rc = fossil_print_error(rc, z);
1240
fossil_free(z);
1241
db_force_rollback();
1242
fossil_exit(rc);
1243
}
1244
1245
/* This routine works like fossil_fatal() except that if called
1246
** recursively, the recursive call is a no-op.
1247
**
1248
** Use this in places where an error might occur while doing
1249
** fatal error shutdown processing. Unlike fossil_panic() and
1250
** fossil_fatal() which never return, this routine might return if
1251
** the fatal error handing is already in process. The caller must
1252
** be prepared for this routine to return.
1253
*/
1254
void fossil_fatal_recursive(const char *zFormat, ...){
1255
char *z;
1256
va_list ap;
1257
int rc = 1;
1258
if( mainInFatalError ) return;
1259
mainInFatalError = 1;
1260
va_start(ap, zFormat);
1261
z = vmprintf(zFormat, ap);
1262
va_end(ap);
1263
rc = fossil_print_error(rc, z);
1264
db_force_rollback();
1265
fossil_exit(rc);
1266
}
1267
1268
1269
/* Print a warning message.
1270
**
1271
** Unlike fossil_fatal() and fossil_panic(), this routine does return
1272
** and processing attempts to continue. A message is written to the
1273
** error log, however, so this routine should only be used for situations
1274
** that require administrator or developer attention. Minor problems
1275
** in user inputs should not use this routine.
1276
*/
1277
void fossil_warning(const char *zFormat, ...){
1278
char *z;
1279
va_list ap;
1280
va_start(ap, zFormat);
1281
z = vmprintf(zFormat, ap);
1282
va_end(ap);
1283
fossil_errorlog("warning: %s", z);
1284
#ifdef FOSSIL_ENABLE_JSON
1285
if( g.json.isJsonMode!=0 ){
1286
/*
1287
** Avoid calling into the JSON support subsystem if it
1288
** has not yet been initialized, e.g. early SQLite log
1289
** messages, etc.
1290
*/
1291
if( !json_is_bootstrapped_early() ) json_bootstrap_early();
1292
json_warn( FSL_JSON_W_UNKNOWN, "%s", z );
1293
}else
1294
#endif
1295
{
1296
if( g.cgiOutput==1 ){
1297
etag_cancel();
1298
cgi_printf("<p class=\"generalError\">\n%h\n</p>\n", z);
1299
}else{
1300
fossil_force_newline();
1301
fossil_trace("%s\n", z);
1302
Turn off any LF to CRLF translation on the stream given as an
1303
** argument. This is a no-op on unix but is necessary on windows.
1304
*/
1305
void fossil_binary_mode(FILE *p){
1306
#if defined(_WIN32)
1307
_setmode(_fileno(p), _O_BINARY);
1308
#endif
1309
#ifdef __EMX__ /* OS/2 */
1310
setmode(fossil_file

Keyboard Shortcuts

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