1/* ***************************************************************
2 * copyright (c) lucent technologies 1997
3 * all rights reserved
4 *
5 * permission to use, copy, modify, and distribute this software and
6 * its documentation for any purpose and without fee is hereby
7 * granted, provided that the above copyright notice appear in all
8 * copies and that both that the copyright notice and this
9 * permission notice and warranty disclaimer appear in supporting
10 * documentation, and that the name lucent technologies or any of
11 * its entities not be used in advertising or publicity pertaining
12 * to distribution of the software without specific, written prior
13 * permission
14 *
15 * LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
17 * IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
18 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
20 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
21 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
22 * THIS SOFTWARE
23 * ************************************************************** */
24
25const char *version = "version 20260426";
26
27#define DEBUG
28#include "awk.h"
29#include <ctype.h>
30#include <locale.h>
31#include <signal.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35
36extern char **environ;
37extern int nfields;
38
39int dbg = 0;
40Awkfloat srand_seed = 1;
41char *cmdname; /* gets argv[0] for error messages */
42extern FILE *yyin; /* lex input file */
43char *lexprog; /* points to program argument if it exists */
44extern int errorflag; /* non-zero if any syntax errors; set by yyerror */
45enum compile_states compile_time = ERROR_PRINTING;
46
47static char **pfile; /* program filenames from -f's */
48static size_t maxpfile; /* max program filename */
49static size_t npfile; /* number of filenames */
50static size_t curpfile; /* current filename */
51
52bool CSV = false; /* true for csv input */
53
54bool safe = false; /* true => "safe" mode */
55
56size_t awk_mb_cur_max = 1;
57
58static noreturn void
59fpecatch(
60 int n
61#ifdef SA_SIGINFO
62 ,
63 siginfo_t *si,
64 void *uc
65#endif
66)
67{
68 (void)n;
69#ifdef SA_SIGINFO
70 (void)uc;
71#endif
72#ifdef SA_SIGINFO
73 const char *mesg = NULL;
74
75 switch (si->si_code) {
76 case FPE_INTDIV:
77 mesg = "Integer divide by zero";
78 break;
79 case FPE_INTOVF:
80 mesg = "Integer overflow";
81 break;
82 case FPE_FLTDIV:
83 mesg = "Floating point divide by zero";
84 break;
85 case FPE_FLTOVF:
86 mesg = "Floating point overflow";
87 break;
88 case FPE_FLTUND:
89 mesg = "Floating point underflow";
90 break;
91 case FPE_FLTRES:
92 mesg = "Floating point inexact result";
93 break;
94 case FPE_FLTINV:
95 mesg = "Invalid Floating point operation";
96 break;
97 case FPE_FLTSUB:
98 mesg = "Subscript out of range";
99 break;
100 case 0:
101 default:
102 mesg = "Unknown error";
103 break;
104 }
105#endif
106 FATAL(
107 "floating point exception"
108#ifdef SA_SIGINFO
109 ": %s",
110 mesg
111#endif
112 );
113}
114
115/* can this work with recursive calls? i dont think so
116 * void segvcatch(int n)
117 * {
118 * fatal("segfault. do you have an unbounded recursive call?", n);
119 * }
120 */
121
122static const char *
123setfs(char *p)
124{
125 /* wart: t=>\t */
126 if (p[0] == 't' && p[1] == '\0')
127 return "\t";
128 return p;
129}
130
131static char *
132getarg(int *argc, char ***argv, const char *msg)
133{
134 if ((*argv)[1][2] != '\0') { /* arg is -fsomething */
135 return &(*argv)[1][2];
136 } else { /* arg is -f something */
137 (*argc)--;
138 (*argv)++;
139 if (*argc <= 1)
140 FATAL("%s", msg);
141 return (*argv)[1];
142 }
143}
144
145int
146main(int argc, char *argv[])
147{
148 const char *fs = NULL;
149 char *fn, *vn;
150
151 setlocale(LC_CTYPE, "");
152 setlocale(LC_NUMERIC, "C"); /* for parsing cmdline & prog */
153 awk_mb_cur_max = MB_CUR_MAX;
154 cmdname = argv[0];
155 if (argc == 1) {
156 fprintf(
157 stderr,
158 "usage: %s [-F fs | --csv] [-v var=value] [-f progfile "
159 "| 'prog'] [file ...]\n",
160 cmdname
161 );
162 exit(1);
163 }
164#ifdef SA_SIGINFO
165 {
166 struct sigaction sa;
167 sa.sa_sigaction = fpecatch;
168 sa.sa_flags = SA_SIGINFO;
169 sigemptyset(&sa.sa_mask);
170 (void)sigaction(SIGFPE, &sa, NULL);
171 }
172#else
173 (void)signal(SIGFPE, fpecatch);
174#endif
175 /* signal(sigsegv, segvcatch); experiment */
176
177 /* set and keep track of the random seed */
178 srand_seed = 1;
179 srandom((unsigned long)srand_seed);
180
181 yyin = NULL;
182 symtab = makesymtab(NSYMTAB / NSYMTAB);
183 while (argc > 1 && argv[1][0] == '-' && argv[1][1] != '\0') {
184 if (strcmp(argv[1], "-version") == 0 || strcmp(argv[1], "--version") == 0) {
185 printf("awk %s\n", version);
186 return 0;
187 }
188 if (strcmp(argv[1], "--") == 0) { /* explicit end of args */
189 argc--;
190 argv++;
191 break;
192 }
193 if (strcmp(argv[1], "--csv") == 0) { /* turn on csv input processing */
194 CSV = true;
195 argc--;
196 argv++;
197 continue;
198 }
199 switch (argv[1][1]) {
200 case 's':
201 if (strcmp(argv[1], "-safe") == 0)
202 safe = true;
203 break;
204 case 'f': /* next argument is program filename */
205 fn = getarg(&argc, &argv, "no program filename");
206 if (npfile >= maxpfile) {
207 maxpfile += 20;
208 pfile = (char **)realloc(pfile, maxpfile * sizeof(*pfile));
209 if (pfile == NULL)
210 FATAL(
211 "error allocating space for -f "
212 "options"
213 );
214 }
215 pfile[npfile++] = fn;
216 break;
217 case 'F': /* set field separator */
218 fs = setfs(getarg(&argc, &argv, "no field separator"));
219 break;
220 case 'v': /* -v a=1 to be done NOW. one -v for each */
221 vn = getarg(&argc, &argv, "no variable name");
222 if (isclvar(vn))
223 setclvar(vn);
224 else
225 FATAL("invalid -v option argument: %s", vn);
226 break;
227 case 'd':
228 dbg = atoi(&argv[1][2]);
229 if (dbg == 0)
230 dbg = 1;
231 printf("awk %s\n", version);
232 break;
233 default:
234 WARNING("unknown option %s ignored", argv[1]);
235 break;
236 }
237 argc--;
238 argv++;
239 }
240
241 if (CSV && (fs != NULL || lookup("FS", symtab) != NULL))
242 WARNING("danger: don't set FS when --csv is in effect");
243
244 /* argv[1] is now the first argument */
245 if (npfile == 0) { /* no -f; first argument is program */
246 if (argc <= 1) {
247 if (dbg)
248 exit(0);
249 FATAL("no program given");
250 }
251 DPRINTF("program = |%s|\n", argv[1]);
252 lexprog = argv[1];
253 argc--;
254 argv++;
255 }
256 recinit(recsize);
257 syminit();
258 compile_time = COMPILING;
259 argv[0] = cmdname; /* put prog name at front of arglist */
260 DPRINTF("argc=%d, argv[0]=%s\n", argc, argv[0]);
261 arginit(argc, argv);
262 if (!safe)
263 envinit(environ);
264 yyparse();
265#if 0
266 /* doing this would comply with POSIX, but is not compatible with
267 * other awks and with what most users expect. so comment it out */
268 setlocale(LC_NUMERIC, ""); /* back to whatever it is locally */
269#endif
270 if (fs)
271 *FS = qstring(fs, '\0');
272 DPRINTF("errorflag=%d\n", errorflag);
273 if (errorflag == 0) {
274 compile_time = RUNNING;
275 run(winner);
276 } else
277 bracecheck();
278 return (errorflag);
279}
280
281int
282pgetc(void) /* get 1 character from awk program */
283{
284 int c;
285
286 for (;;) {
287 if (yyin == NULL) {
288 if (curpfile >= npfile)
289 return EOF;
290 if (strcmp(pfile[curpfile], "-") == 0)
291 yyin = stdin;
292 else if ((yyin = fopen(pfile[curpfile], "r")) == NULL)
293 FATAL("can't open file %s", pfile[curpfile]);
294 lineno = 1;
295 }
296 if ((c = getc(yyin)) != EOF)
297 return c;
298 if (yyin != stdin)
299 fclose(yyin);
300 yyin = NULL;
301 curpfile++;
302 }
303}
304
305char *
306cursource(void) /* current source file name */
307{
308 if (npfile > 0)
309 return pfile[curpfile < npfile ? curpfile : curpfile - 1];
310 else
311 return NULL;
312}