1 /*
2  * fontconfig/doc/edit-sgml.c
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 the author(s) not be used in
11  * advertising or publicity pertaining to distribution of the software without
12  * specific, written prior permission.  The authors make 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  * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18  * EVENT SHALL THE AUTHOR(S) 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, int line, const char *arg);
81 
82 static Replace *
83 ReplaceRead (FILE *f, int *linep);
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, int *linep);
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, int *linep);
141 
142 static void
143 DoReplace (FILE *f, int *linep, ReplaceSet *s);
144 
145 #define STRING_INIT 128
146 
147 static void *
New(int size)148 New (int size)
149 {
150     void    *m = malloc (size);
151     if (!m)
152 	abort ();
153     return m;
154 }
155 
156 static void *
Reallocate(void * p,int size)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
Dispose(void * p)167 Dispose (void *p)
168 {
169     free (p);
170 }
171 
172 static String *
StringNew(void)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
StringAdd(String * s,char c)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
StringAddString(String * s,char * buf)195 StringAddString (String *s, char *buf)
196 {
197     while (*buf)
198 	StringAdd (s, *buf++);
199 }
200 
201 static String *
StringMake(char * buf)202 StringMake (char *buf)
203 {
204     String  *s = StringNew ();
205     StringAddString (s, buf);
206     return s;
207 }
208 
209 static void
StringDel(String * s)210 StringDel (String *s)
211 {
212     if (s->len)
213 	s->buf[--s->len] = '\0';
214 }
215 
216 static void
StringPut(FILE * f,String * s)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
StringDispose(String * s)228 StringDispose (String *s)
229 {
230     Dispose (s->buf);
231     Dispose (s);
232 }
233 
234 static Replace *
ReplaceNew(void)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
ReplaceDispose(Replace * r)244 ReplaceDispose (Replace *r)
245 {
246     StringDispose (r->tag);
247     StringDispose (r->text);
248     Dispose (r);
249 }
250 
251 static void
Bail(const char * format,int line,const char * arg)252 Bail (const char *format, int line, const char *arg)
253 {
254     fprintf (stderr, "fatal: ");
255     fprintf (stderr, format, line, arg);
256     fprintf (stderr, "\n");
257     exit (1);
258 }
259 
260 static int
Getc(FILE * f,int * linep)261 Getc (FILE *f, int *linep)
262 {
263     int	c = getc (f);
264     if (c == '\n')
265 	++(*linep);
266     return c;
267 }
268 
269 static void
Ungetc(int c,FILE * f,int * linep)270 Ungetc (int c, FILE *f, int *linep)
271 {
272     if (c == '\n')
273 	--(*linep);
274     ungetc (c, f);
275 }
276 
277 static Replace *
ReplaceRead(FILE * f,int * linep)278 ReplaceRead (FILE *f, int *linep)
279 {
280     int	    c;
281     Replace *r;
282 
283     while ((c = Getc (f, linep)) != '@')
284     {
285 	if (c == EOF)
286 	    return 0;
287     }
288     r = ReplaceNew();
289     while ((c = Getc (f, linep)) != '@')
290     {
291 	if (c == EOF)
292 	{
293 	    ReplaceDispose (r);
294 	    return 0;
295 	}
296 	if (isspace (c))
297 	    Bail ("%d: invalid character after tag %s", *linep, r->tag->buf);
298 	StringAdd (r->tag, c);
299     }
300     if (r->tag->buf[0] == '\0')
301     {
302 	ReplaceDispose (r);
303 	return 0;
304     }
305     while (isspace ((c = Getc (f, linep))))
306 	;
307     Ungetc (c, f, linep);
308     while ((c = Getc (f, linep)) != '@' && c != EOF)
309 	StringAdd (r->text, c);
310     if (c == '@')
311 	Ungetc (c, f, linep);
312     while (isspace (StringLast (r->text)))
313 	StringDel (r->text);
314     if (StringLast(r->text) == '%')
315     {
316 	StringDel (r->text);
317 	StringAdd (r->text, ' ');
318     }
319     return r;
320 }
321 
322 static ReplaceList *
ReplaceListNew(Replace * r,ReplaceList * next)323 ReplaceListNew (Replace *r, ReplaceList *next)
324 {
325     ReplaceList	*l = New (sizeof (ReplaceList));
326     l->r = r;
327     l->next = next;
328     return l;
329 }
330 
331 static void
ReplaceListDispose(ReplaceList * l)332 ReplaceListDispose (ReplaceList *l)
333 {
334     if (l)
335     {
336 	ReplaceListDispose (l->next);
337 	ReplaceDispose (l->r);
338 	Dispose (l);
339     }
340 }
341 
342 static ReplaceSet *
ReplaceSetNew(void)343 ReplaceSetNew (void)
344 {
345     ReplaceSet	*s = New (sizeof (ReplaceSet));
346     s->head = 0;
347     return s;
348 }
349 
350 static void
ReplaceSetDispose(ReplaceSet * s)351 ReplaceSetDispose (ReplaceSet *s)
352 {
353     ReplaceListDispose (s->head);
354     Dispose (s);
355 }
356 
357 static void
ReplaceSetAdd(ReplaceSet * s,Replace * r)358 ReplaceSetAdd (ReplaceSet *s, Replace *r)
359 {
360     s->head = ReplaceListNew (r, s->head);
361 }
362 
363 static Replace *
ReplaceSetFind(ReplaceSet * s,char * tag)364 ReplaceSetFind (ReplaceSet *s, char *tag)
365 {
366     ReplaceList	*l;
367 
368     for (l = s->head; l; l = l->next)
369 	if (!strcmp (tag, l->r->tag->buf))
370 	    return l->r;
371     return 0;
372 }
373 
374 static ReplaceSet *
ReplaceSetRead(FILE * f,int * linep)375 ReplaceSetRead (FILE *f, int *linep)
376 {
377     ReplaceSet	*s = ReplaceSetNew ();
378     Replace	*r;
379 
380     while ((r = ReplaceRead (f, linep)))
381     {
382 	while (ReplaceSetFind (s, r->tag->buf))
383 	    StringAdd (r->tag, '+');
384 	ReplaceSetAdd (s, r);
385     }
386     if (!s->head)
387     {
388 	ReplaceSetDispose (s);
389 	s = 0;
390     }
391     return s;
392 }
393 
394 static SkipStack *
SkipStackPush(SkipStack * prev,int skipping)395 SkipStackPush (SkipStack *prev, int skipping)
396 {
397     SkipStack	*ss = New (sizeof (SkipStack));
398     ss->prev = prev;
399     ss->skipping = skipping;
400     return ss;
401 }
402 
403 static SkipStack *
SkipStackPop(SkipStack * prev)404 SkipStackPop (SkipStack *prev)
405 {
406     SkipStack	*ss = prev->prev;
407     Dispose (prev);
408     return ss;
409 }
410 
411 static LoopStack *
LoopStackPush(LoopStack * prev,FILE * f,char * tag)412 LoopStackPush (LoopStack *prev, FILE *f, char *tag)
413 {
414     LoopStack	*ls = New (sizeof (LoopStack));
415     ls->prev = prev;
416     ls->tag = StringMake (tag);
417     ls->extra = StringNew ();
418     ls->pos = ftell (f);
419     return ls;
420 }
421 
422 static LoopStack *
LoopStackLoop(ReplaceSet * rs,LoopStack * ls,FILE * f)423 LoopStackLoop (ReplaceSet *rs, LoopStack *ls, FILE *f)
424 {
425     String	*s = StringMake (ls->tag->buf);
426     LoopStack	*ret = ls;
427     Bool	loop;
428 
429     StringAdd (ls->extra, '+');
430     StringAddString (s, ls->extra->buf);
431     loop = ReplaceSetFind (rs, s->buf) != 0;
432     StringDispose (s);
433     if (loop)
434 	fseek (f, ls->pos, SEEK_SET);
435     else
436     {
437 	ret = ls->prev;
438 	StringDispose (ls->tag);
439 	StringDispose (ls->extra);
440 	Dispose (ls);
441     }
442     return ret;
443 }
444 
445 static void
LineSkip(FILE * f,int * linep)446 LineSkip (FILE *f, int *linep)
447 {
448     int	c;
449 
450     while ((c = Getc (f, linep)) == '\n')
451 	;
452     Ungetc (c, f, linep);
453 }
454 
455 static void
DoReplace(FILE * f,int * linep,ReplaceSet * s)456 DoReplace (FILE *f, int *linep, ReplaceSet *s)
457 {
458     int		c;
459     String	*tag;
460     Replace	*r;
461     SkipStack	*ss = 0;
462     LoopStack	*ls = 0;
463     int		skipping = 0;
464 
465     while ((c = Getc (f, linep)) != EOF)
466     {
467 	if (c == '@')
468 	{
469 	    tag = StringNew ();
470 	    while ((c = Getc (f, linep)) != '@')
471 	    {
472 		if (c == EOF)
473 		    abort ();
474 		StringAdd (tag, c);
475 	    }
476 	    if (ls)
477 		StringAddString (tag, ls->extra->buf);
478 	    switch (tag->buf[0]) {
479 	    case '?':
480 		ss = SkipStackPush (ss, skipping);
481 		if (!ReplaceSetFind (s, tag->buf + 1))
482 		    skipping++;
483 		LineSkip (f, linep);
484 		break;
485 	    case ':':
486 		if (!ss)
487 		    abort ();
488 		if (ss->skipping == skipping)
489 		    ++skipping;
490 		else
491 		    --skipping;
492 		LineSkip (f, linep);
493 		break;
494 	    case ';':
495 		skipping = ss->skipping;
496 		ss = SkipStackPop (ss);
497 		LineSkip (f, linep);
498 		break;
499 	    case '{':
500 		ls = LoopStackPush (ls, f, tag->buf + 1);
501 		LineSkip (f, linep);
502 		break;
503 	    case '}':
504 		ls = LoopStackLoop (s, ls, f);
505 		LineSkip (f, linep);
506 		break;
507 	    default:
508 		r = ReplaceSetFind (s, tag->buf);
509 		if (r && !skipping)
510 		    StringPut (stdout, r->text);
511 		break;
512 	    }
513 	    StringDispose (tag);
514 	}
515 	else if (!skipping)
516 	    putchar (c);
517     }
518 }
519 
520 int
main(int argc,char ** argv)521 main (int argc, char **argv)
522 {
523     FILE	*f;
524     ReplaceSet	*s;
525     int		iline, oline;
526 
527     if (!argv[1])
528 	Bail ("usage: %*s <template.sgml>", 0, argv[0]);
529     f = fopen (argv[1], "r");
530     if (!f)
531     {
532 	Bail ("can't open file %s", 0, argv[1]);
533 	exit (1);
534     }
535     iline = 1;
536     while ((s = ReplaceSetRead (stdin, &iline)))
537     {
538 	oline = 1;
539 	DoReplace (f, &oline, s);
540 	ReplaceSetDispose (s);
541 	rewind (f);
542     }
543     if (ferror (stdout))
544 	Bail ("%s", 0, "error writing output");
545     exit (0);
546 }
547