blob: 9b9b176d68132457fc5ab705320e8b559e097bc9 [file] [log] [blame]
Theodore Ts'o44339bd1997-10-15 02:47:20 +00001/*
2 * subst.c --- substitution program
3 *
4 * Subst is used as a quicky program to do @ substitutions
5 *
6 */
7
8#include <stdio.h>
9#include <errno.h>
10#include <stdlib.h>
11#include <unistd.h>
12#include <string.h>
13#include <ctype.h>
14
15#ifdef HAVE_GETOPT_H
16#include <getopt.h>
17#endif
18
19
20struct subst_entry {
21 char *name;
22 char *value;
23 struct subst_entry *next;
24};
25
26struct subst_entry *subst_table = 0;
27
28static int add_subst(char *name, char *value)
29{
30 struct subst_entry *ent = 0;
31 int retval;
32
33 retval = ENOMEM;
Theodore Ts'o45d21611998-01-19 14:40:24 +000034 ent = (struct subst_entry *) malloc(sizeof(struct subst_entry));
Theodore Ts'o44339bd1997-10-15 02:47:20 +000035 if (!ent)
36 goto fail;
Theodore Ts'o45d21611998-01-19 14:40:24 +000037 ent->name = (char *) malloc(strlen(name)+1);
Theodore Ts'o44339bd1997-10-15 02:47:20 +000038 if (!ent->name)
39 goto fail;
Theodore Ts'o45d21611998-01-19 14:40:24 +000040 ent->value = (char *) malloc(strlen(value)+1);
Theodore Ts'o44339bd1997-10-15 02:47:20 +000041 if (!ent->value)
42 goto fail;
43 strcpy(ent->name, name);
44 strcpy(ent->value, value);
45 ent->next = subst_table;
46 subst_table = ent;
47 return 0;
48fail:
49 if (ent) {
50 if (ent->name)
51 free(ent->name);
52 if (ent->value)
53 free(ent->value);
54 free(ent);
55 }
56 return retval;
57}
58
59static struct subst_entry *fetch_subst_entry(char *name)
60{
61 struct subst_entry *ent;
62
63 for (ent = subst_table; ent; ent = ent->next) {
64 if (strcmp(name, ent->name) == 0)
65 break;
66 }
67 return ent;
68}
69
Theodore Ts'oe7549ca1998-08-01 04:35:39 +000070/*
71 * Given the starting and ending position of the replacement name,
72 * check to see if it is valid, and pull it out if it is.
73 */
74static char *get_subst_symbol(const char *begin, int len, char prefix)
75{
76 static char replace_name[128];
77 char *cp, *start;
78
79 start = replace_name;
80 if (prefix)
81 *start++ = prefix;
82
83 if (len > sizeof(replace_name)-2)
84 return NULL;
85 memcpy(start, begin, len);
86 start[len] = 0;
87
88 /*
89 * The substitution variable must all be in the of [A-Za-z_].
90 * If it isn't, this must be an invalid symbol name.
91 */
92 for (cp = start; *cp; cp++) {
93 if (!(*cp >= 'a' && *cp <= 'z') &&
94 !(*cp >= 'A' && *cp <= 'Z') &&
95 !(*cp == '_'))
96 return NULL;
97 }
98 return (replace_name);
99}
100
101static void replace_string(char *begin, char *end, char *newstr)
102{
103 int replace_len, len;
104
105 replace_len = strlen(newstr);
106 len = end - begin;
107 if (replace_len != len+1)
108 memmove(end+(replace_len-len-1), end,
109 strlen(end)+1);
110 memcpy(begin, newstr, replace_len);
111}
112
Theodore Ts'o44339bd1997-10-15 02:47:20 +0000113static void substitute_line(char *line)
114{
Theodore Ts'oe7549ca1998-08-01 04:35:39 +0000115 char *ptr, *name_ptr, *end_ptr, *cp, ch;
Theodore Ts'o44339bd1997-10-15 02:47:20 +0000116 struct subst_entry *ent;
Theodore Ts'oe7549ca1998-08-01 04:35:39 +0000117 char *replace_name;
Theodore Ts'o44339bd1997-10-15 02:47:20 +0000118 int len, replace_len;
119
Theodore Ts'oe7549ca1998-08-01 04:35:39 +0000120 /*
121 * Expand all @FOO@ substitutions
122 */
Theodore Ts'o44339bd1997-10-15 02:47:20 +0000123 ptr = line;
124 while (ptr) {
125 name_ptr = strchr(ptr, '@');
126 if (!name_ptr)
Theodore Ts'oe7549ca1998-08-01 04:35:39 +0000127 break; /* No more */
128 if (*(++name_ptr) == '@') {
129 /*
130 * Handle tytso@@mit.edu --> tytso@mit.edu
131 */
132 memmove(name_ptr-1, name_ptr, strlen(name_ptr)+1);
133 ptr = name_ptr+1;
134 continue;
135 }
136 end_ptr = strchr(name_ptr, '@');
Theodore Ts'o44339bd1997-10-15 02:47:20 +0000137 if (!end_ptr)
138 break;
Theodore Ts'oe7549ca1998-08-01 04:35:39 +0000139 len = end_ptr - name_ptr;
140 replace_name = get_subst_symbol(name_ptr, len, 0);
141 if (!replace_name) {
142 ptr = name_ptr;
143 continue;
144 }
Theodore Ts'o44339bd1997-10-15 02:47:20 +0000145 ent = fetch_subst_entry(replace_name);
146 if (!ent) {
147 fprintf(stderr, "Unfound expansion: '%s'\n",
148 replace_name);
149 ptr = end_ptr + 1;
150 continue;
151 }
152#if 0
153 fprintf(stderr, "Replace name = '%s' with '%s'\n",
154 replace_name, ent->value);
155#endif
Theodore Ts'oe7549ca1998-08-01 04:35:39 +0000156 ptr = name_ptr-1;
157 replace_string(ptr, end_ptr, ent->value);
158 }
159 /*
160 * Now do a second pass to expand ${FOO}
161 */
162 ptr = line;
163 while (ptr) {
164 name_ptr = strchr(ptr, '$');
165 if (!name_ptr)
166 break; /* No more */
167 if (*(++name_ptr) != '{') {
168 ptr = name_ptr;
169 continue;
170 }
171 name_ptr++;
172 end_ptr = strchr(name_ptr, '}');
173 if (!end_ptr)
174 break;
175 len = end_ptr - name_ptr;
176 replace_name = get_subst_symbol(name_ptr, len, '$');
177 if (!replace_name) {
178 ptr = name_ptr;
179 continue;
180 }
181 ent = fetch_subst_entry(replace_name);
182 if (!ent) {
183 ptr = end_ptr + 1;
184 continue;
185 }
186#if 0
187 fprintf(stderr, "Replace name = '%s' with '%s'\n",
188 replace_name, ent->value);
189#endif
190 ptr = name_ptr-2;
191 replace_string(ptr, end_ptr, ent->value);
Theodore Ts'o44339bd1997-10-15 02:47:20 +0000192 }
193}
194
195static void parse_config_file(FILE *f)
196{
197 char line[2048];
198 char *cp, *ptr;
199
200 while (!feof(f)) {
201 memset(line, 0, sizeof(line));
202 if (fgets(line, sizeof(line), f) == NULL)
203 break;
204 /*
205 * Strip newlines and comments.
206 */
207 cp = strchr(line, '\n');
208 if (cp)
209 *cp = 0;
210 cp = strchr(line, '#');
211 if (cp)
212 *cp = 0;
213 /*
214 * Skip trailing and leading whitespace
215 */
216 for (cp = line + strlen(line) - 1; cp >= line; cp--) {
217 if (*cp == ' ' || *cp == '\t')
218 *cp = 0;
219 else
220 break;
221 }
222 cp = line;
223 while (*cp && isspace(*cp))
224 cp++;
225 ptr = cp;
226 /*
227 * Skip empty lines
228 */
229 if (*ptr == 0)
230 continue;
231 /*
232 * Ignore future extensions
233 */
Theodore Ts'oe7549ca1998-08-01 04:35:39 +0000234 if (*ptr == '@')
Theodore Ts'o44339bd1997-10-15 02:47:20 +0000235 continue;
236 /*
237 * Parse substitutions
238 */
239 for (cp = ptr; *cp; cp++)
240 if (isspace(*cp))
241 break;
242 *cp = 0;
243 for (cp++; *cp; cp++)
244 if (!isspace(*cp))
245 break;
246#if 0
247 printf("Substitute: '%s' for '%s'\n", ptr, cp);
248#endif
249 add_subst(ptr, cp);
250 }
251}
252
253/*
254 * Return 0 if the files are different, 1 if the files are the same.
255 */
256static int compare_file(const char *outfn, const char *newfn)
257{
Theodore Ts'o45d21611998-01-19 14:40:24 +0000258 FILE *old_f, *new_f;
Theodore Ts'o44339bd1997-10-15 02:47:20 +0000259 char oldbuf[2048], newbuf[2048], *oldcp, *newcp;
260 int retval;
261
Theodore Ts'o45d21611998-01-19 14:40:24 +0000262 old_f = fopen(outfn, "r");
263 if (!old_f)
Theodore Ts'o44339bd1997-10-15 02:47:20 +0000264 return 0;
Theodore Ts'o45d21611998-01-19 14:40:24 +0000265 new_f = fopen(newfn, "r");
266 if (!new_f)
Theodore Ts'o44339bd1997-10-15 02:47:20 +0000267 return 0;
268
269 while (1) {
Theodore Ts'o45d21611998-01-19 14:40:24 +0000270 oldcp = fgets(oldbuf, sizeof(oldbuf), old_f);
271 newcp = fgets(newbuf, sizeof(newbuf), new_f);
Theodore Ts'o44339bd1997-10-15 02:47:20 +0000272 if (!oldcp && !newcp) {
273 retval = 1;
274 break;
275 }
276 if (!oldcp || !newcp || strcmp(oldbuf, newbuf)) {
277 retval = 0;
278 break;
279 }
280 }
Theodore Ts'o45d21611998-01-19 14:40:24 +0000281 fclose(old_f);
282 fclose(new_f);
Theodore Ts'o44339bd1997-10-15 02:47:20 +0000283 return retval;
284}
285
286
287
288
289int main(int argc, char **argv)
290{
291 char line[2048];
292 int c;
293 FILE *in, *out;
294 char *outfn = NULL, *newfn = NULL;
295 int verbose = 0;
296
297 while ((c = getopt (argc, argv, "f:v")) != EOF) {
298 switch (c) {
299 case 'f':
300 in = fopen(optarg, "r");
301 if (!in) {
302 perror(optarg);
303 exit(1);
304 }
305 parse_config_file(in);
306 fclose(in);
307 break;
308 case 'v':
309 verbose++;
310 break;
311 default:
312 fprintf(stderr, "%s: [-f config-file] [file]\n",
313 argv[0]);
314 break;
315 }
316 }
317 if (optind < argc) {
318 in = fopen(argv[optind], "r");
319 if (!in) {
320 perror(argv[optind]);
321 exit(1);
322 }
323 optind++;
324 } else
325 in = stdin;
326
327 if (optind < argc) {
328 outfn = argv[optind];
Theodore Ts'o45d21611998-01-19 14:40:24 +0000329 newfn = (char *) malloc(strlen(outfn)+20);
Theodore Ts'o44339bd1997-10-15 02:47:20 +0000330 if (!newfn) {
331 fprintf(stderr, "Memory error! Exiting.\n");
332 exit(1);
333 }
334 strcpy(newfn, outfn);
335 strcat(newfn, ".new");
336 out = fopen(newfn, "w");
337 if (!out) {
338 perror(newfn);
339 exit(1);
340 }
341 } else {
342 out = stdout;
343 outfn = 0;
344 }
345
346 while (!feof(in)) {
347 if (fgets(line, sizeof(line), in) == NULL)
348 break;
349 substitute_line(line);
350 fputs(line, out);
351 }
352 fclose(in);
353 fclose(out);
354 if (outfn) {
355 if (compare_file(outfn, newfn)) {
356 if (verbose)
357 printf("No change, keeping %s.\n", outfn);
358 unlink(newfn);
359 } else {
360 if (verbose)
361 printf("Creating or replacing %s.\n", outfn);
362 rename(newfn, outfn);
363 }
364 }
365 return (0);
366}
367
368