1 #include <stdio.h> 2 #include "lesstest.h" 3 4 extern int verbose; 5 6 // Return the interior string of a quoted string. 7 static char* parse_qstring(const char** s) { 8 while (*(*s) == ' ') ++(*s); 9 if (*(*s)++ != '"') return NULL; 10 const char* start = *s; 11 while (*(*s) != '"' && *(*s) != '\0') ++(*s); 12 char* ret = strndup(start, (*s)-start); 13 if (*(*s) == '"') ++(*s); 14 return ret; 15 } 16 17 static int parse_int(const char** s) { 18 return (int) strtol(*s, (char**)s, 0); 19 } 20 21 // Parse a quoted name and value, 22 // and add them as env vars to the Setup environment. 23 static int parse_env(TestSetup* setup, const char* line, int line_len) { 24 char* name = parse_qstring(&line); 25 char* value = parse_qstring(&line); 26 env_addpair(&setup->env, name, value); 27 free(name); 28 free(value); 29 return 1; 30 } 31 32 static int parse_command(TestSetup* setup, const char* less, const char* line, int line_len) { 33 setup->argv = (char**) malloc(32*sizeof(const char*)); 34 setup->argc = 1; 35 setup->argv[0] = (char*) less; 36 for (;;) { 37 const char* arg = parse_qstring(&line); 38 setup->argv[setup->argc] = (char*) arg; 39 if (arg == NULL) break; 40 setup->argc++; 41 } 42 return 1; 43 } 44 45 static int parse_textfile(TestSetup* setup, const char* line, int line_len, FILE* fd) { 46 const char* filename = parse_qstring(&line); 47 if (access(filename, F_OK) == 0) { 48 fprintf(stderr, "%s already exists\n", filename); 49 return 0; 50 } 51 int fsize = parse_int(&line); 52 int len = strlen(filename)+1; 53 setup->textfile = malloc(len); 54 strcpy(setup->textfile, filename); 55 FILE* textfd = fopen(setup->textfile, "w"); 56 if (textfd == NULL) { 57 fprintf(stderr, "cannot create %s\n", setup->textfile); 58 return 0; 59 } 60 int nread = 0; 61 while (nread < fsize) { 62 char buf[4096]; 63 int chunk = fsize - nread; 64 if (chunk > sizeof(buf)) chunk = sizeof(buf); 65 size_t len = fread(buf, 1, chunk, fd); 66 fwrite(buf, 1, len, textfd); 67 nread += len; 68 } 69 fclose(textfd); 70 return 1; 71 } 72 73 static TestSetup* new_test_setup(void) { 74 TestSetup* setup = (TestSetup*) malloc(sizeof(TestSetup)); 75 setup->textfile = NULL; 76 setup->argv = NULL; 77 setup->argc = 0; 78 env_init(&setup->env); 79 return setup; 80 } 81 82 void free_test_setup(TestSetup* setup) { 83 if (setup->textfile != NULL) { 84 unlink(setup->textfile); 85 free(setup->textfile); 86 } 87 int i; 88 for (i = 1; i < setup->argc; ++i) 89 free(setup->argv[i]); 90 free((void*)setup->argv); 91 free(setup); 92 } 93 94 // Read a newline-terminated line from a file and store it 95 // as a null-terminated string without the newline. 96 int read_zline(FILE* fd, char* line, int line_len) { 97 int nread = 0; 98 while (nread < line_len-1) { 99 int ch = fgetc(fd); 100 if (ch == EOF) return -1; 101 if (ch == '\n') break; 102 line[nread++] = (char) ch; 103 } 104 line[nread] = '\0'; 105 return nread; 106 } 107 108 // Read the header of a .lt file (up to the R line). 109 TestSetup* read_test_setup(FILE* fd, const char* less) { 110 TestSetup* setup = new_test_setup(); 111 int hdr_complete = 0; 112 while (!hdr_complete) { 113 char line[10000]; 114 int line_len = read_zline(fd, line, sizeof(line)); 115 if (line_len < 0) 116 break; 117 if (line_len < 1) 118 continue; 119 switch (line[0]) { 120 case '!': // file header 121 break; 122 case 'T': // test header 123 break; 124 case 'R': // end of test header; start run 125 hdr_complete = 1; 126 break; 127 case 'E': // environment variable 128 if (!parse_env(setup, line+1, line_len-1)) { 129 free_test_setup(setup); 130 return NULL; 131 } 132 break; 133 case 'F': // text file 134 if (!parse_textfile(setup, line+1, line_len-1, fd)) { 135 free_test_setup(setup); 136 return NULL; 137 } 138 break; 139 case 'A': // less cmd line parameters 140 if (!parse_command(setup, less, line+1, line_len-1)) { 141 free_test_setup(setup); 142 return NULL; 143 } 144 break; 145 default: 146 break; 147 } 148 } 149 if (setup->textfile == NULL || setup->argv == NULL) { 150 free_test_setup(setup); 151 return NULL; 152 } 153 if (verbose) { fprintf(stderr, "setup: textfile %s\n", setup->textfile); print_strings("argv:", setup->argv); } 154 return setup; 155 } 156