expand.c

Go to the documentation of this file.
00001 /*-
00002  * Copyright (c) 1991 The Regents of the University of California.
00003  * All rights reserved.
00004  *
00005  * This code is derived from software contributed to Berkeley by
00006  * Kenneth Almquist.
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted provided that the following conditions
00010  * are met:
00011  * 1. Redistributions of source code must retain the above copyright
00012  *    notice, this list of conditions and the following disclaimer.
00013  * 2. Redistributions in binary form must reproduce the above copyright
00014  *    notice, this list of conditions and the following disclaimer in the
00015  *    documentation and/or other materials provided with the distribution.
00016  * 3. All advertising materials mentioning features or use of this software
00017  *    must display the following acknowledgement:
00018  *      This product includes software developed by the University of
00019  *      California, Berkeley and its contributors.
00020  * 4. Neither the name of the University nor the names of its contributors
00021  *    may be used to endorse or promote products derived from this software
00022  *    without specific prior written permission.
00023  *
00024  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
00025  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00026  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00027  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
00028  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00029  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00030  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00031  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00032  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00033  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00034  * SUCH DAMAGE.
00035  */
00036 
00037 #ifndef lint
00038 static char sccsid[] = "@(#)expand.c    5.1 (Berkeley) 3/7/91";
00039 #endif /* not lint */
00040 
00041 /*
00042  * Routines to expand arguments to commands.  We have to deal with
00043  * backquotes, shell variables, and file metacharacters.
00044  */
00045 
00046 #include "shell.h"
00047 #include "main.h"
00048 #include "nodes.h"
00049 #include "eval.h"
00050 #include "expand.h"
00051 #include "syntax.h"
00052 #include "parser.h"
00053 #include "jobs.h"
00054 #include "options.h"
00055 #include "var.h"
00056 #include "input.h"
00057 #include "output.h"
00058 #include "memalloc.h"
00059 #include "error.h"
00060 #include "mystring.h"
00061 #include <sys/types.h>
00062 #include <sys/stat.h>
00063 #include <errno.h>
00064 #include <dirent.h>
00065 #if USEGETPW
00066 #include <pwd.h>
00067 #endif
00068 
00069 /*
00070  * Structure specifying which parts of the string should be searched
00071  * for IFS characters.
00072  */
00073 
00074 struct ifsregion {
00075         struct ifsregion *next; /* next region in list */
00076         int begoff;             /* offset of start of region */
00077         int endoff;             /* offset of end of region */
00078         int nulonly;            /* search for nul bytes only */
00079 };
00080 
00081 
00082 char *expdest;                  /* output of current string */
00083 struct nodelist *argbackq;      /* list of back quote expressions */
00084 struct ifsregion ifsfirst;      /* first struct in list of ifs regions */
00085 struct ifsregion *ifslastp;     /* last struct in list */
00086 struct arglist exparg;          /* holds expanded arg list */
00087 #if UDIR || TILDE
00088 /*
00089  * Set if the last argument processed had /u/logname or ~logname expanded.
00090  * This variable is read by the cd command.
00091  */
00092 int didudir;
00093 #endif
00094 
00095 #ifdef __STDC__
00096 STATIC void argstr(char *, int);
00097 STATIC void expbackq(union node *, int, int);
00098 STATIC char *evalvar(char *, int);
00099 STATIC int varisset(int);
00100 STATIC void varvalue(int, int, int);
00101 STATIC void recordregion(int, int, int);
00102 STATIC void ifsbreakup(char *, struct arglist *);
00103 STATIC void expandmeta(struct strlist *);
00104 STATIC void expmeta(char *, char *);
00105 STATIC void addfname(char *);
00106 STATIC struct strlist *expsort(struct strlist *);
00107 STATIC struct strlist *msort(struct strlist *, int);
00108 STATIC int pmatch(char *, char *);
00109 #else
00110 STATIC void argstr();
00111 STATIC void expbackq();
00112 STATIC char *evalvar();
00113 STATIC int varisset();
00114 STATIC void varvalue();
00115 STATIC void recordregion();
00116 STATIC void ifsbreakup();
00117 STATIC void expandmeta();
00118 STATIC void expmeta();
00119 STATIC void addfname();
00120 STATIC struct strlist *expsort();
00121 STATIC struct strlist *msort();
00122 STATIC int pmatch();
00123 #endif
00124 #if UDIR || TILDE
00125 #ifdef __STDC__
00126 STATIC char *expudir(char *);
00127 #else
00128 STATIC char *expudir();
00129 #endif
00130 #endif /* UDIR || TILDE */
00131 
00132 
00133 
00134 /*
00135  * Expand shell variables and backquotes inside a here document.
00136  */
00137 
00138 void
00139 expandhere(arg, fd)
00140         union node *arg;        /* the document */
00141         int fd;                 /* where to write the expanded version */
00142         {
00143         herefd = fd;
00144         expandarg(arg, (struct arglist *)NULL, 0);
00145         xwrite(fd, stackblock(), expdest - stackblock());
00146 }
00147 
00148 
00149 /*
00150  * Perform variable substitution and command substitution on an argument,
00151  * placing the resulting list of arguments in arglist.  If full is true,
00152  * perform splitting and file name expansion.  When arglist is NULL, perform
00153  * here document expansion.
00154  */
00155 
00156 void
00157 expandarg(arg, arglist, full)
00158         union node *arg;
00159         struct arglist *arglist;
00160         {
00161         struct strlist *sp;
00162         char *p;
00163 
00164 #if UDIR || TILDE
00165         didudir = 0;
00166 #endif
00167         argbackq = arg->narg.backquote;
00168         STARTSTACKSTR(expdest);
00169         ifsfirst.next = NULL;
00170         ifslastp = NULL;
00171         argstr(arg->narg.text, full);
00172         if (arglist == NULL)
00173                 return;                 /* here document expanded */
00174         STPUTC('\0', expdest);
00175         p = grabstackstr(expdest);
00176         exparg.lastp = &exparg.list;
00177         if (full) {
00178                 ifsbreakup(p, &exparg);
00179                 *exparg.lastp = NULL;
00180                 exparg.lastp = &exparg.list;
00181                 expandmeta(exparg.list);
00182         } else {
00183                 sp = (struct strlist *)stalloc(sizeof (struct strlist));
00184                 sp->text = p;
00185                 *exparg.lastp = sp;
00186                 exparg.lastp = &sp->next;
00187         }
00188         while (ifsfirst.next != NULL) {
00189                 struct ifsregion *ifsp;
00190                 INTOFF;
00191                 ifsp = ifsfirst.next->next;
00192                 ckfree(ifsfirst.next);
00193                 ifsfirst.next = ifsp;
00194                 INTON;
00195         }
00196         *exparg.lastp = NULL;
00197         if (exparg.list) {
00198                 *arglist->lastp = exparg.list;
00199                 arglist->lastp = exparg.lastp;
00200         }
00201 }
00202 
00203 
00204 
00205 /*
00206  * Perform variable and command substitution.  If full is set, output CTLESC
00207  * characters to allow for further processing.  If full is not set, treat
00208  * $@ like $* since no splitting will be performed.
00209  */
00210 
00211 STATIC void
00212 argstr(p, full)
00213         register char *p;
00214         {
00215         char c;
00216 
00217         for (;;) {
00218                 switch (c = *p++) {
00219                 case '\0':
00220                 case CTLENDVAR:
00221                         goto breakloop;
00222                 case CTLESC:
00223                         if (full)
00224                                 STPUTC(c, expdest);
00225                         c = *p++;
00226                         STPUTC(c, expdest);
00227                         break;
00228                 case CTLVAR:
00229                         p = evalvar(p, full);
00230                         break;
00231                 case CTLBACKQ:
00232                 case CTLBACKQ|CTLQUOTE:
00233                         expbackq(argbackq->n, c & CTLQUOTE, full);
00234                         argbackq = argbackq->next;
00235                         break;
00236                 default:
00237                         STPUTC(c, expdest);
00238                 }
00239         }
00240 breakloop:;
00241 }
00242 
00243 
00244 /*
00245  * Expand stuff in backwards quotes.
00246  */
00247 
00248 STATIC void
00249 expbackq(cmd, quoted, full)
00250         union node *cmd;
00251         {
00252         struct backcmd in;
00253         int i;
00254         char buf[128];
00255         char *p;
00256         char *dest = expdest;
00257         struct ifsregion saveifs, *savelastp;
00258         struct nodelist *saveargbackq;
00259         char lastc;
00260         int startloc = dest - stackblock();
00261         char const *syntax = quoted? DQSYNTAX : BASESYNTAX;
00262         int saveherefd;
00263 
00264         INTOFF;
00265         saveifs = ifsfirst;
00266         savelastp = ifslastp;
00267         saveargbackq = argbackq;
00268         saveherefd = herefd;      
00269         herefd = -1;
00270         p = grabstackstr(dest);
00271         evalbackcmd(cmd, &in);
00272         ungrabstackstr(p, dest);
00273         ifsfirst = saveifs;
00274         ifslastp = savelastp;
00275         argbackq = saveargbackq;
00276         herefd = saveherefd;
00277 
00278         p = in.buf;
00279         lastc = '\0';
00280         for (;;) {
00281                 if (--in.nleft < 0) {
00282                         if (in.fd < 0)
00283                                 break;
00284                         while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR);
00285                         TRACE(("expbackq: read returns %d\n", i));
00286                         if (i <= 0)
00287                                 break;
00288                         p = buf;
00289                         in.nleft = i - 1;
00290                 }
00291                 lastc = *p++;
00292                 if (lastc != '\0') {
00293                         if (full && syntax[lastc] == CCTL)
00294                                 STPUTC(CTLESC, dest);
00295                         STPUTC(lastc, dest);
00296                 }
00297         }
00298         if (lastc == '\n') {
00299                 STUNPUTC(dest);
00300         }
00301         if (in.fd >= 0)
00302                 close(in.fd);
00303         if (in.buf)
00304                 ckfree(in.buf);
00305         if (in.jp)
00306                 exitstatus = waitforjob(in.jp);
00307         if (quoted == 0)
00308                 recordregion(startloc, dest - stackblock(), 0);
00309         TRACE(("evalbackq: size=%d: \"%.*s\"\n",
00310                 (dest - stackblock()) - startloc,
00311                 (dest - stackblock()) - startloc,
00312                 stackblock() + startloc));
00313         expdest = dest;
00314         INTON;
00315 }
00316 
00317 
00318 
00319 /*
00320  * Expand a variable, and return a pointer to the next character in the
00321  * input string.
00322  */
00323 
00324 STATIC char *
00325 evalvar(p, full)
00326         char *p;
00327         {
00328         int subtype;
00329         int flags;
00330         char *var;
00331         char *val;
00332         int c;
00333         int set;
00334         int special;
00335         int startloc;
00336 
00337         flags = *p++;
00338         subtype = flags & VSTYPE;
00339         var = p;
00340         special = 0;
00341         if (! is_name(*p))
00342                 special = 1;
00343         p = strchr(p, '=') + 1;
00344 again: /* jump here after setting a variable with ${var=text} */
00345         if (special) {
00346                 set = varisset(*var);
00347                 val = NULL;
00348         } else {
00349                 val = lookupvar(var);
00350                 if (val == NULL || (flags & VSNUL) && val[0] == '\0') {
00351                         val = NULL;
00352                         set = 0;
00353                 } else
00354                         set = 1;
00355         }
00356         startloc = expdest - stackblock();
00357         if (set && subtype != VSPLUS) {
00358                 /* insert the value of the variable */
00359                 if (special) {
00360                         varvalue(*var, flags & VSQUOTE, full);
00361                 } else {
00362                         char const *syntax = (flags & VSQUOTE)? DQSYNTAX : BASESYNTAX;
00363 
00364                         while (*val) {
00365                                 if (full && syntax[*val] == CCTL)
00366                                         STPUTC(CTLESC, expdest);
00367                                 STPUTC(*val++, expdest);
00368                         }
00369                 }
00370         }
00371         if (subtype == VSPLUS)
00372                 set = ! set;
00373         if (((flags & VSQUOTE) == 0 || (*var == '@' && shellparam.nparam != 1))
00374          && (set || subtype == VSNORMAL))
00375                 recordregion(startloc, expdest - stackblock(), flags & VSQUOTE);
00376         if (! set && subtype != VSNORMAL) {
00377                 if (subtype == VSPLUS || subtype == VSMINUS) {
00378                         argstr(p, full);
00379                 } else {
00380                         char *startp;
00381                         int saveherefd = herefd;
00382                         herefd = -1;
00383                         argstr(p, 0);
00384                         STACKSTRNUL(expdest);
00385                         herefd = saveherefd;
00386                         startp = stackblock() + startloc;
00387                         if (subtype == VSASSIGN) {
00388                                 setvar(var, startp, 0);
00389                                 STADJUST(startp - expdest, expdest);
00390                                 flags &=~ VSNUL;
00391                                 goto again;
00392                         }
00393                         /* subtype == VSQUESTION */
00394                         if (*p != CTLENDVAR) {
00395                                 outfmt(&errout, "%s\n", startp);
00396                                 error((char *)NULL);
00397                         }
00398                         error("%.*s: parameter %snot set", p - var - 1,
00399                                 var, (flags & VSNUL)? "null or " : nullstr);
00400                 }
00401         }
00402         if (subtype != VSNORMAL) {      /* skip to end of alternative */
00403                 int nesting = 1;
00404                 for (;;) {
00405                         if ((c = *p++) == CTLESC)
00406                                 p++;
00407                         else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
00408                                 if (set) {
00409                                         if (argbackq != NULL)
00410                                                 argbackq = argbackq->next;
00411                                 }
00412                         } else if (c == CTLVAR) {
00413                                 if ((*p++ & VSTYPE) != VSNORMAL)
00414                                         nesting++;
00415                         } else if (c == CTLENDVAR) {
00416                                 if (--nesting == 0)
00417                                         break;
00418                         }
00419                 }
00420         }
00421         return p;
00422 }
00423 
00424 
00425 
00426 /*
00427  * Test whether a specialized variable is set.
00428  */
00429 
00430 STATIC int
00431 varisset(name)
00432         char name;
00433         {
00434         char **ap;
00435 
00436         if (name == '!') {
00437                 if (backgndpid == -1)
00438                         return 0;
00439         } else if (name == '@' || name == '*') {
00440                 if (*shellparam.p == NULL)
00441                         return 0;
00442         } else if ((unsigned)(name -= '1') <= '9' - '1') {
00443                 ap = shellparam.p;
00444                 do {
00445                         if (*ap++ == NULL)
00446                                 return 0;
00447                 } while (--name >= 0);
00448         }
00449         return 1;
00450 }
00451 
00452 
00453 
00454 /*
00455  * Add the value of a specialized variable to the stack string.
00456  */
00457 
00458 STATIC void
00459 varvalue(name, quoted, allow_split)
00460         char name;
00461         {
00462         int num;
00463         char temp[32];
00464         char *p;
00465         int i;
00466         extern int oexitstatus;
00467         char sep;
00468         char **ap;
00469         char const *syntax;
00470 
00471         switch (name) {
00472         case '$':
00473                 num = rootpid;
00474                 goto numvar;
00475         case '?':
00476                 num = oexitstatus;
00477                 goto numvar;
00478         case '#':
00479                 num = shellparam.nparam;
00480                 goto numvar;
00481         case '!':
00482                 num = backgndpid;
00483 numvar:
00484                 p = temp + 31;
00485                 temp[31] = '\0';
00486                 do {
00487                         *--p = num % 10 + '0';
00488                 } while ((num /= 10) != 0);
00489                 while (*p)
00490                         STPUTC(*p++, expdest);
00491                 break;
00492         case '-':
00493                 for (i = 0 ; optchar[i] ; i++) {
00494                         if (optval[i])
00495                                 STPUTC(optchar[i], expdest);
00496                 }
00497                 break;
00498         case '@':
00499                 if (allow_split) {
00500                         sep = '\0';
00501                         goto allargs;
00502                 }
00503                 /* fall through */                      
00504         case '*':
00505                 sep = ' ';
00506 allargs:
00507                 /* Only emit CTLESC if we will do further processing,
00508                    i.e. if allow_split is set.  */
00509                 syntax = quoted && allow_split ? DQSYNTAX : BASESYNTAX;
00510                 for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
00511                         /* should insert CTLESC characters */
00512                         while (*p) {
00513                                 if (syntax[*p] == CCTL)
00514                                         STPUTC(CTLESC, expdest);
00515                                 STPUTC(*p++, expdest);
00516                         }
00517                         if (*ap)
00518                                 STPUTC(sep, expdest);
00519                 }
00520                 break;
00521         case '0':
00522                 p = arg0;
00523 string:
00524                 /* Only emit CTLESC if we will do further processing,
00525                    i.e. if allow_split is set.  */
00526                 syntax = quoted && allow_split ? DQSYNTAX : BASESYNTAX;
00527                 while (*p) {
00528                         if (syntax[*p] == CCTL)
00529                                 STPUTC(CTLESC, expdest);
00530                         STPUTC(*p++, expdest);
00531                 }
00532                 break;
00533         default:
00534                 if ((unsigned)(name -= '1') <= '9' - '1') {
00535                         p = shellparam.p[name];
00536                         goto string;
00537                 }
00538                 break;
00539         }
00540 }
00541 
00542 
00543 
00544 /*
00545  * Record the the fact that we have to scan this region of the
00546  * string for IFS characters.
00547  */
00548 
00549 STATIC void
00550 recordregion(start, end, nulonly) {
00551         register struct ifsregion *ifsp;
00552 
00553         if (ifslastp == NULL) {
00554                 ifsp = &ifsfirst;
00555         } else {
00556                 ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
00557                 ifslastp->next = ifsp;
00558         }
00559         ifslastp = ifsp;
00560         ifslastp->next = NULL;
00561         ifslastp->begoff = start;
00562         ifslastp->endoff = end;
00563         ifslastp->nulonly = nulonly;
00564 }
00565 
00566 
00567 
00568 /*
00569  * Break the argument string into pieces based upon IFS and add the
00570  * strings to the argument list.  The regions of the string to be
00571  * searched for IFS characters have been stored by recordregion.
00572  */
00573 
00574 STATIC void
00575 ifsbreakup(string, arglist)
00576         char *string;
00577         struct arglist *arglist;
00578         {
00579         struct ifsregion *ifsp;
00580         struct strlist *sp;
00581         char *start;
00582         register char *p;
00583         char *q;
00584         char *ifs;
00585 
00586         start = string;
00587         if (ifslastp != NULL) {
00588                 ifsp = &ifsfirst;
00589                 do {
00590                         p = string + ifsp->begoff;
00591                         ifs = ifsp->nulonly? nullstr : ifsval();
00592                         while (p < string + ifsp->endoff) {
00593                                 q = p;
00594                                 if (*p == CTLESC)
00595                                         p++;
00596                                 if (strchr(ifs, *p++)) {
00597                                         if (q > start || *ifs != ' ') {
00598                                                 *q = '\0';
00599                                                 sp = (struct strlist *)stalloc(sizeof *sp);
00600                                                 sp->text = start;
00601                                                 *arglist->lastp = sp;
00602                                                 arglist->lastp = &sp->next;
00603                                         }
00604                                         if (*ifs == ' ') {
00605                                                 for (;;) {
00606                                                         if (p >= string + ifsp->endoff)
00607                                                                 break;
00608                                                         q = p;
00609                                                         if (*p == CTLESC)
00610                                                                 p++;
00611                                                         if (strchr(ifs, *p++) == NULL) {
00612                                                                 p = q;
00613                                                                 break;
00614                                                         }
00615                                                 }
00616                                         }
00617                                         start = p;
00618                                 }
00619                         }
00620                 } while ((ifsp = ifsp->next) != NULL);
00621                 if (*start || (*ifs != ' ' && start > string)) {
00622                         sp = (struct strlist *)stalloc(sizeof *sp);
00623                         sp->text = start;
00624                         *arglist->lastp = sp;
00625                         arglist->lastp = &sp->next;
00626                 }
00627         } else {
00628                 sp = (struct strlist *)stalloc(sizeof *sp);
00629                 sp->text = start;
00630                 *arglist->lastp = sp;
00631                 arglist->lastp = &sp->next;
00632         }
00633 }
00634 
00635 
00636 
00637 /*
00638  * Expand shell metacharacters.  At this point, the only control characters
00639  * should be escapes.  The results are stored in the list exparg.
00640  */
00641 
00642 char *expdir;
00643 
00644 
00645 STATIC void
00646 expandmeta(str)
00647         struct strlist *str;
00648         {
00649         char *p;
00650         struct strlist **savelastp;
00651         struct strlist *sp;
00652         char c;
00653 
00654         while (str) {
00655                 if (fflag)
00656                         goto nometa;
00657                 p = str->text;
00658 #if UDIR
00659                 if (p[0] == '/' && p[1] == 'u' && p[2] == '/')
00660                         str->text = p = expudir(p);
00661 #endif
00662 #if TILDE
00663                 if (p[0] == '~')
00664                         str->text = p = expudir(p);
00665 #endif
00666                 for (;;) {                      /* fast check for meta chars */
00667                         if ((c = *p++) == '\0')
00668                                 goto nometa;
00669                         if (c == '*' || c == '?' || c == '[' || c == '!')
00670                                 break;
00671                 }
00672                 savelastp = exparg.lastp;
00673                 INTOFF;
00674                 if (expdir == NULL)
00675                 {
00676                         int i = strlen(str->text);
00677                         expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
00678                 }
00679                 expmeta(expdir, str->text);
00680                 ckfree(expdir);
00681                 expdir = NULL;
00682                 INTON;
00683                 if (exparg.lastp == savelastp) {
00684                         if (! zflag) {
00685 nometa:
00686                                 *exparg.lastp = str;
00687                                 rmescapes(str->text);
00688                                 exparg.lastp = &str->next;
00689                         }
00690                 } else {
00691                         *exparg.lastp = NULL;
00692                         *savelastp = sp = expsort(*savelastp);
00693                         while (sp->next != NULL)
00694                                 sp = sp->next;
00695                         exparg.lastp = &sp->next;
00696                 }
00697                 str = str->next;
00698         }
00699 }
00700 
00701 
00702 #if UDIR || TILDE
00703 /*
00704  * UDIR: Expand /u/username into the home directory for the specified user.
00705  * TILDE: Expand ~username into the home directory for the specified user.
00706  * We hope not to use the getpw stuff here, because then we would have to load
00707  * in stdio and who knows what else.  With networked password files there is
00708  * no choice alas.
00709  */
00710 
00711 #define MAXLOGNAME 32
00712 #define MAXPWLINE 128
00713 
00714 char *pfgets();
00715 
00716 
00717 STATIC char *
00718 expudir(path)
00719         char *path;
00720         {
00721         register char *p, *q, *r;
00722         char name[MAXLOGNAME];
00723         char line[MAXPWLINE];
00724         int i;
00725 #if USEGETPW
00726         struct passwd *pw;
00727 #endif
00728 
00729         r = path;                               /* result on failure */
00730         p = r + (*r == '~' ? 1 : 3);    /* the 1 skips "~", 3 skips "/u/" */
00731         q = name;
00732         while (*p && *p != '/') {
00733                 if (q >= name + MAXLOGNAME - 1)
00734                         return r;               /* fail, name too long */
00735                 *q++ = *p++;
00736         }
00737         *q = '\0';
00738 
00739 #if TILDE
00740         if (*name == 0 && *r == '~') {
00741                 /* null name, use $HOME */
00742                 if ((q = lookupvar("HOME")) == NULL)
00743                         return r;               /* fail, home not set */
00744                 i = strlen(q);
00745                 r = stalloc(i + strlen(p) + 1);
00746                 scopy(q, r);
00747                 scopy(p, r + i);
00748                 TRACE(("expudir converts %s to %s\n", path, r));
00749                 didudir = 1;
00750                 path = r;
00751                 return r;
00752         }
00753 #endif
00754 #if !USEGETPW   /* can do without the bloat */
00755         setinputfile("/etc/passwd", 1);
00756         q = line + strlen(name);
00757         while (pfgets(line, MAXPWLINE) != NULL) {
00758                 if (line[0] == name[0] && prefix(name, line) && *q == ':') {
00759                         /* skip to start of home directory */
00760                         i = 4;
00761                         do {
00762                                 while (*++q && *q != ':');
00763                         } while (--i > 0);
00764                         if (*q == '\0')
00765                                 break;          /* fail, corrupted /etc/passwd */
00766                         q++;
00767                         for (r = q ; *r && *r != '\n' && *r != ':' ; r++);
00768                         *r = '\0';              /* nul terminate home directory */
00769                         i = r - q;              /* i = strlen(q) */
00770                         r = stalloc(i + strlen(p) + 1);
00771                         scopy(q, r);
00772                         scopy(p, r + i);
00773                         TRACE(("expudir converts %s to %s\n", path, r));
00774                         didudir = 1;
00775                         path = r;               /* succeed */
00776                         break;
00777                 }
00778         }
00779         popfile();
00780 #else
00781         if ((pw = getpwnam(name)) != NULL) {
00782                 /* user exists */
00783                 q = pw->pw_dir;
00784                 i = strlen(q);
00785                 r = stalloc(i + strlen(p) + 1);
00786                 scopy(q, r);
00787                 scopy(p, r + i);
00788                 TRACE(("expudir converts %s to %s\n", path, r));
00789                 didudir = 1;
00790                 path = r;
00791         }
00792         endpwent();
00793 #endif /* USEGETPW */
00794 
00795         return r;
00796 }
00797 #endif
00798 
00799 
00800 /*
00801  * Do metacharacter (i.e. *, ?, [...]) expansion.
00802  */
00803 
00804 STATIC void
00805 expmeta(enddir, name)
00806         char *enddir;
00807         char *name;
00808         {
00809         register char *p;
00810         char *q;
00811         char *start;
00812         char *endname;
00813         int metaflag;
00814         struct stat statb;
00815         DIR *dirp;
00816         struct dirent *dp;
00817         int atend;
00818         int matchdot;
00819 
00820         metaflag = 0;
00821         start = name;
00822         for (p = name ; ; p++) {
00823                 if (*p == '*' || *p == '?')
00824                         metaflag = 1;
00825                 else if (*p == '[') {
00826                         q = p + 1;
00827                         if (*q == '!')
00828                                 q++;
00829                         for (;;) {
00830                                 if (*q == CTLESC)
00831                                         q++;
00832                                 if (*q == '/' || *q == '\0')
00833                                         break;
00834                                 if (*++q == ']') {
00835                                         metaflag = 1;
00836                                         break;
00837                                 }
00838                         }
00839                 } else if (*p == '!' && p[1] == '!'     && (p == name || p[-1] == '/')) {
00840                         metaflag = 1;
00841                 } else if (*p == '\0')
00842                         break;
00843                 else if (*p == CTLESC)
00844                         p++;
00845                 if (*p == '/') {
00846                         if (metaflag)
00847                                 break;
00848                         start = p + 1;
00849                 }
00850         }
00851         if (metaflag == 0) {    /* we've reached the end of the file name */
00852                 if (enddir != expdir)
00853                         metaflag++;
00854                 for (p = name ; ; p++) {
00855                         if (*p == CTLESC)
00856                                 p++;
00857                         *enddir++ = *p;
00858                         if (*p == '\0')
00859                                 break;
00860                 }
00861                 if (metaflag == 0 || stat(expdir, &statb) >= 0)
00862                         addfname(expdir);
00863                 return;
00864         }
00865         endname = p;
00866         if (start != name) {
00867                 p = name;
00868                 while (p < start) {
00869                         if (*p == CTLESC)
00870                                 p++;
00871                         *enddir++ = *p++;
00872                 }
00873         }
00874         if (enddir == expdir) {
00875                 p = ".";
00876         } else if (enddir == expdir + 1 && *expdir == '/') {
00877                 p = "/";
00878         } else {
00879                 p = expdir;
00880                 enddir[-1] = '\0';
00881         }
00882         if ((dirp = opendir(p)) == NULL)
00883                 return;
00884         if (enddir != expdir)
00885                 enddir[-1] = '/';
00886         if (*endname == 0) {
00887                 atend = 1;
00888         } else {
00889                 atend = 0;
00890                 *endname++ = '\0';
00891         }
00892         matchdot = 0;
00893         if (start[0] == '.' || start[0] == CTLESC && start[1] == '.')
00894                 matchdot++;
00895         while (! int_pending() && (dp = readdir(dirp)) != NULL) {
00896                 if (dp->d_name[0] == '.' && ! matchdot)
00897                         continue;
00898                 if (patmatch(start, dp->d_name)) {
00899                         if (atend) {
00900                                 scopy(dp->d_name, enddir);
00901                                 addfname(expdir);
00902                         } else {
00903                                 char *q;
00904                                 for (p = enddir, q = dp->d_name ; *p++ = *q++ ;);
00905                                 p[-1] = '/';
00906                                 expmeta(p, endname);
00907                         }
00908                 }
00909         }
00910         closedir(dirp);
00911         if (! atend)
00912                 endname[-1] = '/';
00913 }
00914 
00915 
00916 /*
00917  * Add a file name to the list.
00918  */
00919 
00920 STATIC void
00921 addfname(name)
00922         char *name;
00923         {
00924         char *p;
00925         struct strlist *sp;
00926 
00927         p = stalloc(strlen(name) + 1);
00928         scopy(name, p);
00929         sp = (struct strlist *)stalloc(sizeof *sp);
00930         sp->text = p;
00931         *exparg.lastp = sp;
00932         exparg.lastp = &sp->next;
00933 }
00934 
00935 
00936 /*
00937  * Sort the results of file name expansion.  It calculates the number of
00938  * strings to sort and then calls msort (short for merge sort) to do the
00939  * work.
00940  */
00941 
00942 STATIC struct strlist *
00943 expsort(str)
00944         struct strlist *str;
00945         {
00946         int len;
00947         struct strlist *sp;
00948 
00949         len = 0;
00950         for (sp = str ; sp ; sp = sp->next)
00951                 len++;
00952         return msort(str, len);
00953 }
00954 
00955 
00956 STATIC struct strlist *
00957 msort(list, len)
00958         struct strlist *list;
00959         {
00960         struct strlist *p, *q;
00961         struct strlist **lpp;
00962         int half;
00963         int n;
00964 
00965         if (len <= 1)
00966                 return list;
00967         half = len >> 1;      
00968         p = list;
00969         for (n = half ; --n >= 0 ; ) {
00970                 q = p;
00971                 p = p->next;
00972         }
00973         q->next = NULL;                 /* terminate first half of list */
00974         q = msort(list, half);          /* sort first half of list */
00975         p = msort(p, len - half);               /* sort second half */
00976         lpp = &list;
00977         for (;;) {
00978                 if (strcmp(p->text, q->text) < 0) {
00979                         *lpp = p;
00980                         lpp = &p->next;
00981                         if ((p = *lpp) == NULL) {
00982                                 *lpp = q;
00983                                 break;
00984                         }
00985                 } else {
00986                         *lpp = q;
00987                         lpp = &q->next;
00988                         if ((q = *lpp) == NULL) {
00989                                 *lpp = p;
00990                                 break;
00991                         }
00992                 }
00993         }
00994         return list;
00995 }
00996 
00997 
00998 
00999 /*
01000  * Returns true if the pattern matches the string.
01001  */
01002 
01003 int
01004 patmatch(pattern, string)
01005         char *pattern;
01006         char *string;
01007         {
01008         if (pattern[0] == '!' && pattern[1] == '!')
01009                 return 1 - pmatch(pattern + 2, string);
01010         else
01011                 return pmatch(pattern, string);
01012 }
01013 
01014 
01015 STATIC int
01016 pmatch(pattern, string)
01017         char *pattern;
01018         char *string;
01019         {
01020         register char *p, *q;
01021         register char c;
01022 
01023         p = pattern;
01024         q = string;
01025         for (;;) {
01026                 switch (c = *p++) {
01027                 case '\0':
01028                         goto breakloop;
01029                 case CTLESC:
01030                         if (*q++ != *p++)
01031                                 return 0;
01032                         break;
01033                 case '?':
01034                         if (*q++ == '\0')
01035                                 return 0;
01036                         break;
01037                 case '*':
01038                         c = *p;
01039                         if (c != CTLESC && c != '?' && c != '*' && c != '[') {
01040                                 while (*q != c) {
01041                                         if (*q == '\0')
01042                                                 return 0;
01043                                         q++;
01044                                 }
01045                         }
01046                         do {
01047                                 if (pmatch(p, q))
01048                                         return 1;
01049                         } while (*q++ != '\0');
01050                         return 0;
01051                 case '[': {
01052                         char *endp;
01053                         int invert, found;
01054                         char chr;
01055 
01056                         endp = p;
01057                         if (*endp == '!')
01058                                 endp++;
01059                         for (;;) {
01060                                 if (*endp == '\0')
01061                                         goto dft;               /* no matching ] */
01062                                 if (*endp == CTLESC)
01063                                         endp++;
01064                                 if (*++endp == ']')
01065                                         break;
01066                         }
01067                         invert = 0;
01068                         if (*p == '!') {
01069                                 invert++;
01070                                 p++;
01071                         }
01072                         found = 0;
01073                         chr = *q++;
01074                         c = *p++;
01075                         do {
01076                                 if (c == CTLESC)
01077                                         c = *p++;
01078                                 if (*p == '-' && p[1] != ']') {
01079                                         p++;
01080                                         if (*p == CTLESC)
01081                                                 p++;
01082                                         if (chr >= c && chr <= *p)
01083                                                 found = 1;
01084                                         p++;
01085                                 } else {
01086                                         if (chr == c)
01087                                                 found = 1;
01088                                 }
01089                         } while ((c = *p++) != ']');
01090                         if (found == invert)
01091                                 return 0;
01092                         break;
01093                 }
01094 dft:        default:
01095                         if (*q++ != c)
01096                                 return 0;
01097                         break;
01098                 }
01099         }
01100 breakloop:
01101         if (*q != '\0')
01102                 return 0;
01103         return 1;
01104 }
01105 
01106 
01107 
01108 /*
01109  * Remove any CTLESC characters from a string.
01110  */
01111 
01112 void
01113 rmescapes(str)
01114         char *str;
01115         {
01116         register char *p, *q;
01117 
01118         p = str;
01119         while (*p != CTLESC) {
01120                 if (*p++ == '\0')
01121                         return;
01122         }
01123         q = p;
01124         while (*p) {
01125                 if (*p == CTLESC)
01126                         p++;
01127                 *q++ = *p++;
01128         }
01129         *q = '\0';
01130 }
01131 
01132 
01133 
01134 /*
01135  * See if a pattern matches in a case statement.
01136  */
01137 
01138 int
01139 casematch(pattern, val)
01140         union node *pattern;
01141         char *val;
01142         {
01143         struct stackmark smark;
01144         int result;
01145         char *p;
01146 
01147         setstackmark(&smark);
01148         argbackq = pattern->narg.backquote;
01149         STARTSTACKSTR(expdest);
01150         ifslastp = NULL;
01151         /* Preserve any CTLESC characters inserted previously, so that
01152            we won't expand reg exps which are inside strings.  */
01153         argstr(pattern->narg.text, 1);
01154         STPUTC('\0', expdest);
01155         p = grabstackstr(expdest);
01156         result = patmatch(p, val);
01157         popstackmark(&smark);
01158         return result;
01159 }

Generated on Fri Apr 14 22:56:40 2006 for minix by  doxygen 1.4.6