Add the DragonFly cvs id and perform general cleanups on cvs/rcs/sccs ids. Most
[dragonfly.git] / contrib / awk / field.c
1 /*
2  * field.c - routines for dealing with fields and record parsing
3  */
4
5 /* 
6  * Copyright (C) 1986, 1988, 1989, 1991-2000 the Free Software Foundation, Inc.
7  * 
8  * This file is part of GAWK, the GNU implementation of the
9  * AWK Programming Language.
10  * 
11  * GAWK 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 of the License, or
14  * (at your option) any later version.
15  * 
16  * GAWK 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; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
24  *
25  * $FreeBSD: src/contrib/awk/field.c,v 1.4.2.1 2001/01/23 22:08:31 asmodai Exp $
26  * $DragonFly: src/contrib/awk/Attic/field.c,v 1.2 2003/06/17 04:23:58 dillon Exp $
27  */
28
29 #include "awk.h"
30
31 typedef void (* Setfunc) P((long, char *, long, NODE *));
32
33 static long (*parse_field) P((long, char **, int, NODE *,
34                              Regexp *, Setfunc, NODE *));
35 static void rebuild_record P((void));
36 static long re_parse_field P((long, char **, int, NODE *,
37                              Regexp *, Setfunc, NODE *));
38 static long def_parse_field P((long, char **, int, NODE *,
39                               Regexp *, Setfunc, NODE *));
40 static long posix_def_parse_field P((long, char **, int, NODE *,
41                               Regexp *, Setfunc, NODE *));
42 static long null_parse_field P((long, char **, int, NODE *,
43                              Regexp *, Setfunc, NODE *));
44 static long sc_parse_field P((long, char **, int, NODE *,
45                              Regexp *, Setfunc, NODE *));
46 static long fw_parse_field P((long, char **, int, NODE *,
47                              Regexp *, Setfunc, NODE *));
48 static void set_element P((long num, char * str, long len, NODE *arr));
49 static void grow_fields_arr P((long num));
50 static void set_field P((long num, char *str, long len, NODE *dummy));
51
52
53 static char *parse_extent;      /* marks where to restart parse of record */
54 static long parse_high_water = 0; /* field number that we have parsed so far */
55 static long nf_high_water = 0;  /* size of fields_arr */
56 static int resave_fs;
57 static NODE *save_FS;           /* save current value of FS when line is read,
58                                  * to be used in deferred parsing
59                                  */
60 static int *FIELDWIDTHS = NULL;
61
62 NODE **fields_arr;              /* array of pointers to the field nodes */
63 int field0_valid;               /* $(>0) has not been changed yet */
64 int default_FS;                 /* TRUE when FS == " " */
65 Regexp *FS_regexp = NULL;
66 static NODE *Null_field = NULL;
67
68 /* using_FIELDWIDTHS --- static function, macro to avoid overhead */
69 #define using_FIELDWIDTHS()     (parse_field == fw_parse_field)
70
71 /* init_fields --- set up the fields array to start with */
72
73 void
74 init_fields()
75 {
76         NODE *n;
77
78         emalloc(fields_arr, NODE **, sizeof(NODE *), "init_fields");
79         getnode(n);
80         *n = *Nnull_string;
81         n->flags |= (SCALAR|FIELD);
82         n->flags &= ~PERM;
83         fields_arr[0] = n;
84         parse_extent = fields_arr[0]->stptr;
85         save_FS = dupnode(FS_node->var_value);
86         getnode(Null_field);
87         *Null_field = *Nnull_string;
88         Null_field->flags |= (SCALAR|FIELD);
89         Null_field->flags &= ~(NUM|NUMBER|MAYBE_NUM|PERM);
90         field0_valid = TRUE;
91 }
92
93 /* grow_fields --- acquire new fields as needed */
94
95 static void
96 grow_fields_arr(num)
97 long num;
98 {
99         register int t;
100         register NODE *n;
101
102         erealloc(fields_arr, NODE **, (num + 1) * sizeof(NODE *), "grow_fields_arr");
103         for (t = nf_high_water + 1; t <= num; t++) {
104                 getnode(n);
105                 *n = *Null_field;
106                 fields_arr[t] = n;
107         }
108         nf_high_water = num;
109 }
110
111 /* set_field --- set the value of a particular field */
112
113 /*ARGSUSED*/
114 static void
115 set_field(num, str, len, dummy)
116 long num;
117 char *str;
118 long len;
119 NODE *dummy;    /* not used -- just to make interface same as set_element */
120 {
121         register NODE *n;
122
123         if (num > nf_high_water)
124                 grow_fields_arr(num);
125         n = fields_arr[num];
126         n->stptr = str;
127         n->stlen = len;
128         n->flags = (STR|STRING|MAYBE_NUM|SCALAR|FIELD);
129 }
130
131 /* rebuild_record --- Someone assigned a value to $(something).
132                         Fix up $0 to be right */
133
134 static void
135 rebuild_record()
136 {
137         /*
138          * use explicit unsigned longs for lengths, in case
139          * a size_t isn't big enough.
140          */
141         register unsigned long tlen;
142         register unsigned long ofslen;
143         register NODE *tmp;
144         NODE *ofs;
145         char *ops;
146         register char *cops;
147         long i;
148
149         assert(NF != -1);
150
151         tlen = 0;
152         ofs = force_string(OFS_node->var_value);
153         ofslen = ofs->stlen;
154         for (i = NF; i > 0; i--) {
155                 tmp = fields_arr[i];
156                 tmp = force_string(tmp);
157                 tlen += tmp->stlen;
158         }
159         tlen += (NF - 1) * ofslen;
160         if ((long) tlen < 0)
161                 tlen = 0;
162         emalloc(ops, char *, tlen + 2, "rebuild_record");
163         cops = ops;
164         ops[0] = '\0';
165         for (i = 1;  i <= NF; i++) {
166                 tmp = fields_arr[i];
167                 /* copy field */
168                 if (tmp->stlen == 1)
169                         *cops++ = tmp->stptr[0];
170                 else if (tmp->stlen != 0) {
171                         memcpy(cops, tmp->stptr, tmp->stlen);
172                         cops += tmp->stlen;
173                 }
174                 /* copy OFS */
175                 if (i != NF) {
176                         if (ofslen == 1)
177                                 *cops++ = ofs->stptr[0];
178                         else if (ofslen != 0) {
179                                 memcpy(cops, ofs->stptr, ofslen);
180                                 cops += ofslen;
181                         }
182                 }
183         }
184         tmp = make_str_node(ops, tlen, ALREADY_MALLOCED);
185
186         /*
187          * Since we are about to unref fields_arr[0], we want to find
188          * any fields that still point into it, and have them point
189          * into the new field zero.
190          */
191         for (cops = ops, i = 1; i <= NF; i++) {
192                 if (fields_arr[i]->stlen > 0) {
193                         NODE *n;
194                         getnode(n);
195
196                         if ((fields_arr[i]->flags & FIELD) == 0) {
197                                 *n = *Null_field;
198                                 n->stlen = fields_arr[i]->stlen;
199                                 if ((fields_arr[i]->flags & (NUM|NUMBER)) != 0) {
200                                         n->flags |= (fields_arr[i]->flags & (NUM|NUMBER));
201                                         n->numbr = fields_arr[i]->numbr;
202                                 }
203                         } else {
204                                 *n = *(fields_arr[i]);
205                                 n->flags &= ~(MALLOC|TEMP|PERM|STRING);
206                         }
207
208                         n->stptr = cops;
209                         unref(fields_arr[i]);
210                         fields_arr[i] = n;
211                 }
212                 cops += fields_arr[i]->stlen + ofslen;
213         }
214
215         unref(fields_arr[0]);
216
217         fields_arr[0] = tmp;
218         field0_valid = TRUE;
219 }
220
221 /*
222  * set_record:
223  * setup $0, but defer parsing rest of line until reference is made to $(>0)
224  * or to NF.  At that point, parse only as much as necessary.
225  *
226  * Manage a private buffer for the contents of $0.  Doing so keeps us safe
227  * if `getline var' decides to rearrange the contents of the IOBUF that
228  * $0 might have been pointing into.  The cost is the copying of the buffer;
229  * but better correct than fast.
230  */
231 void
232 set_record(buf, cnt, freeold)
233 char *buf;              /* ignored if ! freeold */
234 int cnt;                /* ignored if ! freeold */
235 int freeold;
236 {
237         register int i;
238         NODE *n;
239         static char *databuf;
240         static unsigned long databuf_size;
241 #define INITIAL_SIZE    512
242 #define MAX_SIZE        ((unsigned long) ~0)    /* maximally portable ... */
243
244         NF = -1;
245         for (i = 1; i <= parse_high_water; i++) {
246                 unref(fields_arr[i]);
247                 getnode(n);
248                 *n = *Null_field;
249                 fields_arr[i] = n;
250         }
251
252         parse_high_water = 0;
253         /*
254          * $0 = $0 should resplit using the current value of FS, thus,
255          * this is executed orthogonally to the value of freeold.
256          */
257         if (resave_fs) {
258                 resave_fs = FALSE;
259                 unref(save_FS);
260                 save_FS = dupnode(FS_node->var_value);
261         }
262         if (freeold) {
263                 /* buffer management: */
264                 if (databuf_size == 0) {        /* first time */
265                         emalloc(databuf, char *, INITIAL_SIZE, "set_record");
266                         databuf_size = INITIAL_SIZE;
267                 }
268                 /*
269                  * Make sure there's enough room. Since we sometimes need
270                  * to place a sentinel at the end, we make sure
271                  * databuf_size is > cnt after allocation.
272                  */
273                 if (cnt >= databuf_size) {
274                         while (cnt >= databuf_size && databuf_size <= MAX_SIZE)
275                                 databuf_size *= 2;
276                         erealloc(databuf, char *, databuf_size, "set_record");
277                 }
278                 /* copy the data */
279                 memcpy(databuf, buf, cnt);
280
281                 /* manage field 0: */
282                 unref(fields_arr[0]);
283                 getnode(n);
284                 n->stptr = databuf;
285                 n->stlen = cnt;
286                 n->stref = 1;
287                 n->type = Node_val;
288                 n->stfmt = -1;
289                 n->flags = (STRING|STR|MAYBE_NUM|SCALAR|FIELD);
290                 fields_arr[0] = n;
291         }
292         fields_arr[0]->flags |= MAYBE_NUM;
293         field0_valid = TRUE;
294
295 #undef INITIAL_SIZE
296 #undef MAX_SIZE
297 }
298
299 /* reset_record --- start over again with current $0 */
300
301 void
302 reset_record()
303 {
304         (void) force_string(fields_arr[0]);
305         set_record(fields_arr[0]->stptr, fields_arr[0]->stlen, FALSE);
306 }
307
308 /* set_NF --- handle what happens to $0 and fields when NF is changed */
309
310 void
311 set_NF()
312 {
313         register int i;
314         NODE *n;
315
316         assert(NF != -1);
317
318         NF = (long) force_number(NF_node->var_value);
319         if (NF > nf_high_water)
320                 grow_fields_arr(NF);
321         if (parse_high_water < NF) {
322                 for (i = parse_high_water + 1; i <= NF; i++) {
323                         unref(fields_arr[i]);
324                         getnode(n);
325                         *n = *Null_field;
326                         fields_arr[i] = n;
327                 }
328         } else if (parse_high_water > 0) {
329                 for (i = NF + 1; i <= parse_high_water; i++) {
330                         unref(fields_arr[i]);
331                         getnode(n);
332                         *n = *Null_field;
333                         fields_arr[i] = n;
334                 }
335                 parse_high_water = NF;
336         }
337         field0_valid = FALSE;
338 }
339
340 /*
341  * re_parse_field --- parse fields using a regexp.
342  *
343  * This is called both from get_field() and from do_split()
344  * via (*parse_field)().  This variation is for when FS is a regular
345  * expression -- either user-defined or because RS=="" and FS==" "
346  */
347 static long
348 re_parse_field(up_to, buf, len, fs, rp, set, n)
349 long up_to;     /* parse only up to this field number */
350 char **buf;     /* on input: string to parse; on output: point to start next */
351 int len;
352 NODE *fs;
353 Regexp *rp;
354 Setfunc set;    /* routine to set the value of the parsed field */
355 NODE *n;
356 {
357         register char *scan = *buf;
358         register long nf = parse_high_water;
359         register char *field;
360         register char *end = scan + len;
361
362         if (up_to == HUGE)
363                 nf = 0;
364         if (len == 0)
365                 return nf;
366
367         if (RS_is_null && default_FS)
368                 while (scan < end && (*scan == ' ' || *scan == '\t' || *scan == '\n'))
369                         scan++;
370         field = scan;
371         while (scan < end
372                && research(rp, scan, 0, (end - scan), TRUE) != -1
373                && nf < up_to) {
374                 if (REEND(rp, scan) == RESTART(rp, scan)) {   /* null match */
375                         scan++;
376                         if (scan == end) {
377                                 (*set)(++nf, field, (long)(scan - field), n);
378                                 up_to = nf;
379                                 break;
380                         }
381                         continue;
382                 }
383                 (*set)(++nf, field,
384                        (long)(scan + RESTART(rp, scan) - field), n);
385                 scan += REEND(rp, scan);
386                 field = scan;
387                 if (scan == end)        /* FS at end of record */
388                         (*set)(++nf, field, 0L, n);
389         }
390         if (nf != up_to && scan < end) {
391                 (*set)(++nf, scan, (long)(end - scan), n);
392                 scan = end;
393         }
394         *buf = scan;
395         return (nf);
396 }
397
398 /*
399  * def_parse_field --- default field parsing.
400  *
401  * This is called both from get_field() and from do_split()
402  * via (*parse_field)().  This variation is for when FS is a single space
403  * character.
404  */
405
406 static long
407 def_parse_field(up_to, buf, len, fs, rp, set, n)
408 long up_to;     /* parse only up to this field number */
409 char **buf;     /* on input: string to parse; on output: point to start next */
410 int len;
411 NODE *fs;
412 Regexp *rp;
413 Setfunc set;    /* routine to set the value of the parsed field */
414 NODE *n;
415 {
416         register char *scan = *buf;
417         register long nf = parse_high_water;
418         register char *field;
419         register char *end = scan + len;
420         char sav;
421
422         if (up_to == HUGE)
423                 nf = 0;
424         if (len == 0)
425                 return nf;
426
427         /*
428          * Nasty special case. If FS set to "", return whole record
429          * as first field. This is not worth a separate function.
430          */
431         if (fs->stlen == 0) {
432                 (*set)(++nf, *buf, len, n);
433                 *buf += len;
434                 return nf;
435         }
436
437         /* before doing anything save the char at *end */
438         sav = *end;
439         /* because it will be destroyed now: */
440
441         *end = ' ';     /* sentinel character */
442         for (; nf < up_to; scan++) {
443                 /*
444                  * special case:  fs is single space, strip leading whitespace 
445                  */
446                 while (scan < end && (*scan == ' ' || *scan == '\t' || *scan == '\n'))
447                         scan++;
448                 if (scan >= end)
449                         break;
450                 field = scan;
451                 while (*scan != ' ' && *scan != '\t' && *scan != '\n')
452                         scan++;
453                 (*set)(++nf, field, (long)(scan - field), n);
454                 if (scan == end)
455                         break;
456         }
457
458         /* everything done, restore original char at *end */
459         *end = sav;
460
461         *buf = scan;
462         return nf;
463 }
464
465 /*
466  * posix_def_parse_field --- default field parsing.
467  *
468  * This is called both from get_field() and from do_split()
469  * via (*parse_field)().  This variation is for when FS is a single space
470  * character.  The only difference between this and def_parse_field()
471  * is that this one does not allow newlines to separate fields.
472  */
473
474 static long
475 posix_def_parse_field(up_to, buf, len, fs, rp, set, n)
476 long up_to;     /* parse only up to this field number */
477 char **buf;     /* on input: string to parse; on output: point to start next */
478 int len;
479 NODE *fs;
480 Regexp *rp;
481 Setfunc set;    /* routine to set the value of the parsed field */
482 NODE *n;
483 {
484         register char *scan = *buf;
485         register long nf = parse_high_water;
486         register char *field;
487         register char *end = scan + len;
488         char sav;
489
490         if (up_to == HUGE)
491                 nf = 0;
492         if (len == 0)
493                 return nf;
494
495         /*
496          * Nasty special case. If FS set to "", return whole record
497          * as first field. This is not worth a separate function.
498          */
499         if (fs->stlen == 0) {
500                 (*set)(++nf, *buf, len, n);
501                 *buf += len;
502                 return nf;
503         }
504
505         /* before doing anything save the char at *end */
506         sav = *end;
507         /* because it will be destroyed now: */
508
509         *end = ' ';     /* sentinel character */
510         for (; nf < up_to; scan++) {
511                 /*
512                  * special case:  fs is single space, strip leading whitespace 
513                  */
514                 while (scan < end && (*scan == ' ' || *scan == '\t'))
515                         scan++;
516                 if (scan >= end)
517                         break;
518                 field = scan;
519                 while (*scan != ' ' && *scan != '\t')
520                         scan++;
521                 (*set)(++nf, field, (long)(scan - field), n);
522                 if (scan == end)
523                         break;
524         }
525
526         /* everything done, restore original char at *end */
527         *end = sav;
528
529         *buf = scan;
530         return nf;
531 }
532
533 /*
534  * null_parse_field --- each character is a separate field
535  *
536  * This is called both from get_field() and from do_split()
537  * via (*parse_field)().  This variation is for when FS is the null string.
538  */
539 static long
540 null_parse_field(up_to, buf, len, fs, rp, set, n)
541 long up_to;     /* parse only up to this field number */
542 char **buf;     /* on input: string to parse; on output: point to start next */
543 int len;
544 NODE *fs;
545 Regexp *rp;
546 Setfunc set;    /* routine to set the value of the parsed field */
547 NODE *n;
548 {
549         register char *scan = *buf;
550         register long nf = parse_high_water;
551         register char *end = scan + len;
552
553         if (up_to == HUGE)
554                 nf = 0;
555         if (len == 0)
556                 return nf;
557
558         for (; nf < up_to && scan < end; scan++)
559                 (*set)(++nf, scan, 1L, n);
560
561         *buf = scan;
562         return nf;
563 }
564
565 /*
566  * sc_parse_field --- single character field separator
567  *
568  * This is called both from get_field() and from do_split()
569  * via (*parse_field)().  This variation is for when FS is a single character
570  * other than space.
571  */
572 static long
573 sc_parse_field(up_to, buf, len, fs, rp, set, n)
574 long up_to;     /* parse only up to this field number */
575 char **buf;     /* on input: string to parse; on output: point to start next */
576 int len;
577 NODE *fs;
578 Regexp *rp;
579 Setfunc set;    /* routine to set the value of the parsed field */
580 NODE *n;
581 {
582         register char *scan = *buf;
583         register char fschar;
584         register long nf = parse_high_water;
585         register char *field;
586         register char *end = scan + len;
587         int onecase;
588         char sav;
589
590         if (up_to == HUGE)
591                 nf = 0;
592         if (len == 0)
593                 return nf;
594
595         if (RS_is_null && fs->stlen == 0)
596                 fschar = '\n';
597         else
598                 fschar = fs->stptr[0];
599
600         onecase = (IGNORECASE && isalpha(fschar));
601         if (onecase)
602                 fschar = casetable[(int) fschar];
603
604         /* before doing anything save the char at *end */
605         sav = *end;
606         /* because it will be destroyed now: */
607         *end = fschar;  /* sentinel character */
608
609         for (; nf < up_to;) {
610                 field = scan;
611                 if (onecase) {
612                         while (casetable[(int) *scan] != fschar)
613                                 scan++;
614                 } else {
615                         while (*scan != fschar)
616                                 scan++;
617                 }
618                 (*set)(++nf, field, (long)(scan - field), n);
619                 if (scan == end)
620                         break;
621                 scan++;
622                 if (scan == end) {      /* FS at end of record */
623                         (*set)(++nf, field, 0L, n);
624                         break;
625                 }
626         }
627
628         /* everything done, restore original char at *end */
629         *end = sav;
630
631         *buf = scan;
632         return nf;
633 }
634
635 /*
636  * fw_parse_field --- field parsing using FIELDWIDTHS spec
637  *
638  * This is called both from get_field() and from do_split()
639  * via (*parse_field)().  This variation is for fields are fixed widths.
640  */
641 static long
642 fw_parse_field(up_to, buf, len, fs, rp, set, n)
643 long up_to;     /* parse only up to this field number */
644 char **buf;     /* on input: string to parse; on output: point to start next */
645 int len;
646 NODE *fs;
647 Regexp *rp;
648 Setfunc set;    /* routine to set the value of the parsed field */
649 NODE *n;
650 {
651         register char *scan = *buf;
652         register long nf = parse_high_water;
653         register char *end = scan + len;
654
655         if (up_to == HUGE)
656                 nf = 0;
657         if (len == 0)
658                 return nf;
659         for (; nf < up_to && (len = FIELDWIDTHS[nf+1]) != -1; ) {
660                 if (len > end - scan)
661                         len = end - scan;
662                 (*set)(++nf, scan, (long) len, n);
663                 scan += len;
664         }
665         if (len == -1)
666                 *buf = end;
667         else
668                 *buf = scan;
669         return nf;
670 }
671
672 /* get_field --- return a particular $n */
673
674 NODE **
675 get_field(requested, assign)
676 register long requested;
677 Func_ptr *assign;       /* this field is on the LHS of an assign */
678 {
679         /*
680          * if requesting whole line but some other field has been altered,
681          * then the whole line must be rebuilt
682          */
683         if (requested == 0) {
684                 if (! field0_valid) {
685                         /* first, parse remainder of input record */
686                         if (NF == -1) {
687                                 NF = (*parse_field)(HUGE-1, &parse_extent,
688                                         fields_arr[0]->stlen -
689                                         (parse_extent - fields_arr[0]->stptr),
690                                         save_FS, FS_regexp, set_field,
691                                         (NODE *) NULL);
692                                 parse_high_water = NF;
693                         }
694                         rebuild_record();
695                 }
696                 if (assign != NULL)
697                         *assign = reset_record;
698                 return &fields_arr[0];
699         }
700
701         /* assert(requested > 0); */
702
703         if (assign != NULL)
704                 field0_valid = FALSE;           /* $0 needs reconstruction */
705
706         if (requested <= parse_high_water)      /* already parsed this field */
707                 return &fields_arr[requested];
708
709         if (NF == -1) { /* have not yet parsed to end of record */
710                 /*
711                  * parse up to requested fields, calling set_field() for each,
712                  * saving in parse_extent the point where the parse left off
713                  */
714                 if (parse_high_water == 0)      /* starting at the beginning */
715                         parse_extent = fields_arr[0]->stptr;
716                 parse_high_water = (*parse_field)(requested, &parse_extent,
717                      fields_arr[0]->stlen - (parse_extent - fields_arr[0]->stptr),
718                      save_FS, FS_regexp, set_field, (NODE *) NULL);
719
720                 /*
721                  * if we reached the end of the record, set NF to the number of
722                  * fields so far.  Note that requested might actually refer to
723                  * a field that is beyond the end of the record, but we won't
724                  * set NF to that value at this point, since this is only a
725                  * reference to the field and NF only gets set if the field
726                  * is assigned to -- this case is handled below
727                  */
728                 if (parse_extent == fields_arr[0]->stptr + fields_arr[0]->stlen)
729                         NF = parse_high_water;
730                 if (requested == HUGE-1)        /* HUGE-1 means set NF */
731                         requested = parse_high_water;
732         }
733         if (parse_high_water < requested) { /* requested beyond end of record */
734                 if (assign != NULL) {   /* expand record */
735                         if (requested > nf_high_water)
736                                 grow_fields_arr(requested);
737
738                         NF = requested;
739                         parse_high_water = requested;
740                 } else
741                         return &Null_field;
742         }
743
744         return &fields_arr[requested];
745 }
746
747 /* set_element --- set an array element, used by do_split() */
748
749 static void
750 set_element(num, s, len, n)
751 long num;
752 char *s;
753 long len;
754 NODE *n;
755 {
756         register NODE *it;
757
758         it = make_string(s, len);
759         it->flags |= MAYBE_NUM;
760         *assoc_lookup(n, tmp_number((AWKNUM) (num))) = it;
761 }
762
763 /* do_split --- implement split(), semantics are same as for field splitting */
764
765 NODE *
766 do_split(tree)
767 NODE *tree;
768 {
769         NODE *src, *arr, *sep, *tmp;
770         NODE *fs;
771         char *s;
772         long (*parseit) P((long, char **, int, NODE *,
773                          Regexp *, Setfunc, NODE *));
774         Regexp *rp = NULL;
775
776         /*
777          * do dupnode(), to avoid problems like
778          *      x = split(a[1], a, "blah")
779          * since we assoc_clear the array. gack.
780          * this also gives us complete call by value semantics.
781          */
782         tmp = tree_eval(tree->lnode);
783         src = dupnode(tmp);
784         free_temp(tmp);
785
786         arr = tree->rnode->lnode;
787         if (tree->rnode->rnode != NULL)
788                 sep = tree->rnode->rnode->lnode;        /* 3rd arg */
789         else
790                 sep = NULL;
791
792         (void) force_string(src);
793
794         if (arr->type == Node_param_list)
795                 arr = stack_ptr[arr->param_cnt];
796         if (arr->type == Node_array_ref)
797                 arr = arr->orig_array;
798         if (arr->type != Node_var && arr->type != Node_var_array)
799                 fatal("second argument of split is not an array");
800         arr->type = Node_var_array;
801         assoc_clear(arr);
802
803         if ((sep->re_flags & FS_DFLT) != 0 && ! using_FIELDWIDTHS()) {
804                 parseit = parse_field;
805                 fs = force_string(FS_node->var_value);
806                 rp = FS_regexp;
807         } else {
808                 tmp = force_string(tree_eval(sep->re_exp));
809                 if (tmp->stlen == 0)
810                         parseit = null_parse_field;
811                 else if (tmp->stlen == 1 && (sep->re_flags & CONST) == 0) {
812                         if (tmp->stptr[0] == ' ') {
813                                 if (do_posix)
814                                         parseit = posix_def_parse_field;
815                                 else
816                                         parseit = def_parse_field;
817                         } else
818                                 parseit = sc_parse_field;
819                 } else {
820                         parseit = re_parse_field;
821                         rp = re_update(sep);
822                 }
823                 fs = tmp;
824         }
825
826         s = src->stptr;
827         tmp = tmp_number((AWKNUM) (*parseit)(HUGE, &s, (int) src->stlen,
828                                              fs, rp, set_element, arr));
829         unref(src);
830         free_temp(sep);
831         return tmp;
832 }
833
834 /* set_FIELDWIDTHS --- handle an assignment to FIELDWIDTHS */
835
836 void
837 set_FIELDWIDTHS()
838 {
839         register char *scan;
840         char *end;
841         register int i;
842         static int fw_alloc = 1;
843         static int warned = FALSE;
844         extern double strtod();
845
846         if (do_lint && ! warned) {
847                 warned = TRUE;
848                 warning("use of FIELDWIDTHS is a gawk extension");
849         }
850         if (do_traditional)     /* quick and dirty, does the trick */
851                 return;
852
853         /*
854          * If changing the way fields are split, obey least-suprise
855          * semantics, and force $0 to be split totally.
856          */
857         if (fields_arr != NULL)
858                 (void) get_field(HUGE - 1, 0);
859
860         parse_field = fw_parse_field;
861         scan = force_string(FIELDWIDTHS_node->var_value)->stptr;
862         end = scan + 1;
863         if (FIELDWIDTHS == NULL)
864                 emalloc(FIELDWIDTHS, int *, fw_alloc * sizeof(int), "set_FIELDWIDTHS");
865         FIELDWIDTHS[0] = 0;
866         for (i = 1; ; i++) {
867                 if (i >= fw_alloc) {
868                         fw_alloc *= 2;
869                         erealloc(FIELDWIDTHS, int *, fw_alloc * sizeof(int), "set_FIELDWIDTHS");
870                 }
871                 FIELDWIDTHS[i] = (int) strtod(scan, &end);
872                 if (end == scan)
873                         break;
874                 scan = end;
875         }
876         FIELDWIDTHS[i] = -1;
877 }
878
879 void
880 set_FS_if_not_FIELDWIDTHS()
881 {
882         if (parse_field != fw_parse_field)
883                 set_FS();
884 }
885
886 /* set_FS --- handle things when FS is assigned to */
887
888 void
889 set_FS()
890 {
891         char buf[10];
892         NODE *fs;
893         static NODE *save_fs = NULL;
894         static NODE *save_rs = NULL;
895
896         /*
897          * If changing the way fields are split, obey least-suprise
898          * semantics, and force $0 to be split totally.
899          */
900         if (fields_arr != NULL)
901                 (void) get_field(HUGE - 1, 0);
902
903         if (! (save_fs && cmp_nodes(FS_node->var_value, save_fs) == 0
904                 && save_rs && cmp_nodes(RS_node->var_value, save_rs) == 0)) {
905                 unref(save_fs);
906                 save_fs = dupnode(FS_node->var_value);
907                 unref(save_rs);
908                 save_rs = dupnode(RS_node->var_value);
909                 resave_fs = TRUE;
910                 if (FS_regexp) {
911                         refree(FS_regexp);
912                         FS_regexp = NULL;
913                 }
914         }
915         buf[0] = '\0';
916         default_FS = FALSE;
917         fs = force_string(FS_node->var_value);
918         if (! do_traditional && fs->stlen == 0)
919                 parse_field = null_parse_field;
920         else if (fs->stlen > 1)
921                 parse_field = re_parse_field;
922         else if (RS_is_null) {
923                 parse_field = sc_parse_field;
924                 if (fs->stlen == 1) {
925                         if (fs->stptr[0] == ' ') {
926                                 default_FS = TRUE;
927                                 strcpy(buf, "[ \t\n]+");
928                         } else if (fs->stptr[0] != '\n')
929                                 sprintf(buf, "[%c\n]", fs->stptr[0]);
930                 }
931         } else {
932                 if (do_posix)
933                         parse_field = posix_def_parse_field;
934                 else
935                         parse_field = def_parse_field;
936                 if (fs->stptr[0] == ' ' && fs->stlen == 1)
937                         default_FS = TRUE;
938                 else if (fs->stptr[0] != ' ' && fs->stlen == 1) {
939                         if (! IGNORECASE || ! isalpha(fs->stptr[0]))
940                                 parse_field = sc_parse_field;
941                         else if (fs->stptr[0] == '\\')
942                                 /* yet another special case */
943                                 strcpy(buf, "[\\\\]");
944                         else
945                                 sprintf(buf, "[%c]", fs->stptr[0]);
946                 }
947         }
948         if (buf[0] != '\0') {
949                 FS_regexp = make_regexp(buf, strlen(buf), IGNORECASE, TRUE);
950                 parse_field = re_parse_field;
951         } else if (parse_field == re_parse_field) {
952                 FS_regexp = make_regexp(fs->stptr, fs->stlen, IGNORECASE, TRUE);
953         } else
954                 FS_regexp = NULL;
955 }
956
957 /* using_fieldwidths --- is FS or FIELDWIDTHS in use? */
958
959 int
960 using_fieldwidths()
961 {
962         return using_FIELDWIDTHS();
963 }