xref: /plan9/sys/src/libString/s_rdinstack.c (revision dc5a79c1208f0704eeb474acc990728f8b4854f5)
1 
2 #include <u.h>
3 #include <libc.h>
4 #include <bio.h>
5 #include "String.h"
6 
7 struct Sinstack{
8 	int	depth;
9 	Biobuf	*fp[32];	/* hard limit to avoid infinite recursion */
10 };
11 
12 /* initialize */
13 extern Sinstack *
s_allocinstack(char * file)14 s_allocinstack(char *file)
15 {
16 	Sinstack *sp;
17 	Biobuf *fp;
18 
19 	fp = Bopen(file, OREAD);
20 	if(fp == nil)
21 		return nil;
22 
23 	sp = malloc(sizeof *sp);
24 	sp->depth = 0;
25 	sp->fp[0] = fp;
26 	return sp;
27 }
28 
29 extern void
s_freeinstack(Sinstack * sp)30 s_freeinstack(Sinstack *sp)
31 {
32 	while(sp->depth >= 0)
33 		Bterm(sp->fp[sp->depth--]);
34 	free(sp);
35 }
36 
37 /*  Append an input line to a String.
38  *
39  *  Empty lines and leading whitespace are removed.
40  */
41 static char *
rdline(Biobuf * fp,String * to)42 rdline(Biobuf *fp, String *to)
43 {
44 	int c;
45 	int len = 0;
46 
47 	c = Bgetc(fp);
48 
49 	/* eat leading white */
50 	while(c==' ' || c=='\t' || c=='\n' || c=='\r')
51 		c = Bgetc(fp);
52 
53 	if(c < 0)
54 		return 0;
55 
56 	for(;;){
57 		switch(c) {
58 		case -1:
59 			goto out;
60 		case '\\':
61 			c = Bgetc(fp);
62 			if (c != '\n') {
63 				s_putc(to, '\\');
64 				s_putc(to, c);
65 				len += 2;
66 			}
67 			break;
68 		case '\r':
69 			break;
70 		case '\n':
71 			if(len != 0)
72 				goto out;
73 			break;
74 		default:
75 			s_putc(to, c);
76 			len++;
77 			break;
78 		}
79 		c = Bgetc(fp);
80 	}
81 out:
82 	s_terminate(to);
83 	return to->ptr - len;
84 }
85 
86 /* Append an input line to a String.
87  *
88  * Returns a pointer to the character string (or 0).
89  * Leading whitespace and newlines are removed.
90  * Lines starting with #include cause us to descend into the new file.
91  * Empty lines and other lines starting with '#' are ignored.
92  */
93 extern char *
s_rdinstack(Sinstack * sp,String * to)94 s_rdinstack(Sinstack *sp, String *to)
95 {
96 	char *p;
97 	Biobuf *fp, *nfp;
98 
99 	s_terminate(to);
100 	fp = sp->fp[sp->depth];
101 
102 	for(;;){
103 		p = rdline(fp, to);
104 		if(p == nil){
105 			if(sp->depth == 0)
106 				break;
107 			Bterm(fp);
108 			sp->depth--;
109 			return s_rdinstack(sp, to);
110 		}
111 
112 		if(strncmp(p, "#include", 8) == 0 && (p[8] == ' ' || p[8] == '\t')){
113 			to->ptr = p;
114 			p += 8;
115 
116 			/* sanity (and looping) */
117 			if(sp->depth >= nelem(sp->fp))
118 				sysfatal("s_recgetline: includes too deep");
119 
120 			/* skip white */
121 			while(*p == ' ' || *p == '\t')
122 				p++;
123 
124 			nfp = Bopen(p, OREAD);
125 			if(nfp == nil)
126 				continue;
127 			sp->depth++;
128 			sp->fp[sp->depth] = nfp;
129 			return s_rdinstack(sp, to);
130 		}
131 
132 		/* got milk? */
133 		if(*p != '#')
134 			break;
135 
136 		/* take care of comments */
137 		to->ptr = p;
138 		s_terminate(to);
139 	}
140 	return p;
141 }
142