Fossil SCM

fossil-scm / src / blob.c
Blame History Raw 2020 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*, unsigned int); /* 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(i64 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, unsigned int 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((i64)newSize);
221
pNew = fossil_realloc(pBlob->aData, newSize);
222
pBlob->aData = pNew;
223
pBlob->nAlloc = 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, unsigned int newSize){
243
if( newSize==0 ){
244
*pBlob = empty_blob;
245
}else{
246
char *pNew;
247
blob_assert_safe_size((i64)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 = 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
sqlite3_int64 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
sqlite3_int64 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, int n){
420
int 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, int n){
439
int 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, unsigned int 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, unsigned int newSize){
642
blob_assert_safe_size( (i64)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
/*
660
** Call dehttpize on a blob. This causes an ephemeral blob to be
661
** materialized.
662
*/
663
void blob_dehttpize(Blob *pBlob){
664
blob_materialize(pBlob);
665
pBlob->nUsed = dehttpize(pBlob->aData);
666
}
667
668
/*
669
** Extract N bytes from blob pFrom and use it to initialize blob pTo.
670
** Return the actual number of bytes extracted. The cursor position
671
** is advanced by the number of bytes extracted.
672
**
673
** After this call completes, pTo will be an ephemeral blob.
674
*/
675
int blob_extract(Blob *pFrom, int N, Blob *pTo){
676
blob_is_init(pFrom);
677
assert_blob_is_reset(pTo);
678
if( pFrom->iCursor + N > pFrom->nUsed ){
679
N = pFrom->nUsed - pFrom->iCursor;
680
if( N<=0 ){
681
blob_zero(pTo);
682
return 0;
683
}
684
}
685
pTo->nUsed = N;
686
pTo->nAlloc = N;
687
pTo->aData = &pFrom->aData[pFrom->iCursor];
688
pTo->iCursor = 0;
689
pTo->xRealloc = blobReallocStatic;
690
pFrom->iCursor += N;
691
return N;
692
}
693
694
/*
695
** Extract N **lines** of text from blob pFrom beginning at the current
696
** cursor position and use that text to initialize blob pTo. Unlike the
697
** blob_extract() routine, the cursor position is unchanged.
698
**
699
** pTo is assumed to be uninitialized.
700
**
701
** After this call completes, pTo will be an ephemeral blob.
702
*/
703
int blob_extract_lines(Blob *pFrom, int N, Blob *pTo){
704
int i;
705
int mx;
706
int iStart;
707
int n;
708
const char *z;
709
710
blob_zero(pTo);
711
z = pFrom->aData;
712
i = pFrom->iCursor;
713
mx = pFrom->nUsed;
714
while( N>0 ){
715
while( i<mx && z[i]!='\n' ){ i++; }
716
if( i>=mx ) break;
717
i++;
718
N--;
719
}
720
iStart = pFrom->iCursor;
721
n = blob_extract(pFrom, i-pFrom->iCursor, pTo);
722
pFrom->iCursor = iStart;
723
return n;
724
}
725
726
/*
727
** Return the number of lines of text in the blob. If the last
728
** line is incomplete (if it does not have a \n at the end) then
729
** it still counts.
730
*/
731
int blob_linecount(Blob *p){
732
int n = 0;
733
int i;
734
for(i=0; i<p->nUsed; i++){
735
if( p->aData[i]=='\n' ) n++;
736
}
737
if( p->nUsed>0 && p->aData[p->nUsed-1]!='\n' ) n++;
738
return n;
739
}
740
741
/*
742
** Rewind the cursor on a blob back to the beginning.
743
*/
744
void blob_rewind(Blob *p){
745
p->iCursor = 0;
746
}
747
748
/*
749
** Truncate a blob to the specified length in bytes.
750
*/
751
void blob_truncate(Blob *p, int sz){
752
if( sz>=0 && sz<(int)(p->nUsed) ) p->nUsed = sz;
753
}
754
755
/*
756
** Truncate a blob to the specified length in bytes. If truncation
757
** results in an incomplete UTF-8 sequence at the end, remove up
758
** to three more bytes back to the last complete UTF-8 sequence.
759
*/
760
void blob_truncate_utf8(Blob *p, int sz){
761
if( sz>=0 && sz<(int)(p->nUsed) ){
762
p->nUsed = utf8_nearest_codepoint(p->aData,sz);
763
}
764
}
765
766
/*
767
** Seek the cursor in a blob to the indicated offset.
768
*/
769
int blob_seek(Blob *p, int offset, int whence){
770
if( whence==BLOB_SEEK_SET ){
771
p->iCursor = offset;
772
}else if( whence==BLOB_SEEK_CUR ){
773
p->iCursor += offset;
774
}
775
if( p->iCursor>p->nUsed ){
776
p->iCursor = p->nUsed;
777
}
778
return p->iCursor;
779
}
780
781
/*
782
** Return the current offset into the blob
783
*/
784
int blob_tell(Blob *p){
785
return p->iCursor;
786
}
787
788
/*
789
** Extract a single line of text from pFrom beginning at the current
790
** cursor location and use that line of text to initialize pTo.
791
** pTo will include the terminating \n. Return the number of bytes
792
** in the line including the \n at the end. 0 is returned at
793
** end-of-file.
794
**
795
** The cursor of pFrom is left pointing at the first byte past the
796
** \n that terminated the line.
797
**
798
** pTo will be an ephemeral blob. If pFrom changes, it might alter
799
** pTo as well.
800
*/
801
int blob_line(Blob *pFrom, Blob *pTo){
802
char *aData = pFrom->aData;
803
int n = pFrom->nUsed;
804
int i = pFrom->iCursor;
805
806
while( i<n && aData[i]!='\n' ){ i++; }
807
if( i<n ){
808
assert( aData[i]=='\n' );
809
i++;
810
}
811
blob_extract(pFrom, i-pFrom->iCursor, pTo);
812
return pTo->nUsed;
813
}
814
815
/*
816
** Trim whitespace off of the end of a blob. Return the number
817
** of characters remaining.
818
**
819
** All this does is reduce the length counter. This routine does
820
** not insert a new zero terminator.
821
*/
822
int blob_trim(Blob *p){
823
char *z = p->aData;
824
int n = p->nUsed;
825
while( n>0 && fossil_isspace(z[n-1]) ){ n--; }
826
p->nUsed = n;
827
return n;
828
}
829
830
/*
831
** Extract a single token from pFrom and use it to initialize pTo.
832
** Return the number of bytes in the token. If no token is found,
833
** return 0.
834
**
835
** A token consists of one or more non-space characters. Leading
836
** whitespace is ignored.
837
**
838
** The cursor of pFrom is left pointing at the first character past
839
** the end of the token.
840
**
841
** pTo will be an ephemeral blob. If pFrom changes, it might alter
842
** pTo as well.
843
*/
844
int blob_token(Blob *pFrom, Blob *pTo){
845
char *aData = pFrom->aData;
846
int n = pFrom->nUsed;
847
int i = pFrom->iCursor;
848
while( i<n && fossil_isspace(aData[i]) ){ i++; }
849
pFrom->iCursor = i;
850
while( i<n && !fossil_isspace(aData[i]) ){ i++; }
851
blob_extract(pFrom, i-pFrom->iCursor, pTo);
852
while( i<n && fossil_isspace(aData[i]) ){ i++; }
853
pFrom->iCursor = i;
854
return pTo->nUsed;
855
}
856
857
/*
858
** Extract a single SQL token from pFrom and use it to initialize pTo.
859
** Return the number of bytes in the token. If no token is found,
860
** return 0.
861
**
862
** An SQL token consists of one or more non-space characters. If the
863
** first character is ' then the token is terminated by a matching '
864
** (ignoring double '') or by the end of the string
865
**
866
** The cursor of pFrom is left pointing at the first character past
867
** the end of the token.
868
**
869
** pTo will be an ephemeral blob. If pFrom changes, it might alter
870
** pTo as well.
871
*/
872
int blob_sqltoken(Blob *pFrom, Blob *pTo){
873
char *aData = pFrom->aData;
874
int n = pFrom->nUsed;
875
int i = pFrom->iCursor;
876
while( i<n && fossil_isspace(aData[i]) ){ i++; }
877
pFrom->iCursor = i;
878
if( aData[i]=='\'' ){
879
i++;
880
while( i<n ){
881
if( aData[i]=='\'' ){
882
if( aData[++i]!='\'' ) break;
883
}
884
i++;
885
}
886
}else{
887
while( i<n && !fossil_isspace(aData[i]) ){ i++; }
888
}
889
blob_extract(pFrom, i-pFrom->iCursor, pTo);
890
while( i<n && fossil_isspace(aData[i]) ){ i++; }
891
pFrom->iCursor = i;
892
return pTo->nUsed;
893
}
894
895
/*
896
** Extract everything from the current cursor to the end of the blob
897
** into a new blob. The new blob is an ephemeral reference to the
898
** original blob. The cursor of the original blob is unchanged.
899
*/
900
int blob_tail(Blob *pFrom, Blob *pTo){
901
int iCursor = pFrom->iCursor;
902
blob_extract(pFrom, pFrom->nUsed-pFrom->iCursor, pTo);
903
pFrom->iCursor = iCursor;
904
return pTo->nUsed;
905
}
906
907
/*
908
** Copy N lines of text from pFrom into pTo. The copy begins at the
909
** current cursor position of pIn. The pIn cursor is left pointing
910
** at the first character past the last \n copied.
911
**
912
** If pTo==NULL then this routine simply skips over N lines.
913
*/
914
void blob_copy_lines(Blob *pTo, Blob *pFrom, int N){
915
char *z = pFrom->aData;
916
int i = pFrom->iCursor;
917
int n = pFrom->nUsed;
918
int cnt = 0;
919
920
if( N==0 ) return;
921
while( i<n ){
922
if( z[i]=='\n' ){
923
cnt++;
924
if( cnt==N ){
925
i++;
926
break;
927
}
928
}
929
i++;
930
}
931
if( pTo ){
932
blob_append(pTo, &pFrom->aData[pFrom->iCursor], i - pFrom->iCursor);
933
}
934
pFrom->iCursor = i;
935
}
936
937
/*
938
** Remove comment lines (starting with '#') from a blob pIn.
939
** Keep lines starting with "\#" but remove the initial backslash.
940
**
941
** Store the result in pOut. It is ok for pIn and pOut to be the same blob.
942
**
943
** pOut must either be the same as pIn or else uninitialized.
944
*/
945
void blob_strip_comment_lines(Blob *pIn, Blob *pOut){
946
char *z = pIn->aData;
947
unsigned int i = 0;
948
unsigned int n = pIn->nUsed;
949
unsigned int lineStart = 0;
950
unsigned int copyStart = 0;
951
int doCopy = 1;
952
Blob temp;
953
blob_zero(&temp);
954
955
while( i<n ){
956
if( i==lineStart && z[i]=='#' ){
957
copyStart = i;
958
doCopy = 0;
959
}else if( i==lineStart && z[i]=='\\' && z[i+1]=='#' ){
960
/* keep lines starting with an escaped '#' (and unescape it) */
961
copyStart = i + 1;
962
}
963
if( z[i]=='\n' ){
964
if( doCopy ) blob_append(&temp,&pIn->aData[copyStart], i - copyStart + 1);
965
lineStart = copyStart = i + 1;
966
doCopy = 1;
967
}
968
i++;
969
}
970
/* Last line */
971
if( doCopy ) blob_append(&temp, &pIn->aData[copyStart], i - copyStart);
972
973
if( pOut==pIn ) blob_reset(pOut);
974
*pOut = temp;
975
}
976
977
/*
978
** COMMAND: test-strip-comment-lines
979
**
980
** Usage: %fossil test-strip-comment-lines ?OPTIONS? INPUTFILE
981
**
982
** Read INPUTFILE and print it without comment lines (starting with '#').
983
** Keep lines starting with "\\#" but remove the initial backslash.
984
**
985
** This is used to test and debug the blob_strip_comment_lines() routine.
986
**
987
** Options:
988
** -y|--side-by-side Show diff of INPUTFILE and output side-by-side
989
** -W|--width N Width of lines in side-by-side diff
990
*/
991
void test_strip_comment_lines_cmd(void){
992
Blob f, h; /* uninitialized */
993
Blob out;
994
DiffConfig dCfg;
995
int sbs = 0;
996
const char *z;
997
int w = 0;
998
999
memset(&dCfg, 0, sizeof(dCfg));
1000
1001
sbs = find_option("side-by-side","y",0)!=0;
1002
if( (z = find_option("width","W",1))!=0 && (w = atoi(z))>0 ){
1003
dCfg.wColumn = w;
1004
}
1005
verify_all_options();
1006
if( g.argc!=3 ) usage("INPUTFILE");
1007
1008
blob_read_from_file(&f, g.argv[2], ExtFILE);
1009
blob_strip_comment_lines(&f, &h);
1010
1011
if ( !sbs ){
1012
blob_write_to_file(&h, "-");
1013
}else{
1014
blob_zero(&out);
1015
dCfg.nContext = -1; /* whole content */
1016
dCfg.diffFlags = DIFF_SIDEBYSIDE | DIFF_CONTEXT_EX | DIFF_STRIP_EOLCR;
1017
diff_begin(&dCfg);
1018
text_diff(&f, &h, &out, &dCfg);
1019
blob_write_to_file(&out, "-");
1020
diff_end(&dCfg, 0);
1021
}
1022
}
1023
1024
/*
1025
** Ensure that the text in pBlob ends with '\n'
1026
*/
1027
void blob_add_final_newline(Blob *pBlob){
1028
if( pBlob->nUsed<=0 ) return;
1029
if( pBlob->aData[pBlob->nUsed-1]!='\n' ){
1030
blob_append_char(pBlob, '\n');
1031
}
1032
}
1033
1034
/*
1035
** Return true if the blob contains a valid base16 identifier artifact hash.
1036
**
1037
** The value returned is actually one of HNAME_SHA1 OR HNAME_K256 if the
1038
** hash is valid. Both of these are non-zero and therefore "true".
1039
** If the hash is not valid, then HNAME_ERROR is returned, which is zero or
1040
** false.
1041
*/
1042
int blob_is_hname(Blob *pBlob){
1043
return hname_validate(blob_buffer(pBlob), blob_size(pBlob));
1044
}
1045
1046
/*
1047
** Return true if the blob contains a valid filename
1048
*/
1049
int blob_is_filename(Blob *pBlob){
1050
return file_is_simple_pathname(blob_str(pBlob), 1);
1051
}
1052
1053
/*
1054
** Return true if the blob contains a valid 32-bit integer. Store
1055
** the integer value in *pValue.
1056
*/
1057
int blob_is_int(Blob *pBlob, int *pValue){
1058
const char *z = blob_buffer(pBlob);
1059
int i, n, c, v;
1060
n = blob_size(pBlob);
1061
v = 0;
1062
for(i=0; i<n && (c = z[i])!=0 && c>='0' && c<='9'; i++){
1063
v = v*10 + c - '0';
1064
}
1065
if( i==n ){
1066
*pValue = v;
1067
return 1;
1068
}else{
1069
return 0;
1070
}
1071
}
1072
1073
/*
1074
** Return true if the blob contains a valid 64-bit integer. Store
1075
** the integer value in *pValue.
1076
*/
1077
int blob_is_int64(Blob *pBlob, sqlite3_int64 *pValue){
1078
const char *z = blob_buffer(pBlob);
1079
int i, n, c;
1080
sqlite3_int64 v;
1081
n = blob_size(pBlob);
1082
v = 0;
1083
for(i=0; i<n && (c = z[i])!=0 && c>='0' && c<='9'; i++){
1084
v = v*10 + c - '0';
1085
}
1086
if( i==n ){
1087
*pValue = v;
1088
return 1;
1089
}else{
1090
return 0;
1091
}
1092
}
1093
1094
/*
1095
** Zero or reset an array of Blobs.
1096
*/
1097
void blobarray_zero(Blob *aBlob, int n){
1098
int i;
1099
for(i=0; i<n; i++) blob_zero(&aBlob[i]);
1100
}
1101
void blobarray_reset(Blob *aBlob, int n){
1102
int i;
1103
for(i=0; i<n; i++) blob_reset(&aBlob[i]);
1104
}
1105
/*
1106
** Allocate array of n blobs and initialize each element with `empty_blob`
1107
*/
1108
Blob* blobarray_new(int n){
1109
int i;
1110
Blob *aBlob = fossil_malloc(sizeof(Blob)*n);
1111
for(i=0; i<n; i++) aBlob[i] = empty_blob;
1112
return aBlob;
1113
}
1114
/*
1115
** Free array of n blobs some of which may be empty (have NULL buffer)
1116
*/
1117
void blobarray_delete(Blob *aBlob, int n){
1118
int i;
1119
for(i=0; i<n; i++){
1120
if( blob_buffer(aBlob+i) ) blob_reset(aBlob+i);
1121
}
1122
fossil_free(aBlob);
1123
}
1124
1125
/*
1126
** Parse a blob into space-separated tokens. Store each token in
1127
** an element of the blobarray aToken[]. aToken[] is nToken elements in
1128
** size. Return the number of tokens seen.
1129
*/
1130
int blob_tokenize(Blob *pIn, Blob *aToken, int nToken){
1131
int i;
1132
for(i=0; i<nToken && blob_token(pIn, &aToken[i]); i++){}
1133
return i;
1134
}
1135
1136
/*
1137
** Do printf-style string rendering and append the results to a blob. Or
1138
** if pBlob==0, do printf-style string rendering directly to stdout.
1139
**
1140
** The blob_appendf() version sets the BLOBFLAG_NotSQL bit in Blob.blobFlags
1141
** whereas blob_append_sql() does not.
1142
*/
1143
void blob_appendf(Blob *pBlob, const char *zFormat, ...){
1144
va_list ap;
1145
va_start(ap, zFormat);
1146
vxprintf(pBlob, zFormat, ap);
1147
va_end(ap);
1148
if( pBlob ) pBlob->blobFlags |= BLOBFLAG_NotSQL;
1149
}
1150
void blob_append_sql(Blob *pBlob, const char *zFormat, ...){
1151
va_list ap;
1152
va_start(ap, zFormat);
1153
vxprintf(pBlob, zFormat, ap);
1154
va_end(ap);
1155
}
1156
void blob_vappendf(Blob *pBlob, const char *zFormat, va_list ap){
1157
vxprintf(pBlob, zFormat, ap);
1158
}
1159
1160
/*
1161
** Initialize a blob to the data on an input channel. Return
1162
** the number of bytes read into the blob. Any prior content
1163
** of the blob is discarded, not freed.
1164
*/
1165
int blob_read_from_channel(Blob *pBlob, FILE *in, int nToRead){
1166
size_t n;
1167
blob_zero(pBlob);
1168
if( nToRead<0 ){
1169
char zBuf[10000];
1170
while( !feof(in) ){
1171
n = fread(zBuf, 1, sizeof(zBuf), in);
1172
if( n>0 ){
1173
blob_append(pBlob, zBuf, n);
1174
}
1175
}
1176
}else{
1177
blob_resize(pBlob, nToRead);
1178
n = fread(blob_buffer(pBlob), 1, nToRead, in);
1179
blob_resize(pBlob, n);
1180
}
1181
return blob_size(pBlob);
1182
}
1183
1184
/*
1185
** Initialize a blob to the data read from HTTP input. Return
1186
** the number of bytes read into the blob. Any prior content
1187
** of the blob is discarded, not freed.
1188
*/
1189
int blob_read_from_cgi(Blob *pBlob, int nToRead){
1190
size_t n;
1191
blob_zero(pBlob);
1192
if( nToRead<0 ){
1193
char zBuf[10000];
1194
while( !cgi_feof() ){
1195
n = cgi_fread(zBuf, sizeof(zBuf));
1196
if( n>0 ){
1197
blob_append(pBlob, zBuf, n);
1198
}
1199
}
1200
}else{
1201
blob_resize(pBlob, nToRead);
1202
n = cgi_fread(blob_buffer(pBlob), nToRead);
1203
blob_resize(pBlob, n);
1204
}
1205
return blob_size(pBlob);
1206
}
1207
1208
/*
1209
** Initialize a blob to be the content of a file. If the filename
1210
** is blank or "-" then read from standard input.
1211
**
1212
** If zFilename is a symbolic link, behavior depends on the eFType
1213
** parameter:
1214
**
1215
** * If eFType is ExtFILE or allow-symlinks is OFF, then the
1216
** pBlob is initialized to the *content* of the object to which
1217
** the zFilename symlink points.
1218
**
1219
** * If eFType is RepoFILE and allow-symlinks is ON, then the
1220
** pBlob is initialized to the *name* of the object to which
1221
** the zFilename symlink points.
1222
**
1223
** Any prior content of the blob is discarded, not freed.
1224
**
1225
** Return the number of bytes read. Calls fossil_fatal() on error (i.e.
1226
** it exit()s and does not return).
1227
*/
1228
sqlite3_int64 blob_read_from_file(
1229
Blob *pBlob, /* The blob to be initialized */
1230
const char *zFilename, /* Extract content from this file */
1231
int eFType /* ExtFILE or RepoFILE - see above */
1232
){
1233
sqlite3_int64 size, got;
1234
FILE *in;
1235
if( zFilename==0 || zFilename[0]==0
1236
|| (zFilename[0]=='-' && zFilename[1]==0) ){
1237
return blob_read_from_channel(pBlob, stdin, -1);
1238
}
1239
if( file_islink(zFilename) ){
1240
return blob_read_link(pBlob, zFilename);
1241
}
1242
size = file_size(zFilename, eFType);
1243
blob_zero(pBlob);
1244
if( size<0 ){
1245
fossil_fatal("no such file: %s", zFilename);
1246
}
1247
if( size==0 ){
1248
return 0;
1249
}
1250
blob_resize(pBlob, size);
1251
in = fossil_fopen(zFilename, "rb");
1252
if( in==0 ){
1253
fossil_fatal("cannot open %s for reading", zFilename);
1254
}
1255
got = fread(blob_buffer(pBlob), 1, size, in);
1256
fclose(in);
1257
if( got<size ){
1258
blob_resize(pBlob, got);
1259
}
1260
return got;
1261
}
1262
1263
/*
1264
** Reads symlink destination path and puts int into blob.
1265
** Any prior content of the blob is discarded, not freed.
1266
**
1267
** Returns length of destination path.
1268
**
1269
** On windows, zeros blob and returns 0.
1270
*/
1271
int blob_read_link(Blob *pBlob, const char *zFilename){
1272
#if !defined(_WIN32)
1273
char zBuf[1024];
1274
ssize_t len = readlink(zFilename, zBuf, 1023);
1275
if( len < 0 ){
1276
fossil_fatal("cannot read symbolic link %s", zFilename);
1277
}
1278
zBuf[len] = 0; /* null-terminate */
1279
blob_zero(pBlob);
1280
blob_appendf(pBlob, "%s", zBuf);
1281
return len;
1282
#else
1283
blob_zero(pBlob);
1284
return 0;
1285
#endif
1286
}
1287
1288
/*
1289
** Write the content of a blob into a file.
1290
**
1291
** If the filename is blank or "-" then write to standard output.
1292
**
1293
** This routine always assumes ExtFILE. If zFilename is a symbolic link
1294
** then the content is written into the object that symbolic link points
1295
** to, not into the symbolic link itself. This is true regardless of
1296
** the allow-symlinks setting.
1297
**
1298
** Return the number of bytes written.
1299
*/
1300
int blob_write_to_file(Blob *pBlob, const char *zFilename){
1301
FILE *out;
1302
int nWrote;
1303
1304
if( zFilename[0]==0 || (zFilename[0]=='-' && zFilename[1]==0) ){
1305
blob_is_init(pBlob);
1306
#if defined(_WIN32)
1307
nWrote = fossil_utf8_to_console(blob_buffer(pBlob), blob_size(pBlob), 0);
1308
if( nWrote>=0 ) return nWrote;
1309
fflush(stdout);
1310
_setmode(_fileno(stdout), _O_BINARY);
1311
#endif
1312
nWrote = fwrite(blob_buffer(pBlob), 1, blob_size(pBlob), stdout);
1313
#if defined(_WIN32)
1314
fflush(stdout);
1315
_setmode(_fileno(stdout), _O_TEXT);
1316
#endif
1317
}else{
1318
file_mkfolder(zFilename, ExtFILE, 1, 0);
1319
out = fossil_fopen(zFilename, "wb");
1320
if( out==0 ){
1321
#if defined(_WIN32)
1322
const char *zReserved = file_is_win_reserved(zFilename);
1323
if( zReserved ){
1324
fossil_fatal("cannot open \"%s\" because \"%s\" is "
1325
"a reserved name on Windows", zFilename, zReserved);
1326
}
1327
#endif
1328
fossil_fatal_recursive("unable to open file \"%s\" for writing",
1329
zFilename);
1330
return 0;
1331
}
1332
blob_is_init(pBlob);
1333
nWrote = fwrite(blob_buffer(pBlob), 1, blob_size(pBlob), out);
1334
fclose(out);
1335
if( nWrote!=(int)blob_size(pBlob) ){
1336
fossil_fatal_recursive("short write: %d of %d bytes to %s", nWrote,
1337
blob_size(pBlob), zFilename);
1338
}
1339
}
1340
return nWrote;
1341
}
1342
1343
/*
1344
** Compress a blob pIn. Store the result in pOut. It is ok for pIn and
1345
** pOut to be the same blob.
1346
**
1347
** pOut must either be the same as pIn or else uninitialized.
1348
*/
1349
void blob_compress(Blob *pIn, Blob *pOut){
1350
unsigned int nIn = blob_size(pIn);
1351
unsigned int nOut = 13 + nIn + (nIn+999)/1000;
1352
unsigned long int nOut2;
1353
unsigned char *outBuf;
1354
Blob temp;
1355
blob_zero(&temp);
1356
blob_resize(&temp, nOut+4);
1357
outBuf = (unsigned char*)blob_buffer(&temp);
1358
outBuf[0] = nIn>>24 & 0xff;
1359
outBuf[1] = nIn>>16 & 0xff;
1360
outBuf[2] = nIn>>8 & 0xff;
1361
outBuf[3] = nIn & 0xff;
1362
nOut2 = (long int)nOut;
1363
compress(&outBuf[4], &nOut2,
1364
(unsigned char*)blob_buffer(pIn), blob_size(pIn));
1365
if( pOut==pIn ) blob_reset(pOut);
1366
assert_blob_is_reset(pOut);
1367
*pOut = temp;
1368
blob_resize(pOut, nOut2+4);
1369
}
1370
1371
/*
1372
** COMMAND: test-compress
1373
**
1374
** Usage: %fossil test-compress INPUTFILE OUTPUTFILE
1375
**
1376
** Run compression on INPUTFILE and write the result into OUTPUTFILE.
1377
**
1378
** This is used to test and debug the blob_compress() routine.
1379
*/
1380
void compress_cmd(void){
1381
Blob f;
1382
if( g.argc!=4 ) usage("INPUTFILE OUTPUTFILE");
1383
blob_read_from_file(&f, g.argv[2], ExtFILE);
1384
blob_compress(&f, &f);
1385
blob_write_to_file(&f, g.argv[3]);
1386
}
1387
1388
/*
1389
** Compress the concatenation of a blobs pIn1 and pIn2. Store the result
1390
** in pOut.
1391
**
1392
** pOut must be either uninitialized or must be the same as either pIn1 or
1393
** pIn2.
1394
*/
1395
void blob_compress2(Blob *pIn1, Blob *pIn2, Blob *pOut){
1396
unsigned int nIn = blob_size(pIn1) + blob_size(pIn2);
1397
unsigned int nOut = 13 + nIn + (nIn+999)/1000;
1398
unsigned char *outBuf;
1399
z_stream stream;
1400
Blob temp;
1401
blob_zero(&temp);
1402
blob_resize(&temp, nOut+4);
1403
outBuf = (unsigned char*)blob_buffer(&temp);
1404
outBuf[0] = nIn>>24 & 0xff;
1405
outBuf[1] = nIn>>16 & 0xff;
1406
outBuf[2] = nIn>>8 & 0xff;
1407
outBuf[3] = nIn & 0xff;
1408
stream.zalloc = (alloc_func)0;
1409
stream.zfree = (free_func)0;
1410
stream.opaque = 0;
1411
stream.avail_out = nOut;
1412
stream.next_out = &outBuf[4];
1413
deflateInit(&stream, 9);
1414
stream.avail_in = blob_size(pIn1);
1415
stream.next_in = (unsigned char*)blob_buffer(pIn1);
1416
deflate(&stream, 0);
1417
stream.avail_in = blob_size(pIn2);
1418
stream.next_in = (unsigned char*)blob_buffer(pIn2);
1419
deflate(&stream, 0);
1420
deflate(&stream, Z_FINISH);
1421
blob_resize(&temp, stream.total_out + 4);
1422
deflateEnd(&stream);
1423
if( pOut==pIn1 ) blob_reset(pOut);
1424
if( pOut==pIn2 ) blob_reset(pOut);
1425
assert_blob_is_reset(pOut);
1426
*pOut = temp;
1427
}
1428
1429
/*
1430
** COMMAND: test-compress-2
1431
**
1432
** Usage: %fossil test-compress-2 IN1 IN2 OUT
1433
**
1434
** Read files IN1 and IN2, concatenate the content, compress the
1435
** content, then write results into OUT.
1436
**
1437
** This is used to test and debug the blob_compress2() routine.
1438
*/
1439
void compress2_cmd(void){
1440
Blob f1, f2;
1441
if( g.argc!=5 ) usage("INPUTFILE1 INPUTFILE2 OUTPUTFILE");
1442
blob_read_from_file(&f1, g.argv[2], ExtFILE);
1443
blob_read_from_file(&f2, g.argv[3], ExtFILE);
1444
blob_compress2(&f1, &f2, &f1);
1445
blob_write_to_file(&f1, g.argv[4]);
1446
}
1447
1448
/*
1449
** Uncompress blob pIn and store the result in pOut. It is ok for pIn and
1450
** pOut to be the same blob.
1451
**
1452
** pOut must be either uninitialized or the same as pIn.
1453
*/
1454
int blob_uncompress(Blob *pIn, Blob *pOut){
1455
unsigned int nOut;
1456
unsigned char *inBuf;
1457
unsigned int nIn = blob_size(pIn);
1458
Blob temp;
1459
int rc;
1460
unsigned long int nOut2;
1461
if( nIn<=4 ){
1462
return 0;
1463
}
1464
inBuf = (unsigned char*)blob_buffer(pIn);
1465
nOut = (inBuf[0]<<24) + (inBuf[1]<<16) + (inBuf[2]<<8) + inBuf[3];
1466
blob_zero(&temp);
1467
blob_resize(&temp, nOut+1);
1468
nOut2 = (long int)nOut;
1469
rc = uncompress((unsigned char*)blob_buffer(&temp), &nOut2,
1470
&inBuf[4], nIn - 4);
1471
if( rc!=Z_OK ){
1472
blob_reset(&temp);
1473
return 1;
1474
}
1475
blob_resize(&temp, nOut2);
1476
if( pOut==pIn ) blob_reset(pOut);
1477
assert_blob_is_reset(pOut);
1478
*pOut = temp;
1479
return 0;
1480
}
1481
1482
/*
1483
** COMMAND: test-uncompress
1484
**
1485
** Usage: %fossil test-uncompress IN OUT
1486
**
1487
** Read the content of file IN, uncompress that content, and write the
1488
** result into OUT. This command is intended for testing of the
1489
** blob_compress() function.
1490
*/
1491
void uncompress_cmd(void){
1492
Blob f;
1493
if( g.argc!=4 ) usage("INPUTFILE OUTPUTFILE");
1494
blob_read_from_file(&f, g.argv[2], ExtFILE);
1495
blob_uncompress(&f, &f);
1496
blob_write_to_file(&f, g.argv[3]);
1497
}
1498
1499
/*
1500
** COMMAND: test-cycle-compress
1501
**
1502
** Compress and uncompress each file named on the command line.
1503
** Verify that the original content is recovered.
1504
*/
1505
void test_cycle_compress(void){
1506
int i;
1507
Blob b1, b2, b3;
1508
for(i=2; i<g.argc; i++){
1509
blob_read_from_file(&b1, g.argv[i], ExtFILE);
1510
blob_compress(&b1, &b2);
1511
blob_uncompress(&b2, &b3);
1512
if( blob_compare(&b1, &b3) ){
1513
fossil_fatal("compress/uncompress cycle failed for %s", g.argv[i]);
1514
}
1515
blob_reset(&b1);
1516
blob_reset(&b2);
1517
blob_reset(&b3);
1518
}
1519
fossil_print("ok\n");
1520
}
1521
1522
/*
1523
** Convert every \n character in the given blob into \r\n.
1524
*/
1525
void blob_add_cr(Blob *p){
1526
char *z = p->aData;
1527
int j = p->nUsed;
1528
int i, n;
1529
for(i=n=0; i<j; i++){
1530
if( z[i]=='\n' ) n++;
1531
}
1532
j += n;
1533
if( j>=(int)(p->nAlloc) ){
1534
blob_resize(p, j);
1535
z = p->aData;
1536
}
1537
p->nUsed = j;
1538
z[j] = 0;
1539
while( j>i ){
1540
if( (z[--j] = z[--i]) =='\n' ){
1541
z[--j] = '\r';
1542
}
1543
}
1544
}
1545
1546
/*
1547
** Remove every \r character from the given blob, replacing each one with
1548
** a \n character if it was not already part of a \r\n pair.
1549
*/
1550
void blob_to_lf_only(Blob *p){
1551
int i, j;
1552
char *z = blob_materialize(p);
1553
for(i=j=0; z[i]; i++){
1554
if( z[i]!='\r' ) z[j++] = z[i];
1555
else if( z[i+1]!='\n' ) z[j++] = '\n';
1556
}
1557
z[j] = 0;
1558
p->nUsed = j;
1559
}
1560
1561
/*
1562
** Convert blob from cp1252 to UTF-8. As cp1252 is a superset
1563
** of iso8859-1, this is useful on UNIX as well.
1564
**
1565
** This table contains the character translations for 0x80..0xA0.
1566
*/
1567
1568
static const unsigned short cp1252[32] = {
1569
0x20ac, 0x81, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
1570
0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x8D, 0x017D, 0x8F,
1571
0x90, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
1572
0x2DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x9D, 0x017E, 0x0178
1573
};
1574
1575
void blob_cp1252_to_utf8(Blob *p){
1576
unsigned char *z = (unsigned char *)p->aData;
1577
int j = p->nUsed;
1578
int i, n;
1579
for(i=n=0; i<j; i++){
1580
if( z[i]>=0x80 ){
1581
if( (z[i]<0xa0) && (cp1252[z[i]&0x1f]>=0x800) ){
1582
n++;
1583
}
1584
n++;
1585
}
1586
}
1587
j += n;
1588
if( j>=(int)(p->nAlloc) ){
1589
blob_resize(p, j);
1590
z = (unsigned char *)p->aData;
1591
}
1592
p->nUsed = j;
1593
z[j] = 0;
1594
while( j>i ){
1595
if( z[--i]>=0x80 ){
1596
if( z[i]<0xa0 ){
1597
unsigned short sym = cp1252[z[i]&0x1f];
1598
if( sym>=0x800 ){
1599
z[--j] = 0x80 | (sym&0x3f);
1600
z[--j] = 0x80 | ((sym>>6)&0x3f);
1601
z[--j] = 0xe0 | (sym>>12);
1602
}else{
1603
z[--j] = 0x80 | (sym&0x3f);
1604
z[--j] = 0xc0 | (sym>>6);
1605
}
1606
}else{
1607
z[--j] = 0x80 | (z[i]&0x3f);
1608
z[--j] = 0xC0 | (z[i]>>6);
1609
}
1610
}else{
1611
z[--j] = z[i];
1612
}
1613
}
1614
}
1615
1616
/*
1617
** ASCII (for reference):
1618
** x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf
1619
** 0x ^` ^a ^b ^c ^d ^e ^f ^g \b \t \n () \f \r ^n ^o
1620
** 1x ^p ^q ^r ^s ^t ^u ^v ^w ^x ^y ^z ^{ ^| ^} ^~ ^
1621
** 2x () ! " # $ % & ' ( ) * + , - . /
1622
** 3x 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
1623
** 4x @ A B C D E F G H I J K L M N O
1624
** 5x P Q R S T U V W X Y Z [ \ ] ^ _
1625
** 6x ` a b c d e f g h i j k l m n o
1626
** 7x p q r s t u v w x y z { | } ~ ^_
1627
*/
1628
1629
/*
1630
** Meanings for bytes in a filename:
1631
**
1632
** 0 Ordinary character. No encoding required
1633
** 1 Needs to be escaped
1634
** 2 Illegal character. Do not allow in a filename
1635
** 3 First byte of a 2-byte UTF-8
1636
** 4 First byte of a 3-byte UTF-8
1637
** 5 First byte of a 4-byte UTF-8
1638
*/
1639
static const char aSafeChar[256] = {
1640
#ifdef _WIN32
1641
/* Windows
1642
** Prohibit: all control characters, including tab, \r and \n.
1643
** Escape: (space) " # $ % & ' ( ) * ; < > ? [ ] ^ ` { | }
1644
*/
1645
/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf */
1646
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0x */
1647
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 1x */
1648
1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 2x */
1649
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, /* 3x */
1650
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 4x */
1651
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, /* 5x */
1652
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 6x */
1653
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, /* 7x */
1654
#else
1655
/* Unix
1656
** Prohibit: all control characters, including tab, \r and \n
1657
** Escape: (space) ! " # $ % & ' ( ) * ; < > ? [ \ ] ^ ` { | }
1658
*/
1659
/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf */
1660
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0x */
1661
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 1x */
1662
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 2x */
1663
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, /* 3x */
1664
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 4x */
1665
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, /* 5x */
1666
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 6x */
1667
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, /* 7x */
1668
#endif
1669
/* all bytes 0x80 through 0xbf are unescaped, being secondary
1670
** bytes to UTF8 characters. Bytes 0xc0 through 0xff are the
1671
** first byte of a UTF8 character and do get escaped */
1672
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 8x */
1673
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 9x */
1674
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* ax */
1675
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* bx */
1676
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* cx */
1677
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* dx */
1678
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, /* ex */
1679
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 /* fx */
1680
};
1681
1682
/*
1683
** pBlob is a shell command under construction. This routine safely
1684
** appends filename argument zIn.
1685
**
1686
** The argument is escaped if it contains white space or other characters
1687
** that need to be escaped for the shell. If zIn contains characters
1688
** that cannot be safely escaped, then throw a fatal error.
1689
**
1690
** If the isFilename argument is true, then the argument is expected
1691
** to be a filename. As shell commands commonly have command-line
1692
** options that begin with "-" and since we do not want an attacker
1693
** to be able to invoke these switches using filenames that begin
1694
** with "-", if zIn begins with "-", prepend an additional "./"
1695
** (or ".\\" on Windows).
1696
*/
1697
void blob_append_escaped_arg(Blob *pBlob, const char *zIn, int isFilename){
1698
int i;
1699
unsigned char c;
1700
int needEscape = 0;
1701
int n = blob_size(pBlob);
1702
char *z = blob_buffer(pBlob);
1703
1704
/* Look for illegal byte-sequences and byte-sequences that require
1705
** escaping. No control-characters are allowed. All spaces and
1706
** non-ASCII unicode characters and some punctuation characters require
1707
** escaping. */
1708
for(i=0; (c = (unsigned char)zIn[i])!=0; i++){
1709
if( aSafeChar[c] ){
1710
unsigned char x = aSafeChar[c];
1711
needEscape = 1;
1712
if( x==2 ){
1713
Blob bad;
1714
blob_token(pBlob, &bad);
1715
fossil_fatal("the [%s] argument to the \"%s\" command contains "
1716
"a character (ascii 0x%02x) that is not allowed in "
1717
"filename arguments",
1718
zIn, blob_str(&bad), c);
1719
}else if( x>2 ){
1720
if( (zIn[i+1]&0xc0)!=0x80
1721
|| (x>=4 && (zIn[i+2]&0xc0)!=0x80)
1722
|| (x==5 && (zIn[i+3]&0xc0)!=0x80)
1723
){
1724
Blob bad;
1725
blob_token(pBlob, &bad);
1726
fossil_fatal("the [%s] argument to the \"%s\" command contains "
1727
"an illegal UTF-8 character",
1728
zIn, blob_str(&bad));
1729
}
1730
i += x-2;
1731
}
1732
}
1733
}
1734
1735
/* Separate from the previous argument by a space */
1736
if( n>0 && !fossil_isspace(z[n-1]) ){
1737
blob_append_char(pBlob, ' ');
1738
}
1739
1740
/* Check for characters that need quoting */
1741
if( !needEscape ){
1742
if( isFilename && zIn[0]=='-' ){
1743
blob_append_char(pBlob, '.');
1744
#if defined(_WIN32)
1745
blob_append_char(pBlob, '\\');
1746
#else
1747
blob_append_char(pBlob, '/');
1748
#endif
1749
}
1750
blob_append(pBlob, zIn, -1);
1751
}else{
1752
#if defined(_WIN32)
1753
/* Quoting strategy for windows:
1754
** Put the entire name inside of "...". Any " characters within
1755
** the name get doubled.
1756
*/
1757
blob_append_char(pBlob, '"');
1758
if( isFilename && zIn[0]=='-' ){
1759
blob_append_char(pBlob, '.');
1760
blob_append_char(pBlob, '\\');
1761
}else if( zIn[0]=='/' ){
1762
blob_append_char(pBlob, '.');
1763
}
1764
for(i=0; (c = (unsigned char)zIn[i])!=0; i++){
1765
blob_append_char(pBlob, (char)c);
1766
if( c=='"' ) blob_append_char(pBlob, '"');
1767
if( c=='\\' ) blob_append_char(pBlob, '\\');
1768
if( c=='%' && isFilename ) blob_append(pBlob, "%cd:~,%", 7);
1769
}
1770
blob_append_char(pBlob, '"');
1771
#else
1772
/* Quoting strategy for unix:
1773
** If the name does not contain ', then surround the whole thing
1774
** with '...'. If there is one or more ' characters within the
1775
** name, then put \ before each special character.
1776
*/
1777
if( strchr(zIn,'\'') ){
1778
if( isFilename && zIn[0]=='-' ){
1779
blob_append_char(pBlob, '.');
1780
blob_append_char(pBlob, '/');
1781
}
1782
for(i=0; (c = (unsigned char)zIn[i])!=0; i++){
1783
if( aSafeChar[c] && aSafeChar[c]!=2 ) blob_append_char(pBlob, '\\');
1784
blob_append_char(pBlob, (char)c);
1785
}
1786
}else{
1787
blob_append_char(pBlob, '\'');
1788
if( isFilename && zIn[0]=='-' ){
1789
blob_append_char(pBlob, '.');
1790
blob_append_char(pBlob, '/');
1791
}
1792
blob_append(pBlob, zIn, -1);
1793
blob_append_char(pBlob, '\'');
1794
}
1795
#endif
1796
}
1797
}
1798
1799
/*
1800
** COMMAND: test-escaped-arg
1801
**
1802
** Usage %fossil ARGS ...
1803
**
1804
** Run each argument through blob_append_escaped_arg() and show the
1805
** result. Append each argument to "fossil test-echo" and run that
1806
** using fossil_system() to verify that it really does get escaped
1807
** correctly.
1808
**
1809
** Other options:
1810
**
1811
** --filename-args BOOL Subsequent arguments are assumed to be
1812
** filenames if BOOL is true, or not if BOOL
1813
** is false. Defaults on.
1814
**
1815
** --hex HEX Skip the --hex flag and instead decode HEX
1816
** into ascii. This provides a way to insert
1817
** unusual characters as an argument for testing.
1818
**
1819
** --compare HEX ASCII Verify that argument ASCII is identical to
1820
** to decoded HEX.
1821
**
1822
** --fuzz N Run N fuzz cases. Each case is a call
1823
** to "fossil test-escaped-arg --compare HEX ARG"
1824
** where HEX and ARG are the same argument.
1825
** The argument is chosen at random.
1826
*/
1827
void test_escaped_arg_command(void){
1828
int i;
1829
Blob x;
1830
const char *zArg;
1831
int isFilename = 1;
1832
char zBuf[100];
1833
blob_init(&x, 0, 0);
1834
for(i=2; i<g.argc; i++){
1835
zArg = g.argv[i];
1836
if( fossil_strcmp(zArg, "--hex")==0 && i+1<g.argc ){
1837
size_t n = strlen(g.argv[++i]);
1838
if( n>=(sizeof(zBuf)-1)*2 ){
1839
fossil_fatal("Argument to --hex is too big");
1840
}
1841
memset(zBuf, 0, sizeof(zBuf));
1842
decode16((const unsigned char*)g.argv[i], (unsigned char*)zBuf, (int)n);
1843
zArg = zBuf;
1844
}else if( fossil_strcmp(zArg, "--compare")==0 && i+2<g.argc ){
1845
size_t n = strlen(g.argv[++i]);
1846
if( n>=(sizeof(zBuf)-1)*2 ){
1847
fossil_fatal("HEX argument to --compare is too big");
1848
}
1849
memset(zBuf, 0, sizeof(zBuf));
1850
if( decode16((const unsigned char*)g.argv[i], (unsigned char*)zBuf,
1851
(int)n) ){
1852
fossil_fatal("HEX decode of %s failed", g.argv[i]);
1853
}
1854
zArg = g.argv[++i];
1855
if( zArg[0]=='-' ){
1856
fossil_fatal("filename argument \"%s\" begins with \"-\"", zArg);
1857
}
1858
#ifdef _WIN32
1859
if( zBuf[0]=='-' && zArg[0]=='.' && zArg[1]=='\\' ) zArg += 2;
1860
#else
1861
if( zBuf[0]=='-' && zArg[0]=='.' && zArg[1]=='/' ) zArg += 2;
1862
#endif
1863
if( strcmp(zBuf, zArg)!=0 ){
1864
fossil_fatal("argument disagree: \"%s\" (%s) versus \"%s\"",
1865
zBuf, g.argv[i-1], zArg);
1866
}
1867
continue;
1868
}else if( fossil_strcmp(zArg, "--fuzz")==0 && i+1<g.argc ){
1869
int n = atoi(g.argv[++i]);
1870
int j;
1871
for(j=0; j<n; j++){
1872
unsigned char m, k;
1873
int rc;
1874
unsigned char zWord[100];
1875
sqlite3_randomness(sizeof(m), &m);
1876
m = (m%40)+5;
1877
sqlite3_randomness(m, zWord); /* Between 5 and 45 bytes of randomness */
1878
for(k=0; k<m; k++){
1879
unsigned char cx = zWord[k];
1880
if( cx<0x20 || cx>=0x7f ){
1881
/* Translate illegal bytes into various non-ASCII unicode
1882
** characters in order to exercise those code paths */
1883
unsigned int u;
1884
if( cx>=0x7f ){
1885
u = cx;
1886
}else if( cx>=0x08 ){
1887
u = 0x800 + cx;
1888
}else{
1889
u = 0x10000 + cx;
1890
}
1891
if( u<0x00080 ){
1892
zWord[k] = u & 0xFF;
1893
}else if( u<0x00800 ){
1894
zWord[k++] = 0xC0 + (u8)((u>>6)&0x1F);
1895
zWord[k] = 0x80 + (u8)(u & 0x3F);
1896
}else if( u<0x10000 ){
1897
zWord[k++] = 0xE0 + (u8)((u>>12)&0x0F);
1898
zWord[k++] = 0x80 + (u8)((u>>6) & 0x3F);
1899
zWord[k] = 0x80 + (u8)(u & 0x3F);
1900
}else{
1901
zWord[k++] = 0xF0 + (u8)((u>>18) & 0x07);
1902
zWord[k++] = 0x80 + (u8)((u>>12) & 0x3F);
1903
zWord[k++] = 0x80 + (u8)((u>>6) & 0x3F);
1904
zWord[k] = 0x80 + (u8)(u & 0x3F);
1905
}
1906
}
1907
}
1908
zWord[k] = 0;
1909
encode16(zWord, (unsigned char*)zBuf, (int)k);
1910
blob_appendf(&x, "%$ test-escaped-arg --compare %s %$",
1911
g.nameOfExe, zBuf,zWord);
1912
rc = fossil_system(blob_str(&x));
1913
if( rc ) fossil_fatal("failed test (%d): %s\n", rc, blob_str(&x));
1914
blob_reset(&x);
1915
}
1916
continue;
1917
}else if( fossil_strcmp(zArg, "--filename-args")==0 ){
1918
if( i+1<g.argc ){
1919
i++;
1920
isFilename = is_truth(g.argv[i]);
1921
}
1922
continue;
1923
}
1924
fossil_print("%3d [%s]: ", i, zArg);
1925
if( isFilename ){
1926
blob_appendf(&x, "%$ test-echo %$", g.nameOfExe, zArg);
1927
}else{
1928
blob_appendf(&x, "%$ test-echo %!$", g.nameOfExe, zArg);
1929
}
1930
fossil_print("%s\n", blob_str(&x));
1931
fossil_system(blob_str(&x));
1932
blob_reset(&x);
1933
}
1934
}
1935
1936
/*
1937
** A read(2)-like impl for the Blob class. Reads (copies) up to nLen
1938
** bytes from pIn, starting at position pIn->iCursor, and copies them
1939
** to pDest (which must be valid memory at least nLen bytes long).
1940
**
1941
** Returns the number of bytes read/copied, which may be less than
1942
** nLen (if end-of-blob is encountered).
1943
**
1944
** Updates pIn's cursor.
1945
**
1946
** Returns 0 if pIn contains no data.
1947
*/
1948
unsigned int blob_read(Blob *pIn, void * pDest, unsigned int nLen ){
1949
if( !pIn->aData || (pIn->iCursor >= pIn->nUsed) ){
1950
return 0;
1951
} else if( (pIn->iCursor + nLen) > (unsigned int)pIn->nUsed ){
1952
nLen = (unsigned int) (pIn->nUsed - pIn->iCursor);
1953
}
1954
assert( pIn->nUsed > pIn->iCursor );
1955
assert( (pIn->iCursor+nLen) <= pIn->nUsed );
1956
if( nLen ){
1957
memcpy( pDest, pIn->aData, nLen );
1958
pIn->iCursor += nLen;
1959
}
1960
return nLen;
1961
}
1962
1963
/*
1964
** Swaps the contents of the given blobs. Results
1965
** are unspecified if either value is NULL or both
1966
** point to the same blob.
1967
*/
1968
void blob_swap( Blob *pLeft, Blob *pRight ){
1969
Blob swap = *pLeft;
1970
*pLeft = *pRight;
1971
*pRight = swap;
1972
}
1973
1974
/*
1975
** Strip a possible byte-order-mark (BOM) from the blob. On Windows, if there
1976
** is either no BOM at all or an (le/be) UTF-16 BOM, a conversion to UTF-8 is
1977
** done. If useMbcs is false and there is no BOM, the input string is assumed
1978
** to be UTF-8 already, so no conversion is done.
1979
*/
1980
void blob_to_utf8_no_bom(Blob *pBlob, int useMbcs){
1981
char *zUtf8;
1982
int bomSize = 0;
1983
int bomReverse = 0;
1984
if( starts_with_utf8_bom(pBlob, &bomSize) ){
1985
struct Blob temp;
1986
zUtf8 = blob_str(pBlob) + bomSize;
1987
blob_zero(&temp);
1988
blob_append(&temp, zUtf8, -1);
1989
blob_swap(pBlob, &temp);
1990
blob_reset(&temp);
1991
}else if( starts_with_utf16_bom(pBlob, &bomSize, &bomReverse) ){
1992
zUtf8 = blob_buffer(pBlob);
1993
if( bomReverse ){
1994
/* Found BOM, but with reversed bytes */
1995
unsigned int i = blob_size(pBlob);
1996
while( i>1 ){
1997
/* swap bytes of unicode representation */
1998
char zTemp = zUtf8[--i];
1999
zUtf8[i] = zUtf8[i-1];
2000
zUtf8[--i] = zTemp;
2001
}
2002
}
2003
/* Make sure the blob contains two terminating 0-bytes */
2004
blob_append(pBlob, "\000\000", 3);
2005
zUtf8 = blob_str(pBlob) + bomSize;
2006
zUtf8 = fossil_unicode_to_utf8(zUtf8);
2007
blob_reset(pBlob);
2008
blob_set_dynamic(pBlob, zUtf8);
2009
}else if( useMbcs && invalid_utf8(pBlob) ){
2010
#if defined(_WIN32)
2011
zUtf8 = fossil_mbcs_to_utf8(blob_str(pBlob));
2012
blob_reset(pBlob);
2013
blob_append(pBlob, zUtf8, -1);
2014
fossil_mbcs_free(zUtf8);
2015
#else
2016
blob_cp1252_to_utf8(pBlob);
2017
#endif /* _WIN32 */
2018
}
2019
}
2020

Keyboard Shortcuts

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