blob: 588a4e35e3cd62df1cba23899e1d84c4036001cf [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
70static void substitute_line(char *line)
71{
72 char *ptr, *name_ptr, *end_ptr;
73 struct subst_entry *ent;
74 char replace_name[128];
75 int len, replace_len;
76
77 ptr = line;
78 while (ptr) {
79 name_ptr = strchr(ptr, '@');
80 if (!name_ptr)
81 break;
82 end_ptr = strchr(name_ptr+1, '@');
83 if (!end_ptr)
84 break;
85 len = end_ptr - name_ptr - 1;
86 memcpy(replace_name, name_ptr+1, len);
87 replace_name[len] = 0;
88 ent = fetch_subst_entry(replace_name);
89 if (!ent) {
90 fprintf(stderr, "Unfound expansion: '%s'\n",
91 replace_name);
92 ptr = end_ptr + 1;
93 continue;
94 }
95#if 0
96 fprintf(stderr, "Replace name = '%s' with '%s'\n",
97 replace_name, ent->value);
98#endif
99 replace_len = strlen(ent->value);
100 if (replace_len != len+2)
101 memmove(end_ptr+(replace_len-len-2), end_ptr,
102 strlen(end_ptr)+1);
103 memcpy(name_ptr, ent->value, replace_len);
104 ptr = name_ptr;
105 }
106}
107
108static void parse_config_file(FILE *f)
109{
110 char line[2048];
111 char *cp, *ptr;
112
113 while (!feof(f)) {
114 memset(line, 0, sizeof(line));
115 if (fgets(line, sizeof(line), f) == NULL)
116 break;
117 /*
118 * Strip newlines and comments.
119 */
120 cp = strchr(line, '\n');
121 if (cp)
122 *cp = 0;
123 cp = strchr(line, '#');
124 if (cp)
125 *cp = 0;
126 /*
127 * Skip trailing and leading whitespace
128 */
129 for (cp = line + strlen(line) - 1; cp >= line; cp--) {
130 if (*cp == ' ' || *cp == '\t')
131 *cp = 0;
132 else
133 break;
134 }
135 cp = line;
136 while (*cp && isspace(*cp))
137 cp++;
138 ptr = cp;
139 /*
140 * Skip empty lines
141 */
142 if (*ptr == 0)
143 continue;
144 /*
145 * Ignore future extensions
146 */
147 if (*ptr == '$')
148 continue;
149 /*
150 * Parse substitutions
151 */
152 for (cp = ptr; *cp; cp++)
153 if (isspace(*cp))
154 break;
155 *cp = 0;
156 for (cp++; *cp; cp++)
157 if (!isspace(*cp))
158 break;
159#if 0
160 printf("Substitute: '%s' for '%s'\n", ptr, cp);
161#endif
162 add_subst(ptr, cp);
163 }
164}
165
166/*
167 * Return 0 if the files are different, 1 if the files are the same.
168 */
169static int compare_file(const char *outfn, const char *newfn)
170{
Theodore Ts'o45d21611998-01-19 14:40:24 +0000171 FILE *old_f, *new_f;
Theodore Ts'o44339bd1997-10-15 02:47:20 +0000172 char oldbuf[2048], newbuf[2048], *oldcp, *newcp;
173 int retval;
174
Theodore Ts'o45d21611998-01-19 14:40:24 +0000175 old_f = fopen(outfn, "r");
176 if (!old_f)
Theodore Ts'o44339bd1997-10-15 02:47:20 +0000177 return 0;
Theodore Ts'o45d21611998-01-19 14:40:24 +0000178 new_f = fopen(newfn, "r");
179 if (!new_f)
Theodore Ts'o44339bd1997-10-15 02:47:20 +0000180 return 0;
181
182 while (1) {
Theodore Ts'o45d21611998-01-19 14:40:24 +0000183 oldcp = fgets(oldbuf, sizeof(oldbuf), old_f);
184 newcp = fgets(newbuf, sizeof(newbuf), new_f);
Theodore Ts'o44339bd1997-10-15 02:47:20 +0000185 if (!oldcp && !newcp) {
186 retval = 1;
187 break;
188 }
189 if (!oldcp || !newcp || strcmp(oldbuf, newbuf)) {
190 retval = 0;
191 break;
192 }
193 }
Theodore Ts'o45d21611998-01-19 14:40:24 +0000194 fclose(old_f);
195 fclose(new_f);
Theodore Ts'o44339bd1997-10-15 02:47:20 +0000196 return retval;
197}
198
199
200
201
202int main(int argc, char **argv)
203{
204 char line[2048];
205 int c;
206 FILE *in, *out;
207 char *outfn = NULL, *newfn = NULL;
208 int verbose = 0;
209
210 while ((c = getopt (argc, argv, "f:v")) != EOF) {
211 switch (c) {
212 case 'f':
213 in = fopen(optarg, "r");
214 if (!in) {
215 perror(optarg);
216 exit(1);
217 }
218 parse_config_file(in);
219 fclose(in);
220 break;
221 case 'v':
222 verbose++;
223 break;
224 default:
225 fprintf(stderr, "%s: [-f config-file] [file]\n",
226 argv[0]);
227 break;
228 }
229 }
230 if (optind < argc) {
231 in = fopen(argv[optind], "r");
232 if (!in) {
233 perror(argv[optind]);
234 exit(1);
235 }
236 optind++;
237 } else
238 in = stdin;
239
240 if (optind < argc) {
241 outfn = argv[optind];
Theodore Ts'o45d21611998-01-19 14:40:24 +0000242 newfn = (char *) malloc(strlen(outfn)+20);
Theodore Ts'o44339bd1997-10-15 02:47:20 +0000243 if (!newfn) {
244 fprintf(stderr, "Memory error! Exiting.\n");
245 exit(1);
246 }
247 strcpy(newfn, outfn);
248 strcat(newfn, ".new");
249 out = fopen(newfn, "w");
250 if (!out) {
251 perror(newfn);
252 exit(1);
253 }
254 } else {
255 out = stdout;
256 outfn = 0;
257 }
258
259 while (!feof(in)) {
260 if (fgets(line, sizeof(line), in) == NULL)
261 break;
262 substitute_line(line);
263 fputs(line, out);
264 }
265 fclose(in);
266 fclose(out);
267 if (outfn) {
268 if (compare_file(outfn, newfn)) {
269 if (verbose)
270 printf("No change, keeping %s.\n", outfn);
271 unlink(newfn);
272 } else {
273 if (verbose)
274 printf("Creating or replacing %s.\n", outfn);
275 rename(newfn, outfn);
276 }
277 }
278 return (0);
279}
280
281