1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
  24  */
  25 
  26 /*
  27  * Copyright (c) 2011 Gary Mills
  28  */
  29 
  30 /*      Copyright (c) 1988 AT&T     */
  31 /*        All Rights Reserved   */
  32 
  33 /*
  34  * Copyright (c) 2018, Joyent, Inc.
  35  */
  36 
  37 #include        <signal.h>
  38 #include        <unistd.h>
  39 #include        <fcntl.h>
  40 #include        "m4.h"
  41 
  42 #if defined(__lint)
  43 extern int yydebug;
  44 #endif
  45 
  46 #define match(c, s)     (c == *s && (!s[1] || inpmatch(s+1)))
  47 
  48 static char     tmp_name[] = "/tmp/m4aXXXXX";
  49 static wchar_t  prev_char;
  50 static int mb_cur_max;
  51 
  52 static void getflags(int *, char ***, int *);
  53 static void initalloc(void);
  54 static void expand(wchar_t **, int);
  55 static void lnsync(FILE *);
  56 static void fpath(FILE *);
  57 static void puttok(wchar_t *);
  58 static void error3(void);
  59 static wchar_t itochr(int);
  60 /*LINTED: E_STATIC_UNUSED*/
  61 static wchar_t *chkbltin(wchar_t *);
  62 static wchar_t *inpmatch(wchar_t *);
  63 static void chkspace(char **, int *, char ***);
  64 static void catchsig(int);
  65 static FILE *m4open(char ***, char *, int *);
  66 static void showwrap(void);
  67 static void sputchr(wchar_t, FILE *);
  68 static void putchr(wchar_t);
  69 static void *xcalloc(size_t, size_t);
  70 static wint_t myfgetwc(FILE *, int);
  71 static wint_t myfputwc(wchar_t, FILE *);
  72 static int myfeof(int);
  73 
  74 int
  75 main(int argc, char **argv)
  76 {
  77         wchar_t t;
  78         int i, opt_end = 0;
  79         int sigs[] = {SIGHUP, SIGINT, SIGPIPE, 0};
  80 
  81 #if defined(__lint)
  82         yydebug = 0;
  83 #endif
  84 
  85         for (i = 0; sigs[i]; ++i) {
  86                 if (signal(sigs[i], SIG_IGN) != SIG_IGN)
  87                         (void) signal(sigs[i], catchsig);
  88         }
  89         tempfile = mktemp(tmp_name);
  90         (void) close(creat(tempfile, 0));
  91 
  92         (void) setlocale(LC_ALL, "");
  93 
  94 #if !defined(TEXT_DOMAIN)       /* Should be defined by cc -D */
  95 #define TEXT_DOMAIN "SYS_TEST"
  96 #endif
  97         (void) textdomain(TEXT_DOMAIN);
  98 
  99         if ((mb_cur_max = MB_CUR_MAX) > 1)
 100                 wide = 1;
 101 
 102         procnam = argv[0];
 103         getflags(&argc, &argv, &opt_end);
 104         initalloc();
 105 
 106         setfname("-");
 107         if (argc > 1) {
 108                 --argc;
 109                 ++argv;
 110                 if (strcmp(argv[0], "-")) {
 111                         ifile[ifx] = m4open(&argv, "r", &argc);
 112                         setfname(argv[0]);
 113                 }
 114         }
 115 
 116         for (;;) {
 117                 token[0] = t = getchr();
 118                 token[1] = EOS;
 119 
 120                 if (t == WEOF) {
 121                         if (ifx > 0) {
 122                                 (void) fclose(ifile[ifx]);
 123                                 ipflr = ipstk[--ifx];
 124                                 continue;
 125                         }
 126 
 127                         getflags(&argc, &argv, &opt_end);
 128 
 129                         if (argc <= 1)
 130                                 /*
 131                                  * If dowrap() has been called, the m4wrap
 132                                  * macro has been processed, and a linked
 133                                  * list of m4wrap strings has been created.
 134                                  * The list starts at wrapstart.
 135                                  */
 136                                 if (wrapstart) {
 137                                         /*
 138                                          * Now that EOF has been processed,
 139                                          * display the m4wrap strings.
 140                                          */
 141                                         showwrap();
 142                                         continue;
 143                                 } else
 144                                         break;
 145                         --argc;
 146                         ++argv;
 147 
 148                         if (ifile[ifx] != stdin)
 149                                 (void) fclose(ifile[ifx]);
 150 
 151                         if (strcmp(argv[0], "-"))
 152                                 ifile[ifx] = m4open(&argv, "r", &argc);
 153                         else
 154                                 ifile[ifx] = stdin;
 155 
 156                         setfname(argv[0]);
 157                         continue;
 158                 }
 159 
 160                 if (is_alpha(t) || t == '_') {
 161                         wchar_t *tp = token+1;
 162                         int tlim = toksize;
 163                         struct nlist    *macadd;  /* temp variable */
 164 
 165                         while ((*tp = getchr()) != WEOF &&
 166                             (is_alnum(*tp) || *tp == '_')) {
 167                                 tp++;
 168                                 if (--tlim <= 0)
 169                                         error2(gettext(
 170                                             "more than %d chars in word"),
 171                                             toksize);
 172                         }
 173                         putbak(*tp);
 174                         *tp = EOS;
 175 
 176                         macadd = lookup(token);
 177                         *Ap = (wchar_t *)macadd;
 178                         if (macadd->def) {
 179                                 if ((wchar_t *)(++Ap) >= astklm) {
 180                                         --Ap;
 181                                         error2(gettext(
 182                                             "more than %d items on "
 183                                             "argument stack"),
 184                                             stksize);
 185                                 }
 186 
 187                                 if (Cp++ == NULL)
 188                                         Cp = callst;
 189 
 190                                 Cp->argp = Ap;
 191                                 *Ap++ = op;
 192                                 puttok(token);
 193                                 stkchr(EOS);
 194                                 t = getchr();
 195                                 putbak(t);
 196 
 197                                 if (t != '(')
 198                                         pbstr(L"()");
 199                                 else    /* try to fix arg count */
 200                                         *Ap++ = op;
 201 
 202                                 Cp->plev = 0;
 203                         } else {
 204                                 puttok(token);
 205                         }
 206                 } else if (match(t, lquote)) {
 207                         int     qlev = 1;
 208 
 209                         for (;;) {
 210                                 token[0] = t = getchr();
 211                                 token[1] = EOS;
 212 
 213                                 if (match(t, rquote)) {
 214                                         if (--qlev > 0)
 215                                                 puttok(token);
 216                                         else
 217                                                 break;
 218                                 } else if (match(t, lquote)) {
 219                                         ++qlev;
 220                                         puttok(token);
 221                                 } else {
 222                                         if (t == WEOF)
 223                                                 error(gettext(
 224                                                 "EOF in quote"));
 225                                         putchr(t);
 226                                 }
 227                         }
 228                 } else if (match(t, lcom) &&
 229                     ((lcom[0] != L'#' || lcom[1] != L'\0') ||
 230                     prev_char != '$')) {
 231 
 232                         /*
 233                          * Don't expand commented macro (between lcom and
 234                          * rcom).
 235                          * What we know so far is that we have found the
 236                          * left comment char (lcom).
 237                          * Make sure we haven't found '#' (lcom) immediately
 238                          * preceded by '$' because we want to expand "$#".
 239                          */
 240 
 241                         puttok(token);
 242                         for (;;) {
 243                                 token[0] = t = getchr();
 244                                 token[1] = EOS;
 245                                 if (match(t, rcom)) {
 246                                         puttok(token);
 247                                         break;
 248                                 } else {
 249                                         if (t == WEOF)
 250                                                 error(gettext(
 251                                                 "EOF in comment"));
 252                                         putchr(t);
 253                                 }
 254                         }
 255                 } else if (Cp == NULL) {
 256                         putchr(t);
 257                 } else if (t == '(') {
 258                         if (Cp->plev)
 259                                 stkchr(t);
 260                         else {
 261                                 /* skip white before arg */
 262                                 while ((t = getchr()) != WEOF && is_space(t))
 263                                         ;
 264 
 265                                 putbak(t);
 266                         }
 267 
 268                         ++Cp->plev;
 269                 } else if (t == ')') {
 270                         --Cp->plev;
 271 
 272                         if (Cp->plev == 0) {
 273                                 stkchr(EOS);
 274                                 expand(Cp->argp, Ap-Cp->argp-1);
 275                                 op = *Cp->argp;
 276                                 Ap = Cp->argp-1;
 277 
 278                                 if (--Cp < callst)
 279                                         Cp = NULL;
 280                         } else
 281                                 stkchr(t);
 282                 } else if (t == ',' && Cp->plev <= 1) {
 283                         stkchr(EOS);
 284                         *Ap = op;
 285 
 286                         if ((wchar_t *)(++Ap) >= astklm) {
 287                                 --Ap;
 288                                 error2(gettext(
 289                                     "more than %d items on argument stack"),
 290                                     stksize);
 291                         }
 292 
 293                         while ((t = getchr()) != WEOF && is_space(t))
 294                                 ;
 295 
 296                         putbak(t);
 297                 } else {
 298                         stkchr(t);
 299                 }
 300         }
 301 
 302         if (Cp != NULL)
 303                 error(gettext(
 304                 "EOF in argument list"));
 305 
 306         delexit(exitstat, 1);
 307         return (0);
 308 }
 309 
 310 static wchar_t *
 311 inpmatch(wchar_t *s)
 312 {
 313         wchar_t *tp = token+1;
 314 
 315         while (*s) {
 316                 *tp = getchr();
 317 
 318                 if (*tp++ != *s++) {
 319                         *tp = EOS;
 320                         pbstr(token+1);
 321                         return (0);
 322                 }
 323         }
 324 
 325         *tp = EOS;
 326         return (token);
 327 }
 328 
 329 static void
 330 getflags(int *xargc, char ***xargv, int *option_end)
 331 {
 332         char    *arg;
 333         char *t;
 334         wchar_t *s[3];
 335 
 336         while (*xargc > 1) {
 337                 arg = (*xargv)[1]; /* point arg to current argument */
 338 
 339                 /*
 340                  * This argument is not an option if it equals "-" or if
 341                  * "--" has already been parsed.
 342                  */
 343                 if (arg[0] != '-' || arg[1] == EOS || *option_end)
 344                         break;
 345                 if (arg[0] == '-' && arg[1] == '-' && arg[2] == '\0') {
 346                         *option_end = 1;
 347                 } else {
 348                         switch (arg[1]) {
 349                         case 'B':
 350                                 chkspace(&arg, xargc, xargv);
 351                                 bufsize = atoi(&arg[2]);
 352                                 if (bufsize <= 0) {
 353                                         bufsize = DEF_BUFSIZE;
 354                                 }
 355                                 break;
 356                         case 'D':
 357                                 initalloc();
 358                                 chkspace(&arg, xargc, xargv);
 359                                 for (t = &arg[2]; *t; t++) {
 360                                         if (*t == '=') {
 361                                                 *t++ = EOS;
 362                                                 break;
 363                                         }
 364                                 }
 365                                 s[1] = str2wstr(&arg[2], 1);
 366                                 s[2] = str2wstr(t, 1);
 367                                 dodef(&s[0], 2);
 368                                 free(s[1]);
 369                                 free(s[2]);
 370                                 break;
 371                         case 'H':
 372                                 chkspace(&arg, xargc, xargv);
 373                                 hshsize = atoi(&arg[2]);
 374                                 if (hshsize <= 0) {
 375                                         hshsize = DEF_HSHSIZE;
 376                                 }
 377                                 break;
 378                         case 'S':
 379                                 chkspace(&arg, xargc, xargv);
 380                                 stksize = atoi(&arg[2]);
 381                                 if (stksize <= 0) {
 382                                         stksize = DEF_STKSIZE;
 383                                 }
 384                                 break;
 385                         case 'T':
 386                                 chkspace(&arg, xargc, xargv);
 387                                 toksize = atoi(&arg[2]);
 388                                 if (toksize <= 0) {
 389                                         toksize = DEF_TOKSIZE;
 390                                 }
 391                                 break;
 392                         case 'U':
 393                                 initalloc();
 394                                 chkspace(&arg, xargc, xargv);
 395                                 s[1] = str2wstr(&arg[2], 1);
 396                                 doundef(&s[0], 1);
 397                                 free(s[1]);
 398                                 break;
 399                         case 'e':
 400                                 setbuf(stdout, NULL);
 401                                 (void) signal(SIGINT, SIG_IGN);
 402                                 break;
 403                         case 's':
 404                                 /* turn on line sync */
 405                                 sflag = 1;
 406                                 break;
 407                         default:
 408                                 (void) fprintf(stderr,
 409                                     gettext("%s: bad option: %s\n"),
 410                                     procnam, arg);
 411                                 delexit(NOT_OK, 0);
 412                         }
 413                 } /* end else not "--" */
 414 
 415                 (*xargv)++;
 416                 --(*xargc);
 417         } /* end while options to process */
 418 }
 419 
 420 /*
 421  * Function: chkspace
 422  *
 423  * If there is a space between the option and its argument,
 424  * adjust argptr so that &arg[2] will point to beginning of the option argument.
 425  * This will ensure that processing in getflags() will work, because &arg[2]
 426  * will point to the beginning of the option argument whether or not we have
 427  * a space between the option and its argument.  If there is a space between
 428  * the option and its argument, also adjust xargv and xargc because we are
 429  * processing the next argument.
 430  */
 431 static void
 432 chkspace(char **argptr, int *xargc, char ***xargv)
 433 {
 434         if ((*argptr)[2] == EOS) {
 435                 /* there is a space between the option and its argument */
 436                 (*xargv)++; /* look at the next argument */
 437                 --(*xargc);
 438                 /*
 439                  * Adjust argptr if the option is followed by an
 440                  * option argument.
 441                  */
 442                 if (*xargc > 1) {
 443                         *argptr = (*xargv)[1];
 444                         /* point &arg[2] to beginning of option argument */
 445                         *argptr -= 2;
 446                 }
 447         }
 448 }
 449 
 450 static void
 451 initalloc(void)
 452 {
 453         static int done = 0;
 454         int     t;
 455 
 456         if (done++)
 457                 return;
 458 
 459         hshtab = xcalloc(hshsize, sizeof (struct nlist *));
 460         callst = xcalloc(stksize/3+1, sizeof (struct call));
 461         Ap = argstk = xcalloc(stksize+3, sizeof (wchar_t *));
 462         ipstk[0] = ipflr = ip = ibuf = xcalloc(bufsize+1, sizeof (wchar_t));
 463         op = obuf = xcalloc(bufsize+1, sizeof (wchar_t));
 464         token = xcalloc(toksize+1, sizeof (wchar_t));
 465 
 466         astklm = (wchar_t *)(&argstk[stksize]);
 467         ibuflm = &ibuf[bufsize];
 468         obuflm = &obuf[bufsize];
 469         toklm = &token[toksize];
 470 
 471         for (t = 0; barray[t].bname; ++t) {
 472                 wchar_t p[2] = {0, EOS};
 473 
 474                 p[0] = builtin(t);
 475                 install(barray[t].bname, p, NOPUSH);
 476         }
 477         install(L"unix", nullstr, NOPUSH);
 478 }
 479 
 480 void
 481 install(wchar_t *nam, wchar_t *val, int mode)
 482 {
 483         struct nlist *np;
 484         wchar_t *cp;
 485         int             l;
 486 
 487         if (mode == PUSH)
 488                 (void) lookup(nam);     /* lookup sets hshval */
 489         else
 490                 while (undef(nam))      /* undef calls lookup */
 491                         ;
 492 
 493         np = xcalloc(1, sizeof (*np));
 494         np->name = wstrdup(nam);
 495         np->next = hshtab[hshval];
 496         hshtab[hshval] = np;
 497 
 498         cp = xcalloc((l = wcslen(val))+1, sizeof (*val));
 499         np->def = cp;
 500         cp = &cp[l];
 501 
 502         while (*val)
 503                 *--cp = *val++;
 504 }
 505 
 506 struct nlist *
 507 lookup(wchar_t *str)
 508 {
 509         wchar_t *s1;
 510         struct nlist    *np;
 511         static struct nlist     nodef;
 512 
 513         s1 = str;
 514 
 515         for (hshval = 0; *s1; )
 516                 hshval += *s1++;
 517 
 518         hshval %= hshsize;
 519 
 520         for (np = hshtab[hshval]; np != NULL; np = np->next) {
 521                 if (*str == *np->name && wcscmp(str, np->name) == 0)
 522                         return (np);
 523         }
 524         return (&nodef);
 525 }
 526 
 527 static void
 528 expand(wchar_t **a1, int c)
 529 {
 530         wchar_t *dp;
 531         struct nlist    *sp;
 532 
 533         sp = (struct nlist *)a1[-1];
 534 
 535         if (sp->tflag || trace) {
 536 #if !defined(__lint)    /* lint doesn't grok "%ws" */
 537                 int     i;
 538 
 539                 (void) fprintf(stderr,
 540                     "Trace(%d): %ws", Cp-callst, a1[0]);
 541 #endif
 542 
 543                 if (c > 0) {
 544 #if !defined(__lint)    /* lint doesn't grok "%ws" */
 545                         (void) fprintf(stderr, "(%ws", chkbltin(a1[1]));
 546                         for (i = 2; i <= c; ++i)
 547                                 (void) fprintf(stderr, ",%ws", chkbltin(a1[i]));
 548 #endif
 549                         (void) fprintf(stderr, ")");
 550                 }
 551                 (void) fprintf(stderr, "\n");
 552         }
 553 
 554         dp = sp->def;
 555 
 556         for (; *dp; ++dp) {
 557                 if (is_builtin(*dp)) {
 558                         (*barray[builtin_idx(*dp)].bfunc)(a1, c);
 559                 } else if (dp[1] == '$') {
 560                         if (is_digit(*dp)) {
 561                                 int     n;
 562                                 if ((n = *dp-'0') <= c)
 563                                         pbstr(a1[n]);
 564                                 ++dp;
 565                         } else if (*dp == '#') {
 566                                 pbnum((long)c);
 567                                 ++dp;
 568                         } else if (*dp == '*' || *dp == '@') {
 569                                 int i = c;
 570                                 wchar_t **a = a1;
 571 
 572                                 if (i > 0)
 573                                         for (;;) {
 574                                                 if (*dp == '@')
 575                                                         pbstr(rquote);
 576 
 577                                                 pbstr(a[i--]);
 578 
 579                                                 if (*dp == '@')
 580                                                         pbstr(lquote);
 581 
 582                                                 if (i <= 0)
 583                                                         break;
 584 
 585                                                 pbstr(L",");
 586                                         }
 587                                 ++dp;
 588                         } else
 589                                 putbak(*dp);
 590                 } else
 591                         putbak(*dp);
 592         }
 593 }
 594 
 595 void
 596 setfname(char *s)
 597 {
 598         if (fname[ifx])
 599                 free(fname[ifx]);
 600         if ((fname[ifx] = strdup(s)) == NULL)
 601                 error(gettext("out of storage"));
 602         fline[ifx] = 1;
 603         nflag = 1;
 604         lnsync(stdout);
 605 }
 606 
 607 static void
 608 lnsync(FILE *iop)
 609 {
 610         static int cline = 0;
 611         static int cfile = 0;
 612 
 613         if (!sflag || iop != stdout)
 614                 return;
 615 
 616         if (nflag || ifx != cfile) {
 617                 nflag = 0;
 618                 cfile = ifx;
 619                 (void) fprintf(iop, "#line %d \"", cline = fline[ifx]);
 620                 fpath(iop);
 621                 (void) fprintf(iop, "\"\n");
 622         } else if (++cline != fline[ifx])
 623                 (void) fprintf(iop, "#line %d\n", cline = fline[ifx]);
 624 }
 625 
 626 static void
 627 fpath(FILE *iop)
 628 {
 629         int     i;
 630 
 631         if (fname[0] == NULL)
 632                 return;
 633 
 634         (void) fprintf(iop, "%s", fname[0]);
 635 
 636         for (i = 1; i <= ifx; ++i)
 637                 (void) fprintf(iop, ":%s", fname[i]);
 638 }
 639 
 640 /* ARGSUSED */
 641 static void
 642 catchsig(int i)
 643 {
 644         (void) signal(SIGHUP, SIG_IGN);
 645         (void) signal(SIGINT, SIG_IGN);
 646         delexit(NOT_OK, 0);
 647 }
 648 
 649 void
 650 delexit(int code, int flushio)
 651 {
 652         int i;
 653 
 654         cf = stdout;
 655 
 656 /*
 657  *      if (ofx != 0) {
 658  *              ofx = 0;
 659  *              code = NOT_OK;
 660  *      }
 661  */
 662         ofx = 0;        /* ensure that everything comes out */
 663         for (i = 1; i < 10; i++)
 664                 undiv(i, code);
 665 
 666         tempfile[7] = 'a';
 667         (void) unlink(tempfile);
 668 
 669         /* flush standard I/O buffers, ie: call exit() not _exit() */
 670         if (flushio)
 671                 exit(code);
 672 
 673         _exit(code);
 674 }
 675 
 676 static void
 677 puttok(wchar_t *tp)
 678 {
 679         if (Cp) {
 680                 while (*tp)
 681                         stkchr(*tp++);
 682         } else if (cf) {
 683                 while (*tp) {
 684                         sputchr(*tp++, cf);
 685                 }
 686         }
 687 }
 688 
 689 void
 690 pbstr(wchar_t *str)
 691 {
 692         wchar_t *p;
 693 
 694         for (p = str + wcslen(str); --p >= str; )
 695                 putbak(*p);
 696 }
 697 
 698 void
 699 undiv(int i, int code)
 700 {
 701         FILE *fp;
 702         wint_t c;
 703 
 704         if (i < 1 || i > 9 || i == ofx || !ofile[i])
 705                 return;
 706 
 707         (void) fclose(ofile[i]);
 708         tempfile[7] = 'a'+i;
 709 
 710         if (code == OK && cf) {
 711                 fp = xfopen(tempfile, "r");
 712 
 713                 if (wide) {
 714                         while ((c = myfgetwc(fp, -1)) != WEOF)
 715                                 sputchr((wchar_t)c, cf);
 716                 } else {
 717                         while ((c = (wint_t)getc(fp)) != WEOF)
 718                                 sputchr((wchar_t)c, cf);
 719                 }
 720 
 721                 (void) fclose(fp);
 722         }
 723 
 724         (void) unlink(tempfile);
 725         ofile[i] = NULL;
 726 }
 727 
 728 void
 729 pbnum(long num)
 730 {
 731         pbnbr(num, 10, 1);
 732 }
 733 
 734 void
 735 pbnbr(long nbr, int base, int len)
 736 {
 737         int     neg = 0;
 738 
 739         if (base <= 0)
 740                 return;
 741 
 742         if (nbr < 0)
 743                 neg = 1;
 744         else
 745                 nbr = -nbr;
 746 
 747         while (nbr < 0) {
 748                 int     i;
 749                 if (base > 1) {
 750                         i = nbr%base;
 751                         nbr /= base;
 752 #if (-3 % 2) != -1
 753                         while (i > 0) {
 754                                 i -= base;
 755                                 ++nbr;
 756                         }
 757 #endif
 758                         i = -i;
 759                 } else {
 760                         i = 1;
 761                         ++nbr;
 762                 }
 763                 putbak(itochr(i));
 764                 --len;
 765         }
 766 
 767         while (--len >= 0)
 768                 putbak('0');
 769 
 770         if (neg)
 771                 putbak('-');
 772 }
 773 
 774 static wchar_t
 775 itochr(int i)
 776 {
 777         if (i > 9)
 778                 return ((wchar_t)(i-10+'A'));
 779         else
 780                 return ((wchar_t)(i+'0'));
 781 }
 782 
 783 long
 784 ctol(wchar_t *str)
 785 {
 786         int sign;
 787         long num;
 788 
 789         while (is_space(*str))
 790                 ++str;
 791         num = 0;
 792         if (*str == '-') {
 793                 sign = -1;
 794                 ++str;
 795         } else
 796                 sign = 1;
 797         while (is_digit(*str))
 798                 num = num*10 + *str++ - '0';
 799         return (sign * num);
 800 }
 801 
 802 int
 803 min(int a, int b)
 804 {
 805         if (a > b)
 806                 return (b);
 807         return (a);
 808 }
 809 
 810 FILE *
 811 xfopen(char *name, char *mode)
 812 {
 813         FILE    *fp;
 814 
 815         if ((fp = fopen(name, mode)) == NULL)
 816                 errorf(gettext("cannot open file: %s"),
 817                     strerror(errno));
 818 
 819         return (fp);
 820 }
 821 
 822 /*
 823  * m4open
 824  *
 825  * Continue processing files when unable to open the given file argument.
 826  */
 827 FILE *
 828 m4open(char ***argvec, char *mode, int *argcnt)
 829 {
 830         FILE    *fp;
 831         char *arg;
 832 
 833         while (*argcnt > 0) {
 834                 arg = (*argvec)[0]; /* point arg to current file name */
 835                 if (arg[0] == '-' && arg[1] == EOS)
 836                         return (stdin);
 837                 else {
 838                         if ((fp = fopen(arg, mode)) == NULL) {
 839                                 (void) fprintf(stderr, gettext(
 840                                 "m4: cannot open %s: "), arg);
 841                                 perror("");
 842                                 if (*argcnt == 1) {
 843                                         /* last arg therefore exit */
 844                                         error3();
 845                                 } else {
 846                                         exitstat = 1;
 847                                         (*argvec)++; /* try next arg */
 848                                         (*argcnt)--;
 849                                 }
 850                         } else
 851                                 break;
 852                 }
 853         }
 854         return (fp);
 855 }
 856 
 857 void *
 858 xmalloc(size_t size)
 859 {
 860         void *ptr;
 861 
 862         if ((ptr = malloc(size)) == NULL)
 863                 error(gettext("out of storage"));
 864         return (ptr);
 865 }
 866 
 867 static void *
 868 xcalloc(size_t nbr, size_t size)
 869 {
 870         void    *ptr;
 871 
 872         ptr = xmalloc(nbr * size);
 873         (void) memset(ptr, '\0', nbr * size);
 874         return (ptr);
 875 }
 876 
 877 /* Typical format: "cannot open file: %s" */
 878 /* PRINTFLIKE1 */
 879 void
 880 errorf(char *str, char *serr)
 881 {
 882         char buf[500];
 883 
 884         (void) snprintf(buf, sizeof (buf), str, serr);
 885         error(buf);
 886 }
 887 
 888 /* PRINTFLIKE1 */
 889 void
 890 error2(char *str, int num)
 891 {
 892         char buf[500];
 893 
 894         (void) snprintf(buf, sizeof (buf), str, num);
 895         error(buf);
 896 }
 897 
 898 void
 899 error(char *str)
 900 {
 901         (void) fprintf(stderr, "\n%s:", procnam);
 902         fpath(stderr);
 903         (void) fprintf(stderr, ":%d %s\n", fline[ifx], str);
 904         error3();
 905 }
 906 
 907 static void
 908 error3()
 909 {
 910         if (Cp) {
 911                 struct call     *mptr;
 912 
 913                 /* fix limit */
 914                 *op = EOS;
 915                 (Cp+1)->argp = Ap+1;
 916 
 917                 for (mptr = callst; mptr <= Cp; ++mptr) {
 918                         wchar_t **aptr, **lim;
 919 
 920                         aptr = mptr->argp;
 921                         lim = (mptr+1)->argp-1;
 922                         if (mptr == callst)
 923                                 (void) fputws(*aptr, stderr);
 924                         ++aptr;
 925                         (void) fputs("(", stderr);
 926                         if (aptr < lim)
 927                                 for (;;) {
 928                                         (void) fputws(*aptr++, stderr);
 929                                         if (aptr >= lim)
 930                                                 break;
 931                                         (void) fputs(",", stderr);
 932                                 }
 933                 }
 934                 while (--mptr >= callst)
 935                         (void) fputs(")", stderr);
 936 
 937                 (void) fputs("\n", stderr);
 938         }
 939         delexit(NOT_OK, 1);
 940 }
 941 
 942 static wchar_t *
 943 chkbltin(wchar_t *s)
 944 {
 945         static wchar_t buf[24];
 946 
 947         if (is_builtin(*s)) {
 948                 (void) swprintf(buf, sizeof (buf)/sizeof (wchar_t), L"<%ls>",
 949                     barray[builtin_idx(*s)].bname);
 950                 return (buf);
 951         }
 952         return (s);
 953 }
 954 
 955 wchar_t
 956 getchr()
 957 {
 958         static wchar_t C;
 959 
 960         prev_char = C;
 961         if (ip > ipflr)
 962                 return (*--ip);
 963         if (wide) {
 964                 C = (wchar_t)(myfeof(ifx) ? WEOF : myfgetwc(NULL, ifx));
 965         } else {
 966                 C = (wchar_t)(feof(ifile[ifx]) ?
 967                     WEOF : (wint_t)getc(ifile[ifx]));
 968         }
 969         if (C == '\n')
 970                 fline[ifx]++;
 971         return (C);
 972 }
 973 
 974 /*
 975  * showwrap
 976  *
 977  * Loop through the list of m4wrap strings.  Call pbstr() so that the
 978  * string will be displayed, then delete the list entry and free the memory
 979  * allocated for it.
 980  */
 981 static void
 982 showwrap()
 983 {
 984         struct Wrap *prev;
 985 
 986         while (wrapstart) {
 987                 pbstr(wrapstart->wrapstr);
 988                 free(wrapstart->wrapstr);
 989                 prev = wrapstart;
 990                 wrapstart = wrapstart->nxt;
 991                 free(prev);
 992         }
 993 }
 994 
 995 static void
 996 sputchr(wchar_t c, FILE *f)
 997 {
 998         wint_t ret;
 999 
1000         if (is_builtin(c))
1001                 return;
1002         if (wide)
1003                 ret = myfputwc(c, f);
1004         else
1005                 ret = (wint_t)putc((int)c, f);
1006         if (ret == WEOF)
1007                 error(gettext("output error"));
1008         if (ret == '\n')
1009                 lnsync(f);
1010 }
1011 
1012 static void
1013 putchr(wchar_t c)
1014 {
1015         wint_t ret;
1016 
1017         if (Cp)
1018                 stkchr(c);
1019         else if (cf) {
1020                 if (sflag)
1021                         sputchr(c, cf);
1022                 else {
1023                         if (is_builtin(c))
1024                                 return;
1025                         if (wide)
1026                                 ret = myfputwc(c, cf);
1027                         else
1028                                 ret = (wint_t)putc((int)c, cf);
1029                         if (ret == WEOF) {
1030                                 error(gettext("output error"));
1031                         }
1032                 }
1033         }
1034 }
1035 
1036 wchar_t *
1037 wstrdup(wchar_t *p)
1038 {
1039         size_t len = wcslen(p);
1040         wchar_t *ret;
1041 
1042         ret = xmalloc((len + 1) * sizeof (wchar_t));
1043         (void) wcscpy(ret, p);
1044         return (ret);
1045 }
1046 
1047 int
1048 wstoi(wchar_t *p)
1049 {
1050         return ((int)wcstol(p, NULL, 10));
1051 }
1052 
1053 char *
1054 wstr2str(wchar_t *from, int alloc)
1055 {
1056         static char *retbuf;
1057         static size_t bsiz;
1058         char *p, *ret;
1059 
1060         if (alloc) {
1061                 ret = p = xmalloc(wcslen(from) * mb_cur_max + 1);
1062         } else {
1063                 while (bsiz < (wcslen(from) * mb_cur_max + 1)) {
1064                         if ((p = realloc(retbuf, bsiz + 256)) == NULL)
1065                                 error(gettext("out of storage"));
1066                         bsiz += 256;
1067                         retbuf = p;
1068                 }
1069                 ret = p = retbuf;
1070         }
1071 
1072         if (wide) {
1073                 while (*from) {
1074                         int len;
1075 
1076                         if (*from & INVALID_CHAR) {
1077                                 *p = (char)(*from & ~INVALID_CHAR);
1078                                 len = 1;
1079                         } else {
1080                                 if ((len = wctomb(p, *from)) == -1) {
1081                                         *p = (char)*from;
1082                                         len = 1;
1083                                 }
1084                         }
1085                         p += len;
1086                         from++;
1087                 }
1088         } else {
1089                 while (*from)
1090                         *p++ = (char)*from++;
1091         }
1092         *p = '\0';
1093 
1094         return (ret);
1095 }
1096 
1097 wchar_t *
1098 str2wstr(char *from, int alloc)
1099 {
1100         static wchar_t *retbuf;
1101         static size_t bsiz;
1102         wchar_t *p, *ret;
1103 
1104         if (alloc) {
1105                 ret = p = xmalloc((strlen(from) + 1) * sizeof (wchar_t));
1106         } else {
1107                 while (bsiz < (strlen(from) + 1)) {
1108                         if ((p = realloc(retbuf,
1109                             (bsiz + 256) * sizeof (wchar_t))) == NULL) {
1110                                 error(gettext("out of storage"));
1111                         }
1112                         bsiz += 256;
1113                         retbuf = p;
1114                 }
1115                 ret = p = retbuf;
1116         }
1117 
1118         if (wide) {
1119                 while (*from) {
1120                         int len;
1121                         wchar_t wc;
1122 
1123                         if ((len = mbtowc(&wc, from, mb_cur_max)) <= 0) {
1124                                 wc = *from | INVALID_CHAR;
1125                                 len = 1;
1126                         }
1127                         *p++ = wc;
1128                         from += len;
1129                 }
1130         } else {
1131                 while (*from)
1132                         *p++ = (unsigned char) *from++;
1133         }
1134         *p = 0;
1135 
1136         return (ret);
1137 }
1138 
1139 static wint_t
1140 myfgetwc(FILE *fp, int idx)
1141 {
1142         int i, c, len, nb;
1143         wchar_t wc;
1144         unsigned char *buf;
1145 
1146         if (fp == NULL)
1147                 fp = ifile[idx];
1148         else
1149                 idx = 10; /* extra slot */
1150         buf = ibuffer[idx].buffer;
1151         nb = ibuffer[idx].nbytes;
1152         len = 0;
1153         for (i = 1; i <= mb_cur_max; i++) {
1154                 if (nb < i) {
1155                         c = getc(fp);
1156                         if (c == EOF) {
1157                                 if (nb == 0)
1158                                         return (WEOF);
1159                                 else
1160                                         break;
1161                         }
1162                         buf[nb++] = (unsigned char)c;
1163                 }
1164                 if ((len = mbtowc(&wc, (char *)buf, i)) >= 0)
1165                         break;
1166         }
1167         if (len <= 0) {
1168                 wc = buf[0] | INVALID_CHAR;
1169                 len = 1;
1170         }
1171         nb -= len;
1172         if (nb > 0) {
1173                 for (i = 0; i < nb; i++)
1174                         buf[i] = buf[i + len];
1175         }
1176         ibuffer[idx].nbytes = nb;
1177         return (wc);
1178 }
1179 
1180 static wint_t
1181 myfputwc(wchar_t wc, FILE *fp)
1182 {
1183         if (wc & INVALID_CHAR) {
1184                 wc &= ~INVALID_CHAR;
1185                 return (fputc((int)wc, fp));
1186         }
1187         return (fputwc(wc, fp));
1188 }
1189 
1190 static int
1191 myfeof(int idx)
1192 {
1193         return (ibuffer[idx].nbytes == 0 && feof(ifile[idx]));
1194 }