2 * Primitive procedures for states.
3 * Copyright (c) 1997-1999 Markku Rossi.
5 * Author: Markku Rossi <mtr@iki.fi>
9 * This file is part of GNU Enscript.
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.
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.
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/>.
28 * Types and definitions.
33 prim (prim_name, args, env, filename, linenum) \
44 fprintf (stderr, _("%s:%d: %s: too few arguments\n"), \
45 filename, linenum, prim_name); \
54 fprintf (stderr, _("%s:%d: %s: too many arguments\n"), \
55 filename, linenum, prim_name); \
60 #define MATCH_ARG(type) \
61 match_arg (prim_name, type, &arg, env, filename, linenum)
63 #define APPEND(data, len) \
65 if (result_len < result_pos + (len)) \
67 result_len += (len) + 1024; \
68 result = xrealloc (result, result_len); \
70 memcpy (result + result_pos, (data), (len)); \
71 result_pos += (len); \
74 #define FMTSPECIAL(ch) \
75 (('0' <= (ch) && (ch) <= '9') || (ch) == '.' || (ch) == '-')
79 * Some forward protypes.
82 static Node *prim_print ___P ((char *prim_name, List *args,
83 Environment *env, char *filename,
84 unsigned int linenum));
92 match_arg (prim_name, type, argp, env, filename, linenum)
100 ListItem *arg = *argp;
104 n = eval_expr ((Expr *) arg->data, env);
105 if (type != nVOID && n->type != type)
107 fprintf (stderr, _("%s:%d: %s: illegal argument type\n"),
108 filename, linenum, prim_name);
120 ListItem *arg = args->head;
124 e = (Expr *) arg->data;
125 if (e->type != eSYMBOL)
127 fprintf (stderr, _("%s:%d: %s: illegal argument type\n"),
128 filename, linenum, prim_name);
131 cp = e->u.node->u.sym;
136 return execute_state (cp);
141 ListItem *arg = args->head;
145 n = MATCH_ARG (nSTRING);
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';
153 n = execute_state (cp);
160 DEFUN (prim_check_namerules)
162 ListItem *arg = args->head;
172 for (i = namerules->head; i; i = i->next)
174 c = (Cons *) i->data;
177 if (re_search (REGEXP (n), current_fname, strlen (current_fname),
178 0, strlen (current_fname), NULL) >= 0)
183 start_state = n->u.sym;
185 n = node_alloc (nINTEGER);
193 n = node_alloc (nINTEGER);
200 DEFUN (prim_check_startrules)
202 ListItem *arg = args->head;
212 for (i = startrules->head; i; i = i->next)
214 c = (Cons *) i->data;
217 if (re_search (REGEXP (n), inbuf, data_in_buffer,
218 0, data_in_buffer, NULL) >= 0)
223 start_state = n->u.sym;
225 n = node_alloc (nINTEGER);
233 n = node_alloc (nINTEGER);
242 ListItem *arg = args->head;
248 for (; arg; arg = arg->next)
250 n = eval_expr ((Expr *) arg->data, env);
251 if (n->type != nSTRING)
253 fprintf (stderr, _("%s:%d: %s: illegal argument type\n"),
254 filename, linenum, prim_name);
258 if (n->u.str.len > 0)
260 data = (char *) xrealloc (data, len + n->u.str.len);
261 memcpy (data + len, n->u.str.data, n->u.str.len);
267 n = node_alloc (nSTRING);
268 n->u.str.data = data;
277 ListItem *arg = args->head;
281 n = MATCH_ARG (nVOID);
284 r = node_alloc (nREAL);
295 r->u.real = (double) n->u.array.len;
299 if (n->u.str.len > sizeof (buf) - 1)
303 memcpy (buf, n->u.str.data, n->u.str.len);
304 buf[n->u.str.len] = '\0';
305 r->u.real = atof (buf);
310 r->u.real = (double) n->u.integer;
314 r->u.real = n->u.real;
325 ListItem *arg = args->head;
330 var = MATCH_ARG (nSTRING);
333 key = (char *) xcalloc (1, var->u.str.len + 1);
334 memcpy (key, var->u.str.data, var->u.str.len);
341 n = node_alloc (nSTRING);
344 n->u.str.data = (char *) xmalloc (1);
349 n->u.str.data = xstrdup (cp);
350 n->u.str.len = strlen (cp);
359 ListItem *arg = args->head;
363 n = MATCH_ARG (nVOID);
366 r = node_alloc (nINTEGER);
377 r->u.integer = n->u.array.len;
381 if (n->u.str.len > sizeof (buf) - 1)
385 memcpy (buf, n->u.str.data, n->u.str.len);
386 buf[n->u.str.len] = '\0';
387 r->u.integer = atoi (buf);
392 r->u.integer = n->u.integer;
396 r->u.integer = (int) n->u.real;
407 ListItem *arg = args->head;
412 for (; arg; arg = arg->next)
414 n = eval_expr ((Expr *) arg->data, env);
418 result += n->u.str.len;
422 result += n->u.array.len;
426 fprintf (stderr, _("%s:%d: %s: illegal argument type\n"),
427 filename, linenum, prim_name);
434 n = node_alloc (nINTEGER);
435 n->u.integer = result;
443 ListItem *arg = args->head;
447 /* Count list length. */
448 for (len = 0; arg; len++, arg = arg->next)
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;
459 for (len = 0; arg; len++, arg = arg->next)
460 n->u.array.array[len] = eval_expr ((Expr *) arg->data, env);
468 fprintf (stderr, _("%s: panic: "), program);
470 prim_print (prim_name, args, env, filename, linenum);
471 fprintf (stderr, "\n");
481 ListItem *arg = args->head;
488 s = MATCH_ARG (nSTRING);
492 sscanf (VERSION, "%d.%d.%d", &over[0], &over[1], &over[2]);
494 /* Required version. */
496 cp = (char *) xcalloc (1, s->u.str.len + 1);
497 memcpy (cp, s->u.str.data, s->u.str.len);
499 if (sscanf (cp, "%d.%d.%d", &rver[0], &rver[1], &rver[2]) != 3)
502 _("%s:%d: %s: malformed version string `%s'\n"),
503 filename, linenum, prim_name, cp);
508 for (i = 0; i < 3; i++)
510 if (over[i] > rver[i])
511 /* Ok, our version is bigger. */
513 if (over[i] < rver[i])
515 /* Fail, our version is too small. */
517 _("%s: FATAL ERROR: States version %s or higher is required for this script\n"),
523 /* Our version is higher or equal to the required one. */
542 fwrite (n->u.str.data, n->u.str.len, 1, ofp);
547 fwrite (n->u.re.data, n->u.re.len, 1, ofp);
552 fprintf (ofp, "%d", n->u.integer);
556 fprintf (ofp, "%f", n->u.real);
560 fprintf (ofp, "%s", n->u.sym);
564 for (i = 0; i < n->u.array.len; i++)
566 print_node (n->u.array.array[i]);
567 if (i + 1 < n->u.array.len)
576 ListItem *arg = args->head;
580 for (; arg; arg = arg->next)
582 n = eval_expr ((Expr *) arg->data, env);
593 ListItem *arg = args->head;
594 Node *from, *start, *end, *n;
598 from = eval_expr ((Expr *) arg->data, env);
601 start = MATCH_ARG (nINTEGER);
602 end = MATCH_ARG (nINTEGER);
605 if (start->u.integer > end->u.integer)
608 _("%s:%d: %s: start offset is bigger than end offset\n"),
609 filename, linenum, prim_name);
613 if (from->type == nSTRING)
615 if (end->u.integer > from->u.str.len)
617 fprintf (stderr, _("%s:%d: %s: offset out of range\n"),
618 filename, linenum, prim_name);
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,
629 else if (from->type == nARRAY)
631 if (end->u.integer > from->u.array.len)
633 fprintf (stderr, _("%s:%d: %s: offset out of range\n"),
634 filename, linenum, prim_name);
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,
645 for (i = 0; i < n->u.array.len; i++)
647 = node_copy (from->u.array.array[i + start->u.integer]);
651 fprintf (stderr, _("%s:%d: %s: illegal argument\n"),
652 filename, linenum, prim_name);
666 ListItem *arg = args->head;
669 str = MATCH_ARG (nSTRING);
672 /* Create a new REGEXP node. */
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';
684 DEFUN (prim_regexp_syntax)
686 ListItem *arg = args->head;
690 ch = MATCH_ARG (nINTEGER);
691 st = MATCH_ARG (nINTEGER);
694 syntax = (char) st->u.integer;
695 if (syntax != 'w' && syntax != ' ')
698 _("%s:%d: %s: illegal regexp character syntax: %c\n"),
699 filename, linenum, prim_name, syntax);
703 re_set_character_syntax ((unsigned char) ch->u.integer, syntax);
709 DEFUN (prim_regmatch)
711 ListItem *arg = args->head;
713 static struct re_registers matches = {0, NULL, NULL};
714 static Node *current_match_node = NULL;
717 str = MATCH_ARG (nSTRING);
718 re = MATCH_ARG (nREGEXP);
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);
727 current_match = NULL;
732 node_free (current_match_node);
733 current_match_node = str;
735 current_match = &matches;
736 current_match_buf = str->u.str.data;
740 n = node_alloc (nINTEGER);
741 n->u.integer = (i >= 0);
748 * Common regular expression substituter for regsub and regsuball.
752 do_regsubsts (str, re, subst, allp)
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;
764 int do_expansions_in_substs = 0;
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] == '$')
770 do_expansions_in_substs = 1;
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);
785 /* Everything before match. */
786 APPEND (str->u.str.data + pos, matches.start[0] - pos);
789 if (!do_expansions_in_substs)
790 APPEND (subst->u.str.data, subst->u.str.len);
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)
798 switch (subst->u.str.data[i])
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]);
821 /* Illegal substitution, just pass it through. */
823 APPEND (subst->u.str.data + i, 1);
828 APPEND (subst->u.str.data + i, 1);
832 pos = matches.end[0];
839 if (num_matches == 0)
841 /* No matches, just return our original string. */
847 APPEND (str->u.str.data + pos, str->u.str.len - pos);
849 /* Create result node. */
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);
865 ListItem *arg = args->head;
866 Node *str, *re, *subst;
868 str = MATCH_ARG (nSTRING);
869 re = MATCH_ARG (nREGEXP);
870 subst = MATCH_ARG (nSTRING);
873 return do_regsubsts (str, re, subst, 0);
877 DEFUN (prim_regsuball)
879 ListItem *arg = args->head;
880 Node *str, *re, *subst;
882 str = MATCH_ARG (nSTRING);
883 re = MATCH_ARG (nREGEXP);
884 subst = MATCH_ARG (nSTRING);
887 return do_regsubsts (str, re, subst, 1);
891 DEFUN (prim_require_state)
893 ListItem *arg = args->head;
898 e = (Expr *) arg->data;
899 if (e->type != eSYMBOL)
901 fprintf (stderr, _("%s:%d: %s: illegal argument type\n"),
902 filename, linenum, prim_name);
905 cp = e->u.node->u.sym;
910 state = lookup_state (cp);
913 fprintf (stderr, _("%s:%d: %s: couldn't define state `%s'\n"),
914 filename, linenum, prim_name, cp);
924 ListItem *arg = args->head;
925 Node *re, *str, *n, *n2;
928 re = MATCH_ARG (nREGEXP);
929 str = MATCH_ARG (nSTRING);
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 *));
937 for (pos = 0; pos < str->u.str.len;)
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);
942 /* No more matches. */
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];
953 * Check that at least one item fits after us (no need to check
954 * when appending the last item).
956 if (n->u.array.len + 1 >= n->u.array.allocated)
958 n->u.array.allocated += 100;
959 n->u.array.array = (Node **) xrealloc (n->u.array.array,
963 n->u.array.array[n->u.array.len++] = n2;
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);
972 n->u.array.array[n->u.array.len++] = n2;
980 ListItem *arg = args->head;
986 unsigned int result_pos = 0;
987 unsigned int result_len = 0;
989 int argument_count = 0;
992 fmt = MATCH_ARG (nSTRING);
993 cp = fmt->u.str.data;
995 /* Process format string and match arguments. */
996 for (i = 0; i < fmt->u.str.len; i++)
998 if (cp[i] == '%' && (i + 1 >= fmt->u.str.len || cp[i + 1] == '%'))
1003 else if (cp[i] == '%')
1010 _("%s: primitive `%s': too few arguments for format\n"),
1011 program, prim_name);
1014 n = eval_expr ((Expr *) arg->data, env);
1017 for (i++, j = 0; i < fmt->u.str.len && FMTSPECIAL (cp[i]); i++, j++)
1018 ifmtopts[j] = cp[i];
1021 if (i >= fmt->u.str.len)
1024 APPEND (ifmtopts, j);
1034 if (n->type != nINTEGER)
1038 _("%s:%d: %s: argument %d doesn't match format\n"),
1039 filename, linenum, prim_name, argument_count);
1042 sprintf (ifmt, "%%%s%c", ifmtopts, cp[i]);
1043 sprintf (buf, ifmt, n->u.integer);
1045 APPEND (buf, strlen (buf));
1049 if (n->type != nINTEGER)
1052 sprintf (ifmt, "%%%s%c", ifmtopts, cp[i]);
1053 sprintf (buf, ifmt, n->u.integer);
1055 APPEND (buf, strlen (buf));
1062 if (n->type != nREAL)
1065 sprintf (ifmt, "%%%s%c", ifmtopts, cp[i]);
1066 sprintf (buf, ifmt, n->u.real);
1068 APPEND (buf, strlen (buf));
1072 if (n->type != nSTRING)
1075 if (ifmtopts[0] != '\0')
1078 _("%s:%d: %s: no extra options can be specified for %%s\n"),
1079 filename, linenum, prim_name);
1082 APPEND (n->u.str.data, n->u.str.len);
1087 _("%s:%d: %s: illegal type specifier `%c'\n"),
1088 filename, linenum, prim_name, cp[i]);
1099 n = node_alloc (nSTRING);
1100 n->u.str.len = result_pos;
1101 n->u.str.data = result;
1109 ListItem *arg = args->head;
1115 s1 = MATCH_ARG (nSTRING);
1116 s2 = MATCH_ARG (nSTRING);
1119 cp1 = s1->u.str.data;
1120 cp2 = s2->u.str.data;
1122 for (i = 0; i < s1->u.str.len && i < s2->u.str.len; i++)
1124 if (cp1[i] < cp2[i])
1129 if (cp1[i] > cp2[i])
1135 /* Strings are so far equal, check lengths. */
1136 if (s1->u.str.len < s2->u.str.len)
1138 else if (s1->u.str.len > s2->u.str.len)
1146 n = node_alloc (nINTEGER);
1147 n->u.integer = result;
1155 ListItem *arg = args->head;
1159 n = MATCH_ARG (nVOID);
1162 r = node_alloc (nSTRING);
1169 r->u.str.data = (char *) xcalloc (1, 1);
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);
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);
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);
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);
1205 DEFUN (prim_strncmp)
1207 ListItem *arg = args->head;
1208 Node *s1, *s2, *len;
1213 s1 = MATCH_ARG (nSTRING);
1214 s2 = MATCH_ARG (nSTRING);
1215 len = MATCH_ARG (nINTEGER);
1218 cp1 = s1->u.str.data;
1219 cp2 = s2->u.str.data;
1221 for (i = 0; i < s1->u.str.len && i < s2->u.str.len && i < len->u.integer; i++)
1223 if (cp1[i] < cp2[i])
1228 if (cp1[i] > cp2[i])
1235 /* Check the limit length. */
1236 if (i >= len->u.integer)
1242 /* One or both strings were shorter than limit, check lengths. */
1243 if (s1->u.str.len < s2->u.str.len)
1245 else if (s1->u.str.len > s2->u.str.len)
1254 n = node_alloc (nINTEGER);
1255 n->u.integer = result;
1261 DEFUN (prim_substring)
1263 ListItem *arg = args->head;
1264 Node *str, *start, *end, *n;
1266 str = MATCH_ARG (nSTRING);
1267 start = MATCH_ARG (nINTEGER);
1268 end = MATCH_ARG (nINTEGER);
1271 if (start->u.integer > end->u.integer)
1274 _("%s:%d: %s: start offset is bigger than end offset\n"),
1275 filename, linenum, prim_name);
1278 if (end->u.integer > str->u.str.len)
1280 fprintf (stderr, _("%s:%d: %s: offset out of range\n"),
1281 filename, linenum, prim_name);
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);
1290 memcpy (n->u.str.data, str->u.str.data + start->u.integer,
1303 ListItem *arg = args->head;
1308 str = MATCH_ARG (nSTRING);
1311 cmd = (char *) xcalloc (1, str->u.str.len + 1);
1312 memcpy (cmd, str->u.str.data, str->u.str.len);
1314 result = system (cmd);
1317 n = node_alloc (nINTEGER);
1318 n->u.integer = result;
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},
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},
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))
1375 fprintf (stderr, _("%s: out of memory\n"), program);