Fossil SCM
If the committed file has CR/NL or UTF-16 (or both), give the user the possibility to convert it to resp NL or UTF-8 (or both) without committing
Commit
c6223a8e2a380c07993fd34d4ea02904f3bea939
Parent
db0c512767ac7e8…
1 file changed
+39
-8
+39
-8
| --- src/checkin.c | ||
| +++ src/checkin.c | ||
| @@ -890,13 +890,17 @@ | ||
| 890 | 890 | |
| 891 | 891 | /* |
| 892 | 892 | ** Issue a warning and give the user an opportunity to abandon out |
| 893 | 893 | ** if a Unicode (UTF-16) byte-order-mark (BOM) or a \r\n line ending |
| 894 | 894 | ** is seen in a text file. |
| 895 | +** | |
| 896 | +** Return 1 if the user pressed 'c'. In that case, the file will have | |
| 897 | +** been converted to UTF-8 (if it was UTF-16) with NL line-endings, | |
| 898 | +** and the original file will have been renamed to "<filename>-original". | |
| 895 | 899 | */ |
| 896 | -static void commit_warning( | |
| 897 | - const Blob *p, /* The content of the file being committed. */ | |
| 900 | +static int commit_warning( | |
| 901 | + Blob *p, /* The content of the file being committed. */ | |
| 898 | 902 | int crnlOk, /* Non-zero if CR/NL warnings should be disabled. */ |
| 899 | 903 | int binOk, /* Non-zero if binary warnings should be disabled. */ |
| 900 | 904 | const char *zFilename /* The full name of the file being committed. */ |
| 901 | 905 | ){ |
| 902 | 906 | int eType; /* return value of looks_like_utf8/utf16() */ |
| @@ -903,50 +907,74 @@ | ||
| 903 | 907 | int fUnicode; /* return value of starts_with_utf16_bom() */ |
| 904 | 908 | char *zMsg; /* Warning message */ |
| 905 | 909 | Blob fname; /* Relative pathname of the file */ |
| 906 | 910 | static int allOk = 0; /* Set to true to disable this routine */ |
| 907 | 911 | |
| 908 | - if( allOk ) return; | |
| 912 | + if( allOk ) return 0; | |
| 909 | 913 | fUnicode = starts_with_utf16_bom(p); |
| 910 | 914 | eType = fUnicode ? looks_like_utf16(p) : looks_like_utf8(p); |
| 911 | 915 | if( eType==0 || eType==-1 || fUnicode ){ |
| 912 | 916 | const char *zWarning; |
| 917 | + const char *c = "c=convert/"; | |
| 913 | 918 | Blob ans; |
| 914 | 919 | char cReply; |
| 915 | 920 | |
| 916 | 921 | if( eType==-1 && fUnicode ){ |
| 917 | 922 | zWarning = "Unicode and CR/NL line endings"; |
| 918 | 923 | }else if( eType==-1 ){ |
| 919 | 924 | if( crnlOk ){ |
| 920 | - return; /* We don't want CR/NL warnings for this file. */ | |
| 925 | + return 0; /* We don't want CR/NL warnings for this file. */ | |
| 921 | 926 | } |
| 922 | 927 | zWarning = "CR/NL line endings"; |
| 923 | 928 | }else if( eType==0 ){ |
| 924 | 929 | if( binOk ){ |
| 925 | - return; /* We don't want binary warnings for this file. */ | |
| 930 | + return 0; /* We don't want binary warnings for this file. */ | |
| 926 | 931 | } |
| 927 | 932 | zWarning = "binary data"; |
| 928 | 933 | }else{ |
| 929 | 934 | zWarning = "Unicode"; |
| 935 | +#ifndef _WIN32 | |
| 936 | + c = ""; /* On UNIX, we cannot convert unicode files */ | |
| 937 | +#endif | |
| 930 | 938 | } |
| 931 | 939 | file_relative_name(zFilename, &fname, 0); |
| 932 | 940 | blob_zero(&ans); |
| 933 | 941 | zMsg = mprintf( |
| 934 | - "%s contains %s. commit anyhow (a=all/y/N)? ", | |
| 935 | - blob_str(&fname), zWarning); | |
| 942 | + "%s contains %s. commit anyhow (a=all/%sy/N)? ", | |
| 943 | + blob_str(&fname), zWarning, c); | |
| 936 | 944 | prompt_user(zMsg, &ans); |
| 937 | 945 | fossil_free(zMsg); |
| 938 | 946 | cReply = blob_str(&ans)[0]; |
| 939 | 947 | if( cReply=='a' || cReply=='A' ){ |
| 940 | 948 | allOk = 1; |
| 949 | + }else if( (cReply=='c' || cReply=='C') | |
| 950 | +#ifndef _WIN32 | |
| 951 | + && fUnicode | |
| 952 | +#endif | |
| 953 | + ){ | |
| 954 | + char *zOrig = file_newname(zFilename, "original", 1); | |
| 955 | + FILE *f; | |
| 956 | + blob_write_to_file(p, zOrig); | |
| 957 | + fossil_free(zOrig); | |
| 958 | + f = fossil_fopen(zFilename, "wb"); | |
| 959 | + if( fUnicode ) { | |
| 960 | + static const unsigned char bom[] = { 0xEF, 0xBB, 0xBF }; | |
| 961 | + fwrite(bom, 1, 3, f); | |
| 962 | + blob_strip_bom(p, 0); | |
| 963 | + } | |
| 964 | + blob_remove_cr(p); | |
| 965 | + fwrite(blob_buffer(p), 1, blob_size(p), f); | |
| 966 | + fclose(f); | |
| 967 | + return 1; | |
| 941 | 968 | }else if( cReply!='y' && cReply!='Y' ){ |
| 942 | 969 | fossil_fatal("Abandoning commit due to %s in %s", |
| 943 | 970 | zWarning, blob_str(&fname)); |
| 944 | 971 | } |
| 945 | 972 | blob_reset(&ans); |
| 946 | 973 | blob_reset(&fname); |
| 947 | 974 | } |
| 975 | + return 0; | |
| 948 | 976 | } |
| 949 | 977 | |
| 950 | 978 | /* |
| 951 | 979 | ** qsort() comparison routine for an array of pointers to strings. |
| 952 | 980 | */ |
| @@ -1044,10 +1072,11 @@ | ||
| 1044 | 1072 | Blob cksum1, cksum2; /* Before and after commit checksums */ |
| 1045 | 1073 | Blob cksum1b; /* Checksum recorded in the manifest */ |
| 1046 | 1074 | int szD; /* Size of the delta manifest */ |
| 1047 | 1075 | int szB; /* Size of the baseline manifest */ |
| 1048 | 1076 | int nConflict = 0; /* Number of unresolved merge conflicts */ |
| 1077 | + int abortCommit = 0; | |
| 1049 | 1078 | Blob ans; |
| 1050 | 1079 | char cReply; |
| 1051 | 1080 | |
| 1052 | 1081 | url_proxy_options(); |
| 1053 | 1082 | noSign = find_option("nosign",0,0)!=0; |
| @@ -1278,11 +1307,11 @@ | ||
| 1278 | 1307 | /* Instead of file content, put link destination path */ |
| 1279 | 1308 | blob_read_link(&content, zFullname); |
| 1280 | 1309 | }else{ |
| 1281 | 1310 | blob_read_from_file(&content, zFullname); |
| 1282 | 1311 | } |
| 1283 | - commit_warning(&content, crnlOk, binOk, zFullname); | |
| 1312 | + abortCommit |= commit_warning(&content, crnlOk, binOk, zFullname); | |
| 1284 | 1313 | if( chnged==1 && contains_merge_marker(&content) ){ |
| 1285 | 1314 | Blob fname; /* Relative pathname of the file */ |
| 1286 | 1315 | |
| 1287 | 1316 | nConflict++; |
| 1288 | 1317 | file_relative_name(zFullname, &fname, 0); |
| @@ -1299,10 +1328,12 @@ | ||
| 1299 | 1328 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid); |
| 1300 | 1329 | } |
| 1301 | 1330 | db_finalize(&q); |
| 1302 | 1331 | if( nConflict && !allowConflict ){ |
| 1303 | 1332 | fossil_fatal("abort due to unresolve merge conflicts"); |
| 1333 | + } else if( abortCommit ){ | |
| 1334 | + fossil_fatal("files are converted on your request. Please re-test before committing"); | |
| 1304 | 1335 | } |
| 1305 | 1336 | |
| 1306 | 1337 | /* Create the new manifest */ |
| 1307 | 1338 | if( blob_size(&comment)==0 ){ |
| 1308 | 1339 | blob_append(&comment, "(no comment)", -1); |
| 1309 | 1340 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -890,13 +890,17 @@ | |
| 890 | |
| 891 | /* |
| 892 | ** Issue a warning and give the user an opportunity to abandon out |
| 893 | ** if a Unicode (UTF-16) byte-order-mark (BOM) or a \r\n line ending |
| 894 | ** is seen in a text file. |
| 895 | */ |
| 896 | static void commit_warning( |
| 897 | const Blob *p, /* The content of the file being committed. */ |
| 898 | int crnlOk, /* Non-zero if CR/NL warnings should be disabled. */ |
| 899 | int binOk, /* Non-zero if binary warnings should be disabled. */ |
| 900 | const char *zFilename /* The full name of the file being committed. */ |
| 901 | ){ |
| 902 | int eType; /* return value of looks_like_utf8/utf16() */ |
| @@ -903,50 +907,74 @@ | |
| 903 | int fUnicode; /* return value of starts_with_utf16_bom() */ |
| 904 | char *zMsg; /* Warning message */ |
| 905 | Blob fname; /* Relative pathname of the file */ |
| 906 | static int allOk = 0; /* Set to true to disable this routine */ |
| 907 | |
| 908 | if( allOk ) return; |
| 909 | fUnicode = starts_with_utf16_bom(p); |
| 910 | eType = fUnicode ? looks_like_utf16(p) : looks_like_utf8(p); |
| 911 | if( eType==0 || eType==-1 || fUnicode ){ |
| 912 | const char *zWarning; |
| 913 | Blob ans; |
| 914 | char cReply; |
| 915 | |
| 916 | if( eType==-1 && fUnicode ){ |
| 917 | zWarning = "Unicode and CR/NL line endings"; |
| 918 | }else if( eType==-1 ){ |
| 919 | if( crnlOk ){ |
| 920 | return; /* We don't want CR/NL warnings for this file. */ |
| 921 | } |
| 922 | zWarning = "CR/NL line endings"; |
| 923 | }else if( eType==0 ){ |
| 924 | if( binOk ){ |
| 925 | return; /* We don't want binary warnings for this file. */ |
| 926 | } |
| 927 | zWarning = "binary data"; |
| 928 | }else{ |
| 929 | zWarning = "Unicode"; |
| 930 | } |
| 931 | file_relative_name(zFilename, &fname, 0); |
| 932 | blob_zero(&ans); |
| 933 | zMsg = mprintf( |
| 934 | "%s contains %s. commit anyhow (a=all/y/N)? ", |
| 935 | blob_str(&fname), zWarning); |
| 936 | prompt_user(zMsg, &ans); |
| 937 | fossil_free(zMsg); |
| 938 | cReply = blob_str(&ans)[0]; |
| 939 | if( cReply=='a' || cReply=='A' ){ |
| 940 | allOk = 1; |
| 941 | }else if( cReply!='y' && cReply!='Y' ){ |
| 942 | fossil_fatal("Abandoning commit due to %s in %s", |
| 943 | zWarning, blob_str(&fname)); |
| 944 | } |
| 945 | blob_reset(&ans); |
| 946 | blob_reset(&fname); |
| 947 | } |
| 948 | } |
| 949 | |
| 950 | /* |
| 951 | ** qsort() comparison routine for an array of pointers to strings. |
| 952 | */ |
| @@ -1044,10 +1072,11 @@ | |
| 1044 | Blob cksum1, cksum2; /* Before and after commit checksums */ |
| 1045 | Blob cksum1b; /* Checksum recorded in the manifest */ |
| 1046 | int szD; /* Size of the delta manifest */ |
| 1047 | int szB; /* Size of the baseline manifest */ |
| 1048 | int nConflict = 0; /* Number of unresolved merge conflicts */ |
| 1049 | Blob ans; |
| 1050 | char cReply; |
| 1051 | |
| 1052 | url_proxy_options(); |
| 1053 | noSign = find_option("nosign",0,0)!=0; |
| @@ -1278,11 +1307,11 @@ | |
| 1278 | /* Instead of file content, put link destination path */ |
| 1279 | blob_read_link(&content, zFullname); |
| 1280 | }else{ |
| 1281 | blob_read_from_file(&content, zFullname); |
| 1282 | } |
| 1283 | commit_warning(&content, crnlOk, binOk, zFullname); |
| 1284 | if( chnged==1 && contains_merge_marker(&content) ){ |
| 1285 | Blob fname; /* Relative pathname of the file */ |
| 1286 | |
| 1287 | nConflict++; |
| 1288 | file_relative_name(zFullname, &fname, 0); |
| @@ -1299,10 +1328,12 @@ | |
| 1299 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid); |
| 1300 | } |
| 1301 | db_finalize(&q); |
| 1302 | if( nConflict && !allowConflict ){ |
| 1303 | fossil_fatal("abort due to unresolve merge conflicts"); |
| 1304 | } |
| 1305 | |
| 1306 | /* Create the new manifest */ |
| 1307 | if( blob_size(&comment)==0 ){ |
| 1308 | blob_append(&comment, "(no comment)", -1); |
| 1309 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -890,13 +890,17 @@ | |
| 890 | |
| 891 | /* |
| 892 | ** Issue a warning and give the user an opportunity to abandon out |
| 893 | ** if a Unicode (UTF-16) byte-order-mark (BOM) or a \r\n line ending |
| 894 | ** is seen in a text file. |
| 895 | ** |
| 896 | ** Return 1 if the user pressed 'c'. In that case, the file will have |
| 897 | ** been converted to UTF-8 (if it was UTF-16) with NL line-endings, |
| 898 | ** and the original file will have been renamed to "<filename>-original". |
| 899 | */ |
| 900 | static int commit_warning( |
| 901 | Blob *p, /* The content of the file being committed. */ |
| 902 | int crnlOk, /* Non-zero if CR/NL warnings should be disabled. */ |
| 903 | int binOk, /* Non-zero if binary warnings should be disabled. */ |
| 904 | const char *zFilename /* The full name of the file being committed. */ |
| 905 | ){ |
| 906 | int eType; /* return value of looks_like_utf8/utf16() */ |
| @@ -903,50 +907,74 @@ | |
| 907 | int fUnicode; /* return value of starts_with_utf16_bom() */ |
| 908 | char *zMsg; /* Warning message */ |
| 909 | Blob fname; /* Relative pathname of the file */ |
| 910 | static int allOk = 0; /* Set to true to disable this routine */ |
| 911 | |
| 912 | if( allOk ) return 0; |
| 913 | fUnicode = starts_with_utf16_bom(p); |
| 914 | eType = fUnicode ? looks_like_utf16(p) : looks_like_utf8(p); |
| 915 | if( eType==0 || eType==-1 || fUnicode ){ |
| 916 | const char *zWarning; |
| 917 | const char *c = "c=convert/"; |
| 918 | Blob ans; |
| 919 | char cReply; |
| 920 | |
| 921 | if( eType==-1 && fUnicode ){ |
| 922 | zWarning = "Unicode and CR/NL line endings"; |
| 923 | }else if( eType==-1 ){ |
| 924 | if( crnlOk ){ |
| 925 | return 0; /* We don't want CR/NL warnings for this file. */ |
| 926 | } |
| 927 | zWarning = "CR/NL line endings"; |
| 928 | }else if( eType==0 ){ |
| 929 | if( binOk ){ |
| 930 | return 0; /* We don't want binary warnings for this file. */ |
| 931 | } |
| 932 | zWarning = "binary data"; |
| 933 | }else{ |
| 934 | zWarning = "Unicode"; |
| 935 | #ifndef _WIN32 |
| 936 | c = ""; /* On UNIX, we cannot convert unicode files */ |
| 937 | #endif |
| 938 | } |
| 939 | file_relative_name(zFilename, &fname, 0); |
| 940 | blob_zero(&ans); |
| 941 | zMsg = mprintf( |
| 942 | "%s contains %s. commit anyhow (a=all/%sy/N)? ", |
| 943 | blob_str(&fname), zWarning, c); |
| 944 | prompt_user(zMsg, &ans); |
| 945 | fossil_free(zMsg); |
| 946 | cReply = blob_str(&ans)[0]; |
| 947 | if( cReply=='a' || cReply=='A' ){ |
| 948 | allOk = 1; |
| 949 | }else if( (cReply=='c' || cReply=='C') |
| 950 | #ifndef _WIN32 |
| 951 | && fUnicode |
| 952 | #endif |
| 953 | ){ |
| 954 | char *zOrig = file_newname(zFilename, "original", 1); |
| 955 | FILE *f; |
| 956 | blob_write_to_file(p, zOrig); |
| 957 | fossil_free(zOrig); |
| 958 | f = fossil_fopen(zFilename, "wb"); |
| 959 | if( fUnicode ) { |
| 960 | static const unsigned char bom[] = { 0xEF, 0xBB, 0xBF }; |
| 961 | fwrite(bom, 1, 3, f); |
| 962 | blob_strip_bom(p, 0); |
| 963 | } |
| 964 | blob_remove_cr(p); |
| 965 | fwrite(blob_buffer(p), 1, blob_size(p), f); |
| 966 | fclose(f); |
| 967 | return 1; |
| 968 | }else if( cReply!='y' && cReply!='Y' ){ |
| 969 | fossil_fatal("Abandoning commit due to %s in %s", |
| 970 | zWarning, blob_str(&fname)); |
| 971 | } |
| 972 | blob_reset(&ans); |
| 973 | blob_reset(&fname); |
| 974 | } |
| 975 | return 0; |
| 976 | } |
| 977 | |
| 978 | /* |
| 979 | ** qsort() comparison routine for an array of pointers to strings. |
| 980 | */ |
| @@ -1044,10 +1072,11 @@ | |
| 1072 | Blob cksum1, cksum2; /* Before and after commit checksums */ |
| 1073 | Blob cksum1b; /* Checksum recorded in the manifest */ |
| 1074 | int szD; /* Size of the delta manifest */ |
| 1075 | int szB; /* Size of the baseline manifest */ |
| 1076 | int nConflict = 0; /* Number of unresolved merge conflicts */ |
| 1077 | int abortCommit = 0; |
| 1078 | Blob ans; |
| 1079 | char cReply; |
| 1080 | |
| 1081 | url_proxy_options(); |
| 1082 | noSign = find_option("nosign",0,0)!=0; |
| @@ -1278,11 +1307,11 @@ | |
| 1307 | /* Instead of file content, put link destination path */ |
| 1308 | blob_read_link(&content, zFullname); |
| 1309 | }else{ |
| 1310 | blob_read_from_file(&content, zFullname); |
| 1311 | } |
| 1312 | abortCommit |= commit_warning(&content, crnlOk, binOk, zFullname); |
| 1313 | if( chnged==1 && contains_merge_marker(&content) ){ |
| 1314 | Blob fname; /* Relative pathname of the file */ |
| 1315 | |
| 1316 | nConflict++; |
| 1317 | file_relative_name(zFullname, &fname, 0); |
| @@ -1299,10 +1328,12 @@ | |
| 1328 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid); |
| 1329 | } |
| 1330 | db_finalize(&q); |
| 1331 | if( nConflict && !allowConflict ){ |
| 1332 | fossil_fatal("abort due to unresolve merge conflicts"); |
| 1333 | } else if( abortCommit ){ |
| 1334 | fossil_fatal("files are converted on your request. Please re-test before committing"); |
| 1335 | } |
| 1336 | |
| 1337 | /* Create the new manifest */ |
| 1338 | if( blob_size(&comment)==0 ){ |
| 1339 | blob_append(&comment, "(no comment)", -1); |
| 1340 |