xref: /netbsd-src/external/bsd/less/dist/lesstest/parse.c (revision e4a6e799a67c2028562d75b4e61407b22434aa36)
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