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