Pristine Ack-5.5
[Ack-5.5.git] / modules / src / input / inp_pkg.body
1 /*      INPUT AND BUFFER HANDLING MODULE        */
2
3 /*
4         Input buffering module: this module contains the routines that
5         offers an input buffering mechanism to the user.
6
7         This module exports the following objects:
8         InsertFile() :  suspend input from current buffer and obtain the
9                         next input characters from the specified file
10         InsertText() :  suspend input from current buffer and take the
11                         specified text as stream of input characters
12         LoadChar() :    (defined in input.h) read next character from
13                         the input ; LoadChar() invokes loadbuf() on
14                         encounting a ASCII NUL character
15         PushBack() :    (defined in input.h) push last character back onto
16                         the input stream; NPUSHBACK characters of pushback
17                         are guaranteed, provided that they have all been read
18                         from the current input stream
19         AtEoIT() :      this routine is called at the end of an inserted text.
20                         A default one is provided, which does nothing.
21         AtEoIF() :      this routine is called at the end of an inserted file.
22                         A default one is provided, which does nothing.
23         
24         Imported objects are:
25         INP_NPUSHBACK, INP_READ_IN_ONE, INP_TYPE, INP_VAR,
26         routines from the "alloc" package, routines from the "storage"
27         package, and routines from the "system" package.
28
29         INP_READ_IN_ONE defined: every input file is read into memory completely
30                 and made an input buffer. Only use it if the size of a file
31                 fits always fits in an integer and you have lots of memory.
32         INP_READ_IN_ONE not defined: the input from files is buffered in
33                 a fixed length input buffer
34         INP_NPUSHBACK: the number of characters pushback
35 */
36
37 #if __STDC__
38 #include <stdlib.h>
39 #else
40 extern char *malloc();
41 #endif
42
43 #include <system.h>
44
45 #ifndef INP_NPUSHBACK
46 #define INP_NPUSHBACK 1
47 #endif
48
49 #if INP_NPUSHBACK < 1
50 #define INP_NPUSHBACK 1
51 #endif
52
53 #ifndef INP_BUFSIZE
54 #define INP_BUFSIZE BUFSIZ
55 #endif
56
57 #if INP_NPUSHBACK > INP_BUFSIZE/2
58 Now this is really ridiculous! You deserve what you get!!
59 #endif
60
61 #ifdef INP_TYPE
62 extern INP_TYPE INP_VAR;
63 #endif /* INP_TYPE */
64
65 #ifdef DEBUG
66 #define INP_PRIVATE
67 #else
68 #define INP_PRIVATE static
69 #endif
70
71 struct INP_buffer_header        {
72         struct INP_buffer_header *bh_next;
73         int bh_size;    /* = strlen (text), should be unsigned  */
74         char *bh_text;  /* pointer to buffer containing text    */
75         char *bh_ipp;   /* current read pointer (= stacked ipp) */
76 #ifdef INP_TYPE
77         INP_TYPE bh_i;  /* user defined */
78 #endif /* INP_TYPE */
79         File *bh_fd;    /* A file descriptor in case of a file */
80         char bh_eofreturned;    /* set if we returned eof for this buffer */
81 };
82
83 #ifndef INP_READ_IN_ONE
84 struct INP_i_buf {
85         struct INP_i_buf *ib_next;
86         char ib_text[INP_BUFSIZE+INP_NPUSHBACK];
87 };
88
89 #endif /* not INP_READ_IN_ONE */
90
91 char *_ipp;
92 INP_PRIVATE struct INP_buffer_header *INP_head, *INP_free;
93
94 #ifdef INP_READ_IN_ONE
95 /*      INP_rdfile() creates a buffer in which the text of the file
96         is situated.  A pointer to the start of this text is
97         returned.  *size is initialized with the buffer length.
98 */
99
100 _PROTOTYPE(INP_PRIVATE int INP_rdfile, (File *, char *, long *, char **));
101
102 INP_PRIVATE int
103 INP_rdfile(fd, fn, size, pbuf)
104         register File *fd;
105         char *fn;               /* file name */
106         register long *size;
107         char **pbuf;            /* output parameter */
108 {
109         extern long sys_filesize();
110         int rsize;
111
112         if (
113              (*size = sys_filesize(fn)) < 0
114            ||
115              ((unsigned) (*size + 1) != (*size + 1))
116            ||
117              !(*pbuf = malloc((unsigned) (*size + 1)))) {
118                 return 0;
119         }
120         if (
121              !sys_read(fd, *pbuf, (int) *size, &rsize)
122            ||
123              *size != rsize
124            )  {
125                 free(*pbuf);
126                 return 0;
127         }
128         (*pbuf)[rsize] = '\0';  /* invoke loadbuf() at end */
129         return 1;
130 }
131 #endif /* INP_READ_IN_ONE */
132
133 #ifndef INP_READ_IN_ONE
134 /*      Input buffer supplying routines: INP_pbuf()
135 */
136
137 INP_PRIVATE struct INP_i_buf *i_ptr;
138
139 _PROTOTYPE(INP_PRIVATE char * INP_pbuf, (void));
140 INP_PRIVATE char *
141 INP_pbuf()
142 {
143         register struct INP_i_buf *ib = 
144                 (struct INP_i_buf *) malloc(sizeof(struct INP_i_buf));
145
146         if (!ib) return 0;
147         ib->ib_next = i_ptr;
148         i_ptr = ib;
149
150         /*      Don't give him all of it, we need some to implement a good
151                 PushBack
152         */
153         return &(ib->ib_text[INP_NPUSHBACK-1]);
154 }
155 #endif /* not INP_READ_IN_ONE */
156
157 /*      Input buffer administration: INP_push_bh() and INP_pop_bh()
158 */
159 _PROTOTYPE(INP_PRIVATE struct INP_buffer_header *INP_push_bh, (void));
160 _PROTOTYPE(INP_PRIVATE int INP_pop_bh, (void));
161
162 INP_PRIVATE struct INP_buffer_header *
163 INP_push_bh()
164 {
165         register struct INP_buffer_header *bh;
166
167         if (bh = INP_head) {
168                 bh->bh_ipp = _ipp;
169 #ifdef INP_TYPE
170                 bh->bh_i = INP_VAR;
171 #endif /* INP_TYPE */
172         }
173         bh = INP_free;
174         if (bh) INP_free = bh->bh_next;
175         else if (!(bh = (struct INP_buffer_header *)malloc(sizeof(struct INP_buffer_header)))) return 0;
176         bh->bh_next = INP_head;
177         bh->bh_eofreturned = 0;
178         INP_head = bh;
179         return bh;
180 }
181
182 /*      INP_pop_bh() uncovers the previous inputbuffer on the stack
183         of headers.  0 is returned if there are no more
184         inputbuffers on the stack, 1 is returned in the other case.
185 */
186 INP_PRIVATE int
187 INP_pop_bh()
188 {
189         register struct INP_buffer_header *bh = INP_head;
190
191
192         bh = bh->bh_next;
193         INP_head->bh_next = INP_free;
194         INP_free = INP_head;
195         INP_head = bh;
196
197         if (!bh)        {       /* no more entries */
198                 INP_head = (struct INP_buffer_header *) 0;
199                 return 0;
200         }
201
202         _ipp = bh->bh_ipp;      /* restore previous input pointer */
203 #ifdef INP_TYPE
204         INP_VAR = bh->bh_i;
205 #endif /* INP_TYPE */
206
207         return 1;
208 }
209
210 #ifndef INP_READ_IN_ONE
211 /*      low level I/O routine : read one block from current input
212         stream : INP_rdblock
213 */
214 _PROTOTYPE(INP_PRIVATE int INP_rdblock, (File *, char *, int *));
215
216 INP_PRIVATE int
217 INP_rdblock(fd, buf, n)
218         File *fd;
219         char *buf;
220         int *n;
221 {
222
223         if (!sys_read(fd, buf, INP_BUFSIZE, n)) {
224                 return 0;
225         }
226         buf[*n] = '\0';
227         return 1;
228 }
229 #endif /* not INP_READ_IN_ONE */
230
231 /*      Miscellaneous routines :
232         INP_mk_filename()
233 */
234 _PROTOTYPE(INP_PRIVATE int INP_mk_filename, (char *, char *, char **));
235
236 /*      INP_mk_filename() concatenates a dir and filename.
237 */
238 INP_PRIVATE int
239 INP_mk_filename(dir, file, newname)
240         register char *dir, *file;
241         char **newname;
242 {
243
244         register char *dst;
245
246         dst = malloc((unsigned) (strlen(dir) + strlen(file) + 2));
247         if (!dst) return 0;
248         *newname = dst;
249         if (*dir) {
250                 while (*dst++ = *dir++) ;
251                 *(dst-1) = '/';
252         }
253         while (*dst++ = *file++);
254         return 1;
255 }
256
257 /*      Interface routines : InsertFile, InsertText, and loadbuf
258 */
259
260 int
261 InsertFile(filnam, table, result)
262         char *filnam;
263         char *table[];
264         char **result;
265 {
266         char *newfn = 0;
267
268 #ifdef INP_READ_IN_ONE
269         char *text;
270         long size;
271 #endif /* INP_READ_IN_ONE */
272         File *fd = 0;
273
274         if (!filnam) fd = STDIN;
275         else {
276                 if (table == 0 || filnam[0] == '/') {
277                                 /* don't look in the table! */
278                         if (!sys_open(filnam, OP_READ, &fd)) return 0;
279                 }
280                 else {
281                         while (*table) {        
282                                 /* look in the directory table */
283                                 if (!INP_mk_filename(*table++, filnam, &newfn)) {
284                                         return 0;
285                                 }
286                                 if (sys_open(newfn, OP_READ, &fd)) {
287                                         /* free filnam ??? NO we don't know 
288                                            where it comes from!
289                                         */
290                                         filnam = newfn;
291                                         break;
292                                 }
293                                 free(newfn);
294                                 newfn = 0;
295                         }
296                 }
297         }
298
299         if (fd) {
300                 register struct INP_buffer_header *bh = INP_push_bh();
301
302                 if (!bh) {
303                         if (fd != STDIN) sys_close(fd);
304                         return 0;
305                 }
306 #ifdef INP_READ_IN_ONE
307                 if (fd == STDIN) return 0;      /* illegal */
308                 if (!INP_rdfile(fd, filnam, &size, &text)) {
309                         sys_close(fd);
310                         return 0;
311                 }
312                 bh->bh_size = size;
313                 _ipp = bh->bh_text = text;
314 #else /* not INP_READ_IN_ONE */
315                 if (
316                      !(_ipp = bh->bh_text = INP_pbuf())
317                    ||
318                      !INP_rdblock(fd,_ipp,&(bh->bh_size))) {
319                         if (fd != STDIN) sys_close(fd);
320                         return 0;
321                 }
322 #endif /* INP_READ_IN_ONE */
323                 bh->bh_fd = fd; /* this is a file */
324                 if (result) *result = filnam;
325                 return 1;
326         }
327         return 0;
328 }
329
330 int
331 InsertText(text, length)
332         char *text;
333 {
334         register struct INP_buffer_header *bh = INP_push_bh();
335
336         if (!bh) return 0;
337         bh->bh_size = (long) length;
338         _ipp = bh->bh_text = text;
339         bh->bh_fd = 0;  /* No file! */
340         return 1;
341 }
342
343 /*      loadbuf() is called if LoadChar meets a '\0' character
344         which may be the end-of-buffer mark of the current input
345         buffer.  The '\0' could be genuine although not likely.
346         Note: this routine is exported due to its occurence in the definition
347         of LoadChar [input.h], that is defined as a macro.
348 */
349 int
350 loadbuf()
351 {
352         register struct INP_buffer_header *bh = INP_head;
353         static char buf[INP_NPUSHBACK + 1];
354         int FromFile;
355
356         if (!bh) {              /* stack exhausted, EOF on sourcefile */
357                 return EOI;
358         }
359
360         if (_ipp < &(bh->bh_text[bh->bh_size])) {
361                         /* a genuine '\0' character has been seen */
362                 return '\0';
363         }
364
365         if (!bh->bh_eofreturned) {
366                 FromFile = (bh->bh_fd != 0);
367
368 #ifndef INP_READ_IN_ONE
369                 if (FromFile && bh->bh_size > 0) {
370 #if INP_NPUSHBACK > 1
371                         register char *so = &(bh->bh_text[bh->bh_size]);
372                         register char *de = bh->bh_text;
373                         register int i = INP_NPUSHBACK - 1;
374
375                         if (i >= bh->bh_size) i = bh->bh_size - 1;
376                         while (i-- > 0) {
377                                 /* make sure PushBack will work */
378                                 *--de = *--so;
379                         }
380 #endif
381                         if ( INP_rdblock(bh->bh_fd, bh->bh_text, &(bh->bh_size)) 
382                            &&
383                              bh->bh_size > 0
384                            ) {
385                                 _ipp = bh->bh_text;
386                                 return *_ipp++;
387                         }
388                 }
389
390 #endif /* not INP_READ_IN_ONE */
391                 if (FromFile && bh->bh_fd != STDIN) sys_close(bh->bh_fd);
392
393 #if INP_NPUSHBACK > 1
394                 {
395                         register char *so = &(bh->bh_text[bh->bh_size]);
396                         register char *de = &buf[INP_NPUSHBACK - 1];
397                         register int i = INP_NPUSHBACK - 1;
398
399                         if (i >= bh->bh_size) i = bh->bh_size - 1;
400                         for (;i > 0; i--) {
401                                 /* make sure PushBack will work */
402                                 *--de = *--so;
403                         }
404                 }
405 #endif
406                 buf[INP_NPUSHBACK-1] = 0;       /* make PushBack work on EOI */
407                 _ipp = &buf[INP_NPUSHBACK];
408
409                 if (bh->bh_fd) {        /* unstack a file */
410 #ifndef INP_READ_IN_ONE
411                         struct INP_i_buf *ib;
412
413                         ib = i_ptr->ib_next;
414                         free((char *) i_ptr);
415                         i_ptr = ib;
416 #else /* INP_READ_IN_ONE */
417                         free(bh->bh_text);
418 #endif /* INP_READ_IN_ONE */
419                 }
420                 bh->bh_text = buf;
421                 bh->bh_size = INP_NPUSHBACK - 1;
422                 if (FromFile) {
423                         if (AtEoIF()) {
424                                 bh->bh_eofreturned = 1;
425                                 return EOI;
426                         }
427                 }
428                 else {
429                         if (AtEoIT()) {
430                                 bh->bh_eofreturned = 1;
431                                 return EOI;
432                         }
433                 }
434         }
435
436         if (bh->bh_eofreturned && _ipp == &buf[INP_NPUSHBACK]) {
437                 return EOI;
438         }
439
440         if (INP_pop_bh()) {
441                 int c;
442                 LoadChar(c);
443                 return c;
444         }
445         _ipp = &buf[INP_NPUSHBACK];
446         return EOI;
447 }