"Fossies" - the Fresh Open Source Software Archive

Member "tcsh-6.22.03/sh.func.c" (18 Nov 2020, 59405 Bytes) of package /linux/misc/tcsh-6.22.03.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "sh.func.c": 6.22.02_vs_6.22.03.

    1 /*
    2  * sh.func.c: csh builtin functions
    3  */
    4 /*-
    5  * Copyright (c) 1980, 1991 The Regents of the University of California.
    6  * All rights reserved.
    7  *
    8  * Redistribution and use in source and binary forms, with or without
    9  * modification, are permitted provided that the following conditions
   10  * are met:
   11  * 1. Redistributions of source code must retain the above copyright
   12  *    notice, this list of conditions and the following disclaimer.
   13  * 2. Redistributions in binary form must reproduce the above copyright
   14  *    notice, this list of conditions and the following disclaimer in the
   15  *    documentation and/or other materials provided with the distribution.
   16  * 3. Neither the name of the University nor the names of its contributors
   17  *    may be used to endorse or promote products derived from this software
   18  *    without specific prior written permission.
   19  *
   20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   30  * SUCH DAMAGE.
   31  */
   32 #include "sh.h"
   33 #include "ed.h"
   34 #include "tw.h"
   35 #include "tc.h"
   36 #ifdef WINNT_NATIVE
   37 #include "nt.const.h"
   38 #endif /* WINNT_NATIVE */
   39 
   40 #if defined (NLS_CATALOGS) && defined(HAVE_ICONV)
   41 static iconv_t catgets_iconv; /* Or (iconv_t)-1 */
   42 #endif
   43 
   44 /*
   45  * C shell
   46  */
   47 
   48 extern int MapsAreInited;
   49 extern int NLSMapsAreInited;
   50 extern int GotTermCaps;
   51 
   52 static int zlast = -1;
   53 
   54 static  void    islogin     (void);
   55 static  void    preread     (void);
   56 static  void    doagain     (void);
   57 static  const char *isrchx  (int);
   58 static  void    search      (int, int, Char *);
   59 static  int getword     (struct Strbuf *);
   60 static  struct wordent  *histgetword    (struct wordent *);
   61 static  void    toend       (void);
   62 static  void    xecho       (int, Char **);
   63 static  int islocale_var    (Char *);
   64 static  void    wpfree      (struct whyle *);
   65 
   66 const struct biltins *
   67 isbfunc(struct command *t)
   68 {
   69     Char *cp = t->t_dcom[0];
   70     const struct biltins *bp, *bp1, *bp2;
   71     static struct biltins label = {"", dozip, 0, 0};
   72     static struct biltins foregnd = {"%job", dofg1, 0, 0};
   73     static struct biltins backgnd = {"%job &", dobg1, 0, 0};
   74 
   75     /*
   76      * We never match a builtin that has quoted the first
   77      * character; this has been the traditional way to escape 
   78      * builtin commands.
   79      */
   80     if (*cp & QUOTE)
   81     return NULL;
   82 
   83     if (*cp != ':' && lastchr(cp) == ':') {
   84     label.bname = short2str(cp);
   85     return (&label);
   86     }
   87     if (*cp == '%') {
   88     if (t->t_dflg & F_AMPERSAND) {
   89         t->t_dflg &= ~F_AMPERSAND;
   90         backgnd.bname = short2str(cp);
   91         return (&backgnd);
   92     }
   93     foregnd.bname = short2str(cp);
   94     return (&foregnd);
   95     }
   96 #ifdef WARP
   97     /*
   98      * This is a perhaps kludgy way to determine if the warp builtin is to be
   99      * acknowledged or not.  If checkwarp() fails, then we are to assume that
  100      * the warp command is invalid, and carry on as we would handle any other
  101      * non-builtin command.         -- JDK 2/4/88
  102      */
  103     if (eq(STRwarp, cp) && !checkwarp()) {
  104     return (0);     /* this builtin disabled */
  105     }
  106 #endif /* WARP */
  107     /*
  108      * Binary search Bp1 is the beginning of the current search range. Bp2 is
  109      * one past the end.
  110      */
  111     for (bp1 = bfunc, bp2 = bfunc + nbfunc; bp1 < bp2;) {
  112     int i;
  113 
  114     bp = bp1 + ((bp2 - bp1) >> 1);
  115     if ((i = ((char) *cp) - *bp->bname) == 0 &&
  116         (i = StrQcmp(cp, str2short(bp->bname))) == 0)
  117         return bp;
  118     if (i < 0)
  119         bp2 = bp;
  120     else
  121         bp1 = bp + 1;
  122     }
  123 #ifdef WINNT_NATIVE
  124     return nt_check_additional_builtins(cp);
  125 #endif /*WINNT_NATIVE*/
  126     return (0);
  127 }
  128 
  129 void
  130 func(struct command *t, const struct biltins *bp)
  131 {
  132     int     i;
  133 
  134     xechoit(t->t_dcom);
  135     setname(bp->bname);
  136     i = blklen(t->t_dcom) - 1;
  137     if (i < bp->minargs)
  138     stderror(ERR_NAME | ERR_TOOFEW);
  139     if (i > bp->maxargs)
  140     stderror(ERR_NAME | ERR_TOOMANY);
  141     (*bp->bfunct) (t->t_dcom, t);
  142 }
  143 
  144 /*ARGSUSED*/
  145 void
  146 doonintr(Char **v, struct command *c)
  147 {
  148     Char *cp;
  149     Char *vv = v[1];
  150 
  151     USE(c);
  152     if (parintr.sa_handler == SIG_IGN)
  153     return;
  154     if (setintr && intty)
  155     stderror(ERR_NAME | ERR_TERMINAL);
  156     cp = gointr;
  157     gointr = 0;
  158     xfree(cp);
  159     if (vv == 0) {
  160     if (setintr)
  161         sigset_interrupting(SIGINT, queue_pintr);
  162     else
  163         (void) signal(SIGINT, SIG_DFL);
  164     gointr = 0;
  165     }
  166     else if (eq((vv = strip(vv)), STRminus)) {
  167     (void) signal(SIGINT, SIG_IGN);
  168     gointr = Strsave(STRminus);
  169     }
  170     else {
  171     gointr = Strsave(vv);
  172     sigset_interrupting(SIGINT, queue_pintr);
  173     }
  174 }
  175 
  176 /*ARGSUSED*/
  177 void
  178 donohup(Char **v, struct command *c)
  179 {
  180     USE(c);
  181     USE(v);
  182     if (intty)
  183     stderror(ERR_NAME | ERR_TERMINAL);
  184     if (setintr == 0) {
  185     (void) signal(SIGHUP, SIG_IGN);
  186     phup_disabled = 1;
  187 #ifdef CC
  188     submit(getpid());
  189 #endif /* CC */
  190     }
  191 }
  192 
  193 /*ARGSUSED*/
  194 void
  195 dohup(Char **v, struct command *c)
  196 {
  197     USE(c);
  198     USE(v);
  199     if (intty)
  200     stderror(ERR_NAME | ERR_TERMINAL);
  201     if (setintr == 0)
  202         sigset_interrupting(SIGHUP, SIG_DFL);
  203 }
  204 
  205 
  206 /*ARGSUSED*/
  207 void
  208 dozip(Char **v, struct command *c)
  209 {
  210     USE(c);
  211     USE(v);
  212 }
  213 
  214 /*ARGSUSED*/
  215 void
  216 dofiletest(Char **v, struct command *c)
  217 {
  218     Char **globbed, **fileptr, *ftest, *res;
  219 
  220     USE(c);
  221     if (*(ftest = *++v) != '-')
  222     stderror(ERR_NAME | ERR_FILEINQ);
  223     ++v;
  224 
  225     v = glob_all_or_error(v);
  226     globbed = v;
  227     cleanup_push(globbed, blk_cleanup);
  228 
  229     while (*(fileptr = v++) != NULL) {
  230     res = filetest(ftest, &fileptr, 0);
  231     cleanup_push(res, xfree);
  232     xprintf("%S", res);
  233     cleanup_until(res);
  234     if (*v)
  235         xprintf(" ");
  236     }
  237     xprintf("\n");
  238 
  239     cleanup_until(globbed);
  240 }
  241 
  242 void
  243 prvars(void)
  244 {
  245     plist(&shvhed, VAR_ALL);
  246 }
  247 
  248 /*ARGSUSED*/
  249 void
  250 doalias(Char **v, struct command *c)
  251 {
  252     struct varent *vp;
  253     Char *p;
  254 
  255     USE(c);
  256     v++;
  257     p = *v++;
  258     if (p == 0)
  259     plist(&aliases, VAR_ALL);
  260     else if (*v == 0) {
  261     vp = adrof1(strip(p), &aliases);
  262     if (vp && vp->vec)
  263         blkpr(vp->vec), xputchar('\n');
  264     }
  265     else {
  266     if (eq(p, STRalias) || eq(p, STRunalias)) {
  267         setname(short2str(p));
  268         stderror(ERR_NAME | ERR_DANGER);
  269     }
  270     set1(strip(p), saveblk(v), &aliases, VAR_READWRITE);
  271     tw_cmd_free();
  272     }
  273 }
  274 
  275 /*ARGSUSED*/
  276 void
  277 unalias(Char **v, struct command *c)
  278 {
  279     USE(c);
  280     unset1(v, &aliases);
  281     tw_cmd_free();
  282 }
  283 
  284 /*ARGSUSED*/
  285 void
  286 dologout(Char **v, struct command *c)
  287 {
  288     USE(c);
  289     USE(v);
  290     islogin();
  291     goodbye(NULL, NULL);
  292 }
  293 
  294 /*ARGSUSED*/
  295 void
  296 dologin(Char **v, struct command *c)
  297 {
  298 #ifdef WINNT_NATIVE
  299     USE(c);
  300     USE(v);
  301 #else /* !WINNT_NATIVE */
  302     char **p = short2blk(v);
  303 
  304     USE(c);
  305     cleanup_push((Char **)p, blk_cleanup);
  306     islogin();
  307     rechist(NULL, adrof(STRsavehist) != NULL);
  308     sigaction(SIGTERM, &parterm, NULL);
  309     (void) execv(_PATH_BIN_LOGIN, p);
  310     (void) execv(_PATH_USRBIN_LOGIN, p);
  311     cleanup_until((Char **)p);
  312     untty();
  313     xexit(1);
  314 #endif /* !WINNT_NATIVE */
  315 }
  316 
  317 
  318 #ifdef NEWGRP
  319 /*ARGSUSED*/
  320 void
  321 donewgrp(Char **v, struct command *c)
  322 {
  323     char **p;
  324     if (chkstop == 0 && setintr)
  325     panystop(0);
  326     sigaction(SIGTERM, &parterm, NULL);
  327     p = short2blk(v);
  328     /*
  329      * From Beto Appleton (beto@aixwiz.austin.ibm.com)
  330      * Newgrp can take 2 arguments...
  331      */
  332     (void) execv(_PATH_BIN_NEWGRP, p);
  333     (void) execv(_PATH_USRBIN_NEWGRP, p);
  334     blkfree((Char **) p);
  335     untty();
  336     xexit(1);
  337 }
  338 #endif /* NEWGRP */
  339 
  340 static void
  341 islogin(void)
  342 {
  343     if (chkstop == 0 && setintr)
  344     panystop(0);
  345     if (loginsh)
  346     return;
  347     stderror(ERR_NOTLOGIN);
  348 }
  349 
  350 void
  351 doif(Char **v, struct command *kp)
  352 {
  353     int i;
  354     Char **vv;
  355 
  356     v++;
  357     i = noexec ? 1 : expr(&v);
  358     vv = v;
  359     if (*vv == NULL)
  360     stderror(ERR_NAME | ERR_EMPTYIF);
  361     if (eq(*vv, STRthen)) {
  362     if (*++vv)
  363         stderror(ERR_NAME | ERR_IMPRTHEN);
  364     setname(short2str(STRthen));
  365     /*
  366      * If expression was zero, then scan to else , otherwise just fall into
  367      * following code.
  368      */
  369     if (!i)
  370         search(TC_IF, 0, NULL);
  371     return;
  372     }
  373     /*
  374      * Simple command attached to this if. Left shift the node in this tree,
  375      * munging it so we can reexecute it.
  376      */
  377     if (i) {
  378     lshift(kp->t_dcom, vv - kp->t_dcom);
  379     reexecute(kp);
  380     donefds();
  381     }
  382 }
  383 
  384 /*
  385  * Reexecute a command, being careful not
  386  * to redo i/o redirection, which is already set up.
  387  */
  388 void
  389 reexecute(struct command *kp)
  390 {
  391     kp->t_dflg &= F_SAVE;
  392     kp->t_dflg |= F_REPEAT;
  393     /*
  394      * If tty is still ours to arbitrate, arbitrate it; otherwise dont even set
  395      * pgrp's as the jobs would then have no way to get the tty (we can't give
  396      * it to them, and our parent wouldn't know their pgrp, etc.
  397      */
  398     execute(kp, (tpgrp > 0 ? tpgrp : -1), NULL, NULL, TRUE);
  399 }
  400 
  401 /*ARGSUSED*/
  402 void
  403 doelse (Char **v, struct command *c)
  404 {
  405     USE(c);
  406     USE(v);
  407     if (!noexec)
  408     search(TC_ELSE, 0, NULL);
  409 }
  410 
  411 /*ARGSUSED*/
  412 void
  413 dogoto(Char **v, struct command *c)
  414 {
  415     Char   *lp;
  416 
  417     USE(c);
  418     lp = globone(v[1], G_ERROR);
  419     cleanup_push(lp, xfree);
  420     if (!noexec)
  421     gotolab(lp);
  422     cleanup_until(lp);
  423 }
  424 
  425 void
  426 gotolab(Char *lab)
  427 {
  428     struct whyle *wp;
  429     /*
  430      * While we still can, locate any unknown ends of existing loops. This
  431      * obscure code is the WORST result of the fact that we don't really parse.
  432      */
  433     zlast = TC_GOTO;
  434     for (wp = whyles; wp; wp = wp->w_next)
  435     if (wp->w_end.type == TCSH_F_SEEK && wp->w_end.f_seek == 0) {
  436         search(TC_BREAK, 0, NULL);
  437         btell(&wp->w_end);
  438     }
  439     else {
  440         bseek(&wp->w_end);
  441     }
  442     search(TC_GOTO, 0, lab);
  443     /*
  444      * Eliminate loops which were exited.
  445      */
  446     wfree();
  447 }
  448 
  449 /*ARGSUSED*/
  450 void
  451 doswitch(Char **v, struct command *c)
  452 {
  453     Char *cp, *lp;
  454 
  455     USE(c);
  456     v++;
  457     if (!*v || *(*v++) != '(')
  458     stderror(ERR_SYNTAX);
  459     cp = **v == ')' ? STRNULL : *v++;
  460     if (*(*v++) != ')')
  461     v--;
  462     if (*v)
  463     stderror(ERR_SYNTAX);
  464     lp = globone(cp, G_ERROR);
  465     cleanup_push(lp, xfree);
  466     if (!noexec)
  467     search(TC_SWITCH, 0, lp);
  468     cleanup_until(lp);
  469 }
  470 
  471 /*ARGSUSED*/
  472 void
  473 dobreak(Char **v, struct command *c)
  474 {
  475     USE(v);
  476     USE(c);
  477     if (whyles == NULL)
  478     stderror(ERR_NAME | ERR_NOTWHILE);
  479     if (!noexec)
  480     toend();
  481 }
  482 
  483 /*ARGSUSED*/
  484 void
  485 doexit(Char **v, struct command *c)
  486 {
  487     USE(c);
  488 
  489     if (chkstop == 0 && (intty || intact) && evalvec == 0)
  490     panystop(0);
  491     /*
  492      * Don't DEMAND parentheses here either.
  493      */
  494     v++;
  495     if (*v) {
  496     setv(STRstatus, putn(expr(&v)), VAR_READWRITE);
  497     if (*v)
  498         stderror(ERR_NAME | ERR_EXPRESSION);
  499     }
  500     btoeof();
  501 #if 0
  502     if (intty)
  503 #endif
  504     /* Always close, why only on ttys? */
  505     xclose(SHIN);
  506 }
  507 
  508 /*ARGSUSED*/
  509 void
  510 doforeach(Char **v, struct command *c)
  511 {
  512     Char *cp, *sp;
  513     struct whyle *nwp;
  514     int gflag;
  515 
  516     USE(c);
  517     v++;
  518     cp = sp = strip(*v);
  519     if (!letter(*cp))
  520     stderror(ERR_NAME | ERR_VARBEGIN);
  521     do {
  522     cp++;
  523     } while (alnum(*cp));
  524     if (*cp != '\0')
  525     stderror(ERR_NAME | ERR_VARALNUM);
  526     cp = *v++;
  527     if (v[0][0] != '(' || v[blklen(v) - 1][0] != ')')
  528     stderror(ERR_NAME | ERR_NOPAREN);
  529     v++;
  530     gflag = tglob(v);
  531     if (gflag) {
  532     v = globall(v, gflag);
  533     if (v == 0 && !noexec)
  534         stderror(ERR_NAME | ERR_NOMATCH);
  535     }
  536     else {
  537     v = saveblk(v);
  538     trim(v);
  539     }
  540     nwp = xcalloc(1, sizeof *nwp);
  541     nwp->w_fe = nwp->w_fe0 = v;
  542     btell(&nwp->w_start);
  543     nwp->w_fename = Strsave(cp);
  544     nwp->w_next = whyles;
  545     nwp->w_end.type = TCSH_F_SEEK;
  546     whyles = nwp;
  547     /*
  548      * Pre-read the loop so as to be more comprehensible to a terminal user.
  549      */
  550     zlast = TC_FOREACH;
  551     if (intty)
  552     preread();
  553     if (!noexec)
  554     doagain();
  555 }
  556 
  557 /*ARGSUSED*/
  558 void
  559 dowhile(Char **v, struct command *c)
  560 {
  561     int status;
  562     int again = whyles != 0 && 
  563               SEEKEQ(&whyles->w_start, &lineloc) &&
  564               whyles->w_fename == 0;
  565 
  566     USE(c);
  567     v++;
  568     /*
  569      * Implement prereading here also, taking care not to evaluate the
  570      * expression before the loop has been read up from a terminal.
  571      */
  572     if (noexec)
  573     status = 0;
  574     else if (intty && !again)
  575     status = !exp0(&v, 1);
  576     else
  577     status = !expr(&v);
  578     if (*v && !noexec)
  579     stderror(ERR_NAME | ERR_EXPRESSION);
  580     if (!again) {
  581     struct whyle *nwp = xcalloc(1, sizeof(*nwp));
  582 
  583     nwp->w_start = lineloc;
  584     nwp->w_end.type = TCSH_F_SEEK;
  585     nwp->w_end.f_seek = 0;
  586     nwp->w_end.a_seek = 0;
  587     nwp->w_next = whyles;
  588     whyles = nwp;
  589     zlast = TC_WHILE;
  590     if (intty) {
  591         /*
  592          * The tty preread
  593          */
  594         preread();
  595         doagain();
  596         return;
  597     }
  598     }
  599     if (status)
  600     /* We ain't gonna loop no more, no more! */
  601     toend();
  602 }
  603 
  604 static void
  605 preread(void)
  606 {
  607     int old_pintr_disabled;
  608 
  609     whyles->w_end.type = TCSH_I_SEEK;
  610     if (setintr)
  611     pintr_push_enable(&old_pintr_disabled);
  612     search(TC_BREAK, 0, NULL);      /* read the expression in */
  613     if (setintr)
  614     cleanup_until(&old_pintr_disabled);
  615     btell(&whyles->w_end);
  616 }
  617 
  618 /*ARGSUSED*/
  619 void
  620 doend(Char **v, struct command *c)
  621 {
  622     USE(v);
  623     USE(c);
  624     if (!whyles)
  625     stderror(ERR_NAME | ERR_NOTWHILE);
  626     btell(&whyles->w_end);
  627     if (!noexec)
  628     doagain();
  629 }
  630 
  631 /*ARGSUSED*/
  632 void
  633 docontin(Char **v, struct command *c)
  634 {
  635     USE(v);
  636     USE(c);
  637     if (!whyles)
  638     stderror(ERR_NAME | ERR_NOTWHILE);
  639     if (!noexec)
  640     doagain();
  641 }
  642 
  643 static void
  644 doagain(void)
  645 {
  646     /* Repeating a while is simple */
  647     if (whyles->w_fename == 0) {
  648     bseek(&whyles->w_start);
  649     return;
  650     }
  651     /*
  652      * The foreach variable list actually has a spurious word ")" at the end of
  653      * the w_fe list.  Thus we are at the of the list if one word beyond this
  654      * is 0.
  655      */
  656     if (!whyles->w_fe[1]) {
  657     dobreak(NULL, NULL);
  658     return;
  659     }
  660     setv(whyles->w_fename, quote(Strsave(*whyles->w_fe++)), VAR_READWRITE);
  661     bseek(&whyles->w_start);
  662 }
  663 
  664 void
  665 dorepeat(Char **v, struct command *kp)
  666 {
  667     int i = 1;
  668 
  669     do {
  670     i *= getn(v[1]);
  671     lshift(v, 2);
  672     } while (v[0] != NULL && Strcmp(v[0], STRrepeat) == 0);
  673     if (noexec)
  674     i = 1;
  675 
  676     if (setintr) {
  677     pintr_disabled++;
  678     cleanup_push(&pintr_disabled, disabled_cleanup);
  679     }
  680     while (i > 0) {
  681     if (setintr && pintr_disabled == 1) {
  682         cleanup_until(&pintr_disabled);
  683         pintr_disabled++;
  684         cleanup_push(&pintr_disabled, disabled_cleanup);
  685     }
  686     reexecute(kp);
  687     --i;
  688     }
  689     if (setintr && pintr_disabled == 1)
  690         cleanup_until(&pintr_disabled);
  691     donefds();
  692 }
  693 
  694 /*ARGSUSED*/
  695 void
  696 doswbrk(Char **v, struct command *c)
  697 {
  698     USE(v);
  699     USE(c);
  700     if (!noexec)
  701     search(TC_BRKSW, 0, NULL);
  702 }
  703 
  704 int
  705 srchx(Char *cp)
  706 {
  707     struct srch *sp, *sp1, *sp2;
  708     int i;
  709 
  710     /*
  711      * Ignore keywords inside heredocs
  712      */
  713     if (inheredoc)
  714     return -1;
  715 
  716     /*
  717      * Binary search Sp1 is the beginning of the current search range. Sp2 is
  718      * one past the end.
  719      */
  720     for (sp1 = srchn, sp2 = srchn + nsrchn; sp1 < sp2;) {
  721     sp = sp1 + ((sp2 - sp1) >> 1);
  722     if ((i = *cp - *sp->s_name) == 0 &&
  723         (i = Strcmp(cp, str2short(sp->s_name))) == 0)
  724         return sp->s_value;
  725     if (i < 0)
  726         sp2 = sp;
  727     else
  728         sp1 = sp + 1;
  729     }
  730     return (-1);
  731 }
  732 
  733 static const char *
  734 isrchx(int n)
  735 {
  736     struct srch *sp, *sp2;
  737 
  738     for (sp = srchn, sp2 = srchn + nsrchn; sp < sp2; sp++)
  739     if (sp->s_value == n)
  740         return (sp->s_name);
  741     return ("");
  742 }
  743 
  744 
  745 static int Stype;
  746 static Char *Sgoal;
  747 
  748 static void
  749 search(int type, int level, Char *goal)
  750 {
  751     struct Strbuf word = Strbuf_INIT;
  752     Char *cp;
  753     struct whyle *wp;
  754     int wlevel = 0;
  755     struct wordent *histent = NULL, *ohistent = NULL;
  756 
  757     Stype = type;
  758     Sgoal = goal;
  759     if (type == TC_GOTO) {
  760     struct Ain a;
  761     a.type = TCSH_F_SEEK;
  762     a.f_seek = 0;
  763     a.a_seek = 0;
  764     bseek(&a);
  765     }
  766     cleanup_push(&word, Strbuf_cleanup);
  767     do {
  768         
  769     if (intty) {
  770         histent = xmalloc(sizeof(*histent));
  771         ohistent = xmalloc(sizeof(*histent));
  772         ohistent->word = STRNULL;
  773         ohistent->next = histent;
  774         histent->prev = ohistent;
  775     }
  776 
  777     if (intty && fseekp == feobp && aret == TCSH_F_SEEK)
  778         printprompt(1, isrchx(type == TC_BREAK ? zlast : type));
  779     /* xprintf("? "), flush(); */
  780     (void) getword(&word);
  781     Strbuf_terminate(&word);
  782 
  783     if (intty && Strlen(word.s) > 0) {
  784         histent->word = Strsave(word.s);
  785         histent->next = xmalloc(sizeof(*histent));
  786         histent->next->prev = histent;
  787         histent = histent->next;
  788     }
  789 
  790     switch (srchx(word.s)) {
  791 
  792     case TC_ELSE:
  793         if (level == 0 && type == TC_IF)
  794         goto end;
  795         break;
  796 
  797     case TC_IF:
  798         while (getword(&word)) {
  799         if (intty) {
  800             histent->word = Strsave(word.s);
  801             histent->next = xmalloc(sizeof(*histent));
  802             histent->next->prev = histent;
  803             histent = histent->next;
  804         }
  805         continue;
  806         }
  807         
  808         if ((type == TC_IF || type == TC_ELSE) &&
  809         eq(word.s, STRthen))
  810         level++;
  811         break;
  812 
  813     case TC_ENDIF:
  814         if (type == TC_IF || type == TC_ELSE)
  815         level--;
  816         break;
  817 
  818     case TC_FOREACH:
  819     case TC_WHILE:
  820         wlevel++;
  821         if (type == TC_BREAK)
  822         level++;
  823         break;
  824 
  825     case TC_END:
  826         if (type == TC_BRKSW) {
  827         if (wlevel == 0) {
  828             wp = whyles;
  829             if (wp) {
  830                 whyles = wp->w_next;
  831                 wpfree(wp);
  832             }
  833         }
  834         }
  835         if (type == TC_BREAK)
  836         level--;
  837         wlevel--;
  838         break;
  839 
  840     case TC_SWITCH:
  841         if (type == TC_SWITCH || type == TC_BRKSW)
  842         level++;
  843         break;
  844 
  845     case TC_ENDSW:
  846         if (type == TC_SWITCH || type == TC_BRKSW)
  847         level--;
  848         break;
  849 
  850     case TC_LABEL:
  851         if (type == TC_GOTO && getword(&word) && eq(word.s, goal))
  852         level = -1;
  853         break;
  854 
  855     default:
  856         if (type != TC_GOTO && (type != TC_SWITCH || level != 0))
  857         break;
  858         if (word.len == 0 || word.s[word.len - 1] != ':')
  859         break;
  860         word.s[--word.len] = 0;
  861         if ((type == TC_GOTO && eq(word.s, goal)) ||
  862         (type == TC_SWITCH && eq(word.s, STRdefault)))
  863         level = -1;
  864         break;
  865 
  866     case TC_CASE:
  867         if (type != TC_SWITCH || level != 0)
  868         break;
  869         (void) getword(&word);
  870         if (word.len != 0 && word.s[word.len - 1] == ':')
  871         word.s[--word.len] = 0;
  872         cp = strip(Dfix1(word.s));
  873         cleanup_push(cp, xfree);
  874         if (Gmatch(goal, cp))
  875         level = -1;
  876         cleanup_until(cp);
  877         break;
  878 
  879     case TC_DEFAULT:
  880         if (type == TC_SWITCH && level == 0)
  881         level = -1;
  882         break;
  883     }
  884     if (intty) {
  885         ohistent->prev = histgetword(histent);
  886         ohistent->prev->next = ohistent;
  887         savehist(ohistent, 0);
  888         freelex(ohistent);
  889         xfree(ohistent);
  890     } else 
  891         (void) getword(NULL);
  892     } while (level >= 0);
  893  end:
  894     cleanup_until(&word);
  895 }
  896 
  897 static struct wordent *
  898 histgetword(struct wordent *histent) 
  899 {
  900     int first;
  901     eChar c, d;
  902     int e;
  903     struct Strbuf *tmp;
  904     tmp = xmalloc(sizeof(*tmp));
  905     tmp->size = 0;
  906     tmp->s = NULL;
  907     c = readc(1);
  908     d = 0;
  909     e = 0;
  910     for (;;) {
  911     tmp->len = 0;
  912     Strbuf_terminate (tmp);
  913     while (c == ' ' || c == '\t')
  914         c = readc(1);
  915     if (c == '#')
  916         do
  917         c = readc(1);
  918         while (c != CHAR_ERR && c != '\n');
  919     if (c == CHAR_ERR)
  920         goto past;
  921     if (c == '\n') 
  922         goto nl;
  923     unreadc(c);
  924     first = 1;
  925     do {
  926         e = (c == '\\');
  927         c = readc(1);
  928         if (c == '\\' && !e) {
  929         if ((c = readc(1)) == '\n') {
  930             e = 1;
  931             c = ' ';
  932         } else {
  933             unreadc(c);
  934             c = '\\';
  935         }
  936         }
  937         if ((c == '\'' || c == '"') && !e) {
  938         if (d == 0)
  939             d = c;
  940         else if (d == c)
  941             d = 0;
  942         }
  943         if (c == CHAR_ERR)
  944         goto past;
  945         
  946         Strbuf_append1(tmp, (Char) c);
  947         
  948         if (!first && !d && c == '(' && !e) {
  949         break;
  950         }
  951         first = 0;
  952     } while (d || e || (c != ' ' && c != '\t' && c != '\n'));
  953     tmp->len--;
  954     if (tmp->len) {
  955         Strbuf_terminate(tmp);
  956         histent->word = Strsave(tmp->s);
  957         histent->next = xmalloc(sizeof (*histent));
  958         histent->next->prev = histent;
  959         histent = histent->next;
  960     }
  961     if (c == '\n') {
  962     nl:
  963         tmp->len = 0;
  964         Strbuf_append1(tmp, (Char) c);
  965         Strbuf_terminate(tmp);
  966         histent->word = Strsave(tmp->s);
  967         return histent;
  968     }
  969     }
  970     
  971 past:
  972     switch (Stype) {
  973 
  974     case TC_IF:
  975     stderror(ERR_NAME | ERR_NOTFOUND, "then/endif");
  976     break;
  977 
  978     case TC_ELSE:
  979     stderror(ERR_NAME | ERR_NOTFOUND, "endif");
  980     break;
  981 
  982     case TC_BRKSW:
  983     case TC_SWITCH:
  984     stderror(ERR_NAME | ERR_NOTFOUND, "endsw");
  985     break;
  986 
  987     case TC_BREAK:
  988     stderror(ERR_NAME | ERR_NOTFOUND, "end");
  989     break;
  990 
  991     case TC_GOTO:
  992     setname(short2str(Sgoal));
  993     stderror(ERR_NAME | ERR_NOTFOUND, "label");
  994     break;
  995 
  996     default:
  997     break;
  998     }
  999     /* NOTREACHED */
 1000     return NULL;
 1001 }
 1002 
 1003 static int
 1004 getword(struct Strbuf *wp)
 1005 {
 1006     int found = 0, first;
 1007     eChar c, d;
 1008 
 1009     if (wp)
 1010     wp->len = 0;
 1011     c = readc(1);
 1012     d = 0;
 1013     do {
 1014     while (c == ' ' || c == '\t')
 1015         c = readc(1);
 1016     if (c == '#')
 1017         do
 1018         c = readc(1);
 1019         while (c != CHAR_ERR && c != '\n');
 1020     if (c == CHAR_ERR)
 1021         goto past;
 1022     if (c == '\n') {
 1023         if (wp)
 1024         break;
 1025         return (0);
 1026     }
 1027     unreadc(c);
 1028     found = 1;
 1029     first = 1;
 1030     do {
 1031         c = readc(1);
 1032         if (c == '\\' && (c = readc(1)) == '\n')
 1033         c = ' ';
 1034         if (c == '\'' || c == '"') {
 1035         if (d == 0)
 1036             d = c;
 1037         else if (d == c)
 1038             d = 0;
 1039         }
 1040         if (c == CHAR_ERR)
 1041         goto past;
 1042         if (wp)
 1043         Strbuf_append1(wp, (Char) c);
 1044         if (!d && c == ')') {
 1045         if (!first && wp) {
 1046             goto past_word_end;
 1047         } else {
 1048             if (wp) {
 1049             wp->len = 1;
 1050             Strbuf_terminate(wp);
 1051             }
 1052             return found;
 1053         }
 1054         }
 1055         if (!first && !d && c == '(') {
 1056         if (wp)
 1057             goto past_word_end;
 1058         else 
 1059             break;
 1060         }
 1061         first = 0;
 1062     } while ((d || (c != ' ' && c != '\t')) && c != '\n');
 1063     } while (wp == 0);
 1064 
 1065  past_word_end:
 1066     unreadc(c);
 1067     if (found) {
 1068     wp->len--;
 1069     Strbuf_terminate(wp);
 1070     }
 1071 
 1072     return (found);
 1073 
 1074 past:
 1075     switch (Stype) {
 1076 
 1077     case TC_IF:
 1078     stderror(ERR_NAME | ERR_NOTFOUND, "then/endif");
 1079     break;
 1080 
 1081     case TC_ELSE:
 1082     stderror(ERR_NAME | ERR_NOTFOUND, "endif");
 1083     break;
 1084 
 1085     case TC_BRKSW:
 1086     case TC_SWITCH:
 1087     stderror(ERR_NAME | ERR_NOTFOUND, "endsw");
 1088     break;
 1089 
 1090     case TC_BREAK:
 1091     stderror(ERR_NAME | ERR_NOTFOUND, "end");
 1092     break;
 1093 
 1094     case TC_GOTO:
 1095     setname(short2str(Sgoal));
 1096     stderror(ERR_NAME | ERR_NOTFOUND, "label");
 1097     break;
 1098 
 1099     default:
 1100     break;
 1101     }
 1102     /* NOTREACHED */
 1103     return (0);
 1104 }
 1105 
 1106 static void
 1107 toend(void)
 1108 {
 1109     if (whyles->w_end.type == TCSH_F_SEEK && whyles->w_end.f_seek == 0) {
 1110     search(TC_BREAK, 0, NULL);
 1111     btell(&whyles->w_end);
 1112     whyles->w_end.f_seek--;
 1113     }
 1114     else {
 1115     bseek(&whyles->w_end);
 1116     }
 1117     wfree();
 1118 }
 1119 
 1120 static void
 1121 wpfree(struct whyle *wp)
 1122 {
 1123     if (wp->w_fe0)
 1124         blkfree(wp->w_fe0);
 1125     xfree(wp->w_fename);
 1126     xfree(wp);
 1127 }
 1128 
 1129 void
 1130 wfree(void)
 1131 {
 1132     struct Ain    o;
 1133     struct whyle *nwp;
 1134 #ifdef lint
 1135     nwp = NULL; /* sun lint is dumb! */
 1136 #endif
 1137 
 1138 #ifdef FDEBUG
 1139     static const char foo[] = "IAFE";
 1140 #endif /* FDEBUG */
 1141 
 1142     btell(&o);
 1143 
 1144 #ifdef FDEBUG
 1145     xprintf("o->type %c o->a_seek %d o->f_seek %d\n",
 1146         foo[o.type + 1], o.a_seek, o.f_seek);
 1147 #endif /* FDEBUG */
 1148 
 1149     for (; whyles; whyles = nwp) {
 1150     struct whyle *wp = whyles;
 1151     nwp = wp->w_next;
 1152 
 1153 #ifdef FDEBUG
 1154     xprintf("start->type %c start->a_seek %d start->f_seek %d\n",
 1155         foo[wp->w_start.type+1], 
 1156         wp->w_start.a_seek, wp->w_start.f_seek);
 1157     xprintf("end->type %c end->a_seek %d end->f_seek %d\n",
 1158         foo[wp->w_end.type + 1], wp->w_end.a_seek, wp->w_end.f_seek);
 1159 #endif /* FDEBUG */
 1160 
 1161     /*
 1162      * XXX: We free loops that have different seek types.
 1163      */
 1164     if (wp->w_end.type != TCSH_I_SEEK && wp->w_start.type == wp->w_end.type &&
 1165         wp->w_start.type == o.type) {
 1166         if (wp->w_end.type == TCSH_F_SEEK) {
 1167         if (o.f_seek >= wp->w_start.f_seek && 
 1168             (wp->w_end.f_seek == 0 || o.f_seek < wp->w_end.f_seek))
 1169             break;
 1170         }
 1171         else {
 1172         if (o.a_seek >= wp->w_start.a_seek && 
 1173             (wp->w_end.a_seek == 0 || o.a_seek < wp->w_end.a_seek))
 1174             break;
 1175         }
 1176     }
 1177 
 1178     wpfree(wp);
 1179     }
 1180 }
 1181 
 1182 /*ARGSUSED*/
 1183 void
 1184 doecho(Char **v, struct command *c)
 1185 {
 1186     USE(c);
 1187     xecho(' ', v);
 1188 }
 1189 
 1190 /*ARGSUSED*/
 1191 void
 1192 doglob(Char **v, struct command *c)
 1193 {
 1194     USE(c);
 1195     xecho(0, v);
 1196     flush();
 1197 }
 1198 
 1199 static void
 1200 xecho(int sep, Char **v)
 1201 {
 1202     Char *cp, **globbed = NULL;
 1203     int     nonl = 0;
 1204     int     echo_style = ECHO_STYLE;
 1205     struct varent *vp;
 1206 
 1207     if ((vp = adrof(STRecho_style)) != NULL && vp->vec != NULL &&
 1208     vp->vec[0] != NULL) {
 1209     if (Strcmp(vp->vec[0], STRbsd) == 0)
 1210         echo_style = BSD_ECHO;
 1211     else if (Strcmp(vp->vec[0], STRsysv) == 0)
 1212         echo_style = SYSV_ECHO;
 1213     else if (Strcmp(vp->vec[0], STRboth) == 0)
 1214         echo_style = BOTH_ECHO;
 1215     else if (Strcmp(vp->vec[0], STRnone) == 0)
 1216         echo_style = NONE_ECHO;
 1217     }
 1218 
 1219     v++;
 1220     if (*v == 0)
 1221     goto done;
 1222     if (setintr) {
 1223     int old_pintr_disabled;
 1224     pintr_push_enable(&old_pintr_disabled);
 1225     v = glob_all_or_error(v);
 1226     cleanup_until(&old_pintr_disabled);
 1227     } else {
 1228     v = glob_all_or_error(v);
 1229     }
 1230     globbed = v;
 1231     if (globbed != NULL)
 1232     cleanup_push(globbed, blk_cleanup);
 1233 
 1234     if ((echo_style & BSD_ECHO) != 0 && sep == ' ' && *v && eq(*v, STRmn))
 1235     nonl++, v++;
 1236 
 1237     while ((cp = *v++) != 0) {
 1238     Char c;
 1239 
 1240     if (setintr) {
 1241         int old_pintr_disabled;
 1242 
 1243         pintr_push_enable(&old_pintr_disabled);
 1244         cleanup_until(&old_pintr_disabled);
 1245     }
 1246     while ((c = *cp++) != 0) {
 1247         if ((echo_style & SYSV_ECHO) != 0 && c == '\\') {
 1248         switch (c = *cp++) {
 1249         case 'a':
 1250             c = '\a';
 1251             break;
 1252         case 'b':
 1253             c = '\b';
 1254             break;
 1255         case 'c':
 1256             nonl = 1;
 1257             goto done;
 1258         case 'e':
 1259 #if 0           /* Windows does not understand \e */
 1260             c = '\e';
 1261 #else
 1262             c = CTL_ESC('\033');
 1263 #endif
 1264             break;
 1265         case 'f':
 1266             c = '\f';
 1267             break;
 1268         case 'n':
 1269             c = '\n';
 1270             break;
 1271         case 'r':
 1272             c = '\r';
 1273             break;
 1274         case 't':
 1275             c = '\t';
 1276             break;
 1277         case 'v':
 1278             c = '\v';
 1279             break;
 1280         case '\\':
 1281             c = '\\';
 1282             break;
 1283         case '0':
 1284             c = 0;
 1285             if (*cp >= '0' && *cp < '8')
 1286             c = c * 8 + *cp++ - '0';
 1287             if (*cp >= '0' && *cp < '8')
 1288             c = c * 8 + *cp++ - '0';
 1289             if (*cp >= '0' && *cp < '8')
 1290             c = c * 8 + *cp++ - '0';
 1291             break;
 1292         case '\0':
 1293             c = '\\';
 1294             cp--;
 1295             break;
 1296         default:
 1297             xputchar('\\' | QUOTE);
 1298             break;
 1299         }
 1300         }
 1301         xputwchar(c | QUOTE);
 1302 
 1303     }
 1304     if (*v)
 1305         xputchar(sep | QUOTE);
 1306     }
 1307 done:
 1308     if (sep && nonl == 0)
 1309     xputchar('\n');
 1310     else
 1311     flush();
 1312     if (globbed != NULL)
 1313     cleanup_until(globbed);
 1314 }
 1315 
 1316 /* check whether an environment variable should invoke 'set_locale()' */
 1317 static int
 1318 islocale_var(Char *var)
 1319 {
 1320     static Char *locale_vars[] = {
 1321     STRLANG,    STRLC_ALL,  STRLC_CTYPE,    STRLC_NUMERIC,
 1322     STRLC_TIME, STRLC_COLLATE,  STRLC_MESSAGES, STRLC_MONETARY, 0
 1323     };
 1324     Char **v;
 1325 
 1326     for (v = locale_vars; *v; ++v)
 1327     if (eq(var, *v))
 1328         return 1;
 1329     return 0;
 1330 }
 1331 
 1332 static void
 1333 xlate_cr_cleanup(void *dummy)
 1334 {
 1335     USE(dummy);
 1336     xlate_cr = 0;
 1337 }
 1338 
 1339 /*ARGSUSED*/
 1340 void
 1341 doprintenv(Char **v, struct command *c) 
 1342 {
 1343     Char   *e;
 1344 
 1345     USE(c);
 1346     v++;
 1347     if (*v == 0) {
 1348     Char **ep;
 1349 
 1350     xlate_cr = 1;
 1351     cleanup_push(&xlate_cr, xlate_cr_cleanup);
 1352     for (ep = STR_environ; *ep; ep++) {
 1353         if (setintr) {
 1354         int old_pintr_disabled;
 1355 
 1356         pintr_push_enable(&old_pintr_disabled);
 1357         cleanup_until(&old_pintr_disabled);
 1358         }
 1359         xprintf("%S\n", *ep);
 1360     }
 1361     cleanup_until(&xlate_cr);
 1362     }
 1363     else if ((e = tgetenv(*v)) != NULL) {
 1364     int old_output_raw;
 1365 
 1366     old_output_raw = output_raw;
 1367     output_raw = 1;
 1368     cleanup_push(&old_output_raw, output_raw_restore);
 1369     xprintf("%S\n", e);
 1370     cleanup_until(&old_output_raw);
 1371     }
 1372     else
 1373     setcopy(STRstatus, STR1, VAR_READWRITE);
 1374 }
 1375 
 1376 /* from "Karl Berry." <karl%mote.umb.edu@relay.cs.net> -- for NeXT things
 1377    (and anything else with a modern compiler) */
 1378 
 1379 /*ARGSUSED*/
 1380 void
 1381 dosetenv(Char **v, struct command *c)
 1382 {
 1383     Char   *vp, *lp;
 1384 
 1385     USE(c);
 1386     if (*++v == 0) {
 1387     doprintenv(--v, 0);
 1388     return;
 1389     }
 1390 
 1391     vp = *v++;
 1392     lp = vp;
 1393 
 1394     if (!letter(*lp))
 1395     stderror(ERR_NAME | ERR_VARBEGIN);
 1396     do {
 1397     lp++;
 1398     } while (alnum(*lp) || *lp == '.');
 1399     if (*lp != '\0')
 1400     stderror(ERR_NAME | ERR_VARALNUM);
 1401 
 1402     if ((lp = *v++) == 0)
 1403     lp = STRNULL;
 1404 
 1405     lp = globone(lp, G_APPEND);
 1406     cleanup_push(lp, xfree);
 1407     tsetenv(vp, lp);
 1408     if (eq(vp, STRKPATH)) {
 1409         importpath(lp);
 1410     dohash(NULL, NULL);
 1411     cleanup_until(lp);
 1412     return;
 1413     }
 1414 
 1415 #ifdef apollo
 1416     if (eq(vp, STRSYSTYPE)) {
 1417     dohash(NULL, NULL);
 1418     cleanup_until(lp);
 1419     return;
 1420     }
 1421 #endif /* apollo */
 1422 
 1423     /* dspkanji/dspmbyte autosetting */
 1424     /* PATCH IDEA FROM Issei.Suzuki VERY THANKS */
 1425 #if defined(DSPMBYTE)
 1426     if(eq(vp, STRLANG) && !adrof(CHECK_MBYTEVAR)) {
 1427     autoset_dspmbyte(lp);
 1428     }
 1429 #endif
 1430 
 1431     if (islocale_var(vp)) {
 1432 #ifdef NLS
 1433     int     k;
 1434 
 1435 # ifdef SETLOCALEBUG
 1436     dont_free = 1;
 1437 # endif /* SETLOCALEBUG */
 1438     (void) setlocale(LC_ALL, "");
 1439 # ifdef LC_COLLATE
 1440     (void) setlocale(LC_COLLATE, "");
 1441 # endif
 1442 # ifdef LC_CTYPE
 1443     (void) setlocale(LC_CTYPE, ""); /* for iscntrl */
 1444 # endif /* LC_CTYPE */
 1445 # if defined(AUTOSET_KANJI)
 1446         autoset_kanji();
 1447 # endif /* AUTOSET_KANJI */
 1448 # ifdef NLS_CATALOGS
 1449 #  ifdef LC_MESSAGES
 1450     (void) setlocale(LC_MESSAGES, "");
 1451 #  endif /* LC_MESSAGES */
 1452     nlsclose();
 1453     nlsinit();
 1454 # endif /* NLS_CATALOGS */
 1455 # ifdef SETLOCALEBUG
 1456     dont_free = 0;
 1457 # endif /* SETLOCALEBUG */
 1458 # ifdef STRCOLLBUG
 1459     fix_strcoll_bug();
 1460 # endif /* STRCOLLBUG */
 1461     tw_cmd_free();  /* since the collation sequence has changed */
 1462     for (k = 0200; k <= 0377 && !Isprint(CTL_ESC(k)); k++)
 1463         continue;
 1464     AsciiOnly = MB_CUR_MAX == 1 && k > 0377;
 1465 #else /* !NLS */
 1466     AsciiOnly = 0;
 1467 #endif /* NLS */
 1468     NLSMapsAreInited = 0;
 1469     ed_Init();
 1470     if (MapsAreInited && !NLSMapsAreInited)
 1471         ed_InitNLSMaps();
 1472     cleanup_until(lp);
 1473     return;
 1474     }
 1475 
 1476 #ifdef NLS_CATALOGS
 1477     if (eq(vp, STRNLSPATH)) {
 1478     nlsclose();
 1479     nlsinit();
 1480     }
 1481 #endif
 1482 
 1483     if (eq(vp, STRNOREBIND)) {
 1484     NoNLSRebind = 1;
 1485     MapsAreInited = 0;
 1486     NLSMapsAreInited = 0;
 1487     ed_InitMaps();
 1488     cleanup_until(lp);
 1489     return;
 1490     }
 1491 #ifdef WINNT_NATIVE
 1492     if (eq(vp, STRtcshlang)) {
 1493     nlsinit();
 1494     cleanup_until(lp);
 1495     return;
 1496     }
 1497 #endif /* WINNT_NATIVE */
 1498     if (eq(vp, STRKTERM)) {
 1499     char *t;
 1500 
 1501     setv(STRterm, quote(lp), VAR_READWRITE);    /* lp memory used here */
 1502     cleanup_ignore(lp);
 1503     cleanup_until(lp);
 1504     t = short2str(lp);
 1505     if (noediting && strcmp(t, "unknown") != 0 && strcmp(t,"dumb") != 0) {
 1506         editing = 1;
 1507         noediting = 0;
 1508         setNS(STRedit);
 1509     }
 1510     GotTermCaps = 0;
 1511     ed_Init();
 1512     return;
 1513     }
 1514 
 1515     if (eq(vp, STRKHOME)) {
 1516     Char *canon;
 1517     /*
 1518      * convert to canonical pathname (possibly resolving symlinks)
 1519      */
 1520     cleanup_ignore(lp);
 1521     canon = dcanon(lp, lp);
 1522     cleanup_push(canon, xfree);
 1523     setv(STRhome, quote(canon), VAR_READWRITE); /* lp memory used here */
 1524     cleanup_ignore(canon);
 1525     cleanup_until(canon);
 1526 
 1527     /* fix directory stack for new tilde home */
 1528     dtilde();
 1529     return;
 1530     }
 1531 
 1532     if (eq(vp, STRKSHLVL)) {
 1533     setv(STRshlvl, quote(lp), VAR_READWRITE); /* lp memory used here */
 1534     cleanup_ignore(lp);
 1535     cleanup_until(lp);
 1536     return;
 1537     }
 1538 
 1539     if (eq(vp, STRKUSER)) {
 1540     setv(STRuser, quote(lp), VAR_READWRITE);    /* lp memory used here */
 1541     cleanup_ignore(lp);
 1542     cleanup_until(lp);
 1543     return;
 1544     }
 1545 
 1546     if (eq(vp, STRKGROUP)) {
 1547     setv(STRgroup, quote(lp), VAR_READWRITE); /* lp memory used here */
 1548     cleanup_ignore(lp);
 1549     cleanup_until(lp);
 1550     return;
 1551     }
 1552 
 1553 #ifdef COLOR_LS_F
 1554     if (eq(vp, STRLS_COLORS)) {
 1555         parseLS_COLORS(lp);
 1556     cleanup_until(lp);
 1557     return;
 1558     }
 1559     if (eq(vp, STRLSCOLORS)) {
 1560         parseLSCOLORS(lp);
 1561     cleanup_until(lp);
 1562     return;
 1563     }
 1564 #endif /* COLOR_LS_F */
 1565 
 1566 #ifdef SIG_WINDOW
 1567     /*
 1568      * Load/Update $LINES $COLUMNS
 1569      */
 1570     if ((eq(lp, STRNULL) && (eq(vp, STRLINES) || eq(vp, STRCOLUMNS))) ||
 1571     eq(vp, STRTERMCAP)) {
 1572     cleanup_until(lp);
 1573     check_window_size(1);
 1574     return;
 1575     }
 1576 
 1577     /*
 1578      * Change the size to the one directed by $LINES and $COLUMNS
 1579      */
 1580     if (eq(vp, STRLINES) || eq(vp, STRCOLUMNS)) {
 1581 #if 0
 1582     GotTermCaps = 0;
 1583 #endif
 1584     cleanup_until(lp);
 1585     ed_Init();
 1586     return;
 1587     }
 1588 #endif /* SIG_WINDOW */
 1589     cleanup_until(lp);
 1590 }
 1591 
 1592 /*ARGSUSED*/
 1593 void
 1594 dounsetenv(Char **v, struct command *c)
 1595 {
 1596     Char  **ep, *p, *n, *name;
 1597     int     i, maxi;
 1598 
 1599     USE(c);
 1600     /*
 1601      * Find the longest environment variable
 1602      */
 1603     for (maxi = 0, ep = STR_environ; *ep; ep++) {
 1604     for (i = 0, p = *ep; *p && *p != '='; p++, i++)
 1605         continue;
 1606     if (i > maxi)
 1607         maxi = i;
 1608     }
 1609 
 1610     name = xmalloc((maxi + 1) * sizeof(Char));
 1611     cleanup_push(name, xfree);
 1612 
 1613     while (++v && *v) 
 1614     for (maxi = 1; maxi;)
 1615         for (maxi = 0, ep = STR_environ; *ep; ep++) {
 1616         for (n = name, p = *ep; *p && *p != '='; *n++ = *p++)
 1617             continue;
 1618         *n = '\0';
 1619         if (!Gmatch(name, *v))
 1620             continue;
 1621         maxi = 1;
 1622 
 1623         /* Unset the name. This wasn't being done until
 1624          * later but most of the stuff following won't
 1625          * work (particularly the setlocale() and getenv()
 1626          * stuff) as intended until the name is actually
 1627          * removed. (sg)
 1628          */
 1629         Unsetenv(name);
 1630 
 1631         if (eq(name, STRNOREBIND)) {
 1632             NoNLSRebind = 0;
 1633             MapsAreInited = 0;
 1634             NLSMapsAreInited = 0;
 1635             ed_InitMaps();
 1636         }
 1637 #ifdef apollo
 1638         else if (eq(name, STRSYSTYPE))
 1639             dohash(NULL, NULL);
 1640 #endif /* apollo */
 1641         else if (islocale_var(name)) {
 1642 #ifdef NLS
 1643             int     k;
 1644 
 1645 # ifdef SETLOCALEBUG
 1646             dont_free = 1;
 1647 # endif /* SETLOCALEBUG */
 1648             (void) setlocale(LC_ALL, "");
 1649 # ifdef LC_COLLATE
 1650             (void) setlocale(LC_COLLATE, "");
 1651 # endif
 1652 # ifdef LC_CTYPE
 1653             (void) setlocale(LC_CTYPE, ""); /* for iscntrl */
 1654 # endif /* LC_CTYPE */
 1655 # ifdef NLS_CATALOGS
 1656 #  ifdef LC_MESSAGES
 1657             (void) setlocale(LC_MESSAGES, "");
 1658 #  endif /* LC_MESSAGES */
 1659             nlsclose();
 1660             nlsinit();
 1661 # endif /* NLS_CATALOGS */
 1662 # ifdef SETLOCALEBUG
 1663             dont_free = 0;
 1664 # endif /* SETLOCALEBUG */
 1665 # ifdef STRCOLLBUG
 1666             fix_strcoll_bug();
 1667 # endif /* STRCOLLBUG */
 1668             tw_cmd_free();/* since the collation sequence has changed */
 1669             for (k = 0200; k <= 0377 && !Isprint(CTL_ESC(k)); k++)
 1670             continue;
 1671             AsciiOnly = MB_CUR_MAX == 1 && k > 0377;
 1672 #else /* !NLS */
 1673             AsciiOnly = getenv("LANG") == NULL &&
 1674             getenv("LC_CTYPE") == NULL;
 1675 #endif /* NLS */
 1676             NLSMapsAreInited = 0;
 1677             ed_Init();
 1678             if (MapsAreInited && !NLSMapsAreInited)
 1679             ed_InitNLSMaps();
 1680 
 1681         }
 1682 #ifdef WINNT_NATIVE
 1683         else if (eq(name,(STRtcshlang))) {
 1684             nls_dll_unload();
 1685             nlsinit();
 1686         }
 1687 #endif /* WINNT_NATIVE */
 1688 #ifdef COLOR_LS_F
 1689         else if (eq(name, STRLS_COLORS))
 1690             parseLS_COLORS(n);
 1691         else if (eq(name, STRLSCOLORS))
 1692             parseLSCOLORS(n);
 1693 #endif /* COLOR_LS_F */
 1694 #ifdef NLS_CATALOGS
 1695         else if (eq(name, STRNLSPATH)) {
 1696             nlsclose();
 1697             nlsinit();
 1698         }
 1699 #endif
 1700         /*
 1701          * start again cause the environment changes
 1702          */
 1703         break;
 1704         }
 1705     cleanup_until(name);
 1706 }
 1707 
 1708 void
 1709 tsetenv(const Char *name, const Char *val)
 1710 {
 1711 #ifdef SETENV_IN_LIB
 1712 /*
 1713  * XXX: This does not work right, since tcsh cannot track changes to
 1714  * the environment this way. (the builtin setenv without arguments does
 1715  * not print the right stuff neither does unsetenv). This was for Mach,
 1716  * it is not needed anymore.
 1717  */
 1718 #undef setenv
 1719     char   *cname;
 1720 
 1721     if (name == NULL)
 1722     return;
 1723     cname = strsave(short2str(name));
 1724     setenv(cname, short2str(val), 1);
 1725     xfree(cname);
 1726 #else /* !SETENV_IN_LIB */
 1727     Char **ep = STR_environ;
 1728     const Char *ccp;
 1729     Char *cp, *dp;
 1730     Char   *blk[2];
 1731     Char  **oep = ep;
 1732 
 1733 #ifdef WINNT_NATIVE
 1734     nt_set_env(name,val);
 1735 #endif /* WINNT_NATIVE */
 1736     for (; *ep; ep++) {
 1737 #ifdef WINNT_NATIVE
 1738     for (ccp = name, dp = *ep; *ccp && Tolower(*ccp & TRIM) == Tolower(*dp);
 1739                 ccp++, dp++)
 1740 #else
 1741     for (ccp = name, dp = *ep; *ccp && (*ccp & TRIM) == *dp; ccp++, dp++)
 1742 #endif /* WINNT_NATIVE */
 1743         continue;
 1744     if (*ccp != 0 || *dp != '=')
 1745         continue;
 1746     cp = Strspl(STRequal, val);
 1747     xfree(*ep);
 1748     *ep = strip(Strspl(name, cp));
 1749     xfree(cp);
 1750     blkfree((Char **) environ);
 1751     environ = short2blk(STR_environ);
 1752     return;
 1753     }
 1754     cp = Strspl(name, STRequal);
 1755     blk[0] = strip(Strspl(cp, val));
 1756     xfree(cp);
 1757     blk[1] = 0;
 1758     STR_environ = blkspl(STR_environ, blk);
 1759     blkfree((Char **) environ);
 1760     environ = short2blk(STR_environ);
 1761     xfree(oep);
 1762 #endif /* SETENV_IN_LIB */
 1763 }
 1764 
 1765 void
 1766 Unsetenv(Char *name)
 1767 {
 1768     Char **ep = STR_environ;
 1769     Char *cp, *dp;
 1770     Char **oep = ep;
 1771 
 1772 #ifdef WINNT_NATIVE
 1773     nt_set_env(name,NULL);
 1774 #endif /*WINNT_NATIVE */
 1775     for (; *ep; ep++) {
 1776     for (cp = name, dp = *ep; *cp && *cp == *dp; cp++, dp++)
 1777         continue;
 1778     if (*cp != 0 || *dp != '=')
 1779         continue;
 1780     cp = *ep;
 1781     *ep = 0;
 1782     STR_environ = blkspl(STR_environ, ep + 1);
 1783     blkfree((Char **) environ);
 1784     environ = short2blk(STR_environ);
 1785     *ep = cp;
 1786     xfree(cp);
 1787     xfree(oep);
 1788     return;
 1789     }
 1790 }
 1791 
 1792 /*ARGSUSED*/
 1793 void
 1794 doumask(Char **v, struct command *c)
 1795 {
 1796     Char *cp = v[1];
 1797     int i;
 1798 
 1799     USE(c);
 1800     if (cp == 0) {
 1801     i = (int)umask(0);
 1802     (void) umask(i);
 1803     xprintf("%o\n", i);
 1804     return;
 1805     }
 1806     i = 0;
 1807     while (Isdigit(*cp) && *cp != '8' && *cp != '9')
 1808     i = i * 8 + *cp++ - '0';
 1809     if (*cp || i < 0 || i > 0777)
 1810     stderror(ERR_NAME | ERR_MASK);
 1811     (void) umask(i);
 1812 }
 1813 
 1814 #ifndef HAVENOLIMIT
 1815 # ifndef BSDLIMIT
 1816    typedef long RLIM_TYPE;
 1817 #  ifdef _OSD_POSIX /* BS2000 */
 1818 #   include <ulimit.h>
 1819 #  endif
 1820 #  ifndef RLIM_INFINITY
 1821 #   if !defined(_MINIX) && !defined(__clipper__) && !defined(_CRAY)
 1822     extern RLIM_TYPE ulimit();
 1823 #   endif /* ! _MINIX && !__clipper__ */
 1824 #   define RLIM_INFINITY 0x003fffff
 1825 #   define RLIMIT_FSIZE 1
 1826 #  endif /* RLIM_INFINITY */
 1827 #  ifdef aiws
 1828 #   define toset(a) (((a) == 3) ? 1004 : (a) + 1)
 1829 #   define RLIMIT_DATA  3
 1830 #   define RLIMIT_STACK 1005
 1831 #  else /* aiws */
 1832 #   define toset(a) ((a) + 1)
 1833 #  endif /* aiws */
 1834 # else /* BSDLIMIT */
 1835 #  if (defined(BSD4_4) || defined(__linux__) || defined(__GNU__) || defined(__GLIBC__) || (HPUXVERSION >= 1100)) && !defined(__386BSD__)
 1836     typedef rlim_t RLIM_TYPE;
 1837 #  else
 1838 #   if defined(SOLARIS2) || (defined(sgi) && SYSVREL > 3)
 1839      typedef rlim_t RLIM_TYPE;
 1840 #   else
 1841 #    if defined(_SX)
 1842       typedef long long RLIM_TYPE;
 1843 #    else /* !_SX */
 1844       typedef unsigned long RLIM_TYPE;
 1845 #    endif /* _SX */
 1846 #   endif /* SOLARIS2 || (sgi && SYSVREL > 3) */
 1847 #  endif /* BSD4_4 && !__386BSD__  */
 1848 # endif /* BSDLIMIT */
 1849 
 1850 # if (HPUXVERSION > 700) && (HPUXVERSION < 1100) && defined(BSDLIMIT)
 1851 /* Yes hpux8.0 has limits but <sys/resource.h> does not make them public */
 1852 /* Yes, we could have defined _KERNEL, and -I/etc/conf/h, but is that better? */
 1853 #  ifndef RLIMIT_CPU
 1854 #   define RLIMIT_CPU       0
 1855 #   define RLIMIT_FSIZE     1
 1856 #   define RLIMIT_DATA      2
 1857 #   define RLIMIT_STACK     3
 1858 #   define RLIMIT_CORE      4
 1859 #   define RLIMIT_RSS       5
 1860 #   define RLIMIT_NOFILE    6
 1861 #  endif /* RLIMIT_CPU */
 1862 #  ifndef RLIM_INFINITY
 1863 #   define RLIM_INFINITY    0x7fffffff
 1864 #  endif /* RLIM_INFINITY */
 1865    /*
 1866     * old versions of HP/UX counted limits in 512 bytes
 1867     */
 1868 #  ifndef SIGRTMIN
 1869 #   define FILESIZE512
 1870 #  endif /* SIGRTMIN */
 1871 # endif /* (HPUXVERSION > 700) && (HPUXVERSION < 1100) && BSDLIMIT */
 1872 
 1873 # if SYSVREL > 3 && defined(BSDLIMIT) && !defined(_SX)
 1874 /* In order to use rusage, we included "/usr/ucbinclude/sys/resource.h" in */
 1875 /* sh.h.  However, some SVR4 limits are defined in <sys/resource.h>.  Rather */
 1876 /* than include both and get warnings, we define the extra SVR4 limits here. */
 1877 /* XXX: I don't understand if RLIMIT_AS is defined, why don't we define */
 1878 /* RLIMIT_VMEM based on it? */
 1879 #  ifndef RLIMIT_VMEM
 1880 #   define RLIMIT_VMEM  6
 1881 #  endif
 1882 #  ifndef RLIMIT_AS
 1883 #   define RLIMIT_AS    RLIMIT_VMEM
 1884 #  endif
 1885 # endif /* SYSVREL > 3 && BSDLIMIT */
 1886 
 1887 # if (defined(__linux__) || defined(__GNU__) || defined(__GLIBC__))
 1888 #  if defined(RLIMIT_AS) && !defined(RLIMIT_VMEM)
 1889 #   define RLIMIT_VMEM  RLIMIT_AS
 1890 #  endif
 1891 /*
 1892  * Oh well, <asm-generic/resource.h> has it, but <bits/resource.h> does not
 1893  * Linux headers: When the left hand does not know what the right hand does.
 1894  */
 1895 #  if defined(RLIMIT_RTPRIO) && !defined(RLIMIT_RTTIME)
 1896 #   define RLIMIT_RTTIME (RLIMIT_RTPRIO + 1)
 1897 #  endif
 1898 # endif
 1899 
 1900 struct limits limits[] = 
 1901 {
 1902 # ifdef RLIMIT_CPU
 1903     { RLIMIT_CPU,   "cputime",  1,  "seconds"   },
 1904 # endif /* RLIMIT_CPU */
 1905 
 1906 # ifdef RLIMIT_FSIZE
 1907 #  ifndef aiws
 1908     { RLIMIT_FSIZE,     "filesize", 1024,   "kbytes"    },
 1909 #  else
 1910     { RLIMIT_FSIZE,     "filesize", 512,    "blocks"    },
 1911 #  endif /* aiws */
 1912 # endif /* RLIMIT_FSIZE */
 1913 
 1914 # ifdef RLIMIT_DATA
 1915     { RLIMIT_DATA,  "datasize", 1024,   "kbytes"    },
 1916 # endif /* RLIMIT_DATA */
 1917 
 1918 # ifdef RLIMIT_STACK
 1919 #  ifndef aiws
 1920     { RLIMIT_STACK,     "stacksize",    1024,   "kbytes"    },
 1921 #  else
 1922     { RLIMIT_STACK,     "stacksize",    1024 * 1024,    "kbytes"},
 1923 #  endif /* aiws */
 1924 # endif /* RLIMIT_STACK */
 1925 
 1926 # ifdef RLIMIT_CORE
 1927     { RLIMIT_CORE,  "coredumpsize", 1024,   "kbytes"    },
 1928 # endif /* RLIMIT_CORE */
 1929 
 1930 # ifdef RLIMIT_RSS
 1931     { RLIMIT_RSS,   "memoryuse",    1024,   "kbytes"    },
 1932 # endif /* RLIMIT_RSS */
 1933 
 1934 # ifdef RLIMIT_UMEM
 1935     { RLIMIT_UMEM,  "memoryuse",    1024,   "kbytes"    },
 1936 # endif /* RLIMIT_UMEM */
 1937 
 1938 # ifdef RLIMIT_VMEM
 1939     { RLIMIT_VMEM,  "vmemoryuse",   1024,   "kbytes"    },
 1940 # endif /* RLIMIT_VMEM */
 1941 
 1942 # if defined(RLIMIT_HEAP) /* found on BS2000/OSD systems */
 1943     { RLIMIT_HEAP,  "heapsize", 1024,   "kbytes"    },
 1944 # endif /* RLIMIT_HEAP */
 1945 
 1946 # ifdef RLIMIT_NOFILE
 1947     { RLIMIT_NOFILE,    "descriptors", 1,   ""      },
 1948 # endif /* RLIMIT_NOFILE */
 1949 
 1950 # ifdef RLIMIT_NPTS
 1951     { RLIMIT_NPTS,  "pseudoterminals", 1,   ""      },
 1952 # endif /* RLIMIT_NPTS */
 1953 
 1954 # ifdef RLIMIT_KQUEUES
 1955     { RLIMIT_KQUEUES,   "kqueues",  1,  ""      },
 1956 # endif /* RLIMIT_KQUEUES */
 1957 
 1958 # ifdef RLIMIT_CONCUR
 1959     { RLIMIT_CONCUR,    "concurrency", 1,   "thread(s)" },
 1960 # endif /* RLIMIT_CONCUR */
 1961 
 1962 # ifdef RLIMIT_MEMLOCK
 1963     { RLIMIT_MEMLOCK,   "memorylocked", 1024,   "kbytes"    },
 1964 # endif /* RLIMIT_MEMLOCK */
 1965 
 1966 # ifdef RLIMIT_NPROC
 1967     { RLIMIT_NPROC, "maxproc",  1,  ""      },
 1968 # endif /* RLIMIT_NPROC */
 1969 
 1970 # ifdef RLIMIT_NTHR
 1971     { RLIMIT_NTHR,  "maxthread",    1,  ""      },
 1972 # endif /* RLIMIT_NTHR */
 1973 
 1974 # if defined(RLIMIT_OFILE) && !defined(RLIMIT_NOFILE)
 1975     { RLIMIT_OFILE, "openfiles",    1,  ""      },
 1976 # endif /* RLIMIT_OFILE && !defined(RLIMIT_NOFILE) */
 1977 
 1978 # ifdef RLIMIT_SBSIZE
 1979     { RLIMIT_SBSIZE,    "sbsize",   1,  ""      },
 1980 # endif /* RLIMIT_SBSIZE */
 1981 
 1982 # ifdef RLIMIT_SWAP 
 1983     { RLIMIT_SWAP,  "swapsize", 1024,   "kbytes"    }, 
 1984 # endif /* RLIMIT_SWAP */ 
 1985 
 1986 # ifdef RLIMIT_LOCKS 
 1987     { RLIMIT_LOCKS, "maxlocks", 1,  ""      }, 
 1988 # endif /* RLIMIT_LOCKS */ 
 1989 
 1990 # ifdef RLIMIT_POSIXLOCKS
 1991     { RLIMIT_POSIXLOCKS,"posixlocks",   1,  ""      },
 1992 # endif /* RLIMIT_POSIXLOCKS */
 1993 
 1994 # ifdef RLIMIT_SIGPENDING 
 1995     { RLIMIT_SIGPENDING,"maxsignal",    1,  ""      }, 
 1996 # endif /* RLIMIT_SIGPENDING */ 
 1997 
 1998 # ifdef RLIMIT_MSGQUEUE 
 1999     { RLIMIT_MSGQUEUE,  "maxmessage",   1,  ""      }, 
 2000 # endif /* RLIMIT_MSGQUEUE */ 
 2001 
 2002 # ifdef RLIMIT_NICE 
 2003     { RLIMIT_NICE,  "maxnice",  1,  ""      }, 
 2004 # endif /* RLIMIT_NICE */ 
 2005 
 2006 # ifdef RLIMIT_RTPRIO 
 2007     { RLIMIT_RTPRIO,    "maxrtprio",    1,  ""      }, 
 2008 # endif /* RLIMIT_RTPRIO */ 
 2009 
 2010 # ifdef RLIMIT_RTTIME 
 2011     { RLIMIT_RTTIME,    "maxrttime",    1,  "usec"      }, 
 2012 # endif /* RLIMIT_RTTIME */ 
 2013 
 2014     { -1,       NULL,       0,  NULL        }
 2015 };
 2016 
 2017 static struct limits *findlim   (Char *);
 2018 static RLIM_TYPE getval     (struct limits *, Char **);
 2019 static int strtail      (Char *, const char *);
 2020 static void limtail     (Char *, const char *);
 2021 static void limtail2        (Char *, const char *, const char *);
 2022 static void plim        (struct limits *, int);
 2023 static int setlim       (struct limits *, int, RLIM_TYPE);
 2024 
 2025 #ifdef convex
 2026 static  RLIM_TYPE
 2027 restrict_limit(double value)
 2028 {
 2029     /*
 2030      * is f too large to cope with? return the maximum or minimum int
 2031      */
 2032     if (value > (double) INT_MAX)
 2033     return (RLIM_TYPE) INT_MAX;
 2034     else if (value < (double) INT_MIN)
 2035     return (RLIM_TYPE) INT_MIN;
 2036     else
 2037     return (RLIM_TYPE) value;
 2038 }
 2039 #else /* !convex */
 2040 # define restrict_limit(x)  ((RLIM_TYPE) (x))
 2041 #endif /* convex */
 2042 
 2043 
 2044 static struct limits *
 2045 findlim(Char *cp)
 2046 {
 2047     struct limits *lp, *res;
 2048 
 2049     res = NULL;
 2050     for (lp = limits; lp->limconst >= 0; lp++)
 2051     if (prefix(cp, str2short(lp->limname))) {
 2052         if (res)
 2053         stderror(ERR_NAME | ERR_AMBIG);
 2054         res = lp;
 2055     }
 2056     if (res)
 2057     return (res);
 2058     stderror(ERR_NAME | ERR_LIMIT);
 2059     /* NOTREACHED */
 2060     return (0);
 2061 }
 2062 
 2063 /*ARGSUSED*/
 2064 void
 2065 dolimit(Char **v, struct command *c)
 2066 {
 2067     struct limits *lp;
 2068     RLIM_TYPE limit;
 2069     int    hard = 0;
 2070 
 2071     USE(c);
 2072     v++;
 2073     if (*v && eq(*v, STRmh)) {
 2074     hard = 1;
 2075     v++;
 2076     }
 2077     if (*v == 0) {
 2078     for (lp = limits; lp->limconst >= 0; lp++)
 2079         plim(lp, hard);
 2080     return;
 2081     }
 2082     lp = findlim(v[0]);
 2083     if (v[1] == 0) {
 2084     plim(lp, hard);
 2085     return;
 2086     }
 2087     limit = getval(lp, v + 1);
 2088     if (setlim(lp, hard, limit) < 0)
 2089     stderror(ERR_SILENT);
 2090 }
 2091 
 2092 static  RLIM_TYPE
 2093 getval(struct limits *lp, Char **v)
 2094 {
 2095     float f;
 2096     Char   *cp = *v++;
 2097 
 2098     f = atof(short2str(cp));
 2099 
 2100 # ifdef convex
 2101     /*
 2102      * is f too large to cope with. limit f to minint, maxint  - X-6768 by
 2103      * strike
 2104      */
 2105     if ((f < (double) INT_MIN) || (f > (double) INT_MAX)) {
 2106     stderror(ERR_NAME | ERR_TOOLARGE);
 2107     }
 2108 # endif /* convex */
 2109 
 2110     while (Isdigit(*cp) || *cp == '.' || *cp == 'e' || *cp == 'E')
 2111     cp++;
 2112     if (*cp == 0) {
 2113     if (*v == 0)
 2114         return restrict_limit((f * lp->limdiv) + 0.5);
 2115     cp = *v;
 2116     }
 2117     switch (*cp) {
 2118 # ifdef RLIMIT_CPU
 2119     case ':':
 2120     if (lp->limconst != RLIMIT_CPU)
 2121         goto badscal;
 2122     return f == 0.0 ? (RLIM_TYPE) 0 : restrict_limit((f * 60.0 + atof(short2str(cp + 1))));
 2123     case 'h':
 2124     if (lp->limconst != RLIMIT_CPU)
 2125         goto badscal;
 2126     limtail(cp, "hours");
 2127     f *= 3600.0;
 2128     break;
 2129 # endif /* RLIMIT_CPU */
 2130     case 'm':
 2131 # ifdef RLIMIT_CPU
 2132     if (lp->limconst == RLIMIT_CPU) {
 2133         limtail(cp, "minutes");
 2134         f *= 60.0;
 2135         break;
 2136     }
 2137 # endif /* RLIMIT_CPU */
 2138     limtail2(cp, "megabytes", "mbytes");
 2139     f *= 1024.0 * 1024.0;
 2140     break;
 2141 # ifdef RLIMIT_CPU
 2142     case 's':
 2143     if (lp->limconst != RLIMIT_CPU)
 2144         goto badscal;
 2145     limtail(cp, "seconds");
 2146     break;
 2147 # endif /* RLIMIT_CPU */
 2148     case 'G':
 2149     *cp = 'g';
 2150     /*FALLTHROUGH*/
 2151     case 'g':
 2152 # ifdef RLIMIT_CPU
 2153     if (lp->limconst == RLIMIT_CPU)
 2154         goto badscal;
 2155 # endif /* RLIMIT_CPU */
 2156     limtail2(cp, "gigabytes", "gbytes");
 2157     f *= 1024.0 * 1024.0 * 1024.0;
 2158     break;
 2159     case 'M':
 2160 # ifdef RLIMIT_CPU
 2161     if (lp->limconst == RLIMIT_CPU)
 2162         goto badscal;
 2163 # endif /* RLIMIT_CPU */
 2164     *cp = 'm';
 2165     limtail2(cp, "megabytes", "mbytes");
 2166     f *= 1024.0 * 1024.0;
 2167     break;
 2168     case 'k':
 2169 # ifdef RLIMIT_CPU
 2170     if (lp->limconst == RLIMIT_CPU)
 2171         goto badscal;
 2172 # endif /* RLIMIT_CPU */
 2173     limtail2(cp, "kilobytes", "kbytes");
 2174     f *= 1024.0;
 2175     break;
 2176     case 'b':
 2177 # ifdef RLIMIT_CPU
 2178     if (lp->limconst == RLIMIT_CPU)
 2179         goto badscal;
 2180 # endif /* RLIMIT_CPU */
 2181     limtail(cp, "blocks");
 2182     f *= 512.0;
 2183     break;
 2184     case 'u':
 2185     limtail(cp, "unlimited");
 2186     return ((RLIM_TYPE) RLIM_INFINITY);
 2187     default:
 2188 # ifdef RLIMIT_CPU
 2189 badscal:
 2190 # endif /* RLIMIT_CPU */
 2191     stderror(ERR_NAME | ERR_SCALEF);
 2192     }
 2193 # ifdef convex
 2194     return f == 0.0 ? (RLIM_TYPE) 0 : restrict_limit((f + 0.5));
 2195 # else
 2196     f += 0.5;
 2197     if (f > (float) ((RLIM_TYPE) RLIM_INFINITY))
 2198     return ((RLIM_TYPE) RLIM_INFINITY);
 2199     else
 2200     return ((RLIM_TYPE) f);
 2201 # endif /* convex */
 2202 }
 2203 
 2204 static int
 2205 strtail(Char *cp, const char *str)
 2206 {
 2207     while (*cp && *cp == (Char)*str)
 2208     cp++, str++;
 2209     return (*cp != '\0');
 2210 }
 2211 
 2212 static void
 2213 limtail(Char *cp, const char *str)
 2214 {
 2215     if (strtail(cp, str))
 2216     stderror(ERR_BADSCALE, str);
 2217 }
 2218 
 2219 static void
 2220 limtail2(Char *cp, const char *str1, const char *str2)
 2221 {
 2222     if (strtail(cp, str1) && strtail(cp, str2))
 2223     stderror(ERR_BADSCALE, str1);
 2224 }
 2225 
 2226 /*ARGSUSED*/
 2227 static void
 2228 plim(struct limits *lp, int hard)
 2229 {
 2230 # ifdef BSDLIMIT
 2231     struct rlimit rlim;
 2232 # endif /* BSDLIMIT */
 2233     RLIM_TYPE limit;
 2234     int     xdiv = lp->limdiv;
 2235 
 2236     xprintf("%-13.13s", lp->limname);
 2237 
 2238 # ifndef BSDLIMIT
 2239     limit = ulimit(lp->limconst, 0);
 2240 #  ifdef aiws
 2241     if (lp->limconst == RLIMIT_DATA)
 2242     limit -= 0x20000000;
 2243 #  endif /* aiws */
 2244 # else /* BSDLIMIT */
 2245     (void) getrlimit(lp->limconst, &rlim);
 2246     limit = hard ? rlim.rlim_max : rlim.rlim_cur;
 2247 # endif /* BSDLIMIT */
 2248 
 2249 # if !defined(BSDLIMIT) || defined(FILESIZE512)
 2250     /*
 2251      * Christos: filesize comes in 512 blocks. we divide by 2 to get 1024
 2252      * blocks. Note we cannot pre-multiply cause we might overflow (A/UX)
 2253      */
 2254     if (lp->limconst == RLIMIT_FSIZE) {
 2255     if (limit >= (RLIM_INFINITY / 512))
 2256         limit = RLIM_INFINITY;
 2257     else
 2258         xdiv = (xdiv == 1024 ? 2 : 1);
 2259     }
 2260 # endif /* !BSDLIMIT || FILESIZE512 */
 2261 
 2262     if (limit == RLIM_INFINITY)
 2263     xprintf("unlimited");
 2264     else
 2265 # if defined(RLIMIT_CPU) && defined(_OSD_POSIX)
 2266     if (lp->limconst == RLIMIT_CPU &&
 2267         (unsigned long)limit >= 0x7ffffffdUL)
 2268     xprintf("unlimited");
 2269     else
 2270 # endif
 2271 # ifdef RLIMIT_CPU
 2272     if (lp->limconst == RLIMIT_CPU)
 2273     psecs(limit);
 2274     else
 2275 # endif /* RLIMIT_CPU */
 2276     xprintf("%ld %s", (long) (limit / xdiv), lp->limscale);
 2277     xputchar('\n');
 2278 }
 2279 
 2280 /*ARGSUSED*/
 2281 void
 2282 dounlimit(Char **v, struct command *c)
 2283 {
 2284     struct limits *lp;
 2285     int    lerr = 0;
 2286     int    hard = 0;
 2287     int    force = 0;
 2288 
 2289     USE(c);
 2290     while (*++v && **v == '-') {
 2291     Char   *vp = *v;
 2292     while (*++vp)
 2293         switch (*vp) {
 2294         case 'f':
 2295         force = 1;
 2296         break;
 2297         case 'h':
 2298         hard = 1;
 2299         break;
 2300         default:
 2301         stderror(ERR_ULIMUS);
 2302         break;
 2303         }
 2304     }
 2305 
 2306     if (*v == 0) {
 2307     for (lp = limits; lp->limconst >= 0; lp++)
 2308         if (setlim(lp, hard, (RLIM_TYPE) RLIM_INFINITY) < 0)
 2309         lerr++;
 2310     if (!force && lerr)
 2311         stderror(ERR_SILENT);
 2312     return;
 2313     }
 2314     while (*v) {
 2315     lp = findlim(*v++);
 2316     if (setlim(lp, hard, (RLIM_TYPE) RLIM_INFINITY) < 0 && !force)
 2317         stderror(ERR_SILENT);
 2318     }
 2319 }
 2320 
 2321 static int
 2322 setlim(struct limits *lp, int hard, RLIM_TYPE limit)
 2323 {
 2324 # ifdef BSDLIMIT
 2325     struct rlimit rlim;
 2326 
 2327     (void) getrlimit(lp->limconst, &rlim);
 2328 
 2329 #  ifdef FILESIZE512
 2330     /* Even though hpux has setrlimit(), it expects fsize in 512 byte blocks */
 2331     if (limit != RLIM_INFINITY && lp->limconst == RLIMIT_FSIZE)
 2332     limit /= 512;
 2333 #  endif /* FILESIZE512 */
 2334     if (hard)
 2335     rlim.rlim_max = limit;
 2336     else if (limit == RLIM_INFINITY && euid != 0)
 2337     rlim.rlim_cur = rlim.rlim_max;
 2338     else
 2339     rlim.rlim_cur = limit;
 2340 
 2341     if (rlim.rlim_cur > rlim.rlim_max)
 2342     rlim.rlim_max = rlim.rlim_cur;
 2343 
 2344     if (setrlimit(lp->limconst, &rlim) < 0) {
 2345 # else /* BSDLIMIT */
 2346     if (limit != RLIM_INFINITY && lp->limconst == RLIMIT_FSIZE)
 2347     limit /= 512;
 2348 # ifdef aiws
 2349     if (lp->limconst == RLIMIT_DATA)
 2350     limit += 0x20000000;
 2351 # endif /* aiws */
 2352     if (ulimit(toset(lp->limconst), limit) < 0) {
 2353 # endif /* BSDLIMIT */
 2354         int err;
 2355         char *op, *type;
 2356 
 2357     err = errno;
 2358     op = strsave(limit == RLIM_INFINITY ? CGETS(15, 2, "remove") :
 2359                 CGETS(15, 3, "set"));
 2360     cleanup_push(op, xfree);
 2361     type = strsave(hard ? CGETS(15, 4, " hard") : "");
 2362     cleanup_push(type, xfree);
 2363     xprintf(CGETS(15, 1, "%s: %s: Can't %s%s limit (%s)\n"), bname,
 2364         lp->limname, op, type, strerror(err));
 2365     cleanup_until(op);
 2366     return (-1);
 2367     }
 2368     return (0);
 2369 }
 2370 
 2371 #endif /* !HAVENOLIMIT */
 2372 
 2373 /*ARGSUSED*/
 2374 void
 2375 dosuspend(Char **v, struct command *c)
 2376 {
 2377 #ifdef BSDJOBS
 2378     struct sigaction old;
 2379 #endif /* BSDJOBS */
 2380 
 2381     USE(c);
 2382     USE(v);
 2383 
 2384     if (loginsh)
 2385     stderror(ERR_SUSPLOG);
 2386     untty();
 2387 
 2388 #ifdef BSDJOBS
 2389     sigaction(SIGTSTP, NULL, &old);
 2390     signal(SIGTSTP, SIG_DFL);
 2391     (void) kill(0, SIGTSTP);
 2392     /* the shell stops here */
 2393     sigaction(SIGTSTP, &old, NULL);
 2394 #else /* !BSDJOBS */
 2395     stderror(ERR_JOBCONTROL);
 2396 #endif /* BSDJOBS */
 2397 
 2398 #ifdef BSDJOBS
 2399     if (tpgrp != -1) {
 2400     if (grabpgrp(FSHTTY, opgrp) == -1)
 2401         stderror(ERR_SYSTEM, "tcgetpgrp", strerror(errno));
 2402     (void) setpgid(0, shpgrp);
 2403     (void) tcsetpgrp(FSHTTY, shpgrp);
 2404     }
 2405 #endif /* BSDJOBS */
 2406     (void) setdisc(FSHTTY);
 2407 }
 2408 
 2409 /* This is the dreaded EVAL built-in.
 2410  *   If you don't fiddle with file descriptors, and reset didfds,
 2411  *   this command will either ignore redirection inside or outside
 2412  *   its arguments, e.g. eval "date >x"  vs.  eval "date" >x
 2413  *   The stuff here seems to work, but I did it by trial and error rather
 2414  *   than really knowing what was going on.  If tpgrp is zero, we are
 2415  *   probably a background eval, e.g. "eval date &", and we want to
 2416  *   make sure that any processes we start stay in our pgrp.
 2417  *   This is also the case for "time eval date" -- stay in same pgrp.
 2418  *   Otherwise, under stty tostop, processes will stop in the wrong
 2419  *   pgrp, with no way for the shell to get them going again.  -IAN!
 2420  */
 2421 
 2422 struct doeval_state
 2423 {
 2424     Char **evalvec, *evalp;
 2425     int didfds;
 2426 #ifndef CLOSE_ON_EXEC
 2427     int didcch;
 2428 #endif
 2429     int saveIN, saveOUT, saveDIAG;
 2430     int SHIN, SHOUT, SHDIAG;
 2431 };
 2432 
 2433 static void
 2434 doeval_cleanup(void *xstate)
 2435 {
 2436     struct doeval_state *state;
 2437 
 2438     state = xstate;
 2439     evalvec = state->evalvec;
 2440     evalp = state->evalp;
 2441     doneinp = 0;
 2442 #ifndef CLOSE_ON_EXEC
 2443     didcch = state->didcch;
 2444 #endif /* CLOSE_ON_EXEC */
 2445     didfds = state->didfds;
 2446     if (state->saveIN != SHIN)
 2447     xclose(SHIN);
 2448     if (state->saveOUT != SHOUT)
 2449     xclose(SHOUT);
 2450     if (state->saveDIAG != SHDIAG)
 2451     xclose(SHDIAG);
 2452     close_on_exec(SHIN = dmove(state->saveIN, state->SHIN), 1);
 2453     close_on_exec(SHOUT = dmove(state->saveOUT, state->SHOUT), 1);
 2454     close_on_exec(SHDIAG = dmove(state->saveDIAG, state->SHDIAG), 1);
 2455     if (didfds) {
 2456     close_on_exec(dcopy(SHIN, 0), 1);
 2457     close_on_exec(dcopy(SHOUT, 1), 1);
 2458     close_on_exec(dcopy(SHDIAG, 2), 1);
 2459     }
 2460 }
 2461 
 2462 static Char **Ggv;
 2463 /*ARGSUSED*/
 2464 void
 2465 doeval(Char **v, struct command *c)
 2466 {
 2467     struct doeval_state state;
 2468     int gflag, my_reenter;
 2469     Char **gv;
 2470     jmp_buf_t osetexit;
 2471 
 2472     USE(c);
 2473     v++;
 2474     if (*v == 0)
 2475     return;
 2476     gflag = tglob(v);
 2477     if (gflag) {
 2478     gv = v = globall(v, gflag);
 2479     if (v == 0)
 2480         stderror(ERR_NOMATCH);
 2481     cleanup_push(gv, blk_cleanup);
 2482     v = copyblk(v);
 2483     }
 2484     else {
 2485     gv = NULL;
 2486     v = copyblk(v);
 2487     trim(v);
 2488     }
 2489 
 2490     Ggv = gv;
 2491     state.evalvec = evalvec;
 2492     state.evalp = evalp;
 2493     state.didfds = didfds;
 2494 #ifndef CLOSE_ON_EXEC
 2495     state.didcch = didcch;
 2496 #endif /* CLOSE_ON_EXEC */
 2497     state.SHIN = SHIN;
 2498     state.SHOUT = SHOUT;
 2499     state.SHDIAG = SHDIAG;
 2500 
 2501     (void)close_on_exec(state.saveIN = dcopy(SHIN, -1), 1);
 2502     (void)close_on_exec(state.saveOUT = dcopy(SHOUT, -1), 1);
 2503     (void)close_on_exec(state.saveDIAG = dcopy(SHDIAG, -1), 1);
 2504 
 2505     cleanup_push(&state, doeval_cleanup);
 2506 
 2507     getexit(osetexit);
 2508 
 2509     /* PWP: setjmp/longjmp bugfix for optimizing compilers */
 2510 #ifdef cray
 2511     my_reenter = 1;             /* assume non-zero return val */
 2512     if (setexit() == 0) {
 2513     my_reenter = 0;         /* Oh well, we were wrong */
 2514 #else /* !cray */
 2515     if ((my_reenter = setexit()) == 0) {
 2516 #endif /* cray */
 2517     evalvec = v;
 2518     evalp = 0;
 2519     (void)close_on_exec(SHIN = dcopy(0, -1), 1);
 2520     (void)close_on_exec(SHOUT = dcopy(1, -1), 1);
 2521     (void)close_on_exec(SHDIAG = dcopy(2, -1), 1);
 2522 #ifndef CLOSE_ON_EXEC
 2523     didcch = 0;
 2524 #endif /* CLOSE_ON_EXEC */
 2525     didfds = 0;
 2526     gv = Ggv;
 2527     process(0);
 2528     Ggv = gv;
 2529     }
 2530 
 2531     if (my_reenter == 0) {
 2532     cleanup_until(&state);
 2533     if (Ggv)
 2534         cleanup_until(Ggv);
 2535     }
 2536 
 2537     resexit(osetexit);
 2538     if (my_reenter)
 2539     stderror(ERR_SILENT);
 2540 }
 2541 
 2542 /*************************************************************************/
 2543 /* print list of builtin commands */
 2544 
 2545 static void
 2546 lbuffed_cleanup (void *dummy)
 2547 {
 2548     USE(dummy);
 2549     lbuffed = 1;
 2550 }
 2551 
 2552 /*ARGSUSED*/
 2553 void
 2554 dobuiltins(Char **v, struct command *c)
 2555 {
 2556     /* would use print_by_column() in tw.parse.c but that assumes
 2557      * we have an array of Char * to pass.. (sg)
 2558      */
 2559     const struct biltins *b;
 2560     int row, col, columns, rows;
 2561     unsigned int w, maxwidth;
 2562 
 2563     USE(c);
 2564     USE(v);
 2565     lbuffed = 0;        /* turn off line buffering */
 2566     cleanup_push(&lbuffed, lbuffed_cleanup);
 2567 
 2568     /* find widest string */
 2569     for (maxwidth = 0, b = bfunc; b < &bfunc[nbfunc]; ++b)
 2570     maxwidth = max(maxwidth, strlen(b->bname));
 2571     ++maxwidth;                 /* for space */
 2572 
 2573     columns = (TermH + 1) / maxwidth;   /* PWP: terminal size change */
 2574     if (!columns)
 2575     columns = 1;
 2576     rows = (nbfunc + (columns - 1)) / columns;
 2577 
 2578     for (b = bfunc, row = 0; row < rows; row++) {
 2579     for (col = 0; col < columns; col++) {
 2580         if (b < &bfunc[nbfunc]) {
 2581         w = strlen(b->bname);
 2582         xprintf("%s", b->bname);
 2583         if (col < (columns - 1))    /* Not last column? */
 2584             for (; w < maxwidth; w++)
 2585             xputchar(' ');
 2586         ++b;
 2587         }
 2588     }
 2589     if (row < (rows - 1)) {
 2590         if (Tty_raw_mode)
 2591         xputchar('\r');
 2592         xputchar('\n');
 2593     }
 2594     }
 2595 #ifdef WINNT_NATIVE
 2596     nt_print_builtins(maxwidth);
 2597 #else
 2598     if (Tty_raw_mode)
 2599     xputchar('\r');
 2600     xputchar('\n');
 2601 #endif /* WINNT_NATIVE */
 2602 
 2603     cleanup_until(&lbuffed);        /* turn back on line buffering */
 2604     flush();
 2605 }
 2606 
 2607 #ifdef NLS_CATALOGS
 2608 char *
 2609 xcatgets(nl_catd ctd, int set_id, int msg_id, const char *s)
 2610 {
 2611     char *res;
 2612 
 2613     errno = 0;
 2614     while ((res = catgets(ctd, set_id, msg_id, s)) == s && errno == EINTR) {
 2615     handle_pending_signals();
 2616     errno = 0;
 2617     }
 2618     return res;
 2619 }
 2620 
 2621 
 2622 # if defined(HAVE_ICONV) && defined(HAVE_NL_LANGINFO)
 2623 char *
 2624 iconv_catgets(nl_catd ctd, int set_id, int msg_id, const char *s)
 2625 {
 2626     static char *buf = NULL;
 2627     static size_t buf_size = 0;
 2628   
 2629     char *orig, *dest, *p;
 2630     ICONV_CONST char *src;
 2631     size_t src_size, dest_size;
 2632   
 2633     orig = xcatgets(ctd, set_id, msg_id, s);
 2634     if (catgets_iconv == (iconv_t)-1 || orig == s)
 2635         return orig;
 2636     src = orig;
 2637     src_size = strlen(src) + 1;
 2638     if (buf == NULL && (buf = xmalloc(buf_size = src_size + 32)) == NULL)
 2639     return orig;
 2640     dest = buf;
 2641     while (src_size != 0) {
 2642         dest_size = buf + buf_size - dest;
 2643     if (iconv(catgets_iconv, &src, &src_size, &dest, &dest_size)
 2644         == (size_t)-1) {
 2645         switch (errno) {
 2646         case E2BIG:
 2647         if ((p = xrealloc(buf, buf_size * 2)) == NULL)
 2648             return orig;
 2649         buf_size *= 2;
 2650         dest = p + (dest - buf);
 2651         buf = p;
 2652         break;
 2653         
 2654         case EILSEQ: case EINVAL: default:
 2655         return orig;
 2656         }
 2657     }
 2658     }
 2659     return buf;
 2660 }
 2661 # endif /* HAVE_ICONV && HAVE_NL_LANGINFO */
 2662 #endif /* NLS_CATALOGS */
 2663 
 2664 void
 2665 nlsinit(void)
 2666 {
 2667 #ifdef NLS_CATALOGS
 2668     static const char default_catalog[] = "tcsh";
 2669 
 2670     char *catalog = (char *)(intptr_t)default_catalog;
 2671 
 2672     if (adrof(STRcatalog) != NULL)
 2673     catalog = xasprintf("tcsh.%s", short2str(varval(STRcatalog)));
 2674 #ifdef NL_CAT_LOCALE /* POSIX-compliant. */
 2675     /*
 2676      * Check if LC_MESSAGES is set in the environment and use it, if so.
 2677      * If not, fall back to the setting of LANG.
 2678      */
 2679     catd = catopen(catalog, tgetenv(STRLC_MESSAGES) ? NL_CAT_LOCALE : 0);
 2680 #else /* pre-POSIX */
 2681 # ifndef MCLoadBySet
 2682 #  define MCLoadBySet 0
 2683 #  endif
 2684     catd = catopen(catalog, MCLoadBySet);
 2685 #endif
 2686     if (catalog != default_catalog)
 2687     xfree(catalog);
 2688 #if defined(HAVE_ICONV) && defined(HAVE_NL_LANGINFO)
 2689     /* xcatgets (), not CGETS, the charset name should be in ASCII anyway. */
 2690     catgets_iconv = iconv_open (nl_langinfo (CODESET),
 2691                 xcatgets(catd, 255, 1, "UTF-8"));
 2692 #endif /* HAVE_ICONV && HAVE_NL_LANGINFO */
 2693 #endif /* NLS_CATALOGS */
 2694 #ifdef WINNT_NATIVE
 2695     nls_dll_init();
 2696 #endif /* WINNT_NATIVE */
 2697     errinit();      /* init the errorlist in correct locale */
 2698     mesginit();     /* init the messages for signals */
 2699     dateinit();     /* init the messages for dates */
 2700     editinit();     /* init the editor messages */
 2701     terminit();     /* init the termcap messages */
 2702 }
 2703 
 2704 void
 2705 nlsclose(void)
 2706 {
 2707 #ifdef NLS_CATALOGS
 2708 #if defined(HAVE_ICONV) && defined(HAVE_NL_LANGINFO)
 2709     if (catgets_iconv != (iconv_t)-1) {
 2710     iconv_close(catgets_iconv);
 2711     catgets_iconv = (iconv_t)-1;
 2712     }
 2713 #endif /* HAVE_ICONV && HAVE_NL_LANGINFO */
 2714     if (catd != (nl_catd)-1) {
 2715     /*
 2716      * catclose can call other functions which can call longjmp
 2717      * making us re-enter this code. Prevent infinite recursion
 2718      * by resetting catd. Problem reported and solved by:
 2719      * Gerhard Niklasch
 2720      */
 2721     nl_catd oldcatd = catd;
 2722     catd = (nl_catd)-1;
 2723     while (catclose(oldcatd) == -1 && errno == EINTR)
 2724         handle_pending_signals();
 2725     }
 2726 #endif /* NLS_CATALOGS */
 2727 }
 2728 
 2729 int
 2730 getYN(const char *prompt)
 2731 {
 2732     int doit;
 2733     char c;
 2734 
 2735     xprintf("%s", prompt);
 2736     flush();
 2737     (void) force_read(SHIN, &c, sizeof(c));
 2738     /* 
 2739      * Perhaps we should use the yesexpr from the
 2740      * actual locale
 2741      */
 2742     doit = (strchr(CGETS(22, 14, "Yy"), c) != NULL);
 2743     while (c != '\n' && force_read(SHIN, &c, sizeof(c)) == sizeof(c))
 2744     continue;
 2745     return doit;
 2746 }