Fossil SCM

fossil-scm / src / blob.c
Blame History Raw 2048 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
** A Blob is a variable-length containers for arbitrary string
19
** or binary data.
20
*/
21
#include "config.h"
22
#include <zlib.h>
23
#include "blob.h"
24
#if defined(_WIN32)
25
#include <fcntl.h>
26
#include <io.h>
27
#endif
28
29
#if INTERFACE
30
/*
31
** A Blob can hold a string or a binary object of arbitrary size. The
32
** size changes as necessary.
33
*/
34
struct Blob {
35
unsigned int nUsed; /* Number of bytes used in aData[] */
36
unsigned int nAlloc; /* Number of bytes allocated for aData[] */
37
unsigned int iCursor; /* Next character of input to parse */
38
unsigned int blobFlags; /* One or more BLOBFLAG_* bits */
39
char *aData; /* Where the information is stored */
40
void (*xRealloc)(Blob*,u64); /* Function to reallocate the buffer */
41
};
42
43
/*
44
** Allowed values for Blob.blobFlags
45
*/
46
#define BLOBFLAG_NotSQL 0x0001 /* Non-SQL text */
47
48
/*
49
** The current size of a Blob
50
*/
51
#define blob_size(X) ((X)->nUsed)
52
53
/*
54
** The buffer holding the blob data
55
*/
56
#define blob_buffer(X) ((X)->aData)
57
58
/*
59
** Number of elements that fits into the current blob's size
60
*/
61
#define blob_count(X,elType) (blob_size(X)/sizeof(elType))
62
63
/*
64
** Append blob contents to another
65
*/
66
#define blob_appendb(dest, src) \
67
blob_append((dest), blob_buffer(src), blob_size(src))
68
69
/*
70
** Append a string literal to a blob
71
** TODO: Consider renaming to blob_appendl()
72
*/
73
#define blob_append_literal(blob, literal) \
74
blob_append((blob), "" literal, (sizeof literal)-1)
75
/*
76
* The empty string in the second argument leads to a syntax error
77
* when the macro is not used with a string literal. Unfortunately
78
* the error is not overly explicit.
79
*/
80
81
/*
82
** TODO: Suggested for removal because the name seems misleading.
83
*/
84
#define blob_append_string blob_append_literal
85
86
/*
87
** Seek whence parameter values
88
*/
89
#define BLOB_SEEK_SET 1
90
#define BLOB_SEEK_CUR 2
91
92
#endif /* INTERFACE */
93
94
/*
95
** Make sure a blob is initialized
96
*/
97
#define blob_is_init(x) \
98
assert((x)->xRealloc==blobReallocMalloc || (x)->xRealloc==blobReallocStatic)
99
100
/*
101
** Make sure a blob does not contain malloced memory.
102
**
103
** This might fail if we are unlucky and x is uninitialized. For that
104
** reason it should only be used locally for debugging. Leave it turned
105
** off for production.
106
*/
107
#if 0 /* Enable for debugging only */
108
#define assert_blob_is_reset(x) assert(blob_is_reset(x))
109
#else
110
#define assert_blob_is_reset(x)
111
#endif
112
113
114
115
/*
116
** We find that the built-in isspace() function does not work for
117
** some international character sets. So here is a substitute.
118
*/
119
int fossil_isspace(char c){
120
return c==' ' || (c<='\r' && c>='\t');
121
}
122
123
/*
124
** Other replacements for ctype.h functions.
125
*/
126
int fossil_islower(char c){ return c>='a' && c<='z'; }
127
int fossil_isupper(char c){ return c>='A' && c<='Z'; }
128
int fossil_isdigit(char c){ return c>='0' && c<='9'; }
129
int fossil_isxdigit(char c){ return (c>='0' && c<='9') || (c>='a' && c<='f'); }
130
int fossil_isXdigit(char c){
131
return (c>='0' && c<='9') || (c>='A' && c<='F') || (c>='a' && c<='f');
132
}
133
int fossil_tolower(char c){
134
return fossil_isupper(c) ? c - 'A' + 'a' : c;
135
}
136
int fossil_toupper(char c){
137
return fossil_islower(c) ? c - 'a' + 'A' : c;
138
}
139
int fossil_isalpha(char c){
140
return (c>='a' && c<='z') || (c>='A' && c<='Z');
141
}
142
int fossil_isalnum(char c){
143
return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9');
144
}
145
146
/* Return true if and only if the entire string consists of only
147
** alphanumeric characters.
148
*/
149
int fossil_no_strange_characters(const char *z){
150
while( z && (fossil_isalnum(z[0]) || z[0]=='_' || z[0]=='-') ) z++;
151
return z[0]==0;
152
}
153
154
155
/*
156
** COMMAND: test-isspace
157
**
158
** Verify that the fossil_isspace() routine is working correctly by
159
** testing it on all possible inputs.
160
*/
161
void isspace_cmd(void){
162
int i;
163
for(i=0; i<=255; i++){
164
if( i==' ' || i=='\n' || i=='\t' || i=='\v'
165
|| i=='\f' || i=='\r' ){
166
assert( fossil_isspace((char)i) );
167
}else{
168
assert( !fossil_isspace((char)i) );
169
}
170
}
171
fossil_print("All 256 characters OK\n");
172
}
173
174
/*
175
** This routine is called if a blob operation fails because we
176
** have run out of memory.
177
*/
178
static void blob_panic(void){
179
static const char zErrMsg[] = "out of memory\n";
180
fputs(zErrMsg, stderr);
181
fossil_exit(1);
182
}
183
184
/*
185
** Maximum size of a Blob's managed memory. This is ~2GB, largely for
186
** historical reasons.
187
**
188
*/
189
#define MAX_BLOB_SIZE 0x7fff0000
190
191
/*
192
** If n >= MAX_BLOB_SIZE, calls blob_panic(),
193
** else this is a no-op.
194
*/
195
static void blob_assert_safe_size(u64 n){
196
if( n>=(i64)MAX_BLOB_SIZE ){
197
blob_panic();
198
}
199
}
200
201
/*
202
** A reallocation function that assumes that aData came from malloc().
203
** This function attempts to resize the buffer of the blob to hold
204
** newSize bytes.
205
**
206
** No attempt is made to recover from an out-of-memory error.
207
** If an OOM error occurs, an error message is printed on stderr
208
** and the program exits.
209
*/
210
void blobReallocMalloc(Blob *pBlob, u64 newSize){
211
if( newSize==0 ){
212
free(pBlob->aData);
213
pBlob->aData = 0;
214
pBlob->nAlloc = 0;
215
pBlob->nUsed = 0;
216
pBlob->iCursor = 0;
217
pBlob->blobFlags = 0;
218
}else if( newSize>pBlob->nAlloc || newSize+4000<pBlob->nAlloc ){
219
char *pNew;
220
blob_assert_safe_size(newSize);
221
pNew = fossil_realloc(pBlob->aData, newSize);
222
pBlob->aData = pNew;
223
pBlob->nAlloc = (unsigned int)newSize;
224
if( pBlob->nUsed>pBlob->nAlloc ){
225
pBlob->nUsed = pBlob->nAlloc;
226
}
227
}
228
}
229
230
/*
231
** An initializer for Blobs
232
*/
233
#if INTERFACE
234
#define BLOB_INITIALIZER {0,0,0,0,0,blobReallocMalloc}
235
#endif
236
const Blob empty_blob = BLOB_INITIALIZER;
237
238
/*
239
** A reallocation function for when the initial string is in unmanaged
240
** space. Copy the string to memory obtained from malloc().
241
*/
242
static void blobReallocStatic(Blob *pBlob, u64 newSize){
243
if( newSize==0 ){
244
*pBlob = empty_blob;
245
}else{
246
char *pNew;
247
blob_assert_safe_size(newSize);
248
pNew = fossil_malloc( newSize );
249
if( pBlob->nUsed>newSize ) pBlob->nUsed = newSize;
250
memcpy(pNew, pBlob->aData, pBlob->nUsed);
251
pBlob->aData = pNew;
252
pBlob->xRealloc = blobReallocMalloc;
253
pBlob->nAlloc = (unsigned int)newSize;
254
}
255
}
256
257
/*
258
** Reset a blob to be an empty container.
259
*/
260
void blob_reset(Blob *pBlob){
261
blob_is_init(pBlob);
262
pBlob->xRealloc(pBlob, 0);
263
}
264
265
266
/*
267
** Return true if the blob has been zeroed - in other words if it contains
268
** no malloced memory. This only works reliably if the blob has been
269
** initialized - it can return a false negative on an uninitialized blob.
270
*/
271
int blob_is_reset(Blob *pBlob){
272
if( pBlob==0 ) return 1;
273
if( pBlob->nUsed ) return 0;
274
if( pBlob->xRealloc==blobReallocMalloc && pBlob->nAlloc ) return 0;
275
return 1;
276
}
277
278
/*
279
** Initialize a blob to a string or byte-array constant of a specified length.
280
** Any prior data in the blob is discarded.
281
*/
282
void blob_init(Blob *pBlob, const char *zData, int size){
283
assert_blob_is_reset(pBlob);
284
if( zData==0 ){
285
*pBlob = empty_blob;
286
}else{
287
if( size<=0 ) size = strlen(zData);
288
pBlob->nUsed = pBlob->nAlloc = size;
289
pBlob->aData = (char*)zData;
290
pBlob->iCursor = 0;
291
pBlob->blobFlags = 0;
292
pBlob->xRealloc = blobReallocStatic;
293
}
294
}
295
296
/*
297
** Initialize a blob to a nul-terminated string.
298
** Any prior data in the blob is discarded.
299
*/
300
void blob_set(Blob *pBlob, const char *zStr){
301
blob_init(pBlob, zStr, -1);
302
}
303
304
/*
305
** Initialize a blob to a nul-terminated string obtained from fossil_malloc().
306
** The blob will take responsibility for freeing the string.
307
*/
308
void blob_set_dynamic(Blob *pBlob, char *zStr){
309
blob_init(pBlob, zStr, -1);
310
pBlob->xRealloc = blobReallocMalloc;
311
}
312
313
/*
314
** Initialize a blob to an empty string.
315
*/
316
void blob_zero(Blob *pBlob){
317
static const char zEmpty[] = "";
318
assert_blob_is_reset(pBlob);
319
pBlob->nUsed = 0;
320
pBlob->nAlloc = 1;
321
pBlob->aData = (char*)zEmpty;
322
pBlob->iCursor = 0;
323
pBlob->blobFlags = 0;
324
pBlob->xRealloc = blobReallocStatic;
325
}
326
327
/*
328
** Append text or data to the end of a blob. Or, if pBlob==NULL, send
329
** the text to standard output in terminal mode, or to standard CGI output
330
** in CGI mode.
331
**
332
** If nData<0 then output all of aData up to the first 0x00 byte.
333
**
334
** Use the blob_append() routine in all application code. The blob_append()
335
** routine is faster, but blob_append_full() handles all the corner cases.
336
** The blob_append() routine automatically calls blob_append_full() if
337
** necessary.
338
*/
339
static void blob_append_full(Blob *pBlob, const char *aData, int nData){
340
u64 nNew;
341
/* assert( aData!=0 || nData==0 ); // omitted for speed */
342
/* blob_is_init(pBlob); // omitted for speed */
343
if( nData<0 ) nData = strlen(aData);
344
if( nData==0 ) return;
345
if( pBlob==0 ){
346
if( g.cgiOutput ){
347
pBlob = cgi_output_blob();
348
}else{
349
fossil_puts(aData, 0, nData);
350
return;
351
}
352
}
353
nNew = pBlob->nUsed;
354
nNew += nData;
355
if( nNew >= pBlob->nAlloc ){
356
nNew += pBlob->nAlloc;
357
nNew += 100;
358
blob_assert_safe_size(nNew);
359
pBlob->xRealloc(pBlob, (unsigned)nNew);
360
if( pBlob->nUsed + nData >= pBlob->nAlloc ){
361
blob_panic();
362
}
363
}
364
memcpy(&pBlob->aData[pBlob->nUsed], aData, nData);
365
pBlob->nUsed += nData;
366
pBlob->aData[pBlob->nUsed] = 0; /* Blobs are always nul-terminated */
367
}
368
void blob_append(Blob *pBlob, const char *aData, int nData){
369
u64 nUsed;
370
/* assert( aData!=0 || nData==0 ); // omitted for speed */
371
if( nData<=0 || pBlob==0 || pBlob->nUsed + nData >= pBlob->nAlloc ){
372
blob_append_full(pBlob, aData, nData);
373
return;
374
}
375
nUsed = pBlob->nUsed;
376
pBlob->nUsed += nData;
377
pBlob->aData[pBlob->nUsed] = 0;
378
memcpy(&pBlob->aData[nUsed], aData, nData);
379
}
380
381
/*
382
** Append a single character to the blob. If pBlob is zero then the
383
** character is written directly to stdout.
384
*/
385
void blob_append_char(Blob *pBlob, char c){
386
if( pBlob==0 || pBlob->nUsed+1 >= pBlob->nAlloc ){
387
blob_append_full(pBlob, &c, 1);
388
}else{
389
pBlob->aData[pBlob->nUsed++] = c;
390
}
391
}
392
393
/*
394
** Copy a blob. pTo is reinitialized to be a copy of pFrom.
395
*/
396
void blob_copy(Blob *pTo, Blob *pFrom){
397
blob_is_init(pFrom);
398
blob_zero(pTo);
399
blob_append(pTo, blob_buffer(pFrom), blob_size(pFrom));
400
}
401
402
/*
403
** Append the second blob onto the end of the first blob and reset the
404
** second blob. If the first blob (pTo) is NULL, then the content
405
** of the second blob is written to stdout or to CGI depending on if the
406
** Fossil is running in terminal or CGI mode.
407
*/
408
void blob_append_xfer(Blob *pTo, Blob *pFrom){
409
blob_append(pTo, blob_buffer(pFrom), blob_size(pFrom));
410
blob_reset(pFrom);
411
}
412
413
/*
414
** Write into pOut, a string literal representation for the first n bytes
415
** of z[]. The string literal representation is compatible with C, TCL,
416
** and JSON. Double-quotes are added to both ends. Double-quote and
417
** backslash characters are escaped.
418
*/
419
void blob_append_tcl_literal(Blob *pOut, const char *z, size_t n){
420
size_t i;
421
blob_append_char(pOut, '"');
422
for(i=0; i<n; i++){
423
char c = z[i];
424
switch( c ){
425
case '\r': c = 'r';
426
case '[':
427
case ']':
428
case '$':
429
case '"':
430
case '\\':
431
blob_append_char(pOut, '\\');
432
default:
433
blob_append_char(pOut, c);
434
}
435
}
436
blob_append_char(pOut, '"');
437
}
438
void blob_append_json_literal(Blob *pOut, const char *z, size_t n){
439
size_t i;
440
blob_append_char(pOut, '"');
441
for(i=0; i<n; i++){
442
char c = z[i];
443
switch( c ){
444
case 0x00:
445
case 0x01:
446
case 0x02:
447
case 0x03:
448
case 0x04:
449
case 0x05:
450
case 0x06:
451
case 0x07: c += '0' - 0x00; blob_append(pOut, "\\u000",5); break;
452
case 0x0b:
453
case 0x0e:
454
case 0x0f: c += 'a' - 0x0a; blob_append(pOut, "\\u000",5); break;
455
case 0x10:
456
case 0x11:
457
case 0x12:
458
case 0x13:
459
case 0x14:
460
case 0x15:
461
case 0x16:
462
case 0x17:
463
case 0x18:
464
case 0x19: c += '0' - 0x10; blob_append(pOut, "\\u001",5); break;
465
case 0x1a:
466
case 0x1b:
467
case 0x1c:
468
case 0x1d:
469
case 0x1e:
470
case 0x1f: c += 'a' - 0x1a; blob_append(pOut, "\\u001",5); break;
471
case '\b': c = 'b'; blob_append_char(pOut, '\\'); break;
472
case '\t': c = 't'; blob_append_char(pOut, '\\'); break;
473
case '\r': c = 'r'; blob_append_char(pOut, '\\'); break;
474
case '\n': c = 'n'; blob_append_char(pOut, '\\'); break;
475
case '\f': c = 'f'; blob_append_char(pOut, '\\'); break;
476
case '"': blob_append_char(pOut, '\\'); break;
477
case '\\': blob_append_char(pOut, '\\'); break;
478
default: break;
479
}
480
blob_append_char(pOut, c);
481
}
482
blob_append_char(pOut, '"');
483
}
484
485
486
/*
487
** Return a pointer to a null-terminated string for a blob.
488
*/
489
char *blob_str(Blob *p){
490
blob_is_init(p);
491
if( p->nUsed==0 ){
492
blob_append_char(p, 0); /* NOTE: Changes nUsed. */
493
p->nUsed = 0;
494
}
495
if( p->nUsed<p->nAlloc ){
496
p->aData[p->nUsed] = 0;
497
}else{
498
blob_materialize(p);
499
}
500
return p->aData;
501
}
502
503
/*
504
** Compute the string length of a Blob. If there are embedded
505
** nul characters, truncate the to blob at the first nul.
506
*/
507
int blob_strlen(Blob *p){
508
char *z = blob_str(p);
509
if( z==0 ) return 0;
510
p->nUsed = (int)strlen(p->aData);
511
return p->nUsed;
512
}
513
514
/*
515
** Return a pointer to a null-terminated string for a blob that has
516
** been created using blob_append_sql() and not blob_appendf(). If
517
** text was ever added using blob_appendf() then throw an error.
518
*/
519
char *blob_sql_text(Blob *p){
520
blob_is_init(p);
521
if( (p->blobFlags & BLOBFLAG_NotSQL) ){
522
fossil_panic("use of blob_appendf() to construct SQL text");
523
}
524
return blob_str(p);
525
}
526
527
528
/*
529
** Return a pointer to a null-terminated string for a blob.
530
**
531
** WARNING: If the blob is ephemeral, it might cause a '\000'
532
** character to be inserted into the middle of the parent blob.
533
** Example: Suppose p is a token extracted from some larger
534
** blob pBig using blob_token(). If you call this routine on p,
535
** then a '\000' character will be inserted in the middle of
536
** pBig in order to cause p to be nul-terminated. If pBig
537
** should not be modified, then use blob_str() instead of this
538
** routine. blob_str() will make a copy of the p if necessary
539
** to avoid modifying pBig.
540
*/
541
char *blob_terminate(Blob *p){
542
blob_is_init(p);
543
if( p->nUsed==0 ) return "";
544
p->aData[p->nUsed] = 0;
545
return p->aData;
546
}
547
548
/*
549
** Compare two blobs. Return negative, zero, or positive if the first
550
** blob is less then, equal to, or greater than the second.
551
*/
552
int blob_compare(const Blob *pA, const Blob *pB){
553
int szA, szB, sz, rc;
554
blob_is_init(pA);
555
blob_is_init(pB);
556
szA = blob_size(pA);
557
szB = blob_size(pB);
558
sz = szA<szB ? szA : szB;
559
rc = memcmp(blob_buffer(pA), blob_buffer(pB), sz);
560
if( rc==0 ){
561
rc = szA - szB;
562
}
563
return rc;
564
}
565
566
/*
567
** Compare two blobs in constant time and return zero if they are equal.
568
** Constant time comparison only applies for blobs of the same length.
569
** If lengths are different, immediately returns 1.
570
*/
571
int blob_constant_time_cmp(Blob *pA, Blob *pB){
572
int szA, szB, i;
573
unsigned char *buf1, *buf2;
574
unsigned char rc = 0;
575
576
blob_is_init(pA);
577
blob_is_init(pB);
578
szA = blob_size(pA);
579
szB = blob_size(pB);
580
if( szA!=szB || szA==0 ) return 1;
581
582
buf1 = (unsigned char*)blob_buffer(pA);
583
buf2 = (unsigned char*)blob_buffer(pB);
584
585
for( i=0; i<szA; i++ ){
586
rc = rc | (buf1[i] ^ buf2[i]);
587
}
588
589
return rc;
590
}
591
592
/*
593
** Compare a blob to a string. Return TRUE if they are equal.
594
*/
595
int blob_eq_str(Blob *pBlob, const char *z, int n){
596
Blob t;
597
blob_is_init(pBlob);
598
if( n<=0 ) n = (int)strlen(z);
599
t.aData = (char*)z;
600
t.nUsed = n;
601
t.xRealloc = blobReallocStatic;
602
return blob_compare(pBlob, &t)==0;
603
}
604
605
/*
606
** This macro compares a blob against a string constant. We use the sizeof()
607
** operator on the string constant twice, so it really does need to be a
608
** string literal or character array - not a character pointer.
609
*/
610
#if INTERFACE
611
# define blob_eq(B,S) \
612
((B)->nUsed==sizeof(S"")-1 && memcmp((B)->aData,S,sizeof(S)-1)==0)
613
#endif
614
615
616
/*
617
** Attempt to resize a blob so that its internal buffer is
618
** nByte in size. The blob is truncated if necessary.
619
*/
620
void blob_resize(Blob *pBlob, u64 newSize){
621
pBlob->xRealloc(pBlob, newSize+1);
622
pBlob->nUsed = newSize;
623
pBlob->aData[newSize] = 0;
624
}
625
626
/*
627
** Ensures that the given blob has at least the given amount of memory
628
** allocated to it. Does not modify pBlob->nUsed nor will it reduce
629
** the currently-allocated amount of memory.
630
**
631
** For semantic compatibility with blob_append_full(), if newSize is
632
** >=MAX_BLOB_SIZE then this function will trigger blob_panic(). If it
633
** didn't, it would be possible to bypass that hard-coded limit via
634
** this function.
635
**
636
** We've had at least one report:
637
** https://fossil-scm.org/forum/forumpost/b7bbd28db4
638
** which implies that this is unconditionally failing on mingw 32-bit
639
** builds.
640
*/
641
void blob_reserve(Blob *pBlob, u64 newSize){
642
blob_assert_safe_size( newSize );
643
if(newSize>pBlob->nAlloc){
644
pBlob->xRealloc(pBlob, newSize+1);
645
pBlob->aData[newSize] = 0;
646
}
647
}
648
649
/*
650
** Make sure a blob is nul-terminated and is not a pointer to unmanaged
651
** space. Return a pointer to the data.
652
*/
653
char *blob_materialize(Blob *pBlob){
654
blob_resize(pBlob, pBlob->nUsed);
655
return pBlob->aData;
656
}
657
658
/*
659
** Call dehttpize on a blob. This causes an ephemeral blob to be
660
** materialized.
661
*/
662
void blob_dehttpize(Blob *pBlob){
663
blob_materialize(pBlob);
664
pBlob->nUsed = dehttpize(pBlob->aData);
665
}
666
667
/*
668
** Extract N bytes from blob pFrom and use it to initialize blob pTo.
669
** Return the actual number of bytes extracted. The cursor position
670
** is advanced by the number of bytes extracted.
671
**
672
** After this call completes, pTo will be an ephemeral blob.
673
*/
674
int blob_extract(Blob *pFrom, int N, Blob *pTo){
675
blob_is_init(pFrom);
676
assert_blob_is_reset(pTo);
677
if( pFrom->iCursor + N > pFrom->nUsed ){
678
N = pFrom->nUsed - pFrom->iCursor;
679
if( N<=0 ){
680
blob_zero(pTo);
681
return 0;
682
}
683
}
684
pTo->nUsed = N;
685
pTo->nAlloc = N;
686
pTo->aData = &pFrom->aData[pFrom->iCursor];
687
pTo->iCursor = 0;
688
pTo->xRealloc = blobReallocStatic;
689
pFrom->iCursor += N;
690
return N;
691
}
692
693
/*
694
** Extract N **lines** of text from blob pFrom beginning at the current
695
** cursor position and use that text to initialize blob pTo. Unlike the
696
** blob_extract() routine, the cursor position is unchanged.
697
**
698
** pTo is assumed to be uninitialized.
699
**
700
** After this call completes, pTo will be an ephemeral blob.
701
*/
702
int blob_extract_lines(Blob *pFrom, int N, Blob *pTo){
703
int i;
704
int mx;
705
int iStart;
706
int n;
707
const char *z;
708
709
blob_zero(pTo);
710
z = pFrom->aData;
711
i = pFrom->iCursor;
712
mx = pFrom->nUsed;
713
while( N>0 ){
714
while( i<mx && z[i]!='\n' ){ i++; }
715
if( i>=mx ) break;
716
i++;
717
N--;
718
}
719
iStart = pFrom->iCursor;
720
n = blob_extract(pFrom, i-pFrom->iCursor, pTo);
721
pFrom->iCursor = iStart;
722
return n;
723
}
724
725
/*
726
** Return the number of lines of text in the blob. If the last
727
** line is incomplete (if it does not have a \n at the end) then
728
** it still counts.
729
*/
730
int blob_linecount(Blob *p){
731
int n = 0;
732
int i;
733
for(i=0; i<p->nUsed; i++){
734
if( p->aData[i]=='\n' ) n++;
735
}
736
if( p->nUsed>0 && p->aData[p->nUsed-1]!='\n' ) n++;
737
return n;
738
}
739
740
/*
741
** Rewind the cursor on a blob back to the beginning.
742
*/
743
void blob_rewind(Blob *p){
744
p->iCursor = 0;
745
}
746
747
/*
748
** Truncate a blob to the specified length in bytes.
749
*/
750
void blob_truncate(Blob *p, int sz){
751
if( sz>=0 && sz<(int)(p->nUsed) ) p->nUsed = sz;
752
}
753
754
/*
755
** Truncate a blob to the specified length in bytes. If truncation
756
** results in an incomplete UTF-8 sequence at the end, remove up
757
** to three more bytes back to the last complete UTF-8 sequence.
758
*/
759
void blob_truncate_utf8(Blob *p, int sz){
760
if( sz>=0 && sz<(int)(p->nUsed) ){
761
p->nUsed = utf8_nearest_codepoint(p->aData,sz);
762
}
763
}
764
765
/*
766
** Seek the cursor in a blob to the indicated offset.
767
*/
768
int blob_seek(Blob *p, int offset, int whence){
769
if( whence==BLOB_SEEK_SET ){
770
p->iCursor = offset;
771
}else if( whence==BLOB_SEEK_CUR ){
772
p->iCursor += offset;
773
}
774
if( p->iCursor>p->nUsed ){
775
p->iCursor = p->nUsed;
776
}
777
return p->iCursor;
778
}
779
780
/*
781
** Return the current offset into the blob
782
*/
783
int blob_tell(Blob *p){
784
return p->iCursor;
785
}
786
787
/*
788
** Extract a single line of text from pFrom beginning at the current
789
** cursor location and use that line of text to initialize pTo.
790
** pTo will include the terminating \n. Return the number of bytes
791
** in the line including the \n at the end. 0 is returned at
792
** end-of-file.
793
**
794
** The cursor of pFrom is left pointing at the first byte past the
795
** \n that terminated the line.
796
**
797
** pTo will be an ephemeral blob. If pFrom changes, it might alter
798
** pTo as well.
799
*/
800
int blob_line(Blob *pFrom, Blob *pTo){
801
char *aData = pFrom->aData;
802
int n = pFrom->nUsed;
803
int i = pFrom->iCursor;
804
805
while( i<n && aData[i]!='\n' ){ i++; }
806
if( i<n ){
807
assert( aData[i]=='\n' );
808
i++;
809
}
810
blob_extract(pFrom, i-pFrom->iCursor, pTo);
811
return pTo->nUsed;
812
}
813
814
/*
815
** Trim whitespace off of the end of a blob. Return the number
816
** of characters remaining.
817
**
818
** All this does is reduce the length counter. This routine does
819
** not insert a new zero terminator.
820
*/
821
int blob_trim(Blob *p){
822
char *z = p->aData;
823
int n = p->nUsed;
824
while( n>0 && fossil_isspace(z[n-1]) ){ n--; }
825
p->nUsed = n;
826
return n;
827
}
828
829
/*
830
** Extract a single token from pFrom and use it to initialize pTo.
831
** Return the number of bytes in the token. If no token is found,
832
** return 0.
833
**
834
** A token consists of one or more non-space characters. Leading
835
** whitespace is ignored.
836
**
837
** The cursor of pFrom is left pointing at the first character past
838
** the end of the token.
839
**
840
** pTo will be an ephemeral blob. If pFrom changes, it might alter
841
** pTo as well.
842
*/
843
int blob_token(Blob *pFrom, Blob *pTo){
844
char *aData = pFrom->aData;
845
int n = pFrom->nUsed;
846
int i = pFrom->iCursor;
847
while( i<n && fossil_isspace(aData[i]) ){ i++; }
848
pFrom->iCursor = i;
849
while( i<n && !fossil_isspace(aData[i]) ){ i++; }
850
blob_extract(pFrom, i-pFrom->iCursor, pTo);
851
while( i<n && fossil_isspace(aData[i]) ){ i++; }
852
pFrom->iCursor = i;
853
return pTo->nUsed;
854
}
855
856
/*
857
** Extract a single SQL token from pFrom and use it to initialize pTo.
858
** Return the number of bytes in the token. If no token is found,
859
** return 0.
860
**
861
** An SQL token consists of one or more non-space characters. If the
862
** first character is ' then the token is terminated by a matching '
863
** (ignoring double '') or by the end of the string
864
**
865
** The cursor of pFrom is left pointing at the first character past
866
** the end of the token.
867
**
868
** pTo will be an ephemeral blob. If pFrom changes, it might alter
869
** pTo as well.
870
*/
871
int blob_sqltoken(Blob *pFrom, Blob *pTo){
872
char *aData = pFrom->aData;
873
int n = pFrom->nUsed;
874
int i = pFrom->iCursor;
875
while( i<n && fossil_isspace(aData[i]) ){ i++; }
876
pFrom->iCursor = i;
877
if( aData[i]=='\'' ){
878
i++;
879
while( i<n ){
880
if( aData[i]=='\'' ){
881
if( aData[++i]!='\'' ) break;
882
}
883
i++;
884
}
885
}else{
886
while( i<n && !fossil_isspace(aData[i]) ){ i++; }
887
}
888
blob_extract(pFrom, i-pFrom->iCursor, pTo);
889
while( i<n && fossil_isspace(aData[i]) ){ i++; }
890
pFrom->iCursor = i;
891
return pTo->nUsed;
892
}
893
894
/*
895
** Extract everything from the current cursor to the end of the blob
896
** into a new blob. The new blob is an ephemeral reference to the
897
** original blob. The cursor of the original blob is unchanged.
898
*/
899
int blob_tail(Blob *pFrom, Blob *pTo){
900
int iCursor = pFrom->iCursor;
901
blob_extract(pFrom, pFrom->nUsed-pFrom->iCursor, pTo);
902
pFrom->iCursor = iCursor;
903
return pTo->nUsed;
904
}
905
906
/*
907
** Copy N lines of text from pFrom into pTo. The copy begins at the
908
** current cursor position of pIn. The pIn cursor is left pointing
909
** at the first character past the last \n copied.
910
**
911
** If pTo==NULL then this routine simply skips over N lines.
912
*/
913
void blob_copy_lines(Blob *pTo, Blob *pFrom, int N){
914
char *z = pFrom->aData;
915
int i = pFrom->iCursor;
916
int n = pFrom->nUsed;
917
int cnt = 0;
918
919
if( N==0 ) return;
920
while( i<n ){
921
if( z[i]=='\n' ){
922
cnt++;
923
if( cnt==N ){
924
i++;
925
break;
926
}
927
}
928
i++;
929
}
930
if( pTo ){
931
blob_append(pTo, &pFrom->aData[pFrom->iCursor], i - pFrom->iCursor);
932
}
933
pFrom->iCursor = i;
934
}
935
936
/*
937
** Remove comment lines (starting with '#') from a blob pIn.
938
** Keep lines starting with "\#" but remove the initial backslash.
939
**
940
** Store the result in pOut. It is ok for pIn and pOut to be the same blob.
941
**
942
** pOut must either be the same as pIn or else uninitialized.
943
*/
944
void blob_strip_comment_lines(Blob *pIn, Blob *pOut){
945
char *z = pIn->aData;
946
unsigned int i = 0;
947
unsigned int n = pIn->nUsed;
948
unsigned int lineStart = 0;
949
unsigned int copyStart = 0;
950
int doCopy = 1;
951
Blob temp;
952
blob_zero(&temp);
953
954
while( i<n ){
955
if( i==lineStart && z[i]=='#' ){
956
copyStart = i;
957
doCopy = 0;
958
}else if( i==lineStart && z[i]=='\\' && z[i+1]=='#' ){
959
/* keep lines starting with an escaped '#' (and unescape it) */
960
copyStart = i + 1;
961
}
962
if( z[i]=='\n' ){
963
if( doCopy ) blob_append(&temp,&pIn->aData[copyStart], i - copyStart + 1);
964
lineStart = copyStart = i + 1;
965
doCopy = 1;
966
}
967
i++;
968
}
969
/* Last line */
970
if( doCopy ) blob_append(&temp, &pIn->aData[copyStart], i - copyStart);
971
972
if( pOut==pIn ) blob_reset(pOut);
973
*pOut = temp;
974
}
975
976
/*
977
** COMMAND: test-strip-comment-lines
978
**
979
** Usage: %fossil test-strip-comment-lines ?OPTIONS? INPUTFILE
980
**
981
** Read INPUTFILE and print it without comment lines (starting with '#').
982
** Keep lines starting with "\\#" but remove the initial backslash.
983
**
984
** This is used to test and debug the blob_strip_comment_lines() routine.
985
**
986
** Options:
987
** -y|--side-by-side Show diff of INPUTFILE and output side-by-side
988
** -W|--width N Width of lines in side-by-side diff
989
*/
990
void test_strip_comment_lines_cmd(void){
991
Blob f, h; /* uninitialized */
992
Blob out;
993
DiffConfig dCfg;
994
int sbs = 0;
995
const char *z;
996
int w = 0;
997
998
memset(&dCfg, 0, sizeof(dCfg));
999
1000
sbs = find_option("side-by-side","y",0)!=0;
1001
if( (z = find_option("width","W",1))!=0 && (w = atoi(z))>0 ){
1002
dCfg.wColumn = w;
1003
}
1004
verify_all_options();
1005
if( g.argc!=3 ) usage("INPUTFILE");
1006
1007
blob_read_from_file(&f, g.argv[2], ExtFILE);
1008
blob_strip_comment_lines(&f, &h);
1009
1010
if ( !sbs ){
1011
blob_write_to_file(&h, "-");
1012
}else{
1013
blob_zero(&out);
1014
dCfg.nContext = -1; /* whole content */
1015
dCfg.diffFlags = DIFF_SIDEBYSIDE | DIFF_CONTEXT_EX | DIFF_STRIP_EOLCR;
1016
diff_begin(&dCfg);
1017
text_diff(&f, &h, &out, &dCfg);
1018
blob_write_to_file(&out, "-");
1019
diff_end(&dCfg, 0);
1020
}
1021
}
1022
1023
/*
1024
** Ensure that the text in pBlob ends with '\n'
1025
*/
1026
void blob_add_final_newline(Blob *pBlob){
1027
if( pBlob->nUsed<=0 ) return;
1028
if( pBlob->aData[pBlob->nUsed-1]!='\n' ){
1029
blob_append_char(pBlob, '\n');
1030
}
1031
}
1032
1033
/*
1034
** Return true if the blob contains a valid base16 identifier artifact hash.
1035
**
1036
** The value returned is actually one of HNAME_SHA1 OR HNAME_K256 if the
1037
** hash is valid. Both of these are non-zero and therefore "true".
1038
** If the hash is not valid, then HNAME_ERROR is returned, which is zero or
1039
** false.
1040
*/
1041
int blob_is_hname(Blob *pBlob){
1042
return hname_validate(blob_buffer(pBlob), blob_size(pBlob));
1043
}
1044
1045
/*
1046
** Return true if the blob contains a valid filename
1047
*/
1048
int blob_is_filename(Blob *pBlob){
1049
return file_is_simple_pathname(blob_str(pBlob), 1);
1050
}
1051
1052
/*
1053
** Return true if the blob contains a valid non-negative 32-bit integer
1054
** and store the integer value in *pValue. If the blob is not a valid
1055
** non-negative 32-bit integer, return false and leave *pValue unchanged.
1056
*/
1057
int blob_is_int(Blob *pBlob, int *pValue){
1058
const char *z = blob_buffer(pBlob);
1059
int i, n, c;
1060
sqlite3_uint64 v;
1061
n = blob_size(pBlob);
1062
v = 0;
1063
for(i=0; i<n && (c = z[i])!=0 && c>='0' && c<='9'; i++){
1064
v = v*10 + c - '0';
1065
if( v>0x7fffffff ) return 0;
1066
}
1067
if( i==n ){
1068
*pValue = (int)v;
1069
return 1;
1070
}else{
1071
return 0;
1072
}
1073
}
1074
1075
/*
1076
** Return true if the blob contains a valid non-negative 64-bit integer
1077
** and store the integer value in *pValue. If the blob is not a valid
1078
** non-negative 64-bit integer, return false and leave *pValue unchanged.
1079
*/
1080
int blob_is_int64(Blob *pBlob, sqlite3_int64 *pValue){
1081
const char *z = blob_buffer(pBlob);
1082
int i, n, c;
1083
sqlite3_uint64 v;
1084
n = blob_size(pBlob);
1085
v = 0;
1086
for(i=0; i<n && (c = z[i])!=0 && c>='0' && c<='9'; i++){
1087
if( v<922337203685477580ULL || (v==922337203685477580ULL && c<='7') ){
1088
v = v*10 + c - '0';
1089
}else{
1090
return 0;
1091
}
1092
}
1093
if( i==n ){
1094
*pValue = (sqlite3_int64)v;
1095
return 1;
1096
}else{
1097
return 0;
1098
}
1099
}
1100
1101
/*
1102
** COMMAND: test-atoi
1103
**
1104
** Use the blob_is_int() and blob_is_int64() routines to convert arguments
1105
** into integers. Used for unit testing of those routines.
1106
*/
1107
void blob_is_int_cmd(void){
1108
int i;
1109
for(i=2; i<g.argc; i++){
1110
Blob x;
1111
int i32 = 0;
1112
sqlite3_int64 i64 = 0;
1113
int rc;
1114
blob_init(&x, g.argv[i], -1);
1115
rc = blob_is_int(&x, &i32);
1116
fossil_print("%20s: 32-bit %d %-10d", g.argv[i], rc, i32);
1117
rc = blob_is_int64(&x, &i64);
1118
fossil_print(" 64-bit %d %lld\n", rc, i64);
1119
}
1120
}
1121
1122
/*
1123
** Zero or reset an array of Blobs.
1124
*/
1125
void blobarray_zero(Blob *aBlob, int n){
1126
int i;
1127
for(i=0; i<n; i++) blob_zero(&aBlob[i]);
1128
}
1129
void blobarray_reset(Blob *aBlob, int n){
1130
int i;
1131
for(i=0; i<n; i++) blob_reset(&aBlob[i]);
1132
}
1133
/*
1134
** Allocate array of n blobs and initialize each element with `empty_blob`
1135
*/
1136
Blob* blobarray_new(int n){
1137
int i;
1138
Blob *aBlob = fossil_malloc(sizeof(Blob)*n);
1139
for(i=0; i<n; i++) aBlob[i] = empty_blob;
1140
return aBlob;
1141
}
1142
/*
1143
** Free array of n blobs some of which may be empty (have NULL buffer)
1144
*/
1145
void blobarray_delete(Blob *aBlob, int n){
1146
int i;
1147
for(i=0; i<n; i++){
1148
if( blob_buffer(aBlob+i) ) blob_reset(aBlob+i);
1149
}
1150
fossil_free(aBlob);
1151
}
1152
1153
/*
1154
** Parse a blob into space-separated tokens. Store each token in
1155
** an element of the blobarray aToken[]. aToken[] is nToken elements in
1156
** size. Return the number of tokens seen.
1157
*/
1158
int blob_tokenize(Blob *pIn, Blob *aToken, int nToken){
1159
int i;
1160
for(i=0; i<nToken && blob_token(pIn, &aToken[i]); i++){}
1161
return i;
1162
}
1163
1164
/*
1165
** Do printf-style string rendering and append the results to a blob. Or
1166
** if pBlob==0, do printf-style string rendering directly to stdout.
1167
**
1168
** The blob_appendf() version sets the BLOBFLAG_NotSQL bit in Blob.blobFlags
1169
** whereas blob_append_sql() does not.
1170
*/
1171
void blob_appendf(Blob *pBlob, const char *zFormat, ...){
1172
va_list ap;
1173
va_start(ap, zFormat);
1174
vxprintf(pBlob, zFormat, ap);
1175
va_end(ap);
1176
if( pBlob ) pBlob->blobFlags |= BLOBFLAG_NotSQL;
1177
}
1178
void blob_append_sql(Blob *pBlob, const char *zFormat, ...){
1179
va_list ap;
1180
va_start(ap, zFormat);
1181
vxprintf(pBlob, zFormat, ap);
1182
va_end(ap);
1183
}
1184
void blob_vappendf(Blob *pBlob, const char *zFormat, va_list ap){
1185
vxprintf(pBlob, zFormat, ap);
1186
}
1187
1188
/*
1189
** Initialize a blob to the data on an input channel. Return
1190
** the number of bytes read into the blob. Any prior content
1191
** of the blob is discarded, not freed.
1192
*/
1193
int blob_read_from_channel(Blob *pBlob, FILE *in, int nToRead){
1194
size_t n;
1195
blob_zero(pBlob);
1196
if( nToRead<0 ){
1197
char zBuf[10000];
1198
while( !feof(in) ){
1199
n = fread(zBuf, 1, sizeof(zBuf), in);
1200
if( n>0 ){
1201
blob_append(pBlob, zBuf, n);
1202
}
1203
}
1204
}else{
1205
blob_resize(pBlob, nToRead);
1206
n = fread(blob_buffer(pBlob), 1, nToRead, in);
1207
blob_resize(pBlob, n);
1208
}
1209
return blob_size(pBlob);
1210
}
1211
1212
/*
1213
** Initialize a blob to the data read from HTTP input. Return
1214
** the number of bytes read into the blob. Any prior content
1215
** of the blob is discarded, not freed.
1216
*/
1217
int blob_read_from_cgi(Blob *pBlob, int nToRead){
1218
size_t n;
1219
blob_zero(pBlob);
1220
if( nToRead<0 ){
1221
char zBuf[10000];
1222
while( !cgi_feof() ){
1223
n = cgi_fread(zBuf, sizeof(zBuf));
1224
if( n>0 ){
1225
blob_append(pBlob, zBuf, n);
1226
}
1227
}
1228
}else{
1229
blob_resize(pBlob, nToRead);
1230
n = cgi_fread(blob_buffer(pBlob), nToRead);
1231
blob_resize(pBlob, n);
1232
}
1233
return blob_size(pBlob);
1234
}
1235
1236
/*
1237
** Initialize a blob to be the content of a file. If the filename
1238
** is blank or "-" then read from standard input.
1239
**
1240
** If zFilename is a symbolic link, behavior depends on the eFType
1241
** parameter:
1242
**
1243
** * If eFType is ExtFILE or allow-symlinks is OFF, then the
1244
** pBlob is initialized to the *content* of the object to which
1245
** the zFilename symlink points.
1246
**
1247
** * If eFType is RepoFILE and allow-symlinks is ON, then the
1248
** pBlob is initialized to the *name* of the object to which
1249
** the zFilename symlink points.
1250
**
1251
** Any prior content of the blob is discarded, not freed.
1252
**
1253
** Return the number of bytes read. Calls fossil_fatal() on error (i.e.
1254
** it exit()s and does not return).
1255
*/
1256
sqlite3_int64 blob_read_from_file(
1257
Blob *pBlob, /* The blob to be initialized */
1258
const char *zFilename, /* Extract content from this file */
1259
int eFType /* ExtFILE or RepoFILE - see above */
1260
){
1261
sqlite3_int64 size, got;
1262
FILE *in;
1263
if( zFilename==0 || zFilename[0]==0
1264
|| (zFilename[0]=='-' && zFilename[1]==0) ){
1265
return blob_read_from_channel(pBlob, stdin, -1);
1266
}
1267
if( file_islink(zFilename) ){
1268
return blob_read_link(pBlob, zFilename);
1269
}
1270
size = file_size(zFilename, eFType);
1271
blob_zero(pBlob);
1272
if( size<0 ){
1273
fossil_fatal("no such file: %s", zFilename);
1274
}
1275
if( size==0 ){
1276
return 0;
1277
}
1278
blob_resize(pBlob, size);
1279
in = fossil_fopen(zFilename, "rb");
1280
if( in==0 ){
1281
fossil_fatal("cannot open %s for reading", zFilename);
1282
}
1283
got = fread(blob_buffer(pBlob), 1, size, in);
1284
fclose(in);
1285
if( got<size ){
1286
blob_resize(pBlob, got);
1287
}
1288
return got;
1289
}
1290
1291
/*
1292
** Reads symlink destination path and puts int into blob.
1293
** Any prior content of the blob is discarded, not freed.
1294
**
1295
** Returns length of destination path.
1296
**
1297
** On windows, zeros blob and returns 0.
1298
*/
1299
int blob_read_link(Blob *pBlob, const char *zFilename){
1300
#if !defined(_WIN32)
1301
char zBuf[1024];
1302
ssize_t len = readlink(zFilename, zBuf, 1023);
1303
if( len < 0 ){
1304
fossil_fatal("cannot read symbolic link %s", zFilename);
1305
}
1306
zBuf[len] = 0; /* null-terminate */
1307
blob_zero(pBlob);
1308
blob_appendf(pBlob, "%s", zBuf);
1309
return len;
1310
#else
1311
blob_zero(pBlob);
1312
return 0;
1313
#endif
1314
}
1315
1316
/*
1317
** Write the content of a blob into a file.
1318
**
1319
** If the filename is blank or "-" then write to standard output.
1320
**
1321
** This routine always assumes ExtFILE. If zFilename is a symbolic link
1322
** then the content is written into the object that symbolic link points
1323
** to, not into the symbolic link itself. This is true regardless of
1324
** the allow-symlinks setting.
1325
**
1326
** Return the number of bytes written.
1327
*/
1328
int blob_write_to_file(Blob *pBlob, const char *zFilename){
1329
FILE *out;
1330
int nWrote;
1331
1332
if( zFilename[0]==0 || (zFilename[0]=='-' && zFilename[1]==0) ){
1333
blob_is_init(pBlob);
1334
#if defined(_WIN32)
1335
nWrote = fossil_utf8_to_console(blob_buffer(pBlob), blob_size(pBlob), 0);
1336
if( nWrote>=0 ) return nWrote;
1337
fflush(stdout);
1338
_setmode(_fileno(stdout), _O_BINARY);
1339
#endif
1340
nWrote = fwrite(blob_buffer(pBlob), 1, blob_size(pBlob), stdout);
1341
#if defined(_WIN32)
1342
fflush(stdout);
1343
_setmode(_fileno(stdout), _O_TEXT);
1344
#endif
1345
}else{
1346
file_mkfolder(zFilename, ExtFILE, 1, 0);
1347
out = fossil_fopen(zFilename, "wb");
1348
if( out==0 ){
1349
#if defined(_WIN32)
1350
const char *zReserved = file_is_win_reserved(zFilename);
1351
if( zReserved ){
1352
fossil_fatal("cannot open \"%s\" because \"%s\" is "
1353
"a reserved name on Windows", zFilename, zReserved);
1354
}
1355
#endif
1356
fossil_fatal_recursive("unable to open file \"%s\" for writing",
1357
zFilename);
1358
return 0;
1359
}
1360
blob_is_init(pBlob);
1361
nWrote = fwrite(blob_buffer(pBlob), 1, blob_size(pBlob), out);
1362
fclose(out);
1363
if( nWrote!=(int)blob_size(pBlob) ){
1364
fossil_fatal_recursive("short write: %d of %d bytes to %s", nWrote,
1365
blob_size(pBlob), zFilename);
1366
}
1367
}
1368
return nWrote;
1369
}
1370
1371
/*
1372
** Compress a blob pIn. Store the result in pOut. It is ok for pIn and
1373
** pOut to be the same blob.
1374
**
1375
** pOut must either be the same as pIn or else uninitialized.
1376
*/
1377
void blob_compress(Blob *pIn, Blob *pOut){
1378
unsigned int nIn = blob_size(pIn);
1379
unsigned int nOut = 13 + nIn + (nIn+999)/1000;
1380
unsigned long int nOut2;
1381
unsigned char *outBuf;
1382
Blob temp;
1383
blob_zero(&temp);
1384
blob_resize(&temp, nOut+4);
1385
outBuf = (unsigned char*)blob_buffer(&temp);
1386
outBuf[0] = nIn>>24 & 0xff;
1387
outBuf[1] = nIn>>16 & 0xff;
1388
outBuf[2] = nIn>>8 & 0xff;
1389
outBuf[3] = nIn & 0xff;
1390
nOut2 = (long int)nOut;
1391
compress(&outBuf[4], &nOut2,
1392
(unsigned char*)blob_buffer(pIn), blob_size(pIn));
1393
if( pOut==pIn ) blob_reset(pOut);
1394
assert_blob_is_reset(pOut);
1395
*pOut = temp;
1396
blob_resize(pOut, nOut2+4);
1397
}
1398
1399
/*
1400
** COMMAND: test-compress
1401
**
1402
** Usage: %fossil test-compress INPUTFILE OUTPUTFILE
1403
**
1404
** Run compression on INPUTFILE and write the result into OUTPUTFILE.
1405
**
1406
** This is used to test and debug the blob_compress() routine.
1407
*/
1408
void compress_cmd(void){
1409
Blob f;
1410
if( g.argc!=4 ) usage("INPUTFILE OUTPUTFILE");
1411
blob_read_from_file(&f, g.argv[2], ExtFILE);
1412
blob_compress(&f, &f);
1413
blob_write_to_file(&f, g.argv[3]);
1414
}
1415
1416
/*
1417
** Compress the concatenation of a blobs pIn1 and pIn2. Store the result
1418
** in pOut.
1419
**
1420
** pOut must be either uninitialized or must be the same as either pIn1 or
1421
** pIn2.
1422
*/
1423
void blob_compress2(Blob *pIn1, Blob *pIn2, Blob *pOut){
1424
unsigned int nIn = blob_size(pIn1) + blob_size(pIn2);
1425
unsigned int nOut = 13 + nIn + (nIn+999)/1000;
1426
unsigned char *outBuf;
1427
z_stream stream;
1428
Blob temp;
1429
blob_zero(&temp);
1430
blob_resize(&temp, nOut+4);
1431
outBuf = (unsigned char*)blob_buffer(&temp);
1432
outBuf[0] = nIn>>24 & 0xff;
1433
outBuf[1] = nIn>>16 & 0xff;
1434
outBuf[2] = nIn>>8 & 0xff;
1435
outBuf[3] = nIn & 0xff;
1436
stream.zalloc = (alloc_func)0;
1437
stream.zfree = (free_func)0;
1438
stream.opaque = 0;
1439
stream.avail_out = nOut;
1440
stream.next_out = &outBuf[4];
1441
deflateInit(&stream, 9);
1442
stream.avail_in = blob_size(pIn1);
1443
stream.next_in = (unsigned char*)blob_buffer(pIn1);
1444
deflate(&stream, 0);
1445
stream.avail_in = blob_size(pIn2);
1446
stream.next_in = (unsigned char*)blob_buffer(pIn2);
1447
deflate(&stream, 0);
1448
deflate(&stream, Z_FINISH);
1449
blob_resize(&temp, stream.total_out + 4);
1450
deflateEnd(&stream);
1451
if( pOut==pIn1 ) blob_reset(pOut);
1452
if( pOut==pIn2 ) blob_reset(pOut);
1453
assert_blob_is_reset(pOut);
1454
*pOut = temp;
1455
}
1456
1457
/*
1458
** COMMAND: test-compress-2
1459
**
1460
** Usage: %fossil test-compress-2 IN1 IN2 OUT
1461
**
1462
** Read files IN1 and IN2, concatenate the content, compress the
1463
** content, then write results into OUT.
1464
**
1465
** This is used to test and debug the blob_compress2() routine.
1466
*/
1467
void compress2_cmd(void){
1468
Blob f1, f2;
1469
if( g.argc!=5 ) usage("INPUTFILE1 INPUTFILE2 OUTPUTFILE");
1470
blob_read_from_file(&f1, g.argv[2], ExtFILE);
1471
blob_read_from_file(&f2, g.argv[3], ExtFILE);
1472
blob_compress2(&f1, &f2, &f1);
1473
blob_write_to_file(&f1, g.argv[4]);
1474
}
1475
1476
/*
1477
** Uncompress blob pIn and store the result in pOut. It is ok for pIn and
1478
** pOut to be the same blob.
1479
**
1480
** pOut must be either uninitialized or the same as pIn.
1481
*/
1482
int blob_uncompress(Blob *pIn, Blob *pOut){
1483
unsigned int nOut;
1484
unsigned char *inBuf;
1485
unsigned int nIn = blob_size(pIn);
1486
Blob temp;
1487
int rc;
1488
unsigned long int nOut2;
1489
if( nIn<=4 ){
1490
return 0;
1491
}
1492
inBuf = (unsigned char*)blob_buffer(pIn);
1493
nOut = (inBuf[0]<<24) + (inBuf[1]<<16) + (inBuf[2]<<8) + inBuf[3];
1494
blob_zero(&temp);
1495
blob_resize(&temp, nOut+1);
1496
nOut2 = (long int)nOut;
1497
rc = uncompress((unsigned char*)blob_buffer(&temp), &nOut2,
1498
&inBuf[4], nIn - 4);
1499
if( rc!=Z_OK ){
1500
blob_reset(&temp);
1501
return 1;
1502
}
1503
blob_resize(&temp, nOut2);
1504
if( pOut==pIn ) blob_reset(pOut);
1505
assert_blob_is_reset(pOut);
1506
*pOut = temp;
1507
return 0;
1508
}
1509
1510
/*
1511
** COMMAND: test-uncompress
1512
**
1513
** Usage: %fossil test-uncompress IN OUT
1514
**
1515
** Read the content of file IN, uncompress that content, and write the
1516
** result into OUT. This command is intended for testing of the
1517
** blob_compress() function.
1518
*/
1519
void uncompress_cmd(void){
1520
Blob f;
1521
if( g.argc!=4 ) usage("INPUTFILE OUTPUTFILE");
1522
blob_read_from_file(&f, g.argv[2], ExtFILE);
1523
blob_uncompress(&f, &f);
1524
blob_write_to_file(&f, g.argv[3]);
1525
}
1526
1527
/*
1528
** COMMAND: test-cycle-compress
1529
**
1530
** Compress and uncompress each file named on the command line.
1531
** Verify that the original content is recovered.
1532
*/
1533
void test_cycle_compress(void){
1534
int i;
1535
Blob b1, b2, b3;
1536
for(i=2; i<g.argc; i++){
1537
blob_read_from_file(&b1, g.argv[i], ExtFILE);
1538
blob_compress(&b1, &b2);
1539
blob_uncompress(&b2, &b3);
1540
if( blob_compare(&b1, &b3) ){
1541
fossil_fatal("compress/uncompress cycle failed for %s", g.argv[i]);
1542
}
1543
blob_reset(&b1);
1544
blob_reset(&b2);
1545
blob_reset(&b3);
1546
}
1547
fossil_print("ok\n");
1548
}
1549
1550
/*
1551
** Convert every \n character in the given blob into \r\n.
1552
*/
1553
void blob_add_cr(Blob *p){
1554
char *z = p->aData;
1555
int j = p->nUsed;
1556
int i, n;
1557
for(i=n=0; i<j; i++){
1558
if( z[i]=='\n' ) n++;
1559
}
1560
j += n;
1561
if( j>=(int)(p->nAlloc) ){
1562
blob_resize(p, j);
1563
z = p->aData;
1564
}
1565
p->nUsed = j;
1566
z[j] = 0;
1567
while( j>i ){
1568
if( (z[--j] = z[--i]) =='\n' ){
1569
z[--j] = '\r';
1570
}
1571
}
1572
}
1573
1574
/*
1575
** Remove every \r character from the given blob, replacing each one with
1576
** a \n character if it was not already part of a \r\n pair.
1577
*/
1578
void blob_to_lf_only(Blob *p){
1579
int i, j;
1580
char *z = blob_materialize(p);
1581
for(i=j=0; z[i]; i++){
1582
if( z[i]!='\r' ) z[j++] = z[i];
1583
else if( z[i+1]!='\n' ) z[j++] = '\n';
1584
}
1585
z[j] = 0;
1586
p->nUsed = j;
1587
}
1588
1589
/*
1590
** Convert blob from cp1252 to UTF-8. As cp1252 is a superset
1591
** of iso8859-1, this is useful on UNIX as well.
1592
**
1593
** This table contains the character translations for 0x80..0xA0.
1594
*/
1595
1596
static const unsigned short cp1252[32] = {
1597
0x20ac, 0x81, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
1598
0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x8D, 0x017D, 0x8F,
1599
0x90, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
1600
0x2DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x9D, 0x017E, 0x0178
1601
};
1602
1603
void blob_cp1252_to_utf8(Blob *p){
1604
unsigned char *z = (unsigned char *)p->aData;
1605
int j = p->nUsed;
1606
int i, n;
1607
for(i=n=0; i<j; i++){
1608
if( z[i]>=0x80 ){
1609
if( (z[i]<0xa0) && (cp1252[z[i]&0x1f]>=0x800) ){
1610
n++;
1611
}
1612
n++;
1613
}
1614
}
1615
j += n;
1616
if( j>=(int)(p->nAlloc) ){
1617
blob_resize(p, j);
1618
z = (unsigned char *)p->aData;
1619
}
1620
p->nUsed = j;
1621
z[j] = 0;
1622
while( j>i ){
1623
if( z[--i]>=0x80 ){
1624
if( z[i]<0xa0 ){
1625
unsigned short sym = cp1252[z[i]&0x1f];
1626
if( sym>=0x800 ){
1627
z[--j] = 0x80 | (sym&0x3f);
1628
z[--j] = 0x80 | ((sym>>6)&0x3f);
1629
z[--j] = 0xe0 | (sym>>12);
1630
}else{
1631
z[--j] = 0x80 | (sym&0x3f);
1632
z[--j] = 0xc0 | (sym>>6);
1633
}
1634
}else{
1635
z[--j] = 0x80 | (z[i]&0x3f);
1636
z[--j] = 0xC0 | (z[i]>>6);
1637
}
1638
}else{
1639
z[--j] = z[i];
1640
}
1641
}
1642
}
1643
1644
/*
1645
** ASCII (for reference):
1646
** x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf
1647
** 0x ^` ^a ^b ^c ^d ^e ^f ^g \b \t \n () \f \r ^n ^o
1648
** 1x ^p ^q ^r ^s ^t ^u ^v ^w ^x ^y ^z ^{ ^| ^} ^~ ^
1649
** 2x () ! " # $ % & ' ( ) * + , - . /
1650
** 3x 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
1651
** 4x @ A B C D E F G H I J K L M N O
1652
** 5x P Q R S T U V W X Y Z [ \ ] ^ _
1653
** 6x ` a b c d e f g h i j k l m n o
1654
** 7x p q r s t u v w x y z { | } ~ ^_
1655
*/
1656
1657
/*
1658
** Meanings for bytes in a filename:
1659
**
1660
** 0 Ordinary character. No encoding required
1661
** 1 Needs to be escaped
1662
** 2 Illegal character. Do not allow in a filename
1663
** 3 First byte of a 2-byte UTF-8
1664
** 4 First byte of a 3-byte UTF-8
1665
** 5 First byte of a 4-byte UTF-8
1666
*/
1667
static const char aSafeChar[256] = {
1668
#ifdef _WIN32
1669
/* Windows
1670
** Prohibit: all control characters, including tab, \r and \n.
1671
** Escape: (space) " # $ % & ' ( ) * ; < > ? [ ] ^ ` { | }
1672
*/
1673
/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf */
1674
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0x */
1675
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 1x */
1676
1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 2x */
1677
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, /* 3x */
1678
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 4x */
1679
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, /* 5x */
1680
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 6x */
1681
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, /* 7x */
1682
#else
1683
/* Unix
1684
** Prohibit: all control characters, including tab, \r and \n
1685
** Escape: (space) ! " # $ % & ' ( ) * ; < > ? [ \ ] ^ ` { | }
1686
*/
1687
/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf */
1688
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0x */
1689
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 1x */
1690
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 2x */
1691
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, /* 3x */
1692
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 4x */
1693
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, /* 5x */
1694
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 6x */
1695
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, /* 7x */
1696
#endif
1697
/* all bytes 0x80 through 0xbf are unescaped, being secondary
1698
** bytes to UTF8 characters. Bytes 0xc0 through 0xff are the
1699
** first byte of a UTF8 character and do get escaped */
1700
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 8x */
1701
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 9x */
1702
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* ax */
1703
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* bx */
1704
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* cx */
1705
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* dx */
1706
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, /* ex */
1707
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 /* fx */
1708
};
1709
1710
/*
1711
** pBlob is a shell command under construction. This routine safely
1712
** appends filename argument zIn.
1713
**
1714
** The argument is escaped if it contains white space or other characters
1715
** that need to be escaped for the shell. If zIn contains characters
1716
** that cannot be safely escaped, then throw a fatal error.
1717
**
1718
** If the isFilename argument is true, then the argument is expected
1719
** to be a filename. As shell commands commonly have command-line
1720
** options that begin with "-" and since we do not want an attacker
1721
** to be able to invoke these switches using filenames that begin
1722
** with "-", if zIn begins with "-", prepend an additional "./"
1723
** (or ".\\" on Windows).
1724
*/
1725
void blob_append_escaped_arg(Blob *pBlob, const char *zIn, int isFilename){
1726
int i;
1727
unsigned char c;
1728
int needEscape = 0;
1729
int n = blob_size(pBlob);
1730
char *z = blob_buffer(pBlob);
1731
1732
/* Look for illegal byte-sequences and byte-sequences that require
1733
** escaping. No control-characters are allowed. All spaces and
1734
** non-ASCII unicode characters and some punctuation characters require
1735
** escaping. */
1736
for(i=0; (c = (unsigned char)zIn[i])!=0; i++){
1737
if( aSafeChar[c] ){
1738
unsigned char x = aSafeChar[c];
1739
needEscape = 1;
1740
if( x==2 ){
1741
Blob bad;
1742
blob_token(pBlob, &bad);
1743
fossil_fatal("the [%s] argument to the \"%s\" command contains "
1744
"a character (ascii 0x%02x) that is not allowed in "
1745
"filename arguments",
1746
zIn, blob_str(&bad), c);
1747
}else if( x>2 ){
1748
if( (zIn[i+1]&0xc0)!=0x80
1749
|| (x>=4 && (zIn[i+2]&0xc0)!=0x80)
1750
|| (x==5 && (zIn[i+3]&0xc0)!=0x80)
1751
){
1752
Blob bad;
1753
blob_token(pBlob, &bad);
1754
fossil_fatal("the [%s] argument to the \"%s\" command contains "
1755
"an illegal UTF-8 character",
1756
zIn, blob_str(&bad));
1757
}
1758
i += x-2;
1759
}
1760
}
1761
}
1762
1763
/* Separate from the previous argument by a space */
1764
if( n>0 && !fossil_isspace(z[n-1]) ){
1765
blob_append_char(pBlob, ' ');
1766
}
1767
1768
/* Check for characters that need quoting */
1769
if( !needEscape ){
1770
if( isFilename && zIn[0]=='-' ){
1771
blob_append_char(pBlob, '.');
1772
#if defined(_WIN32)
1773
blob_append_char(pBlob, '\\');
1774
#else
1775
blob_append_char(pBlob, '/');
1776
#endif
1777
}
1778
blob_append(pBlob, zIn, -1);
1779
}else{
1780
#if defined(_WIN32)
1781
/* Quoting strategy for windows:
1782
** Put the entire name inside of "...". Any " characters within
1783
** the name get doubled.
1784
*/
1785
blob_append_char(pBlob, '"');
1786
if( isFilename && zIn[0]=='-' ){
1787
blob_append_char(pBlob, '.');
1788
blob_append_char(pBlob, '\\');
1789
}else if( zIn[0]=='/' ){
1790
blob_append_char(pBlob, '.');
1791
}
1792
for(i=0; (c = (unsigned char)zIn[i])!=0; i++){
1793
blob_append_char(pBlob, (char)c);
1794
if( c=='"' ) blob_append_char(pBlob, '"');
1795
if( c=='\\' ) blob_append_char(pBlob, '\\');
1796
if( c=='%' && isFilename ) blob_append(pBlob, "%cd:~,%", 7);
1797
}
1798
blob_append_char(pBlob, '"');
1799
#else
1800
/* Quoting strategy for unix:
1801
** If the name does not contain ', then surround the whole thing
1802
** with '...'. If there is one or more ' characters within the
1803
** name, then put \ before each special character.
1804
*/
1805
if( strchr(zIn,'\'') ){
1806
if( isFilename && zIn[0]=='-' ){
1807
blob_append_char(pBlob, '.');
1808
blob_append_char(pBlob, '/');
1809
}
1810
for(i=0; (c = (unsigned char)zIn[i])!=0; i++){
1811
if( aSafeChar[c] && aSafeChar[c]!=2 ) blob_append_char(pBlob, '\\');
1812
blob_append_char(pBlob, (char)c);
1813
}
1814
}else{
1815
blob_append_char(pBlob, '\'');
1816
if( isFilename && zIn[0]=='-' ){
1817
blob_append_char(pBlob, '.');
1818
blob_append_char(pBlob, '/');
1819
}
1820
blob_append(pBlob, zIn, -1);
1821
blob_append_char(pBlob, '\'');
1822
}
1823
#endif
1824
}
1825
}
1826
1827
/*
1828
** COMMAND: test-escaped-arg
1829
**
1830
** Usage %fossil ARGS ...
1831
**
1832
** Run each argument through blob_append_escaped_arg() and show the
1833
** result. Append each argument to "fossil test-echo" and run that
1834
** using fossil_system() to verify that it really does get escaped
1835
** correctly.
1836
**
1837
** Other options:
1838
**
1839
** --filename-args BOOL Subsequent arguments are assumed to be
1840
** filenames if BOOL is true, or not if BOOL
1841
** is false. Defaults on.
1842
**
1843
** --hex HEX Skip the --hex flag and instead decode HEX
1844
** into ascii. This provides a way to insert
1845
** unusual characters as an argument for testing.
1846
**
1847
** --compare HEX ASCII Verify that argument ASCII is identical to
1848
** to decoded HEX.
1849
**
1850
** --fuzz N Run N fuzz cases. Each case is a call
1851
** to "fossil test-escaped-arg --compare HEX ARG"
1852
** where HEX and ARG are the same argument.
1853
** The argument is chosen at random.
1854
*/
1855
void test_escaped_arg_command(void){
1856
int i;
1857
Blob x;
1858
const char *zArg;
1859
int isFilename = 1;
1860
char zBuf[100];
1861
blob_init(&x, 0, 0);
1862
for(i=2; i<g.argc; i++){
1863
zArg = g.argv[i];
1864
if( fossil_strcmp(zArg, "--hex")==0 && i+1<g.argc ){
1865
size_t n = strlen(g.argv[++i]);
1866
if( n>=(sizeof(zBuf)-1)*2 ){
1867
fossil_fatal("Argument to --hex is too big");
1868
}
1869
memset(zBuf, 0, sizeof(zBuf));
1870
decode16((const unsigned char*)g.argv[i], (unsigned char*)zBuf, (int)n);
1871
zArg = zBuf;
1872
}else if( fossil_strcmp(zArg, "--compare")==0 && i+2<g.argc ){
1873
size_t n = strlen(g.argv[++i]);
1874
if( n>=(sizeof(zBuf)-1)*2 ){
1875
fossil_fatal("HEX argument to --compare is too big");
1876
}
1877
memset(zBuf, 0, sizeof(zBuf));
1878
if( decode16((const unsigned char*)g.argv[i], (unsigned char*)zBuf,
1879
(int)n) ){
1880
fossil_fatal("HEX decode of %s failed", g.argv[i]);
1881
}
1882
zArg = g.argv[++i];
1883
if( zArg[0]=='-' ){
1884
fossil_fatal("filename argument \"%s\" begins with \"-\"", zArg);
1885
}
1886
#ifdef _WIN32
1887
if( zBuf[0]=='-' && zArg[0]=='.' && zArg[1]=='\\' ) zArg += 2;
1888
#else
1889
if( zBuf[0]=='-' && zArg[0]=='.' && zArg[1]=='/' ) zArg += 2;
1890
#endif
1891
if( strcmp(zBuf, zArg)!=0 ){
1892
fossil_fatal("argument disagree: \"%s\" (%s) versus \"%s\"",
1893
zBuf, g.argv[i-1], zArg);
1894
}
1895
continue;
1896
}else if( fossil_strcmp(zArg, "--fuzz")==0 && i+1<g.argc ){
1897
int n = atoi(g.argv[++i]);
1898
int j;
1899
for(j=0; j<n; j++){
1900
unsigned char m, k;
1901
int rc;
1902
unsigned char zWord[100];
1903
sqlite3_randomness(sizeof(m), &m);
1904
m = (m%40)+5;
1905
sqlite3_randomness(m, zWord); /* Between 5 and 45 bytes of randomness */
1906
for(k=0; k<m; k++){
1907
unsigned char cx = zWord[k];
1908
if( cx<0x20 || cx>=0x7f ){
1909
/* Translate illegal bytes into various non-ASCII unicode
1910
** characters in order to exercise those code paths */
1911
unsigned int u;
1912
if( cx>=0x7f ){
1913
u = cx;
1914
}else if( cx>=0x08 ){
1915
u = 0x800 + cx;
1916
}else{
1917
u = 0x10000 + cx;
1918
}
1919
if( u<0x00080 ){
1920
zWord[k] = u & 0xFF;
1921
}else if( u<0x00800 ){
1922
zWord[k++] = 0xC0 + (u8)((u>>6)&0x1F);
1923
zWord[k] = 0x80 + (u8)(u & 0x3F);
1924
}else if( u<0x10000 ){
1925
zWord[k++] = 0xE0 + (u8)((u>>12)&0x0F);
1926
zWord[k++] = 0x80 + (u8)((u>>6) & 0x3F);
1927
zWord[k] = 0x80 + (u8)(u & 0x3F);
1928
}else{
1929
zWord[k++] = 0xF0 + (u8)((u>>18) & 0x07);
1930
zWord[k++] = 0x80 + (u8)((u>>12) & 0x3F);
1931
zWord[k++] = 0x80 + (u8)((u>>6) & 0x3F);
1932
zWord[k] = 0x80 + (u8)(u & 0x3F);
1933
}
1934
}
1935
}
1936
zWord[k] = 0;
1937
encode16(zWord, (unsigned char*)zBuf, (int)k);
1938
blob_appendf(&x, "%$ test-escaped-arg --compare %s %$",
1939
g.nameOfExe, zBuf,zWord);
1940
rc = fossil_system(blob_str(&x));
1941
if( rc ) fossil_fatal("failed test (%d): %s\n", rc, blob_str(&x));
1942
blob_reset(&x);
1943
}
1944
continue;
1945
}else if( fossil_strcmp(zArg, "--filename-args")==0 ){
1946
if( i+1<g.argc ){
1947
i++;
1948
isFilename = is_truth(g.argv[i]);
1949
}
1950
continue;
1951
}
1952
fossil_print("%3d [%s]: ", i, zArg);
1953
if( isFilename ){
1954
blob_appendf(&x, "%$ test-echo %$", g.nameOfExe, zArg);
1955
}else{
1956
blob_appendf(&x, "%$ test-echo %!$", g.nameOfExe, zArg);
1957
}
1958
fossil_print("%s\n", blob_str(&x));
1959
fossil_system(blob_str(&x));
1960
blob_reset(&x);
1961
}
1962
}
1963
1964
/*
1965
** A read(2)-like impl for the Blob class. Reads (copies) up to nLen
1966
** bytes from pIn, starting at position pIn->iCursor, and copies them
1967
** to pDest (which must be valid memory at least nLen bytes long).
1968
**
1969
** Returns the number of bytes read/copied, which may be less than
1970
** nLen (if end-of-blob is encountered).
1971
**
1972
** Updates pIn's cursor.
1973
**
1974
** Returns 0 if pIn contains no data.
1975
*/
1976
unsigned int blob_read(Blob *pIn, void * pDest, unsigned int nLen ){
1977
if( !pIn->aData || (pIn->iCursor >= pIn->nUsed) ){
1978
return 0;
1979
} else if( (pIn->iCursor + nLen) > (unsigned int)pIn->nUsed ){
1980
nLen = (unsigned int) (pIn->nUsed - pIn->iCursor);
1981
}
1982
assert( pIn->nUsed > pIn->iCursor );
1983
assert( (pIn->iCursor+nLen) <= pIn->nUsed );
1984
if( nLen ){
1985
memcpy( pDest, pIn->aData, nLen );
1986
pIn->iCursor += nLen;
1987
}
1988
return nLen;
1989
}
1990
1991
/*
1992
** Swaps the contents of the given blobs. Results
1993
** are unspecified if either value is NULL or both
1994
** point to the same blob.
1995
*/
1996
void blob_swap( Blob *pLeft, Blob *pRight ){
1997
Blob swap = *pLeft;
1998
*pLeft = *pRight;
1999
*pRight = swap;
2000
}
2001
2002
/*
2003
** Strip a possible byte-order-mark (BOM) from the blob. On Windows, if there
2004
** is either no BOM at all or an (le/be) UTF-16 BOM, a conversion to UTF-8 is
2005
** done. If useMbcs is false and there is no BOM, the input string is assumed
2006
** to be UTF-8 already, so no conversion is done.
2007
*/
2008
void blob_to_utf8_no_bom(Blob *pBlob, int useMbcs){
2009
char *zUtf8;
2010
int bomSize = 0;
2011
int bomReverse = 0;
2012
if( starts_with_utf8_bom(pBlob, &bomSize) ){
2013
struct Blob temp;
2014
zUtf8 = blob_str(pBlob) + bomSize;
2015
blob_zero(&temp);
2016
blob_append(&temp, zUtf8, -1);
2017
blob_swap(pBlob, &temp);
2018
blob_reset(&temp);
2019
}else if( starts_with_utf16_bom(pBlob, &bomSize, &bomReverse) ){
2020
zUtf8 = blob_buffer(pBlob);
2021
if( bomReverse ){
2022
/* Found BOM, but with reversed bytes */
2023
unsigned int i = blob_size(pBlob);
2024
while( i>1 ){
2025
/* swap bytes of unicode representation */
2026
char zTemp = zUtf8[--i];
2027
zUtf8[i] = zUtf8[i-1];
2028
zUtf8[--i] = zTemp;
2029
}
2030
}
2031
/* Make sure the blob contains two terminating 0-bytes */
2032
blob_append(pBlob, "\000\000", 3);
2033
zUtf8 = blob_str(pBlob) + bomSize;
2034
zUtf8 = fossil_unicode_to_utf8(zUtf8);
2035
blob_reset(pBlob);
2036
blob_set_dynamic(pBlob, zUtf8);
2037
}else if( useMbcs && invalid_utf8(pBlob) ){
2038
#if defined(_WIN32)
2039
zUtf8 = fossil_mbcs_to_utf8(blob_str(pBlob));
2040
blob_reset(pBlob);
2041
blob_append(pBlob, zUtf8, -1);
2042
fossil_mbcs_free(zUtf8);
2043
#else
2044
blob_cp1252_to_utf8(pBlob);
2045
#endif /* _WIN32 */
2046
}
2047
}
2048

Keyboard Shortcuts

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