reenabled swaptest. quake should now load data and start on big endian architectures...
[AROS-Contrib.git] / gnu / abc-shell / misc.c
blob0eb7dbbb1495f5e6167da80a1f053f9c5b113b3a
1 /*
2 * Miscellaneous functions
3 */
5 #include <ctype.h> /* for FILECHCONV */
6 #include <stdio.h>
7 #include "sh.h"
9 #if defined(__AROS__)
10 #include <sys/param.h>
11 #endif
13 short ctypes [UCHAR_MAX+1]; /* type bits for unsigned char */
15 static int do_gmatch(const unsigned char *, const unsigned char *,
16 const unsigned char *, const unsigned char *);
17 static const unsigned char *cclass(const unsigned char *, int);
20 * Fast character classes
22 void
23 setctypes(const char *s, int t)
25 int i;
27 if (t & C_IFS) {
28 for (i = 0; i < UCHAR_MAX+1; i++)
29 ctypes[i] &= ~C_IFS;
30 ctypes[0] |= C_IFS; /* include \0 in C_IFS */
32 while (*s != 0)
33 ctypes[(unsigned char) *s++] |= t;
36 void
37 initctypes(void)
39 int c;
41 for (c = 'a'; c <= 'z'; c++)
42 ctypes[c] |= C_ALPHA;
43 for (c = 'A'; c <= 'Z'; c++)
44 ctypes[c] |= C_ALPHA;
45 ctypes['_'] |= C_ALPHA;
46 setctypes("0123456789", C_DIGIT);
47 setctypes(" \t\n|&;<>()", C_LEX1); /* \0 added automatically */
48 setctypes("*@#!$-?", C_VAR1);
49 setctypes(" \t\n", C_IFSWS);
50 setctypes("=-+?", C_SUBOP1);
51 setctypes("#%", C_SUBOP2);
52 setctypes(" \n\t\"#$&'()*;<>?[\\`|", C_QUOTE);
55 /* convert unsigned long to base N string */
57 char *
58 ulton(long unsigned int n, int base)
60 char *p;
61 static char buf [20];
63 p = &buf[sizeof(buf)];
64 *--p = '\0';
65 do {
66 *--p = "0123456789ABCDEF"[n%base];
67 n /= base;
68 } while (n != 0);
69 return p;
72 char *
73 str_save(const char *s, Area *ap)
75 size_t len;
76 char *p;
78 if (!s)
79 return NULL;
80 len = strlen(s)+1;
81 p = alloc(len, ap);
82 strlcpy(p, s, len+1);
83 return (p);
86 /* Allocate a string of size n+1 and copy upto n characters from the possibly
87 * null terminated string s into it. Always returns a null terminated string
88 * (unless n < 0).
90 char *
91 str_nsave(const char *s, int n, Area *ap)
93 char *ns;
95 if (n < 0)
96 return 0;
97 ns = alloc(n + 1, ap);
98 ns[0] = '\0';
99 return strncat(ns, s, n);
102 /* called from expand.h:XcheckN() to grow buffer */
103 char *
104 Xcheck_grow_(XString *xsp, char *xp, int more)
106 char *old_beg = xsp->beg;
108 xsp->len += more > xsp->len ? more : xsp->len;
109 xsp->beg = aresize(xsp->beg, xsp->len + 8, xsp->areap);
110 xsp->end = xsp->beg + xsp->len;
111 return xsp->beg + (xp - old_beg);
114 const struct option options[] = {
115 /* Special cases (see parse_args()): -A, -o, -s.
116 * Options are sorted by their longnames - the order of these
117 * entries MUST match the order of sh_flag F* enumerations in sh.h.
119 { "allexport", 'a', OF_ANY },
120 { "braceexpand", 0, OF_ANY }, /* non-standard */
121 { "bgnice", 0, OF_ANY },
122 { (char *) 0, 'c', OF_CMDLINE },
123 { "csh-history", 0, OF_ANY }, /* non-standard */
124 { "errexit", 'e', OF_ANY },
125 { "ignoreeof", 0, OF_ANY },
126 { "interactive", 'i', OF_CMDLINE },
127 { "keyword", 'k', OF_ANY },
128 { "markdirs", 'X', OF_ANY },
129 { (char *) 0, 'm', 0 }, /* so FMONITOR not ifdef'd */
130 { "noclobber", 'C', OF_ANY },
131 // { "noexec", 'n', OF_ANY },
132 { "noglob", 'f', OF_ANY },
133 { "nounset", 'u', OF_ANY },
134 { "physical", 0, OF_ANY }, /* non-standard */
135 { "posix", 0, OF_ANY }, /* non-standard */
136 { "sh", 0, OF_ANY }, /* non-standard */
137 { "stdin", 's', OF_CMDLINE }, /* pseudo non-standard */
138 { "trackall", 'h', OF_ANY },
139 { "verbose", 'v', OF_ANY },
140 { "xtrace", 'x', OF_ANY },
141 /* Anonymous flags: used internally by shell only
142 * (not visable to user)
144 { (char *) 0, 0, OF_INTERNAL }, /* FTALKING_I */
148 * translate -o option into F* constant (also used for test -o option)
151 option(const char *n)
153 int i;
155 for (i = 0; i < NELEM(options); i++)
156 if (options[i].name && strcmp(options[i].name, n) == 0)
157 return i;
159 return -1;
162 struct options_info {
163 int opt_width;
164 struct {
165 const char *name;
166 int flag;
167 } opts[NELEM(options)];
170 static char *options_fmt_entry(void *arg, int i, char *buf, int buflen);
171 static void printoptions(int verbose);
173 /* format a single select menu item */
174 static char *
175 options_fmt_entry(void *arg, int i, char *buf, int buflen)
177 struct options_info *oi = (struct options_info *) arg;
179 shf_snprintf(buf, buflen, "%-*s %s",
180 oi->opt_width, oi->opts[i].name,
181 Flag(oi->opts[i].flag) ? "on" : "off");
182 return buf;
185 static void
186 printoptions(int verbose)
188 int i;
190 if (verbose) {
191 struct options_info oi;
192 int n, len;
194 /* verbose version */
195 shprintf("Current option settings\n");
197 for (i = n = oi.opt_width = 0; i < NELEM(options); i++)
198 if (options[i].name) {
199 len = strlen(options[i].name);
200 oi.opts[n].name = options[i].name;
201 oi.opts[n++].flag = i;
202 if (len > oi.opt_width)
203 oi.opt_width = len;
205 print_columns(shl_stdout, n, options_fmt_entry, &oi,
206 oi.opt_width + 5, 1);
207 } else {
208 /* short version ala ksh93 */
209 shprintf("set");
210 for (i = 0; i < NELEM(options); i++)
211 if (Flag(i) && options[i].name)
212 shprintf(" -o %s", options[i].name);
213 shprintf(newline);
217 char *
218 getoptions(void)
220 int i;
221 char m[(int) FNFLAGS + 1];
222 char *cp = m;
224 for (i = 0; i < NELEM(options); i++)
225 if (options[i].c && Flag(i))
226 *cp++ = options[i].c;
227 *cp = 0;
228 return str_save(m, ATEMP);
231 /* change a Flag(*) value; takes care of special actions */
232 void
233 change_flag(enum sh_flag f,
234 int what, /* flag to change */
235 int newval) /* what is changing the flag (command line vs set) */
237 Flag(f) = newval;
238 /* Turning off -p? */
239 if (f == FPOSIX && newval) {
241 Flag(FBRACEEXPAND) = 0
245 /* Changing interactive flag? */
246 if (f == FTALKING) {
247 if ((what == OF_CMDLINE || what == OF_SET) && procpid == kshpid)
248 Flag(FTALKING_I) = newval;
252 /* parse command line & set command arguments. returns the index of
253 * non-option arguments, -1 if there is an error.
256 parse_args(char **argv,
257 int what, /* OF_CMDLINE or OF_SET */
258 int *setargsp)
260 static char cmd_opts[NELEM(options) + 3]; /* o:\0 */
261 static char set_opts[NELEM(options) + 5]; /* Ao;s\0 */
262 char *opts;
263 char *array = (char *) 0;
264 Getopt go;
265 int i, optc, set, sortargs = 0, arrayset = 0;
267 /* First call? Build option strings... */
268 if (cmd_opts[0] == '\0') {
269 char *p, *q;
271 /* see cmd_opts[] declaration */
272 strlcpy(cmd_opts, "o:", sizeof cmd_opts);
273 p = cmd_opts + strlen(cmd_opts);
274 /* see set_opts[] declaration */
275 strlcpy(set_opts, "A:o;s", sizeof set_opts);
276 q = set_opts + strlen(set_opts);
277 for (i = 0; i < NELEM(options); i++) {
278 if (options[i].c) {
279 if (options[i].flags & OF_CMDLINE)
280 *p++ = options[i].c;
281 if (options[i].flags & OF_SET)
282 *q++ = options[i].c;
285 *p = '\0';
286 *q = '\0';
289 if (what == OF_CMDLINE) {
290 opts = cmd_opts;
291 } else
292 opts = set_opts;
293 ksh_getopt_reset(&go, GF_ERROR|GF_PLUSOPT);
294 while ((optc = ksh_getopt(argv, &go, opts)) != -1) {
295 set = (go.info & GI_PLUS) ? 0 : 1;
296 switch (optc) {
297 case 'A':
298 arrayset = set ? 1 : -1;
299 array = go.optarg;
300 break;
302 case 'o':
303 if (go.optarg == (char *) 0) {
304 /* lone -o: print options
306 * Note that on the command line, -o requires
307 * an option (ie, can't get here if what is
308 * OF_CMDLINE).
310 printoptions(set);
311 break;
313 i = option(go.optarg);
314 if (i >= 0 && set == Flag(i))
315 /* Don't check the context if the flag
316 * isn't changing - makes "set -o interactive"
317 * work if you're already interactive. Needed
318 * if the output of "set +o" is to be used.
321 else if (i >= 0 && (options[i].flags & what))
322 change_flag((enum sh_flag) i, what, set);
323 else {
324 bi_errorf("%s: bad option", go.optarg);
325 return -1;
327 break;
329 case '?':
330 return -1;
332 default:
333 /* -s: sort positional params (at&t ksh stupidity) */
334 if (what == OF_SET && optc == 's') {
335 sortargs = 1;
336 break;
338 for (i = 0; i < NELEM(options); i++)
339 if (optc == options[i].c
340 && (what & options[i].flags))
342 change_flag((enum sh_flag) i, what,
343 set);
344 break;
346 if (i == NELEM(options)) {
347 internal_errorf(1, "parse_args: `%c'", optc);
348 return -1; /* not reached */
352 if (!(go.info & GI_MINUSMINUS) && argv[go.optind] &&
353 (argv[go.optind][0] == '-' || argv[go.optind][0] == '+') &&
354 argv[go.optind][1] == '\0')
356 /* lone - clears -v and -x flags */
357 if (argv[go.optind][0] == '-' && !Flag(FPOSIX))
358 Flag(FVERBOSE) = Flag(FXTRACE) = 0;
359 /* set skips lone - or + option */
360 go.optind++;
362 if (setargsp)
363 /* -- means set $#/$* even if there are no arguments */
364 *setargsp = !arrayset && ((go.info & GI_MINUSMINUS) ||
365 argv[go.optind]);
367 if (arrayset && (!*array || *skip_varname(array, false))) {
368 bi_errorf("%s: is not an identifier", array);
369 return -1;
371 if (sortargs) {
372 for (i = go.optind; argv[i]; i++)
374 qsortp((void **) &argv[go.optind], (size_t) (i - go.optind),
375 xstrcmp);
377 if (arrayset) {
378 set_array(array, arrayset, argv + go.optind);
379 for (; argv[go.optind]; go.optind++)
383 return go.optind;
386 /* parse a decimal number: returns 0 if string isn't a number, 1 otherwise */
388 getn(const char *as, int *ai)
390 char *p;
391 int n;
393 n = strtol(as, &p, 10);
395 if (!*as || *p || INT_MIN >= n || n >= INT_MAX)
396 return 0;
398 *ai = (int)n;
399 return 1;
402 /* getn() that prints error */
404 bi_getn(const char *as, int *ai)
406 int rv = getn(as, ai);
408 if (!rv)
409 bi_errorf("%s: bad number", as);
410 return rv;
413 /* -------- gmatch.c -------- */
416 * int gmatch(string, pattern)
417 * char *string, *pattern;
419 * Match a pattern as in sh(1).
420 * pattern character are prefixed with MAGIC by expand.
424 gmatch(const char *s, const char *p, int isfile)
426 const char *se, *pe;
428 if (s == NULL || p == NULL)
429 return 0;
430 se = s + strlen(s);
431 pe = p + strlen(p);
432 /* isfile is false iff no syntax check has been done on
433 * the pattern. If check fails, just to a strcmp().
435 if (!isfile && !has_globbing(p, pe)) {
436 int len = pe - p + 1;
437 char tbuf[64];
438 char *t = len <= sizeof(tbuf) ? tbuf :
439 (char *) alloc(len, ATEMP);
440 debunk(t, p, len);
441 return !strcmp(t, s);
443 return do_gmatch((const unsigned char *) s, (const unsigned char *) se,
444 (const unsigned char *) p, (const unsigned char *) pe);
447 /* Returns if p is a syntacticly correct globbing pattern, false
448 * if it contains no pattern characters or if there is a syntax error.
449 * Syntax errors are:
450 * - [ with no closing ]
451 * - imbalenced $(...) expression
452 * - [...] and *(...) not nested (eg, [a$(b|]c), *(a[b|c]d))
454 /*XXX
455 - if no magic,
456 if dest given, copy to dst
457 return ?
458 - if magic && (no globbing || syntax error)
459 debunk to dst
460 return ?
461 - return ?
464 has_globbing(const char *xp, const char *xpe)
466 const unsigned char *p = (const unsigned char *) xp;
467 const unsigned char *pe = (const unsigned char *) xpe;
468 int c;
469 int nest = 0, bnest = 0;
470 int saw_glob = 0;
471 int in_bracket = 0; /* inside [...] */
473 for (; p < pe; p++) {
474 if (!ISMAGIC(*p))
475 continue;
476 if ((c = *++p) == '*' || c == '?')
477 saw_glob = 1;
478 else if (c == '[') {
479 if (!in_bracket) {
480 saw_glob = 1;
481 in_bracket = 1;
482 if (ISMAGIC(p[1]) && p[2] == NOT)
483 p += 2;
484 if (ISMAGIC(p[1]) && p[2] == ']')
485 p += 2;
487 /* XXX Do we need to check ranges here? POSIX Q */
488 } else if (c == ']') {
489 if (in_bracket) {
490 if (bnest) /* [a*(b]) */
491 return 0;
492 in_bracket = 0;
494 } else if ((c & 0x80) && strchr("*+?@! ", c & 0x7f)) {
495 saw_glob = 1;
496 if (in_bracket)
497 bnest++;
498 else
499 nest++;
500 } else if (c == '|') {
501 if (in_bracket && !bnest) /* *(a[foo|bar]) */
502 return 0;
503 } else if (c == /*(*/ ')') {
504 if (in_bracket) {
505 if (!bnest--) /* *(a[b)c] */
506 return 0;
507 } else if (nest)
508 nest--;
510 /* else must be a MAGIC-MAGIC, or MAGIC-!, MAGIC--, MAGIC-]
511 MAGIC-{, MAGIC-,, MAGIC-} */
513 return saw_glob && !in_bracket && !nest;
516 /* Function must return either 0 or 1 (assumed by code for 0x80|'!') */
517 static int
518 do_gmatch(const unsigned char *s, const unsigned char *se,
519 const unsigned char *p, const unsigned char *pe)
521 int sc, pc;
522 const unsigned char *prest, *psub, *pnext;
523 const unsigned char *srest;
525 if (s == NULL || p == NULL)
526 return 0;
527 while (p < pe) {
528 pc = *p++;
529 sc = s < se ? *s : '\0';
530 s++;
531 if (!ISMAGIC(pc)) {
532 if (sc != pc)
533 return 0;
534 continue;
536 switch (*p++) {
537 case '[':
538 if (sc == 0 || (p = cclass(p, sc)) == NULL)
539 return 0;
540 break;
542 case '?':
543 if (sc == 0)
544 return 0;
545 break;
547 case '*':
548 if (p == pe)
549 return 1;
550 s--;
551 do {
552 if (do_gmatch(s, se, p, pe))
553 return 1;
554 } while (s++ < se);
555 return 0;
558 * [*+?@!](pattern|pattern|..)
560 * Not ifdef'd KSH as this is needed for ${..%..}, etc.
562 case 0x80|'+': /* matches one or more times */
563 case 0x80|'*': /* matches zero or more times */
564 if (!(prest = pat_scan(p, pe, 0)))
565 return 0;
566 s--;
567 /* take care of zero matches */
568 if (p[-1] == (0x80 | '*') &&
569 do_gmatch(s, se, prest, pe))
570 return 1;
571 for (psub = p; ; psub = pnext) {
572 pnext = pat_scan(psub, pe, 1);
573 for (srest = s; srest <= se; srest++) {
574 if (do_gmatch(s, srest, psub, pnext - 2) &&
575 (do_gmatch(srest, se, prest, pe) ||
576 (s != srest && do_gmatch(srest,
577 se, p - 2, pe))))
578 return 1;
580 if (pnext == prest)
581 break;
583 return 0;
585 case 0x80|'?': /* matches zero or once */
586 case 0x80|'@': /* matches one of the patterns */
587 case 0x80|' ': /* simile for @ */
588 if (!(prest = pat_scan(p, pe, 0)))
589 return 0;
590 s--;
591 /* Take care of zero matches */
592 if (p[-1] == (0x80 | '?') &&
593 do_gmatch(s, se, prest, pe))
594 return 1;
595 for (psub = p; ; psub = pnext) {
596 pnext = pat_scan(psub, pe, 1);
597 srest = prest == pe ? se : s;
598 for (; srest <= se; srest++) {
599 if (do_gmatch(s, srest, psub, pnext - 2) &&
600 do_gmatch(srest, se, prest, pe))
601 return 1;
603 if (pnext == prest)
604 break;
606 return 0;
608 case 0x80|'!': /* matches none of the patterns */
609 if (!(prest = pat_scan(p, pe, 0)))
610 return 0;
611 s--;
612 for (srest = s; srest <= se; srest++) {
613 int matched = 0;
615 for (psub = p; ; psub = pnext) {
616 pnext = pat_scan(psub, pe, 1);
617 if (do_gmatch(s, srest, psub,
618 pnext - 2))
620 matched = 1;
621 break;
623 if (pnext == prest)
624 break;
626 if (!matched &&
627 do_gmatch(srest, se, prest, pe))
628 return 1;
630 return 0;
632 default:
633 if (sc != p[-1])
634 return 0;
635 break;
638 return s == se;
641 static const unsigned char *
642 cclass(const unsigned char *p, int sub)
644 int c, d, not, found = 0;
645 const unsigned char *orig_p = p;
647 if ((not = (ISMAGIC(*p) && *++p == NOT)))
648 p++;
649 do {
650 c = *p++;
651 if (ISMAGIC(c)) {
652 c = *p++;
653 if ((c & 0x80) && !ISMAGIC(c)) {
654 c &= 0x7f;/* extended pattern matching: *+?@! */
655 /* XXX the ( char isn't handled as part of [] */
656 if (c == ' ') /* simile for @: plain (..) */
657 c = '(' /*)*/;
660 if (c == '\0')
661 /* No closing ] - act as if the opening [ was quoted */
662 return sub == '[' ? orig_p : NULL;
663 if (ISMAGIC(p[0]) && p[1] == '-' &&
664 (!ISMAGIC(p[2]) || p[3] != ']'))
666 p += 2; /* MAGIC- */
667 d = *p++;
668 if (ISMAGIC(d)) {
669 d = *p++;
670 if ((d & 0x80) && !ISMAGIC(d))
671 d &= 0x7f;
673 /* POSIX says this is an invalid expression */
674 if (c > d)
675 return NULL;
676 } else
677 d = c;
678 if (c == sub || (c <= sub && sub <= d))
679 found = 1;
680 } while (!(ISMAGIC(p[0]) && p[1] == ']'));
682 return (found != not) ? p+2 : NULL;
685 /* Look for next ) or | (if match_sep) in *(foo|bar) pattern */
686 const unsigned char *
687 pat_scan(const unsigned char *p, const unsigned char *pe, int match_sep)
689 int nest = 0;
691 for (; p < pe; p++) {
692 if (!ISMAGIC(*p))
693 continue;
694 if ((*++p == /*(*/ ')' && nest-- == 0) ||
695 (*p == '|' && match_sep && nest == 0))
696 return ++p;
697 if ((*p & 0x80) && strchr("*+?@! ", *p & 0x7f))
698 nest++;
700 return (const unsigned char *) 0;
704 /* -------- qsort.c -------- */
707 * quick sort of array of generic pointers to objects.
709 static void qsort1(void **base, void **lim, int (*f)(void *, void *));
711 void
712 qsortp(void **base, /* base address */
713 size_t n, /* elements */
714 int (*f) (void *, void *)) /* compare function */
716 qsort1(base, base + n, f);
719 #define swap2(a, b) {\
720 void *t; t = *(a); *(a) = *(b); *(b) = t;\
722 #define swap3(a, b, c) {\
723 void *t; t = *(a); *(a) = *(c); *(c) = *(b); *(b) = t;\
726 static void
727 qsort1(void **base, void **lim, int (*f) (void *, void *))
729 void **i, **j;
730 void **lptr, **hptr;
731 size_t n;
732 int c;
734 top:
735 n = (lim - base) / 2;
736 if (n == 0)
737 return;
738 hptr = lptr = base+n;
739 i = base;
740 j = lim - 1;
742 for (;;) {
743 if (i < lptr) {
744 if ((c = (*f)(*i, *lptr)) == 0) {
745 lptr --;
746 swap2(i, lptr);
747 continue;
749 if (c < 0) {
750 i += 1;
751 continue;
755 begin:
756 if (j > hptr) {
757 if ((c = (*f)(*hptr, *j)) == 0) {
758 hptr ++;
759 swap2(hptr, j);
760 goto begin;
762 if (c > 0) {
763 if (i == lptr) {
764 hptr ++;
765 swap3(i, hptr, j);
766 i = lptr += 1;
767 goto begin;
769 swap2(i, j);
770 j -= 1;
771 i += 1;
772 continue;
774 j -= 1;
775 goto begin;
778 if (i == lptr) {
779 if (lptr-base >= lim-hptr) {
780 qsort1(hptr+1, lim, f);
781 lim = lptr;
782 } else {
783 qsort1(base, lptr, f);
784 base = hptr+1;
786 goto top;
789 lptr -= 1;
790 swap3(j, lptr, i);
791 j = hptr -= 1;
796 xstrcmp(void *p1, void *p2)
798 return (strcmp((char *)p1, (char *)p2));
801 /* Initialize a Getopt structure */
802 void
803 ksh_getopt_reset(Getopt *go, int flags)
805 go->optind = 1;
806 go->optarg = (char *) 0;
807 go->p = 0;
808 go->flags = flags;
809 go->info = 0;
810 go->buf[1] = '\0';
814 /* getopt() used for shell built-in commands, the getopts command, and
815 * command line options.
816 * A leading ':' in options means don't print errors, instead return '?'
817 * or ':' and set go->optarg to the offending option character.
818 * If GF_ERROR is set (and option doesn't start with :), errors result in
819 * a call to bi_errorf().
821 * Non-standard features:
822 * - ';' is like ':' in options, except the argument is optional
823 * (if it isn't present, optarg is set to 0).
824 * Used for 'set -o'.
825 * - ',' is like ':' in options, except the argument always immediately
826 * follows the option character (optarg is set to the null string if
827 * the option is missing).
828 * Used for 'read -u2', 'print -u2' and fc -40.
829 * - '#' is like ':' in options, expect that the argument is optional
830 * and must start with a digit. If the argument doesn't start with a
831 * digit, it is assumed to be missing and normal option processing
832 * continues (optarg is set to 0 if the option is missing).
833 * Used for 'typeset -LZ4'.
834 * - accepts +c as well as -c IF the GF_PLUSOPT flag is present. If an
835 * option starting with + is accepted, the GI_PLUS flag will be set
836 * in go->info.
839 ksh_getopt(char **argv, Getopt *go, const char *options)
841 char c;
842 char *o;
844 if (go->p == 0 || (c = argv[go->optind - 1][go->p]) == '\0') {
845 char *arg = argv[go->optind], flag = arg ? *arg : '\0';
847 go->p = 1;
848 if (flag == '-' && arg[1] == '-' && arg[2] == '\0') {
849 go->optind++;
850 go->p = 0;
851 go->info |= GI_MINUSMINUS;
852 return -1;
854 if (arg == (char *) 0 ||
855 ((flag != '-' ) && /* neither a - nor a + (if + allowed) */
856 (!(go->flags & GF_PLUSOPT) || flag != '+')) ||
857 (c = arg[1]) == '\0')
859 go->p = 0;
860 return -1;
862 go->optind++;
863 go->info &= ~(GI_MINUS|GI_PLUS);
864 go->info |= flag == '-' ? GI_MINUS : GI_PLUS;
866 go->p++;
867 if (c == '?' || c == ':' || c == ';' || c == ',' || c == '#' ||
868 !(o = strchr(options, c)))
870 if (options[0] == ':') {
871 go->buf[0] = c;
872 go->optarg = go->buf;
873 } else {
874 warningf(true, "%s%s-%c: unknown option",
875 (go->flags & GF_NONAME) ? "" : argv[0],
876 (go->flags & GF_NONAME) ? "" : ": ", c);
877 if (go->flags & GF_ERROR)
878 bi_errorf(null);
880 return '?';
882 /* : means argument must be present, may be part of option argument
883 * or the next argument
884 * ; same as : but argument may be missing
885 * , means argument is part of option argument, and may be null.
887 if (*++o == ':' || *o == ';') {
888 if (argv[go->optind - 1][go->p])
889 go->optarg = argv[go->optind - 1] + go->p;
890 else if (argv[go->optind])
891 go->optarg = argv[go->optind++];
892 else if (*o == ';')
893 go->optarg = (char *) 0;
894 else {
895 if (options[0] == ':') {
896 go->buf[0] = c;
897 go->optarg = go->buf;
898 return ':';
900 warningf(true, "%s%s-`%c' requires argument",
901 (go->flags & GF_NONAME) ? "" : argv[0],
902 (go->flags & GF_NONAME) ? "" : ": ", c);
903 if (go->flags & GF_ERROR)
904 bi_errorf(null);
905 return '?';
907 go->p = 0;
908 } else if (*o == ',') {
909 /* argument is attatched to option character, even if null */
910 go->optarg = argv[go->optind - 1] + go->p;
911 go->p = 0;
912 } else if (*o == '#') {
913 /* argument is optional and may be attatched or unattatched
914 * but must start with a digit. optarg is set to 0 if the
915 * argument is missing.
917 if (argv[go->optind - 1][go->p]) {
918 if (digit(argv[go->optind - 1][go->p])) {
919 go->optarg = argv[go->optind - 1] + go->p;
920 go->p = 0;
921 } else
922 go->optarg = (char *) 0;;
923 } else {
924 if (argv[go->optind] && digit(argv[go->optind][0])) {
925 go->optarg = argv[go->optind++];
926 go->p = 0;
927 } else
928 go->optarg = (char *) 0;;
931 return c;
934 /* print variable/alias value using necessary quotes
935 * (POSIX says they should be suitable for re-entry...)
936 * No trailing newline is printed.
938 void
939 print_value_quoted(const char *s)
941 const char *p;
942 int inquote = 0;
944 /* Test if any quotes are needed */
945 for (p = s; *p; p++)
946 if (ctype(*p, C_QUOTE))
947 break;
948 if (!*p) {
949 shprintf("%s", s);
950 return;
952 for (p = s; *p; p++) {
953 if (*p == '\'') {
954 shprintf("'\\'" + 1 - inquote);
955 inquote = 0;
956 } else {
957 if (!inquote) {
958 shprintf("'");
959 inquote = 1;
961 shf_putc(*p, shl_stdout);
964 if (inquote)
965 shprintf("'");
968 /* Print things in columns and rows - func() is called to format the ith
969 * element
971 void
972 print_columns(struct shf *shf, int n, char *(*func) (void *, int, char *, int),
973 void *arg, int max_width, int prefcol)
975 char *str = (char *) alloc(max_width + 1, ATEMP);
976 int i;
977 int r, c;
978 int rows, cols;
979 int nspace;
981 /* max_width + 1 for the space. Note that no space
982 * is printed after the last column to avoid problems
983 * with terminals that have auto-wrap.
985 cols = x_cols / (max_width + 1);
986 if (!cols)
987 cols = 1;
988 rows = (n + cols - 1) / cols;
989 if (prefcol && n && cols > rows) {
990 int tmp = rows;
992 rows = cols;
993 cols = tmp;
994 if (rows > n)
995 rows = n;
998 nspace = (x_cols - max_width * cols) / cols;
999 if (nspace <= 0)
1000 nspace = 1;
1001 for (r = 0; r < rows; r++) {
1002 for (c = 0; c < cols; c++) {
1003 i = c * rows + r;
1004 if (i < n) {
1005 shf_fprintf(shf, "%-*s",
1006 max_width,
1007 (*func)(arg, i, str, max_width + 1));
1008 if (c + 1 < cols)
1009 shf_fprintf(shf, "%*s", nspace, null);
1012 shf_putchar('\n', shf);
1014 afree(str, ATEMP);
1017 /* Strip any nul bytes from buf - returns new length (nbytes - # of nuls) */
1019 strip_nuls(char *buf, int nbytes)
1021 char *dst;
1023 /* nbytes check because some systems (older freebsd's) have a buggy
1024 * memchr()
1026 if (nbytes && (dst = memchr(buf, '\0', nbytes))) {
1027 char *end = buf + nbytes;
1028 char *p, *q;
1030 for (p = dst; p < end; p = q) {
1031 /* skip a block of nulls */
1032 while (++p < end && *p == '\0')
1034 /* find end of non-null block */
1035 if (!(q = memchr(p, '\0', end - p)))
1036 q = end;
1037 memmove(dst, p, q - p);
1038 dst += q - p;
1040 *dst = '\0';
1041 return dst - buf;
1043 return nbytes;
1046 /* Like read(2), but if read fails due to non-blocking flag, resets flag
1047 * and restarts read.
1050 blocking_read(int fd, char *buf, int nbytes)
1052 int ret;
1053 int tried_reset = 0;
1055 while ((ret = read(fd, buf, nbytes)) < 0) {
1056 if (!tried_reset && errno == EAGAIN)
1058 int oerrno = errno;
1059 if (reset_nonblock(fd) > 0) {
1060 tried_reset = 1;
1061 continue;
1063 errno = oerrno;
1065 break;
1067 return ret;
1070 /* Reset the non-blocking flag on the specified file descriptor.
1071 * Returns -1 if there was an error, 0 if non-blocking wasn't set,
1072 * 1 if it was.
1075 reset_nonblock(int fd)
1077 int flags;
1079 if ((flags = fcntl(fd, F_GETFL, 0)) < 0)
1080 return -1;
1081 if (!(flags & O_NONBLOCK))
1082 return 0;
1083 flags &= ~O_NONBLOCK;
1084 if (fcntl(fd, F_SETFL, flags) < 0)
1085 return -1;
1086 return 1;
1089 /* Like getcwd(), except bsize is ignored if buf is 0 (PATH_MAX is used) */
1090 char *
1091 ksh_get_wd(char *buf, int bsize)
1093 char *b;
1094 char *ret;
1096 /* Note: we could just use plain getcwd(), but then we'd had to
1097 * inject possibly allocated space into the ATEMP area. */
1098 /* Assume getcwd() available */
1099 if (!buf) {
1100 bsize = MAXPATHLEN;
1101 b = alloc(MAXPATHLEN + 1, ATEMP);
1102 } else
1103 b = buf;
1105 ret = getcwd(b, bsize);
1107 if (!buf) {
1108 if (ret)
1109 ret = aresize(b, strlen(b) + 1, ATEMP);
1110 else
1111 afree(b, ATEMP);
1114 return ret;