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