1 /*
2  * options.c - shell options
3  *
4  * This file is part of zsh, the Z shell.
5  *
6  * Copyright (c) 1992-1997 Paul Falstad
7  * All rights reserved.
8  *
9  * Permission is hereby granted, without written agreement and without
10  * license or royalty fees, to use, copy, modify, and distribute this
11  * software and to distribute modified versions of this software for any
12  * purpose, provided that the above copyright notice and the following
13  * two paragraphs appear in all copies of this software.
14  *
15  * In no event shall Paul Falstad or the Zsh Development Group be liable
16  * to any party for direct, indirect, special, incidental, or consequential
17  * damages arising out of the use of this software and its documentation,
18  * even if Paul Falstad and the Zsh Development Group have been advised of
19  * the possibility of such damage.
20  *
21  * Paul Falstad and the Zsh Development Group specifically disclaim any
22  * warranties, including, but not limited to, the implied warranties of
23  * merchantability and fitness for a particular purpose.  The software
24  * provided hereunder is on an "as is" basis, and Paul Falstad and the
25  * Zsh Development Group have no obligation to provide maintenance,
26  * support, updates, enhancements, or modifications.
27  *
28  */
29 
30 #include "zsh.mdh"
31 #include "options.pro"
32 
33 /* current emulation (used to decide which set of option letters is used) */
34 
35 /**/
36 mod_export int emulation;
37 
38 /* current sticky emulation:  sticky = NULL means none */
39 
40 /**/
41 mod_export Emulation_options sticky;
42 
43 /* the options; e.g. if opts[SHGLOB] != 0, SH_GLOB is turned on */
44 
45 /**/
46 mod_export char opts[OPT_SIZE];
47 
48 /* Option name hash table */
49 
50 /**/
51 mod_export HashTable optiontab;
52 
53 /* The canonical option name table */
54 
55 #define OPT_CSH		EMULATE_CSH
56 #define OPT_KSH		EMULATE_KSH
57 #define OPT_SH		EMULATE_SH
58 #define OPT_ZSH		EMULATE_ZSH
59 
60 #define OPT_ALL		(OPT_CSH|OPT_KSH|OPT_SH|OPT_ZSH)
61 #define OPT_BOURNE	(OPT_KSH|OPT_SH)
62 #define OPT_BSHELL	(OPT_KSH|OPT_SH|OPT_ZSH)
63 #define OPT_NONBOURNE	(OPT_ALL & ~OPT_BOURNE)
64 #define OPT_NONZSH	(OPT_ALL & ~OPT_ZSH)
65 
66 /* option is relevant to emulation */
67 #define OPT_EMULATE	(EMULATE_UNUSED)
68 /* option should never be set by emulate() */
69 #define OPT_SPECIAL	(EMULATE_UNUSED<<1)
70 /* option is an alias to an other option */
71 #define OPT_ALIAS	(EMULATE_UNUSED<<2)
72 
73 #define defset(X, my_emulation) (!!((X)->node.flags & my_emulation))
74 
75 /*
76  * Note that option names should usually be fewer than 20 characters long
77  * to avoid formatting problems.
78  */
79 static struct optname optns[] = {
80 {{NULL, "aliases",	      OPT_EMULATE|OPT_ALL},	 ALIASESOPT},
81 {{NULL, "aliasfuncdef",       OPT_EMULATE|OPT_BOURNE},	 ALIASFUNCDEF},
82 {{NULL, "allexport",	      OPT_EMULATE},		 ALLEXPORT},
83 {{NULL, "alwayslastprompt",   OPT_ALL},			 ALWAYSLASTPROMPT},
84 {{NULL, "alwaystoend",	      0},			 ALWAYSTOEND},
85 {{NULL, "appendcreate",	      OPT_EMULATE|OPT_BOURNE},	 APPENDCREATE},
86 {{NULL, "appendhistory",      OPT_ALL},			 APPENDHISTORY},
87 {{NULL, "autocd",	      OPT_EMULATE},		 AUTOCD},
88 {{NULL, "autocontinue",	      0},			 AUTOCONTINUE},
89 {{NULL, "autolist",	      OPT_ALL},			 AUTOLIST},
90 {{NULL, "automenu",	      OPT_ALL},			 AUTOMENU},
91 {{NULL, "autonamedirs",	      0},			 AUTONAMEDIRS},
92 {{NULL, "autoparamkeys",      OPT_ALL},			 AUTOPARAMKEYS},
93 {{NULL, "autoparamslash",     OPT_ALL},			 AUTOPARAMSLASH},
94 {{NULL, "autopushd",	      0},			 AUTOPUSHD},
95 {{NULL, "autoremoveslash",    OPT_ALL},			 AUTOREMOVESLASH},
96 {{NULL, "autoresume",	      0},			 AUTORESUME},
97 {{NULL, "badpattern",	      OPT_EMULATE|OPT_NONBOURNE},BADPATTERN},
98 {{NULL, "banghist",	      OPT_NONBOURNE},		 BANGHIST},
99 {{NULL, "bareglobqual",       OPT_EMULATE|OPT_ZSH},      BAREGLOBQUAL},
100 {{NULL, "bashautolist",	      0},                        BASHAUTOLIST},
101 {{NULL, "bashrematch",	      0},			 BASHREMATCH},
102 {{NULL, "beep",		      OPT_ALL},			 BEEP},
103 {{NULL, "bgnice",	      OPT_EMULATE|OPT_NONBOURNE},BGNICE},
104 {{NULL, "braceccl",	      OPT_EMULATE},		 BRACECCL},
105 {{NULL, "bsdecho",	      OPT_EMULATE|OPT_SH},	 BSDECHO},
106 {{NULL, "caseglob",	      OPT_ALL},			 CASEGLOB},
107 {{NULL, "casematch",	      OPT_ALL},			 CASEMATCH},
108 {{NULL, "cbases",	      0},			 CBASES},
109 {{NULL, "cprecedences",	      OPT_EMULATE|OPT_NONZSH},	 CPRECEDENCES},
110 {{NULL, "cdablevars",	      OPT_EMULATE},		 CDABLEVARS},
111 {{NULL, "cdsilent",	      0},			 CDSILENT},
112 {{NULL, "chasedots",	      OPT_EMULATE},		 CHASEDOTS},
113 {{NULL, "chaselinks",	      OPT_EMULATE},		 CHASELINKS},
114 {{NULL, "checkjobs",	      OPT_EMULATE|OPT_ZSH},	 CHECKJOBS},
115 {{NULL, "checkrunningjobs",   OPT_EMULATE|OPT_ZSH},	 CHECKRUNNINGJOBS},
116 {{NULL, "clobber",	      OPT_EMULATE|OPT_ALL},	 CLOBBER},
117 {{NULL, "combiningchars",     0},			 COMBININGCHARS},
118 {{NULL, "completealiases",    0},			 COMPLETEALIASES},
119 {{NULL, "completeinword",     0},			 COMPLETEINWORD},
120 {{NULL, "continueonerror",    0},                        CONTINUEONERROR},
121 {{NULL, "correct",	      0},			 CORRECT},
122 {{NULL, "correctall",	      0},			 CORRECTALL},
123 {{NULL, "cshjunkiehistory",   OPT_EMULATE|OPT_CSH},	 CSHJUNKIEHISTORY},
124 {{NULL, "cshjunkieloops",     OPT_EMULATE|OPT_CSH},	 CSHJUNKIELOOPS},
125 {{NULL, "cshjunkiequotes",    OPT_EMULATE|OPT_CSH},	 CSHJUNKIEQUOTES},
126 {{NULL, "cshnullcmd",	      OPT_EMULATE|OPT_CSH},	 CSHNULLCMD},
127 {{NULL, "cshnullglob",	      OPT_EMULATE|OPT_CSH},	 CSHNULLGLOB},
128 {{NULL, "debugbeforecmd",     OPT_ALL},			 DEBUGBEFORECMD},
129 {{NULL, "emacs",	      0},			 EMACSMODE},
130 {{NULL, "equals",	      OPT_EMULATE|OPT_ZSH},	 EQUALS},
131 {{NULL, "errexit",	      OPT_EMULATE},		 ERREXIT},
132 {{NULL, "errreturn",	      OPT_EMULATE},		 ERRRETURN},
133 {{NULL, "exec",		      OPT_ALL},			 EXECOPT},
134 {{NULL, "extendedglob",	      OPT_EMULATE},		 EXTENDEDGLOB},
135 {{NULL, "extendedhistory",    OPT_CSH},			 EXTENDEDHISTORY},
136 {{NULL, "evallineno",	      OPT_EMULATE|OPT_ZSH},	 EVALLINENO},
137 {{NULL, "flowcontrol",	      OPT_ALL},			 FLOWCONTROL},
138 {{NULL, "forcefloat",         0},                        FORCEFLOAT},
139 {{NULL, "functionargzero",    OPT_EMULATE|OPT_NONBOURNE},FUNCTIONARGZERO},
140 {{NULL, "glob",		      OPT_EMULATE|OPT_ALL},	 GLOBOPT},
141 {{NULL, "globalexport",       OPT_EMULATE|OPT_ZSH},	 GLOBALEXPORT},
142 {{NULL, "globalrcs",          OPT_ALL},			 GLOBALRCS},
143 {{NULL, "globassign",	      OPT_EMULATE|OPT_CSH},	 GLOBASSIGN},
144 {{NULL, "globcomplete",	      0},			 GLOBCOMPLETE},
145 {{NULL, "globdots",	      OPT_EMULATE},		 GLOBDOTS},
146 {{NULL, "globstarshort",      OPT_EMULATE},		 GLOBSTARSHORT},
147 {{NULL, "globsubst",	      OPT_EMULATE|OPT_NONZSH},	 GLOBSUBST},
148 {{NULL, "hashcmds",	      OPT_ALL},			 HASHCMDS},
149 {{NULL, "hashdirs",	      OPT_ALL},			 HASHDIRS},
150 {{NULL, "hashexecutablesonly", 0},                       HASHEXECUTABLESONLY},
151 {{NULL, "hashlistall",	      OPT_ALL},			 HASHLISTALL},
152 {{NULL, "histallowclobber",   0},			 HISTALLOWCLOBBER},
153 {{NULL, "histbeep",	      OPT_ALL},			 HISTBEEP},
154 {{NULL, "histexpiredupsfirst",0},			 HISTEXPIREDUPSFIRST},
155 {{NULL, "histfcntllock",      0},			 HISTFCNTLLOCK},
156 {{NULL, "histfindnodups",     0},			 HISTFINDNODUPS},
157 {{NULL, "histignorealldups",  0},			 HISTIGNOREALLDUPS},
158 {{NULL, "histignoredups",     0},			 HISTIGNOREDUPS},
159 {{NULL, "histignorespace",    0},			 HISTIGNORESPACE},
160 {{NULL, "histlexwords",	      0},			 HISTLEXWORDS},
161 {{NULL, "histnofunctions",    0},			 HISTNOFUNCTIONS},
162 {{NULL, "histnostore",	      0},			 HISTNOSTORE},
163 {{NULL, "histsubstpattern",   OPT_EMULATE},              HISTSUBSTPATTERN},
164 {{NULL, "histreduceblanks",   0},			 HISTREDUCEBLANKS},
165 {{NULL, "histsavebycopy",     OPT_ALL},			 HISTSAVEBYCOPY},
166 {{NULL, "histsavenodups",     0},			 HISTSAVENODUPS},
167 {{NULL, "histverify",	      0},			 HISTVERIFY},
168 {{NULL, "hup",		      OPT_EMULATE|OPT_ZSH},	 HUP},
169 {{NULL, "ignorebraces",	      OPT_EMULATE|OPT_SH},	 IGNOREBRACES},
170 {{NULL, "ignoreclosebraces",  OPT_EMULATE},		 IGNORECLOSEBRACES},
171 {{NULL, "ignoreeof",	      0},			 IGNOREEOF},
172 {{NULL, "incappendhistory",   0},			 INCAPPENDHISTORY},
173 {{NULL, "incappendhistorytime",   0},			 INCAPPENDHISTORYTIME},
174 {{NULL, "interactive",	      OPT_SPECIAL},		 INTERACTIVE},
175 {{NULL, "interactivecomments",OPT_BOURNE},		 INTERACTIVECOMMENTS},
176 {{NULL, "ksharrays",	      OPT_EMULATE|OPT_BOURNE},	 KSHARRAYS},
177 {{NULL, "kshautoload",	      OPT_EMULATE|OPT_BOURNE},	 KSHAUTOLOAD},
178 {{NULL, "kshglob",	      OPT_EMULATE|OPT_KSH},	 KSHGLOB},
179 {{NULL, "kshoptionprint",     OPT_EMULATE|OPT_KSH},	 KSHOPTIONPRINT},
180 {{NULL, "kshtypeset",	      0},			 KSHTYPESET},
181 {{NULL, "kshzerosubscript",   0},			 KSHZEROSUBSCRIPT},
182 {{NULL, "listambiguous",      OPT_ALL},			 LISTAMBIGUOUS},
183 {{NULL, "listbeep",	      OPT_ALL},			 LISTBEEP},
184 {{NULL, "listpacked",	      0},			 LISTPACKED},
185 {{NULL, "listrowsfirst",      0},			 LISTROWSFIRST},
186 {{NULL, "listtypes",	      OPT_ALL},			 LISTTYPES},
187 {{NULL, "localoptions",	      OPT_EMULATE|OPT_KSH},	 LOCALOPTIONS},
188 {{NULL, "localloops",	      OPT_EMULATE},		 LOCALLOOPS},
189 {{NULL, "localpatterns",      OPT_EMULATE},		 LOCALPATTERNS},
190 {{NULL, "localtraps",	      OPT_EMULATE|OPT_KSH},	 LOCALTRAPS},
191 {{NULL, "login",	      OPT_SPECIAL},		 LOGINSHELL},
192 {{NULL, "longlistjobs",	      0},			 LONGLISTJOBS},
193 {{NULL, "magicequalsubst",    OPT_EMULATE},		 MAGICEQUALSUBST},
194 {{NULL, "mailwarning",	      0},			 MAILWARNING},
195 {{NULL, "markdirs",	      0},			 MARKDIRS},
196 {{NULL, "menucomplete",	      0},			 MENUCOMPLETE},
197 {{NULL, "monitor",	      OPT_SPECIAL},		 MONITOR},
198 {{NULL, "multibyte",
199 #ifdef MULTIBYTE_SUPPORT
200 			      OPT_ALL
201 #else
202 			      0
203 #endif
204 			      },			 MULTIBYTE},
205 {{NULL, "multifuncdef",	      OPT_EMULATE|OPT_ZSH},	 MULTIFUNCDEF},
206 {{NULL, "multios",	      OPT_EMULATE|OPT_ZSH},	 MULTIOS},
207 {{NULL, "nomatch",	      OPT_EMULATE|OPT_NONBOURNE},NOMATCH},
208 {{NULL, "notify",	      OPT_ZSH},			 NOTIFY},
209 {{NULL, "nullglob",	      OPT_EMULATE},		 NULLGLOB},
210 {{NULL, "numericglobsort",    OPT_EMULATE},		 NUMERICGLOBSORT},
211 {{NULL, "octalzeroes",        OPT_EMULATE|OPT_SH},	 OCTALZEROES},
212 {{NULL, "overstrike",	      0},			 OVERSTRIKE},
213 {{NULL, "pathdirs",	      OPT_EMULATE},		 PATHDIRS},
214 {{NULL, "pathscript",	      OPT_EMULATE|OPT_BOURNE},	 PATHSCRIPT},
215 {{NULL, "pipefail",           OPT_EMULATE},              PIPEFAIL},
216 {{NULL, "posixaliases",       OPT_EMULATE|OPT_BOURNE},	 POSIXALIASES},
217 {{NULL, "posixargzero",       OPT_EMULATE},              POSIXARGZERO},
218 {{NULL, "posixbuiltins",      OPT_EMULATE|OPT_BOURNE},	 POSIXBUILTINS},
219 {{NULL, "posixcd",            OPT_EMULATE|OPT_BOURNE},	 POSIXCD},
220 {{NULL, "posixidentifiers",   OPT_EMULATE|OPT_BOURNE},	 POSIXIDENTIFIERS},
221 {{NULL, "posixjobs",          OPT_EMULATE|OPT_BOURNE},	 POSIXJOBS},
222 {{NULL, "posixstrings",       OPT_EMULATE|OPT_BOURNE},   POSIXSTRINGS},
223 {{NULL, "posixtraps",         OPT_EMULATE|OPT_BOURNE},	 POSIXTRAPS},
224 {{NULL, "printeightbit",      0},                        PRINTEIGHTBIT},
225 {{NULL, "printexitvalue",     0},			 PRINTEXITVALUE},
226 {{NULL, "privileged",	      OPT_SPECIAL},		 PRIVILEGED},
227 {{NULL, "promptbang",	      OPT_KSH},			 PROMPTBANG},
228 {{NULL, "promptcr",	      OPT_ALL},			 PROMPTCR},
229 {{NULL, "promptpercent",      OPT_NONBOURNE},		 PROMPTPERCENT},
230 {{NULL, "promptsp",	      OPT_ALL},			 PROMPTSP},
231 {{NULL, "promptsubst",	      OPT_BOURNE},		 PROMPTSUBST},
232 {{NULL, "pushdignoredups",    OPT_EMULATE},		 PUSHDIGNOREDUPS},
233 {{NULL, "pushdminus",	      OPT_EMULATE},		 PUSHDMINUS},
234 {{NULL, "pushdsilent",	      0},			 PUSHDSILENT},
235 {{NULL, "pushdtohome",	      OPT_EMULATE},		 PUSHDTOHOME},
236 {{NULL, "rcexpandparam",      OPT_EMULATE},		 RCEXPANDPARAM},
237 {{NULL, "rcquotes",	      OPT_EMULATE},		 RCQUOTES},
238 {{NULL, "rcs",		      OPT_ALL},			 RCS},
239 {{NULL, "recexact",	      0},			 RECEXACT},
240 {{NULL, "rematchpcre",	      0},			 REMATCHPCRE},
241 {{NULL, "restricted",	      OPT_SPECIAL},		 RESTRICTED},
242 {{NULL, "rmstarsilent",	      OPT_BOURNE},		 RMSTARSILENT},
243 {{NULL, "rmstarwait",	      0},			 RMSTARWAIT},
244 {{NULL, "sharehistory",	      OPT_KSH},			 SHAREHISTORY},
245 {{NULL, "shfileexpansion",    OPT_EMULATE|OPT_BOURNE},	 SHFILEEXPANSION},
246 {{NULL, "shglob",	      OPT_EMULATE|OPT_BOURNE},	 SHGLOB},
247 {{NULL, "shinstdin",	      OPT_SPECIAL},		 SHINSTDIN},
248 {{NULL, "shnullcmd",          OPT_EMULATE|OPT_BOURNE},	 SHNULLCMD},
249 {{NULL, "shoptionletters",    OPT_EMULATE|OPT_BOURNE},	 SHOPTIONLETTERS},
250 {{NULL, "shortloops",	      OPT_EMULATE|OPT_NONBOURNE},SHORTLOOPS},
251 {{NULL, "shwordsplit",	      OPT_EMULATE|OPT_BOURNE},	 SHWORDSPLIT},
252 {{NULL, "singlecommand",      OPT_SPECIAL},		 SINGLECOMMAND},
253 {{NULL, "singlelinezle",      OPT_KSH},			 SINGLELINEZLE},
254 {{NULL, "sourcetrace",        0},			 SOURCETRACE},
255 {{NULL, "sunkeyboardhack",    0},			 SUNKEYBOARDHACK},
256 {{NULL, "transientrprompt",   0},			 TRANSIENTRPROMPT},
257 {{NULL, "trapsasync",	      0},			 TRAPSASYNC},
258 {{NULL, "typesetsilent",      OPT_EMULATE|OPT_BOURNE},	 TYPESETSILENT},
259 {{NULL, "unset",	      OPT_EMULATE|OPT_BSHELL},	 UNSET},
260 {{NULL, "verbose",	      0},			 VERBOSE},
261 {{NULL, "vi",		      0},			 VIMODE},
262 {{NULL, "warncreateglobal",   OPT_EMULATE},		 WARNCREATEGLOBAL},
263 {{NULL, "warnnestedvar",      OPT_EMULATE},		 WARNNESTEDVAR},
264 {{NULL, "xtrace",	      0},			 XTRACE},
265 {{NULL, "zle",		      OPT_SPECIAL},		 USEZLE},
266 {{NULL, "braceexpand",	      OPT_ALIAS}, /* ksh/bash */ -IGNOREBRACES},
267 {{NULL, "dotglob",	      OPT_ALIAS}, /* bash */	 GLOBDOTS},
268 {{NULL, "hashall",	      OPT_ALIAS}, /* bash */	 HASHCMDS},
269 {{NULL, "histappend",	      OPT_ALIAS}, /* bash */	 APPENDHISTORY},
270 {{NULL, "histexpand",	      OPT_ALIAS}, /* bash */	 BANGHIST},
271 {{NULL, "log",		      OPT_ALIAS}, /* ksh */	 -HISTNOFUNCTIONS},
272 {{NULL, "mailwarn",	      OPT_ALIAS}, /* bash */	 MAILWARNING},
273 {{NULL, "onecmd",	      OPT_ALIAS}, /* bash */	 SINGLECOMMAND},
274 {{NULL, "physical",	      OPT_ALIAS}, /* ksh/bash */ CHASELINKS},
275 {{NULL, "promptvars",	      OPT_ALIAS}, /* bash */	 PROMPTSUBST},
276 {{NULL, "stdin",	      OPT_ALIAS}, /* ksh */	 SHINSTDIN},
277 {{NULL, "trackall",	      OPT_ALIAS}, /* ksh */	 HASHCMDS},
278 {{NULL, "dvorak",	      0},			 DVORAK},
279 {{NULL, NULL, 0}, 0}
280 };
281 
282 /* Option letters */
283 
284 #define optletters (isset(SHOPTIONLETTERS) ? kshletters : zshletters)
285 
286 #define FIRST_OPT '0'
287 #define LAST_OPT 'y'
288 
289 static short zshletters[LAST_OPT - FIRST_OPT + 1] = {
290     /* 0 */  CORRECT,
291     /* 1 */  PRINTEXITVALUE,
292     /* 2 */ -BADPATTERN,
293     /* 3 */ -NOMATCH,
294     /* 4 */  GLOBDOTS,
295     /* 5 */  NOTIFY,
296     /* 6 */  BGNICE,
297     /* 7 */  IGNOREEOF,
298     /* 8 */  MARKDIRS,
299     /* 9 */  AUTOLIST,
300     /* : */  0,
301     /* ; */  0,
302     /* < */  0,
303     /* = */  0,
304     /* > */  0,
305     /* ? */  0,
306     /* @ */  0,
307     /* A */  0,			/* use with set for arrays */
308     /* B */ -BEEP,
309     /* C */ -CLOBBER,
310     /* D */  PUSHDTOHOME,
311     /* E */  PUSHDSILENT,
312     /* F */ -GLOBOPT,
313     /* G */  NULLGLOB,
314     /* H */  RMSTARSILENT,
315     /* I */  IGNOREBRACES,
316     /* J */  AUTOCD,
317     /* K */ -BANGHIST,
318     /* L */  SUNKEYBOARDHACK,
319     /* M */  SINGLELINEZLE,
320     /* N */  AUTOPUSHD,
321     /* O */  CORRECTALL,
322     /* P */  RCEXPANDPARAM,
323     /* Q */  PATHDIRS,
324     /* R */  LONGLISTJOBS,
325     /* S */  RECEXACT,
326     /* T */  CDABLEVARS,
327     /* U */  MAILWARNING,
328     /* V */ -PROMPTCR,
329     /* W */  AUTORESUME,
330     /* X */  LISTTYPES,
331     /* Y */  MENUCOMPLETE,
332     /* Z */  USEZLE,
333     /* [ */  0,
334     /* \ */  0,
335     /* ] */  0,
336     /* ^ */  0,
337     /* _ */  0,
338     /* ` */  0,
339     /* a */  ALLEXPORT,
340     /* b */  0,			/* in non-Bourne shells, end of options */
341     /* c */  0,			/* command follows */
342     /* d */ -GLOBALRCS,
343     /* e */  ERREXIT,
344     /* f */ -RCS,
345     /* g */  HISTIGNORESPACE,
346     /* h */  HISTIGNOREDUPS,
347     /* i */  INTERACTIVE,
348     /* j */  0,
349     /* k */  INTERACTIVECOMMENTS,
350     /* l */  LOGINSHELL,
351     /* m */  MONITOR,
352     /* n */ -EXECOPT,
353     /* o */  0,			/* long option name follows */
354     /* p */  PRIVILEGED,
355     /* q */  0,
356     /* r */  RESTRICTED,
357     /* s */  SHINSTDIN,
358     /* t */  SINGLECOMMAND,
359     /* u */ -UNSET,
360     /* v */  VERBOSE,
361     /* w */  CHASELINKS,
362     /* x */  XTRACE,
363     /* y */  SHWORDSPLIT,
364 };
365 
366 static short kshletters[LAST_OPT - FIRST_OPT + 1] = {
367     /* 0 */  0,
368     /* 1 */  0,
369     /* 2 */  0,
370     /* 3 */  0,
371     /* 4 */  0,
372     /* 5 */  0,
373     /* 6 */  0,
374     /* 7 */  0,
375     /* 8 */  0,
376     /* 9 */  0,
377     /* : */  0,
378     /* ; */  0,
379     /* < */  0,
380     /* = */  0,
381     /* > */  0,
382     /* ? */  0,
383     /* @ */  0,
384     /* A */  0,
385     /* B */  0,
386     /* C */ -CLOBBER,
387     /* D */  0,
388     /* E */  0,
389     /* F */  0,
390     /* G */  0,
391     /* H */  0,
392     /* I */  0,
393     /* J */  0,
394     /* K */  0,
395     /* L */  0,
396     /* M */  0,
397     /* N */  0,
398     /* O */  0,
399     /* P */  0,
400     /* Q */  0,
401     /* R */  0,
402     /* S */  0,
403     /* T */  TRAPSASYNC,
404     /* U */  0,
405     /* V */  0,
406     /* W */  0,
407     /* X */  MARKDIRS,
408     /* Y */  0,
409     /* Z */  0,
410     /* [ */  0,
411     /* \ */  0,
412     /* ] */  0,
413     /* ^ */  0,
414     /* _ */  0,
415     /* ` */  0,
416     /* a */  ALLEXPORT,
417     /* b */  NOTIFY,
418     /* c */  0,
419     /* d */  0,
420     /* e */  ERREXIT,
421     /* f */ -GLOBOPT,
422     /* g */  0,
423     /* h */  0,
424     /* i */  INTERACTIVE,
425     /* j */  0,
426     /* k */  0,
427     /* l */  LOGINSHELL,
428     /* m */  MONITOR,
429     /* n */ -EXECOPT,
430     /* o */  0,
431     /* p */  PRIVILEGED,
432     /* q */  0,
433     /* r */  RESTRICTED,
434     /* s */  SHINSTDIN,
435     /* t */  SINGLECOMMAND,
436     /* u */ -UNSET,
437     /* v */  VERBOSE,
438     /* w */  0,
439     /* x */  XTRACE,
440     /* y */  0,
441 };
442 
443 /* Initialisation of the option name hash table */
444 
445 /**/
446 static void
printoptionnode(HashNode hn,int set)447 printoptionnode(HashNode hn, int set)
448 {
449     Optname on = (Optname) hn;
450     int optno = on->optno;
451 
452     if (optno < 0)
453 	optno = -optno;
454     if (isset(KSHOPTIONPRINT)) {
455 	if (defset(on, emulation))
456 	    printf("no%-19s %s\n", on->node.nam, isset(optno) ? "off" : "on");
457 	else
458 	    printf("%-21s %s\n", on->node.nam, isset(optno) ? "on" : "off");
459     } else if (set == (isset(optno) ^ defset(on, emulation))) {
460 	if (set ^ isset(optno))
461 	    fputs("no", stdout);
462 	puts(on->node.nam);
463     }
464 }
465 
466 /**/
467 void
createoptiontable(void)468 createoptiontable(void)
469 {
470     Optname on;
471 
472     optiontab = newhashtable(101, "optiontab", NULL);
473 
474     optiontab->hash        = hasher;
475     optiontab->emptytable  = NULL;
476     optiontab->filltable   = NULL;
477     optiontab->cmpnodes    = strcmp;
478     optiontab->addnode     = addhashnode;
479     optiontab->getnode     = gethashnode;
480     optiontab->getnode2    = gethashnode2;
481     optiontab->removenode  = NULL;
482     optiontab->disablenode = disablehashnode;
483     optiontab->enablenode  = enablehashnode;
484     optiontab->freenode    = NULL;
485     optiontab->printnode   = printoptionnode;
486 
487     for (on = optns; on->node.nam; on++)
488 	optiontab->addnode(optiontab, on->node.nam, on);
489 }
490 
491 /* Emulation appropriate to the setemulate function */
492 
493 static int setemulate_emulation;
494 
495 /* Option array manipulated within the setemulate function */
496 
497 /**/
498 static char *setemulate_opts;
499 
500 /* Setting of default options */
501 
502 /**/
503 static void
setemulate(HashNode hn,int fully)504 setemulate(HashNode hn, int fully)
505 {
506     Optname on = (Optname) hn;
507 
508     /* Set options: each non-special option is set according to the *
509      * current emulation mode if either it is considered relevant   *
510      * to emulation or we are doing a full emulation (as indicated  *
511      * by the `fully' parameter).                                   */
512     if (!(on->node.flags & OPT_ALIAS) &&
513 	((fully && !(on->node.flags & OPT_SPECIAL)) ||
514 	 (on->node.flags & OPT_EMULATE)))
515 	setemulate_opts[on->optno] = defset(on, setemulate_emulation);
516 }
517 
518 /**/
519 void
installemulation(int new_emulation,char * new_opts)520 installemulation(int new_emulation, char *new_opts)
521 {
522     setemulate_emulation = new_emulation;
523     setemulate_opts = new_opts;
524     scanhashtable(optiontab, 0, 0, 0, setemulate,
525 		  !!(new_emulation & EMULATE_FULLY));
526 }
527 
528 /**/
529 void
emulate(const char * zsh_name,int fully,int * new_emulation,char * new_opts)530 emulate(const char *zsh_name, int fully, int *new_emulation, char *new_opts)
531 {
532     char ch = *zsh_name;
533 
534     if (ch == 'r')
535 	ch = zsh_name[1];
536 
537     /* Work out the new emulation mode */
538     if (ch == 'c')
539 	*new_emulation = EMULATE_CSH;
540     else if (ch == 'k')
541 	*new_emulation = EMULATE_KSH;
542     else if (ch == 's' || ch == 'b')
543 	*new_emulation = EMULATE_SH;
544     else
545 	*new_emulation = EMULATE_ZSH;
546 
547     if (fully)
548 	*new_emulation |= EMULATE_FULLY;
549     installemulation(*new_emulation, new_opts);
550 
551     if (funcstack && funcstack->tp == FS_FUNC) {
552 	/*
553 	 * We are inside a function.  Decide if it's traced.
554 	 * Pedantic note: the function in the function table isn't
555 	 * guaranteed to be what we're executing, but it's
556 	 * close enough.
557 	 */
558 	Shfunc shf = (Shfunc)shfunctab->getnode(shfunctab, funcstack->name);
559 	if (shf && (shf->node.flags & (PM_TAGGED|PM_TAGGED_LOCAL))) {
560 	    /* Tracing is on, so set xtrace */
561 	    new_opts[XTRACE] = 1;
562 	}
563     }
564 }
565 
566 /* setopt, unsetopt */
567 
568 /**/
569 static void
setoption(HashNode hn,int value)570 setoption(HashNode hn, int value)
571 {
572     dosetopt(((Optname) hn)->optno, value, 0, opts);
573 }
574 
575 /**/
576 int
bin_setopt(char * nam,char ** args,UNUSED (Options ops),int isun)577 bin_setopt(char *nam, char **args, UNUSED(Options ops), int isun)
578 {
579     int action, optno, match = 0;
580     int retval = 0;
581 
582     /* With no arguments or options, display options. */
583     if (!*args) {
584 	scanhashtable(optiontab, 1, 0, OPT_ALIAS, optiontab->printnode, !isun);
585 	return 0;
586     }
587 
588     /* loop through command line options (begins with "-" or "+") */
589     while (*args && (**args == '-' || **args == '+')) {
590 	action = (**args == '-') ^ isun;
591 	if(!args[0][1])
592 	    *args = "--";
593 	while (*++*args) {
594 	    if(**args == Meta)
595 		*++*args ^= 32;
596 	    /* The pseudo-option `--' signifies the end of options. */
597 	    if (**args == '-') {
598 		args++;
599 		goto doneoptions;
600 	    } else if (**args == 'o') {
601 		if (!*++*args)
602 		    args++;
603 		if (!*args) {
604 		    zwarnnam(nam, "string expected after -o");
605 		    inittyptab();
606 		    return 1;
607 		}
608 		if(!(optno = optlookup(*args))) {
609 		    zwarnnam(nam, "no such option: %s", *args);
610 		    retval |= 1;
611 		} else if (dosetopt(optno, action, 0, opts)) {
612 		    zwarnnam(nam, "can't change option: %s", *args);
613 		    retval |= 1;
614 		}
615 		break;
616 	    } else if(**args == 'm') {
617 		match = 1;
618 	    } else {
619 		if (!(optno = optlookupc(**args))) {
620 		    zwarnnam(nam, "bad option: -%c", **args);
621 		    retval |= 1;
622 		} else if (dosetopt(optno, action, 0, opts)) {
623 		    zwarnnam(nam, "can't change option: -%c", **args);
624 		    retval |= 1;
625 		}
626 	    }
627 	}
628 	args++;
629     }
630     doneoptions:
631 
632     if (!match) {
633 	/* Not globbing the arguments -- arguments are simply option names. */
634 	while (*args) {
635 	    if(!(optno = optlookup(*args++))) {
636 		zwarnnam(nam, "no such option: %s", args[-1]);
637 		retval |= 1;
638 	    } else if (dosetopt(optno, !isun, 0, opts)) {
639 		zwarnnam(nam, "can't change option: %s", args[-1]);
640 		retval |= 1;
641 	    }
642 	}
643     } else {
644 	/* Globbing option (-m) set. */
645 	while (*args) {
646 	    Patprog pprog;
647 	    char *s, *t;
648 
649 	    t = s = dupstring(*args);
650 	    while (*t)
651 		if (*t == '_')
652 		    chuck(t);
653 		else {
654 		    /* See comment in optlookup() */
655 		    if (*t >= 'A' && *t <= 'Z')
656 			*t = (*t - 'A') + 'a';
657 		    t++;
658 		}
659 
660 	    /* Expand the current arg. */
661 	    tokenize(s);
662 	    if (!(pprog = patcompile(s, PAT_HEAPDUP, NULL))) {
663 		zwarnnam(nam, "bad pattern: %s", *args);
664 		retval |= 1;
665 		break;
666 	    }
667 	    /* Loop over expansions. */
668 	    scanmatchtable(optiontab, pprog, 0, 0, OPT_ALIAS,
669 			   setoption, !isun);
670 	    args++;
671 	}
672     }
673     inittyptab();
674     return retval;
675 }
676 
677 /* Identify an option name */
678 
679 /**/
680 mod_export int
optlookup(char const * name)681 optlookup(char const *name)
682 {
683     char *s, *t;
684     Optname n;
685 
686     s = t = dupstring(name);
687 
688     /* exorcise underscores, and change to lowercase */
689     while (*t)
690 	if (*t == '_')
691 	    chuck(t);
692 	else {
693 	    /*
694 	     * Some locales (in particular tr_TR.UTF-8) may
695 	     * have non-standard mappings of ASCII characters,
696 	     * so be careful.  Option names must be ASCII so
697 	     * we don't need to be too clever.
698 	     */
699 	    if (*t >= 'A' && *t <= 'Z')
700 		*t = (*t - 'A') + 'a';
701 	    t++;
702 	}
703 
704     /* look up name in the table */
705     if (s[0] == 'n' && s[1] == 'o' &&
706 	(n = (Optname) optiontab->getnode(optiontab, s + 2))) {
707 	return -n->optno;
708     } else if ((n = (Optname) optiontab->getnode(optiontab, s)))
709 	return n->optno;
710     else
711 	return OPT_INVALID;
712 }
713 
714 /* Identify an option letter */
715 
716 /**/
717 int
optlookupc(char c)718 optlookupc(char c)
719 {
720     if(c < FIRST_OPT || c > LAST_OPT)
721 	return 0;
722 
723     return optletters[c - FIRST_OPT];
724 }
725 
726 /**/
727 static void
restrictparam(char * nam)728 restrictparam(char *nam)
729 {
730     Param pm = (Param) paramtab->getnode(paramtab, nam);
731 
732     if (pm) {
733 	pm->node.flags |= PM_SPECIAL | PM_RESTRICTED;
734 	return;
735     }
736     createparam(nam, PM_SCALAR | PM_UNSET | PM_SPECIAL | PM_RESTRICTED);
737 }
738 
739 /* list of restricted parameters which are not otherwise special */
740 static char *rparams[] = {
741     "SHELL", "HISTFILE", "LD_LIBRARY_PATH", "LD_AOUT_LIBRARY_PATH",
742     "LD_PRELOAD", "LD_AOUT_PRELOAD", NULL
743 };
744 
745 /* Set or unset an option, as a result of user request.  The option *
746  * number may be negative, indicating that the sense is reversed    *
747  * from the usual meaning of the option.                            */
748 
749 /**/
750 mod_export int
dosetopt(int optno,int value,int force,char * new_opts)751 dosetopt(int optno, int value, int force, char *new_opts)
752 {
753     if(!optno)
754 	return -1;
755     if(optno < 0) {
756 	optno = -optno;
757 	value = !value;
758     }
759     if (optno == RESTRICTED) {
760 	if (isset(RESTRICTED))
761 	    return value ? 0 : -1;
762 	if (value) {
763 	    char **s;
764 
765 	    for (s = rparams; *s; s++)
766 		restrictparam(*s);
767 	}
768     } else if(!force && optno == EXECOPT && !value && interact) {
769 	/* cannot set noexec when interactive */
770 	return -1;
771     } else if(!force && (optno == INTERACTIVE || optno == SHINSTDIN ||
772 	    optno == SINGLECOMMAND)) {
773 	if (new_opts[optno] == value)
774 	    return 0;
775 	/* it is not permitted to change the value of these options */
776 	return -1;
777     } else if(!force && optno == USEZLE && value) {
778 	/* we require a terminal in order to use ZLE */
779 	if(!interact || SHTTY == -1 || !shout)
780 	    return -1;
781     } else if(optno == PRIVILEGED && !value) {
782 	/* unsetting PRIVILEGED causes the shell to make itself unprivileged */
783 
784 /* For simplicity's sake, require both setresgid() and setresuid() up-front. */
785 #if !defined(HAVE_SETRESGID)
786 	zwarnnam("unsetopt",
787 	    "PRIVILEGED: can't drop privileges; setresgid() and friends not available");
788 	return -1;
789 #elif !defined(HAVE_SETRESUID)
790 	zwarnnam("unsetopt",
791 	    "PRIVILEGED: can't drop privileges; setresuid() and friends not available");
792 	return -1;
793 #else
794 	/* If set, return -1 so lastval will be non-zero. */
795 	int failed = 0;
796 	const int orig_euid = geteuid();
797 	const int orig_egid = getegid();
798 
799 	/*
800 	 * Set the GID first as if we set the UID to non-privileged it
801 	 * might be impossible to restore the GID.
802 	 */
803 	if (setresgid(getgid(), getgid(), getgid())) {
804 	    zwarnnam("unsetopt",
805 		"PRIVILEGED: can't drop privileges; failed to change group ID: %e",
806 		errno);
807 	    return -1;
808 	}
809 
810 # ifdef HAVE_INITGROUPS
811 	/* Set the supplementary groups list.
812 	 *
813 	 * Note that on macOS, FreeBSD, and possibly some other platforms,
814 	 * initgroups() resets the EGID to its second argument (see setgroups(2) for
815 	 * details). This has the potential to leave the EGID in an unexpected
816 	 * state. However, it seems common in other projects that do this dance to
817 	 * simply re-use the same GID that's going to become the EGID anyway, in
818 	 * which case it doesn't matter. That's what we do here. It's therefore
819 	 * possible, in some probably uncommon cases, that the shell ends up not
820 	 * having the privileges of the RUID user's primary/passwd group. */
821 	if (geteuid() == 0) {
822 	    struct passwd *pw = getpwuid(getuid());
823 	    if (pw == NULL) {
824 		zwarnnam("unsetopt",
825 		    "can't drop privileges; failed to get user information for uid %L: %e",
826 		    (long)getuid(), errno);
827 		failed = 1;
828 	    /* This may behave strangely in the unlikely event that the same user
829 	     * name appears with multiple UIDs in the passwd database */
830 	    } else if (initgroups(pw->pw_name, getgid())) {
831 		zwarnnam("unsetopt",
832 		    "can't drop privileges; failed to set supplementary group list: %e",
833 		    errno);
834 		return -1;
835 	    }
836 	} else if (getuid() != 0 &&
837 	    (geteuid() != getuid() || orig_egid != getegid())) {
838 	    zwarnnam("unsetopt",
839 		"PRIVILEGED: supplementary group list not changed due to lack of permissions: EUID=%L",
840 		(long)geteuid());
841 	    failed = 1;
842 	}
843 # else
844 	/* initgroups() isn't in POSIX.  If it's not available on the system,
845 	 * we silently skip it. */
846 # endif
847 
848 	/* Set the UID second. */
849 	if (setresuid(getuid(), getuid(), getuid())) {
850 	    zwarnnam("unsetopt",
851 		"PRIVILEGED: can't drop privileges; failed to change user ID: %e",
852 		errno);
853 	    return -1;
854 	}
855 
856 	if (getuid() != 0 && orig_egid != getegid() &&
857 		(setgid(orig_egid) != -1 || setegid(orig_egid) != -1)) {
858 	    zwarnnam("unsetopt",
859 		"PRIVILEGED: can't drop privileges; was able to restore the egid");
860 	    return -1;
861 	}
862 
863 	if (getuid() != 0 && orig_euid != geteuid() &&
864 		(setuid(orig_euid) != -1 || seteuid(orig_euid) != -1)) {
865 	    zwarnnam("unsetopt",
866 		"PRIVILEGED: can't drop privileges; was able to restore the euid");
867 	    return -1;
868 	}
869 
870 	if (failed) {
871 	    /* A warning message has been printed. */
872 	    return -1;
873 	}
874 #endif /* HAVE_SETRESGID && HAVE_SETRESUID */
875 
876 #ifdef JOB_CONTROL
877     } else if (!force && optno == MONITOR && value) {
878 	if (new_opts[optno] == value)
879 	    return 0;
880 	if (SHTTY != -1) {
881 	    origpgrp = GETPGRP();
882 	    acquire_pgrp();
883 	} else
884 	    return -1;
885 #else
886     } else if(optno == MONITOR && value) {
887 	    return -1;
888 #endif /* not JOB_CONTROL */
889 #ifdef GETPWNAM_FAKED
890     } else if(optno == CDABLEVARS && value) {
891 	    return -1;
892 #endif /* GETPWNAM_FAKED */
893     } else if ((optno == EMACSMODE || optno == VIMODE) && value) {
894 	if (sticky && sticky->emulation)
895 	    return -1;
896 	zleentry(ZLE_CMD_SET_KEYMAP, optno);
897 	new_opts[(optno == EMACSMODE) ? VIMODE : EMACSMODE] = 0;
898     } else if (optno == SUNKEYBOARDHACK) {
899 	/* for backward compatibility */
900 	keyboardhackchar = (value ? '`' : '\0');
901     }
902     new_opts[optno] = value;
903     if (optno == BANGHIST || optno == SHINSTDIN)
904 	inittyptab();
905     return 0;
906 }
907 
908 /* Function to get value for special parameter `-' */
909 
910 /**/
911 char *
dashgetfn(UNUSED (Param pm))912 dashgetfn(UNUSED(Param pm))
913 {
914     static char buf[LAST_OPT - FIRST_OPT + 2];
915     char *val = buf;
916     int i;
917 
918     for(i = 0; i <= LAST_OPT - FIRST_OPT; i++) {
919 	int optno = optletters[i];
920 	if(optno && ((optno > 0) ? isset(optno) : unset(-optno)))
921 	    *val++ = FIRST_OPT + i;
922     }
923     *val = '\0';
924     return buf;
925 }
926 
927 /* print options for set -o/+o */
928 
929 /**/
930 void
printoptionstates(int hadplus)931 printoptionstates(int hadplus)
932 {
933     scanhashtable(optiontab, 1, 0, OPT_ALIAS, printoptionnodestate, hadplus);
934 }
935 
936 /**/
937 static void
printoptionnodestate(HashNode hn,int hadplus)938 printoptionnodestate(HashNode hn, int hadplus)
939 {
940     Optname on = (Optname) hn;
941     int optno = on->optno;
942 
943     if (hadplus) {
944 	printf("set %co %s%s\n",
945 	       defset(on, emulation) != isset(optno) ? '-' : '+',
946 	       defset(on, emulation) ? "no" : "",
947 	       on->node.nam);
948     } else {
949 	if (defset(on, emulation))
950 	    printf("no%-19s %s\n", on->node.nam, isset(optno) ? "off" : "on");
951 	else
952 	    printf("%-21s %s\n", on->node.nam, isset(optno) ? "on" : "off");
953     }
954 }
955 
956 /* Print option list for --help */
957 
958 /**/
959 void
printoptionlist(void)960 printoptionlist(void)
961 {
962     short *lp;
963     char c;
964 
965     printf("\nNamed options:\n");
966     scanhashtable(optiontab, 1, 0, OPT_ALIAS, printoptionlist_printoption, 0);
967     printf("\nOption aliases:\n");
968     scanhashtable(optiontab, 1, OPT_ALIAS, 0, printoptionlist_printoption, 0);
969     printf("\nOption letters:\n");
970     for(lp = optletters, c = FIRST_OPT; c <= LAST_OPT; lp++, c++) {
971 	if(!*lp)
972 	    continue;
973 	printf("  -%c  ", c);
974 	printoptionlist_printequiv(*lp);
975     }
976 }
977 
978 /**/
979 static void
printoptionlist_printoption(HashNode hn,UNUSED (int ignored))980 printoptionlist_printoption(HashNode hn, UNUSED(int ignored))
981 {
982     Optname on = (Optname) hn;
983 
984     if(on->node.flags & OPT_ALIAS) {
985 	printf("  --%-19s  ", on->node.nam);
986 	printoptionlist_printequiv(on->optno);
987     } else
988 	printf("  --%s\n", on->node.nam);
989 }
990 
991 /**/
992 static void
printoptionlist_printequiv(int optno)993 printoptionlist_printequiv(int optno)
994 {
995     int isneg = optno < 0;
996 
997     optno *= (isneg ? -1 : 1);
998     printf("  equivalent to --%s%s\n", isneg ? "no-" : "", optns[optno-1].node.nam);
999 }
1000 
1001 /**/
1002 static char *print_emulate_opts;
1003 
1004 /**/
1005 static void
print_emulate_option(HashNode hn,int fully)1006 print_emulate_option(HashNode hn, int fully)
1007 {
1008     Optname on = (Optname) hn;
1009 
1010     if (!(on->node.flags & OPT_ALIAS) &&
1011 	((fully && !(on->node.flags & OPT_SPECIAL)) ||
1012 	 (on->node.flags & OPT_EMULATE)))
1013     {
1014 	if (!print_emulate_opts[on->optno])
1015 	    fputs("no", stdout);
1016 	puts(on->node.nam);
1017     }
1018 }
1019 
1020 /*
1021  * List the settings of options associated with an emulation
1022  */
1023 
1024 /**/
list_emulate_options(char * cmdopts,int fully)1025 void list_emulate_options(char *cmdopts, int fully)
1026 {
1027     print_emulate_opts = cmdopts;
1028     scanhashtable(optiontab, 1, 0, 0, print_emulate_option, fully);
1029 }
1030