CC = c99
CPPFLAGS = -I.
CCCMD = $(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(LDLIBS)
-RCOBJS = builtins.o except.o exec.o fn.o footobar.o getopt.o glob.o glom.o hash.o heredoc.o input.o lex.o list.o main.o match.o nalloc.o open.o y.tab.o print.o redir.o signal.o status.o tree.o utils.o var.o wait.o walk.o which.o sigmsgs.o system.o
+RCOBJS = builtins.o except.o exec.o fn.o footobar.o glob.o glom.o hash.o heredoc.o input.o lex.o list.o main.o match.o nalloc.o open.o y.tab.o print.o redir.o signal.o status.o tree.o utils.o var.o wait.o walk.o which.o sigmsgs.o system.o
all: rc
{ b_shift, "shift" },
{ b_umask, "umask" },
{ b_wait, "wait" },
- { b_whatis, "whatis" },
{ b_dot, "." },
};
return FALSE;
}
-static void b_whatis(char **av) {
- bool ess, eff, vee, pee, bee;
- bool f, found;
- int i, ac, c;
- List *s;
- Node *n;
- char *e;
- for (rc_optind = ac = 0; av[ac] != NULL; ac++)
- ; /* count the arguments for getopt */
- ess = eff = vee = pee = bee = FALSE;
- while ((c = rc_getopt(ac, av, "sfvpb")) != -1)
- switch (c) {
- default: set(FALSE); return;
- case 's': ess = TRUE; break;
- case 'f': eff = TRUE; break;
- case 'v': vee = TRUE; break;
- case 'p': pee = TRUE; break;
- case 'b': bee = TRUE; break;
- }
- av += rc_optind;
- if (*av == NULL) {
- if (vee|eff)
- whatare_all_vars(eff, vee);
- if (ess)
- whatare_all_signals();
- if (bee)
- for (i = 0; i < arraysize(builtins); i++)
- fprint(1, "builtin %s\n", builtins[i].name);
- if (pee)
- fprint(2, "whatis -p: must specify argument\n");
- if (show(FALSE)) /* no options? */
- whatare_all_vars(TRUE, TRUE);
- set(TRUE);
- return;
- }
- found = TRUE;
- for (i = 0; av[i] != NULL; i++) {
- f = FALSE;
- errno = ENOENT;
- if (show(vee) && (s = varlookup(av[i])) != NULL) {
- f = TRUE;
- prettyprint_var(1, av[i], s);
- }
- if (((show(ess)&&issig(av[i])) || show(eff)) && (n = fnlookup(av[i])) != NULL) {
- f = TRUE;
- prettyprint_fn(1, av[i], n);
- } else if (show(bee) && isbuiltin(av[i]) != NULL) {
- f = TRUE;
- fprint(1, "builtin %s\n", av[i]);
- } else if (show(pee) && (e = which(av[i], FALSE)) != NULL) {
- f = TRUE;
- fprint(1, "%S\n", e);
- }
- if (!f) {
- found = FALSE;
- if (errno != ENOENT)
- uerror(av[i]);
- else
- fprint(2, "%s not found\n", av[i]);
- }
- }
- set(found);
-}
-
/* push a string to be eval'ed onto the input stack. evaluate it */
static void b_eval(char **av) {
+++ /dev/null
-#include "rc.h"
-
-int rc_opterr = 1;
-int rc_optind = 1;
-int rc_optopt;
-char *rc_optarg;
-
-/* getopt routine courtesy of David Sanderson */
-
-extern int rc_getopt(int argc, char **argv, char *opts) {
- static int sp = 1;
- int c;
- char *cp;
- if (rc_optind == 0) /* reset rc_getopt() */
- rc_optind = sp = 1;
- if (sp == 1) {
- if (rc_optind >= argc || argv[rc_optind][0] != '-' || argv[rc_optind][1] == '\0') {
- return -1;
- } else if (strcmp(argv[rc_optind], "--") == 0) {
- rc_optind++;
- return -1;
- }
- }
- rc_optopt = c = argv[rc_optind][sp];
- if (c == ':' || (cp=strchr(opts, c)) == 0) {
- fprint(2, "%s: bad option: -%c\n", argv[0], c);
- if (argv[rc_optind][++sp] == '\0') {
- rc_optind++;
- sp = 1;
- }
- return '?';
- }
- if (*++cp == ':') {
- if (argv[rc_optind][sp+1] != '\0') {
- rc_optarg = &argv[rc_optind++][sp+1];
- } else if (++rc_optind >= argc) {
- fprint(2, "%s: option requires an argument -- %c\n", argv[0], c);
- sp = 1;
- return '?';
- } else
- rc_optarg = argv[rc_optind++];
- sp = 1;
- } else {
- if (argv[rc_optind][++sp] == '\0') {
- sp = 1;
- rc_optind++;
- }
- rc_optarg = NULL;
- }
- return c;
-}
end = &buf[m];
remain = bufsize - m;
}
- if ((n = rc_read(fd, end, remain)) <= 0) {
+ if ((n = read(fd, end, remain)) <= 0) {
if (n == 0)
/* break */ break;
else if (errno == EINTR)
if (chars_out >= chars_in) { /* replenish empty buffer */
ssize_t r;
do {
- r = rc_read(istack->fd, inbuf, BUFSIZE);
+ r = read(istack->fd, inbuf, BUFSIZE);
sigchk();
if (r == -1)
switch (errno) {
#include "rc.h"
+char* ARGV0;
bool dashdee, dashee, dashvee, dashex, dasheye,
dashen, dashpee, interactive;
pid_t rc_pid;
static void assigndefault(char *,...);
static void checkfd(int, enum redirtype);
+void usage(void) {
+ printf("Usage: %s [OPTION...] [FILE [ARG...]]\n", ARGV0);
+}
+
extern int main(int argc, char *argv[], char *envp[]) {
char *dashsee[2], *dollarzero, *null[1];
- int c;
initprint();
dashsee[0] = dashsee[1] = NULL;
dollarzero = argv[0];
rc_pid = getpid();
- dashell = (*argv[0] == '-'); /* Unix tradition */
- while ((c = rc_getopt(argc, argv, "c:deiIlnopsvx")) != -1)
- switch (c) {
- case 'c':
- dashsee[0] = rc_optarg;
- goto quitopts;
- case 'd':
- dashdee = TRUE;
- break;
- case 'e':
- dashee = TRUE;
+ dashell = (*argv[0] == '-'); /* login shell, per unix tradition */
+
+ OPTBEGIN {
+ case 'd': dashdee = TRUE; break;
+ case 'e': dashee = TRUE; break;
+ case 'l': dashell = TRUE; break;
+ case 'n': dashen = TRUE; break;
+ case 'o': dashoh = TRUE; break;
+ case 'p': dashpee = TRUE; break;
+ case 's': dashess = TRUE; break;
+ case 'v': dashvee = TRUE; break;
+ case 'x': dashex = TRUE; break;
+ case 'i':
+ dasheye = TRUE;
+ interactive = TRUE;
break;
case 'I':
dashEYE = TRUE;
interactive = FALSE;
break;
- case 'i':
- dasheye = interactive = TRUE;
- break;
- case 'l':
- dashell = TRUE;
- break;
- case 'n':
- dashen = TRUE;
- break;
- case 'o':
- dashoh = TRUE;
- break;
- case 'p':
- dashpee = TRUE;
- break;
- case 's':
- dashess = TRUE;
- break;
- case 'v':
- dashvee = TRUE;
- break;
- case 'x':
- dashex = TRUE;
+ case 'c':
+ dashsee[0] = EOPTARG(usage());
break;
- case '?':
- exit(1);
- }
-quitopts:
- argv += rc_optind;
+ } OPTEND;
+
/* use isatty() iff neither -i nor -I is set, and iff the input is not from a script or -c flags */
if (!dasheye && !dashEYE && dashsee[0] == NULL && (dashess || *argv == NULL))
interactive = isatty(0);
#include <stdlib.h>
#include <stdbool.h>
#include <stdarg.h>
+#include <stdio.h>
+#include <stddef.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#ifdef __DARWIN_NSIG
#define NSIG __DARWIN_NSIG
+#elif _NSIG
+ #define NSIG _NSIG
#endif
typedef struct {
};
/* macros */
-#define EOF (-1)
-#ifndef NULL
-#define NULL 0
-#endif
#define a2u(x) n2u(x, 10)
#define o2u(x) n2u(x, 8)
#define arraysize(a) ((int)(sizeof(a)/sizeof(*a)))
/* redir.c */
extern void doredirs(void);
-
/* signal.c */
extern void initsignal(void);
extern void catcher(int);
extern void (*sys_signal(int, void (*)(int)))(int);
extern void (*sighandlers[])(int);
-
/* status.c */
extern int istrue(void);
extern int getstatus(void);
extern void ssetstatus(char **);
extern char *strstatus(int s);
-
/* system.c or system-bsd.c */
extern void writeall(int, char *, size_t);
-#if HAVE_RESTARTABLE_SYSCALLS
-extern int rc_read(int, char *, size_t);
-extern pid_t rc_wait(int *);
-extern Jbwrap slowbuf;
-extern volatile sig_atomic_t slow;
-
-#else /* HAVE_RESTARTABLE_SYSCALLS */
-
-#define rc_read read
-#define rc_wait wait
-#endif /* HAVE_RESTARTABLE_SYSCALLS */
-
-
/* tree.c */
extern Node *mk(int /*nodetype*/,...);
extern Node *treecpy(Node *, void *(*)(size_t));
extern int gchar(void);
extern void ugchar(int);
-/* $TERM or $TERMCAP has changed */
-extern void termchange(void);
-
/* parse a function from the environment */
extern Node *parseline(char *);
extern int lastchar;
+/* Option Parsing
+ *
+ * This following macros implement a simple POSIX-style option parsing strategy.
+ * They are heavily influenced and inspired by the arg.h file from suckless.org
+ * (http://git.suckless.org/libsl/tree/arg.h). That file is in turn inspired by
+ * the corresponding macros defined in plan9.
+ *
+ * The interface assumes that the main function will have the following
+ * prototype:
+ *
+ * int main(int argc, char** argv);
+ *
+ * An example usage of the interface would look something like the follwoing:
+ *
+ * char* ARGV0;
+ * int main(int argc, char** argv) {
+ * OPTBEGIN {
+ * case 'a': printf("Simple option\n"); break;
+ * case 'b': printf("Option with arg: %s\n", OPTARG()); break;
+ * default: printf("Unknown option!\n");
+ * } OPTEND;
+ * return 0;
+ * }
+ */
+
+/* This variable contains the value of argv[0] so that it can be referenced
+ * again once the option parsing is done. This variable must be defined by the
+ * program.
+ *
+ * NOTE: Ensure that you define this variable with external linkage (i.e. not
+ * static) */
+extern char* ARGV0;
+
+/* This is a helper function used by the following macros to parse the next
+ * option from the command line. */
+static inline char* _getopt_(int* p_argc, char*** p_argv) {
+ if (!(*p_argv)[0][1] && !(*p_argv)[1]) {
+ return (char*)0;
+ } else if ((*p_argv)[0][1]) {
+ return &(*p_argv)[0][1];
+ } else {
+ *p_argv = *p_argv + 1;
+ *p_argc = *p_argc - 1;
+ return (*p_argv)[0];
+ }
+}
+
+/* This macro is almost identical to the ARGBEGIN macro from suckless.org. If
+ * it ain't broke, don't fix it. */
+#define OPTBEGIN \
+ for ( \
+ ARGV0 = *argv, argc--, argv++; \
+ argv[0] && argv[0][1] && argv[0][0] == '-'; \
+ argc--, argv++ \
+ ) { \
+ int brk_; char argc_ , **argv_, *optarg_; \
+ if (argv[0][1] == '-' && !argv[0][2]) { \
+ argv++, argc--; break; \
+ } \
+ for (brk_=0, argv[0]++, argv_=argv; argv[0][0] && !brk_; argv[0]++) { \
+ if (argv_ != argv) break; \
+ argc_ = argv[0][0]; \
+ switch (argc_)
+
+/* Terminate the option parsing. */
+#define OPTEND }}
+
+/* Get the current option character */
+#define OPTC() (argc_)
+
+/* Get an argument from the command line and return it as a string. If no
+ * argument is available, this macro returns NULL */
+#define OPTARG() \
+ (optarg_ = _getopt_(&argc,&argv), brk_ = (optarg_!=0), optarg_)
+
+/* Get an argument from the command line and return it as a string. If no
+ * argument is available, this macro executes the provided code. If that code
+ * returns, then abort is called. */
+#define EOPTARG(code) \
+ (optarg_ = _getopt_(&argc,&argv), \
+ (!optarg_ ? ((code), abort(), (char*)0) : (brk_ = 1, optarg_)))
+
+/* Helper macro to recognize number options */
+#define OPTNUM \
+ case '0': \
+ case '1': \
+ case '2': \
+ case '3': \
+ case '4': \
+ case '5': \
+ case '6': \
+ case '7': \
+ case '8': \
+ case '9'
+
+/* Helper macro to recognize "long" options ala GNU style. */
+#define OPTLONG \
+ case '-'
+
int ret;
Pid *q;
- ret = rc_wait(stat);
+ ret = wait(stat);
if (ret < 0) {
if (errno == ECHILD)