diff options
author | vda <vda@69ca8d6d-28ef-0310-b511-8ec308f3f277> | 2007-02-23 01:04:22 +0000 |
---|---|---|
committer | vda <vda@69ca8d6d-28ef-0310-b511-8ec308f3f277> | 2007-02-23 01:04:22 +0000 |
commit | 2d9c143561b6cc2acdd6b48ac1ca358f0feca2a5 (patch) | |
tree | 7d6ec04eeef948208548e78e0041b49ce8f170e2 /shell/ash.c | |
parent | 439c291be500b06d5052c1dc8507382bac8676cb (diff) | |
download | busybox-w32-2d9c143561b6cc2acdd6b48ac1ca358f0feca2a5.tar.gz busybox-w32-2d9c143561b6cc2acdd6b48ac1ca358f0feca2a5.tar.bz2 busybox-w32-2d9c143561b6cc2acdd6b48ac1ca358f0feca2a5.zip |
ash: cleanup part 2
git-svn-id: svn://busybox.net/trunk/busybox@17954 69ca8d6d-28ef-0310-b511-8ec308f3f277
Diffstat (limited to 'shell/ash.c')
-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); |