1/****************************************************************
2Copyright (C) Lucent Technologies 1997
3All Rights Reserved
4
5Permission to use, copy, modify, and distribute this software and
6its documentation for any purpose and without fee is hereby
7granted, provided that the above copyright notice appear in all
8copies and that both that the copyright notice and this
9permission notice and warranty disclaimer appear in supporting
10documentation, and that the name Lucent Technologies or any of
11its entities not be used in advertising or publicity pertaining
12to distribution of the software without specific, written prior
13permission.
14
15LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
17IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
18SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
20IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
21ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
22THIS 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 don't think so.
116void 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}