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