diff options
| author | Denis Vlasenko <vda.linux@googlemail.com> | 2007-02-23 01:04:22 +0000 |
|---|---|---|
| committer | Denis Vlasenko <vda.linux@googlemail.com> | 2007-02-23 01:04:22 +0000 |
| commit | aa74445514a71aebfa5d9b22c5ba1c47635b4dba (patch) | |
| tree | 7d6ec04eeef948208548e78e0041b49ce8f170e2 /shell | |
| parent | 0c032a49b9464296afe2461928252b1ddf772975 (diff) | |
| download | busybox-w32-aa74445514a71aebfa5d9b22c5ba1c47635b4dba.tar.gz busybox-w32-aa74445514a71aebfa5d9b22c5ba1c47635b4dba.tar.bz2 busybox-w32-aa74445514a71aebfa5d9b22c5ba1c47635b4dba.zip | |
ash: cleanup part 2
Diffstat (limited to 'shell')
| -rw-r--r-- | shell/ash.c | 3378 |
1 files changed, 1640 insertions, 1738 deletions
diff --git a/shell/ash.c b/shell/ash.c index 379e8ab7f..731b07996 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
| @@ -65,6 +65,13 @@ | |||
| 65 | #error "Do not even bother, ash will not run on uClinux" | 65 | #error "Do not even bother, ash will not run on uClinux" |
| 66 | #endif | 66 | #endif |
| 67 | 67 | ||
| 68 | #if DEBUG | ||
| 69 | #define TRACE(param) trace param | ||
| 70 | #define TRACEV(param) tracev param | ||
| 71 | #else | ||
| 72 | #define TRACE(param) | ||
| 73 | #define TRACEV(param) | ||
| 74 | #endif | ||
| 68 | 75 | ||
| 69 | #ifdef __GLIBC__ | 76 | #ifdef __GLIBC__ |
| 70 | /* glibc sucks */ | 77 | /* glibc sucks */ |
| @@ -141,6 +148,11 @@ static char optlist[NOPTS]; | |||
| 141 | 148 | ||
| 142 | /* ============ Misc data */ | 149 | /* ============ Misc data */ |
| 143 | 150 | ||
| 151 | static char nullstr[1]; /* zero length string */ | ||
| 152 | static const char homestr[] = "HOME"; | ||
| 153 | static const char snlfmt[] = "%s\n"; | ||
| 154 | static const char illnum[] = "Illegal number: %s"; | ||
| 155 | |||
| 144 | static int isloginsh; | 156 | static int isloginsh; |
| 145 | /* pid of main shell */ | 157 | /* pid of main shell */ |
| 146 | static int rootpid; | 158 | static int rootpid; |
| @@ -380,6 +392,11 @@ out2str(const char *p) | |||
| 380 | * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up. | 392 | * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up. |
| 381 | */ | 393 | */ |
| 382 | 394 | ||
| 395 | struct strlist { | ||
| 396 | struct strlist *next; | ||
| 397 | char *text; | ||
| 398 | }; | ||
| 399 | |||
| 383 | struct strpush { | 400 | struct strpush { |
| 384 | struct strpush *prev; /* preceding string on stack */ | 401 | struct strpush *prev; /* preceding string on stack */ |
| 385 | char *prevstring; | 402 | char *prevstring; |
| @@ -623,6 +640,16 @@ stunalloc(void *p) | |||
| 623 | stacknxt = p; | 640 | stacknxt = p; |
| 624 | } | 641 | } |
| 625 | 642 | ||
| 643 | /* | ||
| 644 | * Like strdup but works with the ash stack. | ||
| 645 | */ | ||
| 646 | static char * | ||
| 647 | ststrdup(const char *p) | ||
| 648 | { | ||
| 649 | size_t len = strlen(p) + 1; | ||
| 650 | return memcpy(stalloc(len), p, len); | ||
| 651 | } | ||
| 652 | |||
| 626 | static void | 653 | static void |
| 627 | setstackmark(struct stackmark *mark) | 654 | setstackmark(struct stackmark *mark) |
| 628 | { | 655 | { |
| @@ -785,19 +812,1055 @@ stack_putstr(const char *s, char *p) | |||
| 785 | return stack_nputstr(s, strlen(s), p); | 812 | return stack_nputstr(s, strlen(s), p); |
| 786 | } | 813 | } |
| 787 | 814 | ||
| 815 | static char * | ||
| 816 | _STPUTC(int c, char *p) | ||
| 817 | { | ||
| 818 | if (p == sstrend) | ||
| 819 | p = growstackstr(); | ||
| 820 | *p++ = c; | ||
| 821 | return p; | ||
| 822 | } | ||
| 788 | 823 | ||
| 789 | /* ============ Unsorted yet */ | 824 | #define STARTSTACKSTR(p) ((p) = stackblock()) |
| 825 | #define STPUTC(c, p) ((p) = _STPUTC((c), (p))) | ||
| 826 | #define CHECKSTRSPACE(n, p) \ | ||
| 827 | ({ \ | ||
| 828 | char *q = (p); \ | ||
| 829 | size_t l = (n); \ | ||
| 830 | size_t m = sstrend - q; \ | ||
| 831 | if (l > m) \ | ||
| 832 | (p) = makestrspace(l, q); \ | ||
| 833 | 0; \ | ||
| 834 | }) | ||
| 835 | #define USTPUTC(c, p) (*p++ = (c)) | ||
| 836 | #define STACKSTRNUL(p) ((p) == sstrend ? (p = growstackstr(), *p = '\0') : (*p = '\0')) | ||
| 837 | #define STUNPUTC(p) (--p) | ||
| 838 | #define STTOPC(p) p[-1] | ||
| 839 | #define STADJUST(amount, p) (p += (amount)) | ||
| 790 | 840 | ||
| 841 | #define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock()) | ||
| 842 | #define ungrabstackstr(s, p) stunalloc((s)) | ||
| 843 | #define stackstrend() ((void *)sstrend) | ||
| 791 | 844 | ||
| 792 | static void setpwd(const char *, int); | ||
| 793 | 845 | ||
| 794 | /* expand.h */ | 846 | /* ============ String helpers */ |
| 795 | 847 | ||
| 796 | struct strlist { | 848 | /* |
| 797 | struct strlist *next; | 849 | * prefix -- see if pfx is a prefix of string. |
| 798 | char *text; | 850 | */ |
| 851 | static char * | ||
| 852 | prefix(const char *string, const char *pfx) | ||
| 853 | { | ||
| 854 | while (*pfx) { | ||
| 855 | if (*pfx++ != *string++) | ||
| 856 | return 0; | ||
| 857 | } | ||
| 858 | return (char *) string; | ||
| 859 | } | ||
| 860 | |||
| 861 | /* | ||
| 862 | * Check for a valid number. This should be elsewhere. | ||
| 863 | */ | ||
| 864 | static int | ||
| 865 | is_number(const char *p) | ||
| 866 | { | ||
| 867 | do { | ||
| 868 | if (!isdigit(*p)) | ||
| 869 | return 0; | ||
| 870 | } while (*++p != '\0'); | ||
| 871 | return 1; | ||
| 872 | } | ||
| 873 | |||
| 874 | /* | ||
| 875 | * Convert a string of digits to an integer, printing an error message on | ||
| 876 | * failure. | ||
| 877 | */ | ||
| 878 | static int | ||
| 879 | number(const char *s) | ||
| 880 | { | ||
| 881 | if (!is_number(s)) | ||
| 882 | ash_msg_and_raise_error(illnum, s); | ||
| 883 | return atoi(s); | ||
| 884 | } | ||
| 885 | |||
| 886 | /* | ||
| 887 | * Produce a possibly single quoted string suitable as input to the shell. | ||
| 888 | * The return string is allocated on the stack. | ||
| 889 | */ | ||
| 890 | static char * | ||
| 891 | single_quote(const char *s) | ||
| 892 | { | ||
| 893 | char *p; | ||
| 894 | |||
| 895 | STARTSTACKSTR(p); | ||
| 896 | |||
| 897 | do { | ||
| 898 | char *q; | ||
| 899 | size_t len; | ||
| 900 | |||
| 901 | len = strchrnul(s, '\'') - s; | ||
| 902 | |||
| 903 | q = p = makestrspace(len + 3, p); | ||
| 904 | |||
| 905 | *q++ = '\''; | ||
| 906 | q = memcpy(q, s, len) + len; | ||
| 907 | *q++ = '\''; | ||
| 908 | s += len; | ||
| 909 | |||
| 910 | STADJUST(q - p, p); | ||
| 911 | |||
| 912 | len = strspn(s, "'"); | ||
| 913 | if (!len) | ||
| 914 | break; | ||
| 915 | |||
| 916 | q = p = makestrspace(len + 3, p); | ||
| 917 | |||
| 918 | *q++ = '"'; | ||
| 919 | q = memcpy(q, s, len) + len; | ||
| 920 | *q++ = '"'; | ||
| 921 | s += len; | ||
| 922 | |||
| 923 | STADJUST(q - p, p); | ||
| 924 | } while (*s); | ||
| 925 | |||
| 926 | USTPUTC(0, p); | ||
| 927 | |||
| 928 | return stackblock(); | ||
| 929 | } | ||
| 930 | |||
| 931 | |||
| 932 | /* ============ ... */ | ||
| 933 | |||
| 934 | static char **argptr; /* argument list for builtin commands */ | ||
| 935 | static char *optionarg; /* set by nextopt (like getopt) */ | ||
| 936 | static char *optptr; /* used by nextopt */ | ||
| 937 | |||
| 938 | /* | ||
| 939 | * XXX - should get rid of. have all builtins use getopt(3). the | ||
| 940 | * library getopt must have the BSD extension static variable "optreset" | ||
| 941 | * otherwise it can't be used within the shell safely. | ||
| 942 | * | ||
| 943 | * Standard option processing (a la getopt) for builtin routines. The | ||
| 944 | * only argument that is passed to nextopt is the option string; the | ||
| 945 | * other arguments are unnecessary. It return the character, or '\0' on | ||
| 946 | * end of input. | ||
| 947 | */ | ||
| 948 | static int | ||
| 949 | nextopt(const char *optstring) | ||
| 950 | { | ||
| 951 | char *p; | ||
| 952 | const char *q; | ||
| 953 | char c; | ||
| 954 | |||
| 955 | p = optptr; | ||
| 956 | if (p == NULL || *p == '\0') { | ||
| 957 | p = *argptr; | ||
| 958 | if (p == NULL || *p != '-' || *++p == '\0') | ||
| 959 | return '\0'; | ||
| 960 | argptr++; | ||
| 961 | if (LONE_DASH(p)) /* check for "--" */ | ||
| 962 | return '\0'; | ||
| 963 | } | ||
| 964 | c = *p++; | ||
| 965 | for (q = optstring; *q != c; ) { | ||
| 966 | if (*q == '\0') | ||
| 967 | ash_msg_and_raise_error("Illegal option -%c", c); | ||
| 968 | if (*++q == ':') | ||
| 969 | q++; | ||
| 970 | } | ||
| 971 | if (*++q == ':') { | ||
| 972 | if (*p == '\0' && (p = *argptr++) == NULL) | ||
| 973 | ash_msg_and_raise_error("No arg for -%c option", c); | ||
| 974 | optionarg = p; | ||
| 975 | p = NULL; | ||
| 976 | } | ||
| 977 | optptr = p; | ||
| 978 | return c; | ||
| 979 | } | ||
| 980 | |||
| 981 | |||
| 982 | /* ============ Variables */ | ||
| 983 | |||
| 984 | /* flags */ | ||
| 985 | #define VEXPORT 0x01 /* variable is exported */ | ||
| 986 | #define VREADONLY 0x02 /* variable cannot be modified */ | ||
| 987 | #define VSTRFIXED 0x04 /* variable struct is statically allocated */ | ||
| 988 | #define VTEXTFIXED 0x08 /* text is statically allocated */ | ||
| 989 | #define VSTACK 0x10 /* text is allocated on the stack */ | ||
| 990 | #define VUNSET 0x20 /* the variable is not set */ | ||
| 991 | #define VNOFUNC 0x40 /* don't call the callback function */ | ||
| 992 | #define VNOSET 0x80 /* do not set variable - just readonly test */ | ||
| 993 | #define VNOSAVE 0x100 /* when text is on the heap before setvareq */ | ||
| 994 | #ifdef DYNAMIC_VAR | ||
| 995 | # define VDYNAMIC 0x200 /* dynamic variable */ | ||
| 996 | # else | ||
| 997 | # define VDYNAMIC 0 | ||
| 998 | #endif | ||
| 999 | |||
| 1000 | #if ENABLE_LOCALE_SUPPORT | ||
| 1001 | static void change_lc_all(const char *value); | ||
| 1002 | static void change_lc_ctype(const char *value); | ||
| 1003 | #endif | ||
| 1004 | |||
| 1005 | static const char defpathvar[] = "PATH=/usr/local/bin:/usr/bin:/sbin:/bin"; | ||
| 1006 | #ifdef IFS_BROKEN | ||
| 1007 | static const char defifsvar[] = "IFS= \t\n"; | ||
| 1008 | #define defifs (defifsvar + 4) | ||
| 1009 | #else | ||
| 1010 | static const char defifs[] = " \t\n"; | ||
| 1011 | #endif | ||
| 1012 | |||
| 1013 | struct var { | ||
| 1014 | struct var *next; /* next entry in hash list */ | ||
| 1015 | int flags; /* flags are defined above */ | ||
| 1016 | const char *text; /* name=value */ | ||
| 1017 | void (*func)(const char *); /* function to be called when */ | ||
| 1018 | /* the variable gets set/unset */ | ||
| 799 | }; | 1019 | }; |
| 800 | 1020 | ||
| 1021 | struct localvar { | ||
| 1022 | struct localvar *next; /* next local variable in list */ | ||
| 1023 | struct var *vp; /* the variable that was made local */ | ||
| 1024 | int flags; /* saved flags */ | ||
| 1025 | const char *text; /* saved text */ | ||
| 1026 | }; | ||
| 1027 | |||
| 1028 | /* Forward decls for varinit[] */ | ||
| 1029 | #if ENABLE_ASH_MAIL | ||
| 1030 | static void chkmail(void); | ||
| 1031 | static void changemail(const char *); | ||
| 1032 | #endif | ||
| 1033 | static void changepath(const char *); | ||
| 1034 | #if ENABLE_ASH_GETOPTS | ||
| 1035 | static void getoptsreset(const char *); | ||
| 1036 | #endif | ||
| 1037 | #if ENABLE_ASH_RANDOM_SUPPORT | ||
| 1038 | static void change_random(const char *); | ||
| 1039 | #endif | ||
| 1040 | |||
| 1041 | static struct var varinit[] = { | ||
| 1042 | #ifdef IFS_BROKEN | ||
| 1043 | { 0, VSTRFIXED|VTEXTFIXED, defifsvar, 0 }, | ||
| 1044 | #else | ||
| 1045 | { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0", 0 }, | ||
| 1046 | #endif | ||
| 1047 | |||
| 1048 | #if ENABLE_ASH_MAIL | ||
| 1049 | { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0", changemail }, | ||
| 1050 | { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail }, | ||
| 1051 | #endif | ||
| 1052 | |||
| 1053 | { 0, VSTRFIXED|VTEXTFIXED, defpathvar, changepath }, | ||
| 1054 | { 0, VSTRFIXED|VTEXTFIXED, "PS1=$ ", 0 }, | ||
| 1055 | { 0, VSTRFIXED|VTEXTFIXED, "PS2=> ", 0 }, | ||
| 1056 | { 0, VSTRFIXED|VTEXTFIXED, "PS4=+ ", 0 }, | ||
| 1057 | #if ENABLE_ASH_GETOPTS | ||
| 1058 | { 0, VSTRFIXED|VTEXTFIXED, "OPTIND=1", getoptsreset }, | ||
| 1059 | #endif | ||
| 1060 | #if ENABLE_ASH_RANDOM_SUPPORT | ||
| 1061 | {0, VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random }, | ||
| 1062 | #endif | ||
| 1063 | #if ENABLE_LOCALE_SUPPORT | ||
| 1064 | {0, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_ALL\0", change_lc_all }, | ||
| 1065 | {0, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_CTYPE\0", change_lc_ctype }, | ||
| 1066 | #endif | ||
| 1067 | #if ENABLE_FEATURE_EDITING_SAVEHISTORY | ||
| 1068 | {0, VSTRFIXED | VTEXTFIXED | VUNSET, "HISTFILE\0", NULL }, | ||
| 1069 | #endif | ||
| 1070 | }; | ||
| 1071 | |||
| 1072 | #define vifs varinit[0] | ||
| 1073 | #if ENABLE_ASH_MAIL | ||
| 1074 | #define vmail (&vifs)[1] | ||
| 1075 | #define vmpath (&vmail)[1] | ||
| 1076 | #else | ||
| 1077 | #define vmpath vifs | ||
| 1078 | #endif | ||
| 1079 | #define vpath (&vmpath)[1] | ||
| 1080 | #define vps1 (&vpath)[1] | ||
| 1081 | #define vps2 (&vps1)[1] | ||
| 1082 | #define vps4 (&vps2)[1] | ||
| 1083 | #define voptind (&vps4)[1] | ||
| 1084 | #if ENABLE_ASH_GETOPTS | ||
| 1085 | #define vrandom (&voptind)[1] | ||
| 1086 | #else | ||
| 1087 | #define vrandom (&vps4)[1] | ||
| 1088 | #endif | ||
| 1089 | #define defpath (defpathvar + 5) | ||
| 1090 | |||
| 1091 | /* | ||
| 1092 | * The following macros access the values of the above variables. | ||
| 1093 | * They have to skip over the name. They return the null string | ||
| 1094 | * for unset variables. | ||
| 1095 | */ | ||
| 1096 | #define ifsval() (vifs.text + 4) | ||
| 1097 | #define ifsset() ((vifs.flags & VUNSET) == 0) | ||
| 1098 | #define mailval() (vmail.text + 5) | ||
| 1099 | #define mpathval() (vmpath.text + 9) | ||
| 1100 | #define pathval() (vpath.text + 5) | ||
| 1101 | #define ps1val() (vps1.text + 4) | ||
| 1102 | #define ps2val() (vps2.text + 4) | ||
| 1103 | #define ps4val() (vps4.text + 4) | ||
| 1104 | #define optindval() (voptind.text + 7) | ||
| 1105 | |||
| 1106 | #define mpathset() ((vmpath.flags & VUNSET) == 0) | ||
| 1107 | |||
| 1108 | static struct var **hashvar(const char *); | ||
| 1109 | |||
| 1110 | static int loopnest; /* current loop nesting level */ | ||
| 1111 | |||
| 1112 | /* | ||
| 1113 | * The parsefile structure pointed to by the global variable parsefile | ||
| 1114 | * contains information about the current file being read. | ||
| 1115 | */ | ||
| 1116 | struct redirtab { | ||
| 1117 | struct redirtab *next; | ||
| 1118 | int renamed[10]; | ||
| 1119 | int nullredirs; | ||
| 1120 | }; | ||
| 1121 | |||
| 1122 | static struct redirtab *redirlist; | ||
| 1123 | static int nullredirs; | ||
| 1124 | |||
| 1125 | extern char **environ; | ||
| 1126 | |||
| 1127 | static int preverrout_fd; /* save fd2 before print debug if xflag is set. */ | ||
| 1128 | |||
| 1129 | struct shparam { | ||
| 1130 | int nparam; /* # of positional parameters (without $0) */ | ||
| 1131 | unsigned char malloc; /* if parameter list dynamically allocated */ | ||
| 1132 | char **p; /* parameter list */ | ||
| 1133 | #if ENABLE_ASH_GETOPTS | ||
| 1134 | int optind; /* next parameter to be processed by getopts */ | ||
| 1135 | int optoff; /* used by getopts */ | ||
| 1136 | #endif | ||
| 1137 | }; | ||
| 1138 | |||
| 1139 | static struct shparam shellparam; /* $@ current positional parameters */ | ||
| 1140 | |||
| 1141 | #define VTABSIZE 39 | ||
| 1142 | |||
| 1143 | static struct var *vartab[VTABSIZE]; | ||
| 1144 | |||
| 1145 | #if ENABLE_ASH_GETOPTS | ||
| 1146 | static void | ||
| 1147 | getoptsreset(const char *value) | ||
| 1148 | { | ||
| 1149 | shellparam.optind = number(value); | ||
| 1150 | shellparam.optoff = -1; | ||
| 1151 | } | ||
| 1152 | #endif | ||
| 1153 | |||
| 1154 | #define is_name(c) ((c) == '_' || isalpha((unsigned char)(c))) | ||
| 1155 | #define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c))) | ||
| 1156 | |||
| 1157 | /* | ||
| 1158 | * Return of a legal variable name (a letter or underscore followed by zero or | ||
| 1159 | * more letters, underscores, and digits). | ||
| 1160 | */ | ||
| 1161 | static char * | ||
| 1162 | endofname(const char *name) | ||
| 1163 | { | ||
| 1164 | char *p; | ||
| 1165 | |||
| 1166 | p = (char *) name; | ||
| 1167 | if (!is_name(*p)) | ||
| 1168 | return p; | ||
| 1169 | while (*++p) { | ||
| 1170 | if (!is_in_name(*p)) | ||
| 1171 | break; | ||
| 1172 | } | ||
| 1173 | return p; | ||
| 1174 | } | ||
| 1175 | |||
| 1176 | /* | ||
| 1177 | * Compares two strings up to the first = or '\0'. The first | ||
| 1178 | * string must be terminated by '='; the second may be terminated by | ||
| 1179 | * either '=' or '\0'. | ||
| 1180 | */ | ||
| 1181 | static int | ||
| 1182 | varcmp(const char *p, const char *q) | ||
| 1183 | { | ||
| 1184 | int c, d; | ||
| 1185 | |||
| 1186 | while ((c = *p) == (d = *q)) { | ||
| 1187 | if (!c || c == '=') | ||
| 1188 | goto out; | ||
| 1189 | p++; | ||
| 1190 | q++; | ||
| 1191 | } | ||
| 1192 | if (c == '=') | ||
| 1193 | c = 0; | ||
| 1194 | if (d == '=') | ||
| 1195 | d = 0; | ||
| 1196 | out: | ||
| 1197 | return c - d; | ||
| 1198 | } | ||
| 1199 | |||
| 1200 | static int | ||
| 1201 | varequal(const char *a, const char *b) | ||
| 1202 | { | ||
| 1203 | return !varcmp(a, b); | ||
| 1204 | } | ||
| 1205 | |||
| 1206 | /* | ||
| 1207 | * Find the appropriate entry in the hash table from the name. | ||
| 1208 | */ | ||
| 1209 | static struct var ** | ||
| 1210 | hashvar(const char *p) | ||
| 1211 | { | ||
| 1212 | unsigned hashval; | ||
| 1213 | |||
| 1214 | hashval = ((unsigned char) *p) << 4; | ||
| 1215 | while (*p && *p != '=') | ||
| 1216 | hashval += (unsigned char) *p++; | ||
| 1217 | return &vartab[hashval % VTABSIZE]; | ||
| 1218 | } | ||
| 1219 | |||
| 1220 | static int | ||
| 1221 | vpcmp(const void *a, const void *b) | ||
| 1222 | { | ||
| 1223 | return varcmp(*(const char **)a, *(const char **)b); | ||
| 1224 | } | ||
| 1225 | |||
| 1226 | /* | ||
| 1227 | * This routine initializes the builtin variables. | ||
| 1228 | */ | ||
| 1229 | static void | ||
| 1230 | initvar(void) | ||
| 1231 | { | ||
| 1232 | struct var *vp; | ||
| 1233 | struct var *end; | ||
| 1234 | struct var **vpp; | ||
| 1235 | |||
| 1236 | /* | ||
| 1237 | * PS1 depends on uid | ||
| 1238 | */ | ||
| 1239 | #if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT | ||
| 1240 | vps1.text = "PS1=\\w \\$ "; | ||
| 1241 | #else | ||
| 1242 | if (!geteuid()) | ||
| 1243 | vps1.text = "PS1=# "; | ||
| 1244 | #endif | ||
| 1245 | vp = varinit; | ||
| 1246 | end = vp + sizeof(varinit) / sizeof(varinit[0]); | ||
| 1247 | do { | ||
| 1248 | vpp = hashvar(vp->text); | ||
| 1249 | vp->next = *vpp; | ||
| 1250 | *vpp = vp; | ||
| 1251 | } while (++vp < end); | ||
| 1252 | } | ||
| 1253 | |||
| 1254 | static struct var ** | ||
| 1255 | findvar(struct var **vpp, const char *name) | ||
| 1256 | { | ||
| 1257 | for (; *vpp; vpp = &(*vpp)->next) { | ||
| 1258 | if (varequal((*vpp)->text, name)) { | ||
| 1259 | break; | ||
| 1260 | } | ||
| 1261 | } | ||
| 1262 | return vpp; | ||
| 1263 | } | ||
| 1264 | |||
| 1265 | /* | ||
| 1266 | * Find the value of a variable. Returns NULL if not set. | ||
| 1267 | */ | ||
| 1268 | static char * | ||
| 1269 | lookupvar(const char *name) | ||
| 1270 | { | ||
| 1271 | struct var *v; | ||
| 1272 | |||
| 1273 | v = *findvar(hashvar(name), name); | ||
| 1274 | if (v) { | ||
| 1275 | #ifdef DYNAMIC_VAR | ||
| 1276 | /* | ||
| 1277 | * Dynamic variables are implemented roughly the same way they are | ||
| 1278 | * in bash. Namely, they're "special" so long as they aren't unset. | ||
| 1279 | * As soon as they're unset, they're no longer dynamic, and dynamic | ||
| 1280 | * lookup will no longer happen at that point. -- PFM. | ||
| 1281 | */ | ||
| 1282 | if ((v->flags & VDYNAMIC)) | ||
| 1283 | (*v->func)(NULL); | ||
| 1284 | #endif | ||
| 1285 | if (!(v->flags & VUNSET)) | ||
| 1286 | return strchrnul(v->text, '=') + 1; | ||
| 1287 | } | ||
| 1288 | return NULL; | ||
| 1289 | } | ||
| 1290 | |||
| 1291 | /* | ||
| 1292 | * Search the environment of a builtin command. | ||
| 1293 | */ | ||
| 1294 | static char * | ||
| 1295 | bltinlookup(const char *name) | ||
| 1296 | { | ||
| 1297 | struct strlist *sp; | ||
| 1298 | |||
| 1299 | for (sp = cmdenviron; sp; sp = sp->next) { | ||
| 1300 | if (varequal(sp->text, name)) | ||
| 1301 | return strchrnul(sp->text, '=') + 1; | ||
| 1302 | } | ||
| 1303 | return lookupvar(name); | ||
| 1304 | } | ||
| 1305 | |||
| 1306 | /* | ||
| 1307 | * Same as setvar except that the variable and value are passed in | ||
| 1308 | * the first argument as name=value. Since the first argument will | ||
| 1309 | * be actually stored in the table, it should not be a string that | ||
| 1310 | * will go away. | ||
| 1311 | * Called with interrupts off. | ||
| 1312 | */ | ||
| 1313 | static void | ||
| 1314 | setvareq(char *s, int flags) | ||
| 1315 | { | ||
| 1316 | struct var *vp, **vpp; | ||
| 1317 | |||
| 1318 | vpp = hashvar(s); | ||
| 1319 | flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1)); | ||
| 1320 | vp = *findvar(vpp, s); | ||
| 1321 | if (vp) { | ||
| 1322 | if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) { | ||
| 1323 | const char *n; | ||
| 1324 | |||
| 1325 | if (flags & VNOSAVE) | ||
| 1326 | free(s); | ||
| 1327 | n = vp->text; | ||
| 1328 | ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n); | ||
| 1329 | } | ||
| 1330 | |||
| 1331 | if (flags & VNOSET) | ||
| 1332 | return; | ||
| 1333 | |||
| 1334 | if (vp->func && (flags & VNOFUNC) == 0) | ||
| 1335 | (*vp->func)(strchrnul(s, '=') + 1); | ||
| 1336 | |||
| 1337 | if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0) | ||
| 1338 | free((char*)vp->text); | ||
| 1339 | |||
| 1340 | flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET); | ||
| 1341 | } else { | ||
| 1342 | if (flags & VNOSET) | ||
| 1343 | return; | ||
| 1344 | /* not found */ | ||
| 1345 | vp = ckmalloc(sizeof(*vp)); | ||
| 1346 | vp->next = *vpp; | ||
| 1347 | vp->func = NULL; | ||
| 1348 | *vpp = vp; | ||
| 1349 | } | ||
| 1350 | if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE))) | ||
| 1351 | s = ckstrdup(s); | ||
| 1352 | vp->text = s; | ||
| 1353 | vp->flags = flags; | ||
| 1354 | } | ||
| 1355 | |||
| 1356 | /* | ||
| 1357 | * Set the value of a variable. The flags argument is ored with the | ||
| 1358 | * flags of the variable. If val is NULL, the variable is unset. | ||
| 1359 | */ | ||
| 1360 | static void | ||
| 1361 | setvar(const char *name, const char *val, int flags) | ||
| 1362 | { | ||
| 1363 | char *p, *q; | ||
| 1364 | size_t namelen; | ||
| 1365 | char *nameeq; | ||
| 1366 | size_t vallen; | ||
| 1367 | |||
| 1368 | q = endofname(name); | ||
| 1369 | p = strchrnul(q, '='); | ||
| 1370 | namelen = p - name; | ||
| 1371 | if (!namelen || p != q) | ||
| 1372 | ash_msg_and_raise_error("%.*s: bad variable name", namelen, name); | ||
| 1373 | vallen = 0; | ||
| 1374 | if (val == NULL) { | ||
| 1375 | flags |= VUNSET; | ||
| 1376 | } else { | ||
| 1377 | vallen = strlen(val); | ||
| 1378 | } | ||
| 1379 | INT_OFF; | ||
| 1380 | nameeq = ckmalloc(namelen + vallen + 2); | ||
| 1381 | p = memcpy(nameeq, name, namelen) + namelen; | ||
| 1382 | if (val) { | ||
| 1383 | *p++ = '='; | ||
| 1384 | p = memcpy(p, val, vallen) + vallen; | ||
| 1385 | } | ||
| 1386 | *p = '\0'; | ||
| 1387 | setvareq(nameeq, flags | VNOSAVE); | ||
| 1388 | INT_ON; | ||
| 1389 | } | ||
| 1390 | |||
| 1391 | #if ENABLE_ASH_GETOPTS | ||
| 1392 | /* | ||
| 1393 | * Safe version of setvar, returns 1 on success 0 on failure. | ||
| 1394 | */ | ||
| 1395 | static int | ||
| 1396 | setvarsafe(const char *name, const char *val, int flags) | ||
| 1397 | { | ||
| 1398 | int err; | ||
| 1399 | volatile int saveint; | ||
| 1400 | struct jmploc *volatile savehandler = exception_handler; | ||
| 1401 | struct jmploc jmploc; | ||
| 1402 | |||
| 1403 | SAVE_INT(saveint); | ||
| 1404 | if (setjmp(jmploc.loc)) | ||
| 1405 | err = 1; | ||
| 1406 | else { | ||
| 1407 | exception_handler = &jmploc; | ||
| 1408 | setvar(name, val, flags); | ||
| 1409 | err = 0; | ||
| 1410 | } | ||
| 1411 | exception_handler = savehandler; | ||
| 1412 | RESTORE_INT(saveint); | ||
| 1413 | return err; | ||
| 1414 | } | ||
| 1415 | #endif | ||
| 1416 | |||
| 1417 | /* | ||
| 1418 | * Unset the specified variable. | ||
| 1419 | */ | ||
| 1420 | static int | ||
| 1421 | unsetvar(const char *s) | ||
| 1422 | { | ||
| 1423 | struct var **vpp; | ||
| 1424 | struct var *vp; | ||
| 1425 | int retval; | ||
| 1426 | |||
| 1427 | vpp = findvar(hashvar(s), s); | ||
| 1428 | vp = *vpp; | ||
| 1429 | retval = 2; | ||
| 1430 | if (vp) { | ||
| 1431 | int flags = vp->flags; | ||
| 1432 | |||
| 1433 | retval = 1; | ||
| 1434 | if (flags & VREADONLY) | ||
| 1435 | goto out; | ||
| 1436 | #ifdef DYNAMIC_VAR | ||
| 1437 | vp->flags &= ~VDYNAMIC; | ||
| 1438 | #endif | ||
| 1439 | if (flags & VUNSET) | ||
| 1440 | goto ok; | ||
| 1441 | if ((flags & VSTRFIXED) == 0) { | ||
| 1442 | INT_OFF; | ||
| 1443 | if ((flags & (VTEXTFIXED|VSTACK)) == 0) | ||
| 1444 | free((char*)vp->text); | ||
| 1445 | *vpp = vp->next; | ||
| 1446 | free(vp); | ||
| 1447 | INT_ON; | ||
| 1448 | } else { | ||
| 1449 | setvar(s, 0, 0); | ||
| 1450 | vp->flags &= ~VEXPORT; | ||
| 1451 | } | ||
| 1452 | ok: | ||
| 1453 | retval = 0; | ||
| 1454 | } | ||
| 1455 | out: | ||
| 1456 | return retval; | ||
| 1457 | } | ||
| 1458 | |||
| 1459 | /* | ||
| 1460 | * Process a linked list of variable assignments. | ||
| 1461 | */ | ||
| 1462 | static void | ||
| 1463 | listsetvar(struct strlist *list_set_var, int flags) | ||
| 1464 | { | ||
| 1465 | struct strlist *lp = list_set_var; | ||
| 1466 | |||
| 1467 | if (!lp) | ||
| 1468 | return; | ||
| 1469 | INT_OFF; | ||
| 1470 | do { | ||
| 1471 | setvareq(lp->text, flags); | ||
| 1472 | } while ((lp = lp->next)); | ||
| 1473 | INT_ON; | ||
| 1474 | } | ||
| 1475 | |||
| 1476 | /* | ||
| 1477 | * Generate a list of variables satisfying the given conditions. | ||
| 1478 | */ | ||
| 1479 | static char ** | ||
| 1480 | listvars(int on, int off, char ***end) | ||
| 1481 | { | ||
| 1482 | struct var **vpp; | ||
| 1483 | struct var *vp; | ||
| 1484 | char **ep; | ||
| 1485 | int mask; | ||
| 1486 | |||
| 1487 | STARTSTACKSTR(ep); | ||
| 1488 | vpp = vartab; | ||
| 1489 | mask = on | off; | ||
| 1490 | do { | ||
| 1491 | for (vp = *vpp; vp; vp = vp->next) { | ||
| 1492 | if ((vp->flags & mask) == on) { | ||
| 1493 | if (ep == stackstrend()) | ||
| 1494 | ep = growstackstr(); | ||
| 1495 | *ep++ = (char *) vp->text; | ||
| 1496 | } | ||
| 1497 | } | ||
| 1498 | } while (++vpp < vartab + VTABSIZE); | ||
| 1499 | if (ep == stackstrend()) | ||
| 1500 | ep = growstackstr(); | ||
| 1501 | if (end) | ||
| 1502 | *end = ep; | ||
| 1503 | *ep++ = NULL; | ||
| 1504 | return grabstackstr(ep); | ||
| 1505 | } | ||
| 1506 | |||
| 1507 | |||
| 1508 | /* ============ Path search helper | ||
| 1509 | * | ||
| 1510 | * The variable path (passed by reference) should be set to the start | ||
| 1511 | * of the path before the first call; padvance will update | ||
| 1512 | * this value as it proceeds. Successive calls to padvance will return | ||
| 1513 | * the possible path expansions in sequence. If an option (indicated by | ||
| 1514 | * a percent sign) appears in the path entry then the global variable | ||
| 1515 | * pathopt will be set to point to it; otherwise pathopt will be set to | ||
| 1516 | * NULL. | ||
| 1517 | */ | ||
| 1518 | static const char *pathopt; /* set by padvance */ | ||
| 1519 | |||
| 1520 | static char * | ||
| 1521 | padvance(const char **path, const char *name) | ||
| 1522 | { | ||
| 1523 | const char *p; | ||
| 1524 | char *q; | ||
| 1525 | const char *start; | ||
| 1526 | size_t len; | ||
| 1527 | |||
| 1528 | if (*path == NULL) | ||
| 1529 | return NULL; | ||
| 1530 | start = *path; | ||
| 1531 | for (p = start; *p && *p != ':' && *p != '%'; p++); | ||
| 1532 | len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */ | ||
| 1533 | while (stackblocksize() < len) | ||
| 1534 | growstackblock(); | ||
| 1535 | q = stackblock(); | ||
| 1536 | if (p != start) { | ||
| 1537 | memcpy(q, start, p - start); | ||
| 1538 | q += p - start; | ||
| 1539 | *q++ = '/'; | ||
| 1540 | } | ||
| 1541 | strcpy(q, name); | ||
| 1542 | pathopt = NULL; | ||
| 1543 | if (*p == '%') { | ||
| 1544 | pathopt = ++p; | ||
| 1545 | while (*p && *p != ':') p++; | ||
| 1546 | } | ||
| 1547 | if (*p == ':') | ||
| 1548 | *path = p + 1; | ||
| 1549 | else | ||
| 1550 | *path = NULL; | ||
| 1551 | return stalloc(len); | ||
| 1552 | } | ||
| 1553 | |||
| 1554 | |||
| 1555 | /* ============ Prompt */ | ||
| 1556 | |||
| 1557 | static int doprompt; /* if set, prompt the user */ | ||
| 1558 | static int needprompt; /* true if interactive and at start of line */ | ||
| 1559 | |||
| 1560 | #if ENABLE_FEATURE_EDITING | ||
| 1561 | static line_input_t *line_input_state; | ||
| 1562 | static const char *cmdedit_prompt; | ||
| 1563 | static void | ||
| 1564 | putprompt(const char *s) | ||
| 1565 | { | ||
| 1566 | if (ENABLE_ASH_EXPAND_PRMT) { | ||
| 1567 | free((char*)cmdedit_prompt); | ||
| 1568 | cmdedit_prompt = xstrdup(s); | ||
| 1569 | return; | ||
| 1570 | } | ||
| 1571 | cmdedit_prompt = s; | ||
| 1572 | } | ||
| 1573 | #else | ||
| 1574 | static void | ||
| 1575 | putprompt(const char *s) | ||
| 1576 | { | ||
| 1577 | out2str(s); | ||
| 1578 | } | ||
| 1579 | #endif | ||
| 1580 | |||
| 1581 | #if ENABLE_ASH_EXPAND_PRMT | ||
| 1582 | /* expandstr() needs parsing machinery, so it is far away ahead... */ | ||
| 1583 | static const char *expandstr(const char *ps); | ||
| 1584 | #else | ||
| 1585 | #define expandstr(s) s | ||
| 1586 | #endif | ||
| 1587 | |||
| 1588 | static void | ||
| 1589 | setprompt(int whichprompt) | ||
| 1590 | { | ||
| 1591 | const char *prompt; | ||
| 1592 | #if ENABLE_ASH_EXPAND_PRMT | ||
| 1593 | struct stackmark smark; | ||
| 1594 | #endif | ||
| 1595 | |||
| 1596 | needprompt = 0; | ||
| 1597 | |||
| 1598 | switch (whichprompt) { | ||
| 1599 | case 1: | ||
| 1600 | prompt = ps1val(); | ||
| 1601 | break; | ||
| 1602 | case 2: | ||
| 1603 | prompt = ps2val(); | ||
| 1604 | break; | ||
| 1605 | default: /* 0 */ | ||
| 1606 | prompt = nullstr; | ||
| 1607 | } | ||
| 1608 | #if ENABLE_ASH_EXPAND_PRMT | ||
| 1609 | setstackmark(&smark); | ||
| 1610 | stalloc(stackblocksize()); | ||
| 1611 | #endif | ||
| 1612 | putprompt(expandstr(prompt)); | ||
| 1613 | #if ENABLE_ASH_EXPAND_PRMT | ||
| 1614 | popstackmark(&smark); | ||
| 1615 | #endif | ||
| 1616 | } | ||
| 1617 | |||
| 1618 | |||
| 1619 | /* ============ The cd and pwd commands */ | ||
| 1620 | |||
| 1621 | #define CD_PHYSICAL 1 | ||
| 1622 | #define CD_PRINT 2 | ||
| 1623 | |||
| 1624 | static int docd(const char *, int); | ||
| 1625 | |||
| 1626 | static char *curdir = nullstr; /* current working directory */ | ||
| 1627 | static char *physdir = nullstr; /* physical working directory */ | ||
| 1628 | |||
| 1629 | static int | ||
| 1630 | cdopt(void) | ||
| 1631 | { | ||
| 1632 | int flags = 0; | ||
| 1633 | int i, j; | ||
| 1634 | |||
| 1635 | j = 'L'; | ||
| 1636 | while ((i = nextopt("LP"))) { | ||
| 1637 | if (i != j) { | ||
| 1638 | flags ^= CD_PHYSICAL; | ||
| 1639 | j = i; | ||
| 1640 | } | ||
| 1641 | } | ||
| 1642 | |||
| 1643 | return flags; | ||
| 1644 | } | ||
| 1645 | |||
| 1646 | /* | ||
| 1647 | * Update curdir (the name of the current directory) in response to a | ||
| 1648 | * cd command. | ||
| 1649 | */ | ||
| 1650 | static const char * | ||
| 1651 | updatepwd(const char *dir) | ||
| 1652 | { | ||
| 1653 | char *new; | ||
| 1654 | char *p; | ||
| 1655 | char *cdcomppath; | ||
| 1656 | const char *lim; | ||
| 1657 | |||
| 1658 | cdcomppath = ststrdup(dir); | ||
| 1659 | STARTSTACKSTR(new); | ||
| 1660 | if (*dir != '/') { | ||
| 1661 | if (curdir == nullstr) | ||
| 1662 | return 0; | ||
| 1663 | new = stack_putstr(curdir, new); | ||
| 1664 | } | ||
| 1665 | new = makestrspace(strlen(dir) + 2, new); | ||
| 1666 | lim = stackblock() + 1; | ||
| 1667 | if (*dir != '/') { | ||
| 1668 | if (new[-1] != '/') | ||
| 1669 | USTPUTC('/', new); | ||
| 1670 | if (new > lim && *lim == '/') | ||
| 1671 | lim++; | ||
| 1672 | } else { | ||
| 1673 | USTPUTC('/', new); | ||
| 1674 | cdcomppath++; | ||
| 1675 | if (dir[1] == '/' && dir[2] != '/') { | ||
| 1676 | USTPUTC('/', new); | ||
| 1677 | cdcomppath++; | ||
| 1678 | lim++; | ||
| 1679 | } | ||
| 1680 | } | ||
| 1681 | p = strtok(cdcomppath, "/"); | ||
| 1682 | while (p) { | ||
| 1683 | switch (*p) { | ||
| 1684 | case '.': | ||
| 1685 | if (p[1] == '.' && p[2] == '\0') { | ||
| 1686 | while (new > lim) { | ||
| 1687 | STUNPUTC(new); | ||
| 1688 | if (new[-1] == '/') | ||
| 1689 | break; | ||
| 1690 | } | ||
| 1691 | break; | ||
| 1692 | } else if (p[1] == '\0') | ||
| 1693 | break; | ||
| 1694 | /* fall through */ | ||
| 1695 | default: | ||
| 1696 | new = stack_putstr(p, new); | ||
| 1697 | USTPUTC('/', new); | ||
| 1698 | } | ||
| 1699 | p = strtok(0, "/"); | ||
| 1700 | } | ||
| 1701 | if (new > lim) | ||
| 1702 | STUNPUTC(new); | ||
| 1703 | *new = 0; | ||
| 1704 | return stackblock(); | ||
| 1705 | } | ||
| 1706 | |||
| 1707 | /* | ||
| 1708 | * Find out what the current directory is. If we already know the current | ||
| 1709 | * directory, this routine returns immediately. | ||
| 1710 | */ | ||
| 1711 | static char * | ||
| 1712 | getpwd(void) | ||
| 1713 | { | ||
| 1714 | char *dir = getcwd(0, 0); | ||
| 1715 | return dir ? dir : nullstr; | ||
| 1716 | } | ||
| 1717 | |||
| 1718 | static void | ||
| 1719 | setpwd(const char *val, int setold) | ||
| 1720 | { | ||
| 1721 | char *oldcur, *dir; | ||
| 1722 | |||
| 1723 | oldcur = dir = curdir; | ||
| 1724 | |||
| 1725 | if (setold) { | ||
| 1726 | setvar("OLDPWD", oldcur, VEXPORT); | ||
| 1727 | } | ||
| 1728 | INT_OFF; | ||
| 1729 | if (physdir != nullstr) { | ||
| 1730 | if (physdir != oldcur) | ||
| 1731 | free(physdir); | ||
| 1732 | physdir = nullstr; | ||
| 1733 | } | ||
| 1734 | if (oldcur == val || !val) { | ||
| 1735 | char *s = getpwd(); | ||
| 1736 | physdir = s; | ||
| 1737 | if (!val) | ||
| 1738 | dir = s; | ||
| 1739 | } else | ||
| 1740 | dir = ckstrdup(val); | ||
| 1741 | if (oldcur != dir && oldcur != nullstr) { | ||
| 1742 | free(oldcur); | ||
| 1743 | } | ||
| 1744 | curdir = dir; | ||
| 1745 | INT_ON; | ||
| 1746 | setvar("PWD", dir, VEXPORT); | ||
| 1747 | } | ||
| 1748 | |||
| 1749 | static void hashcd(void); | ||
| 1750 | |||
| 1751 | /* | ||
| 1752 | * Actually do the chdir. We also call hashcd to let the routines in exec.c | ||
| 1753 | * know that the current directory has changed. | ||
| 1754 | */ | ||
| 1755 | static int | ||
| 1756 | docd(const char *dest, int flags) | ||
| 1757 | { | ||
| 1758 | const char *dir = 0; | ||
| 1759 | int err; | ||
| 1760 | |||
| 1761 | TRACE(("docd(\"%s\", %d) called\n", dest, flags)); | ||
| 1762 | |||
| 1763 | INT_OFF; | ||
| 1764 | if (!(flags & CD_PHYSICAL)) { | ||
| 1765 | dir = updatepwd(dest); | ||
| 1766 | if (dir) | ||
| 1767 | dest = dir; | ||
| 1768 | } | ||
| 1769 | err = chdir(dest); | ||
| 1770 | if (err) | ||
| 1771 | goto out; | ||
| 1772 | setpwd(dir, 1); | ||
| 1773 | hashcd(); | ||
| 1774 | out: | ||
| 1775 | INT_ON; | ||
| 1776 | return err; | ||
| 1777 | } | ||
| 1778 | |||
| 1779 | static int | ||
| 1780 | cdcmd(int argc, char **argv) | ||
| 1781 | { | ||
| 1782 | const char *dest; | ||
| 1783 | const char *path; | ||
| 1784 | const char *p; | ||
| 1785 | char c; | ||
| 1786 | struct stat statb; | ||
| 1787 | int flags; | ||
| 1788 | |||
| 1789 | flags = cdopt(); | ||
| 1790 | dest = *argptr; | ||
| 1791 | if (!dest) | ||
| 1792 | dest = bltinlookup(homestr); | ||
| 1793 | else if (LONE_DASH(dest)) { | ||
| 1794 | dest = bltinlookup("OLDPWD"); | ||
| 1795 | flags |= CD_PRINT; | ||
| 1796 | } | ||
| 1797 | if (!dest) | ||
| 1798 | dest = nullstr; | ||
| 1799 | if (*dest == '/') | ||
| 1800 | goto step7; | ||
| 1801 | if (*dest == '.') { | ||
| 1802 | c = dest[1]; | ||
| 1803 | dotdot: | ||
| 1804 | switch (c) { | ||
| 1805 | case '\0': | ||
| 1806 | case '/': | ||
| 1807 | goto step6; | ||
| 1808 | case '.': | ||
| 1809 | c = dest[2]; | ||
| 1810 | if (c != '.') | ||
| 1811 | goto dotdot; | ||
| 1812 | } | ||
| 1813 | } | ||
| 1814 | if (!*dest) | ||
| 1815 | dest = "."; | ||
| 1816 | path = bltinlookup("CDPATH"); | ||
| 1817 | if (!path) { | ||
| 1818 | step6: | ||
| 1819 | step7: | ||
| 1820 | p = dest; | ||
| 1821 | goto docd; | ||
| 1822 | } | ||
| 1823 | do { | ||
| 1824 | c = *path; | ||
| 1825 | p = padvance(&path, dest); | ||
| 1826 | if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) { | ||
| 1827 | if (c && c != ':') | ||
| 1828 | flags |= CD_PRINT; | ||
| 1829 | docd: | ||
| 1830 | if (!docd(p, flags)) | ||
| 1831 | goto out; | ||
| 1832 | break; | ||
| 1833 | } | ||
| 1834 | } while (path); | ||
| 1835 | ash_msg_and_raise_error("can't cd to %s", dest); | ||
| 1836 | /* NOTREACHED */ | ||
| 1837 | out: | ||
| 1838 | if (flags & CD_PRINT) | ||
| 1839 | out1fmt(snlfmt, curdir); | ||
| 1840 | return 0; | ||
| 1841 | } | ||
| 1842 | |||
| 1843 | static int | ||
| 1844 | pwdcmd(int argc, char **argv) | ||
| 1845 | { | ||
| 1846 | int flags; | ||
| 1847 | const char *dir = curdir; | ||
| 1848 | |||
| 1849 | flags = cdopt(); | ||
| 1850 | if (flags) { | ||
| 1851 | if (physdir == nullstr) | ||
| 1852 | setpwd(dir, 0); | ||
| 1853 | dir = physdir; | ||
| 1854 | } | ||
| 1855 | out1fmt(snlfmt, dir); | ||
| 1856 | return 0; | ||
| 1857 | } | ||
| 1858 | |||
| 1859 | |||
| 1860 | /* ============ Unsorted yet */ | ||
| 1861 | |||
| 1862 | |||
| 1863 | /* expand.h */ | ||
| 801 | 1864 | ||
| 802 | struct arglist { | 1865 | struct arglist { |
| 803 | struct strlist *list; | 1866 | struct strlist *list; |
| @@ -1046,8 +2109,6 @@ static char *parsenextc; /* copy of parsefile->nextc */ | |||
| 1046 | static int tokpushback; /* last token pushed back */ | 2109 | static int tokpushback; /* last token pushed back */ |
| 1047 | #define NEOF ((union node *)&tokpushback) | 2110 | #define NEOF ((union node *)&tokpushback) |
| 1048 | static int parsebackquote; /* nonzero if we are inside backquotes */ | 2111 | static int parsebackquote; /* nonzero if we are inside backquotes */ |
| 1049 | static int doprompt; /* if set, prompt the user */ | ||
| 1050 | static int needprompt; /* true if interactive and at start of line */ | ||
| 1051 | static int lasttoken; /* last token read */ | 2112 | static int lasttoken; /* last token read */ |
| 1052 | static char *wordtext; /* text of last word returned by readtoken */ | 2113 | static char *wordtext; /* text of last word returned by readtoken */ |
| 1053 | static int checkkwd; | 2114 | static int checkkwd; |
| @@ -1061,20 +2122,8 @@ static char *endofname(const char *); | |||
| 1061 | 2122 | ||
| 1062 | /* shell.h */ | 2123 | /* shell.h */ |
| 1063 | 2124 | ||
| 1064 | static char nullstr[1]; /* zero length string */ | ||
| 1065 | static const char spcstr[] = " "; | 2125 | static const char spcstr[] = " "; |
| 1066 | static const char snlfmt[] = "%s\n"; | ||
| 1067 | static const char dolatstr[] = { CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0' }; | 2126 | static const char dolatstr[] = { CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0' }; |
| 1068 | static const char illnum[] = "Illegal number: %s"; | ||
| 1069 | static const char homestr[] = "HOME"; | ||
| 1070 | |||
| 1071 | #if DEBUG | ||
| 1072 | #define TRACE(param) trace param | ||
| 1073 | #define TRACEV(param) tracev param | ||
| 1074 | #else | ||
| 1075 | #define TRACE(param) | ||
| 1076 | #define TRACEV(param) | ||
| 1077 | #endif | ||
| 1078 | 2127 | ||
| 1079 | #if !defined(__GNUC__) || (__GNUC__ == 2 && __GNUC_MINOR__ < 96) | 2128 | #if !defined(__GNUC__) || (__GNUC__ == 2 && __GNUC_MINOR__ < 96) |
| 1080 | #define __builtin_expect(x, expected_value) (x) | 2129 | #define __builtin_expect(x, expected_value) (x) |
| @@ -1203,10 +2252,6 @@ findkwd(const char *s) | |||
| 1203 | #define PEOA_OR_PEOF PEOF | 2252 | #define PEOA_OR_PEOF PEOF |
| 1204 | #endif | 2253 | #endif |
| 1205 | 2254 | ||
| 1206 | #define is_digit(c) ((unsigned)((c) - '0') <= 9) | ||
| 1207 | #define is_name(c) ((c) == '_' || isalpha((unsigned char)(c))) | ||
| 1208 | #define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c))) | ||
| 1209 | |||
| 1210 | /* C99 say: "char" declaration may be signed or unsigned default */ | 2255 | /* C99 say: "char" declaration may be signed or unsigned default */ |
| 1211 | #define SC2INT(chr2may_be_negative_int) (int)((signed char)chr2may_be_negative_int) | 2256 | #define SC2INT(chr2may_be_negative_int) (int)((signed char)chr2may_be_negative_int) |
| 1212 | 2257 | ||
| @@ -1743,13 +2788,6 @@ static int ulimitcmd(int, char **); | |||
| 1743 | static int killcmd(int, char **); | 2788 | static int killcmd(int, char **); |
| 1744 | #endif | 2789 | #endif |
| 1745 | 2790 | ||
| 1746 | /* mail.h */ | ||
| 1747 | |||
| 1748 | #if ENABLE_ASH_MAIL | ||
| 1749 | static void chkmail(void); | ||
| 1750 | static void changemail(const char *); | ||
| 1751 | #endif | ||
| 1752 | |||
| 1753 | /* exec.h */ | 2791 | /* exec.h */ |
| 1754 | 2792 | ||
| 1755 | /* values of cmdtype */ | 2793 | /* values of cmdtype */ |
| @@ -1879,14 +2917,10 @@ struct cmdentry { | |||
| 1879 | #define DO_ALTPATH 0x08 /* using alternate path */ | 2917 | #define DO_ALTPATH 0x08 /* using alternate path */ |
| 1880 | #define DO_ALTBLTIN 0x20 /* %builtin in alt. path */ | 2918 | #define DO_ALTBLTIN 0x20 /* %builtin in alt. path */ |
| 1881 | 2919 | ||
| 1882 | static const char *pathopt; /* set by padvance */ | ||
| 1883 | |||
| 1884 | static void shellexec(char **, const char *, int) ATTRIBUTE_NORETURN; | 2920 | static void shellexec(char **, const char *, int) ATTRIBUTE_NORETURN; |
| 1885 | static char *padvance(const char **, const char *); | 2921 | static char *padvance(const char **, const char *); |
| 1886 | static void find_command(char *, struct cmdentry *, int, const char *); | 2922 | static void find_command(char *, struct cmdentry *, int, const char *); |
| 1887 | static struct builtincmd *find_builtin(const char *); | 2923 | static struct builtincmd *find_builtin(const char *); |
| 1888 | static void hashcd(void); | ||
| 1889 | static void changepath(const char *); | ||
| 1890 | static void defun(char *, union node *); | 2924 | static void defun(char *, union node *); |
| 1891 | static void unsetfunc(const char *); | 2925 | static void unsetfunc(const char *); |
| 1892 | 2926 | ||
| @@ -1905,222 +2939,11 @@ static arith_t arith(const char *expr, int *perrcode); | |||
| 1905 | 2939 | ||
| 1906 | #if ENABLE_ASH_RANDOM_SUPPORT | 2940 | #if ENABLE_ASH_RANDOM_SUPPORT |
| 1907 | static unsigned long rseed; | 2941 | static unsigned long rseed; |
| 1908 | static void change_random(const char *); | ||
| 1909 | # ifndef DYNAMIC_VAR | 2942 | # ifndef DYNAMIC_VAR |
| 1910 | # define DYNAMIC_VAR | 2943 | # define DYNAMIC_VAR |
| 1911 | # endif | 2944 | # endif |
| 1912 | #endif | 2945 | #endif |
| 1913 | 2946 | ||
| 1914 | /* var.h */ | ||
| 1915 | |||
| 1916 | /* | ||
| 1917 | * Shell variables. | ||
| 1918 | */ | ||
| 1919 | |||
| 1920 | #if ENABLE_ASH_GETOPTS | ||
| 1921 | static void getoptsreset(const char *); | ||
| 1922 | #endif | ||
| 1923 | |||
| 1924 | /* flags */ | ||
| 1925 | #define VEXPORT 0x01 /* variable is exported */ | ||
| 1926 | #define VREADONLY 0x02 /* variable cannot be modified */ | ||
| 1927 | #define VSTRFIXED 0x04 /* variable struct is statically allocated */ | ||
| 1928 | #define VTEXTFIXED 0x08 /* text is statically allocated */ | ||
| 1929 | #define VSTACK 0x10 /* text is allocated on the stack */ | ||
| 1930 | #define VUNSET 0x20 /* the variable is not set */ | ||
| 1931 | #define VNOFUNC 0x40 /* don't call the callback function */ | ||
| 1932 | #define VNOSET 0x80 /* do not set variable - just readonly test */ | ||
| 1933 | #define VNOSAVE 0x100 /* when text is on the heap before setvareq */ | ||
| 1934 | #ifdef DYNAMIC_VAR | ||
| 1935 | # define VDYNAMIC 0x200 /* dynamic variable */ | ||
| 1936 | # else | ||
| 1937 | # define VDYNAMIC 0 | ||
| 1938 | #endif | ||
| 1939 | |||
| 1940 | struct var { | ||
| 1941 | struct var *next; /* next entry in hash list */ | ||
| 1942 | int flags; /* flags are defined above */ | ||
| 1943 | const char *text; /* name=value */ | ||
| 1944 | void (*func)(const char *); /* function to be called when */ | ||
| 1945 | /* the variable gets set/unset */ | ||
| 1946 | }; | ||
| 1947 | |||
| 1948 | struct localvar { | ||
| 1949 | struct localvar *next; /* next local variable in list */ | ||
| 1950 | struct var *vp; /* the variable that was made local */ | ||
| 1951 | int flags; /* saved flags */ | ||
| 1952 | const char *text; /* saved text */ | ||
| 1953 | }; | ||
| 1954 | |||
| 1955 | |||
| 1956 | static struct localvar *localvars; | ||
| 1957 | |||
| 1958 | /* | ||
| 1959 | * Shell variables. | ||
| 1960 | */ | ||
| 1961 | #if ENABLE_LOCALE_SUPPORT | ||
| 1962 | static void change_lc_all(const char *value); | ||
| 1963 | static void change_lc_ctype(const char *value); | ||
| 1964 | #endif | ||
| 1965 | |||
| 1966 | |||
| 1967 | #define VTABSIZE 39 | ||
| 1968 | |||
| 1969 | static const char defpathvar[] = "PATH=/usr/local/bin:/usr/bin:/sbin:/bin"; | ||
| 1970 | #ifdef IFS_BROKEN | ||
| 1971 | static const char defifsvar[] = "IFS= \t\n"; | ||
| 1972 | #define defifs (defifsvar + 4) | ||
| 1973 | #else | ||
| 1974 | static const char defifs[] = " \t\n"; | ||
| 1975 | #endif | ||
| 1976 | |||
| 1977 | |||
| 1978 | static struct var varinit[] = { | ||
| 1979 | #ifdef IFS_BROKEN | ||
| 1980 | { 0, VSTRFIXED|VTEXTFIXED, defifsvar, 0 }, | ||
| 1981 | #else | ||
| 1982 | { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0", 0 }, | ||
| 1983 | #endif | ||
| 1984 | |||
| 1985 | #if ENABLE_ASH_MAIL | ||
| 1986 | { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0", changemail }, | ||
| 1987 | { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail }, | ||
| 1988 | #endif | ||
| 1989 | |||
| 1990 | { 0, VSTRFIXED|VTEXTFIXED, defpathvar, changepath }, | ||
| 1991 | { 0, VSTRFIXED|VTEXTFIXED, "PS1=$ ", 0 }, | ||
| 1992 | { 0, VSTRFIXED|VTEXTFIXED, "PS2=> ", 0 }, | ||
| 1993 | { 0, VSTRFIXED|VTEXTFIXED, "PS4=+ ", 0 }, | ||
| 1994 | #if ENABLE_ASH_GETOPTS | ||
| 1995 | { 0, VSTRFIXED|VTEXTFIXED, "OPTIND=1", getoptsreset }, | ||
| 1996 | #endif | ||
| 1997 | #if ENABLE_ASH_RANDOM_SUPPORT | ||
| 1998 | {0, VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random }, | ||
| 1999 | #endif | ||
| 2000 | #if ENABLE_LOCALE_SUPPORT | ||
| 2001 | {0, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_ALL\0", change_lc_all }, | ||
| 2002 | {0, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_CTYPE\0", change_lc_ctype }, | ||
| 2003 | #endif | ||
| 2004 | #if ENABLE_FEATURE_EDITING_SAVEHISTORY | ||
| 2005 | {0, VSTRFIXED | VTEXTFIXED | VUNSET, "HISTFILE\0", NULL }, | ||
| 2006 | #endif | ||
| 2007 | }; | ||
| 2008 | |||
| 2009 | #define vifs varinit[0] | ||
| 2010 | #if ENABLE_ASH_MAIL | ||
| 2011 | #define vmail (&vifs)[1] | ||
| 2012 | #define vmpath (&vmail)[1] | ||
| 2013 | #else | ||
| 2014 | #define vmpath vifs | ||
| 2015 | #endif | ||
| 2016 | #define vpath (&vmpath)[1] | ||
| 2017 | #define vps1 (&vpath)[1] | ||
| 2018 | #define vps2 (&vps1)[1] | ||
| 2019 | #define vps4 (&vps2)[1] | ||
| 2020 | #define voptind (&vps4)[1] | ||
| 2021 | #if ENABLE_ASH_GETOPTS | ||
| 2022 | #define vrandom (&voptind)[1] | ||
| 2023 | #else | ||
| 2024 | #define vrandom (&vps4)[1] | ||
| 2025 | #endif | ||
| 2026 | #define defpath (defpathvar + 5) | ||
| 2027 | |||
| 2028 | /* | ||
| 2029 | * The following macros access the values of the above variables. | ||
| 2030 | * They have to skip over the name. They return the null string | ||
| 2031 | * for unset variables. | ||
| 2032 | */ | ||
| 2033 | |||
| 2034 | #define ifsval() (vifs.text + 4) | ||
| 2035 | #define ifsset() ((vifs.flags & VUNSET) == 0) | ||
| 2036 | #define mailval() (vmail.text + 5) | ||
| 2037 | #define mpathval() (vmpath.text + 9) | ||
| 2038 | #define pathval() (vpath.text + 5) | ||
| 2039 | #define ps1val() (vps1.text + 4) | ||
| 2040 | #define ps2val() (vps2.text + 4) | ||
| 2041 | #define ps4val() (vps4.text + 4) | ||
| 2042 | #define optindval() (voptind.text + 7) | ||
| 2043 | |||
| 2044 | #define mpathset() ((vmpath.flags & VUNSET) == 0) | ||
| 2045 | |||
| 2046 | static void setvar(const char *, const char *, int); | ||
| 2047 | static void setvareq(char *, int); | ||
| 2048 | static void listsetvar(struct strlist *, int); | ||
| 2049 | static char *lookupvar(const char *); | ||
| 2050 | static char *bltinlookup(const char *); | ||
| 2051 | static char **listvars(int, int, char ***); | ||
| 2052 | #define environment() listvars(VEXPORT, VUNSET, 0) | ||
| 2053 | static int showvars(const char *, int, int); | ||
| 2054 | static void poplocalvars(void); | ||
| 2055 | static int unsetvar(const char *); | ||
| 2056 | #if ENABLE_ASH_GETOPTS | ||
| 2057 | static int setvarsafe(const char *, const char *, int); | ||
| 2058 | #endif | ||
| 2059 | static int varcmp(const char *, const char *); | ||
| 2060 | static struct var **hashvar(const char *); | ||
| 2061 | |||
| 2062 | |||
| 2063 | static int varequal(const char *a, const char *b) | ||
| 2064 | { | ||
| 2065 | return !varcmp(a, b); | ||
| 2066 | } | ||
| 2067 | |||
| 2068 | |||
| 2069 | static int loopnest; /* current loop nesting level */ | ||
| 2070 | |||
| 2071 | /* | ||
| 2072 | * The parsefile structure pointed to by the global variable parsefile | ||
| 2073 | * contains information about the current file being read. | ||
| 2074 | */ | ||
| 2075 | |||
| 2076 | |||
| 2077 | struct redirtab { | ||
| 2078 | struct redirtab *next; | ||
| 2079 | int renamed[10]; | ||
| 2080 | int nullredirs; | ||
| 2081 | }; | ||
| 2082 | |||
| 2083 | static struct redirtab *redirlist; | ||
| 2084 | static int nullredirs; | ||
| 2085 | |||
| 2086 | extern char **environ; | ||
| 2087 | |||
| 2088 | |||
| 2089 | static int preverrout_fd; /* save fd2 before print debug if xflag is set. */ | ||
| 2090 | |||
| 2091 | |||
| 2092 | /* | ||
| 2093 | * Initialization code. | ||
| 2094 | */ | ||
| 2095 | |||
| 2096 | /* | ||
| 2097 | * This routine initializes the builtin variables. | ||
| 2098 | */ | ||
| 2099 | |||
| 2100 | static void initvar(void) | ||
| 2101 | { | ||
| 2102 | struct var *vp; | ||
| 2103 | struct var *end; | ||
| 2104 | struct var **vpp; | ||
| 2105 | |||
| 2106 | /* | ||
| 2107 | * PS1 depends on uid | ||
| 2108 | */ | ||
| 2109 | #if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT | ||
| 2110 | vps1.text = "PS1=\\w \\$ "; | ||
| 2111 | #else | ||
| 2112 | if (!geteuid()) | ||
| 2113 | vps1.text = "PS1=# "; | ||
| 2114 | #endif | ||
| 2115 | vp = varinit; | ||
| 2116 | end = vp + sizeof(varinit) / sizeof(varinit[0]); | ||
| 2117 | do { | ||
| 2118 | vpp = hashvar(vp->text); | ||
| 2119 | vp->next = *vpp; | ||
| 2120 | *vpp = vp; | ||
| 2121 | } while (++vp < end); | ||
| 2122 | } | ||
| 2123 | |||
| 2124 | /* PEOF (the end of file marker) */ | 2947 | /* PEOF (the end of file marker) */ |
| 2125 | 2948 | ||
| 2126 | enum { | 2949 | enum { |
| @@ -2133,10 +2956,6 @@ enum { | |||
| 2133 | * and restores it when files are pushed and popped. The user of this | 2956 | * and restores it when files are pushed and popped. The user of this |
| 2134 | * package must set its value. | 2957 | * package must set its value. |
| 2135 | */ | 2958 | */ |
| 2136 | |||
| 2137 | static int pgetc(void); | ||
| 2138 | static int pgetc2(void); | ||
| 2139 | static int preadbuffer(void); | ||
| 2140 | static void pungetc(void); | 2959 | static void pungetc(void); |
| 2141 | static void pushstring(char *, void *); | 2960 | static void pushstring(char *, void *); |
| 2142 | static void popstring(void); | 2961 | static void popstring(void); |
| @@ -2146,7 +2965,6 @@ static void popfile(void); | |||
| 2146 | static void popallfiles(void); | 2965 | static void popallfiles(void); |
| 2147 | static void closescript(void); | 2966 | static void closescript(void); |
| 2148 | 2967 | ||
| 2149 | |||
| 2150 | /* jobs.h */ | 2968 | /* jobs.h */ |
| 2151 | 2969 | ||
| 2152 | 2970 | ||
| @@ -2220,35 +3038,6 @@ static void showjobs(FILE *, int); | |||
| 2220 | static void readcmdfile(char *); | 3038 | static void readcmdfile(char *); |
| 2221 | 3039 | ||
| 2222 | 3040 | ||
| 2223 | static char *_STPUTC(int c, char *p) | ||
| 2224 | { | ||
| 2225 | if (p == sstrend) | ||
| 2226 | p = growstackstr(); | ||
| 2227 | *p++ = c; | ||
| 2228 | return p; | ||
| 2229 | } | ||
| 2230 | |||
| 2231 | #define STARTSTACKSTR(p) ((p) = stackblock()) | ||
| 2232 | #define STPUTC(c, p) ((p) = _STPUTC((c), (p))) | ||
| 2233 | #define CHECKSTRSPACE(n, p) \ | ||
| 2234 | ({ \ | ||
| 2235 | char *q = (p); \ | ||
| 2236 | size_t l = (n); \ | ||
| 2237 | size_t m = sstrend - q; \ | ||
| 2238 | if (l > m) \ | ||
| 2239 | (p) = makestrspace(l, q); \ | ||
| 2240 | 0; \ | ||
| 2241 | }) | ||
| 2242 | #define USTPUTC(c, p) (*p++ = (c)) | ||
| 2243 | #define STACKSTRNUL(p) ((p) == sstrend? (p = growstackstr(), *p = '\0') : (*p = '\0')) | ||
| 2244 | #define STUNPUTC(p) (--p) | ||
| 2245 | #define STTOPC(p) p[-1] | ||
| 2246 | #define STADJUST(amount, p) (p += (amount)) | ||
| 2247 | |||
| 2248 | #define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock()) | ||
| 2249 | #define ungrabstackstr(s, p) stunalloc((s)) | ||
| 2250 | #define stackstrend() ((void *)sstrend) | ||
| 2251 | |||
| 2252 | /* mystring.h */ | 3041 | /* mystring.h */ |
| 2253 | 3042 | ||
| 2254 | 3043 | ||
| @@ -2258,29 +3047,12 @@ static char *prefix(const char *, const char *); | |||
| 2258 | static int number(const char *); | 3047 | static int number(const char *); |
| 2259 | static int is_number(const char *); | 3048 | static int is_number(const char *); |
| 2260 | static char *single_quote(const char *); | 3049 | static char *single_quote(const char *); |
| 2261 | static char *sstrdup(const char *); | ||
| 2262 | 3050 | ||
| 2263 | #define equal(s1, s2) (strcmp(s1, s2) == 0) | 3051 | #define equal(s1, s2) (strcmp(s1, s2) == 0) |
| 2264 | #define scopy(s1, s2) ((void)strcpy(s2, s1)) | 3052 | #define scopy(s1, s2) ((void)strcpy(s2, s1)) |
| 2265 | 3053 | ||
| 2266 | /* options.h */ | 3054 | /* options.h */ |
| 2267 | 3055 | ||
| 2268 | struct shparam { | ||
| 2269 | int nparam; /* # of positional parameters (without $0) */ | ||
| 2270 | unsigned char malloc; /* if parameter list dynamically allocated */ | ||
| 2271 | char **p; /* parameter list */ | ||
| 2272 | #if ENABLE_ASH_GETOPTS | ||
| 2273 | int optind; /* next parameter to be processed by getopts */ | ||
| 2274 | int optoff; /* used by getopts */ | ||
| 2275 | #endif | ||
| 2276 | }; | ||
| 2277 | |||
| 2278 | |||
| 2279 | static struct shparam shellparam; /* $@ current positional parameters */ | ||
| 2280 | static char **argptr; /* argument list for builtin commands */ | ||
| 2281 | static char *optionarg; /* set by nextopt (like getopt) */ | ||
| 2282 | static char *optptr; /* used by nextopt */ | ||
| 2283 | |||
| 2284 | static char *minusc; /* argument to -c option */ | 3056 | static char *minusc; /* argument to -c option */ |
| 2285 | 3057 | ||
| 2286 | 3058 | ||
| @@ -2550,250 +3322,6 @@ __lookupalias(const char *name) { | |||
| 2550 | } | 3322 | } |
| 2551 | #endif /* ASH_ALIAS */ | 3323 | #endif /* ASH_ALIAS */ |
| 2552 | 3324 | ||
| 2553 | |||
| 2554 | /* cd.c */ | ||
| 2555 | |||
| 2556 | /* | ||
| 2557 | * The cd and pwd commands. | ||
| 2558 | */ | ||
| 2559 | |||
| 2560 | #define CD_PHYSICAL 1 | ||
| 2561 | #define CD_PRINT 2 | ||
| 2562 | |||
| 2563 | static int docd(const char *, int); | ||
| 2564 | static int cdopt(void); | ||
| 2565 | |||
| 2566 | static char *curdir = nullstr; /* current working directory */ | ||
| 2567 | static char *physdir = nullstr; /* physical working directory */ | ||
| 2568 | |||
| 2569 | static int | ||
| 2570 | cdopt(void) | ||
| 2571 | { | ||
| 2572 | int flags = 0; | ||
| 2573 | int i, j; | ||
| 2574 | |||
| 2575 | j = 'L'; | ||
| 2576 | while ((i = nextopt("LP"))) { | ||
| 2577 | if (i != j) { | ||
| 2578 | flags ^= CD_PHYSICAL; | ||
| 2579 | j = i; | ||
| 2580 | } | ||
| 2581 | } | ||
| 2582 | |||
| 2583 | return flags; | ||
| 2584 | } | ||
| 2585 | |||
| 2586 | static int | ||
| 2587 | cdcmd(int argc, char **argv) | ||
| 2588 | { | ||
| 2589 | const char *dest; | ||
| 2590 | const char *path; | ||
| 2591 | const char *p; | ||
| 2592 | char c; | ||
| 2593 | struct stat statb; | ||
| 2594 | int flags; | ||
| 2595 | |||
| 2596 | flags = cdopt(); | ||
| 2597 | dest = *argptr; | ||
| 2598 | if (!dest) | ||
| 2599 | dest = bltinlookup(homestr); | ||
| 2600 | else if (LONE_DASH(dest)) { | ||
| 2601 | dest = bltinlookup("OLDPWD"); | ||
| 2602 | flags |= CD_PRINT; | ||
| 2603 | } | ||
| 2604 | if (!dest) | ||
| 2605 | dest = nullstr; | ||
| 2606 | if (*dest == '/') | ||
| 2607 | goto step7; | ||
| 2608 | if (*dest == '.') { | ||
| 2609 | c = dest[1]; | ||
| 2610 | dotdot: | ||
| 2611 | switch (c) { | ||
| 2612 | case '\0': | ||
| 2613 | case '/': | ||
| 2614 | goto step6; | ||
| 2615 | case '.': | ||
| 2616 | c = dest[2]; | ||
| 2617 | if (c != '.') | ||
| 2618 | goto dotdot; | ||
| 2619 | } | ||
| 2620 | } | ||
| 2621 | if (!*dest) | ||
| 2622 | dest = "."; | ||
| 2623 | path = bltinlookup("CDPATH"); | ||
| 2624 | if (!path) { | ||
| 2625 | step6: | ||
| 2626 | step7: | ||
| 2627 | p = dest; | ||
| 2628 | goto docd; | ||
| 2629 | } | ||
| 2630 | do { | ||
| 2631 | c = *path; | ||
| 2632 | p = padvance(&path, dest); | ||
| 2633 | if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) { | ||
| 2634 | if (c && c != ':') | ||
| 2635 | flags |= CD_PRINT; | ||
| 2636 | docd: | ||
| 2637 | if (!docd(p, flags)) | ||
| 2638 | goto out; | ||
| 2639 | break; | ||
| 2640 | } | ||
| 2641 | } while (path); | ||
| 2642 | ash_msg_and_raise_error("can't cd to %s", dest); | ||
| 2643 | /* NOTREACHED */ | ||
| 2644 | out: | ||
| 2645 | if (flags & CD_PRINT) | ||
| 2646 | out1fmt(snlfmt, curdir); | ||
| 2647 | return 0; | ||
| 2648 | } | ||
| 2649 | |||
| 2650 | |||
| 2651 | /* | ||
| 2652 | * Update curdir (the name of the current directory) in response to a | ||
| 2653 | * cd command. | ||
| 2654 | */ | ||
| 2655 | static const char * updatepwd(const char *dir) | ||
| 2656 | { | ||
| 2657 | char *new; | ||
| 2658 | char *p; | ||
| 2659 | char *cdcomppath; | ||
| 2660 | const char *lim; | ||
| 2661 | |||
| 2662 | cdcomppath = sstrdup(dir); | ||
| 2663 | STARTSTACKSTR(new); | ||
| 2664 | if (*dir != '/') { | ||
| 2665 | if (curdir == nullstr) | ||
| 2666 | return 0; | ||
| 2667 | new = stack_putstr(curdir, new); | ||
| 2668 | } | ||
| 2669 | new = makestrspace(strlen(dir) + 2, new); | ||
| 2670 | lim = stackblock() + 1; | ||
| 2671 | if (*dir != '/') { | ||
| 2672 | if (new[-1] != '/') | ||
| 2673 | USTPUTC('/', new); | ||
| 2674 | if (new > lim && *lim == '/') | ||
| 2675 | lim++; | ||
| 2676 | } else { | ||
| 2677 | USTPUTC('/', new); | ||
| 2678 | cdcomppath++; | ||
| 2679 | if (dir[1] == '/' && dir[2] != '/') { | ||
| 2680 | USTPUTC('/', new); | ||
| 2681 | cdcomppath++; | ||
| 2682 | lim++; | ||
| 2683 | } | ||
| 2684 | } | ||
| 2685 | p = strtok(cdcomppath, "/"); | ||
| 2686 | while (p) { | ||
| 2687 | switch (*p) { | ||
| 2688 | case '.': | ||
| 2689 | if (p[1] == '.' && p[2] == '\0') { | ||
| 2690 | while (new > lim) { | ||
| 2691 | STUNPUTC(new); | ||
| 2692 | if (new[-1] == '/') | ||
| 2693 | break; | ||
| 2694 | } | ||
| 2695 | break; | ||
| 2696 | } else if (p[1] == '\0') | ||
| 2697 | break; | ||
| 2698 | /* fall through */ | ||
| 2699 | default: | ||
| 2700 | new = stack_putstr(p, new); | ||
| 2701 | USTPUTC('/', new); | ||
| 2702 | } | ||
| 2703 | p = strtok(0, "/"); | ||
| 2704 | } | ||
| 2705 | if (new > lim) | ||
| 2706 | STUNPUTC(new); | ||
| 2707 | *new = 0; | ||
| 2708 | return stackblock(); | ||
| 2709 | } | ||
| 2710 | |||
| 2711 | |||
| 2712 | /* | ||
| 2713 | * Actually do the chdir. We also call hashcd to let the routines in exec.c | ||
| 2714 | * know that the current directory has changed. | ||
| 2715 | */ | ||
| 2716 | static int | ||
| 2717 | docd(const char *dest, int flags) | ||
| 2718 | { | ||
| 2719 | const char *dir = 0; | ||
| 2720 | int err; | ||
| 2721 | |||
| 2722 | TRACE(("docd(\"%s\", %d) called\n", dest, flags)); | ||
| 2723 | |||
| 2724 | INT_OFF; | ||
| 2725 | if (!(flags & CD_PHYSICAL)) { | ||
| 2726 | dir = updatepwd(dest); | ||
| 2727 | if (dir) | ||
| 2728 | dest = dir; | ||
| 2729 | } | ||
| 2730 | err = chdir(dest); | ||
| 2731 | if (err) | ||
| 2732 | goto out; | ||
| 2733 | setpwd(dir, 1); | ||
| 2734 | hashcd(); | ||
| 2735 | out: | ||
| 2736 | INT_ON; | ||
| 2737 | return err; | ||
| 2738 | } | ||
| 2739 | |||
| 2740 | /* | ||
| 2741 | * Find out what the current directory is. If we already know the current | ||
| 2742 | * directory, this routine returns immediately. | ||
| 2743 | */ | ||
| 2744 | static char * getpwd(void) | ||
| 2745 | { | ||
| 2746 | char *dir = getcwd(0, 0); | ||
| 2747 | return dir ? dir : nullstr; | ||
| 2748 | } | ||
| 2749 | |||
| 2750 | static int | ||
| 2751 | pwdcmd(int argc, char **argv) | ||
| 2752 | { | ||
| 2753 | int flags; | ||
| 2754 | const char *dir = curdir; | ||
| 2755 | |||
| 2756 | flags = cdopt(); | ||
| 2757 | if (flags) { | ||
| 2758 | if (physdir == nullstr) | ||
| 2759 | setpwd(dir, 0); | ||
| 2760 | dir = physdir; | ||
| 2761 | } | ||
| 2762 | out1fmt(snlfmt, dir); | ||
| 2763 | return 0; | ||
| 2764 | } | ||
| 2765 | |||
| 2766 | static void | ||
| 2767 | setpwd(const char *val, int setold) | ||
| 2768 | { | ||
| 2769 | char *oldcur, *dir; | ||
| 2770 | |||
| 2771 | oldcur = dir = curdir; | ||
| 2772 | |||
| 2773 | if (setold) { | ||
| 2774 | setvar("OLDPWD", oldcur, VEXPORT); | ||
| 2775 | } | ||
| 2776 | INT_OFF; | ||
| 2777 | if (physdir != nullstr) { | ||
| 2778 | if (physdir != oldcur) | ||
| 2779 | free(physdir); | ||
| 2780 | physdir = nullstr; | ||
| 2781 | } | ||
| 2782 | if (oldcur == val || !val) { | ||
| 2783 | char *s = getpwd(); | ||
| 2784 | physdir = s; | ||
| 2785 | if (!val) | ||
| 2786 | dir = s; | ||
| 2787 | } else | ||
| 2788 | dir = ckstrdup(val); | ||
| 2789 | if (oldcur != dir && oldcur != nullstr) { | ||
| 2790 | free(oldcur); | ||
| 2791 | } | ||
| 2792 | curdir = dir; | ||
| 2793 | INT_ON; | ||
| 2794 | setvar("PWD", dir, VEXPORT); | ||
| 2795 | } | ||
| 2796 | |||
| 2797 | /* eval.c */ | 3325 | /* eval.c */ |
| 2798 | 3326 | ||
| 2799 | /* | 3327 | /* |
| @@ -3235,7 +3763,8 @@ evalbackcmd(union node *n, struct backcmd *result) | |||
| 3235 | } | 3763 | } |
| 3236 | 3764 | ||
| 3237 | #if ENABLE_ASH_CMDCMD | 3765 | #if ENABLE_ASH_CMDCMD |
| 3238 | static char ** parse_command_args(char **argv, const char **path) | 3766 | static char ** |
| 3767 | parse_command_args(char **argv, const char **path) | ||
| 3239 | { | 3768 | { |
| 3240 | char *cp, c; | 3769 | char *cp, c; |
| 3241 | 3770 | ||
| @@ -3275,12 +3804,6 @@ static int isassignment(const char *p) | |||
| 3275 | return *q == '='; | 3804 | return *q == '='; |
| 3276 | } | 3805 | } |
| 3277 | 3806 | ||
| 3278 | #if ENABLE_ASH_EXPAND_PRMT | ||
| 3279 | static const char *expandstr(const char *ps); | ||
| 3280 | #else | ||
| 3281 | #define expandstr(s) s | ||
| 3282 | #endif | ||
| 3283 | |||
| 3284 | /* | 3807 | /* |
| 3285 | * Execute a simple command. | 3808 | * Execute a simple command. |
| 3286 | */ | 3809 | */ |
| @@ -3543,6 +4066,40 @@ evalbltin(const struct builtincmd *cmd, int argc, char **argv) | |||
| 3543 | return i; | 4066 | return i; |
| 3544 | } | 4067 | } |
| 3545 | 4068 | ||
| 4069 | static struct localvar *localvars; | ||
| 4070 | |||
| 4071 | /* | ||
| 4072 | * Called after a function returns. | ||
| 4073 | * Interrupts must be off. | ||
| 4074 | */ | ||
| 4075 | static void | ||
| 4076 | poplocalvars(void) | ||
| 4077 | { | ||
| 4078 | struct localvar *lvp; | ||
| 4079 | struct var *vp; | ||
| 4080 | |||
| 4081 | while ((lvp = localvars) != NULL) { | ||
| 4082 | localvars = lvp->next; | ||
| 4083 | vp = lvp->vp; | ||
| 4084 | TRACE(("poplocalvar %s", vp ? vp->text : "-")); | ||
| 4085 | if (vp == NULL) { /* $- saved */ | ||
| 4086 | memcpy(optlist, lvp->text, sizeof(optlist)); | ||
| 4087 | free((char*)lvp->text); | ||
| 4088 | optschanged(); | ||
| 4089 | } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) { | ||
| 4090 | unsetvar(vp->text); | ||
| 4091 | } else { | ||
| 4092 | if (vp->func) | ||
| 4093 | (*vp->func)(strchrnul(lvp->text, '=') + 1); | ||
| 4094 | if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0) | ||
| 4095 | free((char*)vp->text); | ||
| 4096 | vp->flags = lvp->flags; | ||
| 4097 | vp->text = lvp->text; | ||
| 4098 | } | ||
| 4099 | free(lvp); | ||
| 4100 | } | ||
| 4101 | } | ||
| 4102 | |||
| 3546 | static int | 4103 | static int |
| 3547 | evalfun(struct funcnode *func, int argc, char **argv, int flags) | 4104 | evalfun(struct funcnode *func, int argc, char **argv, int flags) |
| 3548 | { | 4105 | { |
| @@ -3588,7 +4145,8 @@ funcdone: | |||
| 3588 | } | 4145 | } |
| 3589 | 4146 | ||
| 3590 | 4147 | ||
| 3591 | static int goodname(const char *p) | 4148 | static int |
| 4149 | goodname(const char *p) | ||
| 3592 | { | 4150 | { |
| 3593 | return !*endofname(p); | 4151 | return !*endofname(p); |
| 3594 | } | 4152 | } |
| @@ -3738,6 +4296,7 @@ static void delete_cmd_entry(void); | |||
| 3738 | * Exec a program. Never returns. If you change this routine, you may | 4296 | * Exec a program. Never returns. If you change this routine, you may |
| 3739 | * have to change the find_command routine as well. | 4297 | * have to change the find_command routine as well. |
| 3740 | */ | 4298 | */ |
| 4299 | #define environment() listvars(VEXPORT, VUNSET, 0) | ||
| 3741 | static void shellexec(char **, const char *, int) ATTRIBUTE_NORETURN; | 4300 | static void shellexec(char **, const char *, int) ATTRIBUTE_NORETURN; |
| 3742 | static void | 4301 | static void |
| 3743 | shellexec(char **argv, const char *path, int idx) | 4302 | shellexec(char **argv, const char *path, int idx) |
| @@ -3845,50 +4404,6 @@ tryexec(char *cmd, char **argv, char **envp) | |||
| 3845 | } | 4404 | } |
| 3846 | 4405 | ||
| 3847 | 4406 | ||
| 3848 | /* | ||
| 3849 | * Do a path search. The variable path (passed by reference) should be | ||
| 3850 | * set to the start of the path before the first call; padvance will update | ||
| 3851 | * this value as it proceeds. Successive calls to padvance will return | ||
| 3852 | * the possible path expansions in sequence. If an option (indicated by | ||
| 3853 | * a percent sign) appears in the path entry then the global variable | ||
| 3854 | * pathopt will be set to point to it; otherwise pathopt will be set to | ||
| 3855 | * NULL. | ||
| 3856 | */ | ||
| 3857 | static char * | ||
| 3858 | padvance(const char **path, const char *name) | ||
| 3859 | { | ||
| 3860 | const char *p; | ||
| 3861 | char *q; | ||
| 3862 | const char *start; | ||
| 3863 | size_t len; | ||
| 3864 | |||
| 3865 | if (*path == NULL) | ||
| 3866 | return NULL; | ||
| 3867 | start = *path; | ||
| 3868 | for (p = start; *p && *p != ':' && *p != '%'; p++); | ||
| 3869 | len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */ | ||
| 3870 | while (stackblocksize() < len) | ||
| 3871 | growstackblock(); | ||
| 3872 | q = stackblock(); | ||
| 3873 | if (p != start) { | ||
| 3874 | memcpy(q, start, p - start); | ||
| 3875 | q += p - start; | ||
| 3876 | *q++ = '/'; | ||
| 3877 | } | ||
| 3878 | strcpy(q, name); | ||
| 3879 | pathopt = NULL; | ||
| 3880 | if (*p == '%') { | ||
| 3881 | pathopt = ++p; | ||
| 3882 | while (*p && *p != ':') p++; | ||
| 3883 | } | ||
| 3884 | if (*p == ':') | ||
| 3885 | *path = p + 1; | ||
| 3886 | else | ||
| 3887 | *path = NULL; | ||
| 3888 | return stalloc(len); | ||
| 3889 | } | ||
| 3890 | |||
| 3891 | |||
| 3892 | /*** Command hashing code ***/ | 4407 | /*** Command hashing code ***/ |
| 3893 | 4408 | ||
| 3894 | static void | 4409 | static void |
| @@ -5652,7 +6167,7 @@ addfname(const char *name) | |||
| 5652 | struct strlist *sp; | 6167 | struct strlist *sp; |
| 5653 | 6168 | ||
| 5654 | sp = stalloc(sizeof(*sp)); | 6169 | sp = stalloc(sizeof(*sp)); |
| 5655 | sp->text = sstrdup(name); | 6170 | sp->text = ststrdup(name); |
| 5656 | *exparg.lastp = sp; | 6171 | *exparg.lastp = sp; |
| 5657 | exparg.lastp = &sp->next; | 6172 | exparg.lastp = &sp->next; |
| 5658 | } | 6173 | } |
| @@ -5987,8 +6502,7 @@ static void pushfile(void); | |||
| 5987 | * Read a character from the script, returning PEOF on end of file. | 6502 | * Read a character from the script, returning PEOF on end of file. |
| 5988 | * Nul characters in the input are silently discarded. | 6503 | * Nul characters in the input are silently discarded. |
| 5989 | */ | 6504 | */ |
| 5990 | 6505 | static int preadbuffer(void); | |
| 5991 | |||
| 5992 | #define pgetc_as_macro() (--parsenleft >= 0? SC2INT(*parsenextc++) : preadbuffer()) | 6506 | #define pgetc_as_macro() (--parsenleft >= 0? SC2INT(*parsenextc++) : preadbuffer()) |
| 5993 | 6507 | ||
| 5994 | #if ENABLE_ASH_OPTIMIZE_FOR_SIZE | 6508 | #if ENABLE_ASH_OPTIMIZE_FOR_SIZE |
| @@ -6007,12 +6521,12 @@ pgetc(void) | |||
| 6007 | } | 6521 | } |
| 6008 | #endif | 6522 | #endif |
| 6009 | 6523 | ||
| 6010 | |||
| 6011 | /* | 6524 | /* |
| 6012 | * Same as pgetc(), but ignores PEOA. | 6525 | * Same as pgetc(), but ignores PEOA. |
| 6013 | */ | 6526 | */ |
| 6014 | #if ENABLE_ASH_ALIAS | 6527 | #if ENABLE_ASH_ALIAS |
| 6015 | static int pgetc2(void) | 6528 | static int |
| 6529 | pgetc2(void) | ||
| 6016 | { | 6530 | { |
| 6017 | int c; | 6531 | int c; |
| 6018 | 6532 | ||
| @@ -6022,7 +6536,8 @@ static int pgetc2(void) | |||
| 6022 | return c; | 6536 | return c; |
| 6023 | } | 6537 | } |
| 6024 | #else | 6538 | #else |
| 6025 | static int pgetc2(void) | 6539 | static int |
| 6540 | pgetc2(void) | ||
| 6026 | { | 6541 | { |
| 6027 | return pgetc_macro(); | 6542 | return pgetc_macro(); |
| 6028 | } | 6543 | } |
| @@ -6031,8 +6546,8 @@ static int pgetc2(void) | |||
| 6031 | /* | 6546 | /* |
| 6032 | * Read a line from the script. | 6547 | * Read a line from the script. |
| 6033 | */ | 6548 | */ |
| 6034 | 6549 | static char * | |
| 6035 | static char * pfgets(char *line, int len) | 6550 | pfgets(char *line, int len) |
| 6036 | { | 6551 | { |
| 6037 | char *p = line; | 6552 | char *p = line; |
| 6038 | int nleft = len; | 6553 | int nleft = len; |
| @@ -6053,27 +6568,6 @@ static char * pfgets(char *line, int len) | |||
| 6053 | return line; | 6568 | return line; |
| 6054 | } | 6569 | } |
| 6055 | 6570 | ||
| 6056 | |||
| 6057 | #if ENABLE_FEATURE_EDITING | ||
| 6058 | static line_input_t *line_input_state; | ||
| 6059 | //static SKIP_ASH_EXPAND_PRMT(const) char *cmdedit_prompt; | ||
| 6060 | static const char *cmdedit_prompt; | ||
| 6061 | static void putprompt(const char *s) | ||
| 6062 | { | ||
| 6063 | if (ENABLE_ASH_EXPAND_PRMT) { | ||
| 6064 | free((char*)cmdedit_prompt); | ||
| 6065 | cmdedit_prompt = xstrdup(s); | ||
| 6066 | return; | ||
| 6067 | } | ||
| 6068 | cmdedit_prompt = s; | ||
| 6069 | } | ||
| 6070 | #else | ||
| 6071 | static void putprompt(const char *s) | ||
| 6072 | { | ||
| 6073 | out2str(s); | ||
| 6074 | } | ||
| 6075 | #endif | ||
| 6076 | |||
| 6077 | #if ENABLE_FEATURE_EDITING_VI | 6571 | #if ENABLE_FEATURE_EDITING_VI |
| 6078 | #define setvimode(on) do { \ | 6572 | #define setvimode(on) do { \ |
| 6079 | if (on) line_input_state->flags |= VI_MODE; \ | 6573 | if (on) line_input_state->flags |= VI_MODE; \ |
| @@ -6083,8 +6577,8 @@ static void putprompt(const char *s) | |||
| 6083 | #define setvimode(on) viflag = 0 /* forcibly keep the option off */ | 6577 | #define setvimode(on) viflag = 0 /* forcibly keep the option off */ |
| 6084 | #endif | 6578 | #endif |
| 6085 | 6579 | ||
| 6086 | 6580 | static int | |
| 6087 | static int preadfd(void) | 6581 | preadfd(void) |
| 6088 | { | 6582 | { |
| 6089 | int nr; | 6583 | int nr; |
| 6090 | char *buf = parsefile->buf; | 6584 | char *buf = parsefile->buf; |
| @@ -7913,113 +8407,6 @@ find_dot_file(char *name) | |||
| 7913 | /* NOTREACHED */ | 8407 | /* NOTREACHED */ |
| 7914 | } | 8408 | } |
| 7915 | 8409 | ||
| 7916 | /* mystring.c */ | ||
| 7917 | |||
| 7918 | /* | ||
| 7919 | * String functions. | ||
| 7920 | * | ||
| 7921 | * number(s) Convert a string of digits to an integer. | ||
| 7922 | * is_number(s) Return true if s is a string of digits. | ||
| 7923 | */ | ||
| 7924 | |||
| 7925 | /* | ||
| 7926 | * prefix -- see if pfx is a prefix of string. | ||
| 7927 | */ | ||
| 7928 | static char * | ||
| 7929 | prefix(const char *string, const char *pfx) | ||
| 7930 | { | ||
| 7931 | while (*pfx) { | ||
| 7932 | if (*pfx++ != *string++) | ||
| 7933 | return 0; | ||
| 7934 | } | ||
| 7935 | return (char *) string; | ||
| 7936 | } | ||
| 7937 | |||
| 7938 | |||
| 7939 | /* | ||
| 7940 | * Convert a string of digits to an integer, printing an error message on | ||
| 7941 | * failure. | ||
| 7942 | */ | ||
| 7943 | static int | ||
| 7944 | number(const char *s) | ||
| 7945 | { | ||
| 7946 | if (!is_number(s)) | ||
| 7947 | ash_msg_and_raise_error(illnum, s); | ||
| 7948 | return atoi(s); | ||
| 7949 | } | ||
| 7950 | |||
| 7951 | |||
| 7952 | /* | ||
| 7953 | * Check for a valid number. This should be elsewhere. | ||
| 7954 | */ | ||
| 7955 | static int | ||
| 7956 | is_number(const char *p) | ||
| 7957 | { | ||
| 7958 | do { | ||
| 7959 | if (!is_digit(*p)) | ||
| 7960 | return 0; | ||
| 7961 | } while (*++p != '\0'); | ||
| 7962 | return 1; | ||
| 7963 | } | ||
| 7964 | |||
| 7965 | |||
| 7966 | /* | ||
| 7967 | * Produce a possibly single quoted string suitable as input to the shell. | ||
| 7968 | * The return string is allocated on the stack. | ||
| 7969 | */ | ||
| 7970 | static char * | ||
| 7971 | single_quote(const char *s) | ||
| 7972 | { | ||
| 7973 | char *p; | ||
| 7974 | |||
| 7975 | STARTSTACKSTR(p); | ||
| 7976 | |||
| 7977 | do { | ||
| 7978 | char *q; | ||
| 7979 | size_t len; | ||
| 7980 | |||
| 7981 | len = strchrnul(s, '\'') - s; | ||
| 7982 | |||
| 7983 | q = p = makestrspace(len + 3, p); | ||
| 7984 | |||
| 7985 | *q++ = '\''; | ||
| 7986 | q = memcpy(q, s, len) + len; | ||
| 7987 | *q++ = '\''; | ||
| 7988 | s += len; | ||
| 7989 | |||
| 7990 | STADJUST(q - p, p); | ||
| 7991 | |||
| 7992 | len = strspn(s, "'"); | ||
| 7993 | if (!len) | ||
| 7994 | break; | ||
| 7995 | |||
| 7996 | q = p = makestrspace(len + 3, p); | ||
| 7997 | |||
| 7998 | *q++ = '"'; | ||
| 7999 | q = memcpy(q, s, len) + len; | ||
| 8000 | *q++ = '"'; | ||
| 8001 | s += len; | ||
| 8002 | |||
| 8003 | STADJUST(q - p, p); | ||
| 8004 | } while (*s); | ||
| 8005 | |||
| 8006 | USTPUTC(0, p); | ||
| 8007 | |||
| 8008 | return stackblock(); | ||
| 8009 | } | ||
| 8010 | |||
| 8011 | |||
| 8012 | /* | ||
| 8013 | * Like strdup but works with the ash stack. | ||
| 8014 | */ | ||
| 8015 | static char * | ||
| 8016 | sstrdup(const char *p) | ||
| 8017 | { | ||
| 8018 | size_t len = strlen(p) + 1; | ||
| 8019 | return memcpy(stalloc(len), p, len); | ||
| 8020 | } | ||
| 8021 | |||
| 8022 | |||
| 8023 | static void | 8410 | static void |
| 8024 | calcsize(union node *n) | 8411 | calcsize(union node *n) |
| 8025 | { | 8412 | { |
| @@ -8241,9 +8628,6 @@ freefunc(struct funcnode *f) | |||
| 8241 | } | 8628 | } |
| 8242 | 8629 | ||
| 8243 | 8630 | ||
| 8244 | static void setoption(int, int); | ||
| 8245 | |||
| 8246 | |||
| 8247 | static void | 8631 | static void |
| 8248 | optschanged(void) | 8632 | optschanged(void) |
| 8249 | { | 8633 | { |
| @@ -8255,7 +8639,8 @@ optschanged(void) | |||
| 8255 | setvimode(viflag); | 8639 | setvimode(viflag); |
| 8256 | } | 8640 | } |
| 8257 | 8641 | ||
| 8258 | static void minus_o(char *name, int val) | 8642 | static void |
| 8643 | minus_o(char *name, int val) | ||
| 8259 | { | 8644 | { |
| 8260 | int i; | 8645 | int i; |
| 8261 | 8646 | ||
| @@ -8275,6 +8660,22 @@ static void minus_o(char *name, int val) | |||
| 8275 | } | 8660 | } |
| 8276 | 8661 | ||
| 8277 | 8662 | ||
| 8663 | static void | ||
| 8664 | setoption(int flag, int val) | ||
| 8665 | { | ||
| 8666 | int i; | ||
| 8667 | |||
| 8668 | for (i = 0; i < NOPTS; i++) { | ||
| 8669 | if (optletters(i) == flag) { | ||
| 8670 | optlist[i] = val; | ||
| 8671 | return; | ||
| 8672 | } | ||
| 8673 | } | ||
| 8674 | ash_msg_and_raise_error("Illegal option -%c", flag); | ||
| 8675 | /* NOTREACHED */ | ||
| 8676 | } | ||
| 8677 | |||
| 8678 | |||
| 8278 | /* | 8679 | /* |
| 8279 | * Process shell options. The global variable argptr contains a pointer | 8680 | * Process shell options. The global variable argptr contains a pointer |
| 8280 | * to the argument list; we advance it past the options. | 8681 | * to the argument list; we advance it past the options. |
| @@ -8329,22 +8730,6 @@ options(int cmdline) | |||
| 8329 | } | 8730 | } |
| 8330 | 8731 | ||
| 8331 | 8732 | ||
| 8332 | static void | ||
| 8333 | setoption(int flag, int val) | ||
| 8334 | { | ||
| 8335 | int i; | ||
| 8336 | |||
| 8337 | for (i = 0; i < NOPTS; i++) { | ||
| 8338 | if (optletters(i) == flag) { | ||
| 8339 | optlist[i] = val; | ||
| 8340 | return; | ||
| 8341 | } | ||
| 8342 | } | ||
| 8343 | ash_msg_and_raise_error("Illegal option -%c", flag); | ||
| 8344 | /* NOTREACHED */ | ||
| 8345 | } | ||
| 8346 | |||
| 8347 | |||
| 8348 | /* | 8733 | /* |
| 8349 | * Set the shell parameters. | 8734 | * Set the shell parameters. |
| 8350 | */ | 8735 | */ |
| @@ -8420,6 +8805,37 @@ shiftcmd(int argc, char **argv) | |||
| 8420 | 8805 | ||
| 8421 | 8806 | ||
| 8422 | /* | 8807 | /* |
| 8808 | * POSIX requires that 'set' (but not export or readonly) output the | ||
| 8809 | * variables in lexicographic order - by the locale's collating order (sigh). | ||
| 8810 | * Maybe we could keep them in an ordered balanced binary tree | ||
| 8811 | * instead of hashed lists. | ||
| 8812 | * For now just roll 'em through qsort for printing... | ||
| 8813 | */ | ||
| 8814 | static int | ||
| 8815 | showvars(const char *sep_prefix, int on, int off) | ||
| 8816 | { | ||
| 8817 | const char *sep; | ||
| 8818 | char **ep, **epend; | ||
| 8819 | |||
| 8820 | ep = listvars(on, off, &epend); | ||
| 8821 | qsort(ep, epend - ep, sizeof(char *), vpcmp); | ||
| 8822 | |||
| 8823 | sep = *sep_prefix ? spcstr : sep_prefix; | ||
| 8824 | |||
| 8825 | for (; ep < epend; ep++) { | ||
| 8826 | const char *p; | ||
| 8827 | const char *q; | ||
| 8828 | |||
| 8829 | p = strchrnul(*ep, '='); | ||
| 8830 | q = nullstr; | ||
| 8831 | if (*p) | ||
| 8832 | q = single_quote(++p); | ||
| 8833 | out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q); | ||
| 8834 | } | ||
| 8835 | return 0; | ||
| 8836 | } | ||
| 8837 | |||
| 8838 | /* | ||
| 8423 | * The set command builtin. | 8839 | * The set command builtin. |
| 8424 | */ | 8840 | */ |
| 8425 | static int | 8841 | static int |
| @@ -8438,15 +8854,6 @@ setcmd(int argc, char **argv) | |||
| 8438 | } | 8854 | } |
| 8439 | 8855 | ||
| 8440 | 8856 | ||
| 8441 | #if ENABLE_ASH_GETOPTS | ||
| 8442 | static void | ||
| 8443 | getoptsreset(const char *value) | ||
| 8444 | { | ||
| 8445 | shellparam.optind = number(value); | ||
| 8446 | shellparam.optoff = -1; | ||
| 8447 | } | ||
| 8448 | #endif | ||
| 8449 | |||
| 8450 | #if ENABLE_LOCALE_SUPPORT | 8857 | #if ENABLE_LOCALE_SUPPORT |
| 8451 | static void change_lc_all(const char *value) | 8858 | static void change_lc_all(const char *value) |
| 8452 | { | 8859 | { |
| @@ -8459,7 +8866,6 @@ static void change_lc_ctype(const char *value) | |||
| 8459 | if (value && *value != '\0') | 8866 | if (value && *value != '\0') |
| 8460 | setlocale(LC_CTYPE, value); | 8867 | setlocale(LC_CTYPE, value); |
| 8461 | } | 8868 | } |
| 8462 | |||
| 8463 | #endif | 8869 | #endif |
| 8464 | 8870 | ||
| 8465 | #if ENABLE_ASH_RANDOM_SUPPORT | 8871 | #if ENABLE_ASH_RANDOM_SUPPORT |
| @@ -8525,7 +8931,7 @@ getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *opt | |||
| 8525 | err |= setvarsafe("OPTARG", s, 0); | 8931 | err |= setvarsafe("OPTARG", s, 0); |
| 8526 | } else { | 8932 | } else { |
| 8527 | fprintf(stderr, "Illegal option -%c\n", c); | 8933 | fprintf(stderr, "Illegal option -%c\n", c); |
| 8528 | (void) unsetvar("OPTARG"); | 8934 | unsetvar("OPTARG"); |
| 8529 | } | 8935 | } |
| 8530 | c = '?'; | 8936 | c = '?'; |
| 8531 | goto out; | 8937 | goto out; |
| @@ -8543,7 +8949,7 @@ getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *opt | |||
| 8543 | c = ':'; | 8949 | c = ':'; |
| 8544 | } else { | 8950 | } else { |
| 8545 | fprintf(stderr, "No arg for -%c option\n", c); | 8951 | fprintf(stderr, "No arg for -%c option\n", c); |
| 8546 | (void) unsetvar("OPTARG"); | 8952 | unsetvar("OPTARG"); |
| 8547 | c = '?'; | 8953 | c = '?'; |
| 8548 | } | 8954 | } |
| 8549 | goto out; | 8955 | goto out; |
| @@ -8586,7 +8992,7 @@ getoptscmd(int argc, char **argv) | |||
| 8586 | 8992 | ||
| 8587 | if (argc < 3) | 8993 | if (argc < 3) |
| 8588 | ash_msg_and_raise_error("Usage: getopts optstring var [arg]"); | 8994 | ash_msg_and_raise_error("Usage: getopts optstring var [arg]"); |
| 8589 | else if (argc == 3) { | 8995 | if (argc == 3) { |
| 8590 | optbase = shellparam.p; | 8996 | optbase = shellparam.p; |
| 8591 | if (shellparam.optind > shellparam.nparam + 1) { | 8997 | if (shellparam.optind > shellparam.nparam + 1) { |
| 8592 | shellparam.optind = 1; | 8998 | shellparam.optind = 1; |
| @@ -8605,82 +9011,8 @@ getoptscmd(int argc, char **argv) | |||
| 8605 | } | 9011 | } |
| 8606 | #endif /* ASH_GETOPTS */ | 9012 | #endif /* ASH_GETOPTS */ |
| 8607 | 9013 | ||
| 8608 | /* | ||
| 8609 | * XXX - should get rid of. have all builtins use getopt(3). the | ||
| 8610 | * library getopt must have the BSD extension static variable "optreset" | ||
| 8611 | * otherwise it can't be used within the shell safely. | ||
| 8612 | * | ||
| 8613 | * Standard option processing (a la getopt) for builtin routines. The | ||
| 8614 | * only argument that is passed to nextopt is the option string; the | ||
| 8615 | * other arguments are unnecessary. It return the character, or '\0' on | ||
| 8616 | * end of input. | ||
| 8617 | */ | ||
| 8618 | static int | ||
| 8619 | nextopt(const char *optstring) | ||
| 8620 | { | ||
| 8621 | char *p; | ||
| 8622 | const char *q; | ||
| 8623 | char c; | ||
| 8624 | 9014 | ||
| 8625 | p = optptr; | 9015 | /* ============ Shell parser */ |
| 8626 | if (p == NULL || *p == '\0') { | ||
| 8627 | p = *argptr; | ||
| 8628 | if (p == NULL || *p != '-' || *++p == '\0') | ||
| 8629 | return '\0'; | ||
| 8630 | argptr++; | ||
| 8631 | if (LONE_DASH(p)) /* check for "--" */ | ||
| 8632 | return '\0'; | ||
| 8633 | } | ||
| 8634 | c = *p++; | ||
| 8635 | for (q = optstring; *q != c; ) { | ||
| 8636 | if (*q == '\0') | ||
| 8637 | ash_msg_and_raise_error("Illegal option -%c", c); | ||
| 8638 | if (*++q == ':') | ||
| 8639 | q++; | ||
| 8640 | } | ||
| 8641 | if (*++q == ':') { | ||
| 8642 | if (*p == '\0' && (p = *argptr++) == NULL) | ||
| 8643 | ash_msg_and_raise_error("No arg for -%c option", c); | ||
| 8644 | optionarg = p; | ||
| 8645 | p = NULL; | ||
| 8646 | } | ||
| 8647 | optptr = p; | ||
| 8648 | return c; | ||
| 8649 | } | ||
| 8650 | |||
| 8651 | |||
| 8652 | /* parser.c */ | ||
| 8653 | |||
| 8654 | |||
| 8655 | /* | ||
| 8656 | * Shell command parser. | ||
| 8657 | */ | ||
| 8658 | |||
| 8659 | #define EOFMARKLEN 79 | ||
| 8660 | |||
| 8661 | struct heredoc { | ||
| 8662 | struct heredoc *next; /* next here document in list */ | ||
| 8663 | union node *here; /* redirection node */ | ||
| 8664 | char *eofmark; /* string indicating end of input */ | ||
| 8665 | int striptabs; /* if set, strip leading tabs */ | ||
| 8666 | }; | ||
| 8667 | |||
| 8668 | static struct heredoc *heredoclist; /* list of here documents to read */ | ||
| 8669 | |||
| 8670 | static union node *list(int); | ||
| 8671 | static union node *andor(void); | ||
| 8672 | static union node *pipeline(void); | ||
| 8673 | static union node *command(void); | ||
| 8674 | static union node *simplecmd(void); | ||
| 8675 | static union node *makename(void); | ||
| 8676 | static void parsefname(void); | ||
| 8677 | static void parseheredoc(void); | ||
| 8678 | static char peektoken(void); | ||
| 8679 | static int readtoken(void); | ||
| 8680 | static int xxreadtoken(void); | ||
| 8681 | static int readtoken1(int firstc, int syntax, char *eofmark, int striptabs); | ||
| 8682 | static int noexpand(char *); | ||
| 8683 | static void setprompt(int); | ||
| 8684 | 9016 | ||
| 8685 | static void raise_error_syntax(const char *) ATTRIBUTE_NORETURN; | 9017 | static void raise_error_syntax(const char *) ATTRIBUTE_NORETURN; |
| 8686 | static void | 9018 | static void |
| @@ -8709,29 +9041,24 @@ raise_error_unexpected_syntax(int token) | |||
| 8709 | /* NOTREACHED */ | 9041 | /* NOTREACHED */ |
| 8710 | } | 9042 | } |
| 8711 | 9043 | ||
| 8712 | /* | 9044 | #define EOFMARKLEN 79 |
| 8713 | * Read and parse a command. Returns NEOF on end of file. (NULL is a | ||
| 8714 | * valid parse tree indicating a blank line.) | ||
| 8715 | */ | ||
| 8716 | static union node * | ||
| 8717 | parsecmd(int interact) | ||
| 8718 | { | ||
| 8719 | int t; | ||
| 8720 | 9045 | ||
| 8721 | tokpushback = 0; | 9046 | struct heredoc { |
| 8722 | doprompt = interact; | 9047 | struct heredoc *next; /* next here document in list */ |
| 8723 | if (doprompt) | 9048 | union node *here; /* redirection node */ |
| 8724 | setprompt(doprompt); | 9049 | char *eofmark; /* string indicating end of input */ |
| 8725 | needprompt = 0; | 9050 | int striptabs; /* if set, strip leading tabs */ |
| 8726 | t = readtoken(); | 9051 | }; |
| 8727 | if (t == TEOF) | ||
| 8728 | return NEOF; | ||
| 8729 | if (t == TNL) | ||
| 8730 | return NULL; | ||
| 8731 | tokpushback++; | ||
| 8732 | return list(1); | ||
| 8733 | } | ||
| 8734 | 9052 | ||
| 9053 | static struct heredoc *heredoclist; /* list of here documents to read */ | ||
| 9054 | |||
| 9055 | /* parsing is heavily cross-recursive, need these forward decls */ | ||
| 9056 | static union node *andor(void); | ||
| 9057 | static union node *pipeline(void); | ||
| 9058 | static union node *parse_command(void); | ||
| 9059 | static void parseheredoc(void); | ||
| 9060 | static char peektoken(void); | ||
| 9061 | static int readtoken(void); | ||
| 8735 | 9062 | ||
| 8736 | static union node * | 9063 | static union node * |
| 8737 | list(int nlflag) | 9064 | list(int nlflag) |
| @@ -8800,7 +9127,6 @@ list(int nlflag) | |||
| 8800 | } | 9127 | } |
| 8801 | } | 9128 | } |
| 8802 | 9129 | ||
| 8803 | |||
| 8804 | static union node * | 9130 | static union node * |
| 8805 | andor(void) | 9131 | andor(void) |
| 8806 | { | 9132 | { |
| @@ -8828,7 +9154,6 @@ andor(void) | |||
| 8828 | } | 9154 | } |
| 8829 | } | 9155 | } |
| 8830 | 9156 | ||
| 8831 | |||
| 8832 | static union node * | 9157 | static union node * |
| 8833 | pipeline(void) | 9158 | pipeline(void) |
| 8834 | { | 9159 | { |
| @@ -8843,7 +9168,7 @@ pipeline(void) | |||
| 8843 | checkkwd = CHKKWD | CHKALIAS; | 9168 | checkkwd = CHKKWD | CHKALIAS; |
| 8844 | } else | 9169 | } else |
| 8845 | tokpushback++; | 9170 | tokpushback++; |
| 8846 | n1 = command(); | 9171 | n1 = parse_command(); |
| 8847 | if (readtoken() == TPIPE) { | 9172 | if (readtoken() == TPIPE) { |
| 8848 | pipenode = stalloc(sizeof(struct npipe)); | 9173 | pipenode = stalloc(sizeof(struct npipe)); |
| 8849 | pipenode->type = NPIPE; | 9174 | pipenode->type = NPIPE; |
| @@ -8855,7 +9180,7 @@ pipeline(void) | |||
| 8855 | prev = lp; | 9180 | prev = lp; |
| 8856 | lp = stalloc(sizeof(struct nodelist)); | 9181 | lp = stalloc(sizeof(struct nodelist)); |
| 8857 | checkkwd = CHKNL | CHKKWD | CHKALIAS; | 9182 | checkkwd = CHKNL | CHKKWD | CHKALIAS; |
| 8858 | lp->n = command(); | 9183 | lp->n = parse_command(); |
| 8859 | prev->next = lp; | 9184 | prev->next = lp; |
| 8860 | } while (readtoken() == TPIPE); | 9185 | } while (readtoken() == TPIPE); |
| 8861 | lp->next = NULL; | 9186 | lp->next = NULL; |
| @@ -8871,9 +9196,172 @@ pipeline(void) | |||
| 8871 | return n1; | 9196 | return n1; |
| 8872 | } | 9197 | } |
| 8873 | 9198 | ||
| 9199 | static union node * | ||
| 9200 | makename(void) | ||
| 9201 | { | ||
| 9202 | union node *n; | ||
| 9203 | |||
| 9204 | n = stalloc(sizeof(struct narg)); | ||
| 9205 | n->type = NARG; | ||
| 9206 | n->narg.next = NULL; | ||
| 9207 | n->narg.text = wordtext; | ||
| 9208 | n->narg.backquote = backquotelist; | ||
| 9209 | return n; | ||
| 9210 | } | ||
| 9211 | |||
| 9212 | static void | ||
| 9213 | fixredir(union node *n, const char *text, int err) | ||
| 9214 | { | ||
| 9215 | TRACE(("Fix redir %s %d\n", text, err)); | ||
| 9216 | if (!err) | ||
| 9217 | n->ndup.vname = NULL; | ||
| 9218 | |||
| 9219 | if (isdigit(text[0]) && text[1] == '\0') | ||
| 9220 | n->ndup.dupfd = digit_val(text[0]); | ||
| 9221 | else if (LONE_DASH(text)) | ||
| 9222 | n->ndup.dupfd = -1; | ||
| 9223 | else { | ||
| 9224 | if (err) | ||
| 9225 | raise_error_syntax("Bad fd number"); | ||
| 9226 | n->ndup.vname = makename(); | ||
| 9227 | } | ||
| 9228 | } | ||
| 9229 | |||
| 9230 | /* | ||
| 9231 | * Returns true if the text contains nothing to expand (no dollar signs | ||
| 9232 | * or backquotes). | ||
| 9233 | */ | ||
| 9234 | static int | ||
| 9235 | noexpand(char *text) | ||
| 9236 | { | ||
| 9237 | char *p; | ||
| 9238 | char c; | ||
| 9239 | |||
| 9240 | p = text; | ||
| 9241 | while ((c = *p++) != '\0') { | ||
| 9242 | if (c == CTLQUOTEMARK) | ||
| 9243 | continue; | ||
| 9244 | if (c == CTLESC) | ||
| 9245 | p++; | ||
| 9246 | else if (SIT(c, BASESYNTAX) == CCTL) | ||
| 9247 | return 0; | ||
| 9248 | } | ||
| 9249 | return 1; | ||
| 9250 | } | ||
| 9251 | |||
| 9252 | static void | ||
| 9253 | parsefname(void) | ||
| 9254 | { | ||
| 9255 | union node *n = redirnode; | ||
| 9256 | |||
| 9257 | if (readtoken() != TWORD) | ||
| 9258 | raise_error_unexpected_syntax(-1); | ||
| 9259 | if (n->type == NHERE) { | ||
| 9260 | struct heredoc *here = heredoc; | ||
| 9261 | struct heredoc *p; | ||
| 9262 | int i; | ||
| 9263 | |||
| 9264 | if (quoteflag == 0) | ||
| 9265 | n->type = NXHERE; | ||
| 9266 | TRACE(("Here document %d\n", n->type)); | ||
| 9267 | if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN) | ||
| 9268 | raise_error_syntax("Illegal eof marker for << redirection"); | ||
| 9269 | rmescapes(wordtext); | ||
| 9270 | here->eofmark = wordtext; | ||
| 9271 | here->next = NULL; | ||
| 9272 | if (heredoclist == NULL) | ||
| 9273 | heredoclist = here; | ||
| 9274 | else { | ||
| 9275 | for (p = heredoclist; p->next; p = p->next); | ||
| 9276 | p->next = here; | ||
| 9277 | } | ||
| 9278 | } else if (n->type == NTOFD || n->type == NFROMFD) { | ||
| 9279 | fixredir(n, wordtext, 0); | ||
| 9280 | } else { | ||
| 9281 | n->nfile.fname = makename(); | ||
| 9282 | } | ||
| 9283 | } | ||
| 9284 | |||
| 9285 | static union node * | ||
| 9286 | simplecmd(void) | ||
| 9287 | { | ||
| 9288 | union node *args, **app; | ||
| 9289 | union node *n = NULL; | ||
| 9290 | union node *vars, **vpp; | ||
| 9291 | union node **rpp, *redir; | ||
| 9292 | int savecheckkwd; | ||
| 9293 | |||
| 9294 | args = NULL; | ||
| 9295 | app = &args; | ||
| 9296 | vars = NULL; | ||
| 9297 | vpp = &vars; | ||
| 9298 | redir = NULL; | ||
| 9299 | rpp = &redir; | ||
| 9300 | |||
| 9301 | savecheckkwd = CHKALIAS; | ||
| 9302 | for (;;) { | ||
| 9303 | checkkwd = savecheckkwd; | ||
| 9304 | switch (readtoken()) { | ||
| 9305 | case TWORD: | ||
| 9306 | n = stalloc(sizeof(struct narg)); | ||
| 9307 | n->type = NARG; | ||
| 9308 | n->narg.text = wordtext; | ||
| 9309 | n->narg.backquote = backquotelist; | ||
| 9310 | if (savecheckkwd && isassignment(wordtext)) { | ||
| 9311 | *vpp = n; | ||
| 9312 | vpp = &n->narg.next; | ||
| 9313 | } else { | ||
| 9314 | *app = n; | ||
| 9315 | app = &n->narg.next; | ||
| 9316 | savecheckkwd = 0; | ||
| 9317 | } | ||
| 9318 | break; | ||
| 9319 | case TREDIR: | ||
| 9320 | *rpp = n = redirnode; | ||
| 9321 | rpp = &n->nfile.next; | ||
| 9322 | parsefname(); /* read name of redirection file */ | ||
| 9323 | break; | ||
| 9324 | case TLP: | ||
| 9325 | if (args && app == &args->narg.next | ||
| 9326 | && !vars && !redir | ||
| 9327 | ) { | ||
| 9328 | struct builtincmd *bcmd; | ||
| 9329 | const char *name; | ||
| 9330 | |||
| 9331 | /* We have a function */ | ||
| 9332 | if (readtoken() != TRP) | ||
| 9333 | raise_error_unexpected_syntax(TRP); | ||
| 9334 | name = n->narg.text; | ||
| 9335 | if (!goodname(name) | ||
| 9336 | || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd)) | ||
| 9337 | ) { | ||
| 9338 | raise_error_syntax("Bad function name"); | ||
| 9339 | } | ||
| 9340 | n->type = NDEFUN; | ||
| 9341 | checkkwd = CHKNL | CHKKWD | CHKALIAS; | ||
| 9342 | n->narg.next = parse_command(); | ||
| 9343 | return n; | ||
| 9344 | } | ||
| 9345 | /* fall through */ | ||
| 9346 | default: | ||
| 9347 | tokpushback++; | ||
| 9348 | goto out; | ||
| 9349 | } | ||
| 9350 | } | ||
| 9351 | out: | ||
| 9352 | *app = NULL; | ||
| 9353 | *vpp = NULL; | ||
| 9354 | *rpp = NULL; | ||
| 9355 | n = stalloc(sizeof(struct ncmd)); | ||
| 9356 | n->type = NCMD; | ||
| 9357 | n->ncmd.args = args; | ||
| 9358 | n->ncmd.assign = vars; | ||
| 9359 | n->ncmd.redirect = redir; | ||
| 9360 | return n; | ||
| 9361 | } | ||
| 8874 | 9362 | ||
| 8875 | static union node * | 9363 | static union node * |
| 8876 | command(void) | 9364 | parse_command(void) |
| 8877 | { | 9365 | { |
| 8878 | union node *n1, *n2; | 9366 | union node *n1, *n2; |
| 8879 | union node *ap, **app; | 9367 | union node *ap, **app; |
| @@ -8918,9 +9406,10 @@ command(void) | |||
| 8918 | case TUNTIL: { | 9406 | case TUNTIL: { |
| 8919 | int got; | 9407 | int got; |
| 8920 | n1 = stalloc(sizeof(struct nbinary)); | 9408 | n1 = stalloc(sizeof(struct nbinary)); |
| 8921 | n1->type = (lasttoken == TWHILE)? NWHILE : NUNTIL; | 9409 | n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL; |
| 8922 | n1->nbinary.ch1 = list(0); | 9410 | n1->nbinary.ch1 = list(0); |
| 8923 | if ((got=readtoken()) != TDO) { | 9411 | got = readtoken(); |
| 9412 | if (got != TDO) { | ||
| 8924 | TRACE(("expecting DO got %s %s\n", tokname(got), | 9413 | TRACE(("expecting DO got %s %s\n", tokname(got), |
| 8925 | got == TWORD ? wordtext : "")); | 9414 | got == TWORD ? wordtext : "")); |
| 8926 | raise_error_unexpected_syntax(TDO); | 9415 | raise_error_unexpected_syntax(TDO); |
| @@ -9065,421 +9554,6 @@ command(void) | |||
| 9065 | return n1; | 9554 | return n1; |
| 9066 | } | 9555 | } |
| 9067 | 9556 | ||
| 9068 | |||
| 9069 | static union node * | ||
| 9070 | simplecmd(void) | ||
| 9071 | { | ||
| 9072 | union node *args, **app; | ||
| 9073 | union node *n = NULL; | ||
| 9074 | union node *vars, **vpp; | ||
| 9075 | union node **rpp, *redir; | ||
| 9076 | int savecheckkwd; | ||
| 9077 | |||
| 9078 | args = NULL; | ||
| 9079 | app = &args; | ||
| 9080 | vars = NULL; | ||
| 9081 | vpp = &vars; | ||
| 9082 | redir = NULL; | ||
| 9083 | rpp = &redir; | ||
| 9084 | |||
| 9085 | savecheckkwd = CHKALIAS; | ||
| 9086 | for (;;) { | ||
| 9087 | checkkwd = savecheckkwd; | ||
| 9088 | switch (readtoken()) { | ||
| 9089 | case TWORD: | ||
| 9090 | n = stalloc(sizeof(struct narg)); | ||
| 9091 | n->type = NARG; | ||
| 9092 | n->narg.text = wordtext; | ||
| 9093 | n->narg.backquote = backquotelist; | ||
| 9094 | if (savecheckkwd && isassignment(wordtext)) { | ||
| 9095 | *vpp = n; | ||
| 9096 | vpp = &n->narg.next; | ||
| 9097 | } else { | ||
| 9098 | *app = n; | ||
| 9099 | app = &n->narg.next; | ||
| 9100 | savecheckkwd = 0; | ||
| 9101 | } | ||
| 9102 | break; | ||
| 9103 | case TREDIR: | ||
| 9104 | *rpp = n = redirnode; | ||
| 9105 | rpp = &n->nfile.next; | ||
| 9106 | parsefname(); /* read name of redirection file */ | ||
| 9107 | break; | ||
| 9108 | case TLP: | ||
| 9109 | if (args && app == &args->narg.next | ||
| 9110 | && !vars && !redir | ||
| 9111 | ) { | ||
| 9112 | struct builtincmd *bcmd; | ||
| 9113 | const char *name; | ||
| 9114 | |||
| 9115 | /* We have a function */ | ||
| 9116 | if (readtoken() != TRP) | ||
| 9117 | raise_error_unexpected_syntax(TRP); | ||
| 9118 | name = n->narg.text; | ||
| 9119 | if (!goodname(name) | ||
| 9120 | || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd)) | ||
| 9121 | ) { | ||
| 9122 | raise_error_syntax("Bad function name"); | ||
| 9123 | } | ||
| 9124 | n->type = NDEFUN; | ||
| 9125 | checkkwd = CHKNL | CHKKWD | CHKALIAS; | ||
| 9126 | n->narg.next = command(); | ||
| 9127 | return n; | ||
| 9128 | } | ||
| 9129 | /* fall through */ | ||
| 9130 | default: | ||
| 9131 | tokpushback++; | ||
| 9132 | goto out; | ||
| 9133 | } | ||
| 9134 | } | ||
| 9135 | out: | ||
| 9136 | *app = NULL; | ||
| 9137 | *vpp = NULL; | ||
| 9138 | *rpp = NULL; | ||
| 9139 | n = stalloc(sizeof(struct ncmd)); | ||
| 9140 | n->type = NCMD; | ||
| 9141 | n->ncmd.args = args; | ||
| 9142 | n->ncmd.assign = vars; | ||
| 9143 | n->ncmd.redirect = redir; | ||
| 9144 | return n; | ||
| 9145 | } | ||
| 9146 | |||
| 9147 | static union node * | ||
| 9148 | makename(void) | ||
| 9149 | { | ||
| 9150 | union node *n; | ||
| 9151 | |||
| 9152 | n = stalloc(sizeof(struct narg)); | ||
| 9153 | n->type = NARG; | ||
| 9154 | n->narg.next = NULL; | ||
| 9155 | n->narg.text = wordtext; | ||
| 9156 | n->narg.backquote = backquotelist; | ||
| 9157 | return n; | ||
| 9158 | } | ||
| 9159 | |||
| 9160 | static void | ||
| 9161 | fixredir(union node *n, const char *text, int err) | ||
| 9162 | { | ||
| 9163 | TRACE(("Fix redir %s %d\n", text, err)); | ||
| 9164 | if (!err) | ||
| 9165 | n->ndup.vname = NULL; | ||
| 9166 | |||
| 9167 | if (is_digit(text[0]) && text[1] == '\0') | ||
| 9168 | n->ndup.dupfd = digit_val(text[0]); | ||
| 9169 | else if (LONE_DASH(text)) | ||
| 9170 | n->ndup.dupfd = -1; | ||
| 9171 | else { | ||
| 9172 | if (err) | ||
| 9173 | raise_error_syntax("Bad fd number"); | ||
| 9174 | n->ndup.vname = makename(); | ||
| 9175 | } | ||
| 9176 | } | ||
| 9177 | |||
| 9178 | |||
| 9179 | static void | ||
| 9180 | parsefname(void) | ||
| 9181 | { | ||
| 9182 | union node *n = redirnode; | ||
| 9183 | |||
| 9184 | if (readtoken() != TWORD) | ||
| 9185 | raise_error_unexpected_syntax(-1); | ||
| 9186 | if (n->type == NHERE) { | ||
| 9187 | struct heredoc *here = heredoc; | ||
| 9188 | struct heredoc *p; | ||
| 9189 | int i; | ||
| 9190 | |||
| 9191 | if (quoteflag == 0) | ||
| 9192 | n->type = NXHERE; | ||
| 9193 | TRACE(("Here document %d\n", n->type)); | ||
| 9194 | if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN) | ||
| 9195 | raise_error_syntax("Illegal eof marker for << redirection"); | ||
| 9196 | rmescapes(wordtext); | ||
| 9197 | here->eofmark = wordtext; | ||
| 9198 | here->next = NULL; | ||
| 9199 | if (heredoclist == NULL) | ||
| 9200 | heredoclist = here; | ||
| 9201 | else { | ||
| 9202 | for (p = heredoclist; p->next; p = p->next); | ||
| 9203 | p->next = here; | ||
| 9204 | } | ||
| 9205 | } else if (n->type == NTOFD || n->type == NFROMFD) { | ||
| 9206 | fixredir(n, wordtext, 0); | ||
| 9207 | } else { | ||
| 9208 | n->nfile.fname = makename(); | ||
| 9209 | } | ||
| 9210 | } | ||
| 9211 | |||
| 9212 | |||
| 9213 | /* | ||
| 9214 | * Input any here documents. | ||
| 9215 | */ | ||
| 9216 | static void | ||
| 9217 | parseheredoc(void) | ||
| 9218 | { | ||
| 9219 | struct heredoc *here; | ||
| 9220 | union node *n; | ||
| 9221 | |||
| 9222 | here = heredoclist; | ||
| 9223 | heredoclist = 0; | ||
| 9224 | |||
| 9225 | while (here) { | ||
| 9226 | if (needprompt) { | ||
| 9227 | setprompt(2); | ||
| 9228 | } | ||
| 9229 | readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX, | ||
| 9230 | here->eofmark, here->striptabs); | ||
| 9231 | n = stalloc(sizeof(struct narg)); | ||
| 9232 | n->narg.type = NARG; | ||
| 9233 | n->narg.next = NULL; | ||
| 9234 | n->narg.text = wordtext; | ||
| 9235 | n->narg.backquote = backquotelist; | ||
| 9236 | here->here->nhere.doc = n; | ||
| 9237 | here = here->next; | ||
| 9238 | } | ||
| 9239 | } | ||
| 9240 | |||
| 9241 | static char | ||
| 9242 | peektoken(void) | ||
| 9243 | { | ||
| 9244 | int t; | ||
| 9245 | |||
| 9246 | t = readtoken(); | ||
| 9247 | tokpushback++; | ||
| 9248 | return tokname_array[t][0]; | ||
| 9249 | } | ||
| 9250 | |||
| 9251 | static int | ||
| 9252 | readtoken(void) | ||
| 9253 | { | ||
| 9254 | int t; | ||
| 9255 | #if DEBUG | ||
| 9256 | int alreadyseen = tokpushback; | ||
| 9257 | #endif | ||
| 9258 | |||
| 9259 | #if ENABLE_ASH_ALIAS | ||
| 9260 | top: | ||
| 9261 | #endif | ||
| 9262 | |||
| 9263 | t = xxreadtoken(); | ||
| 9264 | |||
| 9265 | /* | ||
| 9266 | * eat newlines | ||
| 9267 | */ | ||
| 9268 | if (checkkwd & CHKNL) { | ||
| 9269 | while (t == TNL) { | ||
| 9270 | parseheredoc(); | ||
| 9271 | t = xxreadtoken(); | ||
| 9272 | } | ||
| 9273 | } | ||
| 9274 | |||
| 9275 | if (t != TWORD || quoteflag) { | ||
| 9276 | goto out; | ||
| 9277 | } | ||
| 9278 | |||
| 9279 | /* | ||
| 9280 | * check for keywords | ||
| 9281 | */ | ||
| 9282 | if (checkkwd & CHKKWD) { | ||
| 9283 | const char *const *pp; | ||
| 9284 | |||
| 9285 | pp = findkwd(wordtext); | ||
| 9286 | if (pp) { | ||
| 9287 | lasttoken = t = pp - tokname_array; | ||
| 9288 | TRACE(("keyword %s recognized\n", tokname(t))); | ||
| 9289 | goto out; | ||
| 9290 | } | ||
| 9291 | } | ||
| 9292 | |||
| 9293 | if (checkkwd & CHKALIAS) { | ||
| 9294 | #if ENABLE_ASH_ALIAS | ||
| 9295 | struct alias *ap; | ||
| 9296 | ap = lookupalias(wordtext, 1); | ||
| 9297 | if (ap != NULL) { | ||
| 9298 | if (*ap->val) { | ||
| 9299 | pushstring(ap->val, ap); | ||
| 9300 | } | ||
| 9301 | goto top; | ||
| 9302 | } | ||
| 9303 | #endif | ||
| 9304 | } | ||
| 9305 | out: | ||
| 9306 | checkkwd = 0; | ||
| 9307 | #if DEBUG | ||
| 9308 | if (!alreadyseen) | ||
| 9309 | TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : "")); | ||
| 9310 | else | ||
| 9311 | TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : "")); | ||
| 9312 | #endif | ||
| 9313 | return t; | ||
| 9314 | } | ||
| 9315 | |||
| 9316 | |||
| 9317 | /* | ||
| 9318 | * Read the next input token. | ||
| 9319 | * If the token is a word, we set backquotelist to the list of cmds in | ||
| 9320 | * backquotes. We set quoteflag to true if any part of the word was | ||
| 9321 | * quoted. | ||
| 9322 | * If the token is TREDIR, then we set redirnode to a structure containing | ||
| 9323 | * the redirection. | ||
| 9324 | * In all cases, the variable startlinno is set to the number of the line | ||
| 9325 | * on which the token starts. | ||
| 9326 | * | ||
| 9327 | * [Change comment: here documents and internal procedures] | ||
| 9328 | * [Readtoken shouldn't have any arguments. Perhaps we should make the | ||
| 9329 | * word parsing code into a separate routine. In this case, readtoken | ||
| 9330 | * doesn't need to have any internal procedures, but parseword does. | ||
| 9331 | * We could also make parseoperator in essence the main routine, and | ||
| 9332 | * have parseword (readtoken1?) handle both words and redirection.] | ||
| 9333 | */ | ||
| 9334 | #define NEW_xxreadtoken | ||
| 9335 | #ifdef NEW_xxreadtoken | ||
| 9336 | /* singles must be first! */ | ||
| 9337 | static const char xxreadtoken_chars[7] = { '\n', '(', ')', '&', '|', ';', 0 }; | ||
| 9338 | |||
| 9339 | static const char xxreadtoken_tokens[] = { | ||
| 9340 | TNL, TLP, TRP, /* only single occurrence allowed */ | ||
| 9341 | TBACKGND, TPIPE, TSEMI, /* if single occurrence */ | ||
| 9342 | TEOF, /* corresponds to trailing nul */ | ||
| 9343 | TAND, TOR, TENDCASE, /* if double occurrence */ | ||
| 9344 | }; | ||
| 9345 | |||
| 9346 | #define xxreadtoken_doubles \ | ||
| 9347 | (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars)) | ||
| 9348 | #define xxreadtoken_singles \ | ||
| 9349 | (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1) | ||
| 9350 | |||
| 9351 | static int xxreadtoken(void) | ||
| 9352 | { | ||
| 9353 | int c; | ||
| 9354 | |||
| 9355 | if (tokpushback) { | ||
| 9356 | tokpushback = 0; | ||
| 9357 | return lasttoken; | ||
| 9358 | } | ||
| 9359 | if (needprompt) { | ||
| 9360 | setprompt(2); | ||
| 9361 | } | ||
| 9362 | startlinno = plinno; | ||
| 9363 | for (;;) { /* until token or start of word found */ | ||
| 9364 | c = pgetc_macro(); | ||
| 9365 | |||
| 9366 | if ((c != ' ') && (c != '\t') | ||
| 9367 | #if ENABLE_ASH_ALIAS | ||
| 9368 | && (c != PEOA) | ||
| 9369 | #endif | ||
| 9370 | ) { | ||
| 9371 | if (c == '#') { | ||
| 9372 | while ((c = pgetc()) != '\n' && c != PEOF); | ||
| 9373 | pungetc(); | ||
| 9374 | } else if (c == '\\') { | ||
| 9375 | if (pgetc() != '\n') { | ||
| 9376 | pungetc(); | ||
| 9377 | goto READTOKEN1; | ||
| 9378 | } | ||
| 9379 | startlinno = ++plinno; | ||
| 9380 | if (doprompt) | ||
| 9381 | setprompt(2); | ||
| 9382 | } else { | ||
| 9383 | const char *p | ||
| 9384 | = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1; | ||
| 9385 | |||
| 9386 | if (c != PEOF) { | ||
| 9387 | if (c == '\n') { | ||
| 9388 | plinno++; | ||
| 9389 | needprompt = doprompt; | ||
| 9390 | } | ||
| 9391 | |||
| 9392 | p = strchr(xxreadtoken_chars, c); | ||
| 9393 | if (p == NULL) { | ||
| 9394 | READTOKEN1: | ||
| 9395 | return readtoken1(c, BASESYNTAX, (char *) NULL, 0); | ||
| 9396 | } | ||
| 9397 | |||
| 9398 | if (p - xxreadtoken_chars >= xxreadtoken_singles) { | ||
| 9399 | if (pgetc() == *p) { /* double occurrence? */ | ||
| 9400 | p += xxreadtoken_doubles + 1; | ||
| 9401 | } else { | ||
| 9402 | pungetc(); | ||
| 9403 | } | ||
| 9404 | } | ||
| 9405 | } | ||
| 9406 | return lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars]; | ||
| 9407 | } | ||
| 9408 | } | ||
| 9409 | } /* for */ | ||
| 9410 | } | ||
| 9411 | #else | ||
| 9412 | #define RETURN(token) return lasttoken = token | ||
| 9413 | static int | ||
| 9414 | xxreadtoken(void) | ||
| 9415 | { | ||
| 9416 | int c; | ||
| 9417 | |||
| 9418 | if (tokpushback) { | ||
| 9419 | tokpushback = 0; | ||
| 9420 | return lasttoken; | ||
| 9421 | } | ||
| 9422 | if (needprompt) { | ||
| 9423 | setprompt(2); | ||
| 9424 | } | ||
| 9425 | startlinno = plinno; | ||
| 9426 | for (;;) { /* until token or start of word found */ | ||
| 9427 | c = pgetc_macro(); | ||
| 9428 | switch (c) { | ||
| 9429 | case ' ': case '\t': | ||
| 9430 | #if ENABLE_ASH_ALIAS | ||
| 9431 | case PEOA: | ||
| 9432 | #endif | ||
| 9433 | continue; | ||
| 9434 | case '#': | ||
| 9435 | while ((c = pgetc()) != '\n' && c != PEOF); | ||
| 9436 | pungetc(); | ||
| 9437 | continue; | ||
| 9438 | case '\\': | ||
| 9439 | if (pgetc() == '\n') { | ||
| 9440 | startlinno = ++plinno; | ||
| 9441 | if (doprompt) | ||
| 9442 | setprompt(2); | ||
| 9443 | continue; | ||
| 9444 | } | ||
| 9445 | pungetc(); | ||
| 9446 | goto breakloop; | ||
| 9447 | case '\n': | ||
| 9448 | plinno++; | ||
| 9449 | needprompt = doprompt; | ||
| 9450 | RETURN(TNL); | ||
| 9451 | case PEOF: | ||
| 9452 | RETURN(TEOF); | ||
| 9453 | case '&': | ||
| 9454 | if (pgetc() == '&') | ||
| 9455 | RETURN(TAND); | ||
| 9456 | pungetc(); | ||
| 9457 | RETURN(TBACKGND); | ||
| 9458 | case '|': | ||
| 9459 | if (pgetc() == '|') | ||
| 9460 | RETURN(TOR); | ||
| 9461 | pungetc(); | ||
| 9462 | RETURN(TPIPE); | ||
| 9463 | case ';': | ||
| 9464 | if (pgetc() == ';') | ||
| 9465 | RETURN(TENDCASE); | ||
| 9466 | pungetc(); | ||
| 9467 | RETURN(TSEMI); | ||
| 9468 | case '(': | ||
| 9469 | RETURN(TLP); | ||
| 9470 | case ')': | ||
| 9471 | RETURN(TRP); | ||
| 9472 | default: | ||
| 9473 | goto breakloop; | ||
| 9474 | } | ||
| 9475 | } | ||
| 9476 | breakloop: | ||
| 9477 | return readtoken1(c, BASESYNTAX, (char *)NULL, 0); | ||
| 9478 | #undef RETURN | ||
| 9479 | } | ||
| 9480 | #endif /* NEW_xxreadtoken */ | ||
| 9481 | |||
| 9482 | |||
| 9483 | /* | 9557 | /* |
| 9484 | * If eofmark is NULL, read a word or a redirection symbol. If eofmark | 9558 | * If eofmark is NULL, read a word or a redirection symbol. If eofmark |
| 9485 | * is not NULL, read a here document. In the latter case, eofmark is the | 9559 | * is not NULL, read a here document. In the latter case, eofmark is the |
| @@ -9696,7 +9770,7 @@ readtoken1(int firstc, int syntax, char *eofmark, int striptabs) | |||
| 9696 | if ((c == '>' || c == '<') | 9770 | if ((c == '>' || c == '<') |
| 9697 | && quotef == 0 | 9771 | && quotef == 0 |
| 9698 | && len <= 2 | 9772 | && len <= 2 |
| 9699 | && (*out == '\0' || is_digit(*out))) { | 9773 | && (*out == '\0' || isdigit(*out))) { |
| 9700 | PARSEREDIR(); | 9774 | PARSEREDIR(); |
| 9701 | return lasttoken = TREDIR; | 9775 | return lasttoken = TREDIR; |
| 9702 | } else { | 9776 | } else { |
| @@ -9864,11 +9938,11 @@ parsesub: { | |||
| 9864 | STPUTC(c, out); | 9938 | STPUTC(c, out); |
| 9865 | c = pgetc(); | 9939 | c = pgetc(); |
| 9866 | } while (c > PEOA_OR_PEOF && is_in_name(c)); | 9940 | } while (c > PEOA_OR_PEOF && is_in_name(c)); |
| 9867 | } else if (is_digit(c)) { | 9941 | } else if (isdigit(c)) { |
| 9868 | do { | 9942 | do { |
| 9869 | STPUTC(c, out); | 9943 | STPUTC(c, out); |
| 9870 | c = pgetc(); | 9944 | c = pgetc(); |
| 9871 | } while (is_digit(c)); | 9945 | } while (isdigit(c)); |
| 9872 | } else if (is_special(c)) { | 9946 | } else if (is_special(c)) { |
| 9873 | USTPUTC(c, out); | 9947 | USTPUTC(c, out); |
| 9874 | c = pgetc(); | 9948 | c = pgetc(); |
| @@ -10040,10 +10114,8 @@ parsebackq: { | |||
| 10040 | 10114 | ||
| 10041 | if (oldstyle) | 10115 | if (oldstyle) |
| 10042 | doprompt = saveprompt; | 10116 | doprompt = saveprompt; |
| 10043 | else { | 10117 | else if (readtoken() != TRP) |
| 10044 | if (readtoken() != TRP) | 10118 | raise_error_unexpected_syntax(TRP); |
| 10045 | raise_error_unexpected_syntax(TRP); | ||
| 10046 | } | ||
| 10047 | 10119 | ||
| 10048 | (*nlpp)->n = n; | 10120 | (*nlpp)->n = n; |
| 10049 | if (oldstyle) { | 10121 | if (oldstyle) { |
| @@ -10102,47 +10174,296 @@ parsearith: { | |||
| 10102 | 10174 | ||
| 10103 | } /* end of readtoken */ | 10175 | } /* end of readtoken */ |
| 10104 | 10176 | ||
| 10105 | |||
| 10106 | /* | 10177 | /* |
| 10107 | * Returns true if the text contains nothing to expand (no dollar signs | 10178 | * Read the next input token. |
| 10108 | * or backquotes). | 10179 | * If the token is a word, we set backquotelist to the list of cmds in |
| 10180 | * backquotes. We set quoteflag to true if any part of the word was | ||
| 10181 | * quoted. | ||
| 10182 | * If the token is TREDIR, then we set redirnode to a structure containing | ||
| 10183 | * the redirection. | ||
| 10184 | * In all cases, the variable startlinno is set to the number of the line | ||
| 10185 | * on which the token starts. | ||
| 10186 | * | ||
| 10187 | * [Change comment: here documents and internal procedures] | ||
| 10188 | * [Readtoken shouldn't have any arguments. Perhaps we should make the | ||
| 10189 | * word parsing code into a separate routine. In this case, readtoken | ||
| 10190 | * doesn't need to have any internal procedures, but parseword does. | ||
| 10191 | * We could also make parseoperator in essence the main routine, and | ||
| 10192 | * have parseword (readtoken1?) handle both words and redirection.] | ||
| 10109 | */ | 10193 | */ |
| 10194 | #define NEW_xxreadtoken | ||
| 10195 | #ifdef NEW_xxreadtoken | ||
| 10196 | /* singles must be first! */ | ||
| 10197 | static const char xxreadtoken_chars[7] = { '\n', '(', ')', '&', '|', ';', 0 }; | ||
| 10198 | |||
| 10199 | static const char xxreadtoken_tokens[] = { | ||
| 10200 | TNL, TLP, TRP, /* only single occurrence allowed */ | ||
| 10201 | TBACKGND, TPIPE, TSEMI, /* if single occurrence */ | ||
| 10202 | TEOF, /* corresponds to trailing nul */ | ||
| 10203 | TAND, TOR, TENDCASE, /* if double occurrence */ | ||
| 10204 | }; | ||
| 10205 | |||
| 10206 | #define xxreadtoken_doubles \ | ||
| 10207 | (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars)) | ||
| 10208 | #define xxreadtoken_singles \ | ||
| 10209 | (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1) | ||
| 10210 | |||
| 10110 | static int | 10211 | static int |
| 10111 | noexpand(char *text) | 10212 | xxreadtoken(void) |
| 10112 | { | 10213 | { |
| 10113 | char *p; | 10214 | int c; |
| 10114 | char c; | ||
| 10115 | 10215 | ||
| 10116 | p = text; | 10216 | if (tokpushback) { |
| 10117 | while ((c = *p++) != '\0') { | 10217 | tokpushback = 0; |
| 10118 | if (c == CTLQUOTEMARK) | 10218 | return lasttoken; |
| 10219 | } | ||
| 10220 | if (needprompt) { | ||
| 10221 | setprompt(2); | ||
| 10222 | } | ||
| 10223 | startlinno = plinno; | ||
| 10224 | for (;;) { /* until token or start of word found */ | ||
| 10225 | c = pgetc_macro(); | ||
| 10226 | |||
| 10227 | if ((c != ' ') && (c != '\t') | ||
| 10228 | #if ENABLE_ASH_ALIAS | ||
| 10229 | && (c != PEOA) | ||
| 10230 | #endif | ||
| 10231 | ) { | ||
| 10232 | if (c == '#') { | ||
| 10233 | while ((c = pgetc()) != '\n' && c != PEOF); | ||
| 10234 | pungetc(); | ||
| 10235 | } else if (c == '\\') { | ||
| 10236 | if (pgetc() != '\n') { | ||
| 10237 | pungetc(); | ||
| 10238 | goto READTOKEN1; | ||
| 10239 | } | ||
| 10240 | startlinno = ++plinno; | ||
| 10241 | if (doprompt) | ||
| 10242 | setprompt(2); | ||
| 10243 | } else { | ||
| 10244 | const char *p | ||
| 10245 | = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1; | ||
| 10246 | |||
| 10247 | if (c != PEOF) { | ||
| 10248 | if (c == '\n') { | ||
| 10249 | plinno++; | ||
| 10250 | needprompt = doprompt; | ||
| 10251 | } | ||
| 10252 | |||
| 10253 | p = strchr(xxreadtoken_chars, c); | ||
| 10254 | if (p == NULL) { | ||
| 10255 | READTOKEN1: | ||
| 10256 | return readtoken1(c, BASESYNTAX, (char *) NULL, 0); | ||
| 10257 | } | ||
| 10258 | |||
| 10259 | if (p - xxreadtoken_chars >= xxreadtoken_singles) { | ||
| 10260 | if (pgetc() == *p) { /* double occurrence? */ | ||
| 10261 | p += xxreadtoken_doubles + 1; | ||
| 10262 | } else { | ||
| 10263 | pungetc(); | ||
| 10264 | } | ||
| 10265 | } | ||
| 10266 | } | ||
| 10267 | return lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars]; | ||
| 10268 | } | ||
| 10269 | } | ||
| 10270 | } /* for */ | ||
| 10271 | } | ||
| 10272 | #else | ||
| 10273 | #define RETURN(token) return lasttoken = token | ||
| 10274 | static int | ||
| 10275 | xxreadtoken(void) | ||
| 10276 | { | ||
| 10277 | int c; | ||
| 10278 | |||
| 10279 | if (tokpushback) { | ||
| 10280 | tokpushback = 0; | ||
| 10281 | return lasttoken; | ||
| 10282 | } | ||
| 10283 | if (needprompt) { | ||
| 10284 | setprompt(2); | ||
| 10285 | } | ||
| 10286 | startlinno = plinno; | ||
| 10287 | for (;;) { /* until token or start of word found */ | ||
| 10288 | c = pgetc_macro(); | ||
| 10289 | switch (c) { | ||
| 10290 | case ' ': case '\t': | ||
| 10291 | #if ENABLE_ASH_ALIAS | ||
| 10292 | case PEOA: | ||
| 10293 | #endif | ||
| 10119 | continue; | 10294 | continue; |
| 10120 | if (c == CTLESC) | 10295 | case '#': |
| 10121 | p++; | 10296 | while ((c = pgetc()) != '\n' && c != PEOF); |
| 10122 | else if (SIT(c, BASESYNTAX) == CCTL) | 10297 | pungetc(); |
| 10123 | return 0; | 10298 | continue; |
| 10299 | case '\\': | ||
| 10300 | if (pgetc() == '\n') { | ||
| 10301 | startlinno = ++plinno; | ||
| 10302 | if (doprompt) | ||
| 10303 | setprompt(2); | ||
| 10304 | continue; | ||
| 10305 | } | ||
| 10306 | pungetc(); | ||
| 10307 | goto breakloop; | ||
| 10308 | case '\n': | ||
| 10309 | plinno++; | ||
| 10310 | needprompt = doprompt; | ||
| 10311 | RETURN(TNL); | ||
| 10312 | case PEOF: | ||
| 10313 | RETURN(TEOF); | ||
| 10314 | case '&': | ||
| 10315 | if (pgetc() == '&') | ||
| 10316 | RETURN(TAND); | ||
| 10317 | pungetc(); | ||
| 10318 | RETURN(TBACKGND); | ||
| 10319 | case '|': | ||
| 10320 | if (pgetc() == '|') | ||
| 10321 | RETURN(TOR); | ||
| 10322 | pungetc(); | ||
| 10323 | RETURN(TPIPE); | ||
| 10324 | case ';': | ||
| 10325 | if (pgetc() == ';') | ||
| 10326 | RETURN(TENDCASE); | ||
| 10327 | pungetc(); | ||
| 10328 | RETURN(TSEMI); | ||
| 10329 | case '(': | ||
| 10330 | RETURN(TLP); | ||
| 10331 | case ')': | ||
| 10332 | RETURN(TRP); | ||
| 10333 | default: | ||
| 10334 | goto breakloop; | ||
| 10335 | } | ||
| 10124 | } | 10336 | } |
| 10125 | return 1; | 10337 | breakloop: |
| 10338 | return readtoken1(c, BASESYNTAX, (char *)NULL, 0); | ||
| 10339 | #undef RETURN | ||
| 10126 | } | 10340 | } |
| 10341 | #endif /* NEW_xxreadtoken */ | ||
| 10342 | |||
| 10343 | static int | ||
| 10344 | readtoken(void) | ||
| 10345 | { | ||
| 10346 | int t; | ||
| 10347 | #if DEBUG | ||
| 10348 | int alreadyseen = tokpushback; | ||
| 10349 | #endif | ||
| 10350 | |||
| 10351 | #if ENABLE_ASH_ALIAS | ||
| 10352 | top: | ||
| 10353 | #endif | ||
| 10354 | |||
| 10355 | t = xxreadtoken(); | ||
| 10356 | |||
| 10357 | /* | ||
| 10358 | * eat newlines | ||
| 10359 | */ | ||
| 10360 | if (checkkwd & CHKNL) { | ||
| 10361 | while (t == TNL) { | ||
| 10362 | parseheredoc(); | ||
| 10363 | t = xxreadtoken(); | ||
| 10364 | } | ||
| 10365 | } | ||
| 10127 | 10366 | ||
| 10367 | if (t != TWORD || quoteflag) { | ||
| 10368 | goto out; | ||
| 10369 | } | ||
| 10370 | |||
| 10371 | /* | ||
| 10372 | * check for keywords | ||
| 10373 | */ | ||
| 10374 | if (checkkwd & CHKKWD) { | ||
| 10375 | const char *const *pp; | ||
| 10376 | |||
| 10377 | pp = findkwd(wordtext); | ||
| 10378 | if (pp) { | ||
| 10379 | lasttoken = t = pp - tokname_array; | ||
| 10380 | TRACE(("keyword %s recognized\n", tokname(t))); | ||
| 10381 | goto out; | ||
| 10382 | } | ||
| 10383 | } | ||
| 10384 | |||
| 10385 | if (checkkwd & CHKALIAS) { | ||
| 10386 | #if ENABLE_ASH_ALIAS | ||
| 10387 | struct alias *ap; | ||
| 10388 | ap = lookupalias(wordtext, 1); | ||
| 10389 | if (ap != NULL) { | ||
| 10390 | if (*ap->val) { | ||
| 10391 | pushstring(ap->val, ap); | ||
| 10392 | } | ||
| 10393 | goto top; | ||
| 10394 | } | ||
| 10395 | #endif | ||
| 10396 | } | ||
| 10397 | out: | ||
| 10398 | checkkwd = 0; | ||
| 10399 | #if DEBUG | ||
| 10400 | if (!alreadyseen) | ||
| 10401 | TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : "")); | ||
| 10402 | else | ||
| 10403 | TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : "")); | ||
| 10404 | #endif | ||
| 10405 | return t; | ||
| 10406 | } | ||
| 10407 | |||
| 10408 | static char | ||
| 10409 | peektoken(void) | ||
| 10410 | { | ||
| 10411 | int t; | ||
| 10412 | |||
| 10413 | t = readtoken(); | ||
| 10414 | tokpushback++; | ||
| 10415 | return tokname_array[t][0]; | ||
| 10416 | } | ||
| 10128 | 10417 | ||
| 10129 | /* | 10418 | /* |
| 10130 | * Return of a legal variable name (a letter or underscore followed by zero or | 10419 | * Read and parse a command. Returns NEOF on end of file. (NULL is a |
| 10131 | * more letters, underscores, and digits). | 10420 | * valid parse tree indicating a blank line.) |
| 10132 | */ | 10421 | */ |
| 10133 | static char * | 10422 | static union node * |
| 10134 | endofname(const char *name) | 10423 | parsecmd(int interact) |
| 10135 | { | 10424 | { |
| 10136 | char *p; | 10425 | int t; |
| 10137 | 10426 | ||
| 10138 | p = (char *) name; | 10427 | tokpushback = 0; |
| 10139 | if (!is_name(*p)) | 10428 | doprompt = interact; |
| 10140 | return p; | 10429 | if (doprompt) |
| 10141 | while (*++p) { | 10430 | setprompt(doprompt); |
| 10142 | if (!is_in_name(*p)) | 10431 | needprompt = 0; |
| 10143 | break; | 10432 | t = readtoken(); |
| 10433 | if (t == TEOF) | ||
| 10434 | return NEOF; | ||
| 10435 | if (t == TNL) | ||
| 10436 | return NULL; | ||
| 10437 | tokpushback++; | ||
| 10438 | return list(1); | ||
| 10439 | } | ||
| 10440 | |||
| 10441 | /* | ||
| 10442 | * Input any here documents. | ||
| 10443 | */ | ||
| 10444 | static void | ||
| 10445 | parseheredoc(void) | ||
| 10446 | { | ||
| 10447 | struct heredoc *here; | ||
| 10448 | union node *n; | ||
| 10449 | |||
| 10450 | here = heredoclist; | ||
| 10451 | heredoclist = 0; | ||
| 10452 | |||
| 10453 | while (here) { | ||
| 10454 | if (needprompt) { | ||
| 10455 | setprompt(2); | ||
| 10456 | } | ||
| 10457 | readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX, | ||
| 10458 | here->eofmark, here->striptabs); | ||
| 10459 | n = stalloc(sizeof(struct narg)); | ||
| 10460 | n->narg.type = NARG; | ||
| 10461 | n->narg.next = NULL; | ||
| 10462 | n->narg.text = wordtext; | ||
| 10463 | n->narg.backquote = backquotelist; | ||
| 10464 | here->here->nhere.doc = n; | ||
| 10465 | here = here->next; | ||
| 10144 | } | 10466 | } |
| 10145 | return p; | ||
| 10146 | } | 10467 | } |
| 10147 | 10468 | ||
| 10148 | 10469 | ||
| @@ -10171,35 +10492,6 @@ expandstr(const char *ps) | |||
| 10171 | } | 10492 | } |
| 10172 | #endif | 10493 | #endif |
| 10173 | 10494 | ||
| 10174 | static void setprompt(int whichprompt) | ||
| 10175 | { | ||
| 10176 | const char *prompt; | ||
| 10177 | #if ENABLE_ASH_EXPAND_PRMT | ||
| 10178 | struct stackmark smark; | ||
| 10179 | #endif | ||
| 10180 | |||
| 10181 | needprompt = 0; | ||
| 10182 | |||
| 10183 | switch (whichprompt) { | ||
| 10184 | case 1: | ||
| 10185 | prompt = ps1val(); | ||
| 10186 | break; | ||
| 10187 | case 2: | ||
| 10188 | prompt = ps2val(); | ||
| 10189 | break; | ||
| 10190 | default: /* 0 */ | ||
| 10191 | prompt = nullstr; | ||
| 10192 | } | ||
| 10193 | #if ENABLE_ASH_EXPAND_PRMT | ||
| 10194 | setstackmark(&smark); | ||
| 10195 | stalloc(stackblocksize()); | ||
| 10196 | #endif | ||
| 10197 | putprompt(expandstr(prompt)); | ||
| 10198 | #if ENABLE_ASH_EXPAND_PRMT | ||
| 10199 | popstackmark(&smark); | ||
| 10200 | #endif | ||
| 10201 | } | ||
| 10202 | |||
| 10203 | 10495 | ||
| 10204 | /* | 10496 | /* |
| 10205 | * Execute a command or commands contained in a string. | 10497 | * Execute a command or commands contained in a string. |
| @@ -10719,7 +11011,6 @@ copyfd(int from, int to) | |||
| 10719 | return newfd; | 11011 | return newfd; |
| 10720 | } | 11012 | } |
| 10721 | 11013 | ||
| 10722 | |||
| 10723 | static int | 11014 | static int |
| 10724 | redirectsafe(union node *redir, int flags) | 11015 | redirectsafe(union node *redir, int flags) |
| 10725 | { | 11016 | { |
| @@ -10750,7 +11041,6 @@ static void sharg(union node *, FILE *); | |||
| 10750 | static void indent(int, char *, FILE *); | 11041 | static void indent(int, char *, FILE *); |
| 10751 | static void trstring(char *); | 11042 | static void trstring(char *); |
| 10752 | 11043 | ||
| 10753 | |||
| 10754 | static void | 11044 | static void |
| 10755 | showtree(union node *n) | 11045 | showtree(union node *n) |
| 10756 | { | 11046 | { |
| @@ -10758,7 +11048,6 @@ showtree(union node *n) | |||
| 10758 | shtree(n, 1, NULL, stdout); | 11048 | shtree(n, 1, NULL, stdout); |
| 10759 | } | 11049 | } |
| 10760 | 11050 | ||
| 10761 | |||
| 10762 | static void | 11051 | static void |
| 10763 | shtree(union node *n, int ind, char *pfx, FILE *fp) | 11052 | shtree(union node *n, int ind, char *pfx, FILE *fp) |
| 10764 | { | 11053 | { |
| @@ -10808,7 +11097,6 @@ shtree(union node *n, int ind, char *pfx, FILE *fp) | |||
| 10808 | } | 11097 | } |
| 10809 | } | 11098 | } |
| 10810 | 11099 | ||
| 10811 | |||
| 10812 | static void | 11100 | static void |
| 10813 | shcmd(union node *cmd, FILE *fp) | 11101 | shcmd(union node *cmd, FILE *fp) |
| 10814 | { | 11102 | { |
| @@ -10849,7 +11137,6 @@ shcmd(union node *cmd, FILE *fp) | |||
| 10849 | } | 11137 | } |
| 10850 | } | 11138 | } |
| 10851 | 11139 | ||
| 10852 | |||
| 10853 | static void | 11140 | static void |
| 10854 | sharg(union node *arg, FILE *fp) | 11141 | sharg(union node *arg, FILE *fp) |
| 10855 | { | 11142 | { |
| @@ -11328,7 +11615,6 @@ dotrap(void) | |||
| 11328 | return skip; | 11615 | return skip; |
| 11329 | } | 11616 | } |
| 11330 | 11617 | ||
| 11331 | |||
| 11332 | /* | 11618 | /* |
| 11333 | * Controls whether the shell is interactive or not. | 11619 | * Controls whether the shell is interactive or not. |
| 11334 | */ | 11620 | */ |
| @@ -11361,11 +11647,11 @@ setinteractive(int on) | |||
| 11361 | #endif | 11647 | #endif |
| 11362 | } | 11648 | } |
| 11363 | 11649 | ||
| 11364 | |||
| 11365 | #if !ENABLE_FEATURE_SH_EXTRA_QUIET | 11650 | #if !ENABLE_FEATURE_SH_EXTRA_QUIET |
| 11366 | /*** List the available builtins ***/ | 11651 | /*** List the available builtins ***/ |
| 11367 | 11652 | ||
| 11368 | static int helpcmd(int argc, char **argv) | 11653 | static int |
| 11654 | helpcmd(int argc, char **argv) | ||
| 11369 | { | 11655 | { |
| 11370 | int col, i; | 11656 | int col, i; |
| 11371 | 11657 | ||
| @@ -11392,7 +11678,6 @@ static int helpcmd(int argc, char **argv) | |||
| 11392 | } | 11678 | } |
| 11393 | #endif /* FEATURE_SH_EXTRA_QUIET */ | 11679 | #endif /* FEATURE_SH_EXTRA_QUIET */ |
| 11394 | 11680 | ||
| 11395 | |||
| 11396 | /* | 11681 | /* |
| 11397 | * Called to exit the shell. | 11682 | * Called to exit the shell. |
| 11398 | */ | 11683 | */ |
| @@ -11427,258 +11712,6 @@ exitshell(void) | |||
| 11427 | /* NOTREACHED */ | 11712 | /* NOTREACHED */ |
| 11428 | } | 11713 | } |
| 11429 | 11714 | ||
| 11430 | /* var.c */ | ||
| 11431 | |||
| 11432 | static struct var *vartab[VTABSIZE]; | ||
| 11433 | |||
| 11434 | static int vpcmp(const void *, const void *); | ||
| 11435 | static struct var **findvar(struct var **, const char *); | ||
| 11436 | |||
| 11437 | /* | ||
| 11438 | * Initialize the variable symbol tables and import the environment | ||
| 11439 | */ | ||
| 11440 | |||
| 11441 | |||
| 11442 | #if ENABLE_ASH_GETOPTS | ||
| 11443 | /* | ||
| 11444 | * Safe version of setvar, returns 1 on success 0 on failure. | ||
| 11445 | */ | ||
| 11446 | static int | ||
| 11447 | setvarsafe(const char *name, const char *val, int flags) | ||
| 11448 | { | ||
| 11449 | int err; | ||
| 11450 | volatile int saveint; | ||
| 11451 | struct jmploc *volatile savehandler = exception_handler; | ||
| 11452 | struct jmploc jmploc; | ||
| 11453 | |||
| 11454 | SAVE_INT(saveint); | ||
| 11455 | if (setjmp(jmploc.loc)) | ||
| 11456 | err = 1; | ||
| 11457 | else { | ||
| 11458 | exception_handler = &jmploc; | ||
| 11459 | setvar(name, val, flags); | ||
| 11460 | err = 0; | ||
| 11461 | } | ||
| 11462 | exception_handler = savehandler; | ||
| 11463 | RESTORE_INT(saveint); | ||
| 11464 | return err; | ||
| 11465 | } | ||
| 11466 | #endif | ||
| 11467 | |||
| 11468 | |||
| 11469 | /* | ||
| 11470 | * Set the value of a variable. The flags argument is ored with the | ||
| 11471 | * flags of the variable. If val is NULL, the variable is unset. | ||
| 11472 | */ | ||
| 11473 | static void | ||
| 11474 | setvar(const char *name, const char *val, int flags) | ||
| 11475 | { | ||
| 11476 | char *p, *q; | ||
| 11477 | size_t namelen; | ||
| 11478 | char *nameeq; | ||
| 11479 | size_t vallen; | ||
| 11480 | |||
| 11481 | q = endofname(name); | ||
| 11482 | p = strchrnul(q, '='); | ||
| 11483 | namelen = p - name; | ||
| 11484 | if (!namelen || p != q) | ||
| 11485 | ash_msg_and_raise_error("%.*s: bad variable name", namelen, name); | ||
| 11486 | vallen = 0; | ||
| 11487 | if (val == NULL) { | ||
| 11488 | flags |= VUNSET; | ||
| 11489 | } else { | ||
| 11490 | vallen = strlen(val); | ||
| 11491 | } | ||
| 11492 | INT_OFF; | ||
| 11493 | nameeq = ckmalloc(namelen + vallen + 2); | ||
| 11494 | p = memcpy(nameeq, name, namelen) + namelen; | ||
| 11495 | if (val) { | ||
| 11496 | *p++ = '='; | ||
| 11497 | p = memcpy(p, val, vallen) + vallen; | ||
| 11498 | } | ||
| 11499 | *p = '\0'; | ||
| 11500 | setvareq(nameeq, flags | VNOSAVE); | ||
| 11501 | INT_ON; | ||
| 11502 | } | ||
| 11503 | |||
| 11504 | |||
| 11505 | /* | ||
| 11506 | * Same as setvar except that the variable and value are passed in | ||
| 11507 | * the first argument as name=value. Since the first argument will | ||
| 11508 | * be actually stored in the table, it should not be a string that | ||
| 11509 | * will go away. | ||
| 11510 | * Called with interrupts off. | ||
| 11511 | */ | ||
| 11512 | static void | ||
| 11513 | setvareq(char *s, int flags) | ||
| 11514 | { | ||
| 11515 | struct var *vp, **vpp; | ||
| 11516 | |||
| 11517 | vpp = hashvar(s); | ||
| 11518 | flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1)); | ||
| 11519 | vp = *findvar(vpp, s); | ||
| 11520 | if (vp) { | ||
| 11521 | if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) { | ||
| 11522 | const char *n; | ||
| 11523 | |||
| 11524 | if (flags & VNOSAVE) | ||
| 11525 | free(s); | ||
| 11526 | n = vp->text; | ||
| 11527 | ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n); | ||
| 11528 | } | ||
| 11529 | |||
| 11530 | if (flags & VNOSET) | ||
| 11531 | return; | ||
| 11532 | |||
| 11533 | if (vp->func && (flags & VNOFUNC) == 0) | ||
| 11534 | (*vp->func)(strchrnul(s, '=') + 1); | ||
| 11535 | |||
| 11536 | if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0) | ||
| 11537 | free((char*)vp->text); | ||
| 11538 | |||
| 11539 | flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET); | ||
| 11540 | } else { | ||
| 11541 | if (flags & VNOSET) | ||
| 11542 | return; | ||
| 11543 | /* not found */ | ||
| 11544 | vp = ckmalloc(sizeof(*vp)); | ||
| 11545 | vp->next = *vpp; | ||
| 11546 | vp->func = NULL; | ||
| 11547 | *vpp = vp; | ||
| 11548 | } | ||
| 11549 | if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE))) | ||
| 11550 | s = ckstrdup(s); | ||
| 11551 | vp->text = s; | ||
| 11552 | vp->flags = flags; | ||
| 11553 | } | ||
| 11554 | |||
| 11555 | |||
| 11556 | /* | ||
| 11557 | * Process a linked list of variable assignments. | ||
| 11558 | */ | ||
| 11559 | static void | ||
| 11560 | listsetvar(struct strlist *list_set_var, int flags) | ||
| 11561 | { | ||
| 11562 | struct strlist *lp = list_set_var; | ||
| 11563 | |||
| 11564 | if (!lp) | ||
| 11565 | return; | ||
| 11566 | INT_OFF; | ||
| 11567 | do { | ||
| 11568 | setvareq(lp->text, flags); | ||
| 11569 | } while ((lp = lp->next)); | ||
| 11570 | INT_ON; | ||
| 11571 | } | ||
| 11572 | |||
| 11573 | |||
| 11574 | /* | ||
| 11575 | * Find the value of a variable. Returns NULL if not set. | ||
| 11576 | */ | ||
| 11577 | static char * | ||
| 11578 | lookupvar(const char *name) | ||
| 11579 | { | ||
| 11580 | struct var *v; | ||
| 11581 | |||
| 11582 | v = *findvar(hashvar(name), name); | ||
| 11583 | if (v) { | ||
| 11584 | #ifdef DYNAMIC_VAR | ||
| 11585 | /* | ||
| 11586 | * Dynamic variables are implemented roughly the same way they are | ||
| 11587 | * in bash. Namely, they're "special" so long as they aren't unset. | ||
| 11588 | * As soon as they're unset, they're no longer dynamic, and dynamic | ||
| 11589 | * lookup will no longer happen at that point. -- PFM. | ||
| 11590 | */ | ||
| 11591 | if ((v->flags & VDYNAMIC)) | ||
| 11592 | (*v->func)(NULL); | ||
| 11593 | #endif | ||
| 11594 | if (!(v->flags & VUNSET)) | ||
| 11595 | return strchrnul(v->text, '=') + 1; | ||
| 11596 | } | ||
| 11597 | |||
| 11598 | return NULL; | ||
| 11599 | } | ||
| 11600 | |||
| 11601 | |||
| 11602 | /* | ||
| 11603 | * Search the environment of a builtin command. | ||
| 11604 | */ | ||
| 11605 | static char * | ||
| 11606 | bltinlookup(const char *name) | ||
| 11607 | { | ||
| 11608 | struct strlist *sp; | ||
| 11609 | |||
| 11610 | for (sp = cmdenviron; sp; sp = sp->next) { | ||
| 11611 | if (varequal(sp->text, name)) | ||
| 11612 | return strchrnul(sp->text, '=') + 1; | ||
| 11613 | } | ||
| 11614 | return lookupvar(name); | ||
| 11615 | } | ||
| 11616 | |||
| 11617 | |||
| 11618 | /* | ||
| 11619 | * Generate a list of variables satisfying the given conditions. | ||
| 11620 | */ | ||
| 11621 | static char ** | ||
| 11622 | listvars(int on, int off, char ***end) | ||
| 11623 | { | ||
| 11624 | struct var **vpp; | ||
| 11625 | struct var *vp; | ||
| 11626 | char **ep; | ||
| 11627 | int mask; | ||
| 11628 | |||
| 11629 | STARTSTACKSTR(ep); | ||
| 11630 | vpp = vartab; | ||
| 11631 | mask = on | off; | ||
| 11632 | do { | ||
| 11633 | for (vp = *vpp; vp; vp = vp->next) | ||
| 11634 | if ((vp->flags & mask) == on) { | ||
| 11635 | if (ep == stackstrend()) | ||
| 11636 | ep = growstackstr(); | ||
| 11637 | *ep++ = (char *) vp->text; | ||
| 11638 | } | ||
| 11639 | } while (++vpp < vartab + VTABSIZE); | ||
| 11640 | if (ep == stackstrend()) | ||
| 11641 | ep = growstackstr(); | ||
| 11642 | if (end) | ||
| 11643 | *end = ep; | ||
| 11644 | *ep++ = NULL; | ||
| 11645 | return grabstackstr(ep); | ||
| 11646 | } | ||
| 11647 | |||
| 11648 | |||
| 11649 | /* | ||
| 11650 | * POSIX requires that 'set' (but not export or readonly) output the | ||
| 11651 | * variables in lexicographic order - by the locale's collating order (sigh). | ||
| 11652 | * Maybe we could keep them in an ordered balanced binary tree | ||
| 11653 | * instead of hashed lists. | ||
| 11654 | * For now just roll 'em through qsort for printing... | ||
| 11655 | */ | ||
| 11656 | static int | ||
| 11657 | showvars(const char *sep_prefix, int on, int off) | ||
| 11658 | { | ||
| 11659 | const char *sep; | ||
| 11660 | char **ep, **epend; | ||
| 11661 | |||
| 11662 | ep = listvars(on, off, &epend); | ||
| 11663 | qsort(ep, epend - ep, sizeof(char *), vpcmp); | ||
| 11664 | |||
| 11665 | sep = *sep_prefix ? spcstr : sep_prefix; | ||
| 11666 | |||
| 11667 | for (; ep < epend; ep++) { | ||
| 11668 | const char *p; | ||
| 11669 | const char *q; | ||
| 11670 | |||
| 11671 | p = strchrnul(*ep, '='); | ||
| 11672 | q = nullstr; | ||
| 11673 | if (*p) | ||
| 11674 | q = single_quote(++p); | ||
| 11675 | |||
| 11676 | out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q); | ||
| 11677 | } | ||
| 11678 | |||
| 11679 | return 0; | ||
| 11680 | } | ||
| 11681 | |||
| 11682 | 11715 | ||
| 11683 | /* | 11716 | /* |
| 11684 | * The export and readonly commands. | 11717 | * The export and readonly commands. |
| @@ -11781,39 +11814,6 @@ localcmd(int argc, char **argv) | |||
| 11781 | 11814 | ||
| 11782 | 11815 | ||
| 11783 | /* | 11816 | /* |
| 11784 | * Called after a function returns. | ||
| 11785 | * Interrupts must be off. | ||
| 11786 | */ | ||
| 11787 | static void | ||
| 11788 | poplocalvars(void) | ||
| 11789 | { | ||
| 11790 | struct localvar *lvp; | ||
| 11791 | struct var *vp; | ||
| 11792 | |||
| 11793 | while ((lvp = localvars) != NULL) { | ||
| 11794 | localvars = lvp->next; | ||
| 11795 | vp = lvp->vp; | ||
| 11796 | TRACE(("poplocalvar %s", vp ? vp->text : "-")); | ||
| 11797 | if (vp == NULL) { /* $- saved */ | ||
| 11798 | memcpy(optlist, lvp->text, sizeof(optlist)); | ||
| 11799 | free((char*)lvp->text); | ||
| 11800 | optschanged(); | ||
| 11801 | } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) { | ||
| 11802 | unsetvar(vp->text); | ||
| 11803 | } else { | ||
| 11804 | if (vp->func) | ||
| 11805 | (*vp->func)(strchrnul(lvp->text, '=') + 1); | ||
| 11806 | if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0) | ||
| 11807 | free((char*)vp->text); | ||
| 11808 | vp->flags = lvp->flags; | ||
| 11809 | vp->text = lvp->text; | ||
| 11810 | } | ||
| 11811 | free(lvp); | ||
| 11812 | } | ||
| 11813 | } | ||
| 11814 | |||
| 11815 | |||
| 11816 | /* | ||
| 11817 | * The unset builtin command. We unset the function before we unset the | 11817 | * The unset builtin command. We unset the function before we unset the |
| 11818 | * variable to allow a function to be unset when there is a readonly variable | 11818 | * variable to allow a function to be unset when there is a readonly variable |
| 11819 | * with the same name. | 11819 | * with the same name. |
| @@ -11844,104 +11844,6 @@ unsetcmd(int argc, char **argv) | |||
| 11844 | } | 11844 | } |
| 11845 | 11845 | ||
| 11846 | 11846 | ||
| 11847 | /* | ||
| 11848 | * Unset the specified variable. | ||
| 11849 | */ | ||
| 11850 | static int | ||
| 11851 | unsetvar(const char *s) | ||
| 11852 | { | ||
| 11853 | struct var **vpp; | ||
| 11854 | struct var *vp; | ||
| 11855 | int retval; | ||
| 11856 | |||
| 11857 | vpp = findvar(hashvar(s), s); | ||
| 11858 | vp = *vpp; | ||
| 11859 | retval = 2; | ||
| 11860 | if (vp) { | ||
| 11861 | int flags = vp->flags; | ||
| 11862 | |||
| 11863 | retval = 1; | ||
| 11864 | if (flags & VREADONLY) | ||
| 11865 | goto out; | ||
| 11866 | #ifdef DYNAMIC_VAR | ||
| 11867 | vp->flags &= ~VDYNAMIC; | ||
| 11868 | #endif | ||
| 11869 | if (flags & VUNSET) | ||
| 11870 | goto ok; | ||
| 11871 | if ((flags & VSTRFIXED) == 0) { | ||
| 11872 | INT_OFF; | ||
| 11873 | if ((flags & (VTEXTFIXED|VSTACK)) == 0) | ||
| 11874 | free((char*)vp->text); | ||
| 11875 | *vpp = vp->next; | ||
| 11876 | free(vp); | ||
| 11877 | INT_ON; | ||
| 11878 | } else { | ||
| 11879 | setvar(s, 0, 0); | ||
| 11880 | vp->flags &= ~VEXPORT; | ||
| 11881 | } | ||
| 11882 | ok: | ||
| 11883 | retval = 0; | ||
| 11884 | } | ||
| 11885 | out: | ||
| 11886 | return retval; | ||
| 11887 | } | ||
| 11888 | |||
| 11889 | |||
| 11890 | /* | ||
| 11891 | * Find the appropriate entry in the hash table from the name. | ||
| 11892 | */ | ||
| 11893 | static struct var ** | ||
| 11894 | hashvar(const char *p) | ||
| 11895 | { | ||
| 11896 | unsigned int hashval; | ||
| 11897 | |||
| 11898 | hashval = ((unsigned char) *p) << 4; | ||
| 11899 | while (*p && *p != '=') | ||
| 11900 | hashval += (unsigned char) *p++; | ||
| 11901 | return &vartab[hashval % VTABSIZE]; | ||
| 11902 | } | ||
| 11903 | |||
| 11904 | |||
| 11905 | /* | ||
| 11906 | * Compares two strings up to the first = or '\0'. The first | ||
| 11907 | * string must be terminated by '='; the second may be terminated by | ||
| 11908 | * either '=' or '\0'. | ||
| 11909 | */ | ||
| 11910 | static int | ||
| 11911 | varcmp(const char *p, const char *q) | ||
| 11912 | { | ||
| 11913 | int c, d; | ||
| 11914 | |||
| 11915 | while ((c = *p) == (d = *q)) { | ||
| 11916 | if (!c || c == '=') | ||
| 11917 | goto out; | ||
| 11918 | p++; | ||
| 11919 | q++; | ||
| 11920 | } | ||
| 11921 | if (c == '=') | ||
| 11922 | c = 0; | ||
| 11923 | if (d == '=') | ||
| 11924 | d = 0; | ||
| 11925 | out: | ||
| 11926 | return c - d; | ||
| 11927 | } | ||
| 11928 | |||
| 11929 | static int | ||
| 11930 | vpcmp(const void *a, const void *b) | ||
| 11931 | { | ||
| 11932 | return varcmp(*(const char **)a, *(const char **)b); | ||
| 11933 | } | ||
| 11934 | |||
| 11935 | static struct var ** | ||
| 11936 | findvar(struct var **vpp, const char *name) | ||
| 11937 | { | ||
| 11938 | for (; *vpp; vpp = &(*vpp)->next) { | ||
| 11939 | if (varequal((*vpp)->text, name)) { | ||
| 11940 | break; | ||
| 11941 | } | ||
| 11942 | } | ||
| 11943 | return vpp; | ||
| 11944 | } | ||
| 11945 | /* setmode.c */ | 11847 | /* setmode.c */ |
| 11946 | 11848 | ||
| 11947 | #include <sys/times.h> | 11849 | #include <sys/times.h> |
| @@ -12274,7 +12176,7 @@ static int umaskcmd(int argc, char **argv) | |||
| 12274 | out1fmt("%.4o\n", mask); | 12176 | out1fmt("%.4o\n", mask); |
| 12275 | } | 12177 | } |
| 12276 | } else { | 12178 | } else { |
| 12277 | if (is_digit((unsigned char) *ap)) { | 12179 | if (isdigit((unsigned char) *ap)) { |
| 12278 | mask = 0; | 12180 | mask = 0; |
| 12279 | do { | 12181 | do { |
| 12280 | if (*ap >= '8' || *ap < '0') | 12182 | if (*ap >= '8' || *ap < '0') |
| @@ -13012,7 +12914,7 @@ static arith_t arith(const char *expr, int *perrcode) | |||
| 13012 | lasttok = TOK_NUM; | 12914 | lasttok = TOK_NUM; |
| 13013 | continue; | 12915 | continue; |
| 13014 | } | 12916 | } |
| 13015 | if (is_digit(arithval)) { | 12917 | if (isdigit(arithval)) { |
| 13016 | numstackptr->var = NULL; | 12918 | numstackptr->var = NULL; |
| 13017 | #if ENABLE_ASH_MATH_SUPPORT_64 | 12919 | #if ENABLE_ASH_MATH_SUPPORT_64 |
| 13018 | numstackptr->val = strtoll(expr, (char **) &expr, 0); | 12920 | numstackptr->val = strtoll(expr, (char **) &expr, 0); |
