1 #include <stdio.h>
2 #include "lesstest.h"
3
4 extern int verbose;
5
6 // Return the interior string of a quoted string.
parse_qstring(const char ** s)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
parse_int(const char ** s)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.
parse_env(TestSetup * setup,const char * line,int line_len)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
parse_command(TestSetup * setup,const char * less,const char * line,int line_len)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
parse_textfile(TestSetup * setup,const char * line,int line_len,FILE * fd)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
new_test_setup(void)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
free_test_setup(TestSetup * setup)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.
read_zline(FILE * fd,char * line,int line_len)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).
read_test_setup(FILE * fd,const char * less)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