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