]> git.wh0rd.org - fontconfig.git/blob - doc/edit-sgml.c
77cc807c1d0f16791212de7d384b7a2c0d6431f2
[fontconfig.git] / doc / edit-sgml.c
1 /*
2 * $Id$
3 *
4 * Copyright © 2003 Keith Packard
5 *
6 * Permission to use, copy, modify, distribute, and sell this software and its
7 * documentation for any purpose is hereby granted without fee, provided that
8 * the above copyright notice appear in all copies and that both that
9 * copyright notice and this permission notice appear in supporting
10 * documentation, and that the name of Keith Packard not be used in
11 * advertising or publicity pertaining to distribution of the software without
12 * specific, written prior permission. Keith Packard makes no
13 * representations about the suitability of this software for any purpose. It
14 * is provided "as is" without express or implied warranty.
15 *
16 * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18 * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
19 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
20 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
21 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
22 * PERFORMANCE OF THIS SOFTWARE.
23 */
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <ctype.h>
29
30 static void *
31 New (int size);
32
33 static void *
34 Reallocate (void *p, int size);
35
36 static void
37 Dispose (void *p);
38
39 typedef enum { False, True } Bool;
40
41 typedef struct {
42 char *buf;
43 int size;
44 int len;
45 } String;
46
47 static String *
48 StringNew (void);
49
50 static void
51 StringAdd (String *s, char c);
52
53 static void
54 StringAddString (String *s, char *buf);
55
56 static String *
57 StringMake (char *buf);
58
59 static void
60 StringDel (String *s);
61
62 static void
63 StringPut (FILE *f, String *s);
64
65 static void
66 StringDispose (String *s);
67
68 typedef struct {
69 String *tag;
70 String *text;
71 } Replace;
72
73 static Replace *
74 ReplaceNew (void);
75
76 static void
77 ReplaceDispose (Replace *r);
78
79 static void
80 Bail (const char *format, const char *arg);
81
82 static Replace *
83 ReplaceRead (FILE *f);
84
85 typedef struct _replaceList {
86 struct _replaceList *next;
87 Replace *r;
88 } ReplaceList;
89
90 static ReplaceList *
91 ReplaceListNew (Replace *r, ReplaceList *next);
92
93 static void
94 ReplaceListDispose (ReplaceList *l);
95
96 typedef struct {
97 ReplaceList *head;
98 } ReplaceSet;
99
100 static ReplaceSet *
101 ReplaceSetNew (void);
102
103 static void
104 ReplaceSetDispose (ReplaceSet *s);
105
106 static void
107 ReplaceSetAdd (ReplaceSet *s, Replace *r);
108
109 static Replace *
110 ReplaceSetFind (ReplaceSet *s, char *tag);
111
112 static ReplaceSet *
113 ReplaceSetRead (FILE *f);
114
115 typedef struct _skipStack {
116 struct _skipStack *prev;
117 int skipping;
118 } SkipStack;
119
120 static SkipStack *
121 SkipStackPush (SkipStack *prev, int skipping);
122
123 static SkipStack *
124 SkipStackPop (SkipStack *prev);
125
126 typedef struct _loopStack {
127 struct _loopStack *prev;
128 String *tag;
129 String *extra;
130 long pos;
131 } LoopStack;
132
133 static LoopStack *
134 LoopStackPush (LoopStack *prev, FILE *f, char *tag);
135
136 static LoopStack *
137 LoopStackLoop (ReplaceSet *rs, LoopStack *ls, FILE *f);
138
139 static void
140 LineSkip (FILE *f);
141
142 static void
143 DoReplace (FILE *f, ReplaceSet *s);
144
145 #define STRING_INIT 128
146
147 static void *
148 New (int size)
149 {
150 void *m = malloc (size);
151 if (!m)
152 abort ();
153 return m;
154 }
155
156 static void *
157 Reallocate (void *p, int size)
158 {
159 void *r = realloc (p, size);
160
161 if (!r)
162 abort ();
163 return r;
164 }
165
166 static void
167 Dispose (void *p)
168 {
169 free (p);
170 }
171
172 static String *
173 StringNew (void)
174 {
175 String *s;
176
177 s = New (sizeof (String));
178 s->buf = New (STRING_INIT);
179 s->size = STRING_INIT - 1;
180 s->buf[0] = '\0';
181 s->len = 0;
182 return s;
183 }
184
185 static void
186 StringAdd (String *s, char c)
187 {
188 if (s->len == s->size)
189 s->buf = Reallocate (s->buf, (s->size *= 2) + 1);
190 s->buf[s->len++] = c;
191 s->buf[s->len] = '\0';
192 }
193
194 static void
195 StringAddString (String *s, char *buf)
196 {
197 while (*buf)
198 StringAdd (s, *buf++);
199 }
200
201 static String *
202 StringMake (char *buf)
203 {
204 String *s = StringNew ();
205 StringAddString (s, buf);
206 return s;
207 }
208
209 static void
210 StringDel (String *s)
211 {
212 if (s->len)
213 s->buf[--s->len] = '\0';
214 }
215
216 static void
217 StringPut (FILE *f, String *s)
218 {
219 char *b = s->buf;
220
221 while (*b)
222 putc (*b++, f);
223 }
224
225 #define StringLast(s) ((s)->len ? (s)->buf[(s)->len - 1] : '\0')
226
227 static void
228 StringDispose (String *s)
229 {
230 Dispose (s->buf);
231 Dispose (s);
232 }
233
234 static Replace *
235 ReplaceNew (void)
236 {
237 Replace *r = New (sizeof (Replace));
238 r->tag = StringNew ();
239 r->text = StringNew ();
240 return r;
241 }
242
243 static void
244 ReplaceDispose (Replace *r)
245 {
246 StringDispose (r->tag);
247 StringDispose (r->text);
248 Dispose (r);
249 }
250
251 static void
252 Bail (const char *format, const char *arg)
253 {
254 fprintf (stderr, "fatal: ");
255 fprintf (stderr, format, arg);
256 fprintf (stderr, "\n");
257 exit (1);
258 }
259
260 static Replace *
261 ReplaceRead (FILE *f)
262 {
263 int c;
264 Replace *r;
265
266 while ((c = getc (f)) != '@')
267 {
268 if (c == EOF)
269 return 0;
270 }
271 r = ReplaceNew();
272 while ((c = getc (f)) != '@')
273 {
274 if (c == EOF)
275 {
276 ReplaceDispose (r);
277 return 0;
278 }
279 if (isspace (c))
280 Bail ("invalid character after tag %s", r->tag->buf);
281 StringAdd (r->tag, c);
282 }
283 if (r->tag->buf[0] == '\0')
284 {
285 ReplaceDispose (r);
286 return 0;
287 }
288 while (isspace ((c = getc (f))))
289 ;
290 ungetc (c, f);
291 while ((c = getc (f)) != '@' && c != EOF)
292 StringAdd (r->text, c);
293 if (c == '@')
294 ungetc (c, f);
295 while (isspace (StringLast (r->text)))
296 StringDel (r->text);
297 return r;
298 }
299
300 static ReplaceList *
301 ReplaceListNew (Replace *r, ReplaceList *next)
302 {
303 ReplaceList *l = New (sizeof (ReplaceList));
304 l->r = r;
305 l->next = next;
306 return l;
307 }
308
309 static void
310 ReplaceListDispose (ReplaceList *l)
311 {
312 if (l)
313 {
314 ReplaceListDispose (l->next);
315 ReplaceDispose (l->r);
316 Dispose (l);
317 }
318 }
319
320 static ReplaceSet *
321 ReplaceSetNew (void)
322 {
323 ReplaceSet *s = New (sizeof (ReplaceSet));
324 s->head = 0;
325 return s;
326 }
327
328 static void
329 ReplaceSetDispose (ReplaceSet *s)
330 {
331 ReplaceListDispose (s->head);
332 Dispose (s);
333 }
334
335 static void
336 ReplaceSetAdd (ReplaceSet *s, Replace *r)
337 {
338 s->head = ReplaceListNew (r, s->head);
339 }
340
341 static Replace *
342 ReplaceSetFind (ReplaceSet *s, char *tag)
343 {
344 ReplaceList *l;
345
346 for (l = s->head; l; l = l->next)
347 if (!strcmp (tag, l->r->tag->buf))
348 return l->r;
349 return 0;
350 }
351
352 static ReplaceSet *
353 ReplaceSetRead (FILE *f)
354 {
355 ReplaceSet *s = ReplaceSetNew ();
356 Replace *r;
357
358 while ((r = ReplaceRead (f)))
359 {
360 while (ReplaceSetFind (s, r->tag->buf))
361 StringAdd (r->tag, '+');
362 ReplaceSetAdd (s, r);
363 }
364 if (!s->head)
365 {
366 ReplaceSetDispose (s);
367 s = 0;
368 }
369 return s;
370 }
371
372 static SkipStack *
373 SkipStackPush (SkipStack *prev, int skipping)
374 {
375 SkipStack *ss = New (sizeof (SkipStack));
376 ss->prev = prev;
377 ss->skipping = skipping;
378 return ss;
379 }
380
381 static SkipStack *
382 SkipStackPop (SkipStack *prev)
383 {
384 SkipStack *ss = prev->prev;
385 Dispose (prev);
386 return ss;
387 }
388
389 static LoopStack *
390 LoopStackPush (LoopStack *prev, FILE *f, char *tag)
391 {
392 LoopStack *ls = New (sizeof (LoopStack));
393 ls->prev = prev;
394 ls->tag = StringMake (tag);
395 ls->extra = StringNew ();
396 ls->pos = ftell (f);
397 return ls;
398 }
399
400 static LoopStack *
401 LoopStackLoop (ReplaceSet *rs, LoopStack *ls, FILE *f)
402 {
403 String *s = StringMake (ls->tag->buf);
404 LoopStack *ret = ls;
405 Bool loop;
406
407 StringAdd (ls->extra, '+');
408 StringAddString (s, ls->extra->buf);
409 loop = ReplaceSetFind (rs, s->buf) != 0;
410 StringDispose (s);
411 if (loop)
412 fseek (f, ls->pos, SEEK_SET);
413 else
414 {
415 ret = ls->prev;
416 StringDispose (ls->tag);
417 StringDispose (ls->extra);
418 Dispose (ls);
419 }
420 return ret;
421 }
422
423 static void
424 LineSkip (FILE *f)
425 {
426 int c;
427
428 while ((c = getc (f)) == '\n')
429 ;
430 ungetc (c, f);
431 }
432
433 static void
434 DoReplace (FILE *f, ReplaceSet *s)
435 {
436 int c;
437 String *tag;
438 Replace *r;
439 SkipStack *ss = 0;
440 LoopStack *ls = 0;
441 int skipping = 0;
442
443 while ((c = getc (f)) != EOF)
444 {
445 if (c == '@')
446 {
447 tag = StringNew ();
448 while ((c = getc (f)) != '@')
449 {
450 if (c == EOF)
451 abort ();
452 StringAdd (tag, c);
453 }
454 if (ls)
455 StringAddString (tag, ls->extra->buf);
456 switch (tag->buf[0]) {
457 case '?':
458 ss = SkipStackPush (ss, skipping);
459 if (!ReplaceSetFind (s, tag->buf + 1))
460 skipping++;
461 LineSkip (f);
462 break;
463 case ':':
464 if (!ss)
465 abort ();
466 if (ss->skipping == skipping)
467 ++skipping;
468 else
469 --skipping;
470 LineSkip (f);
471 break;
472 case ';':
473 skipping = ss->skipping;
474 ss = SkipStackPop (ss);
475 LineSkip (f);
476 break;
477 case '{':
478 ls = LoopStackPush (ls, f, tag->buf + 1);
479 LineSkip (f);
480 break;
481 case '}':
482 ls = LoopStackLoop (s, ls, f);
483 LineSkip (f);
484 break;
485 default:
486 r = ReplaceSetFind (s, tag->buf);
487 if (r && !skipping)
488 StringPut (stdout, r->text);
489 break;
490 }
491 StringDispose (tag);
492 }
493 else if (!skipping)
494 putchar (c);
495 }
496 }
497
498 int
499 main (int argc, char **argv)
500 {
501 FILE *f;
502 ReplaceSet *s;
503
504 if (!argv[1])
505 Bail ("usage: %s <template.sgml>", argv[0]);
506 f = fopen (argv[1], "r");
507 if (!f)
508 {
509 Bail ("can't open file %s", argv[1]);
510 exit (1);
511 }
512 while ((s = ReplaceSetRead (stdin)))
513 {
514 DoReplace (f, s);
515 ReplaceSetDispose (s);
516 rewind (f);
517 }
518 if (ferror (stdout))
519 Bail ("%s", "error writing output");
520 exit (0);
521 }