Fossil SCM
Allow unicode files for Wiki-rendering on Windows. <p>Trying to commit a file with an UTF-16 BOM will now give a warning, just as a file containing crlf
Commit
70b4f105ebaab44c807092d06c74943768c00391
Parent
2a15d87edbd7948…
6 files changed
+19
-8
+19
-8
+10
-2
+3
-13
+8
-18
+8
-18
+19
-8
| --- src/checkin.c | ||
| +++ src/checkin.c | ||
| @@ -882,35 +882,46 @@ | ||
| 882 | 882 | if( pnFBcard ) *pnFBcard = nFBcard; |
| 883 | 883 | } |
| 884 | 884 | |
| 885 | 885 | /* |
| 886 | 886 | ** Issue a warning and give the user an opportunity to abandon out |
| 887 | -** if a \r\n line ending is seen in a text file. | |
| 887 | +** if unicode or a \r\n line ending is seen in a text file. | |
| 888 | 888 | */ |
| 889 | -static void cr_warning(const Blob *p, const char *zFilename){ | |
| 889 | +static void encoding_warning(const Blob *p, int crnlOk, const char *zFilename){ | |
| 890 | + int looksLike; /* return value of looks_like_text() */ | |
| 890 | 891 | char *zMsg; /* Warning message */ |
| 891 | 892 | Blob fname; /* Relative pathname of the file */ |
| 892 | 893 | static int allOk = 0; /* Set to true to disable this routine */ |
| 893 | 894 | |
| 894 | 895 | if( allOk ) return; |
| 895 | - if( looks_like_text(p)<0 ){ | |
| 896 | + looksLike = looks_like_text(p); | |
| 897 | + if( looksLike<0 ){ | |
| 898 | + const char *type; | |
| 896 | 899 | Blob ans; |
| 897 | 900 | char cReply; |
| 898 | 901 | |
| 902 | + if( looksLike&1 ){ | |
| 903 | + if( crnlOk ){ | |
| 904 | + return; /* We don't want CrLf warnings for this file. */ | |
| 905 | + } | |
| 906 | + type = "CR/NL line endings"; | |
| 907 | + }else{ | |
| 908 | + type = "unicode"; | |
| 909 | + } | |
| 899 | 910 | file_relative_name(zFilename, &fname, 0); |
| 900 | 911 | blob_zero(&ans); |
| 901 | 912 | zMsg = mprintf( |
| 902 | - "%s contains CR/NL line endings; commit anyhow (a=all/y/N)?", | |
| 903 | - blob_str(&fname)); | |
| 913 | + "%s contains %s; commit anyhow (a=all/y/N)?", | |
| 914 | + blob_str(&fname), type); | |
| 904 | 915 | prompt_user(zMsg, &ans); |
| 905 | 916 | fossil_free(zMsg); |
| 906 | 917 | cReply = blob_str(&ans)[0]; |
| 907 | 918 | if( cReply=='a' || cReply=='A' ){ |
| 908 | 919 | allOk = 1; |
| 909 | 920 | }else if( cReply!='y' && cReply!='Y' ){ |
| 910 | - fossil_fatal("Abandoning commit due to CR/NL line endings in %s", | |
| 911 | - blob_str(&fname)); | |
| 921 | + fossil_fatal("Abandoning commit due to %s in %s", | |
| 922 | + type, blob_str(&fname)); | |
| 912 | 923 | } |
| 913 | 924 | blob_reset(&ans); |
| 914 | 925 | blob_reset(&fname); |
| 915 | 926 | } |
| 916 | 927 | } |
| @@ -1221,11 +1232,11 @@ | ||
| 1221 | 1232 | /* Instead of file content, put link destination path */ |
| 1222 | 1233 | blob_read_link(&content, zFullname); |
| 1223 | 1234 | }else{ |
| 1224 | 1235 | blob_read_from_file(&content, zFullname); |
| 1225 | 1236 | } |
| 1226 | - if( !crnlOk ) cr_warning(&content, zFullname); | |
| 1237 | + encoding_warning(&content, crnlOk, zFullname); | |
| 1227 | 1238 | if( chnged==1 && contains_merge_marker(&content) ){ |
| 1228 | 1239 | Blob fname; /* Relative pathname of the file */ |
| 1229 | 1240 | |
| 1230 | 1241 | nConflict++; |
| 1231 | 1242 | file_relative_name(zFullname, &fname, 0); |
| 1232 | 1243 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -882,35 +882,46 @@ | |
| 882 | if( pnFBcard ) *pnFBcard = nFBcard; |
| 883 | } |
| 884 | |
| 885 | /* |
| 886 | ** Issue a warning and give the user an opportunity to abandon out |
| 887 | ** if a \r\n line ending is seen in a text file. |
| 888 | */ |
| 889 | static void cr_warning(const Blob *p, const char *zFilename){ |
| 890 | char *zMsg; /* Warning message */ |
| 891 | Blob fname; /* Relative pathname of the file */ |
| 892 | static int allOk = 0; /* Set to true to disable this routine */ |
| 893 | |
| 894 | if( allOk ) return; |
| 895 | if( looks_like_text(p)<0 ){ |
| 896 | Blob ans; |
| 897 | char cReply; |
| 898 | |
| 899 | file_relative_name(zFilename, &fname, 0); |
| 900 | blob_zero(&ans); |
| 901 | zMsg = mprintf( |
| 902 | "%s contains CR/NL line endings; commit anyhow (a=all/y/N)?", |
| 903 | blob_str(&fname)); |
| 904 | prompt_user(zMsg, &ans); |
| 905 | fossil_free(zMsg); |
| 906 | cReply = blob_str(&ans)[0]; |
| 907 | if( cReply=='a' || cReply=='A' ){ |
| 908 | allOk = 1; |
| 909 | }else if( cReply!='y' && cReply!='Y' ){ |
| 910 | fossil_fatal("Abandoning commit due to CR/NL line endings in %s", |
| 911 | blob_str(&fname)); |
| 912 | } |
| 913 | blob_reset(&ans); |
| 914 | blob_reset(&fname); |
| 915 | } |
| 916 | } |
| @@ -1221,11 +1232,11 @@ | |
| 1221 | /* Instead of file content, put link destination path */ |
| 1222 | blob_read_link(&content, zFullname); |
| 1223 | }else{ |
| 1224 | blob_read_from_file(&content, zFullname); |
| 1225 | } |
| 1226 | if( !crnlOk ) cr_warning(&content, zFullname); |
| 1227 | if( chnged==1 && contains_merge_marker(&content) ){ |
| 1228 | Blob fname; /* Relative pathname of the file */ |
| 1229 | |
| 1230 | nConflict++; |
| 1231 | file_relative_name(zFullname, &fname, 0); |
| 1232 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -882,35 +882,46 @@ | |
| 882 | if( pnFBcard ) *pnFBcard = nFBcard; |
| 883 | } |
| 884 | |
| 885 | /* |
| 886 | ** Issue a warning and give the user an opportunity to abandon out |
| 887 | ** if unicode or a \r\n line ending is seen in a text file. |
| 888 | */ |
| 889 | static void encoding_warning(const Blob *p, int crnlOk, const char *zFilename){ |
| 890 | int looksLike; /* return value of looks_like_text() */ |
| 891 | char *zMsg; /* Warning message */ |
| 892 | Blob fname; /* Relative pathname of the file */ |
| 893 | static int allOk = 0; /* Set to true to disable this routine */ |
| 894 | |
| 895 | if( allOk ) return; |
| 896 | looksLike = looks_like_text(p); |
| 897 | if( looksLike<0 ){ |
| 898 | const char *type; |
| 899 | Blob ans; |
| 900 | char cReply; |
| 901 | |
| 902 | if( looksLike&1 ){ |
| 903 | if( crnlOk ){ |
| 904 | return; /* We don't want CrLf warnings for this file. */ |
| 905 | } |
| 906 | type = "CR/NL line endings"; |
| 907 | }else{ |
| 908 | type = "unicode"; |
| 909 | } |
| 910 | file_relative_name(zFilename, &fname, 0); |
| 911 | blob_zero(&ans); |
| 912 | zMsg = mprintf( |
| 913 | "%s contains %s; commit anyhow (a=all/y/N)?", |
| 914 | blob_str(&fname), type); |
| 915 | prompt_user(zMsg, &ans); |
| 916 | fossil_free(zMsg); |
| 917 | cReply = blob_str(&ans)[0]; |
| 918 | if( cReply=='a' || cReply=='A' ){ |
| 919 | allOk = 1; |
| 920 | }else if( cReply!='y' && cReply!='Y' ){ |
| 921 | fossil_fatal("Abandoning commit due to %s in %s", |
| 922 | type, blob_str(&fname)); |
| 923 | } |
| 924 | blob_reset(&ans); |
| 925 | blob_reset(&fname); |
| 926 | } |
| 927 | } |
| @@ -1221,11 +1232,11 @@ | |
| 1232 | /* Instead of file content, put link destination path */ |
| 1233 | blob_read_link(&content, zFullname); |
| 1234 | }else{ |
| 1235 | blob_read_from_file(&content, zFullname); |
| 1236 | } |
| 1237 | encoding_warning(&content, crnlOk, zFullname); |
| 1238 | if( chnged==1 && contains_merge_marker(&content) ){ |
| 1239 | Blob fname; /* Relative pathname of the file */ |
| 1240 | |
| 1241 | nConflict++; |
| 1242 | file_relative_name(zFullname, &fname, 0); |
| 1243 |
+19
-8
| --- src/checkin.c | ||
| +++ src/checkin.c | ||
| @@ -882,35 +882,46 @@ | ||
| 882 | 882 | if( pnFBcard ) *pnFBcard = nFBcard; |
| 883 | 883 | } |
| 884 | 884 | |
| 885 | 885 | /* |
| 886 | 886 | ** Issue a warning and give the user an opportunity to abandon out |
| 887 | -** if a \r\n line ending is seen in a text file. | |
| 887 | +** if unicode or a \r\n line ending is seen in a text file. | |
| 888 | 888 | */ |
| 889 | -static void cr_warning(const Blob *p, const char *zFilename){ | |
| 889 | +static void encoding_warning(const Blob *p, int crnlOk, const char *zFilename){ | |
| 890 | + int looksLike; /* return value of looks_like_text() */ | |
| 890 | 891 | char *zMsg; /* Warning message */ |
| 891 | 892 | Blob fname; /* Relative pathname of the file */ |
| 892 | 893 | static int allOk = 0; /* Set to true to disable this routine */ |
| 893 | 894 | |
| 894 | 895 | if( allOk ) return; |
| 895 | - if( looks_like_text(p)<0 ){ | |
| 896 | + looksLike = looks_like_text(p); | |
| 897 | + if( looksLike<0 ){ | |
| 898 | + const char *type; | |
| 896 | 899 | Blob ans; |
| 897 | 900 | char cReply; |
| 898 | 901 | |
| 902 | + if( looksLike&1 ){ | |
| 903 | + if( crnlOk ){ | |
| 904 | + return; /* We don't want CrLf warnings for this file. */ | |
| 905 | + } | |
| 906 | + type = "CR/NL line endings"; | |
| 907 | + }else{ | |
| 908 | + type = "unicode"; | |
| 909 | + } | |
| 899 | 910 | file_relative_name(zFilename, &fname, 0); |
| 900 | 911 | blob_zero(&ans); |
| 901 | 912 | zMsg = mprintf( |
| 902 | - "%s contains CR/NL line endings; commit anyhow (a=all/y/N)?", | |
| 903 | - blob_str(&fname)); | |
| 913 | + "%s contains %s; commit anyhow (a=all/y/N)?", | |
| 914 | + blob_str(&fname), type); | |
| 904 | 915 | prompt_user(zMsg, &ans); |
| 905 | 916 | fossil_free(zMsg); |
| 906 | 917 | cReply = blob_str(&ans)[0]; |
| 907 | 918 | if( cReply=='a' || cReply=='A' ){ |
| 908 | 919 | allOk = 1; |
| 909 | 920 | }else if( cReply!='y' && cReply!='Y' ){ |
| 910 | - fossil_fatal("Abandoning commit due to CR/NL line endings in %s", | |
| 911 | - blob_str(&fname)); | |
| 921 | + fossil_fatal("Abandoning commit due to %s in %s", | |
| 922 | + type, blob_str(&fname)); | |
| 912 | 923 | } |
| 913 | 924 | blob_reset(&ans); |
| 914 | 925 | blob_reset(&fname); |
| 915 | 926 | } |
| 916 | 927 | } |
| @@ -1221,11 +1232,11 @@ | ||
| 1221 | 1232 | /* Instead of file content, put link destination path */ |
| 1222 | 1233 | blob_read_link(&content, zFullname); |
| 1223 | 1234 | }else{ |
| 1224 | 1235 | blob_read_from_file(&content, zFullname); |
| 1225 | 1236 | } |
| 1226 | - if( !crnlOk ) cr_warning(&content, zFullname); | |
| 1237 | + encoding_warning(&content, crnlOk, zFullname); | |
| 1227 | 1238 | if( chnged==1 && contains_merge_marker(&content) ){ |
| 1228 | 1239 | Blob fname; /* Relative pathname of the file */ |
| 1229 | 1240 | |
| 1230 | 1241 | nConflict++; |
| 1231 | 1242 | file_relative_name(zFullname, &fname, 0); |
| 1232 | 1243 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -882,35 +882,46 @@ | |
| 882 | if( pnFBcard ) *pnFBcard = nFBcard; |
| 883 | } |
| 884 | |
| 885 | /* |
| 886 | ** Issue a warning and give the user an opportunity to abandon out |
| 887 | ** if a \r\n line ending is seen in a text file. |
| 888 | */ |
| 889 | static void cr_warning(const Blob *p, const char *zFilename){ |
| 890 | char *zMsg; /* Warning message */ |
| 891 | Blob fname; /* Relative pathname of the file */ |
| 892 | static int allOk = 0; /* Set to true to disable this routine */ |
| 893 | |
| 894 | if( allOk ) return; |
| 895 | if( looks_like_text(p)<0 ){ |
| 896 | Blob ans; |
| 897 | char cReply; |
| 898 | |
| 899 | file_relative_name(zFilename, &fname, 0); |
| 900 | blob_zero(&ans); |
| 901 | zMsg = mprintf( |
| 902 | "%s contains CR/NL line endings; commit anyhow (a=all/y/N)?", |
| 903 | blob_str(&fname)); |
| 904 | prompt_user(zMsg, &ans); |
| 905 | fossil_free(zMsg); |
| 906 | cReply = blob_str(&ans)[0]; |
| 907 | if( cReply=='a' || cReply=='A' ){ |
| 908 | allOk = 1; |
| 909 | }else if( cReply!='y' && cReply!='Y' ){ |
| 910 | fossil_fatal("Abandoning commit due to CR/NL line endings in %s", |
| 911 | blob_str(&fname)); |
| 912 | } |
| 913 | blob_reset(&ans); |
| 914 | blob_reset(&fname); |
| 915 | } |
| 916 | } |
| @@ -1221,11 +1232,11 @@ | |
| 1221 | /* Instead of file content, put link destination path */ |
| 1222 | blob_read_link(&content, zFullname); |
| 1223 | }else{ |
| 1224 | blob_read_from_file(&content, zFullname); |
| 1225 | } |
| 1226 | if( !crnlOk ) cr_warning(&content, zFullname); |
| 1227 | if( chnged==1 && contains_merge_marker(&content) ){ |
| 1228 | Blob fname; /* Relative pathname of the file */ |
| 1229 | |
| 1230 | nConflict++; |
| 1231 | file_relative_name(zFullname, &fname, 0); |
| 1232 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -882,35 +882,46 @@ | |
| 882 | if( pnFBcard ) *pnFBcard = nFBcard; |
| 883 | } |
| 884 | |
| 885 | /* |
| 886 | ** Issue a warning and give the user an opportunity to abandon out |
| 887 | ** if unicode or a \r\n line ending is seen in a text file. |
| 888 | */ |
| 889 | static void encoding_warning(const Blob *p, int crnlOk, const char *zFilename){ |
| 890 | int looksLike; /* return value of looks_like_text() */ |
| 891 | char *zMsg; /* Warning message */ |
| 892 | Blob fname; /* Relative pathname of the file */ |
| 893 | static int allOk = 0; /* Set to true to disable this routine */ |
| 894 | |
| 895 | if( allOk ) return; |
| 896 | looksLike = looks_like_text(p); |
| 897 | if( looksLike<0 ){ |
| 898 | const char *type; |
| 899 | Blob ans; |
| 900 | char cReply; |
| 901 | |
| 902 | if( looksLike&1 ){ |
| 903 | if( crnlOk ){ |
| 904 | return; /* We don't want CrLf warnings for this file. */ |
| 905 | } |
| 906 | type = "CR/NL line endings"; |
| 907 | }else{ |
| 908 | type = "unicode"; |
| 909 | } |
| 910 | file_relative_name(zFilename, &fname, 0); |
| 911 | blob_zero(&ans); |
| 912 | zMsg = mprintf( |
| 913 | "%s contains %s; commit anyhow (a=all/y/N)?", |
| 914 | blob_str(&fname), type); |
| 915 | prompt_user(zMsg, &ans); |
| 916 | fossil_free(zMsg); |
| 917 | cReply = blob_str(&ans)[0]; |
| 918 | if( cReply=='a' || cReply=='A' ){ |
| 919 | allOk = 1; |
| 920 | }else if( cReply!='y' && cReply!='Y' ){ |
| 921 | fossil_fatal("Abandoning commit due to %s in %s", |
| 922 | type, blob_str(&fname)); |
| 923 | } |
| 924 | blob_reset(&ans); |
| 925 | blob_reset(&fname); |
| 926 | } |
| 927 | } |
| @@ -1221,11 +1232,11 @@ | |
| 1232 | /* Instead of file content, put link destination path */ |
| 1233 | blob_read_link(&content, zFullname); |
| 1234 | }else{ |
| 1235 | blob_read_from_file(&content, zFullname); |
| 1236 | } |
| 1237 | encoding_warning(&content, crnlOk, zFullname); |
| 1238 | if( chnged==1 && contains_merge_marker(&content) ){ |
| 1239 | Blob fname; /* Relative pathname of the file */ |
| 1240 | |
| 1241 | nConflict++; |
| 1242 | file_relative_name(zFullname, &fname, 0); |
| 1243 |
+10
-2
| --- src/diff.c | ||
| +++ src/diff.c | ||
| @@ -48,11 +48,11 @@ | ||
| 48 | 48 | "cannot compute difference between binary files\n" |
| 49 | 49 | |
| 50 | 50 | #define DIFF_CANNOT_COMPUTE_SYMLINK \ |
| 51 | 51 | "cannot compute difference between symlink and regular file\n" |
| 52 | 52 | |
| 53 | -#define looks_like_binary(blob) (looks_like_text((blob)) == 0) | |
| 53 | +#define looks_like_binary(blob) ((looks_like_text(blob)&1) == 0) | |
| 54 | 54 | #endif /* INTERFACE */ |
| 55 | 55 | |
| 56 | 56 | /* |
| 57 | 57 | ** Maximum length of a line in a text file. (8192) |
| 58 | 58 | */ |
| @@ -170,14 +170,15 @@ | ||
| 170 | 170 | *pnLine = nLine; |
| 171 | 171 | return a; |
| 172 | 172 | } |
| 173 | 173 | |
| 174 | 174 | /* |
| 175 | -** Returns 1, if the file appears text, and does not contain CrLf | |
| 175 | +** Returns 1, if everything OK | |
| 176 | 176 | ** Returns 0 if the specified content appears to be binary or |
| 177 | 177 | ** contains a line that is too long |
| 178 | 178 | ** Returns -1, if the file appears text, but it contains CrLf |
| 179 | +** Returns -2, if the file starts with an UTF-16 BOM (le or be) | |
| 179 | 180 | */ |
| 180 | 181 | int looks_like_text(const Blob *pContent){ |
| 181 | 182 | const char *z = blob_buffer(pContent); |
| 182 | 183 | unsigned int n = blob_size(pContent); |
| 183 | 184 | int j, c; |
| @@ -186,10 +187,17 @@ | ||
| 186 | 187 | /* Check individual lines. |
| 187 | 188 | */ |
| 188 | 189 | if( n==0 ) return result; /* Empty file -> text */ |
| 189 | 190 | c = *z; |
| 190 | 191 | if( c==0 ) return 0; /* \000 byte in a file -> binary */ |
| 192 | + if ( n > 1 ){ | |
| 193 | + if ( (c==(char)0xff) && (z[1]==(char)0xfe) ){ | |
| 194 | + return -2; | |
| 195 | + } else if ( (c==(char)0xfe) && (z[1]==(char)0xff) ){ | |
| 196 | + return -2; | |
| 197 | + } | |
| 198 | + } | |
| 191 | 199 | j = (c!='\n'); |
| 192 | 200 | while( --n>0 ){ |
| 193 | 201 | c = *++z; ++j; |
| 194 | 202 | if( c==0 ) return 0; /* \000 byte in a file -> binary */ |
| 195 | 203 | if( c=='\n' ){ |
| 196 | 204 |
| --- src/diff.c | |
| +++ src/diff.c | |
| @@ -48,11 +48,11 @@ | |
| 48 | "cannot compute difference between binary files\n" |
| 49 | |
| 50 | #define DIFF_CANNOT_COMPUTE_SYMLINK \ |
| 51 | "cannot compute difference between symlink and regular file\n" |
| 52 | |
| 53 | #define looks_like_binary(blob) (looks_like_text((blob)) == 0) |
| 54 | #endif /* INTERFACE */ |
| 55 | |
| 56 | /* |
| 57 | ** Maximum length of a line in a text file. (8192) |
| 58 | */ |
| @@ -170,14 +170,15 @@ | |
| 170 | *pnLine = nLine; |
| 171 | return a; |
| 172 | } |
| 173 | |
| 174 | /* |
| 175 | ** Returns 1, if the file appears text, and does not contain CrLf |
| 176 | ** Returns 0 if the specified content appears to be binary or |
| 177 | ** contains a line that is too long |
| 178 | ** Returns -1, if the file appears text, but it contains CrLf |
| 179 | */ |
| 180 | int looks_like_text(const Blob *pContent){ |
| 181 | const char *z = blob_buffer(pContent); |
| 182 | unsigned int n = blob_size(pContent); |
| 183 | int j, c; |
| @@ -186,10 +187,17 @@ | |
| 186 | /* Check individual lines. |
| 187 | */ |
| 188 | if( n==0 ) return result; /* Empty file -> text */ |
| 189 | c = *z; |
| 190 | if( c==0 ) return 0; /* \000 byte in a file -> binary */ |
| 191 | j = (c!='\n'); |
| 192 | while( --n>0 ){ |
| 193 | c = *++z; ++j; |
| 194 | if( c==0 ) return 0; /* \000 byte in a file -> binary */ |
| 195 | if( c=='\n' ){ |
| 196 |
| --- src/diff.c | |
| +++ src/diff.c | |
| @@ -48,11 +48,11 @@ | |
| 48 | "cannot compute difference between binary files\n" |
| 49 | |
| 50 | #define DIFF_CANNOT_COMPUTE_SYMLINK \ |
| 51 | "cannot compute difference between symlink and regular file\n" |
| 52 | |
| 53 | #define looks_like_binary(blob) ((looks_like_text(blob)&1) == 0) |
| 54 | #endif /* INTERFACE */ |
| 55 | |
| 56 | /* |
| 57 | ** Maximum length of a line in a text file. (8192) |
| 58 | */ |
| @@ -170,14 +170,15 @@ | |
| 170 | *pnLine = nLine; |
| 171 | return a; |
| 172 | } |
| 173 | |
| 174 | /* |
| 175 | ** Returns 1, if everything OK |
| 176 | ** Returns 0 if the specified content appears to be binary or |
| 177 | ** contains a line that is too long |
| 178 | ** Returns -1, if the file appears text, but it contains CrLf |
| 179 | ** Returns -2, if the file starts with an UTF-16 BOM (le or be) |
| 180 | */ |
| 181 | int looks_like_text(const Blob *pContent){ |
| 182 | const char *z = blob_buffer(pContent); |
| 183 | unsigned int n = blob_size(pContent); |
| 184 | int j, c; |
| @@ -186,10 +187,17 @@ | |
| 187 | /* Check individual lines. |
| 188 | */ |
| 189 | if( n==0 ) return result; /* Empty file -> text */ |
| 190 | c = *z; |
| 191 | if( c==0 ) return 0; /* \000 byte in a file -> binary */ |
| 192 | if ( n > 1 ){ |
| 193 | if ( (c==(char)0xff) && (z[1]==(char)0xfe) ){ |
| 194 | return -2; |
| 195 | } else if ( (c==(char)0xfe) && (z[1]==(char)0xff) ){ |
| 196 | return -2; |
| 197 | } |
| 198 | } |
| 199 | j = (c!='\n'); |
| 200 | while( --n>0 ){ |
| 201 | c = *++z; ++j; |
| 202 | if( c==0 ) return 0; /* \000 byte in a file -> binary */ |
| 203 | if( c=='\n' ){ |
| 204 |
+3
-13
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -476,13 +476,12 @@ | ||
| 476 | 476 | int n; /* Number of bytes in one line */ |
| 477 | 477 | char *z; /* General use string pointer */ |
| 478 | 478 | char **newArgv; /* New expanded g.argv under construction */ |
| 479 | 479 | char const * zFileName; /* input file name */ |
| 480 | 480 | FILE * zInFile; /* input FILE */ |
| 481 | - int foundBom = -1; /* -1= not searched yet, 0 = no; 1=yes */ | |
| 482 | 481 | #ifdef _WIN32 |
| 483 | - wchar_t buf[MAX_PATH]; | |
| 482 | + WCHAR buf[MAX_PATH]; | |
| 484 | 483 | #endif |
| 485 | 484 | |
| 486 | 485 | g.argc = argc; |
| 487 | 486 | g.argv = argv; |
| 488 | 487 | #ifdef _WIN32 |
| @@ -514,10 +513,11 @@ | ||
| 514 | 513 | if(stdin != zInFile){ |
| 515 | 514 | fclose(zInFile); |
| 516 | 515 | } |
| 517 | 516 | zInFile = NULL; |
| 518 | 517 | } |
| 518 | + blob_strip_bom(&file, 1); | |
| 519 | 519 | z = blob_str(&file); |
| 520 | 520 | for(k=0, nLine=1; z[k]; k++) if( z[k]=='\n' ) nLine++; |
| 521 | 521 | newArgv = fossil_malloc( sizeof(char*)*(g.argc + nLine*2) ); |
| 522 | 522 | for(j=0; j<i; j++) newArgv[j] = g.argv[j]; |
| 523 | 523 | |
| @@ -524,24 +524,14 @@ | ||
| 524 | 524 | blob_rewind(&file); |
| 525 | 525 | while( (n = blob_line(&file, &line))>0 ){ |
| 526 | 526 | if( n<=1 ) continue; |
| 527 | 527 | z = blob_buffer(&line); |
| 528 | 528 | z[n-1] = 0; |
| 529 | - if (foundBom == -1) { | |
| 530 | - static const unsigned char bom[] = { 0xEF, 0xBB, 0xBF }; | |
| 531 | - foundBom = memcmp(z, bom, 3)==0; | |
| 532 | - if( foundBom ) { | |
| 533 | - z += 3; n -= 3; | |
| 534 | - } | |
| 535 | - } | |
| 536 | 529 | if((n>1) && ('\r'==z[n-2])){ |
| 537 | 530 | if(n==2) continue /*empty line*/; |
| 538 | 531 | z[n-2] = 0; |
| 539 | 532 | } |
| 540 | - if (!foundBom) { | |
| 541 | - z = fossil_mbcs_to_utf8(z); | |
| 542 | - } | |
| 543 | 533 | newArgv[j++] = z; |
| 544 | 534 | if( z[0]=='-' ){ |
| 545 | 535 | for(k=1; z[k] && !fossil_isspace(z[k]); k++){} |
| 546 | 536 | if( z[k] ){ |
| 547 | 537 | z[k] = 0; |
| @@ -850,11 +840,11 @@ | ||
| 850 | 840 | #if defined(_WIN32) |
| 851 | 841 | /* On windows, we have to put double-quotes around the entire command. |
| 852 | 842 | ** Who knows why - this is just the way windows works. |
| 853 | 843 | */ |
| 854 | 844 | char *zNewCmd = mprintf("\"%s\"", zOrigCmd); |
| 855 | - wchar_t *zUnicode = fossil_utf8_to_unicode(zNewCmd); | |
| 845 | + WCHAR *zUnicode = fossil_utf8_to_unicode(zNewCmd); | |
| 856 | 846 | if( g.fSystemTrace ) { |
| 857 | 847 | char *zOut = mprintf("SYSTEM: %s\n", zNewCmd); |
| 858 | 848 | fossil_puts(zOut, 1); |
| 859 | 849 | fossil_free(zOut); |
| 860 | 850 | } |
| 861 | 851 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -476,13 +476,12 @@ | |
| 476 | int n; /* Number of bytes in one line */ |
| 477 | char *z; /* General use string pointer */ |
| 478 | char **newArgv; /* New expanded g.argv under construction */ |
| 479 | char const * zFileName; /* input file name */ |
| 480 | FILE * zInFile; /* input FILE */ |
| 481 | int foundBom = -1; /* -1= not searched yet, 0 = no; 1=yes */ |
| 482 | #ifdef _WIN32 |
| 483 | wchar_t buf[MAX_PATH]; |
| 484 | #endif |
| 485 | |
| 486 | g.argc = argc; |
| 487 | g.argv = argv; |
| 488 | #ifdef _WIN32 |
| @@ -514,10 +513,11 @@ | |
| 514 | if(stdin != zInFile){ |
| 515 | fclose(zInFile); |
| 516 | } |
| 517 | zInFile = NULL; |
| 518 | } |
| 519 | z = blob_str(&file); |
| 520 | for(k=0, nLine=1; z[k]; k++) if( z[k]=='\n' ) nLine++; |
| 521 | newArgv = fossil_malloc( sizeof(char*)*(g.argc + nLine*2) ); |
| 522 | for(j=0; j<i; j++) newArgv[j] = g.argv[j]; |
| 523 | |
| @@ -524,24 +524,14 @@ | |
| 524 | blob_rewind(&file); |
| 525 | while( (n = blob_line(&file, &line))>0 ){ |
| 526 | if( n<=1 ) continue; |
| 527 | z = blob_buffer(&line); |
| 528 | z[n-1] = 0; |
| 529 | if (foundBom == -1) { |
| 530 | static const unsigned char bom[] = { 0xEF, 0xBB, 0xBF }; |
| 531 | foundBom = memcmp(z, bom, 3)==0; |
| 532 | if( foundBom ) { |
| 533 | z += 3; n -= 3; |
| 534 | } |
| 535 | } |
| 536 | if((n>1) && ('\r'==z[n-2])){ |
| 537 | if(n==2) continue /*empty line*/; |
| 538 | z[n-2] = 0; |
| 539 | } |
| 540 | if (!foundBom) { |
| 541 | z = fossil_mbcs_to_utf8(z); |
| 542 | } |
| 543 | newArgv[j++] = z; |
| 544 | if( z[0]=='-' ){ |
| 545 | for(k=1; z[k] && !fossil_isspace(z[k]); k++){} |
| 546 | if( z[k] ){ |
| 547 | z[k] = 0; |
| @@ -850,11 +840,11 @@ | |
| 850 | #if defined(_WIN32) |
| 851 | /* On windows, we have to put double-quotes around the entire command. |
| 852 | ** Who knows why - this is just the way windows works. |
| 853 | */ |
| 854 | char *zNewCmd = mprintf("\"%s\"", zOrigCmd); |
| 855 | wchar_t *zUnicode = fossil_utf8_to_unicode(zNewCmd); |
| 856 | if( g.fSystemTrace ) { |
| 857 | char *zOut = mprintf("SYSTEM: %s\n", zNewCmd); |
| 858 | fossil_puts(zOut, 1); |
| 859 | fossil_free(zOut); |
| 860 | } |
| 861 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -476,13 +476,12 @@ | |
| 476 | int n; /* Number of bytes in one line */ |
| 477 | char *z; /* General use string pointer */ |
| 478 | char **newArgv; /* New expanded g.argv under construction */ |
| 479 | char const * zFileName; /* input file name */ |
| 480 | FILE * zInFile; /* input FILE */ |
| 481 | #ifdef _WIN32 |
| 482 | WCHAR buf[MAX_PATH]; |
| 483 | #endif |
| 484 | |
| 485 | g.argc = argc; |
| 486 | g.argv = argv; |
| 487 | #ifdef _WIN32 |
| @@ -514,10 +513,11 @@ | |
| 513 | if(stdin != zInFile){ |
| 514 | fclose(zInFile); |
| 515 | } |
| 516 | zInFile = NULL; |
| 517 | } |
| 518 | blob_strip_bom(&file, 1); |
| 519 | z = blob_str(&file); |
| 520 | for(k=0, nLine=1; z[k]; k++) if( z[k]=='\n' ) nLine++; |
| 521 | newArgv = fossil_malloc( sizeof(char*)*(g.argc + nLine*2) ); |
| 522 | for(j=0; j<i; j++) newArgv[j] = g.argv[j]; |
| 523 | |
| @@ -524,24 +524,14 @@ | |
| 524 | blob_rewind(&file); |
| 525 | while( (n = blob_line(&file, &line))>0 ){ |
| 526 | if( n<=1 ) continue; |
| 527 | z = blob_buffer(&line); |
| 528 | z[n-1] = 0; |
| 529 | if((n>1) && ('\r'==z[n-2])){ |
| 530 | if(n==2) continue /*empty line*/; |
| 531 | z[n-2] = 0; |
| 532 | } |
| 533 | newArgv[j++] = z; |
| 534 | if( z[0]=='-' ){ |
| 535 | for(k=1; z[k] && !fossil_isspace(z[k]); k++){} |
| 536 | if( z[k] ){ |
| 537 | z[k] = 0; |
| @@ -850,11 +840,11 @@ | |
| 840 | #if defined(_WIN32) |
| 841 | /* On windows, we have to put double-quotes around the entire command. |
| 842 | ** Who knows why - this is just the way windows works. |
| 843 | */ |
| 844 | char *zNewCmd = mprintf("\"%s\"", zOrigCmd); |
| 845 | WCHAR *zUnicode = fossil_utf8_to_unicode(zNewCmd); |
| 846 | if( g.fSystemTrace ) { |
| 847 | char *zOut = mprintf("SYSTEM: %s\n", zNewCmd); |
| 848 | fossil_puts(zOut, 1); |
| 849 | fossil_free(zOut); |
| 850 | } |
| 851 |
+8
-18
| --- src/wikiformat.c | ||
| +++ src/wikiformat.c | ||
| @@ -315,11 +315,11 @@ | ||
| 315 | 315 | { "tbody", MARKUP_TBODY, MUTYPE_BLOCK, |
| 316 | 316 | AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, |
| 317 | 317 | { "td", MARKUP_TD, MUTYPE_TD, |
| 318 | 318 | AMSK_ALIGN|AMSK_BGCOLOR|AMSK_COLSPAN| |
| 319 | 319 | AMSK_ROWSPAN|AMSK_VALIGN|AMSK_CLASS|AMSK_STYLE }, |
| 320 | - { "tfoot", MARKUP_TFOOT, MUTYPE_BLOCK, | |
| 320 | + { "tfoot", MARKUP_TFOOT, MUTYPE_BLOCK, | |
| 321 | 321 | AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, |
| 322 | 322 | { "th", MARKUP_TH, MUTYPE_TD, |
| 323 | 323 | AMSK_ALIGN|AMSK_BGCOLOR|AMSK_COLSPAN| |
| 324 | 324 | AMSK_ROWSPAN|AMSK_VALIGN|AMSK_CLASS|AMSK_STYLE }, |
| 325 | 325 | { "thead", MARKUP_THEAD, MUTYPE_BLOCK, |
| @@ -856,11 +856,11 @@ | ||
| 856 | 856 | ** <a class="button" href="../index.wiki">Index</a> |
| 857 | 857 | ** |
| 858 | 858 | ** If the markup matches this pattern, and if the WIKI_BUTTONS flag was |
| 859 | 859 | ** passed to wiki_convert(), then transform this link into a submenu |
| 860 | 860 | ** button, skip the text, and set *pN equal to the total length of the |
| 861 | -** text through the end of </a> and return true. If the markup does | |
| 861 | +** text through the end of </a> and return true. If the markup does | |
| 862 | 862 | ** not match or if WIKI_BUTTONS is not set, then make no changes to *pN |
| 863 | 863 | ** and return false. |
| 864 | 864 | */ |
| 865 | 865 | static int isButtonHyperlink( |
| 866 | 866 | Renderer *p, /* Renderer state */ |
| @@ -1019,11 +1019,11 @@ | ||
| 1019 | 1019 | static int in_this_repo(const char *zUuid){ |
| 1020 | 1020 | static Stmt q; |
| 1021 | 1021 | int rc; |
| 1022 | 1022 | int n; |
| 1023 | 1023 | char zU2[UUID_SIZE+1]; |
| 1024 | - db_static_prepare(&q, | |
| 1024 | + db_static_prepare(&q, | |
| 1025 | 1025 | "SELECT 1 FROM blob WHERE uuid>=:u AND uuid<:u2" |
| 1026 | 1026 | ); |
| 1027 | 1027 | db_bind_text(&q, ":u", zUuid); |
| 1028 | 1028 | n = (int)strlen(zUuid); |
| 1029 | 1029 | if( n>=sizeof(zU2) ) n = sizeof(zU2)-1; |
| @@ -1165,11 +1165,11 @@ | ||
| 1165 | 1165 | zTerm = "]</a>"; |
| 1166 | 1166 | } |
| 1167 | 1167 | }else if( strlen(zTarget)>=10 && fossil_isdigit(zTarget[0]) && zTarget[4]=='-' |
| 1168 | 1168 | && db_int(0, "SELECT datetime(%Q) NOT NULL", zTarget) ){ |
| 1169 | 1169 | blob_appendf(p->pOut, "<a href=\"%R/timeline?c=%T\">", zTarget); |
| 1170 | - }else if( strncmp(zTarget, "wiki:", 5)==0 | |
| 1170 | + }else if( strncmp(zTarget, "wiki:", 5)==0 | |
| 1171 | 1171 | && wiki_name_is_wellformed((const unsigned char*)zTarget) ){ |
| 1172 | 1172 | zTarget += 5; |
| 1173 | 1173 | blob_appendf(p->pOut, "<a href=\"%R/wiki?name=%T\">", zTarget); |
| 1174 | 1174 | }else if( wiki_name_is_wellformed((const unsigned char *)zTarget) ){ |
| 1175 | 1175 | blob_appendf(p->pOut, "<a href=\"%R/wiki?name=%T\">", zTarget); |
| @@ -1547,29 +1547,18 @@ | ||
| 1547 | 1547 | } |
| 1548 | 1548 | z += n; |
| 1549 | 1549 | } |
| 1550 | 1550 | } |
| 1551 | 1551 | |
| 1552 | -/* | |
| 1553 | -** Skip over the UTF-8 Byte-Order-Mark that some broken Windows | |
| 1554 | -** tools add to the beginning of text files. | |
| 1555 | -*/ | |
| 1556 | -char *skip_bom(char *z){ | |
| 1557 | - static const unsigned char bom[] = { 0xEF, 0xBB, 0xBF }; | |
| 1558 | - if( z && memcmp(z, bom, 3)==0 ) z += 3; | |
| 1559 | - return z; | |
| 1560 | -} | |
| 1561 | - | |
| 1562 | 1552 | /* |
| 1563 | 1553 | ** Transform the text in the pIn blob. Write the results |
| 1564 | 1554 | ** into the pOut blob. The pOut blob should already be |
| 1565 | 1555 | ** initialized. The output is merely appended to pOut. |
| 1566 | 1556 | ** If pOut is NULL, then the output is appended to the CGI |
| 1567 | 1557 | ** reply. |
| 1568 | 1558 | */ |
| 1569 | 1559 | void wiki_convert(Blob *pIn, Blob *pOut, int flags){ |
| 1570 | - char *z; | |
| 1571 | 1560 | Renderer renderer; |
| 1572 | 1561 | |
| 1573 | 1562 | memset(&renderer, 0, sizeof(renderer)); |
| 1574 | 1563 | renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH|flags; |
| 1575 | 1564 | if( flags & WIKI_NOBLOCK ){ |
| @@ -1587,12 +1576,12 @@ | ||
| 1587 | 1576 | renderer.pOut = pOut; |
| 1588 | 1577 | }else{ |
| 1589 | 1578 | renderer.pOut = cgi_output_blob(); |
| 1590 | 1579 | } |
| 1591 | 1580 | |
| 1592 | - z = skip_bom(blob_str(pIn)); | |
| 1593 | - wiki_render(&renderer, z); | |
| 1581 | + blob_strip_bom(pIn, 0); | |
| 1582 | + wiki_render(&renderer, blob_str(pIn)); | |
| 1594 | 1583 | endAutoParagraph(&renderer); |
| 1595 | 1584 | while( renderer.nStack ){ |
| 1596 | 1585 | popStack(&renderer); |
| 1597 | 1586 | } |
| 1598 | 1587 | blob_append(renderer.pOut, "\n", 1); |
| @@ -1630,11 +1619,12 @@ | ||
| 1630 | 1619 | */ |
| 1631 | 1620 | int wiki_find_title(Blob *pIn, Blob *pTitle, Blob *pTail){ |
| 1632 | 1621 | char *z; |
| 1633 | 1622 | int i; |
| 1634 | 1623 | int iStart; |
| 1635 | - z = skip_bom(blob_str(pIn)); | |
| 1624 | + blob_strip_bom(pIn, 0); | |
| 1625 | + z = blob_str(pIn); | |
| 1636 | 1626 | for(i=0; fossil_isspace(z[i]); i++){} |
| 1637 | 1627 | if( z[i]!='<' ) return 0; |
| 1638 | 1628 | i++; |
| 1639 | 1629 | if( strncmp(&z[i],"title>", 6)!=0 ) return 0; |
| 1640 | 1630 | iStart = i+6; |
| 1641 | 1631 |
| --- src/wikiformat.c | |
| +++ src/wikiformat.c | |
| @@ -315,11 +315,11 @@ | |
| 315 | { "tbody", MARKUP_TBODY, MUTYPE_BLOCK, |
| 316 | AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, |
| 317 | { "td", MARKUP_TD, MUTYPE_TD, |
| 318 | AMSK_ALIGN|AMSK_BGCOLOR|AMSK_COLSPAN| |
| 319 | AMSK_ROWSPAN|AMSK_VALIGN|AMSK_CLASS|AMSK_STYLE }, |
| 320 | { "tfoot", MARKUP_TFOOT, MUTYPE_BLOCK, |
| 321 | AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, |
| 322 | { "th", MARKUP_TH, MUTYPE_TD, |
| 323 | AMSK_ALIGN|AMSK_BGCOLOR|AMSK_COLSPAN| |
| 324 | AMSK_ROWSPAN|AMSK_VALIGN|AMSK_CLASS|AMSK_STYLE }, |
| 325 | { "thead", MARKUP_THEAD, MUTYPE_BLOCK, |
| @@ -856,11 +856,11 @@ | |
| 856 | ** <a class="button" href="../index.wiki">Index</a> |
| 857 | ** |
| 858 | ** If the markup matches this pattern, and if the WIKI_BUTTONS flag was |
| 859 | ** passed to wiki_convert(), then transform this link into a submenu |
| 860 | ** button, skip the text, and set *pN equal to the total length of the |
| 861 | ** text through the end of </a> and return true. If the markup does |
| 862 | ** not match or if WIKI_BUTTONS is not set, then make no changes to *pN |
| 863 | ** and return false. |
| 864 | */ |
| 865 | static int isButtonHyperlink( |
| 866 | Renderer *p, /* Renderer state */ |
| @@ -1019,11 +1019,11 @@ | |
| 1019 | static int in_this_repo(const char *zUuid){ |
| 1020 | static Stmt q; |
| 1021 | int rc; |
| 1022 | int n; |
| 1023 | char zU2[UUID_SIZE+1]; |
| 1024 | db_static_prepare(&q, |
| 1025 | "SELECT 1 FROM blob WHERE uuid>=:u AND uuid<:u2" |
| 1026 | ); |
| 1027 | db_bind_text(&q, ":u", zUuid); |
| 1028 | n = (int)strlen(zUuid); |
| 1029 | if( n>=sizeof(zU2) ) n = sizeof(zU2)-1; |
| @@ -1165,11 +1165,11 @@ | |
| 1165 | zTerm = "]</a>"; |
| 1166 | } |
| 1167 | }else if( strlen(zTarget)>=10 && fossil_isdigit(zTarget[0]) && zTarget[4]=='-' |
| 1168 | && db_int(0, "SELECT datetime(%Q) NOT NULL", zTarget) ){ |
| 1169 | blob_appendf(p->pOut, "<a href=\"%R/timeline?c=%T\">", zTarget); |
| 1170 | }else if( strncmp(zTarget, "wiki:", 5)==0 |
| 1171 | && wiki_name_is_wellformed((const unsigned char*)zTarget) ){ |
| 1172 | zTarget += 5; |
| 1173 | blob_appendf(p->pOut, "<a href=\"%R/wiki?name=%T\">", zTarget); |
| 1174 | }else if( wiki_name_is_wellformed((const unsigned char *)zTarget) ){ |
| 1175 | blob_appendf(p->pOut, "<a href=\"%R/wiki?name=%T\">", zTarget); |
| @@ -1547,29 +1547,18 @@ | |
| 1547 | } |
| 1548 | z += n; |
| 1549 | } |
| 1550 | } |
| 1551 | |
| 1552 | /* |
| 1553 | ** Skip over the UTF-8 Byte-Order-Mark that some broken Windows |
| 1554 | ** tools add to the beginning of text files. |
| 1555 | */ |
| 1556 | char *skip_bom(char *z){ |
| 1557 | static const unsigned char bom[] = { 0xEF, 0xBB, 0xBF }; |
| 1558 | if( z && memcmp(z, bom, 3)==0 ) z += 3; |
| 1559 | return z; |
| 1560 | } |
| 1561 | |
| 1562 | /* |
| 1563 | ** Transform the text in the pIn blob. Write the results |
| 1564 | ** into the pOut blob. The pOut blob should already be |
| 1565 | ** initialized. The output is merely appended to pOut. |
| 1566 | ** If pOut is NULL, then the output is appended to the CGI |
| 1567 | ** reply. |
| 1568 | */ |
| 1569 | void wiki_convert(Blob *pIn, Blob *pOut, int flags){ |
| 1570 | char *z; |
| 1571 | Renderer renderer; |
| 1572 | |
| 1573 | memset(&renderer, 0, sizeof(renderer)); |
| 1574 | renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH|flags; |
| 1575 | if( flags & WIKI_NOBLOCK ){ |
| @@ -1587,12 +1576,12 @@ | |
| 1587 | renderer.pOut = pOut; |
| 1588 | }else{ |
| 1589 | renderer.pOut = cgi_output_blob(); |
| 1590 | } |
| 1591 | |
| 1592 | z = skip_bom(blob_str(pIn)); |
| 1593 | wiki_render(&renderer, z); |
| 1594 | endAutoParagraph(&renderer); |
| 1595 | while( renderer.nStack ){ |
| 1596 | popStack(&renderer); |
| 1597 | } |
| 1598 | blob_append(renderer.pOut, "\n", 1); |
| @@ -1630,11 +1619,12 @@ | |
| 1630 | */ |
| 1631 | int wiki_find_title(Blob *pIn, Blob *pTitle, Blob *pTail){ |
| 1632 | char *z; |
| 1633 | int i; |
| 1634 | int iStart; |
| 1635 | z = skip_bom(blob_str(pIn)); |
| 1636 | for(i=0; fossil_isspace(z[i]); i++){} |
| 1637 | if( z[i]!='<' ) return 0; |
| 1638 | i++; |
| 1639 | if( strncmp(&z[i],"title>", 6)!=0 ) return 0; |
| 1640 | iStart = i+6; |
| 1641 |
| --- src/wikiformat.c | |
| +++ src/wikiformat.c | |
| @@ -315,11 +315,11 @@ | |
| 315 | { "tbody", MARKUP_TBODY, MUTYPE_BLOCK, |
| 316 | AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, |
| 317 | { "td", MARKUP_TD, MUTYPE_TD, |
| 318 | AMSK_ALIGN|AMSK_BGCOLOR|AMSK_COLSPAN| |
| 319 | AMSK_ROWSPAN|AMSK_VALIGN|AMSK_CLASS|AMSK_STYLE }, |
| 320 | { "tfoot", MARKUP_TFOOT, MUTYPE_BLOCK, |
| 321 | AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, |
| 322 | { "th", MARKUP_TH, MUTYPE_TD, |
| 323 | AMSK_ALIGN|AMSK_BGCOLOR|AMSK_COLSPAN| |
| 324 | AMSK_ROWSPAN|AMSK_VALIGN|AMSK_CLASS|AMSK_STYLE }, |
| 325 | { "thead", MARKUP_THEAD, MUTYPE_BLOCK, |
| @@ -856,11 +856,11 @@ | |
| 856 | ** <a class="button" href="../index.wiki">Index</a> |
| 857 | ** |
| 858 | ** If the markup matches this pattern, and if the WIKI_BUTTONS flag was |
| 859 | ** passed to wiki_convert(), then transform this link into a submenu |
| 860 | ** button, skip the text, and set *pN equal to the total length of the |
| 861 | ** text through the end of </a> and return true. If the markup does |
| 862 | ** not match or if WIKI_BUTTONS is not set, then make no changes to *pN |
| 863 | ** and return false. |
| 864 | */ |
| 865 | static int isButtonHyperlink( |
| 866 | Renderer *p, /* Renderer state */ |
| @@ -1019,11 +1019,11 @@ | |
| 1019 | static int in_this_repo(const char *zUuid){ |
| 1020 | static Stmt q; |
| 1021 | int rc; |
| 1022 | int n; |
| 1023 | char zU2[UUID_SIZE+1]; |
| 1024 | db_static_prepare(&q, |
| 1025 | "SELECT 1 FROM blob WHERE uuid>=:u AND uuid<:u2" |
| 1026 | ); |
| 1027 | db_bind_text(&q, ":u", zUuid); |
| 1028 | n = (int)strlen(zUuid); |
| 1029 | if( n>=sizeof(zU2) ) n = sizeof(zU2)-1; |
| @@ -1165,11 +1165,11 @@ | |
| 1165 | zTerm = "]</a>"; |
| 1166 | } |
| 1167 | }else if( strlen(zTarget)>=10 && fossil_isdigit(zTarget[0]) && zTarget[4]=='-' |
| 1168 | && db_int(0, "SELECT datetime(%Q) NOT NULL", zTarget) ){ |
| 1169 | blob_appendf(p->pOut, "<a href=\"%R/timeline?c=%T\">", zTarget); |
| 1170 | }else if( strncmp(zTarget, "wiki:", 5)==0 |
| 1171 | && wiki_name_is_wellformed((const unsigned char*)zTarget) ){ |
| 1172 | zTarget += 5; |
| 1173 | blob_appendf(p->pOut, "<a href=\"%R/wiki?name=%T\">", zTarget); |
| 1174 | }else if( wiki_name_is_wellformed((const unsigned char *)zTarget) ){ |
| 1175 | blob_appendf(p->pOut, "<a href=\"%R/wiki?name=%T\">", zTarget); |
| @@ -1547,29 +1547,18 @@ | |
| 1547 | } |
| 1548 | z += n; |
| 1549 | } |
| 1550 | } |
| 1551 | |
| 1552 | /* |
| 1553 | ** Transform the text in the pIn blob. Write the results |
| 1554 | ** into the pOut blob. The pOut blob should already be |
| 1555 | ** initialized. The output is merely appended to pOut. |
| 1556 | ** If pOut is NULL, then the output is appended to the CGI |
| 1557 | ** reply. |
| 1558 | */ |
| 1559 | void wiki_convert(Blob *pIn, Blob *pOut, int flags){ |
| 1560 | Renderer renderer; |
| 1561 | |
| 1562 | memset(&renderer, 0, sizeof(renderer)); |
| 1563 | renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH|flags; |
| 1564 | if( flags & WIKI_NOBLOCK ){ |
| @@ -1587,12 +1576,12 @@ | |
| 1576 | renderer.pOut = pOut; |
| 1577 | }else{ |
| 1578 | renderer.pOut = cgi_output_blob(); |
| 1579 | } |
| 1580 | |
| 1581 | blob_strip_bom(pIn, 0); |
| 1582 | wiki_render(&renderer, blob_str(pIn)); |
| 1583 | endAutoParagraph(&renderer); |
| 1584 | while( renderer.nStack ){ |
| 1585 | popStack(&renderer); |
| 1586 | } |
| 1587 | blob_append(renderer.pOut, "\n", 1); |
| @@ -1630,11 +1619,12 @@ | |
| 1619 | */ |
| 1620 | int wiki_find_title(Blob *pIn, Blob *pTitle, Blob *pTail){ |
| 1621 | char *z; |
| 1622 | int i; |
| 1623 | int iStart; |
| 1624 | blob_strip_bom(pIn, 0); |
| 1625 | z = blob_str(pIn); |
| 1626 | for(i=0; fossil_isspace(z[i]); i++){} |
| 1627 | if( z[i]!='<' ) return 0; |
| 1628 | i++; |
| 1629 | if( strncmp(&z[i],"title>", 6)!=0 ) return 0; |
| 1630 | iStart = i+6; |
| 1631 |
+8
-18
| --- src/wikiformat.c | ||
| +++ src/wikiformat.c | ||
| @@ -315,11 +315,11 @@ | ||
| 315 | 315 | { "tbody", MARKUP_TBODY, MUTYPE_BLOCK, |
| 316 | 316 | AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, |
| 317 | 317 | { "td", MARKUP_TD, MUTYPE_TD, |
| 318 | 318 | AMSK_ALIGN|AMSK_BGCOLOR|AMSK_COLSPAN| |
| 319 | 319 | AMSK_ROWSPAN|AMSK_VALIGN|AMSK_CLASS|AMSK_STYLE }, |
| 320 | - { "tfoot", MARKUP_TFOOT, MUTYPE_BLOCK, | |
| 320 | + { "tfoot", MARKUP_TFOOT, MUTYPE_BLOCK, | |
| 321 | 321 | AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, |
| 322 | 322 | { "th", MARKUP_TH, MUTYPE_TD, |
| 323 | 323 | AMSK_ALIGN|AMSK_BGCOLOR|AMSK_COLSPAN| |
| 324 | 324 | AMSK_ROWSPAN|AMSK_VALIGN|AMSK_CLASS|AMSK_STYLE }, |
| 325 | 325 | { "thead", MARKUP_THEAD, MUTYPE_BLOCK, |
| @@ -856,11 +856,11 @@ | ||
| 856 | 856 | ** <a class="button" href="../index.wiki">Index</a> |
| 857 | 857 | ** |
| 858 | 858 | ** If the markup matches this pattern, and if the WIKI_BUTTONS flag was |
| 859 | 859 | ** passed to wiki_convert(), then transform this link into a submenu |
| 860 | 860 | ** button, skip the text, and set *pN equal to the total length of the |
| 861 | -** text through the end of </a> and return true. If the markup does | |
| 861 | +** text through the end of </a> and return true. If the markup does | |
| 862 | 862 | ** not match or if WIKI_BUTTONS is not set, then make no changes to *pN |
| 863 | 863 | ** and return false. |
| 864 | 864 | */ |
| 865 | 865 | static int isButtonHyperlink( |
| 866 | 866 | Renderer *p, /* Renderer state */ |
| @@ -1019,11 +1019,11 @@ | ||
| 1019 | 1019 | static int in_this_repo(const char *zUuid){ |
| 1020 | 1020 | static Stmt q; |
| 1021 | 1021 | int rc; |
| 1022 | 1022 | int n; |
| 1023 | 1023 | char zU2[UUID_SIZE+1]; |
| 1024 | - db_static_prepare(&q, | |
| 1024 | + db_static_prepare(&q, | |
| 1025 | 1025 | "SELECT 1 FROM blob WHERE uuid>=:u AND uuid<:u2" |
| 1026 | 1026 | ); |
| 1027 | 1027 | db_bind_text(&q, ":u", zUuid); |
| 1028 | 1028 | n = (int)strlen(zUuid); |
| 1029 | 1029 | if( n>=sizeof(zU2) ) n = sizeof(zU2)-1; |
| @@ -1165,11 +1165,11 @@ | ||
| 1165 | 1165 | zTerm = "]</a>"; |
| 1166 | 1166 | } |
| 1167 | 1167 | }else if( strlen(zTarget)>=10 && fossil_isdigit(zTarget[0]) && zTarget[4]=='-' |
| 1168 | 1168 | && db_int(0, "SELECT datetime(%Q) NOT NULL", zTarget) ){ |
| 1169 | 1169 | blob_appendf(p->pOut, "<a href=\"%R/timeline?c=%T\">", zTarget); |
| 1170 | - }else if( strncmp(zTarget, "wiki:", 5)==0 | |
| 1170 | + }else if( strncmp(zTarget, "wiki:", 5)==0 | |
| 1171 | 1171 | && wiki_name_is_wellformed((const unsigned char*)zTarget) ){ |
| 1172 | 1172 | zTarget += 5; |
| 1173 | 1173 | blob_appendf(p->pOut, "<a href=\"%R/wiki?name=%T\">", zTarget); |
| 1174 | 1174 | }else if( wiki_name_is_wellformed((const unsigned char *)zTarget) ){ |
| 1175 | 1175 | blob_appendf(p->pOut, "<a href=\"%R/wiki?name=%T\">", zTarget); |
| @@ -1547,29 +1547,18 @@ | ||
| 1547 | 1547 | } |
| 1548 | 1548 | z += n; |
| 1549 | 1549 | } |
| 1550 | 1550 | } |
| 1551 | 1551 | |
| 1552 | -/* | |
| 1553 | -** Skip over the UTF-8 Byte-Order-Mark that some broken Windows | |
| 1554 | -** tools add to the beginning of text files. | |
| 1555 | -*/ | |
| 1556 | -char *skip_bom(char *z){ | |
| 1557 | - static const unsigned char bom[] = { 0xEF, 0xBB, 0xBF }; | |
| 1558 | - if( z && memcmp(z, bom, 3)==0 ) z += 3; | |
| 1559 | - return z; | |
| 1560 | -} | |
| 1561 | - | |
| 1562 | 1552 | /* |
| 1563 | 1553 | ** Transform the text in the pIn blob. Write the results |
| 1564 | 1554 | ** into the pOut blob. The pOut blob should already be |
| 1565 | 1555 | ** initialized. The output is merely appended to pOut. |
| 1566 | 1556 | ** If pOut is NULL, then the output is appended to the CGI |
| 1567 | 1557 | ** reply. |
| 1568 | 1558 | */ |
| 1569 | 1559 | void wiki_convert(Blob *pIn, Blob *pOut, int flags){ |
| 1570 | - char *z; | |
| 1571 | 1560 | Renderer renderer; |
| 1572 | 1561 | |
| 1573 | 1562 | memset(&renderer, 0, sizeof(renderer)); |
| 1574 | 1563 | renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH|flags; |
| 1575 | 1564 | if( flags & WIKI_NOBLOCK ){ |
| @@ -1587,12 +1576,12 @@ | ||
| 1587 | 1576 | renderer.pOut = pOut; |
| 1588 | 1577 | }else{ |
| 1589 | 1578 | renderer.pOut = cgi_output_blob(); |
| 1590 | 1579 | } |
| 1591 | 1580 | |
| 1592 | - z = skip_bom(blob_str(pIn)); | |
| 1593 | - wiki_render(&renderer, z); | |
| 1581 | + blob_strip_bom(pIn, 0); | |
| 1582 | + wiki_render(&renderer, blob_str(pIn)); | |
| 1594 | 1583 | endAutoParagraph(&renderer); |
| 1595 | 1584 | while( renderer.nStack ){ |
| 1596 | 1585 | popStack(&renderer); |
| 1597 | 1586 | } |
| 1598 | 1587 | blob_append(renderer.pOut, "\n", 1); |
| @@ -1630,11 +1619,12 @@ | ||
| 1630 | 1619 | */ |
| 1631 | 1620 | int wiki_find_title(Blob *pIn, Blob *pTitle, Blob *pTail){ |
| 1632 | 1621 | char *z; |
| 1633 | 1622 | int i; |
| 1634 | 1623 | int iStart; |
| 1635 | - z = skip_bom(blob_str(pIn)); | |
| 1624 | + blob_strip_bom(pIn, 0); | |
| 1625 | + z = blob_str(pIn); | |
| 1636 | 1626 | for(i=0; fossil_isspace(z[i]); i++){} |
| 1637 | 1627 | if( z[i]!='<' ) return 0; |
| 1638 | 1628 | i++; |
| 1639 | 1629 | if( strncmp(&z[i],"title>", 6)!=0 ) return 0; |
| 1640 | 1630 | iStart = i+6; |
| 1641 | 1631 |
| --- src/wikiformat.c | |
| +++ src/wikiformat.c | |
| @@ -315,11 +315,11 @@ | |
| 315 | { "tbody", MARKUP_TBODY, MUTYPE_BLOCK, |
| 316 | AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, |
| 317 | { "td", MARKUP_TD, MUTYPE_TD, |
| 318 | AMSK_ALIGN|AMSK_BGCOLOR|AMSK_COLSPAN| |
| 319 | AMSK_ROWSPAN|AMSK_VALIGN|AMSK_CLASS|AMSK_STYLE }, |
| 320 | { "tfoot", MARKUP_TFOOT, MUTYPE_BLOCK, |
| 321 | AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, |
| 322 | { "th", MARKUP_TH, MUTYPE_TD, |
| 323 | AMSK_ALIGN|AMSK_BGCOLOR|AMSK_COLSPAN| |
| 324 | AMSK_ROWSPAN|AMSK_VALIGN|AMSK_CLASS|AMSK_STYLE }, |
| 325 | { "thead", MARKUP_THEAD, MUTYPE_BLOCK, |
| @@ -856,11 +856,11 @@ | |
| 856 | ** <a class="button" href="../index.wiki">Index</a> |
| 857 | ** |
| 858 | ** If the markup matches this pattern, and if the WIKI_BUTTONS flag was |
| 859 | ** passed to wiki_convert(), then transform this link into a submenu |
| 860 | ** button, skip the text, and set *pN equal to the total length of the |
| 861 | ** text through the end of </a> and return true. If the markup does |
| 862 | ** not match or if WIKI_BUTTONS is not set, then make no changes to *pN |
| 863 | ** and return false. |
| 864 | */ |
| 865 | static int isButtonHyperlink( |
| 866 | Renderer *p, /* Renderer state */ |
| @@ -1019,11 +1019,11 @@ | |
| 1019 | static int in_this_repo(const char *zUuid){ |
| 1020 | static Stmt q; |
| 1021 | int rc; |
| 1022 | int n; |
| 1023 | char zU2[UUID_SIZE+1]; |
| 1024 | db_static_prepare(&q, |
| 1025 | "SELECT 1 FROM blob WHERE uuid>=:u AND uuid<:u2" |
| 1026 | ); |
| 1027 | db_bind_text(&q, ":u", zUuid); |
| 1028 | n = (int)strlen(zUuid); |
| 1029 | if( n>=sizeof(zU2) ) n = sizeof(zU2)-1; |
| @@ -1165,11 +1165,11 @@ | |
| 1165 | zTerm = "]</a>"; |
| 1166 | } |
| 1167 | }else if( strlen(zTarget)>=10 && fossil_isdigit(zTarget[0]) && zTarget[4]=='-' |
| 1168 | && db_int(0, "SELECT datetime(%Q) NOT NULL", zTarget) ){ |
| 1169 | blob_appendf(p->pOut, "<a href=\"%R/timeline?c=%T\">", zTarget); |
| 1170 | }else if( strncmp(zTarget, "wiki:", 5)==0 |
| 1171 | && wiki_name_is_wellformed((const unsigned char*)zTarget) ){ |
| 1172 | zTarget += 5; |
| 1173 | blob_appendf(p->pOut, "<a href=\"%R/wiki?name=%T\">", zTarget); |
| 1174 | }else if( wiki_name_is_wellformed((const unsigned char *)zTarget) ){ |
| 1175 | blob_appendf(p->pOut, "<a href=\"%R/wiki?name=%T\">", zTarget); |
| @@ -1547,29 +1547,18 @@ | |
| 1547 | } |
| 1548 | z += n; |
| 1549 | } |
| 1550 | } |
| 1551 | |
| 1552 | /* |
| 1553 | ** Skip over the UTF-8 Byte-Order-Mark that some broken Windows |
| 1554 | ** tools add to the beginning of text files. |
| 1555 | */ |
| 1556 | char *skip_bom(char *z){ |
| 1557 | static const unsigned char bom[] = { 0xEF, 0xBB, 0xBF }; |
| 1558 | if( z && memcmp(z, bom, 3)==0 ) z += 3; |
| 1559 | return z; |
| 1560 | } |
| 1561 | |
| 1562 | /* |
| 1563 | ** Transform the text in the pIn blob. Write the results |
| 1564 | ** into the pOut blob. The pOut blob should already be |
| 1565 | ** initialized. The output is merely appended to pOut. |
| 1566 | ** If pOut is NULL, then the output is appended to the CGI |
| 1567 | ** reply. |
| 1568 | */ |
| 1569 | void wiki_convert(Blob *pIn, Blob *pOut, int flags){ |
| 1570 | char *z; |
| 1571 | Renderer renderer; |
| 1572 | |
| 1573 | memset(&renderer, 0, sizeof(renderer)); |
| 1574 | renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH|flags; |
| 1575 | if( flags & WIKI_NOBLOCK ){ |
| @@ -1587,12 +1576,12 @@ | |
| 1587 | renderer.pOut = pOut; |
| 1588 | }else{ |
| 1589 | renderer.pOut = cgi_output_blob(); |
| 1590 | } |
| 1591 | |
| 1592 | z = skip_bom(blob_str(pIn)); |
| 1593 | wiki_render(&renderer, z); |
| 1594 | endAutoParagraph(&renderer); |
| 1595 | while( renderer.nStack ){ |
| 1596 | popStack(&renderer); |
| 1597 | } |
| 1598 | blob_append(renderer.pOut, "\n", 1); |
| @@ -1630,11 +1619,12 @@ | |
| 1630 | */ |
| 1631 | int wiki_find_title(Blob *pIn, Blob *pTitle, Blob *pTail){ |
| 1632 | char *z; |
| 1633 | int i; |
| 1634 | int iStart; |
| 1635 | z = skip_bom(blob_str(pIn)); |
| 1636 | for(i=0; fossil_isspace(z[i]); i++){} |
| 1637 | if( z[i]!='<' ) return 0; |
| 1638 | i++; |
| 1639 | if( strncmp(&z[i],"title>", 6)!=0 ) return 0; |
| 1640 | iStart = i+6; |
| 1641 |
| --- src/wikiformat.c | |
| +++ src/wikiformat.c | |
| @@ -315,11 +315,11 @@ | |
| 315 | { "tbody", MARKUP_TBODY, MUTYPE_BLOCK, |
| 316 | AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, |
| 317 | { "td", MARKUP_TD, MUTYPE_TD, |
| 318 | AMSK_ALIGN|AMSK_BGCOLOR|AMSK_COLSPAN| |
| 319 | AMSK_ROWSPAN|AMSK_VALIGN|AMSK_CLASS|AMSK_STYLE }, |
| 320 | { "tfoot", MARKUP_TFOOT, MUTYPE_BLOCK, |
| 321 | AMSK_ALIGN|AMSK_CLASS|AMSK_STYLE }, |
| 322 | { "th", MARKUP_TH, MUTYPE_TD, |
| 323 | AMSK_ALIGN|AMSK_BGCOLOR|AMSK_COLSPAN| |
| 324 | AMSK_ROWSPAN|AMSK_VALIGN|AMSK_CLASS|AMSK_STYLE }, |
| 325 | { "thead", MARKUP_THEAD, MUTYPE_BLOCK, |
| @@ -856,11 +856,11 @@ | |
| 856 | ** <a class="button" href="../index.wiki">Index</a> |
| 857 | ** |
| 858 | ** If the markup matches this pattern, and if the WIKI_BUTTONS flag was |
| 859 | ** passed to wiki_convert(), then transform this link into a submenu |
| 860 | ** button, skip the text, and set *pN equal to the total length of the |
| 861 | ** text through the end of </a> and return true. If the markup does |
| 862 | ** not match or if WIKI_BUTTONS is not set, then make no changes to *pN |
| 863 | ** and return false. |
| 864 | */ |
| 865 | static int isButtonHyperlink( |
| 866 | Renderer *p, /* Renderer state */ |
| @@ -1019,11 +1019,11 @@ | |
| 1019 | static int in_this_repo(const char *zUuid){ |
| 1020 | static Stmt q; |
| 1021 | int rc; |
| 1022 | int n; |
| 1023 | char zU2[UUID_SIZE+1]; |
| 1024 | db_static_prepare(&q, |
| 1025 | "SELECT 1 FROM blob WHERE uuid>=:u AND uuid<:u2" |
| 1026 | ); |
| 1027 | db_bind_text(&q, ":u", zUuid); |
| 1028 | n = (int)strlen(zUuid); |
| 1029 | if( n>=sizeof(zU2) ) n = sizeof(zU2)-1; |
| @@ -1165,11 +1165,11 @@ | |
| 1165 | zTerm = "]</a>"; |
| 1166 | } |
| 1167 | }else if( strlen(zTarget)>=10 && fossil_isdigit(zTarget[0]) && zTarget[4]=='-' |
| 1168 | && db_int(0, "SELECT datetime(%Q) NOT NULL", zTarget) ){ |
| 1169 | blob_appendf(p->pOut, "<a href=\"%R/timeline?c=%T\">", zTarget); |
| 1170 | }else if( strncmp(zTarget, "wiki:", 5)==0 |
| 1171 | && wiki_name_is_wellformed((const unsigned char*)zTarget) ){ |
| 1172 | zTarget += 5; |
| 1173 | blob_appendf(p->pOut, "<a href=\"%R/wiki?name=%T\">", zTarget); |
| 1174 | }else if( wiki_name_is_wellformed((const unsigned char *)zTarget) ){ |
| 1175 | blob_appendf(p->pOut, "<a href=\"%R/wiki?name=%T\">", zTarget); |
| @@ -1547,29 +1547,18 @@ | |
| 1547 | } |
| 1548 | z += n; |
| 1549 | } |
| 1550 | } |
| 1551 | |
| 1552 | /* |
| 1553 | ** Transform the text in the pIn blob. Write the results |
| 1554 | ** into the pOut blob. The pOut blob should already be |
| 1555 | ** initialized. The output is merely appended to pOut. |
| 1556 | ** If pOut is NULL, then the output is appended to the CGI |
| 1557 | ** reply. |
| 1558 | */ |
| 1559 | void wiki_convert(Blob *pIn, Blob *pOut, int flags){ |
| 1560 | Renderer renderer; |
| 1561 | |
| 1562 | memset(&renderer, 0, sizeof(renderer)); |
| 1563 | renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH|flags; |
| 1564 | if( flags & WIKI_NOBLOCK ){ |
| @@ -1587,12 +1576,12 @@ | |
| 1576 | renderer.pOut = pOut; |
| 1577 | }else{ |
| 1578 | renderer.pOut = cgi_output_blob(); |
| 1579 | } |
| 1580 | |
| 1581 | blob_strip_bom(pIn, 0); |
| 1582 | wiki_render(&renderer, blob_str(pIn)); |
| 1583 | endAutoParagraph(&renderer); |
| 1584 | while( renderer.nStack ){ |
| 1585 | popStack(&renderer); |
| 1586 | } |
| 1587 | blob_append(renderer.pOut, "\n", 1); |
| @@ -1630,11 +1619,12 @@ | |
| 1619 | */ |
| 1620 | int wiki_find_title(Blob *pIn, Blob *pTitle, Blob *pTail){ |
| 1621 | char *z; |
| 1622 | int i; |
| 1623 | int iStart; |
| 1624 | blob_strip_bom(pIn, 0); |
| 1625 | z = blob_str(pIn); |
| 1626 | for(i=0; fossil_isspace(z[i]); i++){} |
| 1627 | if( z[i]!='<' ) return 0; |
| 1628 | i++; |
| 1629 | if( strncmp(&z[i],"title>", 6)!=0 ) return 0; |
| 1630 | iStart = i+6; |
| 1631 |