Fossil SCM

Converted "fossil diff" and "fossil gdiff" to the new array-based setting mechanism. The legacy system(3) style shell command string methods are still available for use with --command and legacy single-string settings, but if you pass 2+ values to "fossil setting diff-command VALUES..." (ditto gdiff-command) it's now stored as an array, triggering this new mechanism. This permits old settings to continue working, letting you upgrade to the new mechanism at will.

wyoung 2021-06-22 08:34 fossil-spawn
Commit 68055fd3edc7c6447e44fad2a70d7ef3109ebec28ac21acda617517774c94056
+2 -2
--- src/checkin.c
+++ src/checkin.c
@@ -1358,19 +1358,19 @@
13581358
break;
13591359
}
13601360
diffFiles[i].nName = strlen(diffFiles[i].zName);
13611361
diffFiles[i].nUsed = 0;
13621362
}
1363
- diff_against_disk(0, 0, diff_get_binary_glob(),
1363
+ diff_against_disk(0, 0, 0, diff_get_binary_glob(),
13641364
db_get_boolean("diff-binary", 1),
13651365
DIFF_VERBOSE, diffFiles, &prompt);
13661366
for( i=0; diffFiles[i].zName; ++i ){
13671367
fossil_free(diffFiles[i].zName);
13681368
}
13691369
fossil_free(diffFiles);
13701370
}else{
1371
- diff_against_disk(0, 0, diff_get_binary_glob(),
1371
+ diff_against_disk(0, 0, 0, diff_get_binary_glob(),
13721372
db_get_boolean("diff-binary", 1),
13731373
DIFF_VERBOSE, 0, &prompt);
13741374
}
13751375
}
13761376
prompt_for_user_comment(pComment, &prompt);
13771377
--- src/checkin.c
+++ src/checkin.c
@@ -1358,19 +1358,19 @@
1358 break;
1359 }
1360 diffFiles[i].nName = strlen(diffFiles[i].zName);
1361 diffFiles[i].nUsed = 0;
1362 }
1363 diff_against_disk(0, 0, diff_get_binary_glob(),
1364 db_get_boolean("diff-binary", 1),
1365 DIFF_VERBOSE, diffFiles, &prompt);
1366 for( i=0; diffFiles[i].zName; ++i ){
1367 fossil_free(diffFiles[i].zName);
1368 }
1369 fossil_free(diffFiles);
1370 }else{
1371 diff_against_disk(0, 0, diff_get_binary_glob(),
1372 db_get_boolean("diff-binary", 1),
1373 DIFF_VERBOSE, 0, &prompt);
1374 }
1375 }
1376 prompt_for_user_comment(pComment, &prompt);
1377
--- src/checkin.c
+++ src/checkin.c
@@ -1358,19 +1358,19 @@
1358 break;
1359 }
1360 diffFiles[i].nName = strlen(diffFiles[i].zName);
1361 diffFiles[i].nUsed = 0;
1362 }
1363 diff_against_disk(0, 0, 0, diff_get_binary_glob(),
1364 db_get_boolean("diff-binary", 1),
1365 DIFF_VERBOSE, diffFiles, &prompt);
1366 for( i=0; diffFiles[i].zName; ++i ){
1367 fossil_free(diffFiles[i].zName);
1368 }
1369 fossil_free(diffFiles);
1370 }else{
1371 diff_against_disk(0, 0, 0, diff_get_binary_glob(),
1372 db_get_boolean("diff-binary", 1),
1373 DIFF_VERBOSE, 0, &prompt);
1374 }
1375 }
1376 prompt_for_user_comment(pComment, &prompt);
1377
+11
--- src/db.c
+++ src/db.c
@@ -3187,10 +3187,21 @@
31873187
}else{
31883188
z = fossil_strdup(zDefault);
31893189
}
31903190
}
31913191
return z;
3192
+}
3193
+const char ** db_get_array(size_t *pnValues, const char *zName, const char* zDefault){
3194
+ const char *value = db_get(zName, zDefault ? zDefault : "[]");
3195
+ if( value[0]=='[' && value[strlen(value)-1]==']' ){
3196
+ return json_deserialize_array(pnValues, value);
3197
+ }else{
3198
+ const char** azValues = fossil_malloc(sizeof(char*)*2);
3199
+ azValues[0] = value;
3200
+ azValues[1] = 0;
3201
+ return azValues;
3202
+ }
31923203
}
31933204
char *db_get_mtime(const char *zName, const char *zFormat, const char *zDefault){
31943205
char *z = 0;
31953206
if( g.repositoryOpen ){
31963207
z = db_text(0, "SELECT mtime FROM config WHERE name=%Q", zName);
31973208
--- src/db.c
+++ src/db.c
@@ -3187,10 +3187,21 @@
3187 }else{
3188 z = fossil_strdup(zDefault);
3189 }
3190 }
3191 return z;
 
 
 
 
 
 
 
 
 
 
 
