warnx("Job %d already in background", (pid_t)jobid);
return 1;
}
- } else
- for (jobs.c = MINUSONE(jobs, t); CURRENT->type == BACKGROUND; DEC(jobs, c))
- if (jobs.c == jobs.b) {
- warnx("No suspended jobs to run in background");
- return 1;
- }
+ } else {
+ for (jobs.c = MINUSONE(jobs, t); jobs.c != MINUSONE(jobs, b); DEC(jobs, c))
+ if (CURRENT->type == SUSPENDED) break;
+ if (jobs.c == MINUSONE(jobs, b)) {
+ warnx("No suspended jobs to run in background");
+ return 1;
+ }
+ }
job = deletejob();
if (!(push(&jobs, job))) {
#include <string.h>
#include <sys/errno.h>
-#include "history.h"
#include "input.h"
#include "stack.h"
#include "utils.h"
+#define HISTLEN 100
#define HISTNAME ".ashhistory"
static char *histpath, histarr[HISTLEN + 1][BUFLEN + 1];
-#define HISTLEN 100
-#define BUFLEN 1000
-
extern struct stack history;
void readhist(void);
#include <unistd.h>
#include "history.h"
+#include "input.h"
#include "job.h"
#include "stack.h"
BACKSPACE = '\177',
};
-static char buffer[1 + BUFLEN + 2];
+static char buffer[BUFLEN + 2]; // Terminating ';'
char *input(void) {
- char *start, *cursor, *end;
+ char *cursor, *end;
int c, i;
signal(SIGCHLD, waitbg); // TODO: Use sigaction for portability
reset:
- end = cursor = start = buffer + 1;
- *history.t = *start = '\0';
+ end = cursor = buffer;
+ *history.t = *buffer = '\0';
history.c = history.t;
- while (start == end) {
+ while (buffer == end) {
fputs(PROMPT, stdout);
while ((c = getchar()) != '\n') {
if (c >= ' ' && c <= '~') {
- if (end - start == BUFLEN) continue;
+ if (end - buffer == BUFLEN) continue;
memmove(cursor + 1, cursor, end - cursor);
*cursor++ = c;
*++end = '\0';
case CLEAR:
fputs("\033[H\033[J", stdout);
fputs(PROMPT, stdout);
- fputs(start, stdout);
+ fputs(buffer, stdout);
continue;
case ESCAPE:
switch ((c = getchar())) {
while (cursor != end && *cursor == ' ') putchar(*cursor++);
break;
case BACKWARD:
- while (cursor != start && *(cursor - 1) == ' ') putchar((--cursor, '\b'));
- while (cursor != start && *(cursor - 1) != ' ') putchar((--cursor, '\b'));
+ while (cursor != buffer && *(cursor - 1) == ' ') putchar((--cursor, '\b'));
+ while (cursor != buffer && *(cursor - 1) != ' ') putchar((--cursor, '\b'));
break;
case ARROW:
switch ((c = getchar())) {
if (history.c == (c == UP ? history.b : history.t)) continue;
putchar('\r');
- for (i = end - start + strlen(PROMPT); i > 0; --i) putchar(' ');
+ for (i = end - buffer + strlen(PROMPT); i > 0; --i) putchar(' ');
putchar('\r');
- if (strcmp(history.c, start) != 0) strcpy(history.t, start);
+ if (strcmp(history.c, buffer) != 0) strcpy(history.t, buffer);
if (c == UP) DEC(history, c); else INC(history, c);
- strcpy(start, history.c);
- end = cursor = start + strlen(start);
+ strcpy(buffer, history.c);
+ end = cursor = buffer + strlen(buffer);
fputs(PROMPT, stdout);
- fputs(start, stdout);
+ fputs(buffer, stdout);
break;
case LEFT:
- if (cursor > start) putchar((--cursor, '\b'));
+ if (cursor > buffer) putchar((--cursor, '\b'));
break;
case RIGHT:
if (cursor < end) putchar(*cursor++);
}
break;
case BACKSPACE:
- if (cursor == start) continue;
+ if (cursor == buffer) continue;
memmove(cursor - 1, cursor, end - cursor);
--cursor;
*--end = '\0';
}
}
fpurge(stdout);
- push(&history, start);
+ push(&history, buffer);
- *buffer = ';';
*end = ';';
*++end = '\0';
+#define BUFLEN 1000
+
char *input(void);
void *deletejob(void) {
memcpy(jobs.t, jobs.c, jobs.size);
memmove(jobs.c, PLUSONE(jobs, c), jobs.t - jobs.c);
- return MINUSONE(jobs, t);
+ return DEC(jobs, t);
}
void waitbg(int sig) {
+#include <err.h>
#include <fcntl.h>
+#include <limits.h>
#include <stddef.h>
#include <stdlib.h>
-#include <err.h>
-#include <limits.h>
-#include <stdio.h> // DEBUG
+#include <stdio.h> // XXX
-#include "history.h"
+#include "input.h"
#include "lex.h"
-static char *tokens[1 + BUFLEN + 1];
-static struct cmd cmds[1 + (BUFLEN + 1) / 2 + 1] = {{.args = tokens}};
-
-void printfreds(struct cmd *c) {
- struct fred *f;
+#define MAXCMDS 100
- for (f = c->freds; f->mode != END; ++f) {
- printf("%d ", f->newfd);
- switch (f->mode) {
- case READ:
- fputs("<", stdout);
- break;
- case WRITE:
- fputs(">", stdout);
- break;
- case READWRITE:
- fputs("<>", stdout);
- break;
- case APPEND:
- fputs(">>", stdout);
- break;
- default:;
- }
- switch (f->type) {
- case FD:
- printf(" #%d\n", f->old.fd);
- break;
- case NAME:
- printf(" %s\n", f->old.name);
- break;
- default:;
- }
- }
-}
+static char *tokens[BUFLEN + 1];
+static struct cmd cmds[MAXCMDS + 1];
+struct cmd empty = {0};
struct cmd *lex(char *b) {
char **t, *end;
struct cmd *c;
- struct fred *f;
- long test;
+ long l;
if (!b) return NULL;
t = tokens;
c = cmds;
- while (*b) switch (*b) {
+ for (c->args = NULL, c->r = c->rds; *b; ++b) switch (*b) {
default:
- if (!*(b - 1)) { // Start of a token
- if (!c->args) c->args = t; // Start of a command
- *t++ = b;
- }
- ++b;
- break;
- case ' ':
- *b++ = '\0';
+ if (!c->args) c->args = t;
+ if (!*(b - 1)) *t++ = b;
break;
case '<':
case '>':
+ c->r->newfd = *b == '>';
if (*(b - 1)) {
- if ((test = strtol(*--t, &end, 10)) < 0 || test > INT_MAX || end != b) {
+ if (c->args == --t) c->args = NULL;
+ if ((l = strtol(*t, &end, 10)) < 0 || l > INT_MAX || end != b) {
warnx("Invalid file redirection operator");
- c->args = NULL;
- return c - 1;
+ return ∅
}
- f->newfd = (int)test;
- if (c->args == t) c->args = NULL;
- } else f->newfd = *b == '>';
- f->mode = *b++;
- if (*b == '>') {
+ c->r->newfd = l;
+ }
+ c->r->mode = *b;
+ if (*(b + 1) == '>') {
+ ++c->r->mode;
++b;
- ++f->mode;
}
- f->old.name = b;
-
- (++f)->mode = END;
-
- if (*b == '&') ++b;
+ c->r++->oldname = b + 1;
+ if (*(b + 1) == '&') ++b;
break;
case '&':
case '|':
- if (*b == *(b + 1)) *b = '\0';
case ';':
if (c->args) {
- *t++ = NULL;
- c->type = !*b ? *b++ + 1 : *b;
- *b++ = '\0';
- for (f = c->freds; f->mode; ++f) {
- if (*f->old.name == '&') {
- if ((test = strtol(++f->old.name, &end, 10)) < 0
- || test > INT_MAX || (*end && !*f->old.name)) {
- warnx("Invalid file redirection operator");
- c->args = NULL;
- return c - 1;
- }
- f->type = FD;
- f->old.fd = (int)test;
- } else f->type = NAME;
+ if ((c->term = *b) != ';' && *b == *(b + 1)) {
+ ++c->term;
+ *b++ = '\0';
+ }
+ *b = '\0';
+ c->r->mode = END;
+ for (c->r = c->rds; c->r->mode; ++c->r) if (*c->r->oldname == '&') {
+ if ((l = strtol(++c->r->oldname, &end, 10)) < 0 || l > INT_MAX || *end) {
+ warnx("Invalid file redirection");
+ return ∅
+ }
+ c->r->oldfd = l;
+ c->r->oldname = NULL;
}
(++c)->args = NULL;
- } else {
- if (!*b) ++b;
- *b++ = '\0';
+ *t++ = NULL;
}
- f = c->freds;
- f->mode = END;
+ c->r = c->rds;
+ case ' ':
+ *b = '\0';
}
- switch ((c - 1)->type) {
+ switch ((c - 1)->term) {
case AND:
case PIPE:
case OR:
warnx("Command left open-ended");
- return c - 1;
+ return ∅
default:
- *c = (struct cmd){0};
+ break;
}
return cmds;
-enum terminator {
- SEMI = ';',
- BG = '&',
- AND,
- PIPE = '|',
- OR,
-};
-
-enum mode {
+enum accessmode {
END,
READ = '<',
READWRITE,
APPEND,
};
-enum type {
- FD,
- NAME,
+struct redirect {
+ enum accessmode mode;
+ int oldfd, newfd;
+ char *oldname;
};
-// if (cmd->f->type == NAME) cmd->f->old.fd = open(cmd->f->old.name, mode);
-// dup2(cmd->f->newfd, cmd->f->old.fd);
-// if (cmd->f->type == NAME) close(cmd->f->old.fd);
-//
-// // vs.
-//
-// if (*cmd->f->oldfd == '&') fd = open(++cmd->f->oldfd, mode);
-// dup2(cmd->f->newfd, cmd->
-
-struct fred {
- int newfd;
- enum mode mode;
- enum type type;
- union {
- int fd;
- char *name;
- } old;
+enum terminator {
+ SEMI = ';',
+ BG = '&',
+ AND,
+ PIPE = '|',
+ OR,
};
-/* a>&b -> dup2(b, a); reopen(a, "w"); | (1)>&3 -> dup2(3, 1);
- * a<&b -> dup2(b, a); reopen(a, "r"); | (0)<&3 -> dup2(3, 0);
- * x >a >b >c ...
- */
-
+#define MAXRDS 25
struct cmd {
char **args;
- enum terminator type;
- struct fred freds[(BUFLEN - 1) / 3 + 1];
+ struct redirect *r, rds[MAXRDS + 1];
+ enum terminator term;
int pipe[2];
};
-void printfreds(struct cmd *c);
+extern struct cmd empty;
+
struct cmd *lex(char *b);
return result;
}
-static int redirectfiles(struct fred *f) {
+static int redirectfiles(struct redirect *r) {
int oflag, fd;
- for (; f->mode; ++f) {
- if (f->type == NAME) {
- switch (f->mode) {
+ for (; r->mode; ++r) {
+ if (r->oldname) {
+ switch (r->mode) {
case READ:
oflag = O_RDONLY;
break;
break;
default:;
}
- if ((fd = open(f->old.name, oflag, 0644)) == -1) {
- warn("Unable to open `%s'", f->old.name);
+ if ((fd = open(r->oldname, oflag, 0644)) == -1) {
+ warn("Unable to open `%s'", r->oldname);
return 0;
}
- f->old.fd = fd;
+ r->oldfd = fd;
}
- if (dup2(f->old.fd, f->newfd) == -1) {
- warn("Unable to redirect %d to %d", f->newfd, f->old.fd);
+ if (dup2(r->oldfd, r->newfd) == -1) {
+ warn("Unable to redirect %d to %d", r->newfd, r->oldfd);
return 0;
}
- if (f->type == NAME) {
- if (close(f->old.fd) == -1) {
- warn("Unable to close file descriptor %d", f->old.fd);
+ if (r->oldname) {
+ if (close(r->oldfd) == -1) {
+ warn("Unable to close file descriptor %d", r->oldfd);
return 0;
}
}
initterm();
readhist();
- while ((cmd = lex(input()))) {
- while (prev = cmd++, cmd->args) {
- ispipe = cmd->type == PIPE || prev->type == PIPE;
- ispipestart = ispipe && prev->type != PIPE;
- ispipeend = ispipe && cmd->type != PIPE;
+ for (prev = ∅ (cmd = lex(input())); prev = &empty) {
+ for (; cmd->args; prev = cmd++) {
+ ispipe = cmd->term == PIPE || prev->term == PIPE;
+ ispipestart = ispipe && prev->term != PIPE;
+ ispipeend = ispipe && cmd->term != PIPE;
if (ispipe) {
if (!ispipeend && pipe(cmd->pipe) == -1) {
if (!closepipe(cmd)) exit(EXIT_FAILURE);
}
- if (!redirectfiles(cmd->freds)) exit(EXIT_FAILURE);
+ if (!redirectfiles(cmd->rds)) exit(EXIT_FAILURE);
if (isbuiltin(cmd->args, &status)) exit(EXIT_SUCCESS);
if (execvp(*cmd->args, cmd->args) == -1)
err(EXIT_FAILURE, "Couldn't find `%s' command", *cmd->args);
jobid = ((struct job *)(ispipeend ? pull : peek)(&jobs))->id;
}
} else {
- if (cmd->freds->mode == END && isbuiltin(cmd->args, &status)) break;
+ if (cmd->rds->mode == END && isbuiltin(cmd->args, &status)) break;
if ((jobid = cpid = fork()) == -1) {
warn("Unable to create child process");
break;
} else if (cpid == 0) {
- if (!redirectfiles(cmd->freds)) exit(EXIT_FAILURE);
+ if (!redirectfiles(cmd->rds)) exit(EXIT_FAILURE);
if (isbuiltin(cmd->args, &status)) exit(EXIT_SUCCESS);
if (execvp(*cmd->args, cmd->args) == -1)
err(EXIT_FAILURE, "Couldn't find `%s' command", *cmd->args);
}
job = (struct job){.id = jobid, .config = canonical, .type = BACKGROUND};
- if (ispipestart || cmd->type == BG) {
+ if (ispipestart || cmd->term == BG) {
if (!push(&jobs, &job)) {
warn("Unable to add command to background; "
"too many processes in the background");
if (ispipestart) closepipe(cmd);
break;
}
- } else if (cmd->type != PIPE) {
+ } else if (cmd->term != PIPE) {
if (!setfg(job)) break;
status = waitfg(job);
- if (cmd->type == AND && status != 0) break;
- if (cmd->type == OR && status == 0) break;
+ if (cmd->term == AND && status != 0) break;
+ if (cmd->term == OR && status == 0) break;
}
}
waitbg(0);