blob: cddc516d4c65ea2d9f5ad289c4f8a87a83dacc67 [file] [log] [blame]
Elliott Hughesfc0307d2016-02-02 15:26:47 -08001/* $OpenBSD: misc.c,v 1.41 2015/09/10 22:48:58 nicm Exp $ */
2/* $OpenBSD: path.c,v 1.13 2015/09/05 09:47:08 jsg Exp $ */
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07003
4/*-
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00005 * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
Elliott Hughesa3c3f962017-04-12 16:52:30 -07006 * 2011, 2012, 2013, 2014, 2015, 2016, 2017
Elliott Hughesfc0307d2016-02-02 15:26:47 -08007 * mirabilos <m@mirbsd.org>
Elliott Hughes23925bb2017-09-22 16:04:20 -07008 * Copyright (c) 2015
9 * Daniel Richard G. <skunk@iSKUNK.ORG>
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -070010 *
11 * Provided that these terms and disclaimer and all copyright notices
12 * are retained or reproduced in an accompanying document, permission
13 * is granted to deal in this work without restriction, including un-
14 * limited rights to use, publicly perform, distribute, sell, modify,
15 * merge, give away, or sublicence.
16 *
17 * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
18 * the utmost extent permitted by applicable law, neither express nor
19 * implied; without malicious intent or gross negligence. In no event
20 * may a licensor, author or contributor be held liable for indirect,
21 * direct, other damage, loss, or other issues arising in any way out
22 * of dealing in the work, even if advised of the possibility of such
23 * damage or existence of a defect, except proven that it results out
24 * of said person's immediate fault when using the work as intended.
25 */
26
27#include "sh.h"
28#if !HAVE_GETRUSAGE
29#include <sys/times.h>
30#endif
31#if HAVE_GRP_H
32#include <grp.h>
33#endif
34
Elliott Hughes47086262019-03-26 12:34:31 -070035__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.293 2018/08/10 02:53:35 tg Exp $");
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +000036
37#define KSH_CHVT_FLAG
38#ifdef MKSH_SMALL
39#undef KSH_CHVT_FLAG
40#endif
41#ifdef TIOCSCTTY
42#define KSH_CHVT_CODE
43#define KSH_CHVT_FLAG
44#endif
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -070045
Geremy Condra03ebf062011-10-12 18:17:24 -070046/* type bits for unsigned char */
47unsigned char chtypes[UCHAR_MAX + 1];
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -070048
Geremy Condra03ebf062011-10-12 18:17:24 -070049static const unsigned char *pat_scan(const unsigned char *,
Elliott Hughes737fdce2014-08-07 12:59:26 -070050 const unsigned char *, bool) MKSH_A_PURE;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -070051static int do_gmatch(const unsigned char *, const unsigned char *,
Elliott Hughes23925bb2017-09-22 16:04:20 -070052 const unsigned char *, const unsigned char *,
53 const unsigned char *) MKSH_A_PURE;
Elliott Hughes96b43632015-07-17 11:39:41 -070054static const unsigned char *gmatch_cclass(const unsigned char *, unsigned char)
Elliott Hughes737fdce2014-08-07 12:59:26 -070055 MKSH_A_PURE;
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +000056#ifdef KSH_CHVT_CODE
Thorsten Glaser811a5752013-07-25 14:24:45 +000057static void chvt(const Getopt *);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -070058#endif
59
Geremy Condra03ebf062011-10-12 18:17:24 -070060/*XXX this should go away */
61static int make_path(const char *, const char *, char **, XString *, int *);
62
63#ifdef SETUID_CAN_FAIL_WITH_EAGAIN
64/* we don't need to check for other codes, EPERM won't happen */
65#define DO_SETUID(func, argvec) do { \
66 if ((func argvec) && errno == EAGAIN) \
67 errorf("%s failed with EAGAIN, probably due to a" \
68 " too low process limit; aborting", #func); \
69} while (/* CONSTCOND */ 0)
70#else
71#define DO_SETUID(func, argvec) func argvec
72#endif
73
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -070074
75/* called from XcheckN() to grow buffer */
76char *
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +000077Xcheck_grow(XString *xsp, const char *xp, size_t more)
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -070078{
79 const char *old_beg = xsp->beg;
80
Geremy Condra03ebf062011-10-12 18:17:24 -070081 if (more < xsp->len)
82 more = xsp->len;
83 /* (xsp->len + X_EXTRA) never overflows */
84 checkoktoadd(more, xsp->len + X_EXTRA);
85 xsp->beg = aresize(xsp->beg, (xsp->len += more) + X_EXTRA, xsp->areap);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -070086 xsp->end = xsp->beg + xsp->len;
87 return (xsp->beg + (xp - old_beg));
88}
89
Thorsten Glaser811a5752013-07-25 14:24:45 +000090
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -070091#define SHFLAGS_DEFNS
Elliott Hughes96b43632015-07-17 11:39:41 -070092#define FN(sname,cname,flags,ochar) \
93 static const struct { \
94 /* character flag (if any) */ \
95 char c; \
96 /* OF_* */ \
97 unsigned char optflags; \
98 /* long name of option */ \
99 char name[sizeof(sname)]; \
100 } shoptione_ ## cname = { \
101 ochar, flags, sname \
102 };
Elliott Hughes737fdce2014-08-07 12:59:26 -0700103#include "sh_flags.gen"
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700104
Thorsten Glaser811a5752013-07-25 14:24:45 +0000105#define OFC(i) (options[i][-2])
106#define OFF(i) (((const unsigned char *)options[i])[-1])
107#define OFN(i) (options[i])
108
109const char * const options[] = {
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700110#define SHFLAGS_ITEMS
Elliott Hughes737fdce2014-08-07 12:59:26 -0700111#include "sh_flags.gen"
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700112};
113
114/*
115 * translate -o option into F* constant (also used for test -o option)
116 */
117size_t
118option(const char *n)
119{
Thorsten Glaser811a5752013-07-25 14:24:45 +0000120 size_t i = 0;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700121
Elliott Hughes23925bb2017-09-22 16:04:20 -0700122 if (ctype(n[0], C_MINUS | C_PLUS) && n[1] && !n[2])
Thorsten Glaser811a5752013-07-25 14:24:45 +0000123 while (i < NELEM(options)) {
124 if (OFC(i) == n[1])
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700125 return (i);
Thorsten Glaser811a5752013-07-25 14:24:45 +0000126 ++i;
127 }
128 else
129 while (i < NELEM(options)) {
130 if (!strcmp(OFN(i), n))
131 return (i);
132 ++i;
133 }
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700134
135 return ((size_t)-1);
136}
137
138struct options_info {
139 int opt_width;
140 int opts[NELEM(options)];
141};
142
Elliott Hughes96b43632015-07-17 11:39:41 -0700143static void options_fmt_entry(char *, size_t, unsigned int, const void *);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700144static void printoptions(bool);
145
146/* format a single select menu item */
Elliott Hughes96b43632015-07-17 11:39:41 -0700147static void
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000148options_fmt_entry(char *buf, size_t buflen, unsigned int i, const void *arg)
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700149{
150 const struct options_info *oi = (const struct options_info *)arg;
151
152 shf_snprintf(buf, buflen, "%-*s %s",
Thorsten Glaser811a5752013-07-25 14:24:45 +0000153 oi->opt_width, OFN(oi->opts[i]),
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700154 Flag(oi->opts[i]) ? "on" : "off");
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700155}
156
157static void
158printoptions(bool verbose)
159{
Geremy Condra03ebf062011-10-12 18:17:24 -0700160 size_t i = 0;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700161
162 if (verbose) {
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000163 size_t n = 0, len, octs = 0;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700164 struct options_info oi;
Elliott Hughes966dd552016-12-08 15:56:04 -0800165 struct columnise_opts co;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700166
167 /* verbose version */
168 shf_puts("Current option settings\n", shl_stdout);
169
170 oi.opt_width = 0;
Geremy Condra03ebf062011-10-12 18:17:24 -0700171 while (i < NELEM(options)) {
Thorsten Glaser811a5752013-07-25 14:24:45 +0000172 if ((len = strlen(OFN(i)))) {
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700173 oi.opts[n++] = i;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700174 if (len > octs)
175 octs = len;
Thorsten Glaser811a5752013-07-25 14:24:45 +0000176 len = utf_mbswidth(OFN(i));
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000177 if ((int)len > oi.opt_width)
178 oi.opt_width = (int)len;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700179 }
180 ++i;
181 }
Elliott Hughes966dd552016-12-08 15:56:04 -0800182 co.shf = shl_stdout;
183 co.linesep = '\n';
184 co.prefcol = co.do_last = true;
185 print_columns(&co, n, options_fmt_entry, &oi,
186 octs + 4, oi.opt_width + 4);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700187 } else {
Geremy Condra03ebf062011-10-12 18:17:24 -0700188 /* short version like AT&T ksh93 */
189 shf_puts(Tset, shl_stdout);
Thorsten Glaser811a5752013-07-25 14:24:45 +0000190 while (i < NELEM(options)) {
191 if (Flag(i) && OFN(i)[0])
192 shprintf(" -o %s", OFN(i));
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700193 ++i;
194 }
195 shf_putc('\n', shl_stdout);
196 }
197}
198
199char *
200getoptions(void)
201{
Thorsten Glaser811a5752013-07-25 14:24:45 +0000202 size_t i = 0;
203 char c, m[(int)FNFLAGS + 1];
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700204 char *cp = m;
205
Thorsten Glaser811a5752013-07-25 14:24:45 +0000206 while (i < NELEM(options)) {
207 if ((c = OFC(i)) && Flag(i))
208 *cp++ = c;
209 ++i;
210 }
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700211 strndupx(cp, m, cp - m, ATEMP);
212 return (cp);
213}
214
215/* change a Flag(*) value; takes care of special actions */
216void
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000217change_flag(enum sh_flag f, int what, bool newset)
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700218{
219 unsigned char oldval;
Thorsten Glaser811a5752013-07-25 14:24:45 +0000220 unsigned char newval = (newset ? 1 : 0);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700221
Thorsten Glaser811a5752013-07-25 14:24:45 +0000222 if (f == FXTRACE) {
223 change_xtrace(newval, true);
224 return;
225 }
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700226 oldval = Flag(f);
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000227 Flag(f) = newval = (newset ? 1 : 0);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700228#ifndef MKSH_UNEMPLOYED
229 if (f == FMONITOR) {
230 if (what != OF_CMDLINE && newval != oldval)
231 j_change();
232 } else
233#endif
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000234#ifndef MKSH_NO_CMDLINE_EDITING
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700235 if ((
236#if !MKSH_S_NOVI
237 f == FVI ||
238#endif
239 f == FEMACS || f == FGMACS) && newval) {
240#if !MKSH_S_NOVI
241 Flag(FVI) =
242#endif
243 Flag(FEMACS) = Flag(FGMACS) = 0;
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000244 Flag(f) = newval;
245 } else
246#endif
247 if (f == FPRIVILEGED && oldval && !newval) {
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700248 /* Turning off -p? */
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700249
Geremy Condra03ebf062011-10-12 18:17:24 -0700250 /*XXX this can probably be optimised */
251 kshegid = kshgid = getgid();
Elliott Hughes737fdce2014-08-07 12:59:26 -0700252 ksheuid = kshuid = getuid();
Geremy Condra03ebf062011-10-12 18:17:24 -0700253#if HAVE_SETRESUGID
254 DO_SETUID(setresgid, (kshegid, kshegid, kshegid));
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700255#if HAVE_SETGROUPS
Geremy Condra03ebf062011-10-12 18:17:24 -0700256 /* setgroups doesn't EAGAIN on Linux */
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700257 setgroups(1, &kshegid);
258#endif
Geremy Condra03ebf062011-10-12 18:17:24 -0700259 DO_SETUID(setresuid, (ksheuid, ksheuid, ksheuid));
Elliott Hughes737fdce2014-08-07 12:59:26 -0700260#else /* !HAVE_SETRESUGID */
Elliott Hughes50012062015-03-10 22:22:24 -0700261 /* setgid, setegid, seteuid don't EAGAIN on Linux */
262 setgid(kshegid);
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000263#ifndef MKSH__NO_SETEUGID
Geremy Condra03ebf062011-10-12 18:17:24 -0700264 setegid(kshegid);
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000265#endif
Elliott Hughes50012062015-03-10 22:22:24 -0700266 DO_SETUID(setuid, (ksheuid));
267#ifndef MKSH__NO_SETEUGID
268 seteuid(ksheuid);
269#endif
Elliott Hughes737fdce2014-08-07 12:59:26 -0700270#endif /* !HAVE_SETRESUGID */
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700271 } else if ((f == FPOSIX || f == FSH) && newval) {
Thorsten Glaser811a5752013-07-25 14:24:45 +0000272 /* Turning on -o posix or -o sh? */
273 Flag(FBRACEEXPAND) = 0;
Elliott Hughes23925bb2017-09-22 16:04:20 -0700274 /* Turning on -o posix? */
275 if (f == FPOSIX) {
276 /* C locale required for compliance */
277 UTFMODE = 0;
278 }
Thorsten Glaser811a5752013-07-25 14:24:45 +0000279 } else if (f == FTALKING) {
280 /* Changing interactive flag? */
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700281 if ((what == OF_CMDLINE || what == OF_SET) && procpid == kshpid)
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000282 Flag(FTALKING_I) = newval;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700283 }
284}
285
Thorsten Glaser811a5752013-07-25 14:24:45 +0000286void
287change_xtrace(unsigned char newval, bool dosnapshot)
288{
Elliott Hughes50012062015-03-10 22:22:24 -0700289 static bool in_xtrace;
290
291 if (in_xtrace)
292 return;
293
Thorsten Glaser811a5752013-07-25 14:24:45 +0000294 if (!dosnapshot && newval == Flag(FXTRACE))
295 return;
296
297 if (Flag(FXTRACE) == 2) {
298 shf_putc('\n', shl_xtrace);
299 Flag(FXTRACE) = 1;
300 shf_flush(shl_xtrace);
301 }
302
303 if (!dosnapshot && Flag(FXTRACE) == 1)
304 switch (newval) {
305 case 1:
306 return;
307 case 2:
308 goto changed_xtrace;
309 }
310
311 shf_flush(shl_xtrace);
312 if (shl_xtrace->fd != 2)
313 close(shl_xtrace->fd);
314 if (!newval || (shl_xtrace->fd = savefd(2)) == -1)
315 shl_xtrace->fd = 2;
316
317 changed_xtrace:
Elliott Hughes50012062015-03-10 22:22:24 -0700318 if ((Flag(FXTRACE) = newval) == 2) {
319 in_xtrace = true;
320 Flag(FXTRACE) = 0;
Thorsten Glaser811a5752013-07-25 14:24:45 +0000321 shf_puts(substitute(str_val(global("PS4")), 0), shl_xtrace);
Elliott Hughes50012062015-03-10 22:22:24 -0700322 Flag(FXTRACE) = 2;
323 in_xtrace = false;
324 }
Thorsten Glaser811a5752013-07-25 14:24:45 +0000325}
326
Geremy Condra03ebf062011-10-12 18:17:24 -0700327/*
328 * Parse command line and set command arguments. Returns the index of
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700329 * non-option arguments, -1 if there is an error.
330 */
331int
332parse_args(const char **argv,
Elliott Hughes77740fc2016-08-12 15:06:53 -0700333 /* OF_FIRSTTIME, OF_CMDLINE, or OF_SET */
Geremy Condra03ebf062011-10-12 18:17:24 -0700334 int what,
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700335 bool *setargsp)
336{
Elliott Hughes737fdce2014-08-07 12:59:26 -0700337 static const char cmd_opts[] =
338#define SHFLAGS_NOT_SET
339#define SHFLAGS_OPTCS
340#include "sh_flags.gen"
341#undef SHFLAGS_NOT_SET
342 ;
343 static const char set_opts[] =
344#define SHFLAGS_NOT_CMD
345#define SHFLAGS_OPTCS
346#include "sh_flags.gen"
347#undef SHFLAGS_NOT_CMD
348 ;
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000349 bool set;
Elliott Hughes737fdce2014-08-07 12:59:26 -0700350 const char *opts;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700351 const char *array = NULL;
352 Getopt go;
353 size_t i;
Geremy Condra03ebf062011-10-12 18:17:24 -0700354 int optc, arrayset = 0;
355 bool sortargs = false;
Thorsten Glaser811a5752013-07-25 14:24:45 +0000356 bool fcompatseen = false;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700357
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700358 if (what == OF_CMDLINE) {
359 const char *p = argv[0], *q;
Geremy Condra03ebf062011-10-12 18:17:24 -0700360 /*
361 * Set FLOGIN before parsing options so user can clear
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700362 * flag using +l.
363 */
364 if (*p != '-')
365 for (q = p; *q; )
Elliott Hughes966dd552016-12-08 15:56:04 -0800366 if (mksh_cdirsep(*q++))
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700367 p = q;
368 Flag(FLOGIN) = (*p == '-');
369 opts = cmd_opts;
370 } else if (what == OF_FIRSTTIME) {
371 opts = cmd_opts;
372 } else
373 opts = set_opts;
374 ksh_getopt_reset(&go, GF_ERROR|GF_PLUSOPT);
375 while ((optc = ksh_getopt(argv, &go, opts)) != -1) {
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000376 set = tobool(!(go.info & GI_PLUS));
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700377 switch (optc) {
378 case 'A':
379 if (what == OF_FIRSTTIME)
380 break;
381 arrayset = set ? 1 : -1;
382 array = go.optarg;
383 break;
384
385 case 'o':
386 if (what == OF_FIRSTTIME)
387 break;
388 if (go.optarg == NULL) {
Geremy Condra03ebf062011-10-12 18:17:24 -0700389 /*
390 * lone -o: print options
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700391 *
392 * Note that on the command line, -o requires
393 * an option (ie, can't get here if what is
394 * OF_CMDLINE).
395 */
396 printoptions(set);
397 break;
398 }
399 i = option(go.optarg);
Thorsten Glaser811a5752013-07-25 14:24:45 +0000400 if ((i == FPOSIX || i == FSH) && set && !fcompatseen) {
401 /*
402 * If running 'set -o posix' or
403 * 'set -o sh', turn off the other;
404 * if running 'set -o posix -o sh'
405 * allow both to be set though.
406 */
407 Flag(FPOSIX) = 0;
408 Flag(FSH) = 0;
409 fcompatseen = true;
410 }
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000411 if ((i != (size_t)-1) && (set ? 1U : 0U) == Flag(i))
Geremy Condra03ebf062011-10-12 18:17:24 -0700412 /*
413 * Don't check the context if the flag
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700414 * isn't changing - makes "set -o interactive"
415 * work if you're already interactive. Needed
416 * if the output of "set +o" is to be used.
417 */
418 ;
Thorsten Glaser811a5752013-07-25 14:24:45 +0000419 else if ((i != (size_t)-1) && (OFF(i) & what))
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700420 change_flag((enum sh_flag)i, what, set);
421 else {
Elliott Hughes77740fc2016-08-12 15:06:53 -0700422 bi_errorf(Tf_sD_s, go.optarg,
423 Tunknown_option);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700424 return (-1);
425 }
426 break;
427
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000428#ifdef KSH_CHVT_FLAG
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700429 case 'T':
430 if (what != OF_FIRSTTIME)
431 break;
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000432#ifndef KSH_CHVT_CODE
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700433 errorf("no TIOCSCTTY ioctl");
434#else
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000435 change_flag(FTALKING, OF_CMDLINE, true);
Thorsten Glaser811a5752013-07-25 14:24:45 +0000436 chvt(&go);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700437 break;
438#endif
439#endif
440
441 case '?':
442 return (-1);
443
444 default:
445 if (what == OF_FIRSTTIME)
446 break;
447 /* -s: sort positional params (AT&T ksh stupidity) */
448 if (what == OF_SET && optc == 's') {
Geremy Condra03ebf062011-10-12 18:17:24 -0700449 sortargs = true;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700450 break;
451 }
452 for (i = 0; i < NELEM(options); i++)
Thorsten Glaser811a5752013-07-25 14:24:45 +0000453 if (optc == OFC(i) &&
454 (what & OFF(i))) {
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700455 change_flag((enum sh_flag)i, what, set);
456 break;
457 }
458 if (i == NELEM(options))
459 internal_errorf("parse_args: '%c'", optc);
460 }
461 }
462 if (!(go.info & GI_MINUSMINUS) && argv[go.optind] &&
Elliott Hughes23925bb2017-09-22 16:04:20 -0700463 ctype(argv[go.optind][0], C_MINUS | C_PLUS) &&
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700464 argv[go.optind][1] == '\0') {
465 /* lone - clears -v and -x flags */
Thorsten Glaser811a5752013-07-25 14:24:45 +0000466 if (argv[go.optind][0] == '-') {
467 Flag(FVERBOSE) = 0;
468 change_xtrace(0, false);
469 }
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700470 /* set skips lone - or + option */
471 go.optind++;
472 }
473 if (setargsp)
474 /* -- means set $#/$* even if there are no arguments */
475 *setargsp = !arrayset && ((go.info & GI_MINUSMINUS) ||
476 argv[go.optind]);
477
Geremy Condra03ebf062011-10-12 18:17:24 -0700478 if (arrayset) {
479 const char *ccp = NULL;
480
Elliott Hughesa3c3f962017-04-12 16:52:30 -0700481 if (array && *array)
Geremy Condra03ebf062011-10-12 18:17:24 -0700482 ccp = skip_varname(array, false);
483 if (!ccp || !(!ccp[0] || (ccp[0] == '+' && !ccp[1]))) {
Elliott Hughes77740fc2016-08-12 15:06:53 -0700484 bi_errorf(Tf_sD_s, array, Tnot_ident);
Geremy Condra03ebf062011-10-12 18:17:24 -0700485 return (-1);
486 }
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700487 }
488 if (sortargs) {
489 for (i = go.optind; argv[i]; i++)
490 ;
491 qsort(&argv[go.optind], i - go.optind, sizeof(void *),
Elliott Hughes23925bb2017-09-22 16:04:20 -0700492 ascpstrcmp);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700493 }
494 if (arrayset)
Geremy Condra03ebf062011-10-12 18:17:24 -0700495 go.optind += set_array(array, tobool(arrayset > 0),
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700496 argv + go.optind);
497
498 return (go.optind);
499}
500
501/* parse a decimal number: returns 0 if string isn't a number, 1 otherwise */
502int
503getn(const char *s, int *ai)
504{
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000505 char c;
Thorsten Glaser811a5752013-07-25 14:24:45 +0000506 mksh_ari_u num;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700507 bool neg = false;
508
Thorsten Glaser811a5752013-07-25 14:24:45 +0000509 num.u = 0;
510
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700511 do {
512 c = *s++;
Elliott Hughes23925bb2017-09-22 16:04:20 -0700513 } while (ctype(c, C_SPACE));
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000514
515 switch (c) {
516 case '-':
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700517 neg = true;
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000518 /* FALLTHROUGH */
519 case '+':
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700520 c = *s++;
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000521 break;
522 }
523
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700524 do {
Elliott Hughes23925bb2017-09-22 16:04:20 -0700525 if (!ctype(c, C_DIGIT))
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000526 /* not numeric */
527 return (0);
Thorsten Glaser811a5752013-07-25 14:24:45 +0000528 if (num.u > 214748364U)
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000529 /* overflow on multiplication */
530 return (0);
Elliott Hughes96b43632015-07-17 11:39:41 -0700531 num.u = num.u * 10U + (unsigned int)ksh_numdig(c);
Thorsten Glaser811a5752013-07-25 14:24:45 +0000532 /* now: num.u <= 2147483649U */
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700533 } while ((c = *s++));
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700534
Thorsten Glaser811a5752013-07-25 14:24:45 +0000535 if (num.u > (neg ? 2147483648U : 2147483647U))
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000536 /* overflow for signed 32-bit int */
537 return (0);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700538
Thorsten Glaser811a5752013-07-25 14:24:45 +0000539 if (neg)
540 num.u = -num.u;
541 *ai = num.i;
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +0000542 return (1);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700543}
544
Geremy Condra03ebf062011-10-12 18:17:24 -0700545/**
546 * pattern simplifications:
547 * - @(x) -> x (not @(x|y) though)
548 * - ** -> *
549 */
550static void *
551simplify_gmatch_pattern(const unsigned char *sp)
552{
553 uint8_t c;
554 unsigned char *cp, *dp;
555 const unsigned char *ps, *se;
556
557 cp = alloc(strlen((const void *)sp) + 1, ATEMP);
558 goto simplify_gmatch_pat1a;
559
560 /* foo@(b@(a)r)b@(a|a)z -> foobarb@(a|a)z */
561 simplify_gmatch_pat1:
562 sp = cp;
563 simplify_gmatch_pat1a:
564 dp = cp;
Elliott Hughes23925bb2017-09-22 16:04:20 -0700565 se = strnul(sp);
Geremy Condra03ebf062011-10-12 18:17:24 -0700566 while ((c = *sp++)) {
567 if (!ISMAGIC(c)) {
568 *dp++ = c;
569 continue;
570 }
571 switch ((c = *sp++)) {
572 case 0x80|'@':
573 /* simile for @ */
574 case 0x80|' ':
575 /* check whether it has only one clause */
576 ps = pat_scan(sp, se, true);
577 if (!ps || ps[-1] != /*(*/ ')')
578 /* nope */
579 break;
580 /* copy inner clause until matching close */
581 ps -= 2;
582 while ((const unsigned char *)sp < ps)
583 *dp++ = *sp++;
584 /* skip MAGIC and closing parenthesis */
585 sp += 2;
586 /* copy the rest of the pattern */
587 memmove(dp, sp, strlen((const void *)sp) + 1);
588 /* redo from start */
589 goto simplify_gmatch_pat1;
590 }
591 *dp++ = MAGIC;
592 *dp++ = c;
593 }
594 *dp = '\0';
595
596 /* collapse adjacent asterisk wildcards */
597 sp = dp = cp;
598 while ((c = *sp++)) {
599 if (!ISMAGIC(c)) {
600 *dp++ = c;
601 continue;
602 }
603 switch ((c = *sp++)) {
604 case '*':
605 while (ISMAGIC(sp[0]) && sp[1] == c)
606 sp += 2;
607 break;
608 }
609 *dp++ = MAGIC;
610 *dp++ = c;
611 }
612 *dp = '\0';
613
614 /* return the result, allocated from ATEMP */
615 return (cp);
616}
617
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700618/* -------- gmatch.c -------- */
619
620/*
621 * int gmatch(string, pattern)
622 * char *string, *pattern;
623 *
624 * Match a pattern as in sh(1).
625 * pattern character are prefixed with MAGIC by expand.
626 */
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700627int
628gmatchx(const char *s, const char *p, bool isfile)
629{
630 const char *se, *pe;
Geremy Condra03ebf062011-10-12 18:17:24 -0700631 char *pnew;
632 int rv;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700633
634 if (s == NULL || p == NULL)
635 return (0);
636
Elliott Hughes23925bb2017-09-22 16:04:20 -0700637 pe = strnul(p);
Geremy Condra03ebf062011-10-12 18:17:24 -0700638 /*
639 * isfile is false iff no syntax check has been done on
Elliott Hughes23925bb2017-09-22 16:04:20 -0700640 * the pattern. If check fails, just do a strcmp().
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700641 */
Elliott Hughes23925bb2017-09-22 16:04:20 -0700642 if (!isfile && !has_globbing(p)) {
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700643 size_t len = pe - p + 1;
644 char tbuf[64];
645 char *t = len <= sizeof(tbuf) ? tbuf : alloc(len, ATEMP);
646 debunk(t, p, len);
647 return (!strcmp(t, s));
648 }
Elliott Hughes23925bb2017-09-22 16:04:20 -0700649 se = strnul(s);
Geremy Condra03ebf062011-10-12 18:17:24 -0700650
651 /*
652 * since the do_gmatch() engine sucks so much, we must do some
653 * pattern simplifications
654 */
655 pnew = simplify_gmatch_pattern((const unsigned char *)p);
Elliott Hughes23925bb2017-09-22 16:04:20 -0700656 pe = strnul(pnew);
Geremy Condra03ebf062011-10-12 18:17:24 -0700657
658 rv = do_gmatch((const unsigned char *)s, (const unsigned char *)se,
Elliott Hughes23925bb2017-09-22 16:04:20 -0700659 (const unsigned char *)pnew, (const unsigned char *)pe,
660 (const unsigned char *)s);
Geremy Condra03ebf062011-10-12 18:17:24 -0700661 afree(pnew, ATEMP);
662 return (rv);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700663}
664
Geremy Condra03ebf062011-10-12 18:17:24 -0700665/**
666 * Returns if p is a syntacticly correct globbing pattern, false
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700667 * if it contains no pattern characters or if there is a syntax error.
668 * Syntax errors are:
669 * - [ with no closing ]
670 * - imbalanced $(...) expression
Elliott Hughes23925bb2017-09-22 16:04:20 -0700671 * - [...] and *(...) not nested (eg, @(a[b|)c], *(a[b|c]d))
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700672 */
673/*XXX
Geremy Condra03ebf062011-10-12 18:17:24 -0700674 * - if no magic,
675 * if dest given, copy to dst
676 * return ?
677 * - if magic && (no globbing || syntax error)
678 * debunk to dst
679 * return ?
680 * - return ?
681 */
Elliott Hughes23925bb2017-09-22 16:04:20 -0700682bool
683has_globbing(const char *pat)
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700684{
Elliott Hughes23925bb2017-09-22 16:04:20 -0700685 unsigned char c, subc;
Geremy Condra03ebf062011-10-12 18:17:24 -0700686 bool saw_glob = false;
Elliott Hughes23925bb2017-09-22 16:04:20 -0700687 unsigned int nest = 0;
688 const unsigned char *p = (const unsigned char *)pat;
689 const unsigned char *s;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700690
Elliott Hughes23925bb2017-09-22 16:04:20 -0700691 while ((c = *p++)) {
692 /* regular character? ok. */
693 if (!ISMAGIC(c))
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700694 continue;
Elliott Hughes23925bb2017-09-22 16:04:20 -0700695 /* MAGIC + NUL? abort. */
696 if (!(c = *p++))
697 return (false);
698 /* some specials */
Elliott Hughesdd4abe02018-02-05 15:55:19 -0800699 if (ord(c) == ORD('*') || ord(c) == ORD('?')) {
Elliott Hughes23925bb2017-09-22 16:04:20 -0700700 /* easy glob, accept */
Geremy Condra03ebf062011-10-12 18:17:24 -0700701 saw_glob = true;
Elliott Hughesdd4abe02018-02-05 15:55:19 -0800702 } else if (ord(c) == ORD('[')) {
Elliott Hughes23925bb2017-09-22 16:04:20 -0700703 /* bracket expression; eat negation and initial ] */
Elliott Hughesdd4abe02018-02-05 15:55:19 -0800704 if (ISMAGIC(p[0]) && ord(p[1]) == ORD('!'))
Elliott Hughes23925bb2017-09-22 16:04:20 -0700705 p += 2;
Elliott Hughesdd4abe02018-02-05 15:55:19 -0800706 if (ISMAGIC(p[0]) && ord(p[1]) == ORD(']'))
Elliott Hughes23925bb2017-09-22 16:04:20 -0700707 p += 2;
708 /* check next string part */
709 s = p;
710 while ((c = *s++)) {
711 /* regular chars are ok */
712 if (!ISMAGIC(c))
713 continue;
714 /* MAGIC + NUL cannot happen */
715 if (!(c = *s++))
716 return (false);
717 /* terminating bracket? */
Elliott Hughesdd4abe02018-02-05 15:55:19 -0800718 if (ord(c) == ORD(']')) {
Elliott Hughes23925bb2017-09-22 16:04:20 -0700719 /* accept and continue */
720 p = s;
721 saw_glob = true;
722 break;
723 }
724 /* sub-bracket expressions */
Elliott Hughesdd4abe02018-02-05 15:55:19 -0800725 if (ord(c) == ORD('[') && (
Elliott Hughes23925bb2017-09-22 16:04:20 -0700726 /* collating element? */
Elliott Hughesdd4abe02018-02-05 15:55:19 -0800727 ord(*s) == ORD('.') ||
Elliott Hughes23925bb2017-09-22 16:04:20 -0700728 /* equivalence class? */
Elliott Hughesdd4abe02018-02-05 15:55:19 -0800729 ord(*s) == ORD('=') ||
Elliott Hughes23925bb2017-09-22 16:04:20 -0700730 /* character class? */
Elliott Hughesdd4abe02018-02-05 15:55:19 -0800731 ord(*s) == ORD(':'))) {
Elliott Hughes23925bb2017-09-22 16:04:20 -0700732 /* must stop with exactly the same c */
733 subc = *s++;
734 /* arbitrarily many chars in betwixt */
735 while ((c = *s++))
736 /* but only this sequence... */
737 if (c == subc && ISMAGIC(*s) &&
Elliott Hughesdd4abe02018-02-05 15:55:19 -0800738 ord(s[1]) == ORD(']')) {
Elliott Hughes23925bb2017-09-22 16:04:20 -0700739 /* accept, terminate */
740 s += 2;
741 break;
742 }
743 /* EOS without: reject bracket expr */
744 if (!c)
745 break;
746 /* continue; */
747 }
748 /* anything else just goes on */
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700749 }
Elliott Hughes23925bb2017-09-22 16:04:20 -0700750 } else if ((c & 0x80) && ctype(c & 0x7F, C_PATMO | C_SPC)) {
751 /* opening pattern */
Geremy Condra03ebf062011-10-12 18:17:24 -0700752 saw_glob = true;
Elliott Hughes23925bb2017-09-22 16:04:20 -0700753 ++nest;
Elliott Hughesdd4abe02018-02-05 15:55:19 -0800754 } else if (ord(c) == ORD(/*(*/ ')')) {
Elliott Hughes23925bb2017-09-22 16:04:20 -0700755 /* closing pattern */
756 if (nest)
757 --nest;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700758 }
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700759 }
Elliott Hughes23925bb2017-09-22 16:04:20 -0700760 return (saw_glob && !nest);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700761}
762
763/* Function must return either 0 or 1 (assumed by code for 0x80|'!') */
764static int
765do_gmatch(const unsigned char *s, const unsigned char *se,
Elliott Hughes23925bb2017-09-22 16:04:20 -0700766 const unsigned char *p, const unsigned char *pe,
767 const unsigned char *smin)
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700768{
Elliott Hughes23925bb2017-09-22 16:04:20 -0700769 unsigned char sc, pc, sl = 0;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700770 const unsigned char *prest, *psub, *pnext;
771 const unsigned char *srest;
772
773 if (s == NULL || p == NULL)
774 return (0);
Elliott Hughes23925bb2017-09-22 16:04:20 -0700775 if (s > smin && s <= se)
776 sl = s[-1];
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700777 while (p < pe) {
778 pc = *p++;
779 sc = s < se ? *s : '\0';
780 s++;
781 if (!ISMAGIC(pc)) {
782 if (sc != pc)
783 return (0);
Elliott Hughes23925bb2017-09-22 16:04:20 -0700784 sl = sc;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700785 continue;
786 }
Elliott Hughes23925bb2017-09-22 16:04:20 -0700787 switch (ord(*p++)) {
Elliott Hughesdd4abe02018-02-05 15:55:19 -0800788 case ORD('['):
Elliott Hughes23925bb2017-09-22 16:04:20 -0700789 /* BSD cclass extension? */
Elliott Hughesdd4abe02018-02-05 15:55:19 -0800790 if (ISMAGIC(p[0]) && ord(p[1]) == ORD('[') &&
791 ord(p[2]) == ORD(':') &&
Elliott Hughes23925bb2017-09-22 16:04:20 -0700792 ctype((pc = p[3]), C_ANGLE) &&
Elliott Hughesdd4abe02018-02-05 15:55:19 -0800793 ord(p[4]) == ORD(':') &&
794 ISMAGIC(p[5]) && ord(p[6]) == ORD(']') &&
795 ISMAGIC(p[7]) && ord(p[8]) == ORD(']')) {
Elliott Hughes23925bb2017-09-22 16:04:20 -0700796 /* zero-length match */
797 --s;
798 p += 9;
799 /* word begin? */
Elliott Hughesdd4abe02018-02-05 15:55:19 -0800800 if (ord(pc) == ORD('<') &&
Elliott Hughes23925bb2017-09-22 16:04:20 -0700801 !ctype(sl, C_ALNUX) &&
802 ctype(sc, C_ALNUX))
803 break;
804 /* word end? */
Elliott Hughesdd4abe02018-02-05 15:55:19 -0800805 if (ord(pc) == ORD('>') &&
Elliott Hughes23925bb2017-09-22 16:04:20 -0700806 ctype(sl, C_ALNUX) &&
807 !ctype(sc, C_ALNUX))
808 break;
809 /* neither */
810 return (0);
811 }
Elliott Hughes96b43632015-07-17 11:39:41 -0700812 if (sc == 0 || (p = gmatch_cclass(p, sc)) == NULL)
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700813 return (0);
814 break;
815
Elliott Hughesdd4abe02018-02-05 15:55:19 -0800816 case ORD('?'):
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700817 if (sc == 0)
818 return (0);
819 if (UTFMODE) {
820 --s;
821 s += utf_ptradj((const void *)s);
822 }
823 break;
824
Elliott Hughesdd4abe02018-02-05 15:55:19 -0800825 case ORD('*'):
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700826 if (p == pe)
827 return (1);
828 s--;
829 do {
Elliott Hughes23925bb2017-09-22 16:04:20 -0700830 if (do_gmatch(s, se, p, pe, smin))
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700831 return (1);
832 } while (s++ < se);
833 return (0);
834
835 /**
Elliott Hughes23925bb2017-09-22 16:04:20 -0700836 * [+*?@!](pattern|pattern|..)
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700837 * This is also needed for ${..%..}, etc.
838 */
Geremy Condra03ebf062011-10-12 18:17:24 -0700839
840 /* matches one or more times */
Elliott Hughesdd4abe02018-02-05 15:55:19 -0800841 case ORD('+') | 0x80:
Geremy Condra03ebf062011-10-12 18:17:24 -0700842 /* matches zero or more times */
Elliott Hughesdd4abe02018-02-05 15:55:19 -0800843 case ORD('*') | 0x80:
Geremy Condra03ebf062011-10-12 18:17:24 -0700844 if (!(prest = pat_scan(p, pe, false)))
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700845 return (0);
846 s--;
847 /* take care of zero matches */
Elliott Hughesdd4abe02018-02-05 15:55:19 -0800848 if (ord(p[-1]) == (0x80 | ORD('*')) &&
Elliott Hughes23925bb2017-09-22 16:04:20 -0700849 do_gmatch(s, se, prest, pe, smin))
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700850 return (1);
851 for (psub = p; ; psub = pnext) {
Geremy Condra03ebf062011-10-12 18:17:24 -0700852 pnext = pat_scan(psub, pe, true);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700853 for (srest = s; srest <= se; srest++) {
Elliott Hughes23925bb2017-09-22 16:04:20 -0700854 if (do_gmatch(s, srest, psub, pnext - 2, smin) &&
855 (do_gmatch(srest, se, prest, pe, smin) ||
856 (s != srest &&
857 do_gmatch(srest, se, p - 2, pe, smin))))
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700858 return (1);
859 }
860 if (pnext == prest)
861 break;
862 }
863 return (0);
864
Geremy Condra03ebf062011-10-12 18:17:24 -0700865 /* matches zero or once */
Elliott Hughesdd4abe02018-02-05 15:55:19 -0800866 case ORD('?') | 0x80:
Geremy Condra03ebf062011-10-12 18:17:24 -0700867 /* matches one of the patterns */
Elliott Hughesdd4abe02018-02-05 15:55:19 -0800868 case ORD('@') | 0x80:
Geremy Condra03ebf062011-10-12 18:17:24 -0700869 /* simile for @ */
Elliott Hughesdd4abe02018-02-05 15:55:19 -0800870 case ORD(' ') | 0x80:
Geremy Condra03ebf062011-10-12 18:17:24 -0700871 if (!(prest = pat_scan(p, pe, false)))
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700872 return (0);
873 s--;
874 /* Take care of zero matches */
Elliott Hughesdd4abe02018-02-05 15:55:19 -0800875 if (ord(p[-1]) == (0x80 | ORD('?')) &&
Elliott Hughes23925bb2017-09-22 16:04:20 -0700876 do_gmatch(s, se, prest, pe, smin))
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700877 return (1);
878 for (psub = p; ; psub = pnext) {
Geremy Condra03ebf062011-10-12 18:17:24 -0700879 pnext = pat_scan(psub, pe, true);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700880 srest = prest == pe ? se : s;
881 for (; srest <= se; srest++) {
Elliott Hughes23925bb2017-09-22 16:04:20 -0700882 if (do_gmatch(s, srest, psub, pnext - 2, smin) &&
883 do_gmatch(srest, se, prest, pe, smin))
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700884 return (1);
885 }
886 if (pnext == prest)
887 break;
888 }
889 return (0);
890
Geremy Condra03ebf062011-10-12 18:17:24 -0700891 /* matches none of the patterns */
Elliott Hughesdd4abe02018-02-05 15:55:19 -0800892 case ORD('!') | 0x80:
Geremy Condra03ebf062011-10-12 18:17:24 -0700893 if (!(prest = pat_scan(p, pe, false)))
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700894 return (0);
895 s--;
896 for (srest = s; srest <= se; srest++) {
897 int matched = 0;
898
899 for (psub = p; ; psub = pnext) {
Geremy Condra03ebf062011-10-12 18:17:24 -0700900 pnext = pat_scan(psub, pe, true);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700901 if (do_gmatch(s, srest, psub,
Elliott Hughes23925bb2017-09-22 16:04:20 -0700902 pnext - 2, smin)) {
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700903 matched = 1;
904 break;
905 }
906 if (pnext == prest)
907 break;
908 }
909 if (!matched &&
Elliott Hughes23925bb2017-09-22 16:04:20 -0700910 do_gmatch(srest, se, prest, pe, smin))
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700911 return (1);
912 }
913 return (0);
914
915 default:
916 if (sc != p[-1])
917 return (0);
918 break;
919 }
Elliott Hughes23925bb2017-09-22 16:04:20 -0700920 sl = sc;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700921 }
922 return (s == se);
923}
924
Elliott Hughes23925bb2017-09-22 16:04:20 -0700925/*XXX this is a prime example for bsearch or a const hashtable */
926static const struct cclass {
927 const char *name;
928 uint32_t value;
929} cclasses[] = {
930 /* POSIX */
931 { "alnum", C_ALNUM },
932 { "alpha", C_ALPHA },
933 { "blank", C_BLANK },
934 { "cntrl", C_CNTRL },
935 { "digit", C_DIGIT },
936 { "graph", C_GRAPH },
937 { "lower", C_LOWER },
938 { "print", C_PRINT },
939 { "punct", C_PUNCT },
940 { "space", C_SPACE },
941 { "upper", C_UPPER },
942 { "xdigit", C_SEDEC },
943 /* BSD */
944 /* "<" and ">" are handled inline */
945 /* GNU bash */
946 { "ascii", C_ASCII },
947 { "word", C_ALNUX },
948 /* mksh */
949 { "sh_alias", C_ALIAS },
950 { "sh_edq", C_EDQ },
951 { "sh_ifs", C_IFS },
952 { "sh_ifsws", C_IFSWS },
953 { "sh_nl", C_NL },
954 { "sh_quote", C_QUOTE },
955 /* sentinel */
956 { NULL, 0 }
957};
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700958
Elliott Hughes23925bb2017-09-22 16:04:20 -0700959static const unsigned char *
960gmatch_cclass(const unsigned char *pat, unsigned char sc)
961{
962 unsigned char c, subc, lc;
963 const unsigned char *p = pat, *s;
964 bool found = false;
965 bool negated = false;
966 char *subp;
967
968 /* check for negation */
Elliott Hughesdd4abe02018-02-05 15:55:19 -0800969 if (ISMAGIC(p[0]) && ord(p[1]) == ORD('!')) {
Elliott Hughes23925bb2017-09-22 16:04:20 -0700970 p += 2;
971 negated = true;
972 }
973 /* make initial ] non-MAGIC */
Elliott Hughesdd4abe02018-02-05 15:55:19 -0800974 if (ISMAGIC(p[0]) && ord(p[1]) == ORD(']'))
Elliott Hughes23925bb2017-09-22 16:04:20 -0700975 ++p;
976 /* iterate over bracket expression, debunk()ing on the fly */
977 while ((c = *p++)) {
978 nextc:
979 /* non-regular character? */
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -0700980 if (ISMAGIC(c)) {
Elliott Hughes23925bb2017-09-22 16:04:20 -0700981 /* MAGIC + NUL cannot happen */
982 if (!(c = *p++))
983 break;
984 /* terminating bracket? */
Elliott Hughesdd4abe02018-02-05 15:55:19 -0800985 if (ord(c) == ORD(']')) {
Elliott Hughes23925bb2017-09-22 16:04:20 -0700986 /* accept and return */
987 return (found != negated ? p : NULL);
988 }
989 /* sub-bracket expressions */
Elliott Hughesdd4abe02018-02-05 15:55:19 -0800990 if (ord(c) == ORD('[') && (
Elliott Hughes23925bb2017-09-22 16:04:20 -0700991 /* collating element? */
Elliott Hughesdd4abe02018-02-05 15:55:19 -0800992 ord(*p) == ORD('.') ||
Elliott Hughes23925bb2017-09-22 16:04:20 -0700993 /* equivalence class? */
Elliott Hughesdd4abe02018-02-05 15:55:19 -0800994 ord(*p) == ORD('=') ||
Elliott Hughes23925bb2017-09-22 16:04:20 -0700995 /* character class? */
Elliott Hughesdd4abe02018-02-05 15:55:19 -0800996 ord(*p) == ORD(':'))) {
Elliott Hughes23925bb2017-09-22 16:04:20 -0700997 /* must stop with exactly the same c */
998 subc = *p++;
999 /* save away start of substring */
1000 s = p;
1001 /* arbitrarily many chars in betwixt */
1002 while ((c = *p++))
1003 /* but only this sequence... */
1004 if (c == subc && ISMAGIC(*p) &&
Elliott Hughesdd4abe02018-02-05 15:55:19 -08001005 ord(p[1]) == ORD(']')) {
Elliott Hughes23925bb2017-09-22 16:04:20 -07001006 /* accept, terminate */
1007 p += 2;
1008 break;
1009 }
1010 /* EOS without: reject bracket expr */
1011 if (!c)
1012 break;
1013 /* debunk substring */
1014 strndupx(subp, s, p - s - 3, ATEMP);
1015 debunk(subp, subp, p - s - 3 + 1);
1016 cclass_common:
1017 /* whither subexpression */
Elliott Hughesdd4abe02018-02-05 15:55:19 -08001018 if (ord(subc) == ORD(':')) {
Elliott Hughes23925bb2017-09-22 16:04:20 -07001019 const struct cclass *cls = cclasses;
1020
1021 /* search for name in cclass list */
1022 while (cls->name)
1023 if (!strcmp(subp, cls->name)) {
1024 /* found, match? */
1025 if (ctype(sc,
1026 cls->value))
1027 found = true;
1028 /* break either way */
1029 break;
1030 } else
1031 ++cls;
1032 /* that's all here */
1033 afree(subp, ATEMP);
1034 continue;
1035 }
1036 /* collating element or equivalence class */
1037 /* Note: latter are treated as former */
1038 if (ctype(subp[0], C_ASCII) && !subp[1])
1039 /* [.a.] where a is one ASCII char */
1040 c = subp[0];
1041 else
1042 /* force no match */
1043 c = 0;
1044 /* no longer needed */
1045 afree(subp, ATEMP);
1046 } else if (!ISMAGIC(c) && (c & 0x80)) {
1047 /* 0x80|' ' is plain (...) */
1048 if ((c &= 0x7F) != ' ') {
1049 /* check single match NOW */
1050 if (sc == c)
1051 found = true;
1052 /* next character is (...) */
1053 }
1054 c = '(' /*)*/;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001055 }
1056 }
Elliott Hughes23925bb2017-09-22 16:04:20 -07001057 /* range expression? */
Elliott Hughesdd4abe02018-02-05 15:55:19 -08001058 if (!(ISMAGIC(p[0]) && ord(p[1]) == ORD('-') &&
Elliott Hughes23925bb2017-09-22 16:04:20 -07001059 /* not terminating bracket? */
Elliott Hughesdd4abe02018-02-05 15:55:19 -08001060 (!ISMAGIC(p[2]) || ord(p[3]) != ORD(']')))) {
Elliott Hughes23925bb2017-09-22 16:04:20 -07001061 /* no, check single match */
1062 if (sc == c)
1063 /* note: sc is never NUL */
1064 found = true;
1065 /* do the next "first" character */
1066 continue;
1067 }
1068 /* save lower range bound */
1069 lc = c;
1070 /* skip over the range operator */
1071 p += 2;
1072 /* do the same shit as above... almost */
1073 subc = 0;
1074 if (!(c = *p++))
1075 break;
1076 /* non-regular character? */
1077 if (ISMAGIC(c)) {
1078 /* MAGIC + NUL cannot happen */
1079 if (!(c = *p++))
1080 break;
1081 /* sub-bracket expressions */
Elliott Hughesdd4abe02018-02-05 15:55:19 -08001082 if (ord(c) == ORD('[') && (
Elliott Hughes23925bb2017-09-22 16:04:20 -07001083 /* collating element? */
Elliott Hughesdd4abe02018-02-05 15:55:19 -08001084 ord(*p) == ORD('.') ||
Elliott Hughes23925bb2017-09-22 16:04:20 -07001085 /* equivalence class? */
Elliott Hughesdd4abe02018-02-05 15:55:19 -08001086 ord(*p) == ORD('=') ||
Elliott Hughes23925bb2017-09-22 16:04:20 -07001087 /* character class? */
Elliott Hughesdd4abe02018-02-05 15:55:19 -08001088 ord(*p) == ORD(':'))) {
Elliott Hughes23925bb2017-09-22 16:04:20 -07001089 /* must stop with exactly the same c */
1090 subc = *p++;
1091 /* save away start of substring */
1092 s = p;
1093 /* arbitrarily many chars in betwixt */
1094 while ((c = *p++))
1095 /* but only this sequence... */
1096 if (c == subc && ISMAGIC(*p) &&
Elliott Hughesdd4abe02018-02-05 15:55:19 -08001097 ord(p[1]) == ORD(']')) {
Elliott Hughes23925bb2017-09-22 16:04:20 -07001098 /* accept, terminate */
1099 p += 2;
1100 break;
1101 }
1102 /* EOS without: reject bracket expr */
1103 if (!c)
1104 break;
1105 /* debunk substring */
1106 strndupx(subp, s, p - s - 3, ATEMP);
1107 debunk(subp, subp, p - s - 3 + 1);
1108 /* whither subexpression */
Elliott Hughesdd4abe02018-02-05 15:55:19 -08001109 if (ord(subc) == ORD(':')) {
Elliott Hughes23925bb2017-09-22 16:04:20 -07001110 /* oops, not a range */
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001111
Elliott Hughes23925bb2017-09-22 16:04:20 -07001112 /* match single previous char */
1113 if (lc && (sc == lc))
1114 found = true;
1115 /* match hyphen-minus */
Elliott Hughesdd4abe02018-02-05 15:55:19 -08001116 if (ord(sc) == ORD('-'))
Elliott Hughes23925bb2017-09-22 16:04:20 -07001117 found = true;
1118 /* handle cclass common part */
1119 goto cclass_common;
1120 }
1121 /* collating element or equivalence class */
1122 /* Note: latter are treated as former */
1123 if (ctype(subp[0], C_ASCII) && !subp[1])
1124 /* [.a.] where a is one ASCII char */
1125 c = subp[0];
1126 else
1127 /* force no match */
1128 c = 0;
1129 /* no longer needed */
1130 afree(subp, ATEMP);
1131 /* other meaning below */
1132 subc = 0;
1133 } else if (c == (0x80 | ' ')) {
1134 /* 0x80|' ' is plain (...) */
1135 c = '(' /*)*/;
1136 } else if (!ISMAGIC(c) && (c & 0x80)) {
1137 c &= 0x7F;
1138 subc = '(' /*)*/;
1139 }
1140 }
1141 /* now do the actual range match check */
1142 if (lc != 0 /* && c != 0 */ &&
1143 asciibetical(lc) <= asciibetical(sc) &&
1144 asciibetical(sc) <= asciibetical(c))
1145 found = true;
1146 /* forced next character? */
1147 if (subc) {
1148 c = subc;
1149 goto nextc;
1150 }
1151 /* otherwise, just go on with the pattern string */
1152 }
1153 /* if we broke here, the bracket expression was invalid */
Elliott Hughesdd4abe02018-02-05 15:55:19 -08001154 if (ord(sc) == ORD('['))
Elliott Hughes23925bb2017-09-22 16:04:20 -07001155 /* initial opening bracket as literal match */
1156 return (pat);
1157 /* or rather no match */
1158 return (NULL);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001159}
1160
1161/* Look for next ) or | (if match_sep) in *(foo|bar) pattern */
Geremy Condra03ebf062011-10-12 18:17:24 -07001162static const unsigned char *
1163pat_scan(const unsigned char *p, const unsigned char *pe, bool match_sep)
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001164{
1165 int nest = 0;
1166
1167 for (; p < pe; p++) {
1168 if (!ISMAGIC(*p))
1169 continue;
1170 if ((*++p == /*(*/ ')' && nest-- == 0) ||
1171 (*p == '|' && match_sep && nest == 0))
1172 return (p + 1);
Elliott Hughes23925bb2017-09-22 16:04:20 -07001173 if ((*p & 0x80) && ctype(*p & 0x7F, C_PATMO | C_SPC))
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001174 nest++;
1175 }
1176 return (NULL);
1177}
1178
1179int
Elliott Hughes23925bb2017-09-22 16:04:20 -07001180ascstrcmp(const void *s1, const void *s2)
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001181{
Elliott Hughes23925bb2017-09-22 16:04:20 -07001182 const uint8_t *cp1 = s1, *cp2 = s2;
1183
1184 while (*cp1 == *cp2) {
1185 if (*cp1++ == '\0')
1186 return (0);
1187 ++cp2;
1188 }
1189 return ((int)asciibetical(*cp1) - (int)asciibetical(*cp2));
1190}
1191
1192int
1193ascpstrcmp(const void *pstr1, const void *pstr2)
1194{
1195 return (ascstrcmp(*(const char * const *)pstr1,
1196 *(const char * const *)pstr2));
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001197}
1198
1199/* Initialise a Getopt structure */
1200void
1201ksh_getopt_reset(Getopt *go, int flags)
1202{
1203 go->optind = 1;
1204 go->optarg = NULL;
1205 go->p = 0;
1206 go->flags = flags;
1207 go->info = 0;
1208 go->buf[1] = '\0';
1209}
1210
1211
Geremy Condra03ebf062011-10-12 18:17:24 -07001212/**
1213 * getopt() used for shell built-in commands, the getopts command, and
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001214 * command line options.
1215 * A leading ':' in options means don't print errors, instead return '?'
1216 * or ':' and set go->optarg to the offending option character.
1217 * If GF_ERROR is set (and option doesn't start with :), errors result in
1218 * a call to bi_errorf().
1219 *
1220 * Non-standard features:
1221 * - ';' is like ':' in options, except the argument is optional
1222 * (if it isn't present, optarg is set to 0).
1223 * Used for 'set -o'.
1224 * - ',' is like ':' in options, except the argument always immediately
1225 * follows the option character (optarg is set to the null string if
1226 * the option is missing).
1227 * Used for 'read -u2', 'print -u2' and fc -40.
1228 * - '#' is like ':' in options, expect that the argument is optional
1229 * and must start with a digit. If the argument doesn't start with a
1230 * digit, it is assumed to be missing and normal option processing
1231 * continues (optarg is set to 0 if the option is missing).
1232 * Used for 'typeset -LZ4'.
1233 * - accepts +c as well as -c IF the GF_PLUSOPT flag is present. If an
1234 * option starting with + is accepted, the GI_PLUS flag will be set
1235 * in go->info.
1236 */
1237int
1238ksh_getopt(const char **argv, Getopt *go, const char *optionsp)
1239{
1240 char c;
1241 const char *o;
1242
1243 if (go->p == 0 || (c = argv[go->optind - 1][go->p]) == '\0') {
1244 const char *arg = argv[go->optind], flag = arg ? *arg : '\0';
1245
1246 go->p = 1;
Elliott Hughes96b43632015-07-17 11:39:41 -07001247 if (flag == '-' && ksh_isdash(arg + 1)) {
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001248 go->optind++;
1249 go->p = 0;
1250 go->info |= GI_MINUSMINUS;
1251 return (-1);
1252 }
1253 if (arg == NULL ||
Geremy Condra03ebf062011-10-12 18:17:24 -07001254 ((flag != '-' ) &&
1255 /* neither a - nor a + (if + allowed) */
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001256 (!(go->flags & GF_PLUSOPT) || flag != '+')) ||
1257 (c = arg[1]) == '\0') {
1258 go->p = 0;
1259 return (-1);
1260 }
1261 go->optind++;
1262 go->info &= ~(GI_MINUS|GI_PLUS);
1263 go->info |= flag == '-' ? GI_MINUS : GI_PLUS;
1264 }
1265 go->p++;
Elliott Hughes23925bb2017-09-22 16:04:20 -07001266 if (ctype(c, C_QUEST | C_COLON | C_HASH) || c == ';' || c == ',' ||
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001267 !(o = cstrchr(optionsp, c))) {
1268 if (optionsp[0] == ':') {
1269 go->buf[0] = c;
1270 go->optarg = go->buf;
1271 } else {
Elliott Hughes77740fc2016-08-12 15:06:53 -07001272 warningf(true, Tf_optfoo,
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001273 (go->flags & GF_NONAME) ? "" : argv[0],
Elliott Hughes77740fc2016-08-12 15:06:53 -07001274 (go->flags & GF_NONAME) ? "" : Tcolsp,
1275 c, Tunknown_option);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001276 if (go->flags & GF_ERROR)
1277 bi_errorfz();
1278 }
1279 return ('?');
1280 }
Geremy Condra03ebf062011-10-12 18:17:24 -07001281 /**
1282 * : means argument must be present, may be part of option argument
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001283 * or the next argument
1284 * ; same as : but argument may be missing
1285 * , means argument is part of option argument, and may be null.
1286 */
1287 if (*++o == ':' || *o == ';') {
1288 if (argv[go->optind - 1][go->p])
1289 go->optarg = argv[go->optind - 1] + go->p;
1290 else if (argv[go->optind])
1291 go->optarg = argv[go->optind++];
1292 else if (*o == ';')
1293 go->optarg = NULL;
1294 else {
1295 if (optionsp[0] == ':') {
1296 go->buf[0] = c;
1297 go->optarg = go->buf;
1298 return (':');
1299 }
Elliott Hughes77740fc2016-08-12 15:06:53 -07001300 warningf(true, Tf_optfoo,
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001301 (go->flags & GF_NONAME) ? "" : argv[0],
Elliott Hughes77740fc2016-08-12 15:06:53 -07001302 (go->flags & GF_NONAME) ? "" : Tcolsp,
1303 c, Treq_arg);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001304 if (go->flags & GF_ERROR)
1305 bi_errorfz();
1306 return ('?');
1307 }
1308 go->p = 0;
1309 } else if (*o == ',') {
1310 /* argument is attached to option character, even if null */
1311 go->optarg = argv[go->optind - 1] + go->p;
1312 go->p = 0;
1313 } else if (*o == '#') {
Geremy Condra03ebf062011-10-12 18:17:24 -07001314 /*
1315 * argument is optional and may be attached or unattached
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001316 * but must start with a digit. optarg is set to 0 if the
1317 * argument is missing.
1318 */
1319 if (argv[go->optind - 1][go->p]) {
Elliott Hughes23925bb2017-09-22 16:04:20 -07001320 if (ctype(argv[go->optind - 1][go->p], C_DIGIT)) {
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001321 go->optarg = argv[go->optind - 1] + go->p;
1322 go->p = 0;
1323 } else
1324 go->optarg = NULL;
1325 } else {
Elliott Hughes23925bb2017-09-22 16:04:20 -07001326 if (argv[go->optind] &&
1327 ctype(argv[go->optind][0], C_DIGIT)) {
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001328 go->optarg = argv[go->optind++];
1329 go->p = 0;
1330 } else
1331 go->optarg = NULL;
1332 }
1333 }
1334 return (c);
1335}
1336
Geremy Condra03ebf062011-10-12 18:17:24 -07001337/*
1338 * print variable/alias value using necessary quotes
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001339 * (POSIX says they should be suitable for re-entry...)
1340 * No trailing newline is printed.
1341 */
1342void
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00001343print_value_quoted(struct shf *shf, const char *s)
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001344{
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00001345 unsigned char c;
1346 const unsigned char *p = (const unsigned char *)s;
1347 bool inquote = true;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001348
Elliott Hughes47086262019-03-26 12:34:31 -07001349 /* first, special-case empty strings (for re-entrancy) */
1350 if (!*s) {
1351 shf_putc('\'', shf);
1352 shf_putc('\'', shf);
1353 return;
1354 }
1355
1356 /* non-empty; check whether any quotes are needed */
Elliott Hughes23925bb2017-09-22 16:04:20 -07001357 while (rtt2asc(c = *p++) >= 32)
1358 if (ctype(c, C_QUOTE | C_SPC))
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00001359 inquote = false;
Geremy Condra03ebf062011-10-12 18:17:24 -07001360
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00001361 p = (const unsigned char *)s;
1362 if (c == 0) {
1363 if (inquote) {
1364 /* nope, use the shortcut */
1365 shf_puts(s, shf);
1366 return;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001367 }
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00001368
1369 /* otherwise, quote nicely via state machine */
1370 while ((c = *p++) != 0) {
1371 if (c == '\'') {
1372 /*
1373 * multiple single quotes or any of them
1374 * at the beginning of a string look nicer
1375 * this way than when simply substituting
1376 */
1377 if (inquote) {
1378 shf_putc('\'', shf);
1379 inquote = false;
1380 }
1381 shf_putc('\\', shf);
1382 } else if (!inquote) {
1383 shf_putc('\'', shf);
1384 inquote = true;
1385 }
1386 shf_putc(c, shf);
1387 }
1388 } else {
1389 unsigned int wc;
1390 size_t n;
1391
1392 /* use $'...' quote format */
1393 shf_putc('$', shf);
1394 shf_putc('\'', shf);
1395 while ((c = *p) != 0) {
Elliott Hughes23925bb2017-09-22 16:04:20 -07001396#ifndef MKSH_EBCDIC
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00001397 if (c >= 0xC2) {
1398 n = utf_mbtowc(&wc, (const char *)p);
1399 if (n != (size_t)-1) {
1400 p += n;
1401 shf_fprintf(shf, "\\u%04X", wc);
1402 continue;
1403 }
1404 }
Elliott Hughes23925bb2017-09-22 16:04:20 -07001405#endif
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00001406 ++p;
1407 switch (c) {
1408 /* see unbksl() in this file for comments */
Elliott Hughes23925bb2017-09-22 16:04:20 -07001409 case KSH_BEL:
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00001410 c = 'a';
1411 if (0)
1412 /* FALLTHROUGH */
1413 case '\b':
1414 c = 'b';
1415 if (0)
1416 /* FALLTHROUGH */
1417 case '\f':
1418 c = 'f';
1419 if (0)
1420 /* FALLTHROUGH */
1421 case '\n':
1422 c = 'n';
1423 if (0)
1424 /* FALLTHROUGH */
1425 case '\r':
1426 c = 'r';
1427 if (0)
1428 /* FALLTHROUGH */
1429 case '\t':
1430 c = 't';
1431 if (0)
1432 /* FALLTHROUGH */
Elliott Hughes23925bb2017-09-22 16:04:20 -07001433 case KSH_VTAB:
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00001434 c = 'v';
1435 if (0)
1436 /* FALLTHROUGH */
Elliott Hughes23925bb2017-09-22 16:04:20 -07001437 case KSH_ESC:
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00001438 /* take E not e because \e is \ in *roff */
1439 c = 'E';
1440 /* FALLTHROUGH */
1441 case '\\':
1442 shf_putc('\\', shf);
1443
1444 if (0)
1445 /* FALLTHROUGH */
1446 default:
Elliott Hughes23925bb2017-09-22 16:04:20 -07001447#if defined(MKSH_EBCDIC) || defined(MKSH_FAUX_EBCDIC)
1448 if (ksh_isctrl(c))
1449#else
1450 if (!ctype(c, C_PRINT))
1451#endif
1452 {
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00001453 /* FALLTHROUGH */
1454 case '\'':
1455 shf_fprintf(shf, "\\%03o", c);
1456 break;
1457 }
1458
1459 shf_putc(c, shf);
1460 break;
1461 }
1462 }
1463 inquote = true;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001464 }
1465 if (inquote)
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00001466 shf_putc('\'', shf);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001467}
1468
1469/*
1470 * Print things in columns and rows - func() is called to format
1471 * the i-th element
1472 */
1473void
Elliott Hughes966dd552016-12-08 15:56:04 -08001474print_columns(struct columnise_opts *opts, unsigned int n,
Elliott Hughes96b43632015-07-17 11:39:41 -07001475 void (*func)(char *, size_t, unsigned int, const void *),
Elliott Hughes966dd552016-12-08 15:56:04 -08001476 const void *arg, size_t max_oct, size_t max_colz)
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001477{
Elliott Hughes966dd552016-12-08 15:56:04 -08001478 unsigned int i, r = 0, c, rows, cols, nspace, max_col;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001479 char *str;
1480
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00001481 if (!n)
1482 return;
1483
1484 if (max_colz > 2147483646) {
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001485#ifndef MKSH_SMALL
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00001486 internal_warningf("print_columns called with %s=%zu >= INT_MAX",
1487 "max_col", max_colz);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001488#endif
1489 return;
1490 }
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00001491 max_col = (unsigned int)max_colz;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001492
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00001493 if (max_oct > 2147483646) {
Geremy Condra03ebf062011-10-12 18:17:24 -07001494#ifndef MKSH_SMALL
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00001495 internal_warningf("print_columns called with %s=%zu >= INT_MAX",
1496 "max_oct", max_oct);
Geremy Condra03ebf062011-10-12 18:17:24 -07001497#endif
1498 return;
1499 }
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001500 ++max_oct;
1501 str = alloc(max_oct, ATEMP);
1502
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001503 /*
Elliott Hughes96b43632015-07-17 11:39:41 -07001504 * We use (max_col + 2) to consider the separator space.
1505 * Note that no spaces are printed after the last column
1506 * to avoid problems with terminals that have auto-wrap,
1507 * but we need to also take this into account in x_cols.
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001508 */
Elliott Hughes96b43632015-07-17 11:39:41 -07001509 cols = (x_cols + 1) / (max_col + 2);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001510
1511 /* if we can only print one column anyway, skip the goo */
1512 if (cols < 2) {
Elliott Hughes966dd552016-12-08 15:56:04 -08001513 goto prcols_easy;
1514 while (r < n) {
1515 shf_putc(opts->linesep, opts->shf);
1516 prcols_easy:
1517 (*func)(str, max_oct, r++, arg);
1518 shf_puts(str, opts->shf);
Elliott Hughes96b43632015-07-17 11:39:41 -07001519 }
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001520 goto out;
1521 }
1522
1523 rows = (n + cols - 1) / cols;
Elliott Hughes966dd552016-12-08 15:56:04 -08001524 if (opts->prefcol && cols > rows) {
Thorsten Glaser811a5752013-07-25 14:24:45 +00001525 cols = rows;
1526 rows = (n + cols - 1) / cols;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001527 }
1528
Thorsten Glaser811a5752013-07-25 14:24:45 +00001529 nspace = (x_cols - max_col * cols) / cols;
Elliott Hughes96b43632015-07-17 11:39:41 -07001530 if (nspace < 2)
1531 nspace = 2;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001532 max_col = -max_col;
Elliott Hughes966dd552016-12-08 15:56:04 -08001533 goto prcols_hard;
1534 while (r < rows) {
1535 shf_putchar(opts->linesep, opts->shf);
1536 prcols_hard:
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001537 for (c = 0; c < cols; c++) {
Elliott Hughes96b43632015-07-17 11:39:41 -07001538 if ((i = c * rows + r) >= n)
1539 break;
1540 (*func)(str, max_oct, i, arg);
1541 if (i + rows >= n)
Elliott Hughes966dd552016-12-08 15:56:04 -08001542 shf_puts(str, opts->shf);
Elliott Hughes96b43632015-07-17 11:39:41 -07001543 else
Elliott Hughes966dd552016-12-08 15:56:04 -08001544 shf_fprintf(opts->shf, "%*s%*s",
Elliott Hughes77740fc2016-08-12 15:06:53 -07001545 (int)max_col, str, (int)nspace, null);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001546 }
Elliott Hughes966dd552016-12-08 15:56:04 -08001547 ++r;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001548 }
1549 out:
Elliott Hughes966dd552016-12-08 15:56:04 -08001550 if (opts->do_last)
1551 shf_putchar(opts->linesep, opts->shf);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001552 afree(str, ATEMP);
1553}
1554
Elliott Hughesb27ce952015-04-21 13:39:18 -07001555/* strip all NUL bytes from buf; output is NUL-terminated if stripped */
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001556void
Elliott Hughesb27ce952015-04-21 13:39:18 -07001557strip_nuls(char *buf, size_t len)
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001558{
Elliott Hughesb27ce952015-04-21 13:39:18 -07001559 char *cp, *dp, *ep;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001560
Elliott Hughesb27ce952015-04-21 13:39:18 -07001561 if (!len || !(dp = memchr(buf, '\0', len)))
1562 return;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001563
Elliott Hughesb27ce952015-04-21 13:39:18 -07001564 ep = buf + len;
1565 cp = dp;
1566
1567 cp_has_nul_byte:
1568 while (cp++ < ep && *cp == '\0')
1569 ; /* nothing */
1570 while (cp < ep && *cp != '\0')
1571 *dp++ = *cp++;
1572 if (cp < ep)
1573 goto cp_has_nul_byte;
1574
1575 *dp = '\0';
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001576}
1577
Geremy Condra03ebf062011-10-12 18:17:24 -07001578/*
1579 * Like read(2), but if read fails due to non-blocking flag,
1580 * resets flag and restarts read.
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001581 */
Geremy Condra03ebf062011-10-12 18:17:24 -07001582ssize_t
1583blocking_read(int fd, char *buf, size_t nbytes)
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001584{
Geremy Condra03ebf062011-10-12 18:17:24 -07001585 ssize_t ret;
1586 bool tried_reset = false;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001587
1588 while ((ret = read(fd, buf, nbytes)) < 0) {
1589 if (!tried_reset && errno == EAGAIN) {
1590 if (reset_nonblock(fd) > 0) {
Geremy Condra03ebf062011-10-12 18:17:24 -07001591 tried_reset = true;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001592 continue;
1593 }
1594 errno = EAGAIN;
1595 }
1596 break;
1597 }
1598 return (ret);
1599}
1600
Geremy Condra03ebf062011-10-12 18:17:24 -07001601/*
1602 * Reset the non-blocking flag on the specified file descriptor.
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001603 * Returns -1 if there was an error, 0 if non-blocking wasn't set,
1604 * 1 if it was.
1605 */
1606int
1607reset_nonblock(int fd)
1608{
1609 int flags;
1610
1611 if ((flags = fcntl(fd, F_GETFL, 0)) < 0)
1612 return (-1);
1613 if (!(flags & O_NONBLOCK))
1614 return (0);
1615 flags &= ~O_NONBLOCK;
1616 if (fcntl(fd, F_SETFL, flags) < 0)
1617 return (-1);
1618 return (1);
1619}
1620
Geremy Condra03ebf062011-10-12 18:17:24 -07001621/* getcwd(3) equivalent, allocates from ATEMP but doesn't resize */
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001622char *
Geremy Condra03ebf062011-10-12 18:17:24 -07001623ksh_get_wd(void)
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001624{
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00001625#ifdef MKSH__NO_PATH_MAX
Geremy Condra03ebf062011-10-12 18:17:24 -07001626 char *rv, *cp;
1627
1628 if ((cp = get_current_dir_name())) {
1629 strdupx(rv, cp, ATEMP);
1630 free_gnu_gcdn(cp);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001631 } else
Geremy Condra03ebf062011-10-12 18:17:24 -07001632 rv = NULL;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001633#else
Geremy Condra03ebf062011-10-12 18:17:24 -07001634 char *rv;
1635
1636 if (!getcwd((rv = alloc(PATH_MAX + 1, ATEMP)), PATH_MAX)) {
1637 afree(rv, ATEMP);
1638 rv = NULL;
1639 }
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001640#endif
1641
Geremy Condra03ebf062011-10-12 18:17:24 -07001642 return (rv);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001643}
1644
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00001645#ifndef ELOOP
1646#define ELOOP E2BIG
1647#endif
1648
Geremy Condra03ebf062011-10-12 18:17:24 -07001649char *
1650do_realpath(const char *upath)
1651{
1652 char *xp, *ip, *tp, *ipath, *ldest = NULL;
1653 XString xs;
Elliott Hughes50012062015-03-10 22:22:24 -07001654 size_t pos, len;
Geremy Condra03ebf062011-10-12 18:17:24 -07001655 int llen;
1656 struct stat sb;
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00001657#ifdef MKSH__NO_PATH_MAX
Geremy Condra03ebf062011-10-12 18:17:24 -07001658 size_t ldestlen = 0;
1659#define pathlen sb.st_size
1660#define pathcnd (ldestlen < (pathlen + 1))
1661#else
1662#define pathlen PATH_MAX
1663#define pathcnd (!ldest)
1664#endif
1665 /* max. recursion depth */
1666 int symlinks = 32;
1667
Elliott Hughes96b43632015-07-17 11:39:41 -07001668 if (mksh_abspath(upath)) {
Geremy Condra03ebf062011-10-12 18:17:24 -07001669 /* upath is an absolute pathname */
1670 strdupx(ipath, upath, ATEMP);
Elliott Hughesdd4abe02018-02-05 15:55:19 -08001671#ifdef MKSH_DOSPATH
1672 } else if (mksh_drvltr(upath)) {
1673 /* upath is a drive-relative pathname */
1674 if (getdrvwd(&ldest, ord(*upath)))
1675 return (NULL);
1676 /* A:foo -> A:/cwd/foo; A: -> A:/cwd */
1677 ipath = shf_smprintf(Tf_sss, ldest,
1678 upath[2] ? "/" : "", upath + 2);
1679#endif
Geremy Condra03ebf062011-10-12 18:17:24 -07001680 } else {
1681 /* upath is a relative pathname, prepend cwd */
Elliott Hughes96b43632015-07-17 11:39:41 -07001682 if ((tp = ksh_get_wd()) == NULL || !mksh_abspath(tp))
Geremy Condra03ebf062011-10-12 18:17:24 -07001683 return (NULL);
Elliott Hughes77740fc2016-08-12 15:06:53 -07001684 ipath = shf_smprintf(Tf_sss, tp, "/", upath);
Geremy Condra03ebf062011-10-12 18:17:24 -07001685 afree(tp, ATEMP);
1686 }
1687
1688 /* ipath and upath are in memory at the same time -> unchecked */
1689 Xinit(xs, xp, strlen(ip = ipath) + 1, ATEMP);
1690
1691 /* now jump into the deep of the loop */
1692 goto beginning_of_a_pathname;
1693
1694 while (*ip) {
1695 /* skip slashes in input */
Elliott Hughes966dd552016-12-08 15:56:04 -08001696 while (mksh_cdirsep(*ip))
Geremy Condra03ebf062011-10-12 18:17:24 -07001697 ++ip;
1698 if (!*ip)
1699 break;
1700
1701 /* get next pathname component from input */
1702 tp = ip;
Elliott Hughes966dd552016-12-08 15:56:04 -08001703 while (*ip && !mksh_cdirsep(*ip))
Geremy Condra03ebf062011-10-12 18:17:24 -07001704 ++ip;
1705 len = ip - tp;
1706
1707 /* check input for "." and ".." */
1708 if (tp[0] == '.') {
1709 if (len == 1)
1710 /* just continue with the next one */
1711 continue;
1712 else if (len == 2 && tp[1] == '.') {
1713 /* strip off last pathname component */
Elliott Hughesdd4abe02018-02-05 15:55:19 -08001714 /*XXX consider a rooted pathname */
Geremy Condra03ebf062011-10-12 18:17:24 -07001715 while (xp > Xstring(xs, xp))
Elliott Hughes966dd552016-12-08 15:56:04 -08001716 if (mksh_cdirsep(*--xp))
Geremy Condra03ebf062011-10-12 18:17:24 -07001717 break;
1718 /* then continue with the next one */
1719 continue;
1720 }
1721 }
1722
1723 /* store output position away, then append slash to output */
1724 pos = Xsavepos(xs, xp);
1725 /* 1 for the '/' and len + 1 for tp and the NUL from below */
1726 XcheckN(xs, xp, 1 + len + 1);
1727 Xput(xs, xp, '/');
1728
1729 /* append next pathname component to output */
1730 memcpy(xp, tp, len);
1731 xp += len;
1732 *xp = '\0';
1733
1734 /* lstat the current output, see if it's a symlink */
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00001735 if (mksh_lstat(Xstring(xs, xp), &sb)) {
Geremy Condra03ebf062011-10-12 18:17:24 -07001736 /* lstat failed */
1737 if (errno == ENOENT) {
1738 /* because the pathname does not exist */
Elliott Hughes966dd552016-12-08 15:56:04 -08001739 while (mksh_cdirsep(*ip))
Geremy Condra03ebf062011-10-12 18:17:24 -07001740 /* skip any trailing slashes */
1741 ++ip;
1742 /* no more components left? */
1743 if (!*ip)
1744 /* we can still return successfully */
1745 break;
1746 /* more components left? fall through */
1747 }
1748 /* not ENOENT or not at the end of ipath */
1749 goto notfound;
1750 }
1751
1752 /* check if we encountered a symlink? */
1753 if (S_ISLNK(sb.st_mode)) {
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00001754#ifndef MKSH__NO_SYMLINK
Geremy Condra03ebf062011-10-12 18:17:24 -07001755 /* reached maximum recursion depth? */
1756 if (!symlinks--) {
1757 /* yep, prevent infinite loops */
1758 errno = ELOOP;
1759 goto notfound;
1760 }
1761
1762 /* get symlink(7) target */
1763 if (pathcnd) {
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00001764#ifdef MKSH__NO_PATH_MAX
Geremy Condra03ebf062011-10-12 18:17:24 -07001765 if (notoktoadd(pathlen, 1)) {
1766 errno = ENAMETOOLONG;
1767 goto notfound;
1768 }
1769#endif
1770 ldest = aresize(ldest, pathlen + 1, ATEMP);
1771 }
1772 llen = readlink(Xstring(xs, xp), ldest, pathlen);
1773 if (llen < 0)
1774 /* oops... */
1775 goto notfound;
1776 ldest[llen] = '\0';
1777
1778 /*
1779 * restart if symlink target is an absolute path,
1780 * otherwise continue with currently resolved prefix
1781 */
Elliott Hughesdd4abe02018-02-05 15:55:19 -08001782#ifdef MKSH_DOSPATH
1783 assemble_symlink:
1784#endif
Geremy Condra03ebf062011-10-12 18:17:24 -07001785 /* append rest of current input path to link target */
Elliott Hughes77740fc2016-08-12 15:06:53 -07001786 tp = shf_smprintf(Tf_sss, ldest, *ip ? "/" : "", ip);
Geremy Condra03ebf062011-10-12 18:17:24 -07001787 afree(ipath, ATEMP);
1788 ip = ipath = tp;
Elliott Hughesdd4abe02018-02-05 15:55:19 -08001789 if (!mksh_abspath(ipath)) {
1790#ifdef MKSH_DOSPATH
1791 /* symlink target might be drive-relative */
1792 if (mksh_drvltr(ipath)) {
1793 if (getdrvwd(&ldest, ord(*ipath)))
1794 goto notfound;
1795 ip += 2;
1796 goto assemble_symlink;
1797 }
1798#endif
Geremy Condra03ebf062011-10-12 18:17:24 -07001799 /* symlink target is a relative path */
1800 xp = Xrestpos(xs, xp, pos);
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00001801 } else
1802#endif
1803 {
Geremy Condra03ebf062011-10-12 18:17:24 -07001804 /* symlink target is an absolute path */
1805 xp = Xstring(xs, xp);
1806 beginning_of_a_pathname:
Elliott Hughesdd4abe02018-02-05 15:55:19 -08001807 /* assert: mksh_abspath(ip == ipath) */
Geremy Condra03ebf062011-10-12 18:17:24 -07001808 /* assert: xp == xs.beg => start of path */
1809
1810 /* exactly two leading slashes? (SUSv4 3.266) */
Elliott Hughesa3c3f962017-04-12 16:52:30 -07001811 if (ip[1] == ip[0] && !mksh_cdirsep(ip[2])) {
Geremy Condra03ebf062011-10-12 18:17:24 -07001812 /* keep them, e.g. for UNC pathnames */
1813 Xput(xs, xp, '/');
1814 }
Elliott Hughesdd4abe02018-02-05 15:55:19 -08001815#ifdef MKSH_DOSPATH
1816 /* drive letter? */
1817 if (mksh_drvltr(ip)) {
1818 /* keep it */
1819 Xput(xs, xp, *ip++);
1820 Xput(xs, xp, *ip++);
1821 }
1822#endif
Geremy Condra03ebf062011-10-12 18:17:24 -07001823 }
1824 }
1825 /* otherwise (no symlink) merely go on */
1826 }
1827
1828 /*
1829 * either found the target and successfully resolved it,
1830 * or found its parent directory and may create it
1831 */
1832 if (Xlength(xs, xp) == 0)
1833 /*
1834 * if the resolved pathname is "", make it "/",
1835 * otherwise do not add a trailing slash
1836 */
1837 Xput(xs, xp, '/');
1838 Xput(xs, xp, '\0');
1839
1840 /*
1841 * if source path had a trailing slash, check if target path
1842 * is not a non-directory existing file
1843 */
Elliott Hughes966dd552016-12-08 15:56:04 -08001844 if (ip > ipath && mksh_cdirsep(ip[-1])) {
Geremy Condra03ebf062011-10-12 18:17:24 -07001845 if (stat(Xstring(xs, xp), &sb)) {
1846 if (errno != ENOENT)
1847 goto notfound;
1848 } else if (!S_ISDIR(sb.st_mode)) {
1849 errno = ENOTDIR;
1850 goto notfound;
1851 }
1852 /* target now either does not exist or is a directory */
1853 }
1854
1855 /* return target path */
Elliott Hughesfc0307d2016-02-02 15:26:47 -08001856 afree(ldest, ATEMP);
Geremy Condra03ebf062011-10-12 18:17:24 -07001857 afree(ipath, ATEMP);
1858 return (Xclose(xs, xp));
1859
1860 notfound:
1861 /* save; freeing memory might trash it */
1862 llen = errno;
Elliott Hughesfc0307d2016-02-02 15:26:47 -08001863 afree(ldest, ATEMP);
Geremy Condra03ebf062011-10-12 18:17:24 -07001864 afree(ipath, ATEMP);
1865 Xfree(xs, xp);
1866 errno = llen;
1867 return (NULL);
1868
1869#undef pathlen
1870#undef pathcnd
1871}
1872
1873/**
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001874 * Makes a filename into result using the following algorithm.
1875 * - make result NULL
1876 * - if file starts with '/', append file to result & set cdpathp to NULL
1877 * - if file starts with ./ or ../ append cwd and file to result
1878 * and set cdpathp to NULL
1879 * - if the first element of cdpathp doesnt start with a '/' xx or '.' xx
1880 * then cwd is appended to result.
1881 * - the first element of cdpathp is appended to result
1882 * - file is appended to result
1883 * - cdpathp is set to the start of the next element in cdpathp (or NULL
1884 * if there are no more elements.
1885 * The return value indicates whether a non-null element from cdpathp
1886 * was appended to result.
1887 */
Geremy Condra03ebf062011-10-12 18:17:24 -07001888static int
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001889make_path(const char *cwd, const char *file,
Geremy Condra03ebf062011-10-12 18:17:24 -07001890 /* pointer to colon-separated list */
1891 char **cdpathp,
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001892 XString *xsp,
1893 int *phys_pathp)
1894{
1895 int rval = 0;
1896 bool use_cdpath = true;
1897 char *plist;
Geremy Condra03ebf062011-10-12 18:17:24 -07001898 size_t len, plen = 0;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001899 char *xp = Xstring(*xsp, xp);
1900
1901 if (!file)
1902 file = null;
1903
Elliott Hughes96b43632015-07-17 11:39:41 -07001904 if (mksh_abspath(file)) {
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001905 *phys_pathp = 0;
1906 use_cdpath = false;
1907 } else {
1908 if (file[0] == '.') {
1909 char c = file[1];
1910
1911 if (c == '.')
1912 c = file[2];
Elliott Hughes966dd552016-12-08 15:56:04 -08001913 if (mksh_cdirsep(c) || c == '\0')
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001914 use_cdpath = false;
1915 }
1916
1917 plist = *cdpathp;
1918 if (!plist)
1919 use_cdpath = false;
1920 else if (use_cdpath) {
Elliott Hughes96b43632015-07-17 11:39:41 -07001921 char *pend = plist;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001922
Elliott Hughes96b43632015-07-17 11:39:41 -07001923 while (*pend && *pend != MKSH_PATHSEPC)
1924 ++pend;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001925 plen = pend - plist;
1926 *cdpathp = *pend ? pend + 1 : NULL;
1927 }
1928
Elliott Hughes96b43632015-07-17 11:39:41 -07001929 if ((!use_cdpath || !plen || !mksh_abspath(plist)) &&
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001930 (cwd && *cwd)) {
1931 len = strlen(cwd);
1932 XcheckN(*xsp, xp, len);
1933 memcpy(xp, cwd, len);
1934 xp += len;
Elliott Hughes966dd552016-12-08 15:56:04 -08001935 if (!mksh_cdirsep(cwd[len - 1]))
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001936 Xput(*xsp, xp, '/');
1937 }
1938 *phys_pathp = Xlength(*xsp, xp);
1939 if (use_cdpath && plen) {
1940 XcheckN(*xsp, xp, plen);
1941 memcpy(xp, plist, plen);
1942 xp += plen;
Elliott Hughes966dd552016-12-08 15:56:04 -08001943 if (!mksh_cdirsep(plist[plen - 1]))
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001944 Xput(*xsp, xp, '/');
1945 rval = 1;
1946 }
1947 }
1948
1949 len = strlen(file) + 1;
1950 XcheckN(*xsp, xp, len);
1951 memcpy(xp, file, len);
1952
1953 if (!use_cdpath)
1954 *cdpathp = NULL;
1955
1956 return (rval);
1957}
1958
Geremy Condra03ebf062011-10-12 18:17:24 -07001959/*-
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001960 * Simplify pathnames containing "." and ".." entries.
Geremy Condra03ebf062011-10-12 18:17:24 -07001961 *
1962 * simplify_path(this) = that
1963 * /a/b/c/./../d/.. /a/b
1964 * //./C/foo/bar/../baz //C/foo/baz
1965 * /foo/ /foo
1966 * /foo/../../bar /bar
1967 * /foo/./blah/.. /foo
1968 * . .
1969 * .. ..
1970 * ./foo foo
1971 * foo/../../../bar ../../bar
Elliott Hughesdd4abe02018-02-05 15:55:19 -08001972 * C:/foo/../.. C:/
1973 * C:. C:
1974 * C:.. C:..
1975 * C:foo/../../blah C:../blah
1976 *
1977 * XXX consider a rooted pathname: we cannot really 'cd ..' for
1978 * pathnames like: '/', 'c:/', '//foo', '//foo/', '/@unixroot/'
1979 * (no effect), 'c:', 'c:.' (effect is retaining the '../') but
1980 * we need to honour this throughout the shell
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001981 */
1982void
Geremy Condra03ebf062011-10-12 18:17:24 -07001983simplify_path(char *p)
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001984{
Geremy Condra03ebf062011-10-12 18:17:24 -07001985 char *dp, *ip, *sp, *tp;
1986 size_t len;
1987 bool needslash;
Elliott Hughesdd4abe02018-02-05 15:55:19 -08001988#ifdef MKSH_DOSPATH
1989 bool needdot = true;
1990
1991 /* keep drive letter */
1992 if (mksh_drvltr(p)) {
1993 p += 2;
1994 needdot = false;
1995 }
1996#else
1997#define needdot true
1998#endif
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07001999
Geremy Condra03ebf062011-10-12 18:17:24 -07002000 switch (*p) {
2001 case 0:
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002002 return;
Geremy Condra03ebf062011-10-12 18:17:24 -07002003 case '/':
Elliott Hughesa3c3f962017-04-12 16:52:30 -07002004#ifdef MKSH_DOSPATH
Elliott Hughes966dd552016-12-08 15:56:04 -08002005 case '\\':
2006#endif
Elliott Hughesa3c3f962017-04-12 16:52:30 -07002007 /* exactly two leading slashes? (SUSv4 3.266) */
2008 if (p[1] == p[0] && !mksh_cdirsep(p[2]))
2009 /* keep them, e.g. for UNC pathnames */
2010 ++p;
Geremy Condra03ebf062011-10-12 18:17:24 -07002011 needslash = true;
2012 break;
2013 default:
2014 needslash = false;
2015 }
2016 dp = ip = sp = p;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002017
Geremy Condra03ebf062011-10-12 18:17:24 -07002018 while (*ip) {
2019 /* skip slashes in input */
Elliott Hughes966dd552016-12-08 15:56:04 -08002020 while (mksh_cdirsep(*ip))
Geremy Condra03ebf062011-10-12 18:17:24 -07002021 ++ip;
2022 if (!*ip)
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002023 break;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002024
Geremy Condra03ebf062011-10-12 18:17:24 -07002025 /* get next pathname component from input */
2026 tp = ip;
Elliott Hughes966dd552016-12-08 15:56:04 -08002027 while (*ip && !mksh_cdirsep(*ip))
Geremy Condra03ebf062011-10-12 18:17:24 -07002028 ++ip;
2029 len = ip - tp;
2030
2031 /* check input for "." and ".." */
2032 if (tp[0] == '.') {
2033 if (len == 1)
2034 /* just continue with the next one */
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002035 continue;
Geremy Condra03ebf062011-10-12 18:17:24 -07002036 else if (len == 2 && tp[1] == '.') {
Elliott Hughesdd4abe02018-02-05 15:55:19 -08002037 /* parent level, but how? (see above) */
Elliott Hughes96b43632015-07-17 11:39:41 -07002038 if (mksh_abspath(p))
Geremy Condra03ebf062011-10-12 18:17:24 -07002039 /* absolute path, only one way */
2040 goto strip_last_component;
2041 else if (dp > sp) {
2042 /* relative path, with subpaths */
2043 needslash = false;
2044 strip_last_component:
2045 /* strip off last pathname component */
2046 while (dp > sp)
Elliott Hughes966dd552016-12-08 15:56:04 -08002047 if (mksh_cdirsep(*--dp))
Geremy Condra03ebf062011-10-12 18:17:24 -07002048 break;
2049 } else {
2050 /* relative path, at its beginning */
2051 if (needslash)
2052 /* or already dotdot-slash'd */
2053 *dp++ = '/';
2054 /* keep dotdot-slash if not absolute */
2055 *dp++ = '.';
2056 *dp++ = '.';
2057 needslash = true;
2058 sp = dp;
2059 }
2060 /* then continue with the next one */
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002061 continue;
2062 }
2063 }
2064
Geremy Condra03ebf062011-10-12 18:17:24 -07002065 if (needslash)
2066 *dp++ = '/';
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002067
Geremy Condra03ebf062011-10-12 18:17:24 -07002068 /* append next pathname component to output */
2069 memmove(dp, tp, len);
2070 dp += len;
2071
2072 /* append slash if we continue */
2073 needslash = true;
2074 /* try next component */
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002075 }
Elliott Hughesdd4abe02018-02-05 15:55:19 -08002076 if (dp == p) {
2077 /* empty path -> dot (or slash, when absolute) */
2078 if (needslash)
2079 *dp++ = '/';
2080 else if (needdot)
2081 *dp++ = '.';
2082 }
Geremy Condra03ebf062011-10-12 18:17:24 -07002083 *dp = '\0';
Elliott Hughesdd4abe02018-02-05 15:55:19 -08002084#undef needdot
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002085}
2086
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002087void
Geremy Condra03ebf062011-10-12 18:17:24 -07002088set_current_wd(const char *nwd)
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002089{
Geremy Condra03ebf062011-10-12 18:17:24 -07002090 char *allocd = NULL;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002091
Geremy Condra03ebf062011-10-12 18:17:24 -07002092 if (nwd == NULL) {
2093 allocd = ksh_get_wd();
2094 nwd = allocd ? allocd : null;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002095 }
Geremy Condra03ebf062011-10-12 18:17:24 -07002096
2097 afree(current_wd, APERM);
2098 strdupx(current_wd, nwd, APERM);
2099
2100 afree(allocd, ATEMP);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002101}
2102
Geremy Condra03ebf062011-10-12 18:17:24 -07002103int
2104c_cd(const char **wp)
2105{
2106 int optc, rv, phys_path;
2107 bool physical = tobool(Flag(FPHYSICAL));
2108 /* was a node from cdpath added in? */
2109 int cdnode;
2110 /* show where we went?, error for $PWD */
2111 bool printpath = false, eflag = false;
2112 struct tbl *pwd_s, *oldpwd_s;
2113 XString xs;
2114 char *dir, *allocd = NULL, *tryp, *pwd, *cdpath;
2115
2116 while ((optc = ksh_getopt(wp, &builtin_opt, "eLP")) != -1)
2117 switch (optc) {
2118 case 'e':
2119 eflag = true;
2120 break;
2121 case 'L':
2122 physical = false;
2123 break;
2124 case 'P':
2125 physical = true;
2126 break;
2127 case '?':
2128 return (2);
2129 }
2130 wp += builtin_opt.optind;
2131
2132 if (Flag(FRESTRICTED)) {
Elliott Hughes77740fc2016-08-12 15:06:53 -07002133 bi_errorf(Tcant_cd);
Geremy Condra03ebf062011-10-12 18:17:24 -07002134 return (2);
2135 }
2136
Elliott Hughes77740fc2016-08-12 15:06:53 -07002137 pwd_s = global(TPWD);
2138 oldpwd_s = global(TOLDPWD);
Geremy Condra03ebf062011-10-12 18:17:24 -07002139
2140 if (!wp[0]) {
2141 /* No arguments - go home */
2142 if ((dir = str_val(global("HOME"))) == null) {
2143 bi_errorf("no home directory (HOME not set)");
2144 return (2);
2145 }
2146 } else if (!wp[1]) {
2147 /* One argument: - or dir */
2148 strdupx(allocd, wp[0], ATEMP);
2149 if (ksh_isdash((dir = allocd))) {
2150 afree(allocd, ATEMP);
2151 allocd = NULL;
2152 dir = str_val(oldpwd_s);
2153 if (dir == null) {
Elliott Hughes77740fc2016-08-12 15:06:53 -07002154 bi_errorf(Tno_OLDPWD);
Geremy Condra03ebf062011-10-12 18:17:24 -07002155 return (2);
2156 }
2157 printpath = true;
2158 }
2159 } else if (!wp[2]) {
2160 /* Two arguments - substitute arg1 in PWD for arg2 */
2161 size_t ilen, olen, nlen, elen;
2162 char *cp;
2163
2164 if (!current_wd[0]) {
2165 bi_errorf("can't determine current directory");
2166 return (2);
2167 }
2168 /*
2169 * substitute arg1 for arg2 in current path.
2170 * if the first substitution fails because the cd fails
2171 * we could try to find another substitution. For now
2172 * we don't
2173 */
2174 if ((cp = strstr(current_wd, wp[0])) == NULL) {
Elliott Hughes77740fc2016-08-12 15:06:53 -07002175 bi_errorf(Tbadsubst);
Geremy Condra03ebf062011-10-12 18:17:24 -07002176 return (2);
2177 }
2178 /*-
2179 * ilen = part of current_wd before wp[0]
2180 * elen = part of current_wd after wp[0]
2181 * because current_wd and wp[1] need to be in memory at the
2182 * same time beforehand the addition can stay unchecked
2183 */
2184 ilen = cp - current_wd;
2185 olen = strlen(wp[0]);
2186 nlen = strlen(wp[1]);
2187 elen = strlen(current_wd + ilen + olen) + 1;
2188 dir = allocd = alloc(ilen + nlen + elen, ATEMP);
2189 memcpy(dir, current_wd, ilen);
2190 memcpy(dir + ilen, wp[1], nlen);
2191 memcpy(dir + ilen + nlen, current_wd + ilen + olen, elen);
2192 printpath = true;
2193 } else {
Elliott Hughes77740fc2016-08-12 15:06:53 -07002194 bi_errorf(Ttoo_many_args);
Geremy Condra03ebf062011-10-12 18:17:24 -07002195 return (2);
2196 }
2197
Elliott Hughesdd4abe02018-02-05 15:55:19 -08002198#ifdef MKSH_DOSPATH
2199 tryp = NULL;
2200 if (mksh_drvltr(dir) && !mksh_cdirsep(dir[2]) &&
2201 !getdrvwd(&tryp, ord(*dir))) {
2202 dir = shf_smprintf(Tf_sss, tryp,
2203 dir[2] ? "/" : "", dir + 2);
2204 afree(tryp, ATEMP);
2205 afree(allocd, ATEMP);
2206 allocd = dir;
2207 }
2208#endif
2209
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00002210#ifdef MKSH__NO_PATH_MAX
Geremy Condra03ebf062011-10-12 18:17:24 -07002211 /* only a first guess; make_path will enlarge xs if necessary */
2212 XinitN(xs, 1024, ATEMP);
2213#else
2214 XinitN(xs, PATH_MAX, ATEMP);
2215#endif
2216
2217 cdpath = str_val(global("CDPATH"));
2218 do {
2219 cdnode = make_path(current_wd, dir, &cdpath, &xs, &phys_path);
2220 if (physical)
2221 rv = chdir(tryp = Xstring(xs, xp) + phys_path);
2222 else {
2223 simplify_path(Xstring(xs, xp));
2224 rv = chdir(tryp = Xstring(xs, xp));
2225 }
2226 } while (rv < 0 && cdpath != NULL);
2227
2228 if (rv < 0) {
2229 if (cdnode)
Elliott Hughes77740fc2016-08-12 15:06:53 -07002230 bi_errorf(Tf_sD_s, dir, "bad directory");
Geremy Condra03ebf062011-10-12 18:17:24 -07002231 else
Elliott Hughes77740fc2016-08-12 15:06:53 -07002232 bi_errorf(Tf_sD_s, tryp, cstrerror(errno));
Geremy Condra03ebf062011-10-12 18:17:24 -07002233 afree(allocd, ATEMP);
2234 Xfree(xs, xp);
2235 return (2);
2236 }
2237
2238 rv = 0;
2239
2240 /* allocd (above) => dir, which is no longer used */
2241 afree(allocd, ATEMP);
2242 allocd = NULL;
2243
2244 /* Clear out tracked aliases with relative paths */
2245 flushcom(false);
2246
2247 /*
2248 * Set OLDPWD (note: unsetting OLDPWD does not disable this
2249 * setting in AT&T ksh)
2250 */
2251 if (current_wd[0])
2252 /* Ignore failure (happens if readonly or integer) */
2253 setstr(oldpwd_s, current_wd, KSH_RETURN_ERROR);
2254
Elliott Hughes96b43632015-07-17 11:39:41 -07002255 if (!mksh_abspath(Xstring(xs, xp))) {
Geremy Condra03ebf062011-10-12 18:17:24 -07002256 pwd = NULL;
2257 } else if (!physical) {
2258 goto norealpath_PWD;
2259 } else if ((pwd = allocd = do_realpath(Xstring(xs, xp))) == NULL) {
2260 if (eflag)
2261 rv = 1;
2262 norealpath_PWD:
2263 pwd = Xstring(xs, xp);
2264 }
2265
2266 /* Set PWD */
2267 if (pwd) {
2268 char *ptmp = pwd;
2269
2270 set_current_wd(ptmp);
2271 /* Ignore failure (happens if readonly or integer) */
2272 setstr(pwd_s, ptmp, KSH_RETURN_ERROR);
2273 } else {
2274 set_current_wd(null);
2275 pwd = Xstring(xs, xp);
2276 /* XXX unset $PWD? */
2277 if (eflag)
2278 rv = 1;
2279 }
2280 if (printpath || cdnode)
Elliott Hughes77740fc2016-08-12 15:06:53 -07002281 shprintf(Tf_sN, pwd);
Geremy Condra03ebf062011-10-12 18:17:24 -07002282
2283 afree(allocd, ATEMP);
2284 Xfree(xs, xp);
2285 return (rv);
2286}
2287
2288
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00002289#ifdef KSH_CHVT_CODE
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002290extern void chvt_reinit(void);
2291
2292static void
Thorsten Glaser811a5752013-07-25 14:24:45 +00002293chvt(const Getopt *go)
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002294{
Thorsten Glaser811a5752013-07-25 14:24:45 +00002295 const char *dv = go->optarg;
2296 char *cp = NULL;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002297 int fd;
2298
Thorsten Glaser811a5752013-07-25 14:24:45 +00002299 switch (*dv) {
2300 case '-':
2301 dv = "/dev/null";
2302 break;
2303 case '!':
2304 ++dv;
2305 /* FALLTHROUGH */
2306 default: {
2307 struct stat sb;
2308
2309 if (stat(dv, &sb)) {
2310 cp = shf_smprintf("/dev/ttyC%s", dv);
2311 dv = cp;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002312 if (stat(dv, &sb)) {
Thorsten Glaser811a5752013-07-25 14:24:45 +00002313 memmove(cp + 1, cp, /* /dev/tty */ 8);
2314 dv = cp + 1;
2315 if (stat(dv, &sb)) {
Elliott Hughes77740fc2016-08-12 15:06:53 -07002316 errorf(Tf_sD_sD_s, "chvt",
Thorsten Glaser811a5752013-07-25 14:24:45 +00002317 "can't find tty", go->optarg);
2318 }
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002319 }
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002320 }
2321 if (!(sb.st_mode & S_IFCHR))
Elliott Hughes77740fc2016-08-12 15:06:53 -07002322 errorf(Tf_sD_sD_s, "chvt", "not a char device", dv);
Thorsten Glaser811a5752013-07-25 14:24:45 +00002323#ifndef MKSH_DISABLE_REVOKE_WARNING
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002324#if HAVE_REVOKE
Thorsten Glaser811a5752013-07-25 14:24:45 +00002325 if (revoke(dv))
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002326#endif
Elliott Hughes77740fc2016-08-12 15:06:53 -07002327 warningf(false, Tf_sD_s_s, "chvt",
Geremy Condra03ebf062011-10-12 18:17:24 -07002328 "new shell is potentially insecure, can't revoke",
Thorsten Glaser811a5752013-07-25 14:24:45 +00002329 dv);
2330#endif
2331 }
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002332 }
Elliott Hughes96b43632015-07-17 11:39:41 -07002333 if ((fd = binopen2(dv, O_RDWR)) < 0) {
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002334 sleep(1);
Elliott Hughes96b43632015-07-17 11:39:41 -07002335 if ((fd = binopen2(dv, O_RDWR)) < 0) {
Elliott Hughes77740fc2016-08-12 15:06:53 -07002336 errorf(Tf_sD_s_s, "chvt", Tcant_open, dv);
Thorsten Glaser811a5752013-07-25 14:24:45 +00002337 }
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002338 }
Thorsten Glaser811a5752013-07-25 14:24:45 +00002339 if (go->optarg[0] != '!') {
2340 switch (fork()) {
2341 case -1:
Elliott Hughes77740fc2016-08-12 15:06:53 -07002342 errorf(Tf_sD_s_s, "chvt", "fork", "failed");
Thorsten Glaser811a5752013-07-25 14:24:45 +00002343 case 0:
2344 break;
2345 default:
2346 exit(0);
2347 }
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002348 }
2349 if (setsid() == -1)
Elliott Hughes77740fc2016-08-12 15:06:53 -07002350 errorf(Tf_sD_s_s, "chvt", "setsid", "failed");
Thorsten Glaser811a5752013-07-25 14:24:45 +00002351 if (go->optarg[0] != '-') {
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002352 if (ioctl(fd, TIOCSCTTY, NULL) == -1)
Elliott Hughes77740fc2016-08-12 15:06:53 -07002353 errorf(Tf_sD_s_s, "chvt", "TIOCSCTTY", "failed");
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002354 if (tcflush(fd, TCIOFLUSH))
Elliott Hughes77740fc2016-08-12 15:06:53 -07002355 errorf(Tf_sD_s_s, "chvt", "TCIOFLUSH", "failed");
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002356 }
2357 ksh_dup2(fd, 0, false);
2358 ksh_dup2(fd, 1, false);
2359 ksh_dup2(fd, 2, false);
2360 if (fd > 2)
2361 close(fd);
Thorsten Glaser811a5752013-07-25 14:24:45 +00002362 rndset((unsigned long)chvt_rndsetup(go, sizeof(Getopt)));
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002363 chvt_reinit();
2364}
2365#endif
2366
2367#ifdef DEBUG
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002368char *
2369strchr(char *p, int ch)
2370{
2371 for (;; ++p) {
2372 if (*p == ch)
2373 return (p);
2374 if (!*p)
2375 return (NULL);
2376 }
2377 /* NOTREACHED */
2378}
2379
2380char *
2381strstr(char *b, const char *l)
2382{
2383 char first, c;
2384 size_t n;
2385
2386 if ((first = *l++) == '\0')
2387 return (b);
2388 n = strlen(l);
2389 strstr_look:
2390 while ((c = *b++) != first)
2391 if (c == '\0')
2392 return (NULL);
2393 if (strncmp(b, l, n))
2394 goto strstr_look;
2395 return (b - 1);
2396}
2397#endif
2398
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00002399#if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002400char *
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00002401strndup_i(const char *src, size_t len, Area *ap)
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002402{
2403 char *dst = NULL;
2404
2405 if (src != NULL) {
2406 dst = alloc(len + 1, ap);
2407 memcpy(dst, src, len);
2408 dst[len] = '\0';
2409 }
2410 return (dst);
2411}
2412
2413char *
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00002414strdup_i(const char *src, Area *ap)
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002415{
Thorsten Glaserc2dc5de2013-02-18 23:02:51 +00002416 return (src == NULL ? NULL : strndup_i(src, strlen(src), ap));
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002417}
2418#endif
2419
2420#if !HAVE_GETRUSAGE
2421#define INVTCK(r,t) do { \
2422 r.tv_usec = ((t) % (1000000 / CLK_TCK)) * (1000000 / CLK_TCK); \
2423 r.tv_sec = (t) / CLK_TCK; \
2424} while (/* CONSTCOND */ 0)
2425
2426int
2427getrusage(int what, struct rusage *ru)
2428{
2429 struct tms tms;
2430 clock_t u, s;
2431
2432 if (/* ru == NULL || */ times(&tms) == (clock_t)-1)
2433 return (-1);
2434
2435 switch (what) {
2436 case RUSAGE_SELF:
2437 u = tms.tms_utime;
2438 s = tms.tms_stime;
2439 break;
2440 case RUSAGE_CHILDREN:
2441 u = tms.tms_cutime;
2442 s = tms.tms_cstime;
2443 break;
2444 default:
2445 errno = EINVAL;
2446 return (-1);
2447 }
2448 INVTCK(ru->ru_utime, u);
2449 INVTCK(ru->ru_stime, s);
2450 return (0);
2451}
2452#endif
2453
2454/*
2455 * process the string available via fg (get a char)
2456 * and fp (put back a char) for backslash escapes,
2457 * assuming the first call to *fg gets the char di-
2458 * rectly after the backslash; return the character
Elliott Hughes47086262019-03-26 12:34:31 -07002459 * (0..0xFF), UCS (wc + 0x100), or -1 if no known
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002460 * escape sequence was found
2461 */
2462int
2463unbksl(bool cstyle, int (*fg)(void), void (*fp)(int))
2464{
Elliott Hughesa3c3f962017-04-12 16:52:30 -07002465 int wc, i, c, fc, n;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002466
2467 fc = (*fg)();
2468 switch (fc) {
2469 case 'a':
Elliott Hughes23925bb2017-09-22 16:04:20 -07002470 wc = KSH_BEL;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002471 break;
2472 case 'b':
2473 wc = '\b';
2474 break;
2475 case 'c':
2476 if (!cstyle)
2477 goto unknown_escape;
2478 c = (*fg)();
Elliott Hughes23925bb2017-09-22 16:04:20 -07002479 wc = ksh_toctrl(c);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002480 break;
2481 case 'E':
2482 case 'e':
Elliott Hughes23925bb2017-09-22 16:04:20 -07002483 wc = KSH_ESC;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002484 break;
2485 case 'f':
2486 wc = '\f';
2487 break;
2488 case 'n':
2489 wc = '\n';
2490 break;
2491 case 'r':
2492 wc = '\r';
2493 break;
2494 case 't':
2495 wc = '\t';
2496 break;
2497 case 'v':
Elliott Hughes23925bb2017-09-22 16:04:20 -07002498 wc = KSH_VTAB;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002499 break;
2500 case '1':
2501 case '2':
2502 case '3':
2503 case '4':
2504 case '5':
2505 case '6':
2506 case '7':
2507 if (!cstyle)
2508 goto unknown_escape;
2509 /* FALLTHROUGH */
2510 case '0':
2511 if (cstyle)
2512 (*fp)(fc);
2513 /*
2514 * look for an octal number with up to three
2515 * digits, not counting the leading zero;
2516 * convert it to a raw octet
2517 */
2518 wc = 0;
2519 i = 3;
2520 while (i--)
Elliott Hughes23925bb2017-09-22 16:04:20 -07002521 if (ctype((c = (*fg)()), C_OCTAL))
Elliott Hughes96b43632015-07-17 11:39:41 -07002522 wc = (wc << 3) + ksh_numdig(c);
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002523 else {
2524 (*fp)(c);
2525 break;
2526 }
2527 break;
2528 case 'U':
2529 i = 8;
Geremy Condra03ebf062011-10-12 18:17:24 -07002530 if (/* CONSTCOND */ 0)
Elliott Hughes96b43632015-07-17 11:39:41 -07002531 /* FALLTHROUGH */
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002532 case 'u':
Elliott Hughes96b43632015-07-17 11:39:41 -07002533 i = 4;
Geremy Condra03ebf062011-10-12 18:17:24 -07002534 if (/* CONSTCOND */ 0)
Elliott Hughes96b43632015-07-17 11:39:41 -07002535 /* FALLTHROUGH */
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002536 case 'x':
Elliott Hughes96b43632015-07-17 11:39:41 -07002537 i = cstyle ? -1 : 2;
Geremy Condra03ebf062011-10-12 18:17:24 -07002538 /**
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002539 * x: look for a hexadecimal number with up to
2540 * two (C style: arbitrary) digits; convert
Elliott Hughes47086262019-03-26 12:34:31 -07002541 * to raw octet (C style: UCS if >0xFF)
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002542 * u/U: look for a hexadecimal number with up to
Elliott Hughes47086262019-03-26 12:34:31 -07002543 * four (U: eight) digits; convert to UCS
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002544 */
2545 wc = 0;
Elliott Hughesa3c3f962017-04-12 16:52:30 -07002546 n = 0;
2547 while (n < i || i == -1) {
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002548 wc <<= 4;
Elliott Hughes23925bb2017-09-22 16:04:20 -07002549 if (!ctype((c = (*fg)()), C_SEDEC)) {
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002550 wc >>= 4;
2551 (*fp)(c);
2552 break;
2553 }
Elliott Hughes23925bb2017-09-22 16:04:20 -07002554 if (ctype(c, C_DIGIT))
2555 wc += ksh_numdig(c);
2556 else if (ctype(c, C_UPPER))
2557 wc += ksh_numuc(c) + 10;
2558 else
2559 wc += ksh_numlc(c) + 10;
Elliott Hughesa3c3f962017-04-12 16:52:30 -07002560 ++n;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002561 }
Elliott Hughesa3c3f962017-04-12 16:52:30 -07002562 if (!n)
2563 goto unknown_escape;
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002564 if ((cstyle && wc > 0xFF) || fc != 'x')
Elliott Hughes47086262019-03-26 12:34:31 -07002565 /* UCS marker */
Jean-Baptiste Queru5155f1c2011-06-16 10:05:28 -07002566 wc += 0x100;
2567 break;
2568 case '\'':
2569 if (!cstyle)
2570 goto unknown_escape;
2571 wc = '\'';
2572 break;
2573 case '\\':
2574 wc = '\\';
2575 break;
2576 default:
2577 unknown_escape:
2578 (*fp)(fc);
2579 return (-1);
2580 }
2581
2582 return (wc);
2583}