Use standard INSTALL file, and replace old addresses in README.
[enscript.git] / states / prims.c
1 /*
2  * Primitive procedures for states.
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  * Types and definitions.
29  */
30
31 #define DEFUN(prim)                             \
32 static Node *                                   \
33 prim (prim_name, args, env, filename, linenum)  \
34      char *prim_name;                           \
35      List *args;                                \
36      Environment *env;                          \
37      char *filename;                            \
38      unsigned int linenum;
39
40 #define NEED_ARG()                                              \
41 do {                                                            \
42   if (arg == NULL)                                              \
43     {                                                           \
44       fprintf (stderr, _("%s:%d: %s: too few arguments\n"),     \
45                filename, linenum, prim_name);                   \
46       exit (1);                                                 \
47     }                                                           \
48 } while (0)
49
50 #define LAST_ARG()                                              \
51   do {                                                          \
52     if (arg != NULL)                                            \
53       {                                                         \
54         fprintf (stderr, _("%s:%d: %s: too many arguments\n"),  \
55                  filename, linenum, prim_name);                 \
56         exit (1);                                               \
57       }                                                         \
58   } while (0)
59
60 #define MATCH_ARG(type) \
61   match_arg (prim_name, type, &arg, env, filename, linenum)
62
63 #define APPEND(data, len)                               \
64   do {                                                  \
65     if (result_len < result_pos + (len))                \
66       {                                                 \
67         result_len += (len) + 1024;                     \
68         result = xrealloc (result, result_len);         \
69       }                                                 \
70     memcpy (result + result_pos, (data), (len));        \
71     result_pos += (len);                                \
72   } while (0)
73
74 #define FMTSPECIAL(ch) \
75      (('0' <= (ch) && (ch) <= '9') || (ch) == '.' || (ch) == '-')
76
77
78 /*
79  * Some forward protypes.
80  */
81
82 static Node *prim_print ___P ((char *prim_name, List *args,
83                                Environment *env, char *filename,
84                                unsigned int linenum));
85
86
87 /*
88  * Static functions.
89  */
90
91 static Node *
92 match_arg (prim_name, type, argp, env, filename, linenum)
93      char *prim_name;
94      NodeType type;
95      ListItem **argp;
96      Environment *env;
97      char *filename;
98      unsigned int linenum;
99 {
100   ListItem *arg = *argp;
101   Node *n;
102
103   NEED_ARG ();
104   n = eval_expr ((Expr *) arg->data, env);
105   if (type != nVOID && n->type != type)
106     {
107       fprintf (stderr, _("%s:%d: %s: illegal argument type\n"),
108                filename, linenum, prim_name);
109       exit (1);
110     }
111   *argp = arg->next;
112
113   return n;
114 }
115
116 /* Primitives. */
117
118 DEFUN (prim_call)
119 {
120   ListItem *arg = args->head;
121   Expr *e;
122   char *cp;
123
124   e = (Expr *) arg->data;
125   if (e->type != eSYMBOL)
126     {
127       fprintf (stderr, _("%s:%d: %s: illegal argument type\n"),
128                filename, linenum, prim_name);
129       exit (1);
130     }
131   cp = e->u.node->u.sym;
132
133   arg = arg->next;
134   LAST_ARG ();
135
136   return execute_state (cp);
137 }
138
139 DEFUN (prim_calln)
140 {
141   ListItem *arg = args->head;
142   Node *n;
143   char *cp;
144
145   n = MATCH_ARG (nSTRING);
146   LAST_ARG ();
147
148   cp = xmalloc (n->u.str.len + 1);
149   memcpy (cp, n->u.str.data, n->u.str.len);
150   cp[n->u.str.len] = '\0';
151
152   node_free (n);
153   n = execute_state (cp);
154   xfree (cp);
155
156   return n;
157 }
158
159
160 DEFUN (prim_check_namerules)
161 {
162   ListItem *arg = args->head;
163   ListItem *i;
164   Cons *c;
165   Node *n;
166
167   LAST_ARG ();
168
169   if (start_state)
170     goto return_false;
171
172   for (i = namerules->head; i; i = i->next)
173     {
174       c = (Cons *) i->data;
175       n = (Node *) c->car;
176
177       if (re_search (REGEXP (n), current_fname, strlen (current_fname),
178                      0, strlen (current_fname), NULL) >= 0)
179         {
180           /* This is it. */
181           n = (Node *) c->cdr;
182
183           start_state = n->u.sym;
184
185           n = node_alloc (nINTEGER);
186           n->u.integer = 1;
187           return n;
188         }
189     }
190
191 return_false:
192
193   n = node_alloc (nINTEGER);
194   n->u.integer = 0;
195
196   return n;
197 }
198
199
200 DEFUN (prim_check_startrules)
201 {
202   ListItem *arg = args->head;
203   ListItem *i;
204   Cons *c;
205   Node *n;
206
207   LAST_ARG ();
208
209   if (start_state)
210     goto return_false;
211
212   for (i = startrules->head; i; i = i->next)
213     {
214       c = (Cons *) i->data;
215       n = (Node *) c->car;
216
217       if (re_search (REGEXP (n), inbuf, data_in_buffer,
218                      0, data_in_buffer, NULL) >= 0)
219         {
220           /* This is it. */
221           n = (Node *) c->cdr;
222
223           start_state = n->u.sym;
224
225           n = node_alloc (nINTEGER);
226           n->u.integer = 1;
227           return n;
228         }
229     }
230
231 return_false:
232
233   n = node_alloc (nINTEGER);
234   n->u.integer = 0;
235
236   return n;
237 }
238
239
240 DEFUN (prim_concat)
241 {
242   ListItem *arg = args->head;
243   Node *n;
244   int len = 0;
245   char *data = NULL;
246
247   NEED_ARG ();
248   for (; arg; arg = arg->next)
249     {
250       n = eval_expr ((Expr *) arg->data, env);
251       if (n->type != nSTRING)
252         {
253           fprintf (stderr, _("%s:%d: %s: illegal argument type\n"),
254                    filename, linenum, prim_name);
255           exit (1);
256         }
257
258       if (n->u.str.len > 0)
259         {
260           data = (char *) xrealloc (data, len + n->u.str.len);
261           memcpy (data + len, n->u.str.data, n->u.str.len);
262           len += n->u.str.len;
263         }
264       node_free (n);
265     }
266
267   n = node_alloc (nSTRING);
268   n->u.str.data = data;
269   n->u.str.len = len;
270
271   return n;
272 }
273
274
275 DEFUN (prim_float)
276 {
277   ListItem *arg = args->head;
278   Node *n, *r;
279   char buf[512];
280
281   n = MATCH_ARG (nVOID);
282   LAST_ARG ();
283
284   r = node_alloc (nREAL);
285
286   switch (n->type)
287     {
288     case nVOID:
289     case nREGEXP:
290     case nSYMBOL:
291       r->u.real = 0.0;
292       break;
293
294     case nARRAY:
295       r->u.real = (double) n->u.array.len;
296       break;
297
298     case nSTRING:
299       if (n->u.str.len > sizeof (buf) - 1)
300         r->u.real = 0.0;
301       else
302         {
303           memcpy (buf, n->u.str.data, n->u.str.len);
304           buf[n->u.str.len] = '\0';
305           r->u.real = atof (buf);
306         }
307       break;
308
309     case nINTEGER:
310       r->u.real = (double) n->u.integer;
311       break;
312
313     case nREAL:
314       r->u.real = n->u.real;
315       break;
316     }
317
318   node_free (n);
319   return r;
320 }
321
322
323 DEFUN (prim_getenv)
324 {
325   ListItem *arg = args->head;
326   Node *var, *n;
327   char *key;
328   char *cp;
329
330   var = MATCH_ARG (nSTRING);
331   LAST_ARG ();
332
333   key = (char *) xcalloc (1, var->u.str.len + 1);
334   memcpy (key, var->u.str.data, var->u.str.len);
335
336   cp = getenv (key);
337
338   node_free (var);
339   xfree (key);
340
341   n = node_alloc (nSTRING);
342   if (cp == NULL)
343     {
344       n->u.str.data = (char *) xmalloc (1);
345       n->u.str.len = 0;
346     }
347   else
348     {
349       n->u.str.data = xstrdup (cp);
350       n->u.str.len = strlen (cp);
351     }
352
353   return n;
354 }
355
356
357 DEFUN (prim_int)
358 {
359   ListItem *arg = args->head;
360   Node *n, *r;
361   char buf[512];
362
363   n = MATCH_ARG (nVOID);
364   LAST_ARG ();
365
366   r = node_alloc (nINTEGER);
367
368   switch (n->type)
369     {
370     case nVOID:
371     case nREGEXP:
372     case nSYMBOL:
373       r->u.integer = 0;
374       break;
375
376     case nARRAY:
377       r->u.integer = n->u.array.len;
378       break;
379
380     case nSTRING:
381       if (n->u.str.len > sizeof (buf) - 1)
382         r->u.integer = 0;
383       else
384         {
385           memcpy (buf, n->u.str.data, n->u.str.len);
386           buf[n->u.str.len] = '\0';
387           r->u.integer = atoi (buf);
388         }
389       break;
390
391     case nINTEGER:
392       r->u.integer = n->u.integer;
393       break;
394
395     case nREAL:
396       r->u.integer = (int) n->u.real;
397       break;
398     }
399
400   node_free (n);
401   return r;
402 }
403
404
405 DEFUN (prim_length)
406 {
407   ListItem *arg = args->head;
408   Node *n;
409   int result = 0;
410
411   NEED_ARG ();
412   for (; arg; arg = arg->next)
413     {
414       n = eval_expr ((Expr *) arg->data, env);
415       switch (n->type)
416         {
417         case nSTRING:
418           result += n->u.str.len;
419           break;
420
421         case nARRAY:
422           result += n->u.array.len;
423           break;
424
425         default:
426           fprintf (stderr, _("%s:%d: %s: illegal argument type\n"),
427                    filename, linenum, prim_name);
428           exit (1);
429           break;
430         }
431       node_free (n);
432     }
433
434   n = node_alloc (nINTEGER);
435   n->u.integer = result;
436
437   return n;
438 }
439
440
441 DEFUN (prim_list)
442 {
443   ListItem *arg = args->head;
444   unsigned int len;
445   Node *n;
446
447   /* Count list length. */
448   for (len = 0; arg; len++, arg = arg->next)
449     ;
450   arg = args->head;
451
452   /* Create list node. */
453   n = node_alloc (nARRAY);
454   n->u.array.array = (Node **) xcalloc (len + 1, sizeof (Node *));
455   n->u.array.allocated = len + 1;
456   n->u.array.len = len;
457
458   /* Fill it up. */
459   for (len = 0; arg; len++, arg = arg->next)
460     n->u.array.array[len] = eval_expr ((Expr *) arg->data, env);
461
462   return n;
463 }
464
465
466 DEFUN (prim_panic)
467 {
468   fprintf (stderr, _("%s: panic: "), program);
469   ofp = stderr;
470   prim_print (prim_name, args, env, filename, linenum);
471   fprintf (stderr, "\n");
472   exit (1);
473
474   /* NOTREACHED */
475   return nvoid;
476 }
477
478
479 DEFUN (prim_prereq)
480 {
481   ListItem *arg = args->head;
482   Node *s;
483   int over[3];
484   int rver[3];
485   char *cp;
486   int i;
487
488   s = MATCH_ARG (nSTRING);
489   LAST_ARG ();
490
491   /* Our version. */
492   sscanf (VERSION, "%d.%d.%d", &over[0], &over[1], &over[2]);
493
494   /* Required version. */
495
496   cp = (char *) xcalloc (1, s->u.str.len + 1);
497   memcpy (cp, s->u.str.data, s->u.str.len);
498
499   if (sscanf (cp, "%d.%d.%d", &rver[0], &rver[1], &rver[2]) != 3)
500     {
501       fprintf (stderr,
502                _("%s:%d: %s: malformed version string `%s'\n"),
503                filename, linenum, prim_name, cp);
504       exit (1);
505     }
506
507   /* Check version. */
508   for (i = 0; i < 3; i++)
509     {
510       if (over[i] > rver[i])
511         /* Ok, our version is bigger. */
512         break;
513       if (over[i] < rver[i])
514         {
515           /* Fail, our version is too small. */
516           fprintf (stderr,
517                    _("%s: FATAL ERROR: States version %s or higher is required for this script\n"),
518                    program, cp);
519           exit (1);
520         }
521     }
522
523   /* Our version is higher or equal to the required one. */
524   xfree (cp);
525
526   return nvoid;
527 }
528
529
530 static void
531 print_node (n)
532      Node *n;
533 {
534   unsigned int i;
535
536   switch (n->type)
537     {
538     case nVOID:
539       break;
540
541     case nSTRING:
542       fwrite (n->u.str.data, n->u.str.len, 1, ofp);
543       break;
544
545     case nREGEXP:
546       fputc ('/', ofp);
547       fwrite (n->u.re.data, n->u.re.len, 1, ofp);
548       fputc ('/', ofp);
549       break;
550
551     case nINTEGER:
552       fprintf (ofp, "%d", n->u.integer);
553       break;
554
555     case nREAL:
556       fprintf (ofp, "%f", n->u.real);
557       break;
558
559     case nSYMBOL:
560       fprintf (ofp, "%s", n->u.sym);
561       break;
562
563     case nARRAY:
564       for (i = 0; i < n->u.array.len; i++)
565         {
566           print_node (n->u.array.array[i]);
567           if (i + 1 < n->u.array.len)
568             fprintf (ofp, " ");
569         }
570     }
571 }
572
573
574 DEFUN (prim_print)
575 {
576   ListItem *arg = args->head;
577   Node *n;
578
579   NEED_ARG ();
580   for (; arg; arg = arg->next)
581     {
582       n = eval_expr ((Expr *) arg->data, env);
583       print_node (n);
584       node_free (n);
585     }
586
587   return nvoid;
588 }
589
590
591 DEFUN (prim_range)
592 {
593   ListItem *arg = args->head;
594   Node *from, *start, *end, *n;
595   int i;
596
597   NEED_ARG ();
598   from = eval_expr ((Expr *) arg->data, env);
599   arg = arg->next;
600
601   start = MATCH_ARG (nINTEGER);
602   end = MATCH_ARG (nINTEGER);
603   LAST_ARG ();
604
605   if (start->u.integer > end->u.integer)
606     {
607       fprintf (stderr,
608                _("%s:%d: %s: start offset is bigger than end offset\n"),
609                filename, linenum, prim_name);
610       exit (1);
611     }
612
613   if (from->type == nSTRING)
614     {
615       if (end->u.integer > from->u.str.len)
616         {
617           fprintf (stderr, _("%s:%d: %s: offset out of range\n"),
618                    filename, linenum, prim_name);
619           exit (1);
620         }
621
622       n = node_alloc (nSTRING);
623       n->u.str.len = end->u.integer - start->u.integer;
624       /* +1 to avoid zero allocation */
625       n->u.str.data = (char *) xmalloc (n->u.str.len + 1);
626       memcpy (n->u.str.data, from->u.str.data + start->u.integer,
627               n->u.str.len);
628     }
629   else if (from->type == nARRAY)
630     {
631       if (end->u.integer > from->u.array.len)
632         {
633           fprintf (stderr, _("%s:%d: %s: offset out of range\n"),
634                    filename, linenum, prim_name);
635           exit (1);
636         }
637
638       n = node_alloc (nARRAY);
639       n->u.array.len = end->u.integer - start->u.integer;
640       /* +1 to avoid zero allocation */
641       n->u.array.allocated = n->u.array.len + 1;
642       n->u.array.array = (Node **) xcalloc (n->u.array.allocated,
643                                             sizeof (Node *));
644
645       for (i = 0; i < n->u.array.len; i++)
646         n->u.array.array[i]
647           = node_copy (from->u.array.array[i + start->u.integer]);
648     }
649   else
650     {
651       fprintf (stderr, _("%s:%d: %s: illegal argument\n"),
652                filename, linenum, prim_name);
653       exit (1);
654     }
655
656   node_free (from);
657   node_free (start);
658   node_free (end);
659
660   return n;
661 }
662
663
664 DEFUN (prim_regexp)
665 {
666   ListItem *arg = args->head;
667   Node *str, *n;
668
669   str = MATCH_ARG (nSTRING);
670   LAST_ARG ();
671
672   /* Create a new REGEXP node. */
673
674   n = node_alloc (nREGEXP);
675   n->u.re.data = xmalloc (str->u.str.len + 1);
676   n->u.re.len = str->u.str.len;
677   memcpy (n->u.re.data, str->u.str.data, str->u.str.len);
678   n->u.re.data[str->u.str.len] = '\0';
679
680   return n;
681 }
682
683
684 DEFUN (prim_regexp_syntax)
685 {
686   ListItem *arg = args->head;
687   Node *ch, *st;
688   char syntax;
689
690   ch = MATCH_ARG (nINTEGER);
691   st = MATCH_ARG (nINTEGER);
692   LAST_ARG ();
693
694   syntax = (char) st->u.integer;
695   if (syntax != 'w' && syntax != ' ')
696     {
697       fprintf (stderr,
698                _("%s:%d: %s: illegal regexp character syntax: %c\n"),
699                filename, linenum, prim_name, syntax);
700       exit (1);
701     }
702
703   re_set_character_syntax ((unsigned char) ch->u.integer, syntax);
704
705   return nvoid;
706 }
707
708
709 DEFUN (prim_regmatch)
710 {
711   ListItem *arg = args->head;
712   Node *str, *re, *n;
713   static struct re_registers matches = {0, NULL, NULL};
714   static Node *current_match_node = NULL;
715   int i;
716
717   str = MATCH_ARG (nSTRING);
718   re = MATCH_ARG (nREGEXP);
719   LAST_ARG ();
720
721   /* Search for match. */
722   i = re_search (REGEXP (re), str->u.str.data, str->u.str.len,
723                  0, str->u.str.len, &matches);
724
725   if (i < 0)
726     {
727       current_match = NULL;
728       node_free (str);
729     }
730   else
731     {
732       node_free (current_match_node);
733       current_match_node = str;
734
735       current_match = &matches;
736       current_match_buf = str->u.str.data;
737     }
738   node_free (re);
739
740   n = node_alloc (nINTEGER);
741   n->u.integer = (i >= 0);
742
743   return n;
744 }
745
746
747 /*
748  * Common regular expression substituter for regsub and regsuball.
749  */
750
751 Node *
752 do_regsubsts (str, re, subst, allp)
753      Node *str;
754      Node *re;
755      Node *subst;
756      int allp;
757 {
758   int i, pos, j;
759   static struct re_registers matches = {0, NULL, NULL};
760   static char *result = NULL;
761   static unsigned int result_len = 0;
762   unsigned int result_pos = 0;
763   int num_matches = 0;
764   int do_expansions_in_substs = 0;
765
766   /* Do we have to do expansions in the substitution string. */
767   for (i = 0; i < subst->u.str.len; i++)
768     if (subst->u.str.data[i] == '$')
769       {
770         do_expansions_in_substs = 1;
771         break;
772       }
773
774   pos = 0;
775   while (1)
776     {
777       /* Search for match. */
778       i = re_search (REGEXP (re), str->u.str.data, str->u.str.len,
779                      pos, str->u.str.len - pos, &matches);
780       if (i < 0)
781         goto out;
782
783       num_matches++;
784
785       /* Everything before match. */
786       APPEND (str->u.str.data + pos, matches.start[0] - pos);
787
788       /* Append match. */
789       if (!do_expansions_in_substs)
790         APPEND (subst->u.str.data, subst->u.str.len);
791       else
792         {
793           /* Must process substitution string. */
794           for (i = 0; i < subst->u.str.len; i++)
795             if (subst->u.str.data[i] == '$' && i + 1 < subst->u.str.len)
796               {
797                 i++;
798                 switch (subst->u.str.data[i])
799                   {
800                   case '$':
801                     APPEND ("$", 1);
802                     break;
803
804                   case '0':
805                   case '1':
806                   case '2':
807                   case '3':
808                   case '4':
809                   case '5':
810                   case '6':
811                   case '7':
812                   case '8':
813                   case '9':
814                     j = subst->u.str.data[i] - '0';
815                     if (matches.start[j] >= 0)
816                       APPEND (str->u.str.data + matches.start[j],
817                               matches.end[j] - matches.start[j]);
818                     break;
819
820                   default:
821                     /* Illegal substitution, just pass it through. */
822                     APPEND ("$", 1);
823                     APPEND (subst->u.str.data + i, 1);
824                     break;
825                   }
826               }
827             else
828               APPEND (subst->u.str.data + i, 1);
829         }
830
831       /* Update pos. */
832       pos = matches.end[0];
833
834       if (!allp)
835         break;
836     }
837
838 out:
839   if (num_matches == 0)
840     {
841       /* No matches, just return our original string. */
842       node_free (re);
843       node_free (subst);
844       return str;
845     }
846
847   APPEND (str->u.str.data + pos, str->u.str.len - pos);
848
849   /* Create result node. */
850   node_free (str);
851   node_free (re);
852   node_free (subst);
853
854   str = node_alloc (nSTRING);
855   str->u.str.len = result_pos;
856   str->u.str.data = xmalloc (result_pos);
857   memcpy (str->u.str.data, result, result_pos);
858
859   return str;
860 }
861
862
863 DEFUN (prim_regsub)
864 {
865   ListItem *arg = args->head;
866   Node *str, *re, *subst;
867
868   str = MATCH_ARG (nSTRING);
869   re = MATCH_ARG (nREGEXP);
870   subst = MATCH_ARG (nSTRING);
871   LAST_ARG ();
872
873   return do_regsubsts (str, re, subst, 0);
874 }
875
876
877 DEFUN (prim_regsuball)
878 {
879   ListItem *arg = args->head;
880   Node *str, *re, *subst;
881
882   str = MATCH_ARG (nSTRING);
883   re = MATCH_ARG (nREGEXP);
884   subst = MATCH_ARG (nSTRING);
885   LAST_ARG ();
886
887   return do_regsubsts (str, re, subst, 1);
888 }
889
890
891 DEFUN (prim_require_state)
892 {
893   ListItem *arg = args->head;
894   Expr *e;
895   char *cp;
896   State *state;
897
898   e = (Expr *) arg->data;
899   if (e->type != eSYMBOL)
900     {
901       fprintf (stderr, _("%s:%d: %s: illegal argument type\n"),
902                filename, linenum, prim_name);
903       exit (1);
904     }
905   cp = e->u.node->u.sym;
906
907   arg = arg->next;
908   LAST_ARG ();
909
910   state = lookup_state (cp);
911   if (state == NULL)
912     {
913       fprintf (stderr, _("%s:%d: %s: couldn't define state `%s'\n"),
914                filename, linenum, prim_name, cp);
915       exit (1);
916     }
917
918   return nvoid;
919 }
920
921
922 DEFUN (prim_split)
923 {
924   ListItem *arg = args->head;
925   Node *re, *str, *n, *n2;
926   int pos, i;
927
928   re = MATCH_ARG (nREGEXP);
929   str = MATCH_ARG (nSTRING);
930   LAST_ARG ();
931
932   /* Create a new array node. */
933   n = node_alloc (nARRAY);
934   n->u.array.allocated = 100;
935   n->u.array.array = (Node **) xcalloc (n->u.array.allocated, sizeof (Node *));
936
937   for (pos = 0; pos < str->u.str.len;)
938     {
939       i = re_search (REGEXP (re), str->u.str.data, str->u.str.len,
940                      pos, str->u.str.len - pos, &re->u.re.matches);
941       if (i < 0)
942         /* No more matches. */
943         break;
944
945       /* Append the string before the first match. */
946       n2 = node_alloc (nSTRING);
947       n2->u.str.len = i - pos;
948       n2->u.str.data = (char *) xmalloc (n2->u.str.len + 1);
949       memcpy (n2->u.str.data, str->u.str.data + pos, n2->u.str.len);
950       pos = re->u.re.matches.end[0];
951
952       /*
953        * Check that at least one item fits after us (no need to check
954        * when appending the last item).
955        */
956       if (n->u.array.len + 1 >= n->u.array.allocated)
957         {
958           n->u.array.allocated += 100;
959           n->u.array.array = (Node **) xrealloc (n->u.array.array,
960                                                  n->u.array.allocated
961                                                  * sizeof (Node *));
962         }
963       n->u.array.array[n->u.array.len++] = n2;
964     }
965
966   /* Append all the remaining data. */
967   n2 = node_alloc (nSTRING);
968   n2->u.str.len = str->u.str.len - pos;
969   n2->u.str.data = (char *) xmalloc (n2->u.str.len + 1);
970   memcpy (n2->u.str.data, str->u.str.data + pos, n2->u.str.len);
971
972   n->u.array.array[n->u.array.len++] = n2;
973
974   return n;
975 }
976
977
978 DEFUN (prim_sprintf)
979 {
980   ListItem *arg = args->head;
981   Node *fmt, *n;
982   char buf[512];
983   char ifmt[256];
984   char ifmtopts[256];
985   char *result = NULL;
986   unsigned int result_pos = 0;
987   unsigned int result_len = 0;
988   int i, j;
989   int argument_count = 0;
990   char *cp;
991
992   fmt = MATCH_ARG (nSTRING);
993   cp = fmt->u.str.data;
994
995   /* Process format string and match arguments. */
996   for (i = 0; i < fmt->u.str.len; i++)
997     {
998       if (cp[i] == '%' && (i + 1 >= fmt->u.str.len || cp[i + 1] == '%'))
999         {
1000           i++;
1001           APPEND (cp + i, 1);
1002         }
1003       else if (cp[i] == '%')
1004         {
1005           argument_count++;
1006
1007           if (arg == NULL)
1008             {
1009               fprintf (stderr,
1010                        _("%s: primitive `%s': too few arguments for format\n"),
1011                        program, prim_name);
1012               exit (1);
1013             }
1014           n = eval_expr ((Expr *) arg->data, env);
1015           arg = arg->next;
1016
1017           for (i++, j = 0; i < fmt->u.str.len && FMTSPECIAL (cp[i]); i++, j++)
1018             ifmtopts[j] = cp[i];
1019           ifmtopts[j] = '\0';
1020
1021           if (i >= fmt->u.str.len)
1022             {
1023               APPEND ("%", 1);
1024               APPEND (ifmtopts, j);
1025               continue;
1026             }
1027
1028           /* Field type. */
1029           switch (cp[i])
1030             {
1031             case 'x':
1032             case 'X':
1033             case 'd':
1034               if (n->type != nINTEGER)
1035                 {
1036                 no_match:
1037                   fprintf (stderr,
1038                            _("%s:%d: %s: argument %d doesn't match format\n"),
1039                            filename, linenum, prim_name, argument_count);
1040                   exit (1);
1041                 }
1042               sprintf (ifmt, "%%%s%c", ifmtopts, cp[i]);
1043               sprintf (buf, ifmt, n->u.integer);
1044
1045               APPEND (buf, strlen (buf));
1046               break;
1047
1048             case 'c':
1049               if (n->type != nINTEGER)
1050                 goto no_match;
1051
1052               sprintf (ifmt, "%%%s%c", ifmtopts, cp[i]);
1053               sprintf (buf, ifmt, n->u.integer);
1054
1055               APPEND (buf, strlen (buf));
1056               break;
1057
1058             case 'f':
1059             case 'g':
1060             case 'e':
1061             case 'E':
1062               if (n->type != nREAL)
1063                 goto no_match;
1064
1065               sprintf (ifmt, "%%%s%c", ifmtopts, cp[i]);
1066               sprintf (buf, ifmt, n->u.real);
1067
1068               APPEND (buf, strlen (buf));
1069               break;
1070
1071             case 's':
1072               if (n->type != nSTRING)
1073                 goto no_match;
1074
1075               if (ifmtopts[0] != '\0')
1076                 {
1077                   fprintf (stderr,
1078                            _("%s:%d: %s: no extra options can be specified for %%s\n"),
1079                            filename, linenum, prim_name);
1080                   exit (1);
1081                 }
1082               APPEND (n->u.str.data, n->u.str.len);
1083               break;
1084
1085             default:
1086               fprintf (stderr,
1087                        _("%s:%d: %s: illegal type specifier `%c'\n"),
1088                        filename, linenum, prim_name, cp[i]);
1089               exit (1);
1090               break;
1091             }
1092         }
1093       else
1094         APPEND (cp + i, 1);
1095     }
1096
1097   node_free (fmt);
1098
1099   n = node_alloc (nSTRING);
1100   n->u.str.len = result_pos;
1101   n->u.str.data = result;
1102
1103   return n;
1104 }
1105
1106 \f
1107 DEFUN (prim_strcmp)
1108 {
1109   ListItem *arg = args->head;
1110   Node *s1, *s2;
1111   Node *n;
1112   int i, result;
1113   char *cp1, *cp2;
1114
1115   s1 = MATCH_ARG (nSTRING);
1116   s2 = MATCH_ARG (nSTRING);
1117   LAST_ARG ();
1118
1119   cp1 = s1->u.str.data;
1120   cp2 = s2->u.str.data;
1121
1122   for (i = 0; i < s1->u.str.len && i < s2->u.str.len; i++)
1123     {
1124       if (cp1[i] < cp2[i])
1125         {
1126           result = -1;
1127           goto out;
1128         }
1129       if (cp1[i] > cp2[i])
1130         {
1131           result = 1;
1132           goto out;
1133         }
1134     }
1135   /* Strings are so far equal, check lengths. */
1136   if (s1->u.str.len < s2->u.str.len)
1137     result = -1;
1138   else if (s1->u.str.len > s2->u.str.len)
1139     result = 1;
1140   else
1141     result = 0;
1142
1143 out:
1144   node_free (s1);
1145   node_free (s2);
1146   n = node_alloc (nINTEGER);
1147   n->u.integer = result;
1148
1149   return n;
1150 }
1151
1152
1153 DEFUN (prim_string)
1154 {
1155   ListItem *arg = args->head;
1156   Node *n, *r;
1157   char buf[512];
1158
1159   n = MATCH_ARG (nVOID);
1160   LAST_ARG ();
1161
1162   r = node_alloc (nSTRING);
1163
1164   switch (n->type)
1165     {
1166     case nVOID:
1167     case nREGEXP:
1168     case nARRAY:
1169       r->u.str.data = (char *) xcalloc (1, 1);
1170       r->u.str.len = 0;
1171       break;
1172
1173     case nSYMBOL:
1174       r->u.str.len = strlen (n->u.sym);
1175       r->u.str.data = (char *) xmalloc (r->u.str.len);
1176       memcpy (r->u.str.data, n->u.sym, r->u.str.len);
1177       break;
1178
1179     case nSTRING:
1180       r->u.str.len = n->u.str.len;
1181       r->u.str.data = (char *) xmalloc (n->u.str.len);
1182       memcpy (r->u.str.data, n->u.str.data, n->u.str.len);
1183       break;
1184
1185     case nINTEGER:
1186       sprintf (buf, "%d", n->u.integer);
1187       r->u.str.len = strlen (buf);
1188       r->u.str.data = (char *) xmalloc (r->u.str.len);
1189       memcpy (r->u.str.data, buf, r->u.str.len);
1190       break;
1191
1192     case nREAL:
1193       sprintf (buf, "%f", n->u.real);
1194       r->u.str.len = strlen (buf);
1195       r->u.str.data = (char *) xmalloc (r->u.str.len);
1196       memcpy (r->u.str.data, buf, r->u.str.len);
1197       break;
1198     }
1199
1200   node_free (n);
1201   return r;
1202 }
1203
1204
1205 DEFUN (prim_strncmp)
1206 {
1207   ListItem *arg = args->head;
1208   Node *s1, *s2, *len;
1209   Node *n;
1210   int i, result;
1211   char *cp1, *cp2;
1212
1213   s1 = MATCH_ARG (nSTRING);
1214   s2 = MATCH_ARG (nSTRING);
1215   len = MATCH_ARG (nINTEGER);
1216   LAST_ARG ();
1217
1218   cp1 = s1->u.str.data;
1219   cp2 = s2->u.str.data;
1220
1221   for (i = 0; i < s1->u.str.len && i < s2->u.str.len && i < len->u.integer; i++)
1222     {
1223       if (cp1[i] < cp2[i])
1224         {
1225           result = -1;
1226           goto out;
1227         }
1228       if (cp1[i] > cp2[i])
1229         {
1230           result = 1;
1231           goto out;
1232         }
1233     }
1234
1235   /* Check the limit length. */
1236   if (i >= len->u.integer)
1237     {
1238       result = 0;
1239       goto out;
1240     }
1241
1242   /* One or both strings were shorter than limit, check lengths. */
1243   if (s1->u.str.len < s2->u.str.len)
1244     result = -1;
1245   else if (s1->u.str.len > s2->u.str.len)
1246     result = 1;
1247   else
1248     result = 0;
1249
1250 out:
1251   node_free (s1);
1252   node_free (s2);
1253   node_free (len);
1254   n = node_alloc (nINTEGER);
1255   n->u.integer = result;
1256
1257   return n;
1258 }
1259
1260
1261 DEFUN (prim_substring)
1262 {
1263   ListItem *arg = args->head;
1264   Node *str, *start, *end, *n;
1265
1266   str = MATCH_ARG (nSTRING);
1267   start = MATCH_ARG (nINTEGER);
1268   end = MATCH_ARG (nINTEGER);
1269   LAST_ARG ();
1270
1271   if (start->u.integer > end->u.integer)
1272     {
1273       fprintf (stderr,
1274                _("%s:%d: %s: start offset is bigger than end offset\n"),
1275                filename, linenum, prim_name);
1276       exit (1);
1277     }
1278   if (end->u.integer > str->u.str.len)
1279     {
1280       fprintf (stderr, _("%s:%d: %s: offset out of range\n"),
1281                filename, linenum, prim_name);
1282       exit (1);
1283     }
1284
1285   n = node_alloc (nSTRING);
1286   n->u.str.len = end->u.integer - start->u.integer;
1287   /* +1 to avoid zero allocation */
1288   n->u.str.data = (char *) xmalloc (n->u.str.len + 1);
1289
1290   memcpy (n->u.str.data, str->u.str.data + start->u.integer,
1291           n->u.str.len);
1292
1293   node_free (str);
1294   node_free (start);
1295   node_free (end);
1296
1297   return n;
1298 }
1299
1300
1301 DEFUN (prim_system)
1302 {
1303   ListItem *arg = args->head;
1304   Node *str, *n;
1305   char *cmd;
1306   int result;
1307
1308   str = MATCH_ARG (nSTRING);
1309   LAST_ARG ();
1310
1311   cmd = (char *) xcalloc (1, str->u.str.len + 1);
1312   memcpy (cmd, str->u.str.data, str->u.str.len);
1313
1314   result = system (cmd);
1315   xfree (cmd);
1316
1317   n = node_alloc (nINTEGER);
1318   n->u.integer = result;
1319
1320   return n;
1321 }
1322
1323
1324 /*
1325  * Global functions.
1326  */
1327
1328 static struct
1329 {
1330   char *name;
1331   Primitive prim;
1332 } prims[] =
1333   {
1334     {"call",                    prim_call},
1335     {"calln",                   prim_calln},
1336     {"check_namerules",         prim_check_namerules},
1337     {"check_startrules",        prim_check_startrules},
1338     {"concat",                  prim_concat},
1339     {"float",                   prim_float},
1340     {"getenv",                  prim_getenv},
1341     {"int",                     prim_int},
1342     {"length",                  prim_length},
1343     {"list",                    prim_list},
1344     {"panic",                   prim_panic},
1345     {"prereq",                  prim_prereq},
1346     {"print",                   prim_print},
1347     {"range",                   prim_range},
1348     {"regexp",                  prim_regexp},
1349     {"regexp_syntax",           prim_regexp_syntax},
1350     {"regmatch",                prim_regmatch},
1351     {"regsub",                  prim_regsub},
1352     {"regsuball",               prim_regsuball},
1353     {"require_state",           prim_require_state},
1354     {"split",                   prim_split},
1355     {"sprintf",                 prim_sprintf},
1356     {"strcmp",                  prim_strcmp},
1357     {"string",                  prim_string},
1358     {"strncmp",                 prim_strncmp},
1359     {"substring",               prim_substring},
1360     {"system",                  prim_system},
1361
1362     {NULL, NULL},
1363   };
1364
1365 void
1366 init_primitives ()
1367 {
1368   void *old;
1369   int i;
1370
1371   for (i = 0; prims[i].name; i++)
1372     if (!strhash_put (ns_prims, prims[i].name, strlen (prims[i].name),
1373                       (void *) prims[i].prim, &old))
1374       {
1375         fprintf (stderr, _("%s: out of memory\n"), program);
1376         exit (1);
1377       }
1378 }