URI: 
       prims.c - enscript - GNU Enscript
  HTML git clone git://thinkerwim.org/enscript.git
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       prims.c (26642B)
       ---
            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 
         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 }