Icons are changed
[gnuplot] / src / scanner.c
1 #ifndef lint
2 static char *RCSid() { return RCSid("$Id: scanner.c,v 1.23 2005/11/23 23:33:37 mikulik Exp $"); }
3 #endif
4
5 /* GNUPLOT - scanner.c */
6
7 /*[
8  * Copyright 1986 - 1993, 1998, 2004   Thomas Williams, Colin Kelley
9  *
10  * Permission to use, copy, and distribute this software and its
11  * documentation for any purpose with or without fee is hereby granted,
12  * provided that the above copyright notice appear in all copies and
13  * that both that copyright notice and this permission notice appear
14  * in supporting documentation.
15  *
16  * Permission to modify the software is granted, but not the right to
17  * distribute the complete modified source code.  Modifications are to
18  * be distributed as patches to the released version.  Permission to
19  * distribute binaries produced by compiling modified sources is granted,
20  * provided you
21  *   1. distribute the corresponding source modifications from the
22  *    released version in the form of a patch file along with the binaries,
23  *   2. add special version identification to distinguish your version
24  *    in addition to the base release version number,
25  *   3. provide your name and address as the primary contact for the
26  *    support of your modified version, and
27  *   4. retain our contact information in regard to use of the base
28  *    software.
29  * Permission to distribute the released version of the source code along
30  * with corresponding source modifications in the form of a patch file is
31  * granted with same provisions 2 through 4 for binary distributions.
32  *
33  * This software is provided "as is" without express or implied warranty
34  * to the extent permitted by applicable law.
35 ]*/
36
37 #include "scanner.h"
38
39 #include "alloc.h"
40 #include "command.h"
41 #include "util.h"
42
43 static int get_num __PROTO((char str[]));
44 static void substitute __PROTO((char **strp, size_t *str_lenp, int current));
45
46 #ifdef AMIGA_AC_5
47 #define O_RDONLY        0
48 int open(const char *_name, int _mode,...);
49 int close(int);
50 #endif /* AMIGA_AC_5 */
51
52 #ifdef VMS
53 #include <descrip.h>
54 #define MAILBOX "PLOT$MAILBOX"
55 #define pclose(f) fclose(f)
56 #ifdef __DECC
57 #include <lib$routines.h>       /* avoid some IMPLICITFNC warnings */
58 #include <starlet.h>
59 #endif /* __DECC */
60 #endif /* VMS */
61
62
63 #define isident(c) (isalnum((unsigned char)c) || (c) == '_')
64
65 #define LBRACE '{'
66 #define RBRACE '}'
67
68 #define APPEND_TOKEN {token[t_num].length++; current++;}
69
70 #define SCAN_IDENTIFIER while (isident(expression[current + 1]))\
71                                 APPEND_TOKEN
72
73 static int t_num;               /* number of token I'm working on */
74
75 /*
76  * scanner() breaks expression[] into lexical units, storing them in token[].
77  *   The total number of tokens found is returned as the function
78  *   value.  Scanning will stop when '\0' is found in expression[], or
79  *   when token[] is full.  extend_input_line() is called to extend
80  *   expression array if needed.
81  *
82  *       Scanning is performed by following rules:
83  *
84  *      Current char    token should contain
85  *     -------------    -----------------------
86  *      1.  alpha,_     all following alpha-numerics
87  *      2.  digit       0 or more following digits, 0 or 1 decimal point,
88  *                              0 or more digits, 0 or 1 'e' or 'E',
89  *                              0 or more digits.
90  *      3.  ^,+,-,/     only current char
91  *          %,~,(,)
92  *          [,],;,:,
93  *          ?,comma
94  *          $           for using patch (div)
95  *      4.  &,|,=,*     current char; also next if next is same
96  *      5.  !,<,>       current char; also next if next is =
97  *      6.  ", '        all chars up until matching quote
98  *      7.  #           this token cuts off scanning of the line (DFK).
99  *      8.  `           (command substitution: all characters through the
100  *                      matching backtic are replaced by the output of
101  *                      the contained command, then scanning is restarted.)
102  *
103  *                      white space between tokens is ignored
104  */
105 int
106 scanner(char **expressionp, size_t *expressionlenp)
107 {
108     int current;        /* index of current char in expression[] */
109     char *expression = *expressionp;
110     int quote;
111     char brace;
112
113     for (current = t_num = 0; expression[current] != NUL; current++) {
114         if (t_num + 1 >= token_table_size) {
115             /* leave space for dummy end token */
116             extend_token_table();
117         }
118         if (isspace((unsigned char) expression[current]))
119             continue;           /* skip the whitespace */
120         token[t_num].start_index = current;
121         token[t_num].length = 1;
122         token[t_num].is_token = TRUE;   /* to start with... */
123
124         if (expression[current] == '`') {
125             substitute(expressionp, expressionlenp, current);
126             expression = *expressionp;  /* expression might have moved */
127             current--;
128             continue;
129         }
130         /* allow _ to be the first character of an identifier */
131         if (isalpha((unsigned char) expression[current])
132             || expression[current] == '_') {
133             SCAN_IDENTIFIER;
134         } else if (isdigit((unsigned char) expression[current])
135                    || expression[current] == '.') {
136             token[t_num].is_token = FALSE;
137             token[t_num].length = get_num(&expression[current]);
138             current += (token[t_num].length - 1);
139 #ifdef GP_STRING_VARS
140             if (token[t_num].length == 1 && expression[current] == '.')
141                 token[t_num].is_token = TRUE;
142 #endif
143         } else if (expression[current] == LBRACE) {
144             token[t_num].is_token = FALSE;
145             token[t_num].l_val.type = CMPLX;
146 #ifdef __PUREC__
147             {
148                 char l[80];
149                 if ((sscanf(&expression[++current], "%lf,%lf%[ }]s",
150                             &token[t_num].l_val.v.cmplx_val.real,
151                             &token[t_num].l_val.v.cmplx_val.imag,
152                             &l) != 3) || (!strchr(l, RBRACE)))
153                     int_error(t_num, "invalid complex constant");
154             }
155 #else
156             if ((sscanf(&expression[++current], "%lf , %lf %c",
157                         &token[t_num].l_val.v.cmplx_val.real,
158                         &token[t_num].l_val.v.cmplx_val.imag,
159                         &brace) != 3) || (brace != RBRACE))
160                 int_error(t_num, "invalid complex constant");
161 #endif
162             token[t_num].length += 2;
163             while (expression[++current] != RBRACE) {
164                 token[t_num].length++;
165                 if (expression[current] == NUL)         /* { for vi % */
166                     int_error(t_num, "no matching '}'");
167             }
168         } else if (expression[current] == '\'' ||
169                    expression[current] == '\"') {
170             token[t_num].length++;
171             quote = expression[current];
172             while (expression[++current] != quote) {
173                 if (!expression[current]) {
174                     expression[current] = quote;
175                     expression[current + 1] = NUL;
176                     break;
177                 } else if (quote == '\"'
178                            && expression[current] == '\\'
179                            && expression[current + 1]) {
180                     current++;
181                     token[t_num].length += 2;
182                 } else if (quote == '\"' && expression[current] == '`') {
183                     substitute(expressionp, expressionlenp, current);
184                     expression = *expressionp;  /* it might have moved */
185                     current--;
186                 } else if (quote == '\'' 
187                            && expression[current+1] == '\''
188                            && expression[current+2] == '\'') {
189                     /* look ahead: two subsequent single quotes 
190                      * -> take them in
191                      */
192                     current += 2;
193                     token[t_num].length += 3;
194                 } else
195                     token[t_num].length++;
196             }
197         } else
198             switch (expression[current]) {
199             case '#':           /* DFK: add comments to gnuplot */
200                 goto endline;   /* ignore the rest of the line */
201             case '^':
202             case '+':
203             case '-':
204             case '/':
205             case '%':
206             case '~':
207             case '(':
208             case ')':
209             case '[':
210             case ']':
211             case ';':
212             case ':':
213             case '?':
214             case ',':
215             case '$':           /* div */
216                 break;
217             case '&':
218             case '|':
219             case '=':
220             case '*':
221                 if (expression[current] == expression[current + 1])
222                     APPEND_TOKEN;
223                 break;
224             case '!':
225             case '<':
226             case '>':
227                 if (expression[current + 1] == '=')
228                     APPEND_TOKEN;
229                 break;
230             default:
231                 int_error(t_num, "invalid character %c",expression[current]);
232             }
233         ++t_num;                /* next token if not white space */
234     }
235
236   endline:                      /* comments jump here to ignore line */
237
238 /* Now kludge an extra token which points to '\0' at end of expression[].
239    This is useful so printerror() looks nice even if we've fallen off the
240    line. */
241
242     token[t_num].start_index = current;
243     token[t_num].length = 0;
244     /* print 3+4  then print 3+  is accepted without
245      * this, since string is ignored if it is not
246      * a token
247      */
248     token[t_num].is_token = TRUE;
249     return (t_num);
250 }
251
252
253 static int
254 get_num(char str[])
255 {
256     int count = 0;
257     long lval;
258
259     token[t_num].is_token = FALSE;
260     token[t_num].l_val.type = INTGR;    /* assume unless . or E found */
261     while (isdigit((unsigned char) str[count]))
262         count++;
263     if (str[count] == '.') {
264         token[t_num].l_val.type = CMPLX;
265         /* swallow up digits until non-digit */
266         while (isdigit((unsigned char) str[++count]));
267         /* now str[count] is other than a digit */
268     }
269     if (str[count] == 'e' || str[count] == 'E') {
270         token[t_num].l_val.type = CMPLX;
271 /* modified if statement to allow + sign in exponent
272    rjl 26 July 1988 */
273         count++;
274         if (str[count] == '-' || str[count] == '+')
275             count++;
276         if (!isdigit((unsigned char) str[count])) {
277             token[t_num].start_index += count;
278             int_error(t_num, "expecting exponent");
279         }
280         while (isdigit((unsigned char) str[++count]));
281     }
282     if (token[t_num].l_val.type == INTGR) {
283         lval = atol(str);
284         if ((token[t_num].l_val.v.int_val = lval) != lval)
285             int_error(t_num, "integer overflow; change to floating point");
286     } else {
287         token[t_num].l_val.v.cmplx_val.imag = 0.0;
288 #ifdef HAVE_LOCALE_H
289         /* Always read numbers on command line as C locale */
290         if (strcmp(localeconv()->decimal_point,".")) {
291             char *save_locale = gp_strdup(setlocale(LC_NUMERIC,NULL));
292             setlocale(LC_NUMERIC,"C");
293             token[t_num].l_val.v.cmplx_val.real = atof(str);
294             setlocale(LC_NUMERIC,save_locale);
295             free(save_locale);
296         } else
297 #endif
298             token[t_num].l_val.v.cmplx_val.real = atof(str);
299     }
300     return (count);
301 }
302
303 /* substitute output from ` `
304  * *strp points to the input string.  (*strp)[current] is expected to
305  * be the initial back tic.  Characters through the following back tic
306  * are replaced by the output of the command.  extend_input_line()
307  * is called to extend *strp array if needed.
308  */
309 static void
310 substitute(char **strp, size_t *str_lenp, int current)
311 {
312     char *last;
313     char c;
314     char *str, *pgm, *rest = NULL;
315     char *output;
316     size_t pgm_len, rest_len = 0;
317     int output_pos;
318
319     /* forgive missing closing backquote at end of line */
320     str = *strp + current;
321     last = str;
322     while (*++last) {
323         if (*last == '`')
324             break;
325     }
326     pgm_len = last - str;
327     pgm = gp_alloc(pgm_len, "command string");
328     safe_strncpy(pgm, str + 1, pgm_len); /* omit ` to leave room for NUL */
329
330     /* save rest of line, if any */
331     if (*last) {
332         last++;                 /* advance past ` */
333         rest_len = strlen(last) + 1;
334         if (rest_len > 1) {
335             rest = gp_alloc(rest_len, "input line copy");
336             strcpy(rest, last);
337         }
338     }
339
340     /* do system call */
341     (void) do_system_func(pgm, &output);
342     free(pgm);
343
344     /* now replace ` ` with output */
345     output_pos = 0;
346     while ((c = output[output_pos++])) {
347         if (c != '\n' && c != '\r')
348             (*strp)[current++] = c;
349         if (current == *str_lenp)
350             extend_input_line();
351     }
352     (*strp)[current] = 0;
353     free(output);
354
355     /* tack on rest of line to output */
356     if (rest) {
357         while (current + rest_len > *str_lenp)
358             extend_input_line();
359         strcpy(*strp + current, rest);
360         free(rest);
361     }
362     screen_ok = FALSE;
363
364 }
365
366