COPYING: Update to latest revision of GPLv2.
[enscript.git] / states / process.c
1 /*
2  * Process input according to the specified rules.
3  * Copyright (c) 1997-1999 Markku Rossi.
4  *
5  * Author: Markku Rossi <mtr@iki.fi>
6  */
7
8 /*
9  * This file is part of GNU enscript.
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2, or (at your option)
14  * any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; see the file COPYING.  If not, write to
23  * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
24  * Boston, MA 02110-1301, USA.
25  */
26
27 #include "defs.h"
28
29 /*
30  * Prototypes for static functions.
31  */
32
33 /*
34  * Evaluate the begin rules of state <state>.  The begin rules are
35  * evaluated from parent to child.
36  */
37 static Node *eval_begin_rules ___P ((State *state, int *return_seen));
38
39 /*
40  * Evaluate the end rules of state <state>.  The end rules are
41  * evaluated from child to parent.
42  */
43 static Node *eval_end_rules ___P ((State *state, int *found_return));
44
45 /*
46  * Global functions.
47  */
48
49 void
50 process_file (fname)
51      char *fname;
52 {
53   Node *result;
54   int return_seen = 0;
55
56   start_state = NULL;
57   current_fname = fname;
58   current_linenum = 1;
59
60   /* Init buffer variables. */
61   data_in_buffer = 0;
62   bufpos = 0;
63   eof_seen = 0;
64
65   /* Enter build-in variables. */
66   enter_system_variable ("filename", fname);
67
68   /* Read in the first block of data. */
69   data_in_buffer = fread (inbuf, 1, INBUFSIZE, ifp);
70   if (data_in_buffer < INBUFSIZE)
71     eof_seen = 1;
72
73   if (start_state_arg)
74     start_state = start_state_arg;
75
76   /* Execute start block. */
77   result = eval_statement_list (start_stmts, NULL, &return_seen);
78   node_free (result);
79
80   if (start_state == NULL)
81     {
82       /* No start state found, copy our input to output. */
83       while (data_in_buffer)
84         {
85           fwrite (inbuf, 1, data_in_buffer, ofp);
86           data_in_buffer = fread (inbuf, 1, INBUFSIZE, ifp);
87         }
88     }
89   else
90     {
91       result = execute_state (start_state);
92       node_free (result);
93     }
94 }
95
96
97 Node *
98 execute_state (name)
99      char *name;
100 {
101   State *state;
102   State *s;
103   int to_read, got;
104   ListItem *rule, *first_rule;
105   unsigned int first_idx;
106   unsigned int match_len;
107   Node *result = nvoid;
108   Cons *r;
109   Node *exp;
110   int return_seen = 0;
111   int idx;
112
113   /* Lookup state. */
114   state = lookup_state (name);
115   if (state == NULL)
116     {
117       fprintf (stderr, _("%s: undefined state `%s'\n"), program, name);
118       exit (1);
119     }
120
121   /* Begin rules. */
122   result = eval_begin_rules (state, &return_seen);
123   if (return_seen)
124     goto out;
125
126   /* Execute this state. */
127   while (1)
128     {
129       int eol;
130
131       /* Do we have enough data? */
132       if (bufpos >= data_in_buffer)
133         {
134           if (eof_seen)
135             /* All done. */
136             break;
137
138           /* Read more data. */
139           data_in_buffer = fread (inbuf, 1, INBUFSIZE, ifp);
140           if (data_in_buffer < INBUFSIZE)
141             eof_seen = 1;
142
143           bufpos = 0;
144           continue;
145         }
146
147       /* Check line number. */
148       if (bufpos > 0 && inbuf[bufpos - 1] == '\n')
149         current_linenum++;
150
151       /* Find the end of the input line. */
152       for (eol = bufpos; eol < data_in_buffer && inbuf[eol] != '\n'; eol++)
153         ;
154       if (eol < data_in_buffer && inbuf[eol] == '\n')
155         eol++;
156       if (eol >= data_in_buffer && !eof_seen && bufpos > 0)
157         {
158           /* Must read more data to the buffer. */
159           memmove (inbuf, inbuf + bufpos, eol - bufpos);
160           data_in_buffer = eol - bufpos;
161           bufpos = 0;
162
163           to_read = INBUFSIZE - data_in_buffer;
164           got = fread (inbuf + data_in_buffer, 1, to_read, ifp);
165           if (got < to_read)
166             eof_seen = 1;
167
168           data_in_buffer += got;
169           continue;
170         }
171
172       /* Evaluate state expressions. */
173       first_idx = eol;
174       match_len = 0;
175       first_rule = NULL;
176       current_match = NULL;
177
178       for (s = state; s; s = s->super)
179         {
180           for (rule = s->rules->head; rule; rule = rule->next)
181             {
182               int err;
183
184               r = (Cons *) rule->data;
185               exp = (Node *) r->car;
186               if (exp == RULE_BEGIN || exp == RULE_END)
187                 continue;
188
189               if (exp->type == nSYMBOL)
190                 {
191                   Node *n;
192
193                   /* Lookup this variable by hand from global variables. */
194                   if (!strhash_get (ns_vars, exp->u.sym, strlen (exp->u.sym),
195                                     (void **) &n))
196                     {
197                       fprintf (stderr,
198                                _("%s: error: undefined variable `%s'\n"),
199                                program, exp->u.sym);
200                       exit (1);
201                     }
202                   if (n->type != nREGEXP)
203                     /* Skip this rule */
204                     continue;
205
206                   exp = n;
207                 }
208
209               err = re_search (REGEXP (exp), inbuf, eol, bufpos,
210                                eol - bufpos, &exp->u.re.matches);
211               if (err < 0)
212                 /* No mach. */
213                 continue;
214
215               idx = exp->u.re.matches.start[0];
216               if (idx >= 0
217                   && (idx < first_idx
218                       || (idx == first_idx
219                           && (exp->u.re.matches.end[0]
220                               - exp->u.re.matches.start[0]
221                               > match_len))))
222                 {
223                   first_idx = idx;
224                   first_rule = rule;
225                   match_len = (exp->u.re.matches.end[0]
226                                - exp->u.re.matches.start[0]);
227                   current_match = &exp->u.re.matches;
228                   current_match_buf = inbuf;
229                 }
230             }
231         }
232
233       /* Print all data before the first rule. */
234       fwrite (inbuf + bufpos, 1, first_idx - bufpos, ofp);
235
236       if (first_rule)
237         {
238           /* Execute statements. */
239           bufpos = current_match->end[0];
240
241           node_free (result);
242           result = eval_statement_list ((List *)
243                                         ((Cons *) first_rule->data)->cdr,
244                                         NULL, &return_seen);
245           if (return_seen)
246             goto out;
247         }
248       else
249         bufpos = first_idx;
250     }
251
252 out:
253
254   /* End rules. */
255   {
256     int found = 0;
257     Node *result2;
258
259     result2 = eval_end_rules (state, &found);
260     if (found)
261       {
262         node_free (result);
263         result = result2;
264       }
265   }
266
267   return result;
268 }
269
270 \f
271 /*
272  * Static functions.
273  */
274
275 static Node *
276 eval_begin_rules (state, return_seen)
277      State *state;
278      int *return_seen;
279 {
280   Node *result = nvoid;
281   Cons *r;
282   ListItem *rule;
283
284   /* The begin rules are evaluated from the parent to child. */
285
286   /* Autoload the super if needed. */
287   if (state->super_name && state->super == NULL)
288     {
289       state->super = lookup_state (state->super_name);
290       if (state->super == NULL)
291         {
292           fprintf (stderr, _("%s: undefined super state `%s'\n"),
293                    program, state->super_name);
294           exit (1);
295         }
296     }
297
298   if (state->super)
299     {
300       result = eval_begin_rules (state->super, return_seen);
301       if (*return_seen)
302         return result;
303     }
304
305   /* Eval our begin rule. */
306   for (rule = state->rules->head; rule; rule = rule->next)
307     {
308       r = (Cons *) rule->data;
309       if (r->car == RULE_BEGIN)
310         {
311           node_free (result);
312           result = eval_statement_list ((List *) r->cdr, NULL, return_seen);
313           if (*return_seen)
314             break;
315         }
316     }
317
318   return result;
319 }
320
321
322 static Node *
323 eval_end_rules (state, found_return)
324      State *state;
325      int *found_return;
326 {
327   ListItem *rule;
328   Cons *r;
329   Node *result = nvoid;
330   int return_seen;
331
332   /* The end rules are evaluated from child to parent. */
333
334   for (; state; state = state->super)
335     for (rule = state->rules->head; rule; rule = rule->next)
336       {
337         r = (Cons *) rule->data;
338         if (r->car == RULE_END)
339           {
340             *found_return = 1;
341             node_free (result);
342             result = eval_statement_list ((List *) r->cdr, NULL, &return_seen);
343           }
344       }
345
346   return result;
347 }