OLD | NEW |
1 /* | 1 /* |
2 ** 2013-04-05 | 2 ** 2013-04-05 |
3 ** | 3 ** |
4 ** The author disclaims copyright to this source code. In place of | 4 ** The author disclaims copyright to this source code. In place of |
5 ** a legal notice, here is a blessing: | 5 ** a legal notice, here is a blessing: |
6 ** | 6 ** |
7 ** May you do good and not evil. | 7 ** May you do good and not evil. |
8 ** May you find forgiveness for yourself and forgive others. | 8 ** May you find forgiveness for yourself and forgive others. |
9 ** May you share freely, never taking more than you give. | 9 ** May you share freely, never taking more than you give. |
10 ** | 10 ** |
(...skipping 28 matching lines...) Expand all Loading... |
39 # define WIN32_LEAN_AND_MEAN | 39 # define WIN32_LEAN_AND_MEAN |
40 # include <windows.h> | 40 # include <windows.h> |
41 #else | 41 #else |
42 # include <unistd.h> | 42 # include <unistd.h> |
43 #endif | 43 #endif |
44 #include <stdlib.h> | 44 #include <stdlib.h> |
45 #include <string.h> | 45 #include <string.h> |
46 #include <assert.h> | 46 #include <assert.h> |
47 #include <ctype.h> | 47 #include <ctype.h> |
48 | 48 |
| 49 #define ISSPACE(X) isspace((unsigned char)(X)) |
| 50 #define ISDIGIT(X) isdigit((unsigned char)(X)) |
| 51 |
49 /* The suffix to append to the child command lines, if any */ | 52 /* The suffix to append to the child command lines, if any */ |
50 #if defined(_WIN32) | 53 #if defined(_WIN32) |
51 # define GETPID (int)GetCurrentProcessId | 54 # define GETPID (int)GetCurrentProcessId |
52 #else | 55 #else |
53 # define GETPID getpid | 56 # define GETPID getpid |
54 #endif | 57 #endif |
55 | 58 |
| 59 /* The directory separator character(s) */ |
| 60 #if defined(_WIN32) |
| 61 # define isDirSep(c) (((c) == '/') || ((c) == '\\')) |
| 62 #else |
| 63 # define isDirSep(c) ((c) == '/') |
| 64 #endif |
| 65 |
56 /* Mark a parameter as unused to suppress compiler warnings */ | 66 /* Mark a parameter as unused to suppress compiler warnings */ |
57 #define UNUSED_PARAMETER(x) (void)x | 67 #define UNUSED_PARAMETER(x) (void)x |
58 | 68 |
59 /* Global data | 69 /* Global data |
60 */ | 70 */ |
61 static struct Global { | 71 static struct Global { |
62 char *argv0; /* Name of the executable */ | 72 char *argv0; /* Name of the executable */ |
63 const char *zVfs; /* Name of VFS to use. Often NULL meaning "default" */ | 73 const char *zVfs; /* Name of VFS to use. Often NULL meaning "default" */ |
64 char *zDbFile; /* Name of the database */ | 74 char *zDbFile; /* Name of the database */ |
65 sqlite3 *db; /* Open connection to database */ | 75 sqlite3 *db; /* Open connection to database */ |
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
173 }else{ | 183 }else{ |
174 if( c==c2 ){ | 184 if( c==c2 ){ |
175 seen = 1; | 185 seen = 1; |
176 } | 186 } |
177 prior_c = c2; | 187 prior_c = c2; |
178 } | 188 } |
179 c2 = *(zGlob++); | 189 c2 = *(zGlob++); |
180 } | 190 } |
181 if( c2==0 || (seen ^ invert)==0 ) return 0; | 191 if( c2==0 || (seen ^ invert)==0 ) return 0; |
182 }else if( c=='#' ){ | 192 }else if( c=='#' ){ |
183 if( (z[0]=='-' || z[0]=='+') && isdigit(z[1]) ) z++; | 193 if( (z[0]=='-' || z[0]=='+') && ISDIGIT(z[1]) ) z++; |
184 if( !isdigit(z[0]) ) return 0; | 194 if( !ISDIGIT(z[0]) ) return 0; |
185 z++; | 195 z++; |
186 while( isdigit(z[0]) ){ z++; } | 196 while( ISDIGIT(z[0]) ){ z++; } |
187 }else{ | 197 }else{ |
188 if( c!=(*(z++)) ) return 0; | 198 if( c!=(*(z++)) ) return 0; |
189 } | 199 } |
190 } | 200 } |
191 return *z==0; | 201 return *z==0; |
192 } | 202 } |
193 | 203 |
194 /* | 204 /* |
195 ** Close output stream pOut if it is not stdout or stderr | 205 ** Close output stream pOut if it is not stdout or stderr |
196 */ | 206 */ |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
275 fflush(g.pLog); | 285 fflush(g.pLog); |
276 } | 286 } |
277 sqlite3_free(zMsg); | 287 sqlite3_free(zMsg); |
278 } | 288 } |
279 | 289 |
280 /* | 290 /* |
281 ** Return the length of a string omitting trailing whitespace | 291 ** Return the length of a string omitting trailing whitespace |
282 */ | 292 */ |
283 static int clipLength(const char *z){ | 293 static int clipLength(const char *z){ |
284 int n = (int)strlen(z); | 294 int n = (int)strlen(z); |
285 while( n>0 && isspace(z[n-1]) ){ n--; } | 295 while( n>0 && ISSPACE(z[n-1]) ){ n--; } |
286 return n; | 296 return n; |
287 } | 297 } |
288 | 298 |
289 /* | 299 /* |
290 ** Auxiliary SQL function to return the name of the VFS | 300 ** Auxiliary SQL function to return the name of the VFS |
291 */ | 301 */ |
292 static void vfsNameFunc( | 302 static void vfsNameFunc( |
293 sqlite3_context *context, | 303 sqlite3_context *context, |
294 int argc, | 304 int argc, |
295 sqlite3_value **argv | 305 sqlite3_value **argv |
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
405 static void stringFree(String *p){ | 415 static void stringFree(String *p){ |
406 if( p->z ) sqlite3_free(p->z); | 416 if( p->z ) sqlite3_free(p->z); |
407 memset(p, 0, sizeof(*p)); | 417 memset(p, 0, sizeof(*p)); |
408 } | 418 } |
409 | 419 |
410 /* Append n bytes of text to a string. If n<0 append the entire string. */ | 420 /* Append n bytes of text to a string. If n<0 append the entire string. */ |
411 static void stringAppend(String *p, const char *z, int n){ | 421 static void stringAppend(String *p, const char *z, int n){ |
412 if( n<0 ) n = (int)strlen(z); | 422 if( n<0 ) n = (int)strlen(z); |
413 if( p->n+n>=p->nAlloc ){ | 423 if( p->n+n>=p->nAlloc ){ |
414 int nAlloc = p->nAlloc*2 + n + 100; | 424 int nAlloc = p->nAlloc*2 + n + 100; |
415 char *z = sqlite3_realloc(p->z, nAlloc); | 425 char *zNew = sqlite3_realloc(p->z, nAlloc); |
416 if( z==0 ) fatalError("out of memory"); | 426 if( zNew==0 ) fatalError("out of memory"); |
417 p->z = z; | 427 p->z = zNew; |
418 p->nAlloc = nAlloc; | 428 p->nAlloc = nAlloc; |
419 } | 429 } |
420 memcpy(p->z+p->n, z, n); | 430 memcpy(p->z+p->n, z, n); |
421 p->n += n; | 431 p->n += n; |
422 p->z[p->n] = 0; | 432 p->z[p->n] = 0; |
423 } | 433 } |
424 | 434 |
425 /* Reset a string to an empty string */ | 435 /* Reset a string to an empty string */ |
426 static void stringReset(String *p){ | 436 static void stringReset(String *p){ |
427 if( p->z==0 ) stringAppend(p, " ", 1); | 437 if( p->z==0 ) stringAppend(p, " ", 1); |
428 p->n = 0; | 438 p->n = 0; |
429 p->z[0] = 0; | 439 p->z[0] = 0; |
430 } | 440 } |
431 | 441 |
432 /* Append a new token onto the end of the string */ | 442 /* Append a new token onto the end of the string */ |
433 static void stringAppendTerm(String *p, const char *z){ | 443 static void stringAppendTerm(String *p, const char *z){ |
434 int i; | 444 int i; |
435 if( p->n ) stringAppend(p, " ", 1); | 445 if( p->n ) stringAppend(p, " ", 1); |
436 if( z==0 ){ | 446 if( z==0 ){ |
437 stringAppend(p, "nil", 3); | 447 stringAppend(p, "nil", 3); |
438 return; | 448 return; |
439 } | 449 } |
440 for(i=0; z[i] && !isspace(z[i]); i++){} | 450 for(i=0; z[i] && !ISSPACE(z[i]); i++){} |
441 if( i>0 && z[i]==0 ){ | 451 if( i>0 && z[i]==0 ){ |
442 stringAppend(p, z, i); | 452 stringAppend(p, z, i); |
443 return; | 453 return; |
444 } | 454 } |
445 stringAppend(p, "'", 1); | 455 stringAppend(p, "'", 1); |
446 while( z[0] ){ | 456 while( z[0] ){ |
447 for(i=0; z[i] && z[i]!='\''; i++){} | 457 for(i=0; z[i] && z[i]!='\''; i++){} |
448 if( z[i] ){ | 458 if( z[i] ){ |
449 stringAppend(p, z, i+1); | 459 stringAppend(p, z, i+1); |
450 stringAppend(p, "'", 1); | 460 stringAppend(p, "'", 1); |
(...skipping 234 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
685 z[sz] = 0; | 695 z[sz] = 0; |
686 fclose(in); | 696 fclose(in); |
687 return z; | 697 return z; |
688 } | 698 } |
689 | 699 |
690 /* | 700 /* |
691 ** Return the length of the next token. | 701 ** Return the length of the next token. |
692 */ | 702 */ |
693 static int tokenLength(const char *z, int *pnLine){ | 703 static int tokenLength(const char *z, int *pnLine){ |
694 int n = 0; | 704 int n = 0; |
695 if( isspace(z[0]) || (z[0]=='/' && z[1]=='*') ){ | 705 if( ISSPACE(z[0]) || (z[0]=='/' && z[1]=='*') ){ |
696 int inC = 0; | 706 int inC = 0; |
697 int c; | 707 int c; |
698 if( z[0]=='/' ){ | 708 if( z[0]=='/' ){ |
699 inC = 1; | 709 inC = 1; |
700 n = 2; | 710 n = 2; |
701 } | 711 } |
702 while( (c = z[n++])!=0 ){ | 712 while( (c = z[n++])!=0 ){ |
703 if( c=='\n' ) (*pnLine)++; | 713 if( c=='\n' ) (*pnLine)++; |
704 if( isspace(c) ) continue; | 714 if( ISSPACE(c) ) continue; |
705 if( inC && c=='*' && z[n]=='/' ){ | 715 if( inC && c=='*' && z[n]=='/' ){ |
706 n++; | 716 n++; |
707 inC = 0; | 717 inC = 0; |
708 }else if( !inC && c=='/' && z[n]=='*' ){ | 718 }else if( !inC && c=='/' && z[n]=='*' ){ |
709 n++; | 719 n++; |
710 inC = 1; | 720 inC = 1; |
711 }else if( !inC ){ | 721 }else if( !inC ){ |
712 break; | 722 break; |
713 } | 723 } |
714 } | 724 } |
715 n--; | 725 n--; |
716 }else if( z[0]=='-' && z[1]=='-' ){ | 726 }else if( z[0]=='-' && z[1]=='-' ){ |
717 for(n=2; z[n] && z[n]!='\n'; n++){} | 727 for(n=2; z[n] && z[n]!='\n'; n++){} |
718 if( z[n] ){ (*pnLine)++; n++; } | 728 if( z[n] ){ (*pnLine)++; n++; } |
719 }else if( z[0]=='"' || z[0]=='\'' ){ | 729 }else if( z[0]=='"' || z[0]=='\'' ){ |
720 int delim = z[0]; | 730 int delim = z[0]; |
721 for(n=1; z[n]; n++){ | 731 for(n=1; z[n]; n++){ |
722 if( z[n]=='\n' ) (*pnLine)++; | 732 if( z[n]=='\n' ) (*pnLine)++; |
723 if( z[n]==delim ){ | 733 if( z[n]==delim ){ |
724 n++; | 734 n++; |
725 if( z[n+1]!=delim ) break; | 735 if( z[n+1]!=delim ) break; |
726 } | 736 } |
727 } | 737 } |
728 }else{ | 738 }else{ |
729 int c; | 739 int c; |
730 for(n=1; (c = z[n])!=0 && !isspace(c) && c!='"' && c!='\'' && c!=';'; n++){} | 740 for(n=1; (c = z[n])!=0 && !ISSPACE(c) && c!='"' && c!='\'' && c!=';'; n++){} |
731 } | 741 } |
732 return n; | 742 return n; |
733 } | 743 } |
734 | 744 |
735 /* | 745 /* |
736 ** Copy a single token into a string buffer. | 746 ** Copy a single token into a string buffer. |
737 */ | 747 */ |
738 static int extractToken(const char *zIn, int nIn, char *zOut, int nOut){ | 748 static int extractToken(const char *zIn, int nIn, char *zOut, int nOut){ |
739 int i; | 749 int i; |
740 if( nIn<=0 ){ | 750 if( nIn<=0 ){ |
741 zOut[0] = 0; | 751 zOut[0] = 0; |
742 return 0; | 752 return 0; |
743 } | 753 } |
744 for(i=0; i<nIn && i<nOut-1 && !isspace(zIn[i]); i++){ zOut[i] = zIn[i]; } | 754 for(i=0; i<nIn && i<nOut-1 && !ISSPACE(zIn[i]); i++){ zOut[i] = zIn[i]; } |
745 zOut[i] = 0; | 755 zOut[i] = 0; |
746 return i; | 756 return i; |
747 } | 757 } |
748 | 758 |
749 /* | 759 /* |
750 ** Find the number of characters up to the start of the next "--end" token. | 760 ** Find the number of characters up to the start of the next "--end" token. |
751 */ | 761 */ |
752 static int findEnd(const char *z, int *pnLine){ | 762 static int findEnd(const char *z, int *pnLine){ |
753 int n = 0; | 763 int n = 0; |
754 while( z[n] && (strncmp(z+n,"--end",5) || !isspace(z[n+5])) ){ | 764 while( z[n] && (strncmp(z+n,"--end",5) || !ISSPACE(z[n+5])) ){ |
755 n += tokenLength(z+n, pnLine); | 765 n += tokenLength(z+n, pnLine); |
756 } | 766 } |
757 return n; | 767 return n; |
758 } | 768 } |
759 | 769 |
760 /* | 770 /* |
761 ** Find the number of characters up to the first character past the | 771 ** Find the number of characters up to the first character past the |
762 ** of the next "--endif" or "--else" token. Nested --if commands are | 772 ** of the next "--endif" or "--else" token. Nested --if commands are |
763 ** also skipped. | 773 ** also skipped. |
764 */ | 774 */ |
765 static int findEndif(const char *z, int stopAtElse, int *pnLine){ | 775 static int findEndif(const char *z, int stopAtElse, int *pnLine){ |
766 int n = 0; | 776 int n = 0; |
767 while( z[n] ){ | 777 while( z[n] ){ |
768 int len = tokenLength(z+n, pnLine); | 778 int len = tokenLength(z+n, pnLine); |
769 if( (strncmp(z+n,"--endif",7)==0 && isspace(z[n+7])) | 779 if( (strncmp(z+n,"--endif",7)==0 && ISSPACE(z[n+7])) |
770 || (stopAtElse && strncmp(z+n,"--else",6)==0 && isspace(z[n+6])) | 780 || (stopAtElse && strncmp(z+n,"--else",6)==0 && ISSPACE(z[n+6])) |
771 ){ | 781 ){ |
772 return n+len; | 782 return n+len; |
773 } | 783 } |
774 if( strncmp(z+n,"--if",4)==0 && isspace(z[n+4]) ){ | 784 if( strncmp(z+n,"--if",4)==0 && ISSPACE(z[n+4]) ){ |
775 int skip = findEndif(z+n+len, 0, pnLine); | 785 int skip = findEndif(z+n+len, 0, pnLine); |
776 n += skip + len; | 786 n += skip + len; |
777 }else{ | 787 }else{ |
778 n += len; | 788 n += len; |
779 } | 789 } |
780 } | 790 } |
781 return n; | 791 return n; |
782 } | 792 } |
783 | 793 |
784 /* | 794 /* |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
817 }else{ | 827 }else{ |
818 errorMessage("%stimeout waiting for all clients", zErrPrefix); | 828 errorMessage("%stimeout waiting for all clients", zErrPrefix); |
819 } | 829 } |
820 } | 830 } |
821 } | 831 } |
822 | 832 |
823 /* Return a pointer to the tail of a filename | 833 /* Return a pointer to the tail of a filename |
824 */ | 834 */ |
825 static char *filenameTail(char *z){ | 835 static char *filenameTail(char *z){ |
826 int i, j; | 836 int i, j; |
827 for(i=j=0; z[i]; i++) if( z[i]=='/' ) j = i+1; | 837 for(i=j=0; z[i]; i++) if( isDirSep(z[i]) ) j = i+1; |
828 return z+j; | 838 return z+j; |
829 } | 839 } |
830 | 840 |
831 /* | 841 /* |
832 ** Interpret zArg as a boolean value. Return either 0 or 1. | 842 ** Interpret zArg as a boolean value. Return either 0 or 1. |
833 */ | 843 */ |
834 static int booleanValue(char *zArg){ | 844 static int booleanValue(char *zArg){ |
835 int i; | 845 int i; |
836 if( zArg==0 ) return 0; | 846 if( zArg==0 ) return 0; |
837 for(i=0; zArg[i]>='0' && zArg[i]<='9'; i++){} | 847 for(i=0; zArg[i]>='0' && zArg[i]<='9'; i++){} |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
874 String sResult; | 884 String sResult; |
875 char zCmd[30]; | 885 char zCmd[30]; |
876 char zError[1000]; | 886 char zError[1000]; |
877 char azArg[MX_ARG][100]; | 887 char azArg[MX_ARG][100]; |
878 | 888 |
879 memset(&sResult, 0, sizeof(sResult)); | 889 memset(&sResult, 0, sizeof(sResult)); |
880 stringReset(&sResult); | 890 stringReset(&sResult); |
881 while( (c = zScript[ii])!=0 ){ | 891 while( (c = zScript[ii])!=0 ){ |
882 prevLine = lineno; | 892 prevLine = lineno; |
883 len = tokenLength(zScript+ii, &lineno); | 893 len = tokenLength(zScript+ii, &lineno); |
884 if( isspace(c) || (c=='/' && zScript[ii+1]=='*') ){ | 894 if( ISSPACE(c) || (c=='/' && zScript[ii+1]=='*') ){ |
885 ii += len; | 895 ii += len; |
886 continue; | 896 continue; |
887 } | 897 } |
888 if( c!='-' || zScript[ii+1]!='-' || !isalpha(zScript[ii+2]) ){ | 898 if( c!='-' || zScript[ii+1]!='-' || !isalpha(zScript[ii+2]) ){ |
889 ii += len; | 899 ii += len; |
890 continue; | 900 continue; |
891 } | 901 } |
892 | 902 |
893 /* Run any prior SQL before processing the new --command */ | 903 /* Run any prior SQL before processing the new --command */ |
894 if( ii>iBegin ){ | 904 if( ii>iBegin ){ |
895 char *zSql = sqlite3_mprintf("%.*s", ii-iBegin, zScript+iBegin); | 905 char *zSql = sqlite3_mprintf("%.*s", ii-iBegin, zScript+iBegin); |
896 evalSql(&sResult, zSql); | 906 evalSql(&sResult, zSql); |
897 sqlite3_free(zSql); | 907 sqlite3_free(zSql); |
898 iBegin = ii + len; | 908 iBegin = ii + len; |
899 } | 909 } |
900 | 910 |
901 /* Parse the --command */ | 911 /* Parse the --command */ |
902 if( g.iTrace>=2 ) logMessage("%.*s", len, zScript+ii); | 912 if( g.iTrace>=2 ) logMessage("%.*s", len, zScript+ii); |
903 n = extractToken(zScript+ii+2, len-2, zCmd, sizeof(zCmd)); | 913 n = extractToken(zScript+ii+2, len-2, zCmd, sizeof(zCmd)); |
904 for(nArg=0; n<len-2 && nArg<MX_ARG; nArg++){ | 914 for(nArg=0; n<len-2 && nArg<MX_ARG; nArg++){ |
905 while( n<len-2 && isspace(zScript[ii+2+n]) ){ n++; } | 915 while( n<len-2 && ISSPACE(zScript[ii+2+n]) ){ n++; } |
906 if( n>=len-2 ) break; | 916 if( n>=len-2 ) break; |
907 n += extractToken(zScript+ii+2+n, len-2-n, | 917 n += extractToken(zScript+ii+2+n, len-2-n, |
908 azArg[nArg], sizeof(azArg[nArg])); | 918 azArg[nArg], sizeof(azArg[nArg])); |
909 } | 919 } |
910 for(j=nArg; j<MX_ARG; j++) azArg[j++][0] = 0; | 920 for(j=nArg; j<MX_ARG; j++) azArg[j++][0] = 0; |
911 | 921 |
912 /* | 922 /* |
913 ** --sleep N | 923 ** --sleep N |
914 ** | 924 ** |
915 ** Pause for N milliseconds | 925 ** Pause for N milliseconds |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
962 }else | 972 }else |
963 | 973 |
964 /* | 974 /* |
965 ** --match ANSWER... | 975 ** --match ANSWER... |
966 ** | 976 ** |
967 ** Check to see if output matches ANSWER. Report an error if not. | 977 ** Check to see if output matches ANSWER. Report an error if not. |
968 */ | 978 */ |
969 if( strcmp(zCmd, "match")==0 ){ | 979 if( strcmp(zCmd, "match")==0 ){ |
970 int jj; | 980 int jj; |
971 char *zAns = zScript+ii; | 981 char *zAns = zScript+ii; |
972 for(jj=7; jj<len-1 && isspace(zAns[jj]); jj++){} | 982 for(jj=7; jj<len-1 && ISSPACE(zAns[jj]); jj++){} |
973 zAns += jj; | 983 zAns += jj; |
974 if( len-jj-1!=sResult.n || strncmp(sResult.z, zAns, len-jj-1) ){ | 984 if( len-jj-1!=sResult.n || strncmp(sResult.z, zAns, len-jj-1) ){ |
975 errorMessage("line %d of %s:\nExpected [%.*s]\n Got [%s]", | 985 errorMessage("line %d of %s:\nExpected [%.*s]\n Got [%s]", |
976 prevLine, zFilename, len-jj-1, zAns, sResult.z); | 986 prevLine, zFilename, len-jj-1, zAns, sResult.z); |
977 } | 987 } |
978 g.nTest++; | 988 g.nTest++; |
979 stringReset(&sResult); | 989 stringReset(&sResult); |
980 }else | 990 }else |
981 | 991 |
982 /* | 992 /* |
983 ** --glob ANSWER... | 993 ** --glob ANSWER... |
984 ** --notglob ANSWER.... | 994 ** --notglob ANSWER.... |
985 ** | 995 ** |
986 ** Check to see if output does or does not match the glob pattern | 996 ** Check to see if output does or does not match the glob pattern |
987 ** ANSWER. | 997 ** ANSWER. |
988 */ | 998 */ |
989 if( strcmp(zCmd, "glob")==0 || strcmp(zCmd, "notglob")==0 ){ | 999 if( strcmp(zCmd, "glob")==0 || strcmp(zCmd, "notglob")==0 ){ |
990 int jj; | 1000 int jj; |
991 char *zAns = zScript+ii; | 1001 char *zAns = zScript+ii; |
992 char *zCopy; | 1002 char *zCopy; |
993 int isGlob = (zCmd[0]=='g'); | 1003 int isGlob = (zCmd[0]=='g'); |
994 for(jj=9-3*isGlob; jj<len-1 && isspace(zAns[jj]); jj++){} | 1004 for(jj=9-3*isGlob; jj<len-1 && ISSPACE(zAns[jj]); jj++){} |
995 zAns += jj; | 1005 zAns += jj; |
996 zCopy = sqlite3_mprintf("%.*s", len-jj-1, zAns); | 1006 zCopy = sqlite3_mprintf("%.*s", len-jj-1, zAns); |
997 if( (sqlite3_strglob(zCopy, sResult.z)==0)^isGlob ){ | 1007 if( (sqlite3_strglob(zCopy, sResult.z)==0)^isGlob ){ |
998 errorMessage("line %d of %s:\nExpected [%s]\n Got [%s]", | 1008 errorMessage("line %d of %s:\nExpected [%s]\n Got [%s]", |
999 prevLine, zFilename, zCopy, sResult.z); | 1009 prevLine, zFilename, zCopy, sResult.z); |
1000 } | 1010 } |
1001 sqlite3_free(zCopy); | 1011 sqlite3_free(zCopy); |
1002 g.nTest++; | 1012 g.nTest++; |
1003 stringReset(&sResult); | 1013 stringReset(&sResult); |
1004 }else | 1014 }else |
1005 | 1015 |
1006 /* | 1016 /* |
1007 ** --output | 1017 ** --output |
1008 ** | 1018 ** |
1009 ** Output the result of the previous SQL. | 1019 ** Output the result of the previous SQL. |
1010 */ | 1020 */ |
1011 if( strcmp(zCmd, "output")==0 ){ | 1021 if( strcmp(zCmd, "output")==0 ){ |
1012 logMessage("%s", sResult.z); | 1022 logMessage("%s", sResult.z); |
1013 }else | 1023 }else |
1014 | 1024 |
1015 /* | 1025 /* |
1016 ** --source FILENAME | 1026 ** --source FILENAME |
1017 ** | 1027 ** |
1018 ** Run a subscript from a separate file. | 1028 ** Run a subscript from a separate file. |
1019 */ | 1029 */ |
1020 if( strcmp(zCmd, "source")==0 ){ | 1030 if( strcmp(zCmd, "source")==0 ){ |
1021 char *zNewFile, *zNewScript; | 1031 char *zNewFile, *zNewScript; |
1022 char *zToDel = 0; | 1032 char *zToDel = 0; |
1023 zNewFile = azArg[0]; | 1033 zNewFile = azArg[0]; |
1024 if( zNewFile[0]!='/' ){ | 1034 if( !isDirSep(zNewFile[0]) ){ |
1025 int k; | 1035 int k; |
1026 for(k=(int)strlen(zFilename)-1; k>=0 && zFilename[k]!='/'; k--){} | 1036 for(k=(int)strlen(zFilename)-1; k>=0 && !isDirSep(zFilename[k]); k--){} |
1027 if( k>0 ){ | 1037 if( k>0 ){ |
1028 zNewFile = zToDel = sqlite3_mprintf("%.*s/%s", k,zFilename,zNewFile); | 1038 zNewFile = zToDel = sqlite3_mprintf("%.*s/%s", k,zFilename,zNewFile); |
1029 } | 1039 } |
1030 } | 1040 } |
1031 zNewScript = readFile(zNewFile); | 1041 zNewScript = readFile(zNewFile); |
1032 if( g.iTrace ) logMessage("begin script [%s]\n", zNewFile); | 1042 if( g.iTrace ) logMessage("begin script [%s]\n", zNewFile); |
1033 runScript(0, 0, zNewScript, zNewFile); | 1043 runScript(0, 0, zNewScript, zNewFile); |
1034 sqlite3_free(zNewScript); | 1044 sqlite3_free(zNewScript); |
1035 if( g.iTrace ) logMessage("end script [%s]\n", zNewFile); | 1045 if( g.iTrace ) logMessage("end script [%s]\n", zNewFile); |
1036 sqlite3_free(zToDel); | 1046 sqlite3_free(zToDel); |
1037 }else | 1047 }else |
1038 | 1048 |
1039 /* | 1049 /* |
1040 ** --print MESSAGE.... | 1050 ** --print MESSAGE.... |
1041 ** | 1051 ** |
1042 ** Output the remainder of the line to the log file | 1052 ** Output the remainder of the line to the log file |
1043 */ | 1053 */ |
1044 if( strcmp(zCmd, "print")==0 ){ | 1054 if( strcmp(zCmd, "print")==0 ){ |
1045 int jj; | 1055 int jj; |
1046 for(jj=7; jj<len && isspace(zScript[ii+jj]); jj++){} | 1056 for(jj=7; jj<len && ISSPACE(zScript[ii+jj]); jj++){} |
1047 logMessage("%.*s", len-jj, zScript+ii+jj); | 1057 logMessage("%.*s", len-jj, zScript+ii+jj); |
1048 }else | 1058 }else |
1049 | 1059 |
1050 /* | 1060 /* |
1051 ** --if EXPR | 1061 ** --if EXPR |
1052 ** | 1062 ** |
1053 ** Skip forward to the next matching --endif or --else if EXPR is false. | 1063 ** Skip forward to the next matching --endif or --else if EXPR is false. |
1054 */ | 1064 */ |
1055 if( strcmp(zCmd, "if")==0 ){ | 1065 if( strcmp(zCmd, "if")==0 ){ |
1056 int jj, rc; | 1066 int jj, rc; |
1057 sqlite3_stmt *pStmt; | 1067 sqlite3_stmt *pStmt; |
1058 for(jj=4; jj<len && isspace(zScript[ii+jj]); jj++){} | 1068 for(jj=4; jj<len && ISSPACE(zScript[ii+jj]); jj++){} |
1059 pStmt = prepareSql("SELECT %.*s", len-jj, zScript+ii+jj); | 1069 pStmt = prepareSql("SELECT %.*s", len-jj, zScript+ii+jj); |
1060 rc = sqlite3_step(pStmt); | 1070 rc = sqlite3_step(pStmt); |
1061 if( rc!=SQLITE_ROW || sqlite3_column_int(pStmt, 0)==0 ){ | 1071 if( rc!=SQLITE_ROW || sqlite3_column_int(pStmt, 0)==0 ){ |
1062 ii += findEndif(zScript+ii+len, 1, &lineno); | 1072 ii += findEndif(zScript+ii+len, 1, &lineno); |
1063 } | 1073 } |
1064 sqlite3_finalize(pStmt); | 1074 sqlite3_finalize(pStmt); |
1065 }else | 1075 }else |
1066 | 1076 |
1067 /* | 1077 /* |
1068 ** --else | 1078 ** --else |
(...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1224 } | 1234 } |
1225 } | 1235 } |
1226 return zReturn; | 1236 return zReturn; |
1227 } | 1237 } |
1228 | 1238 |
1229 /* Print a usage message for the program and exit */ | 1239 /* Print a usage message for the program and exit */ |
1230 static void usage(const char *argv0){ | 1240 static void usage(const char *argv0){ |
1231 int i; | 1241 int i; |
1232 const char *zTail = argv0; | 1242 const char *zTail = argv0; |
1233 for(i=0; argv0[i]; i++){ | 1243 for(i=0; argv0[i]; i++){ |
1234 if( argv0[i]=='/' ) zTail = argv0+i+1; | 1244 if( isDirSep(argv0[i]) ) zTail = argv0+i+1; |
1235 } | 1245 } |
1236 fprintf(stderr,"Usage: %s DATABASE ?OPTIONS? ?SCRIPT?\n", zTail); | 1246 fprintf(stderr,"Usage: %s DATABASE ?OPTIONS? ?SCRIPT?\n", zTail); |
1237 exit(1); | 1247 exit(1); |
1238 } | 1248 } |
1239 | 1249 |
1240 /* Report on unrecognized arguments */ | 1250 /* Report on unrecognized arguments */ |
1241 static void unrecognizedArguments( | 1251 static void unrecognizedArguments( |
1242 const char *argv0, | 1252 const char *argv0, |
1243 int nArg, | 1253 int nArg, |
1244 char **azArg | 1254 char **azArg |
1245 ){ | 1255 ){ |
1246 int i; | 1256 int i; |
1247 fprintf(stderr,"%s: unrecognized arguments:", argv0); | 1257 fprintf(stderr,"%s: unrecognized arguments:", argv0); |
1248 for(i=0; i<nArg; i++){ | 1258 for(i=0; i<nArg; i++){ |
1249 fprintf(stderr," %s", azArg[i]); | 1259 fprintf(stderr," %s", azArg[i]); |
1250 } | 1260 } |
1251 fprintf(stderr,"\n"); | 1261 fprintf(stderr,"\n"); |
1252 exit(1); | 1262 exit(1); |
1253 } | 1263 } |
1254 | 1264 |
1255 int main(int argc, char **argv){ | 1265 int SQLITE_CDECL main(int argc, char **argv){ |
1256 const char *zClient; | 1266 const char *zClient; |
1257 int iClient; | 1267 int iClient; |
1258 int n, i; | 1268 int n, i; |
1259 int openFlags = SQLITE_OPEN_READWRITE; | 1269 int openFlags = SQLITE_OPEN_READWRITE; |
1260 int rc; | 1270 int rc; |
1261 char *zScript; | 1271 char *zScript; |
1262 int taskId; | 1272 int taskId; |
1263 const char *zTrace; | 1273 const char *zTrace; |
1264 const char *zCOption; | 1274 const char *zCOption; |
| 1275 const char *zJMode; |
| 1276 const char *zNRep; |
| 1277 int nRep = 1, iRep; |
1265 | 1278 |
1266 g.argv0 = argv[0]; | 1279 g.argv0 = argv[0]; |
1267 g.iTrace = 1; | 1280 g.iTrace = 1; |
1268 if( argc<2 ) usage(argv[0]); | 1281 if( argc<2 ) usage(argv[0]); |
1269 g.zDbFile = argv[1]; | 1282 g.zDbFile = argv[1]; |
1270 if( strglob("*.test", g.zDbFile) ) usage(argv[0]); | 1283 if( strglob("*.test", g.zDbFile) ) usage(argv[0]); |
1271 if( strcmp(sqlite3_sourceid(), SQLITE_SOURCE_ID)!=0 ){ | 1284 if( strcmp(sqlite3_sourceid(), SQLITE_SOURCE_ID)!=0 ){ |
1272 fprintf(stderr, "SQLite library and header mismatch\n" | 1285 fprintf(stderr, "SQLite library and header mismatch\n" |
1273 "Library: %s\n" | 1286 "Library: %s\n" |
1274 "Header: %s\n", | 1287 "Header: %s\n", |
1275 sqlite3_sourceid(), SQLITE_SOURCE_ID); | 1288 sqlite3_sourceid(), SQLITE_SOURCE_ID); |
1276 exit(1); | 1289 exit(1); |
1277 } | 1290 } |
1278 n = argc-2; | 1291 n = argc-2; |
1279 sqlite3_snprintf(sizeof(g.zName), g.zName, "%05d.mptest", GETPID()); | 1292 sqlite3_snprintf(sizeof(g.zName), g.zName, "%05d.mptest", GETPID()); |
| 1293 zJMode = findOption(argv+2, &n, "journalmode", 1); |
| 1294 zNRep = findOption(argv+2, &n, "repeat", 1); |
| 1295 if( zNRep ) nRep = atoi(zNRep); |
| 1296 if( nRep<1 ) nRep = 1; |
1280 g.zVfs = findOption(argv+2, &n, "vfs", 1); | 1297 g.zVfs = findOption(argv+2, &n, "vfs", 1); |
1281 zClient = findOption(argv+2, &n, "client", 1); | 1298 zClient = findOption(argv+2, &n, "client", 1); |
1282 g.zErrLog = findOption(argv+2, &n, "errlog", 1); | 1299 g.zErrLog = findOption(argv+2, &n, "errlog", 1); |
1283 g.zLog = findOption(argv+2, &n, "log", 1); | 1300 g.zLog = findOption(argv+2, &n, "log", 1); |
1284 zTrace = findOption(argv+2, &n, "trace", 1); | 1301 zTrace = findOption(argv+2, &n, "trace", 1); |
1285 if( zTrace ) g.iTrace = atoi(zTrace); | 1302 if( zTrace ) g.iTrace = atoi(zTrace); |
1286 if( findOption(argv+2, &n, "quiet", 0)!=0 ) g.iTrace = 0; | 1303 if( findOption(argv+2, &n, "quiet", 0)!=0 ) g.iTrace = 0; |
1287 g.bSqlTrace = findOption(argv+2, &n, "sqltrace", 0)!=0; | 1304 g.bSqlTrace = findOption(argv+2, &n, "sqltrace", 0)!=0; |
1288 g.bSync = findOption(argv+2, &n, "sync", 0)!=0; | 1305 g.bSync = findOption(argv+2, &n, "sync", 0)!=0; |
1289 if( g.zErrLog ){ | 1306 if( g.zErrLog ){ |
1290 g.pErrLog = fopen(g.zErrLog, "a"); | 1307 g.pErrLog = fopen(g.zErrLog, "a"); |
1291 }else{ | 1308 }else{ |
1292 g.pErrLog = stderr; | 1309 g.pErrLog = stderr; |
1293 } | 1310 } |
1294 if( g.zLog ){ | 1311 if( g.zLog ){ |
1295 g.pLog = fopen(g.zLog, "a"); | 1312 g.pLog = fopen(g.zLog, "a"); |
1296 }else{ | 1313 }else{ |
1297 g.pLog = stdout; | 1314 g.pLog = stdout; |
1298 } | 1315 } |
1299 | 1316 |
1300 sqlite3_config(SQLITE_CONFIG_LOG, sqlErrorCallback, 0); | 1317 sqlite3_config(SQLITE_CONFIG_LOG, sqlErrorCallback, 0); |
1301 if( zClient ){ | 1318 if( zClient ){ |
1302 iClient = atoi(zClient); | 1319 iClient = atoi(zClient); |
1303 if( iClient<1 ) fatalError("illegal client number: %d\n", iClient); | 1320 if( iClient<1 ) fatalError("illegal client number: %d\n", iClient); |
1304 sqlite3_snprintf(sizeof(g.zName), g.zName, "%05d.client%02d", | 1321 sqlite3_snprintf(sizeof(g.zName), g.zName, "%05d.client%02d", |
1305 GETPID(), iClient); | 1322 GETPID(), iClient); |
1306 }else{ | 1323 }else{ |
1307 if( g.iTrace>0 ){ | 1324 if( g.iTrace>0 ){ |
| 1325 printf("BEGIN: %s", argv[0]); |
| 1326 for(i=1; i<argc; i++) printf(" %s", argv[i]); |
| 1327 printf("\n"); |
1308 printf("With SQLite " SQLITE_VERSION " " SQLITE_SOURCE_ID "\n" ); | 1328 printf("With SQLite " SQLITE_VERSION " " SQLITE_SOURCE_ID "\n" ); |
1309 for(i=0; (zCOption = sqlite3_compileoption_get(i))!=0; i++){ | 1329 for(i=0; (zCOption = sqlite3_compileoption_get(i))!=0; i++){ |
1310 printf("-DSQLITE_%s\n", zCOption); | 1330 printf("-DSQLITE_%s\n", zCOption); |
1311 } | 1331 } |
1312 fflush(stdout); | 1332 fflush(stdout); |
1313 } | 1333 } |
1314 iClient = 0; | 1334 iClient = 0; |
1315 unlink(g.zDbFile); | 1335 unlink(g.zDbFile); |
1316 openFlags |= SQLITE_OPEN_CREATE; | 1336 openFlags |= SQLITE_OPEN_CREATE; |
1317 } | 1337 } |
1318 rc = sqlite3_open_v2(g.zDbFile, &g.db, openFlags, g.zVfs); | 1338 rc = sqlite3_open_v2(g.zDbFile, &g.db, openFlags, g.zVfs); |
1319 if( rc ) fatalError("cannot open [%s]", g.zDbFile); | 1339 if( rc ) fatalError("cannot open [%s]", g.zDbFile); |
| 1340 if( zJMode ){ |
| 1341 #if defined(_WIN32) |
| 1342 if( sqlite3_stricmp(zJMode,"persist")==0 |
| 1343 || sqlite3_stricmp(zJMode,"truncate")==0 |
| 1344 ){ |
| 1345 printf("Changing journal mode to DELETE from %s", zJMode); |
| 1346 zJMode = "DELETE"; |
| 1347 } |
| 1348 #endif |
| 1349 runSql("PRAGMA journal_mode=%Q;", zJMode); |
| 1350 } |
| 1351 if( !g.bSync ) trySql("PRAGMA synchronous=OFF"); |
1320 sqlite3_enable_load_extension(g.db, 1); | 1352 sqlite3_enable_load_extension(g.db, 1); |
1321 sqlite3_busy_handler(g.db, busyHandler, 0); | 1353 sqlite3_busy_handler(g.db, busyHandler, 0); |
1322 sqlite3_create_function(g.db, "vfsname", 0, SQLITE_UTF8, 0, | 1354 sqlite3_create_function(g.db, "vfsname", 0, SQLITE_UTF8, 0, |
1323 vfsNameFunc, 0, 0); | 1355 vfsNameFunc, 0, 0); |
1324 sqlite3_create_function(g.db, "eval", 1, SQLITE_UTF8, 0, | 1356 sqlite3_create_function(g.db, "eval", 1, SQLITE_UTF8, 0, |
1325 evalFunc, 0, 0); | 1357 evalFunc, 0, 0); |
1326 g.iTimeout = DEFAULT_TIMEOUT; | 1358 g.iTimeout = DEFAULT_TIMEOUT; |
1327 if( g.bSqlTrace ) sqlite3_trace(g.db, sqlTraceCallback, 0); | 1359 if( g.bSqlTrace ) sqlite3_trace(g.db, sqlTraceCallback, 0); |
1328 if( !g.bSync ) trySql("PRAGMA synchronous=OFF"); | |
1329 if( iClient>0 ){ | 1360 if( iClient>0 ){ |
1330 if( n>0 ) unrecognizedArguments(argv[0], n, argv+2); | 1361 if( n>0 ) unrecognizedArguments(argv[0], n, argv+2); |
1331 if( g.iTrace ) logMessage("start-client"); | 1362 if( g.iTrace ) logMessage("start-client"); |
1332 while(1){ | 1363 while(1){ |
1333 char *zTaskName = 0; | 1364 char *zTaskName = 0; |
1334 rc = startScript(iClient, &zScript, &taskId, &zTaskName); | 1365 rc = startScript(iClient, &zScript, &taskId, &zTaskName); |
1335 if( rc==SQLITE_DONE ) break; | 1366 if( rc==SQLITE_DONE ) break; |
1336 if( g.iTrace ) logMessage("begin %s (%d)", zTaskName, taskId); | 1367 if( g.iTrace ) logMessage("begin %s (%d)", zTaskName, taskId); |
1337 runScript(iClient, taskId, zScript, zTaskName); | 1368 runScript(iClient, taskId, zScript, zTaskName); |
1338 if( g.iTrace ) logMessage("end %s (%d)", zTaskName, taskId); | 1369 if( g.iTrace ) logMessage("end %s (%d)", zTaskName, taskId); |
1339 finishScript(iClient, taskId, 0); | 1370 finishScript(iClient, taskId, 0); |
1340 sqlite3_free(zTaskName); | 1371 sqlite3_free(zTaskName); |
1341 sqlite3_sleep(10); | 1372 sqlite3_sleep(10); |
1342 } | 1373 } |
1343 if( g.iTrace ) logMessage("end-client"); | 1374 if( g.iTrace ) logMessage("end-client"); |
1344 }else{ | 1375 }else{ |
1345 sqlite3_stmt *pStmt; | 1376 sqlite3_stmt *pStmt; |
1346 int iTimeout; | 1377 int iTimeout; |
1347 if( n==0 ){ | 1378 if( n==0 ){ |
1348 fatalError("missing script filename"); | 1379 fatalError("missing script filename"); |
1349 } | 1380 } |
1350 if( n>1 ) unrecognizedArguments(argv[0], n, argv+2); | 1381 if( n>1 ) unrecognizedArguments(argv[0], n, argv+2); |
1351 runSql( | 1382 runSql( |
| 1383 "DROP TABLE IF EXISTS task;\n" |
| 1384 "DROP TABLE IF EXISTS counters;\n" |
| 1385 "DROP TABLE IF EXISTS client;\n" |
1352 "CREATE TABLE task(\n" | 1386 "CREATE TABLE task(\n" |
1353 " id INTEGER PRIMARY KEY,\n" | 1387 " id INTEGER PRIMARY KEY,\n" |
1354 " name TEXT,\n" | 1388 " name TEXT,\n" |
1355 " client INTEGER,\n" | 1389 " client INTEGER,\n" |
1356 " starttime DATE,\n" | 1390 " starttime DATE,\n" |
1357 " endtime DATE,\n" | 1391 " endtime DATE,\n" |
1358 " script TEXT\n" | 1392 " script TEXT\n" |
1359 ");" | 1393 ");" |
1360 "CREATE INDEX task_i1 ON task(client, starttime);\n" | 1394 "CREATE INDEX task_i1 ON task(client, starttime);\n" |
1361 "CREATE INDEX task_i2 ON task(client, endtime);\n" | 1395 "CREATE INDEX task_i2 ON task(client, endtime);\n" |
1362 "CREATE TABLE counters(nError,nTest);\n" | 1396 "CREATE TABLE counters(nError,nTest);\n" |
1363 "INSERT INTO counters VALUES(0,0);\n" | 1397 "INSERT INTO counters VALUES(0,0);\n" |
1364 "CREATE TABLE client(id INTEGER PRIMARY KEY, wantHalt);\n" | 1398 "CREATE TABLE client(id INTEGER PRIMARY KEY, wantHalt);\n" |
1365 ); | 1399 ); |
1366 zScript = readFile(argv[2]); | 1400 zScript = readFile(argv[2]); |
1367 if( g.iTrace ) logMessage("begin script [%s]\n", argv[2]); | 1401 for(iRep=1; iRep<=nRep; iRep++){ |
1368 runScript(0, 0, zScript, argv[2]); | 1402 if( g.iTrace ) logMessage("begin script [%s] cycle %d\n", argv[2], iRep); |
| 1403 runScript(0, 0, zScript, argv[2]); |
| 1404 if( g.iTrace ) logMessage("end script [%s] cycle %d\n", argv[2], iRep); |
| 1405 } |
1369 sqlite3_free(zScript); | 1406 sqlite3_free(zScript); |
1370 if( g.iTrace ) logMessage("end script [%s]\n", argv[2]); | |
1371 waitForClient(0, 2000, "during shutdown...\n"); | 1407 waitForClient(0, 2000, "during shutdown...\n"); |
1372 trySql("UPDATE client SET wantHalt=1"); | 1408 trySql("UPDATE client SET wantHalt=1"); |
1373 sqlite3_sleep(10); | 1409 sqlite3_sleep(10); |
1374 g.iTimeout = 0; | 1410 g.iTimeout = 0; |
1375 iTimeout = 1000; | 1411 iTimeout = 1000; |
1376 while( ((rc = trySql("SELECT 1 FROM client"))==SQLITE_BUSY | 1412 while( ((rc = trySql("SELECT 1 FROM client"))==SQLITE_BUSY |
1377 || rc==SQLITE_ROW) && iTimeout>0 ){ | 1413 || rc==SQLITE_ROW) && iTimeout>0 ){ |
1378 sqlite3_sleep(10); | 1414 sqlite3_sleep(10); |
1379 iTimeout -= 10; | 1415 iTimeout -= 10; |
1380 } | 1416 } |
1381 sqlite3_sleep(100); | 1417 sqlite3_sleep(100); |
1382 pStmt = prepareSql("SELECT nError, nTest FROM counters"); | 1418 pStmt = prepareSql("SELECT nError, nTest FROM counters"); |
1383 iTimeout = 1000; | 1419 iTimeout = 1000; |
1384 while( (rc = sqlite3_step(pStmt))==SQLITE_BUSY && iTimeout>0 ){ | 1420 while( (rc = sqlite3_step(pStmt))==SQLITE_BUSY && iTimeout>0 ){ |
1385 sqlite3_sleep(10); | 1421 sqlite3_sleep(10); |
1386 iTimeout -= 10; | 1422 iTimeout -= 10; |
1387 } | 1423 } |
1388 if( rc==SQLITE_ROW ){ | 1424 if( rc==SQLITE_ROW ){ |
1389 g.nError += sqlite3_column_int(pStmt, 0); | 1425 g.nError += sqlite3_column_int(pStmt, 0); |
1390 g.nTest += sqlite3_column_int(pStmt, 1); | 1426 g.nTest += sqlite3_column_int(pStmt, 1); |
1391 } | 1427 } |
1392 sqlite3_finalize(pStmt); | 1428 sqlite3_finalize(pStmt); |
1393 } | 1429 } |
1394 sqlite3_close(g.db); | 1430 sqlite3_close(g.db); |
1395 maybeClose(g.pLog); | 1431 maybeClose(g.pLog); |
1396 maybeClose(g.pErrLog); | 1432 maybeClose(g.pErrLog); |
1397 if( iClient==0 ){ | 1433 if( iClient==0 ){ |
1398 printf("Summary: %d errors in %d tests\n", g.nError, g.nTest); | 1434 printf("Summary: %d errors out of %d tests\n", g.nError, g.nTest); |
| 1435 printf("END: %s", argv[0]); |
| 1436 for(i=1; i<argc; i++) printf(" %s", argv[i]); |
| 1437 printf("\n"); |
1399 } | 1438 } |
1400 return g.nError>0; | 1439 return g.nError>0; |
1401 } | 1440 } |
OLD | NEW |