3192 }
3193 char *db_get_mtime(const char *zName, const char *zFormat, const char *zDefault){
3194 char *z = 0;
3195 if( g.repositoryOpen ){
3196 z = db_text(0, "SELECT mtime FROM config WHERE name=%Q", zName);
3197
--- src/db.c
+++ src/db.c
@@ -3187,10 +3187,21 @@
3187 }else{
3188 z = fossil_strdup(zDefault);
3189 }
3190 }
3191 return z;
3192 }
3193 const char ** db_get_array(size_t *pnValues, const char *zName, const char* zDefault){
3194 const char *value = db_get(zName, zDefault ? zDefault : "[]");
3195 if( value[0]=='[' && value[strlen(value)-1]==']' ){
3196 return json_deserialize_array(pnValues, value);
3197 }else{
3198 const char** azValues = fossil_malloc(sizeof(char*)*2);
3199 azValues[0] = value;
3200 azValues[1] = 0;
3201 return azValues;
3202 }
3203 }
3204 char *db_get_mtime(const char *zName, const char *zFormat, const char *zDefault){
3205 char *z = 0;
3206 if( g.repositoryOpen ){
3207 z = db_text(0, "SELECT mtime FROM config WHERE name=%Q", zName);
3208
+143 -59
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -164,12 +164,13 @@
164164
** zFile2. The content of pFile1 is in memory. zFile2 exists on disk.
165165
**
166166
** If fSwapDiff is 1, show the set of edits to transform zFile2 into pFile1
167167
** instead of the opposite.
168168
**
169
-** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
170
-** command zDiffCmd to do the diffing.
169
+** Use the internal diff logic if nDiffCmdValues is 0. Otherwise pass the
170
+** command and args in azDiffCmd along with a pair of file names to
171
+** fossil_spawn() to do the diffing externally.
171172
**
172173
** When using an external diff program, zBinGlob contains the GLOB patterns
173174
** for file names to treat as binary. If fIncludeBinary is zero, these files
174175
** will be skipped in addition to files that may contain binary content.
175176
*/
@@ -176,18 +177,19 @@
176177
void diff_file(
177178
Blob *pFile1, /* In memory content to compare from */
178179
int isBin1, /* Does the 'from' content appear to be binary */
179180
const char *zFile2, /* On disk content to compare to */
180181
const char *zName, /* Display name of the file */
181
- const char *zDiffCmd, /* Command for comparison */
182
+ const char *azDiffCmd[], /* External diff cmd and optional args */
183
+ size_t nDiffCmdValues, /* Elements in azDiffCmd; 0 for internal */
182184
const char *zBinGlob, /* Treat file names matching this as binary */
183185
int fIncludeBinary, /* Include binary files for external diff */
184186
u64 diffFlags, /* Flags to control the diff */
185187
int fSwapDiff, /* Diff from Zfile2 to Pfile1 */
186188
Blob *diffBlob /* Blob to store diff output */
187189
){
188
- if( zDiffCmd==0 ){
190
+ if( nDiffCmdValues==0 ){
189191
Blob out; /* Diff output text */
190192
Blob file2; /* Content of zFile2 */
191193
const char *zName2; /* Name of zFile2 for display */
192194
193195
/* Read content of zFile2 into memory */
@@ -232,12 +234,13 @@
232234
}
233235
234236
/* Release memory resources */
235237
blob_reset(&file2);
236238
}else{
239
+ size_t n;
240
+ const char **azFullDiffCmd;
237241
Blob nameFile1; /* Name of temporary file to old pFile1 content */
238
- Blob cmd; /* Text of command to run */
239242
240243
if( !fIncludeBinary ){
241244
Blob file2;
242245
if( isBin1 ){
243246
fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY);
@@ -267,39 +270,90 @@
267270
/* Construct a temporary file to hold pFile1 based on the name of
268271
** zFile2 */
269272
file_tempname(&nameFile1, zFile2, "orig");
270273
blob_write_to_file(pFile1, blob_str(&nameFile1));
271274
272
- /* Construct the external diff command */
273
- blob_zero(&cmd);
274
- blob_append(&cmd, zDiffCmd, -1);
275
- if( fSwapDiff ){
276
- blob_append_escaped_arg(&cmd, zFile2);
277
- blob_append_escaped_arg(&cmd, blob_str(&nameFile1));
275
+ if( nDiffCmdValues==1 ) {
276
+ /* With only one element in the command array, take the legacy
277
+ * path, constructing a shell command string to do the external
278
+ * diff. This works for many cases, but because of the risk
279
+ * of double shell interpretation of quotes, spaces, escape
280
+ * characters, and such, we have to run this command string
281
+ * through a number of prophylactic wrappers around system(3)
282
+ * which can cause problems, as when doing diffs on file names
283
+ * that legitimately contain spaces, quotes, etc.
284
+ */
285
+ Blob cmd; /* Text of command to run */
286
+ blob_zero(&cmd);
287
+ blob_append(&cmd, azDiffCmd[0], -1);
288
+ if( fSwapDiff ){
289
+ blob_append_escaped_arg(&cmd, zFile2);
290
+ blob_append_escaped_arg(&cmd, blob_str(&nameFile1));
291
+ }else{
292
+ blob_append_escaped_arg(&cmd, blob_str(&nameFile1));
293
+ blob_append_escaped_arg(&cmd, zFile2);
294
+ }
295
+
296
+ /* Run the external diff command */
297
+ fossil_system(blob_str(&cmd));
278298
}else{
279
- blob_append_escaped_arg(&cmd, blob_str(&nameFile1));
280
- blob_append_escaped_arg(&cmd, zFile2);
281
- }
299
+ /* Construct an argv style array to do the external diff.
300
+ *
301
+ * This may look more complex, but it bypasses all of the
302
+ * stuff we have to wrap around system(3) because we get a
303
+ * direct binding of our argv[] to the callee's main(argv)
304
+ * on POSIX systems, entirely avoiding the class of double
305
+ * shell interpretation risks. (Windows's API flattens argv,
306
+ * neutering some of this mechanism's benefits, but not all.)
307
+ *
308
+ * Due to the legacy of existing settings, we can't assume
309
+ * it's safe to take this path when the setting is a single
310
+ * string value, since that gets converted to a 1-element
311
+ * array by db_get_array(). Fortunately, most diff type
312
+ * programs offer and benefit from option flags, so you
313
+ * should be able to upgrade to this method by re-applying
314
+ * your settings, making use of the new array storage option:
315
+ *
316
+ * $ fossil set diff-command colordiff -wu
317
+ *
318
+ * as opposed to the legacy method:
319
+ *
320
+ * $ fossil set diff-command 'colordiff -wu'
321
+ *
322
+ * The lack of quoting around those two setting values causes
323
+ * Fossil to store them as an array, which lands you in here.
324
+ */
325
+ azFullDiffCmd = fossil_malloc((nDiffCmdValues+3)*sizeof(char*));
326
+ for( n=0; n<nDiffCmdValues; ++n ) azFullDiffCmd[n] = azDiffCmd[n];
327
+ if( fSwapDiff ){
328
+ azFullDiffCmd[n++] = zFile2;
329
+ azFullDiffCmd[n++] = blob_str(&nameFile1);
330
+ }else{
331
+ azFullDiffCmd[n++] = blob_str(&nameFile1);
332
+ azFullDiffCmd[n++] = zFile2;
333
+ }
334
+ azFullDiffCmd[n] = 0;
282335
283
- /* Run the external diff command */
284
- fossil_system(blob_str(&cmd));
336
+ /* Run the external diff command */
337
+ fossil_spawn(azFullDiffCmd[0], (char*const*)azFullDiffCmd);
338
+ }
285339
286340
/* Delete the temporary file and clean up memory used */
287341
file_delete(blob_str(&nameFile1));
288342
blob_reset(&nameFile1);
289
- blob_reset(&cmd);
290343
}
291344
}
292345
293346
/*
294347
** Show the difference between two files, both in memory.
295348
**
296349
** The difference is the set of edits needed to transform pFile1 into
297350
** pFile2.
298351
**
299
-** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
300
-** command zDiffCmd to do the diffing.
352
+** Use the internal diff logic if nDiffCmdValues is 0. Otherwise pass the
353
+** command and args in azDiffCmd along with a pair of file names to
354
+** fossil_spawn() to do the diffing externally.
301355
**
302356
** When using an external diff program, zBinGlob contains the GLOB patterns
303357
** for file names to treat as binary. If fIncludeBinary is zero, these files
304358
** will be skipped in addition to files that may contain binary content.
305359
*/
@@ -307,17 +361,18 @@
307361
Blob *pFile1, /* In memory content to compare from */
308362
Blob *pFile2, /* In memory content to compare to */
309363
int isBin1, /* Does the 'from' content appear to be binary */
310364
int isBin2, /* Does the 'to' content appear to be binary */
311365
const char *zName, /* Display name of the file */
312
- const char *zDiffCmd, /* Command for comparison */
366
+ const char *azDiffCmd[], /* External diff cmd and optional args */
367
+ size_t nDiffCmdValues, /* Elements in azDiffCmd; 0 for internal */
313368
const char *zBinGlob, /* Treat file names matching this as binary */
314369
int fIncludeBinary, /* Include binary files for external diff */
315370
u64 diffFlags /* Diff flags */
316371
){
317372
if( diffFlags & DIFF_BRIEF ) return;
318
- if( zDiffCmd==0 ){
373
+ if( nDiffCmdValues==0 ){
319374
Blob out; /* Diff output text */
320375
321376
blob_zero(&out);
322377
text_diff(pFile1, pFile2, &out, 0, diffFlags);
323378
if( diffFlags & DIFF_NUMSTAT ){
@@ -328,11 +383,12 @@
328383
}
329384
330385
/* Release memory resources */
331386
blob_reset(&out);
332387
}else{
333
- Blob cmd;
388
+ size_t n;
389
+ const char** azFullDiffCmd;
334390
Blob temp1;
335391
Blob temp2;
336392
337393
if( !fIncludeBinary ){
338394
if( isBin1 || isBin2 ){
@@ -354,44 +410,58 @@
354410
file_tempname(&temp1, zName, "before");
355411
file_tempname(&temp2, zName, "after");
356412
blob_write_to_file(pFile1, blob_str(&temp1));
357413
blob_write_to_file(pFile2, blob_str(&temp2));
358414
359
- /* Construct the external diff command */
360
- blob_zero(&cmd);
361
- blob_append(&cmd, zDiffCmd, -1);
362
- blob_append_escaped_arg(&cmd, blob_str(&temp1));
363
- blob_append_escaped_arg(&cmd, blob_str(&temp2));
364
-
365
- /* Run the external diff command */
366
- fossil_system(blob_str(&cmd));
415
+ if( nDiffCmdValues==1 ) {
416
+ /* Legacy system(3) wrapper method; see diff_file for details. */
417
+ Blob cmd; /* Text of command to run */
418
+ blob_zero(&cmd);
419
+ blob_append(&cmd, azDiffCmd[0], -1);
420
+ blob_append_escaped_arg(&cmd, blob_str(&temp1));
421
+ blob_append_escaped_arg(&cmd, blob_str(&temp2));
422
+
423
+ /* Run the external diff command */
424
+ fossil_system(blob_str(&cmd));
425
+ }else{
426
+ /* Safer argv[] passing method; see diff_file for details. */
427
+ azFullDiffCmd = fossil_malloc((nDiffCmdValues+3)*sizeof(char*));
428
+ for( n=0; n<nDiffCmdValues; ++n ) azFullDiffCmd[n] = azDiffCmd[n];
429
+ azFullDiffCmd[n++] = blob_str(&temp1);
430
+ azFullDiffCmd[n++] = blob_str(&temp2);
431
+ azFullDiffCmd[n] = 0;
432
+
433
+ /* Run the external diff command */
434
+ fossil_spawn(azFullDiffCmd[0], (char*const*)azFullDiffCmd);
435
+ }
367436
368437
/* Delete the temporary file and clean up memory used */
369438
file_delete(blob_str(&temp1));
370439
file_delete(blob_str(&temp2));
371440
372441
blob_reset(&temp1);
373442
blob_reset(&temp2);
374
- blob_reset(&cmd);
375443
}
376444
}
377445
378446
/*
379447
** Run a diff between the version zFrom and files on disk. zFrom might
380448
** be NULL which means to simply show the difference between the edited
381449
** files on disk and the check-out on which they are based.
382450
**
383
-** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
384
-** command zDiffCmd to do the diffing.
451
+** Use the internal diff logic if nDiffCmdValues is 0. Otherwise pass the
452
+** command and args in azDiffCmd along with a pair of file names to
453
+** fossil_spawn() to do the diffing externally.
385454
**
386455
** When using an external diff program, zBinGlob contains the GLOB patterns
387456
** for file names to treat as binary. If fIncludeBinary is zero, these files
388457
** will be skipped in addition to files that may contain binary content.
389458
*/
390459
void diff_against_disk(
391460
const char *zFrom, /* Version to difference from */
392
- const char *zDiffCmd, /* Use this diff command. NULL for built-in */
461
+ const char *azDiffCmd[], /* External diff cmd and optional args */
462
+ size_t nDiffCmdValues, /* Elements in azDiffCmd; 0 for internal */
393463
const char *zBinGlob, /* Treat file names matching this as binary */
394464
int fIncludeBinary, /* Treat file names matching this as binary */
395465
u64 diffFlags, /* Flags controlling diff output */
396466
FileDirList *pFileDir, /* Which files to diff */
397467
Blob *diffBlob /* Blob to output diff instead of stdout */
@@ -499,11 +569,11 @@
499569
}else{
500570
blob_zero(&content);
501571
}
502572
isBin = fIncludeBinary ? 0 : looks_like_binary(&content);
503573
diff_print_index(zPathname, diffFlags, diffBlob);
504
- diff_file(&content, isBin, zFullName, zPathname, zDiffCmd,
574
+ diff_file(&content, isBin, zFullName, zPathname, azDiffCmd, nDiffCmdValues,
505575
zBinGlob, fIncludeBinary, diffFlags, 0, diffBlob);
506576
blob_reset(&content);
507577
}
508578
blob_reset(&fname);
509579
}
@@ -512,19 +582,21 @@
512582
}
513583
514584
/*
515585
** Run a diff between the undo buffer and files on disk.
516586
**
517
-** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
518
-** command zDiffCmd to do the diffing.
587
+** Use the internal diff logic if nDiffCmdValues is 0. Otherwise pass the
588
+** command and args in azDiffCmd along with a pair of file names to
589
+** fossil_spawn() to do the diffing externally.
519590
**
520591
** When using an external diff program, zBinGlob contains the GLOB patterns
521592
** for file names to treat as binary. If fIncludeBinary is zero, these files
522593
** will be skipped in addition to files that may contain binary content.
523594
*/
524595
static void diff_against_undo(
525
- const char *zDiffCmd, /* Use this diff command. NULL for built-in */
596
+ const char *azDiffCmd[], /* External diff cmd and optional args */
597
+ size_t nDiffCmdValues, /* Elements in azDiffCmd; 0 for internal */
526598
const char *zBinGlob, /* Treat file names matching this as binary */
527599
int fIncludeBinary, /* Treat file names matching this as binary */
528600
u64 diffFlags, /* Flags controlling diff output */
529601
FileDirList *pFileDir /* List of files and directories to diff */
530602
){
@@ -537,11 +609,11 @@
537609
const char *zFile = (const char*)db_column_text(&q, 0);
538610
if( !file_dir_match(pFileDir, zFile) ) continue;
539611
zFullName = mprintf("%s%s", g.zLocalRoot, zFile);
540612
db_column_blob(&q, 1, &content);
541613
diff_file(&content, 0, zFullName, zFile,
542
- zDiffCmd, zBinGlob, fIncludeBinary, diffFlags, 0, 0);
614
+ azDiffCmd, nDiffCmdValues, zBinGlob, fIncludeBinary, diffFlags, 0, 0);
543615
fossil_free(zFullName);
544616
blob_reset(&content);
545617
}
546618
db_finalize(&q);
547619
}
@@ -548,21 +620,23 @@
548620
549621
/*
550622
** Show the difference between two files identified by ManifestFile
551623
** entries.
552624
**
553
-** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
554
-** command zDiffCmd to do the diffing.
625
+** Use the internal diff logic if nDiffCmdValues is 0. Otherwise pass the
626
+** command and args in azDiffCmd along with a pair of file names to
627
+** fossil_spawn() to do the diffing externally.
555628
**
556629
** When using an external diff program, zBinGlob contains the GLOB patterns
557630
** for file names to treat as binary. If fIncludeBinary is zero, these files
558631
** will be skipped in addition to files that may contain binary content.
559632
*/
560633
static void diff_manifest_entry(
561634
struct ManifestFile *pFrom,
562635
struct ManifestFile *pTo,
563
- const char *zDiffCmd,
636
+ const char * azDiffCmd[],
637
+ size_t nDiffCmdValues,
564638
const char *zBinGlob,
565639
int fIncludeBinary,
566640
u64 diffFlags
567641
){
568642
Blob f1, f2;
@@ -590,30 +664,32 @@
590664
}else{
591665
blob_zero(&f2);
592666
}
593667
isBin1 = fIncludeBinary ? 0 : looks_like_binary(&f1);
594668
isBin2 = fIncludeBinary ? 0 : looks_like_binary(&f2);
595
- diff_file_mem(&f1, &f2, isBin1, isBin2, zName, zDiffCmd,
669
+ diff_file_mem(&f1, &f2, isBin1, isBin2, zName, azDiffCmd, nDiffCmdValues,
596670
zBinGlob, fIncludeBinary, diffFlags);
597671
blob_reset(&f1);
598672
blob_reset(&f2);
599673
}
600674
601675
/*
602676
** Output the differences between two check-ins.
603677
**
604
-** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
605
-** command zDiffCmd to do the diffing.
678
+** Use the internal diff logic if nDiffCmdValues is 0. Otherwise pass the
679
+** command and args in azDiffCmd along with a pair of file names to
680
+** fossil_spawn() to do the diffing externally.
606681
**
607682
** When using an external diff program, zBinGlob contains the GLOB patterns
608683
** for file names to treat as binary. If fIncludeBinary is zero, these files
609684
** will be skipped in addition to files that may contain binary content.
610685
*/
611686
static void diff_two_versions(
612687
const char *zFrom,
613688
const char *zTo,
614
- const char *zDiffCmd,
689
+ const char *azDiffCmd[],
690
+ size_t nDiffCmdValues,
615691
const char *zBinGlob,
616692
int fIncludeBinary,
617693
u64 diffFlags,
618694
FileDirList *pFileDir
619695
){
@@ -641,11 +717,11 @@
641717
if( file_dir_match(pFileDir, pFromFile->zName) ){
642718
if( (diffFlags & DIFF_NUMSTAT)==0 ){
643719
fossil_print("DELETED %s\n", pFromFile->zName);
644720
}
645721
if( asNewFlag ){
646
- diff_manifest_entry(pFromFile, 0, zDiffCmd, zBinGlob,
722
+ diff_manifest_entry(pFromFile, 0, azDiffCmd, nDiffCmdValues, zBinGlob,
647723
fIncludeBinary, diffFlags);
648724
}
649725
}
650726
pFromFile = manifest_file_next(pFrom,0);
651727
}else if( cmp>0 ){
@@ -652,11 +728,11 @@
652728
if( file_dir_match(pFileDir, pToFile->zName) ){
653729
if( (diffFlags & DIFF_NUMSTAT)==0 ){
654730
fossil_print("ADDED %s\n", pToFile->zName);
655731
}
656732
if( asNewFlag ){
657
- diff_manifest_entry(0, pToFile, zDiffCmd, zBinGlob,
733
+ diff_manifest_entry(0, pToFile, azDiffCmd, nDiffCmdValues, zBinGlob,
658734
fIncludeBinary, diffFlags);
659735
}
660736
}
661737
pToFile = manifest_file_next(pTo,0);
662738
}else if( fossil_strcmp(pFromFile->zUuid, pToFile->zUuid)==0 ){
@@ -667,11 +743,11 @@
667743
}else{
668744
if( file_dir_match(pFileDir, pToFile->zName) ){
669745
if( diffFlags & DIFF_BRIEF ){
670746
fossil_print("CHANGED %s\n", pFromFile->zName);
671747
}else{
672
- diff_manifest_entry(pFromFile, pToFile, zDiffCmd, zBinGlob,
748
+ diff_manifest_entry(pFromFile, pToFile, azDiffCmd, nDiffCmdValues, zBinGlob,
673749
fIncludeBinary, diffFlags);
674750
}
675751
}
676752
pFromFile = manifest_file_next(pFrom,0);
677753
pToFile = manifest_file_next(pTo,0);
@@ -683,26 +759,26 @@
683759
684760
/*
685761
** Return the name of the external diff command, or return NULL if
686762
** no external diff command is defined.
687763
*/
688
-const char *diff_command_external(int guiDiff){
764
+const char ** diff_command_external(size_t *pnValues, int guiDiff){
689765
const char *zDefault;
690766
const char *zName;
691767
692768
if( guiDiff ){
693769
#if defined(_WIN32)
694
- zDefault = "WinDiff.exe";
770
+ zDefault = "[\"WinDiff.exe\"]";
695771
#else
696772
zDefault = 0;
697773
#endif
698774
zName = "gdiff-command";
699775
}else{
700776
zDefault = 0;
701777
zName = "diff-command";
702778
}
703
- return db_get(zName, zDefault);
779
+ return db_get_array(pnValues, zName, zDefault);
704780
}
705781
706782
/*
707783
** Show diff output in a Tcl/Tk window, in response to the --tk option
708784
** to the diff command.
@@ -870,11 +946,12 @@
870946
int verboseFlag; /* True if -v or --verbose flag is used */
871947
const char *zFrom; /* Source version number */
872948
const char *zTo; /* Target version number */
873949
const char *zCheckin; /* Check-in version number */
874950
const char *zBranch; /* Branch to diff */
875
- const char *zDiffCmd = 0; /* External diff command. NULL for internal diff */
951
+ const char **azDiffCmd; /* External diff cmd and optional args */
952
+ size_t nDiffCmdValues = 0; /* Number of elements in azDiffCmd; 0 for internal */
876953
const char *zBinGlob = 0; /* Treat file names matching this as binary */
877954
int fIncludeBinary = 0; /* Include binary files for external diff */
878955
int againstUndo = 0; /* Diff against files in the undo buffer */
879956
u64 diffFlags = 0; /* Flags to control the DIFF */
880957
FileDirList *pFileDir = 0; /* Restrict the diff to these files */
@@ -917,12 +994,19 @@
917994
fossil_fatal("must use --from if --to is present");
918995
}else{
919996
db_find_and_open_repository(0, 0);
920997
}
921998
if( !isInternDiff ){
922
- zDiffCmd = find_option("command", 0, 1);
923
- if( zDiffCmd==0 ) zDiffCmd = diff_command_external(isGDiff);
999
+ const char *zDiffCmd = find_option("command", 0, 1);
1000
+ if( zDiffCmd ){
1001
+ azDiffCmd = malloc(2*sizeof(char*));
1002
+ azDiffCmd[0] = zDiffCmd;
1003
+ azDiffCmd[1] = 0;
1004
+ nDiffCmdValues = 1;
1005
+ }else{
1006
+ azDiffCmd = diff_command_external(&nDiffCmdValues, isGDiff);
1007
+ }
9241008
}
9251009
zBinGlob = diff_get_binary_glob();
9261010
fIncludeBinary = diff_include_binary_files();
9271011
determine_exec_relative_option(1);
9281012
verify_all_options();
@@ -958,17 +1042,17 @@
9581042
if( againstUndo ){
9591043
if( db_lget_int("undo_available",0)==0 ){
9601044
fossil_print("No undo or redo is available\n");
9611045
return;
9621046
}
963
- diff_against_undo(zDiffCmd, zBinGlob, fIncludeBinary,
1047
+ diff_against_undo(azDiffCmd, nDiffCmdValues, zBinGlob, fIncludeBinary,
9641048
diffFlags, pFileDir);
9651049
}else if( zTo==0 ){
966
- diff_against_disk(zFrom, zDiffCmd, zBinGlob, fIncludeBinary,
1050
+ diff_against_disk(zFrom, azDiffCmd, nDiffCmdValues, zBinGlob, fIncludeBinary,
9671051
diffFlags, pFileDir, 0);
9681052
}else{
969
- diff_two_versions(zFrom, zTo, zDiffCmd, zBinGlob, fIncludeBinary,
1053
+ diff_two_versions(zFrom, zTo, azDiffCmd, nDiffCmdValues, zBinGlob, fIncludeBinary,
9701054
diffFlags, pFileDir);
9711055
}
9721056
if( pFileDir ){
9731057
int i;
9741058
for(i=0; pFileDir[i].zName; i++){
@@ -1000,7 +1084,7 @@
10001084
login_check_credentials();
10011085
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
10021086
if( zFrom==0 || zTo==0 ) fossil_redirect_home();
10031087
10041088
cgi_set_content_type("text/plain");
1005
- diff_two_versions(zFrom, zTo, 0, 0, 0, DIFF_VERBOSE, 0);
1089
+ diff_two_versions(zFrom, zTo, 0, 0, 0, 0, DIFF_VERBOSE, 0);
10061090
}
10071091
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -164,12 +164,13 @@
164 ** zFile2. The content of pFile1 is in memory. zFile2 exists on disk.
165 **
166 ** If fSwapDiff is 1, show the set of edits to transform zFile2 into pFile1
167 ** instead of the opposite.
168 **
169 ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
170 ** command zDiffCmd to do the diffing.
 
171 **
172 ** When using an external diff program, zBinGlob contains the GLOB patterns
173 ** for file names to treat as binary. If fIncludeBinary is zero, these files
174 ** will be skipped in addition to files that may contain binary content.
175 */
@@ -176,18 +177,19 @@
176 void diff_file(
177 Blob *pFile1, /* In memory content to compare from */
178 int isBin1, /* Does the 'from' content appear to be binary */
179 const char *zFile2, /* On disk content to compare to */
180 const char *zName, /* Display name of the file */
181 const char *zDiffCmd, /* Command for comparison */
 
182 const char *zBinGlob, /* Treat file names matching this as binary */
183 int fIncludeBinary, /* Include binary files for external diff */
184 u64 diffFlags, /* Flags to control the diff */
185 int fSwapDiff, /* Diff from Zfile2 to Pfile1 */
186 Blob *diffBlob /* Blob to store diff output */
187 ){
188 if( zDiffCmd==0 ){
189 Blob out; /* Diff output text */
190 Blob file2; /* Content of zFile2 */
191 const char *zName2; /* Name of zFile2 for display */
192
193 /* Read content of zFile2 into memory */
@@ -232,12 +234,13 @@
232 }
233
234 /* Release memory resources */
235 blob_reset(&file2);
236 }else{
 
 
237 Blob nameFile1; /* Name of temporary file to old pFile1 content */
238 Blob cmd; /* Text of command to run */
239
240 if( !fIncludeBinary ){
241 Blob file2;
242 if( isBin1 ){
243 fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY);
@@ -267,39 +270,90 @@
267 /* Construct a temporary file to hold pFile1 based on the name of
268 ** zFile2 */
269 file_tempname(&nameFile1, zFile2, "orig");
270 blob_write_to_file(pFile1, blob_str(&nameFile1));
271
272 /* Construct the external diff command */
273 blob_zero(&cmd);
274 blob_append(&cmd, zDiffCmd, -1);
275 if( fSwapDiff ){
276 blob_append_escaped_arg(&cmd, zFile2);
277 blob_append_escaped_arg(&cmd, blob_str(&nameFile1));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
278 }else{
279 blob_append_escaped_arg(&cmd, blob_str(&nameFile1));
280 blob_append_escaped_arg(&cmd, zFile2);
281 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
282
283 /* Run the external diff command */
284 fossil_system(blob_str(&cmd));
 
285
286 /* Delete the temporary file and clean up memory used */
287 file_delete(blob_str(&nameFile1));
288 blob_reset(&nameFile1);
289 blob_reset(&cmd);
290 }
291 }
292
293 /*
294 ** Show the difference between two files, both in memory.
295 **
296 ** The difference is the set of edits needed to transform pFile1 into
297 ** pFile2.
298 **
299 ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
300 ** command zDiffCmd to do the diffing.
 
301 **
302 ** When using an external diff program, zBinGlob contains the GLOB patterns
303 ** for file names to treat as binary. If fIncludeBinary is zero, these files
304 ** will be skipped in addition to files that may contain binary content.
305 */
@@ -307,17 +361,18 @@
307 Blob *pFile1, /* In memory content to compare from */
308 Blob *pFile2, /* In memory content to compare to */
309 int isBin1, /* Does the 'from' content appear to be binary */
310 int isBin2, /* Does the 'to' content appear to be binary */
311 const char *zName, /* Display name of the file */
312 const char *zDiffCmd, /* Command for comparison */
 
313 const char *zBinGlob, /* Treat file names matching this as binary */
314 int fIncludeBinary, /* Include binary files for external diff */
315 u64 diffFlags /* Diff flags */
316 ){
317 if( diffFlags & DIFF_BRIEF ) return;
318 if( zDiffCmd==0 ){
319 Blob out; /* Diff output text */
320
321 blob_zero(&out);
322 text_diff(pFile1, pFile2, &out, 0, diffFlags);
323 if( diffFlags & DIFF_NUMSTAT ){
@@ -328,11 +383,12 @@
328 }
329
330 /* Release memory resources */
331 blob_reset(&out);
332 }else{
333 Blob cmd;
 
334 Blob temp1;
335 Blob temp2;
336
337 if( !fIncludeBinary ){
338 if( isBin1 || isBin2 ){
@@ -354,44 +410,58 @@
354 file_tempname(&temp1, zName, "before");
355 file_tempname(&temp2, zName, "after");
356 blob_write_to_file(pFile1, blob_str(&temp1));
357 blob_write_to_file(pFile2, blob_str(&temp2));
358
359 /* Construct the external diff command */
360 blob_zero(&cmd);
361 blob_append(&cmd, zDiffCmd, -1);
362 blob_append_escaped_arg(&cmd, blob_str(&temp1));
363 blob_append_escaped_arg(&cmd, blob_str(&temp2));
364
365 /* Run the external diff command */
366 fossil_system(blob_str(&cmd));
 
 
 
 
 
 
 
 
 
 
 
 
 
367
368 /* Delete the temporary file and clean up memory used */
369 file_delete(blob_str(&temp1));
370 file_delete(blob_str(&temp2));
371
372 blob_reset(&temp1);
373 blob_reset(&temp2);
374 blob_reset(&cmd);
375 }
376 }
377
378 /*
379 ** Run a diff between the version zFrom and files on disk. zFrom might
380 ** be NULL which means to simply show the difference between the edited
381 ** files on disk and the check-out on which they are based.
382 **
383 ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
384 ** command zDiffCmd to do the diffing.
 
385 **
386 ** When using an external diff program, zBinGlob contains the GLOB patterns
387 ** for file names to treat as binary. If fIncludeBinary is zero, these files
388 ** will be skipped in addition to files that may contain binary content.
389 */
390 void diff_against_disk(
391 const char *zFrom, /* Version to difference from */
392 const char *zDiffCmd, /* Use this diff command. NULL for built-in */
 
393 const char *zBinGlob, /* Treat file names matching this as binary */
394 int fIncludeBinary, /* Treat file names matching this as binary */
395 u64 diffFlags, /* Flags controlling diff output */
396 FileDirList *pFileDir, /* Which files to diff */
397 Blob *diffBlob /* Blob to output diff instead of stdout */
@@ -499,11 +569,11 @@
499 }else{
500 blob_zero(&content);
501 }
502 isBin = fIncludeBinary ? 0 : looks_like_binary(&content);
503 diff_print_index(zPathname, diffFlags, diffBlob);
504 diff_file(&content, isBin, zFullName, zPathname, zDiffCmd,
505 zBinGlob, fIncludeBinary, diffFlags, 0, diffBlob);
506 blob_reset(&content);
507 }
508 blob_reset(&fname);
509 }
@@ -512,19 +582,21 @@
512 }
513
514 /*
515 ** Run a diff between the undo buffer and files on disk.
516 **
517 ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
518 ** command zDiffCmd to do the diffing.
 
519 **
520 ** When using an external diff program, zBinGlob contains the GLOB patterns
521 ** for file names to treat as binary. If fIncludeBinary is zero, these files
522 ** will be skipped in addition to files that may contain binary content.
523 */
524 static void diff_against_undo(
525 const char *zDiffCmd, /* Use this diff command. NULL for built-in */
 
526 const char *zBinGlob, /* Treat file names matching this as binary */
527 int fIncludeBinary, /* Treat file names matching this as binary */
528 u64 diffFlags, /* Flags controlling diff output */
529 FileDirList *pFileDir /* List of files and directories to diff */
530 ){
@@ -537,11 +609,11 @@
537 const char *zFile = (const char*)db_column_text(&q, 0);
538 if( !file_dir_match(pFileDir, zFile) ) continue;
539 zFullName = mprintf("%s%s", g.zLocalRoot, zFile);
540 db_column_blob(&q, 1, &content);
541 diff_file(&content, 0, zFullName, zFile,
542 zDiffCmd, zBinGlob, fIncludeBinary, diffFlags, 0, 0);
543 fossil_free(zFullName);
544 blob_reset(&content);
545 }
546 db_finalize(&q);
547 }
@@ -548,21 +620,23 @@
548
549 /*
550 ** Show the difference between two files identified by ManifestFile
551 ** entries.
552 **
553 ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
554 ** command zDiffCmd to do the diffing.
 
555 **
556 ** When using an external diff program, zBinGlob contains the GLOB patterns
557 ** for file names to treat as binary. If fIncludeBinary is zero, these files
558 ** will be skipped in addition to files that may contain binary content.
559 */
560 static void diff_manifest_entry(
561 struct ManifestFile *pFrom,
562 struct ManifestFile *pTo,
563 const char *zDiffCmd,
 
564 const char *zBinGlob,
565 int fIncludeBinary,
566 u64 diffFlags
567 ){
568 Blob f1, f2;
@@ -590,30 +664,32 @@
590 }else{
591 blob_zero(&f2);
592 }
593 isBin1 = fIncludeBinary ? 0 : looks_like_binary(&f1);
594 isBin2 = fIncludeBinary ? 0 : looks_like_binary(&f2);
595 diff_file_mem(&f1, &f2, isBin1, isBin2, zName, zDiffCmd,
596 zBinGlob, fIncludeBinary, diffFlags);
597 blob_reset(&f1);
598 blob_reset(&f2);
599 }
600
601 /*
602 ** Output the differences between two check-ins.
603 **
604 ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
605 ** command zDiffCmd to do the diffing.
 
606 **
607 ** When using an external diff program, zBinGlob contains the GLOB patterns
608 ** for file names to treat as binary. If fIncludeBinary is zero, these files
609 ** will be skipped in addition to files that may contain binary content.
610 */
611 static void diff_two_versions(
612 const char *zFrom,
613 const char *zTo,
614 const char *zDiffCmd,
 
615 const char *zBinGlob,
616 int fIncludeBinary,
617 u64 diffFlags,
618 FileDirList *pFileDir
619 ){
@@ -641,11 +717,11 @@
641 if( file_dir_match(pFileDir, pFromFile->zName) ){
642 if( (diffFlags & DIFF_NUMSTAT)==0 ){
643 fossil_print("DELETED %s\n", pFromFile->zName);
644 }
645 if( asNewFlag ){
646 diff_manifest_entry(pFromFile, 0, zDiffCmd, zBinGlob,
647 fIncludeBinary, diffFlags);
648 }
649 }
650 pFromFile = manifest_file_next(pFrom,0);
651 }else if( cmp>0 ){
@@ -652,11 +728,11 @@
652 if( file_dir_match(pFileDir, pToFile->zName) ){
653 if( (diffFlags & DIFF_NUMSTAT)==0 ){
654 fossil_print("ADDED %s\n", pToFile->zName);
655 }
656 if( asNewFlag ){
657 diff_manifest_entry(0, pToFile, zDiffCmd, zBinGlob,
658 fIncludeBinary, diffFlags);
659 }
660 }
661 pToFile = manifest_file_next(pTo,0);
662 }else if( fossil_strcmp(pFromFile->zUuid, pToFile->zUuid)==0 ){
@@ -667,11 +743,11 @@
667 }else{
668 if( file_dir_match(pFileDir, pToFile->zName) ){
669 if( diffFlags & DIFF_BRIEF ){
670 fossil_print("CHANGED %s\n", pFromFile->zName);
671 }else{
672 diff_manifest_entry(pFromFile, pToFile, zDiffCmd, zBinGlob,
673 fIncludeBinary, diffFlags);
674 }
675 }
676 pFromFile = manifest_file_next(pFrom,0);
677 pToFile = manifest_file_next(pTo,0);
@@ -683,26 +759,26 @@
683
684 /*
685 ** Return the name of the external diff command, or return NULL if
686 ** no external diff command is defined.
687 */
688 const char *diff_command_external(int guiDiff){
689 const char *zDefault;
690 const char *zName;
691
692 if( guiDiff ){
693 #if defined(_WIN32)
694 zDefault = "WinDiff.exe";
695 #else
696 zDefault = 0;
697 #endif
698 zName = "gdiff-command";
699 }else{
700 zDefault = 0;
701 zName = "diff-command";
702 }
703 return db_get(zName, zDefault);
704 }
705
706 /*
707 ** Show diff output in a Tcl/Tk window, in response to the --tk option
708 ** to the diff command.
@@ -870,11 +946,12 @@
870 int verboseFlag; /* True if -v or --verbose flag is used */
871 const char *zFrom; /* Source version number */
872 const char *zTo; /* Target version number */
873 const char *zCheckin; /* Check-in version number */
874 const char *zBranch; /* Branch to diff */
875 const char *zDiffCmd = 0; /* External diff command. NULL for internal diff */
 
876 const char *zBinGlob = 0; /* Treat file names matching this as binary */
877 int fIncludeBinary = 0; /* Include binary files for external diff */
878 int againstUndo = 0; /* Diff against files in the undo buffer */
879 u64 diffFlags = 0; /* Flags to control the DIFF */
880 FileDirList *pFileDir = 0; /* Restrict the diff to these files */
@@ -917,12 +994,19 @@
917 fossil_fatal("must use --from if --to is present");
918 }else{
919 db_find_and_open_repository(0, 0);
920 }
921 if( !isInternDiff ){
922 zDiffCmd = find_option("command", 0, 1);
923 if( zDiffCmd==0 ) zDiffCmd = diff_command_external(isGDiff);
 
 
 
 
 
 
 
924 }
925 zBinGlob = diff_get_binary_glob();
926 fIncludeBinary = diff_include_binary_files();
927 determine_exec_relative_option(1);
928 verify_all_options();
@@ -958,17 +1042,17 @@
958 if( againstUndo ){
959 if( db_lget_int("undo_available",0)==0 ){
960 fossil_print("No undo or redo is available\n");
961 return;
962 }
963 diff_against_undo(zDiffCmd, zBinGlob, fIncludeBinary,
964 diffFlags, pFileDir);
965 }else if( zTo==0 ){
966 diff_against_disk(zFrom, zDiffCmd, zBinGlob, fIncludeBinary,
967 diffFlags, pFileDir, 0);
968 }else{
969 diff_two_versions(zFrom, zTo, zDiffCmd, zBinGlob, fIncludeBinary,
970 diffFlags, pFileDir);
971 }
972 if( pFileDir ){
973 int i;
974 for(i=0; pFileDir[i].zName; i++){
@@ -1000,7 +1084,7 @@
1000 login_check_credentials();
1001 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1002 if( zFrom==0 || zTo==0 ) fossil_redirect_home();
1003
1004 cgi_set_content_type("text/plain");
1005 diff_two_versions(zFrom, zTo, 0, 0, 0, DIFF_VERBOSE, 0);
1006 }
1007
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -164,12 +164,13 @@
164 ** zFile2. The content of pFile1 is in memory. zFile2 exists on disk.
165 **
166 ** If fSwapDiff is 1, show the set of edits to transform zFile2 into pFile1
167 ** instead of the opposite.
168 **
169 ** Use the internal diff logic if nDiffCmdValues is 0. Otherwise pass the
170 ** command and args in azDiffCmd along with a pair of file names to
171 ** fossil_spawn() to do the diffing externally.
172 **
173 ** When using an external diff program, zBinGlob contains the GLOB patterns
174 ** for file names to treat as binary. If fIncludeBinary is zero, these files
175 ** will be skipped in addition to files that may contain binary content.
176 */
@@ -176,18 +177,19 @@
177 void diff_file(
178 Blob *pFile1, /* In memory content to compare from */
179 int isBin1, /* Does the 'from' content appear to be binary */
180 const char *zFile2, /* On disk content to compare to */
181 const char *zName, /* Display name of the file */
182 const char *azDiffCmd[], /* External diff cmd and optional args */
183 size_t nDiffCmdValues, /* Elements in azDiffCmd; 0 for internal */
184 const char *zBinGlob, /* Treat file names matching this as binary */
185 int fIncludeBinary, /* Include binary files for external diff */
186 u64 diffFlags, /* Flags to control the diff */
187 int fSwapDiff, /* Diff from Zfile2 to Pfile1 */
188 Blob *diffBlob /* Blob to store diff output */
189 ){
190 if( nDiffCmdValues==0 ){
191 Blob out; /* Diff output text */
192 Blob file2; /* Content of zFile2 */
193 const char *zName2; /* Name of zFile2 for display */
194
195 /* Read content of zFile2 into memory */
@@ -232,12 +234,13 @@
234 }
235
236 /* Release memory resources */
237 blob_reset(&file2);
238 }else{
239 size_t n;
240 const char **azFullDiffCmd;
241 Blob nameFile1; /* Name of temporary file to old pFile1 content */
 
242
243 if( !fIncludeBinary ){
244 Blob file2;
245 if( isBin1 ){
246 fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY);
@@ -267,39 +270,90 @@
270 /* Construct a temporary file to hold pFile1 based on the name of
271 ** zFile2 */
272 file_tempname(&nameFile1, zFile2, "orig");
273 blob_write_to_file(pFile1, blob_str(&nameFile1));
274
275 if( nDiffCmdValues==1 ) {
276 /* With only one element in the command array, take the legacy
277 * path, constructing a shell command string to do the external
278 * diff. This works for many cases, but because of the risk
279 * of double shell interpretation of quotes, spaces, escape
280 * characters, and such, we have to run this command string
281 * through a number of prophylactic wrappers around system(3)
282 * which can cause problems, as when doing diffs on file names
283 * that legitimately contain spaces, quotes, etc.
284 */
285 Blob cmd; /* Text of command to run */
286 blob_zero(&cmd);
287 blob_append(&cmd, azDiffCmd[0], -1);
288 if( fSwapDiff ){
289 blob_append_escaped_arg(&cmd, zFile2);
290 blob_append_escaped_arg(&cmd, blob_str(&nameFile1));
291 }else{
292 blob_append_escaped_arg(&cmd, blob_str(&nameFile1));
293 blob_append_escaped_arg(&cmd, zFile2);
294 }
295
296 /* Run the external diff command */
297 fossil_system(blob_str(&cmd));
298 }else{
299 /* Construct an argv style array to do the external diff.
300 *
301 * This may look more complex, but it bypasses all of the
302 * stuff we have to wrap around system(3) because we get a
303 * direct binding of our argv[] to the callee's main(argv)
304 * on POSIX systems, entirely avoiding the class of double
305 * shell interpretation risks. (Windows's API flattens argv,
306 * neutering some of this mechanism's benefits, but not all.)
307 *
308 * Due to the legacy of existing settings, we can't assume
309 * it's safe to take this path when the setting is a single
310 * string value, since that gets converted to a 1-element
311 * array by db_get_array(). Fortunately, most diff type
312 * programs offer and benefit from option flags, so you
313 * should be able to upgrade to this method by re-applying
314 * your settings, making use of the new array storage option:
315 *
316 * $ fossil set diff-command colordiff -wu
317 *
318 * as opposed to the legacy method:
319 *
320 * $ fossil set diff-command 'colordiff -wu'
321 *
322 * The lack of quoting around those two setting values causes
323 * Fossil to store them as an array, which lands you in here.
324 */
325 azFullDiffCmd = fossil_malloc((nDiffCmdValues+3)*sizeof(char*));
326 for( n=0; n<nDiffCmdValues; ++n ) azFullDiffCmd[n] = azDiffCmd[n];
327 if( fSwapDiff ){
328 azFullDiffCmd[n++] = zFile2;
329 azFullDiffCmd[n++] = blob_str(&nameFile1);
330 }else{
331 azFullDiffCmd[n++] = blob_str(&nameFile1);
332 azFullDiffCmd[n++] = zFile2;
333 }
334 azFullDiffCmd[n] = 0;
335
336 /* Run the external diff command */
337 fossil_spawn(azFullDiffCmd[0], (char*const*)azFullDiffCmd);
338 }
339
340 /* Delete the temporary file and clean up memory used */
341 file_delete(blob_str(&nameFile1));
342 blob_reset(&nameFile1);
 
343 }
344 }
345
346 /*
347 ** Show the difference between two files, both in memory.
348 **
349 ** The difference is the set of edits needed to transform pFile1 into
350 ** pFile2.
351 **
352 ** Use the internal diff logic if nDiffCmdValues is 0. Otherwise pass the
353 ** command and args in azDiffCmd along with a pair of file names to
354 ** fossil_spawn() to do the diffing externally.
355 **
356 ** When using an external diff program, zBinGlob contains the GLOB patterns
357 ** for file names to treat as binary. If fIncludeBinary is zero, these files
358 ** will be skipped in addition to files that may contain binary content.
359 */
@@ -307,17 +361,18 @@
361 Blob *pFile1, /* In memory content to compare from */
362 Blob *pFile2, /* In memory content to compare to */
363 int isBin1, /* Does the 'from' content appear to be binary */
364 int isBin2, /* Does the 'to' content appear to be binary */
365 const char *zName, /* Display name of the file */
366 const char *azDiffCmd[], /* External diff cmd and optional args */
367 size_t nDiffCmdValues, /* Elements in azDiffCmd; 0 for internal */
368 const char *zBinGlob, /* Treat file names matching this as binary */
369 int fIncludeBinary, /* Include binary files for external diff */
370 u64 diffFlags /* Diff flags */
371 ){
372 if( diffFlags & DIFF_BRIEF ) return;
373 if( nDiffCmdValues==0 ){
374 Blob out; /* Diff output text */
375
376 blob_zero(&out);
377 text_diff(pFile1, pFile2, &out, 0, diffFlags);
378 if( diffFlags & DIFF_NUMSTAT ){
@@ -328,11 +383,12 @@
383 }
384
385 /* Release memory resources */
386 blob_reset(&out);
387 }else{
388 size_t n;
389 const char** azFullDiffCmd;
390 Blob temp1;
391 Blob temp2;
392
393 if( !fIncludeBinary ){
394 if( isBin1 || isBin2 ){
@@ -354,44 +410,58 @@
410 file_tempname(&temp1, zName, "before");
411 file_tempname(&temp2, zName, "after");
412 blob_write_to_file(pFile1, blob_str(&temp1));
413 blob_write_to_file(pFile2, blob_str(&temp2));
414
415 if( nDiffCmdValues==1 ) {
416 /* Legacy system(3) wrapper method; see diff_file for details. */
417 Blob cmd; /* Text of command to run */
418 blob_zero(&cmd);
419 blob_append(&cmd, azDiffCmd[0], -1);
420 blob_append_escaped_arg(&cmd, blob_str(&temp1));
421 blob_append_escaped_arg(&cmd, blob_str(&temp2));
422
423 /* Run the external diff command */
424 fossil_system(blob_str(&cmd));
425 }else{
426 /* Safer argv[] passing method; see diff_file for details. */
427 azFullDiffCmd = fossil_malloc((nDiffCmdValues+3)*sizeof(char*));
428 for( n=0; n<nDiffCmdValues; ++n ) azFullDiffCmd[n] = azDiffCmd[n];
429 azFullDiffCmd[n++] = blob_str(&temp1);
430 azFullDiffCmd[n++] = blob_str(&temp2);
431 azFullDiffCmd[n] = 0;
432
433 /* Run the external diff command */
434 fossil_spawn(azFullDiffCmd[0], (char*const*)azFullDiffCmd);
435 }
436
437 /* Delete the temporary file and clean up memory used */
438 file_delete(blob_str(&temp1));
439 file_delete(blob_str(&temp2));
440
441 blob_reset(&temp1);
442 blob_reset(&temp2);
 
443 }
444 }
445
446 /*
447 ** Run a diff between the version zFrom and files on disk. zFrom might
448 ** be NULL which means to simply show the difference between the edited
449 ** files on disk and the check-out on which they are based.
450 **
451 ** Use the internal diff logic if nDiffCmdValues is 0. Otherwise pass the
452 ** command and args in azDiffCmd along with a pair of file names to
453 ** fossil_spawn() to do the diffing externally.
454 **
455 ** When using an external diff program, zBinGlob contains the GLOB patterns
456 ** for file names to treat as binary. If fIncludeBinary is zero, these files
457 ** will be skipped in addition to files that may contain binary content.
458 */
459 void diff_against_disk(
460 const char *zFrom, /* Version to difference from */
461 const char *azDiffCmd[], /* External diff cmd and optional args */
462 size_t nDiffCmdValues, /* Elements in azDiffCmd; 0 for internal */
463 const char *zBinGlob, /* Treat file names matching this as binary */
464 int fIncludeBinary, /* Treat file names matching this as binary */
465 u64 diffFlags, /* Flags controlling diff output */
466 FileDirList *pFileDir, /* Which files to diff */
467 Blob *diffBlob /* Blob to output diff instead of stdout */
@@ -499,11 +569,11 @@
569 }else{
570 blob_zero(&content);
571 }
572 isBin = fIncludeBinary ? 0 : looks_like_binary(&content);
573 diff_print_index(zPathname, diffFlags, diffBlob);
574 diff_file(&content, isBin, zFullName, zPathname, azDiffCmd, nDiffCmdValues,
575 zBinGlob, fIncludeBinary, diffFlags, 0, diffBlob);
576 blob_reset(&content);
577 }
578 blob_reset(&fname);
579 }
@@ -512,19 +582,21 @@
582 }
583
584 /*
585 ** Run a diff between the undo buffer and files on disk.
586 **
587 ** Use the internal diff logic if nDiffCmdValues is 0. Otherwise pass the
588 ** command and args in azDiffCmd along with a pair of file names to
589 ** fossil_spawn() to do the diffing externally.
590 **
591 ** When using an external diff program, zBinGlob contains the GLOB patterns
592 ** for file names to treat as binary. If fIncludeBinary is zero, these files
593 ** will be skipped in addition to files that may contain binary content.
594 */
595 static void diff_against_undo(
596 const char *azDiffCmd[], /* External diff cmd and optional args */
597 size_t nDiffCmdValues, /* Elements in azDiffCmd; 0 for internal */
598 const char *zBinGlob, /* Treat file names matching this as binary */
599 int fIncludeBinary, /* Treat file names matching this as binary */
600 u64 diffFlags, /* Flags controlling diff output */
601 FileDirList *pFileDir /* List of files and directories to diff */
602 ){
@@ -537,11 +609,11 @@
609 const char *zFile = (const char*)db_column_text(&q, 0);
610 if( !file_dir_match(pFileDir, zFile) ) continue;
611 zFullName = mprintf("%s%s", g.zLocalRoot, zFile);
612 db_column_blob(&q, 1, &content);
613 diff_file(&content, 0, zFullName, zFile,
614 azDiffCmd, nDiffCmdValues, zBinGlob, fIncludeBinary, diffFlags, 0, 0);
615 fossil_free(zFullName);
616 blob_reset(&content);
617 }
618 db_finalize(&q);
619 }
@@ -548,21 +620,23 @@
620
621 /*
622 ** Show the difference between two files identified by ManifestFile
623 ** entries.
624 **
625 ** Use the internal diff logic if nDiffCmdValues is 0. Otherwise pass the
626 ** command and args in azDiffCmd along with a pair of file names to
627 ** fossil_spawn() to do the diffing externally.
628 **
629 ** When using an external diff program, zBinGlob contains the GLOB patterns
630 ** for file names to treat as binary. If fIncludeBinary is zero, these files
631 ** will be skipped in addition to files that may contain binary content.
632 */
633 static void diff_manifest_entry(
634 struct ManifestFile *pFrom,
635 struct ManifestFile *pTo,
636 const char * azDiffCmd[],
637 size_t nDiffCmdValues,
638 const char *zBinGlob,
639 int fIncludeBinary,
640 u64 diffFlags
641 ){
642 Blob f1, f2;
@@ -590,30 +664,32 @@
664 }else{
665 blob_zero(&f2);
666 }
667 isBin1 = fIncludeBinary ? 0 : looks_like_binary(&f1);
668 isBin2 = fIncludeBinary ? 0 : looks_like_binary(&f2);
669 diff_file_mem(&f1, &f2, isBin1, isBin2, zName, azDiffCmd, nDiffCmdValues,
670 zBinGlob, fIncludeBinary, diffFlags);
671 blob_reset(&f1);
672 blob_reset(&f2);
673 }
674
675 /*
676 ** Output the differences between two check-ins.
677 **
678 ** Use the internal diff logic if nDiffCmdValues is 0. Otherwise pass the
679 ** command and args in azDiffCmd along with a pair of file names to
680 ** fossil_spawn() to do the diffing externally.
681 **
682 ** When using an external diff program, zBinGlob contains the GLOB patterns
683 ** for file names to treat as binary. If fIncludeBinary is zero, these files
684 ** will be skipped in addition to files that may contain binary content.
685 */
686 static void diff_two_versions(
687 const char *zFrom,
688 const char *zTo,
689 const char *azDiffCmd[],
690 size_t nDiffCmdValues,
691 const char *zBinGlob,
692 int fIncludeBinary,
693 u64 diffFlags,
694 FileDirList *pFileDir
695 ){
@@ -641,11 +717,11 @@
717 if( file_dir_match(pFileDir, pFromFile->zName) ){
718 if( (diffFlags & DIFF_NUMSTAT)==0 ){
719 fossil_print("DELETED %s\n", pFromFile->zName);
720 }
721 if( asNewFlag ){
722 diff_manifest_entry(pFromFile, 0, azDiffCmd, nDiffCmdValues, zBinGlob,
723 fIncludeBinary, diffFlags);
724 }
725 }
726 pFromFile = manifest_file_next(pFrom,0);
727 }else if( cmp>0 ){
@@ -652,11 +728,11 @@
728 if( file_dir_match(pFileDir, pToFile->zName) ){
729 if( (diffFlags & DIFF_NUMSTAT)==0 ){
730 fossil_print("ADDED %s\n", pToFile->zName);
731 }
732 if( asNewFlag ){
733 diff_manifest_entry(0, pToFile, azDiffCmd, nDiffCmdValues, zBinGlob,
734 fIncludeBinary, diffFlags);
735 }
736 }
737 pToFile = manifest_file_next(pTo,0);
738 }else if( fossil_strcmp(pFromFile->zUuid, pToFile->zUuid)==0 ){
@@ -667,11 +743,11 @@
743 }else{
744 if( file_dir_match(pFileDir, pToFile->zName) ){
745 if( diffFlags & DIFF_BRIEF ){
746 fossil_print("CHANGED %s\n", pFromFile->zName);
747 }else{
748 diff_manifest_entry(pFromFile, pToFile, azDiffCmd, nDiffCmdValues, zBinGlob,
749 fIncludeBinary, diffFlags);
750 }
751 }
752 pFromFile = manifest_file_next(pFrom,0);
753 pToFile = manifest_file_next(pTo,0);
@@ -683,26 +759,26 @@
759
760 /*
761 ** Return the name of the external diff command, or return NULL if
762 ** no external diff command is defined.
763 */
764 const char ** diff_command_external(size_t *pnValues, int guiDiff){
765 const char *zDefault;
766 const char *zName;
767
768 if( guiDiff ){
769 #if defined(_WIN32)
770 zDefault = "[\"WinDiff.exe\"]";
771 #else
772 zDefault = 0;
773 #endif
774 zName = "gdiff-command";
775 }else{
776 zDefault = 0;
777 zName = "diff-command";
778 }
779 return db_get_array(pnValues, zName, zDefault);
780 }
781
782 /*
783 ** Show diff output in a Tcl/Tk window, in response to the --tk option
784 ** to the diff command.
@@ -870,11 +946,12 @@
946 int verboseFlag; /* True if -v or --verbose flag is used */
947 const char *zFrom; /* Source version number */
948 const char *zTo; /* Target version number */
949 const char *zCheckin; /* Check-in version number */
950 const char *zBranch; /* Branch to diff */
951 const char **azDiffCmd; /* External diff cmd and optional args */
952 size_t nDiffCmdValues = 0; /* Number of elements in azDiffCmd; 0 for internal */
953 const char *zBinGlob = 0; /* Treat file names matching this as binary */
954 int fIncludeBinary = 0; /* Include binary files for external diff */
955 int againstUndo = 0; /* Diff against files in the undo buffer */
956 u64 diffFlags = 0; /* Flags to control the DIFF */
957 FileDirList *pFileDir = 0; /* Restrict the diff to these files */
@@ -917,12 +994,19 @@
994 fossil_fatal("must use --from if --to is present");
995 }else{
996 db_find_and_open_repository(0, 0);
997 }
998 if( !isInternDiff ){
999 const char *zDiffCmd = find_option("command", 0, 1);
1000 if( zDiffCmd ){
1001 azDiffCmd = malloc(2*sizeof(char*));
1002 azDiffCmd[0] = zDiffCmd;
1003 azDiffCmd[1] = 0;
1004 nDiffCmdValues = 1;
1005 }else{
1006 azDiffCmd = diff_command_external(&nDiffCmdValues, isGDiff);
1007 }
1008 }
1009 zBinGlob = diff_get_binary_glob();
1010 fIncludeBinary = diff_include_binary_files();
1011 determine_exec_relative_option(1);
1012 verify_all_options();
@@ -958,17 +1042,17 @@
1042 if( againstUndo ){
1043 if( db_lget_int("undo_available",0)==0 ){
1044 fossil_print("No undo or redo is available\n");
1045 return;
1046 }
1047 diff_against_undo(azDiffCmd, nDiffCmdValues, zBinGlob, fIncludeBinary,
1048 diffFlags, pFileDir);
1049 }else if( zTo==0 ){
1050 diff_against_disk(zFrom, azDiffCmd, nDiffCmdValues, zBinGlob, fIncludeBinary,
1051 diffFlags, pFileDir, 0);
1052 }else{
1053 diff_two_versions(zFrom, zTo, azDiffCmd, nDiffCmdValues, zBinGlob, fIncludeBinary,
1054 diffFlags, pFileDir);
1055 }
1056 if( pFileDir ){
1057 int i;
1058 for(i=0; pFileDir[i].zName; i++){
@@ -1000,7 +1084,7 @@
1084 login_check_credentials();
1085 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1086 if( zFrom==0 || zTo==0 ) fossil_redirect_home();
1087
1088 cgi_set_content_type("text/plain");
1089 diff_two_versions(zFrom, zTo, 0, 0, 0, 0, DIFF_VERBOSE, 0);
1090 }
1091
+11 -9
--- src/stash.c
+++ src/stash.c
@@ -402,11 +402,12 @@
402402
/*
403403
** Show the diffs associate with a single stash.
404404
*/
405405
static void stash_diff(
406406
int stashid, /* The stash entry to diff */
407
- const char *zDiffCmd, /* Command used for diffing */
407
+ const char *azDiffCmd[], /* External diff cmd and optional args */
408
+ size_t nDiffCmdValues, /* Elements in azDiffCmd; 0 for internal */
408409
const char *zBinGlob, /* GLOB pattern to determine binary files */
409410
int fBaseline, /* Diff against original baseline check-in if true */
410411
int fIncludeBinary, /* Do diffs against binary files */
411412
u64 diffFlags /* Other diff flags */
412413
){
@@ -431,20 +432,20 @@
431432
db_ephemeral_blob(&q, 6, &a);
432433
fossil_print("ADDED %s\n", zNew);
433434
diff_print_index(zNew, diffFlags, 0);
434435
isBin1 = 0;
435436
isBin2 = fIncludeBinary ? 0 : looks_like_binary(&a);
436
- diff_file_mem(&empty, &a, isBin1, isBin2, zNew, zDiffCmd,
437
+ diff_file_mem(&empty, &a, isBin1, isBin2, zNew, azDiffCmd, nDiffCmdValues,
437438
zBinGlob, fIncludeBinary, diffFlags);
438439
}else if( isRemoved ){
439440
fossil_print("DELETE %s\n", zOrig);
440441
diff_print_index(zNew, diffFlags, 0);
441442
isBin2 = 0;
442443
if( fBaseline ){
443444
content_get(rid, &a);
444445
isBin1 = fIncludeBinary ? 0 : looks_like_binary(&a);
445
- diff_file_mem(&a, &empty, isBin1, isBin2, zOrig, zDiffCmd,
446
+ diff_file_mem(&a, &empty, isBin1, isBin2, zOrig, azDiffCmd, nDiffCmdValues,
446447
zBinGlob, fIncludeBinary, diffFlags);
447448
}else{
448449
}
449450
}else{
450451
Blob delta;
@@ -460,15 +461,15 @@
460461
blob_delta_apply(&a, &delta, &b);
461462
isBin1 = fIncludeBinary ? 0 : looks_like_binary(&a);
462463
isBin2 = fIncludeBinary ? 0 : looks_like_binary(&b);
463464
if( fBaseline ){
464465
diff_file_mem(&a, &b, isBin1, isBin2, zNew,
465
- zDiffCmd, zBinGlob, fIncludeBinary, diffFlags);
466
+ azDiffCmd, nDiffCmdValues, zBinGlob, fIncludeBinary, diffFlags);
466467
}else{
467468
/*Diff with file on disk using fSwapDiff=1 to show the diff in the
468469
same direction as if fBaseline=1.*/
469
- diff_file(&b, isBin2, zOPath, zNew, zDiffCmd,
470
+ diff_file(&b, isBin2, zOPath, zNew, azDiffCmd, nDiffCmdValues,
470471
zBinGlob, fIncludeBinary, diffFlags, 1, 0);
471472
}
472473
blob_reset(&a);
473474
blob_reset(&b);
474475
}
@@ -738,11 +739,12 @@
738739
|| memcmp(zCmd, "show", nCmd)==0
739740
|| memcmp(zCmd, "gshow", nCmd)==0
740741
|| memcmp(zCmd, "cat", nCmd)==0
741742
|| memcmp(zCmd, "gcat", nCmd)==0
742743
){
743
- const char *zDiffCmd = 0;
744
+ const char **azDiffCmd = 0;
745
+ size_t nDiffCmdValues = 0;
744746
const char *zBinGlob = 0;
745747
int fIncludeBinary = 0;
746748
int fBaseline = 0;
747749
u64 diffFlags;
748750
@@ -753,21 +755,21 @@
753755
db_close(0);
754756
diff_tk(fBaseline ? "stash show" : "stash diff", 3);
755757
return;
756758
}
757759
if( find_option("internal","i",0)==0 ){
758
- zDiffCmd = diff_command_external(zCmd[0]=='g');
760
+ azDiffCmd = diff_command_external(&nDiffCmdValues, zCmd[0]=='g');
759761
}
760762
diffFlags = diff_options();
761763
if( find_option("verbose","v",0)!=0 ) diffFlags |= DIFF_VERBOSE;
762764
if( g.argc>4 ) usage(mprintf("%s ?STASHID? ?DIFF-OPTIONS?", zCmd));
763
- if( zDiffCmd ){
765
+ if( nDiffCmdValues==0 ){
764766
zBinGlob = diff_get_binary_glob();
765767
fIncludeBinary = diff_include_binary_files();
766768
}
767769
stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
768
- stash_diff(stashid, zDiffCmd, zBinGlob, fBaseline, fIncludeBinary,
770
+ stash_diff(stashid, azDiffCmd, nDiffCmdValues, zBinGlob, fBaseline, fIncludeBinary,
769771
diffFlags);
770772
}else
771773
if( memcmp(zCmd, "help", nCmd)==0 ){
772774
g.argv[1] = "help";
773775
g.argv[2] = "stash";
774776
--- src/stash.c
+++ src/stash.c
@@ -402,11 +402,12 @@
402 /*
403 ** Show the diffs associate with a single stash.
404 */
405 static void stash_diff(
406 int stashid, /* The stash entry to diff */
407 const char *zDiffCmd, /* Command used for diffing */
 
408 const char *zBinGlob, /* GLOB pattern to determine binary files */
409 int fBaseline, /* Diff against original baseline check-in if true */
410 int fIncludeBinary, /* Do diffs against binary files */
411 u64 diffFlags /* Other diff flags */
412 ){
@@ -431,20 +432,20 @@
431 db_ephemeral_blob(&q, 6, &a);
432 fossil_print("ADDED %s\n", zNew);
433 diff_print_index(zNew, diffFlags, 0);
434 isBin1 = 0;
435 isBin2 = fIncludeBinary ? 0 : looks_like_binary(&a);
436 diff_file_mem(&empty, &a, isBin1, isBin2, zNew, zDiffCmd,
437 zBinGlob, fIncludeBinary, diffFlags);
438 }else if( isRemoved ){
439 fossil_print("DELETE %s\n", zOrig);
440 diff_print_index(zNew, diffFlags, 0);
441 isBin2 = 0;
442 if( fBaseline ){
443 content_get(rid, &a);
444 isBin1 = fIncludeBinary ? 0 : looks_like_binary(&a);
445 diff_file_mem(&a, &empty, isBin1, isBin2, zOrig, zDiffCmd,
446 zBinGlob, fIncludeBinary, diffFlags);
447 }else{
448 }
449 }else{
450 Blob delta;
@@ -460,15 +461,15 @@
460 blob_delta_apply(&a, &delta, &b);
461 isBin1 = fIncludeBinary ? 0 : looks_like_binary(&a);
462 isBin2 = fIncludeBinary ? 0 : looks_like_binary(&b);
463 if( fBaseline ){
464 diff_file_mem(&a, &b, isBin1, isBin2, zNew,
465 zDiffCmd, zBinGlob, fIncludeBinary, diffFlags);
466 }else{
467 /*Diff with file on disk using fSwapDiff=1 to show the diff in the
468 same direction as if fBaseline=1.*/
469 diff_file(&b, isBin2, zOPath, zNew, zDiffCmd,
470 zBinGlob, fIncludeBinary, diffFlags, 1, 0);
471 }
472 blob_reset(&a);
473 blob_reset(&b);
474 }
@@ -738,11 +739,12 @@
738 || memcmp(zCmd, "show", nCmd)==0
739 || memcmp(zCmd, "gshow", nCmd)==0
740 || memcmp(zCmd, "cat", nCmd)==0
741 || memcmp(zCmd, "gcat", nCmd)==0
742 ){
743 const char *zDiffCmd = 0;
 
744 const char *zBinGlob = 0;
745 int fIncludeBinary = 0;
746 int fBaseline = 0;
747 u64 diffFlags;
748
@@ -753,21 +755,21 @@
753 db_close(0);
754 diff_tk(fBaseline ? "stash show" : "stash diff", 3);
755 return;
756 }
757 if( find_option("internal","i",0)==0 ){
758 zDiffCmd = diff_command_external(zCmd[0]=='g');
759 }
760 diffFlags = diff_options();
761 if( find_option("verbose","v",0)!=0 ) diffFlags |= DIFF_VERBOSE;
762 if( g.argc>4 ) usage(mprintf("%s ?STASHID? ?DIFF-OPTIONS?", zCmd));
763 if( zDiffCmd ){
764 zBinGlob = diff_get_binary_glob();
765 fIncludeBinary = diff_include_binary_files();
766 }
767 stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
768 stash_diff(stashid, zDiffCmd, zBinGlob, fBaseline, fIncludeBinary,
769 diffFlags);
770 }else
771 if( memcmp(zCmd, "help", nCmd)==0 ){
772 g.argv[1] = "help";
773 g.argv[2] = "stash";
774
--- src/stash.c
+++ src/stash.c
@@ -402,11 +402,12 @@
402 /*
403 ** Show the diffs associate with a single stash.
404 */
405 static void stash_diff(
406 int stashid, /* The stash entry to diff */
407 const char *azDiffCmd[], /* External diff cmd and optional args */
408 size_t nDiffCmdValues, /* Elements in azDiffCmd; 0 for internal */
409 const char *zBinGlob, /* GLOB pattern to determine binary files */
410 int fBaseline, /* Diff against original baseline check-in if true */
411 int fIncludeBinary, /* Do diffs against binary files */
412 u64 diffFlags /* Other diff flags */
413 ){
@@ -431,20 +432,20 @@
432 db_ephemeral_blob(&q, 6, &a);
433 fossil_print("ADDED %s\n", zNew);
434 diff_print_index(zNew, diffFlags, 0);
435 isBin1 = 0;
436 isBin2 = fIncludeBinary ? 0 : looks_like_binary(&a);
437 diff_file_mem(&empty, &a, isBin1, isBin2, zNew, azDiffCmd, nDiffCmdValues,
438 zBinGlob, fIncludeBinary, diffFlags);
439 }else if( isRemoved ){
440 fossil_print("DELETE %s\n", zOrig);
441 diff_print_index(zNew, diffFlags, 0);
442 isBin2 = 0;
443 if( fBaseline ){
444 content_get(rid, &a);
445 isBin1 = fIncludeBinary ? 0 : looks_like_binary(&a);
446 diff_file_mem(&a, &empty, isBin1, isBin2, zOrig, azDiffCmd, nDiffCmdValues,
447 zBinGlob, fIncludeBinary, diffFlags);
448 }else{
449 }
450 }else{
451 Blob delta;
@@ -460,15 +461,15 @@
461 blob_delta_apply(&a, &delta, &b);
462 isBin1 = fIncludeBinary ? 0 : looks_like_binary(&a);
463 isBin2 = fIncludeBinary ? 0 : looks_like_binary(&b);
464 if( fBaseline ){
465 diff_file_mem(&a, &b, isBin1, isBin2, zNew,
466 azDiffCmd, nDiffCmdValues, zBinGlob, fIncludeBinary, diffFlags);
467 }else{
468 /*Diff with file on disk using fSwapDiff=1 to show the diff in the
469 same direction as if fBaseline=1.*/
470 diff_file(&b, isBin2, zOPath, zNew, azDiffCmd, nDiffCmdValues,
471 zBinGlob, fIncludeBinary, diffFlags, 1, 0);
472 }
473 blob_reset(&a);
474 blob_reset(&b);
475 }
@@ -738,11 +739,12 @@
739 || memcmp(zCmd, "show", nCmd)==0
740 || memcmp(zCmd, "gshow", nCmd)==0
741 || memcmp(zCmd, "cat", nCmd)==0
742 || memcmp(zCmd, "gcat", nCmd)==0
743 ){
744 const char **azDiffCmd = 0;
745 size_t nDiffCmdValues = 0;
746 const char *zBinGlob = 0;
747 int fIncludeBinary = 0;
748 int fBaseline = 0;
749 u64 diffFlags;
750
@@ -753,21 +755,21 @@
755 db_close(0);
756 diff_tk(fBaseline ? "stash show" : "stash diff", 3);
757 return;
758 }
759 if( find_option("internal","i",0)==0 ){
760 azDiffCmd = diff_command_external(&nDiffCmdValues, zCmd[0]=='g');
761 }
762 diffFlags = diff_options();
763 if( find_option("verbose","v",0)!=0 ) diffFlags |= DIFF_VERBOSE;
764 if( g.argc>4 ) usage(mprintf("%s ?STASHID? ?DIFF-OPTIONS?", zCmd));
765 if( nDiffCmdValues==0 ){
766 zBinGlob = diff_get_binary_glob();
767 fIncludeBinary = diff_include_binary_files();
768 }
769 stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
770 stash_diff(stashid, azDiffCmd, nDiffCmdValues, zBinGlob, fBaseline, fIncludeBinary,
771 diffFlags);
772 }else
773 if( memcmp(zCmd, "help", nCmd)==0 ){
774 g.argv[1] = "help";
775 g.argv[2] = "stash";
776

Keyboard Shortcuts

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