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