{"context", NONE},
{"history", NONE},
{"input", NONE},
- {"job", NONE},
{"main", BUILTINS},
{"options", BUILTINS},
{"parse", BUILTINS},
switch (argc) {
case 1:
for (i = 0; i < aliases.size; ++i)
- printf("%s = \"%s\"\r\n", aliases.entries[i].lhs, aliases.entries[i].rhs);
+ printf("%s = \"%s\"\n", aliases.entries[i].lhs, aliases.entries[i].rhs);
break;
case 3:
if (aliases.size == MAXALIAS) {
#include <limits.h>
#include <signal.h>
#include <stdlib.h>
+#include <string.h>
#include <sys/errno.h>
#include <termios.h>
+#include "bg.h"
#include "builtin.h"
-#include "job.h"
+#include "fg.h"
#include "utils.h"
+#define MAXBG 100
+
+struct bglink {
+ struct bgjob job;
+ struct bglink *next;
+};
+
+static struct {
+ struct bglink entries[MAXBG + 1], *active, *free;
+} bgjobs;
+
+void initbg(void) {
+ size_t i;
+
+ for (i = 0; i < MAXBG - 1; ++i)
+ bgjobs.entries[i].next = &bgjobs.entries[i + 1];
+ bgjobs.free = bgjobs.entries;
+}
+
+int fullbg(void) {
+ return !bgjobs.free;
+}
+
+int pushbg(struct bgjob job) {
+ struct bglink *p;
+
+ if (fullbg()) return 0;
+
+ (p = bgjobs.free)->job = job;
+ bgjobs.free = p->next;
+ p->next = bgjobs.active;
+ bgjobs.active = p;
+
+ return 1;
+}
+
+int peekbg(struct bgjob *job) {
+ if (bgjobs.active && job) *job = bgjobs.active->job;
+ return bgjobs.active != NULL;
+}
+
+int searchbg(pid_t id, struct bgjob *job) {
+ struct bglink *p;
+
+ for (p = bgjobs.active; p; p = p->next) if (p->job.id == id) {
+ if (job) *job = p->job;
+ return 1;
+ }
+
+ return 0;
+}
+
+int pushid(pid_t id) {
+ return pushbg((struct bgjob){.id = id, .config = canonical});
+}
+
+void removeid(pid_t id) {
+ struct bglink *p, *prev;
+
+ for (prev = NULL, p = bgjobs.active; p; prev = p, p = p->next)
+ if (p->job.id == id) break;
+ if (!p) return;
+
+ if (prev) prev->next = p->next; else bgjobs.active = p->next;
+ p->next = bgjobs.free;
+ bgjobs.free = p;
+}
+
+void waitbg(int sig) {
+ int e, s;
+ struct bglink *p;
+ pid_t id;
+
+ (void)sig;
+ e = errno;
+ p = bgjobs.active;
+ while (p) {
+ while ((id = waitpid(-p->job.id, &s, WNOHANG | WUNTRACED)) > 0) {
+ if (WIFSTOPPED(s)) {
+ p->job.suspended = 1;
+ break;
+ }
+ }
+ if (id == -1) {
+ id = p->job.id;
+ p = p->next;
+ removeid(id);
+ } else p = p->next;
+ }
+ errno = e;
+}
+
+void deinitbg(void) {
+ struct bglink *p;
+
+ for (p = bgjobs.active; p; p = p->next) killpg(p->job.id, SIGKILL);
+}
+
BUILTIN(bg) {
+ struct bglink *p;
+ struct bgjob job;
long l;
- pid_t id;
- struct job *job;
switch (argc) {
case 1:
- if (!(job = peeksuspendedjob())) return EXIT_FAILURE;
+ for (p = bgjobs.active; p; p = p->next) if (p->job.suspended) {
+ job = p->job;
+ break;
+ }
+ if (!p) {
+ note("No suspended jobs to run in the background");
+ return EXIT_FAILURE;
+ }
break;
case 2:
errno = 0;
note("Invalid job id %ld", l);
return EXIT_FAILURE;
}
- if (!(job = searchjobid(id = (pid_t)l))) {
- note("Unable to find job %d", id);
+ if (!searchbg(l, &job)) {
+ note("Unable to find job %d", (pid_t)l);
return EXIT_FAILURE;
}
- if (!job->suspended) {
- note("Job %d already in background", id);
+ if (!job.suspended) {
+ note("Job %d already in background", job.id);
return EXIT_FAILURE;
}
break;
default:
return usage(argv[0], "[pgid]");
}
- deletejobid(job->id);
- if (!pushjob(job)) return EXIT_FAILURE;
- if (killpg(job->id, SIGCONT) == -1) {
- note("Unable to wake up suspended job %d", job->id);
+ if (killpg(job.id, SIGCONT) == -1) {
+ note("Unable to wake up suspended job %d", job.id);
return EXIT_FAILURE;
}
- job->suspended = 0;
+ removeid(job.id);
+ job.suspended = 0;
+ pushbg(job);
return EXIT_SUCCESS;
}
--- /dev/null
+struct bgjob {
+ pid_t id;
+ struct termios config;
+ int suspended;
+};
+
+void initbg(void);
+
+int fullbg(void);
+int pushbg(struct bgjob job);
+int peekbg(struct bgjob *job);
+int searchbg(pid_t id, struct bgjob *job);
+
+int pushid(pid_t id);
+void removeid(pid_t id);
+
+void waitbg(int sig);
+
+void deinitbg(void);
}
int usage(char *program, char *options) {
- fprintf(stderr, "Usage: %s %s\r\n", program, options);
+ fprintf(stderr, "Usage: %s %s\n", program, options);
return EXIT_FAILURE;
}
#include <stdio.h>
#include <stdlib.h>
#include <sys/errno.h>
+#include <sys/wait.h>
#include <termios.h>
#include <unistd.h>
+#include "bg.h"
#include "builtin.h"
-#include "job.h"
+#include "context.h"
+#include "input.h"
#include "utils.h"
static struct {
pid_t id;
int status, done;
-} fgpg;
+} fgjob;
struct termios canonical;
static struct termios raw;
-struct sigaction sigchld, sigdfl;
+struct sigaction actbg, actdefault;
+static struct sigaction actall, actignore;
static int setconfig(struct termios *mode) {
if (tcsetattr(STDIN_FILENO, TCSANOW, mode) == -1) {
return 1;
}
-static void sigchldhandler(int sig) {
- int e;
+static void waitall(int sig) {
+ int e, s;
pid_t id;
- struct job *job;
- (void)sig;
e = errno;
- while ((id = waitpid(-1, &fgpg.status, WNOHANG | WUNTRACED)) > 0)
- if ((job = searchjobid(id))) {
- if (WIFSTOPPED(fgpg.status)) job->suspended = 1; else deletejobid(id);
- } else {
- fgpg.done = 1;
- if (WIFSTOPPED(fgpg.status)) continue;
- if (id != fgpg.id) waitpid(fgpg.id, &fgpg.status, 0);
- while (waitpid(-fgpg.id, NULL, 0) != -1);
+ while ((id = waitpid(-fgjob.id, &s, WNOHANG | WUNTRACED)) > 0) {
+ if (id == fgjob.id) fgjob.status = s;
+ if (WIFSTOPPED(s)) {
+ fgjob.status = s;
+ fgjob.done = 1;
+ break;
}
+ }
+ if (id == -1) fgjob.done = 1;
+ waitbg(sig);
errno = e;
}
void initfg(void) {
if (tcgetattr(STDIN_FILENO, &canonical) == -1)
fatal("Unable to get default termios config");
- cfmakeraw(&raw);
+ raw = canonical;
+ raw.c_lflag &= ~(ICANON | ECHO | ISIG);
if (!setconfig(&raw)) exit(EXIT_FAILURE);
- sigchld.sa_handler = sigchldhandler;
- sigdfl.sa_handler = SIG_DFL;
- setsigchld(&sigchld);
+ actbg.sa_handler = waitbg;
+ actall.sa_handler = waitall;
+ actdefault.sa_handler = SIG_DFL;
+ actignore.sa_handler = SIG_IGN;
}
-int setfg(struct job job) {
+int runfg(pid_t id) {
+ struct bgjob job;
+
+ if (!searchbg(id, &job)) job = (struct bgjob){.id = id, .config = canonical};
if (!setconfig(&job.config)) return 0;
- if (tcsetpgrp(STDIN_FILENO, job.id) == -1) {
- note("Unable to bring job %d to foreground", job.id);
+ if (tcsetpgrp(STDIN_FILENO, id) == -1) {
+ note("Unable to bring job %d to foreground", id);
setconfig(&raw);
return 0;
}
- if (killpg(job.id, SIGCONT) == -1) {
- note("Unable to wake up job %d", job.id);
+ if (killpg(id, SIGCONT) == -1) {
+ note("Unable to wake up job %d", id);
return 0;
}
- return 1;
-}
-
-void waitfg(struct job job) {
- struct sigaction sigign;
+ removeid(id);
/* SIGCHLD handler is really the function that reaps the foreground process,
* the waitpid() below is just to block the current thread of execution until
* the foreground process has been reaped. */
- fgpg.id = job.id;
- setsigchld(&sigchld);
- while (waitpid(fgpg.id, NULL, 0) && !fgpg.done);
- fgpg.done = errno = 0;
- setsigchld(&sigdfl);
+ fgjob.id = id;
+ setsigchld(&actall);
+ while (!fgjob.done) sigsuspend(&actall.sa_mask);
+ setsigchld(&actdefault);
+ fgjob.done = errno = 0;
- if (sigaction(SIGTTOU, &(struct sigaction){{SIG_IGN}}, NULL) == -1
+ if (sigaction(SIGTTOU, &actignore, NULL) == -1
|| tcsetpgrp(STDIN_FILENO, getpgrp()) == -1
- || sigaction(SIGTTOU, &sigdfl, NULL) == -1) {
+ || sigaction(SIGTTOU, &actdefault, NULL) == -1) {
note("Unable to reclaim foreground; terminating");
deinit();
exit(EXIT_FAILURE);
}
if (tcgetattr(STDIN_FILENO, &job.config) == -1)
- note("Unable to save termios config of job %d", job.id);
+ note("Unable to save termios config of job %d", id);
setconfig(&raw);
- if (WIFEXITED(fgpg.status)) status = WEXITSTATUS(fgpg.status);
- else if (WIFSTOPPED(fgpg.status)) {
- status = WSTOPSIG(fgpg.status);
+ if (WIFEXITED(fgjob.status)) status = WEXITSTATUS(fgjob.status);
+ else if (WIFSTOPPED(fgjob.status)) {
+ status = WSTOPSIG(fgjob.status);
job.suspended = 1;
- if (!pushjob(&job)) {
- note("Press any key to continue");
+ if (!pushbg(job)) {
+ note("Unable to suspend current process, too many background jobs\n"
+ "(Press any key to continue)");
getchar();
- if (setfg(job)) return waitfg(job);
- note("Manual intervention required for job %d", job.id);
+ return runfg(id);
}
- } else status = WTERMSIG(fgpg.status);
+ } else status = WTERMSIG(fgjob.status);
+
+ return 1;
}
void deinitfg(void) {
}
BUILTIN(fg) {
+ struct bgjob job;
long l;
pid_t id;
- struct job *job;
switch (argc) {
case 1:
- if (!(job = pulljob())) {
+ if (!peekbg(&job)) {
note("No job to bring into the foreground");
return EXIT_FAILURE;
}
+ id = job.id;
break;
case 2:
errno = 0;
note("Invalid process group id %ld", l);
return EXIT_FAILURE;
}
- if (!(job = searchjobid(id))) {
+ id = (pid_t)l;
+ if (!searchbg(id, NULL)) {
note("Unable to find job %d", id);
return EXIT_FAILURE;
}
- deletejobid(id);
break;
default:
return usage(argv[0], "[pgid]");
}
- if (!setfg(*job)) return EXIT_FAILURE;
- waitfg(*job);
+ if (!runfg(id)) return EXIT_FAILURE;
return EXIT_SUCCESS;
}
extern struct termios canonical;
-extern struct sigaction sigchld, sigdfl;
+extern struct sigaction actbg, actdefault;
void setsigchld(struct sigaction *act);
void initfg(void);
-int setfg(struct job job);
-void waitfg(struct job job);
+int runfg(pid_t id);
void deinitfg(void);
int c;
char **v;
- if (argc == 1) {
- fputs("Usage: source file [args ...]\r\n", stderr);
- return EXIT_FAILURE;
- }
+ if (argc == 1) return usage(argv[0], "file [args ...]");
context = (struct context){0};
context.script = argv[1];
if (argc != 2) return usage(argv[0], "name");
if (!(result = getpathtype(argv[1], &type))) {
- printf("%s not found\r\n", argv[1]);
+ printf("%s not found\n", argv[1]);
return EXIT_SUCCESS;
}
fputs(result, stdout);
if (type == BUILTIN) fputs(" is built-in", stdout);
- puts("\r");
+ putchar('\n');
return EXIT_SUCCESS;
}
c->b = NULL;
c->t = NULL;
c->r = NULL;
- *c->current.name = '\0';
+ *c->current.name = *c->buffer = '\0';
c->current.term = SEMI;
return 1;
if (fclose(file) == EOF) fatal("Unable to close history file");
}
-int gethistory(char direction, char *buffer) {
- if (history.c == (direction == UP ? history.b : history.t)) return 0;
+int gethistory(int back, char *buffer) {
+ if (history.c == (back ? history.b : history.t)) return 0;
// Save the most recently modified history entry at the top of the list
if (strcmp(history.entries[history.c], buffer) != 0)
strcpy(history.entries[history.t], buffer);
- strcpy(buffer, history.entries[direction == UP ? DEC(c) : INC(c)]);
+ strcpy(buffer, history.entries[back ? DEC(c) : INC(c)]);
return 1;
}
void inithistory(void);
-int gethistory(char direction, char *buffer);
+int gethistory(int back, char *buffer);
void sethistory(char *buffer);
void deinithistory(void);
#include "input.h"
#include "utils.h"
+enum {
+ CTRLC = '\003',
+ CTRLD,
+ CLEAR = '\014',
+ ESCAPE = '\033',
+
+ // See ESCAPE case in userinput()
+ ALT = '2' + 1,
+
+ UP = 'A',
+ DOWN,
+ RIGHT,
+ LEFT,
+ FORWARD = 'f',
+ BACKWARD = 'b',
+ DEL = '\177',
+};
+
int stringinput(struct context *c) {
char *start;
size_t l;
if (!(p = getenv("PROMPT")) && setenv("PROMPT", p = ">", 1) == -1)
note("Unable to update $PROMPT$ environment variable");
- printf("\r%s ", p);
+ printf("%s ", p);
}
int userinput(struct context *c) {
char *start, *cursor, *end;
- unsigned int current;
- int i;
+ int current, i;
size_t oldlen, newlen;
+ clear(c);
end = cursor = start = c->buffer;
- *c->buffer = '\0';
while (start == end) {
prompt();
- while ((current = getchar()) != '\r') switch (current) {
+ while ((current = getchar()) != '\n') switch (current) {
default:
if (current >= ' ' && current <= '~') {
- if (end - start == MAXCHARS) continue;
+ if (end - start == MAXCHARS) break;
memmove(cursor + 1, cursor, end - cursor);
*cursor++ = current;
*++end = '\0';
}
break;
case CTRLC:
- puts("^C\r");
+ puts("^C");
return quit(c);
case CTRLD:
- puts("^D\r");
+ puts("^D");
return 0;
case CLEAR:
fputs("\033[H\033[J", stdout);
prompt();
fputs(c->buffer, stdout);
- continue;
+ break;
+
+ /* This is a very minimal way to handle arrow keys. All modifiers except for
+ * the ALT key are processed but ignored.
+ *
+ * Reference:
+ * https://en.wikipedia.org/wiki/ANSI_escape_code#Terminal_input_sequences */
case ESCAPE:
- switch ((current = getchar())) {
- case FORWARD:
- while (cursor != end && *cursor != ' ') putchar(*cursor++);
- 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'));
- break;
- case ARROW:
- switch ((current = getchar())) {
+ if ((current = getchar()) == '[') {
+ while ((current = getchar()) >= '0' && current <= '9');
+ if (current == ';') {
+ if ((current = getchar()) == ALT) switch ((current = getchar())) {
+ case LEFT:
+ current = BACKWARD;
+ break;
+ case RIGHT:
+ current = FORWARD;
+ break;
+ } else if ((current = getchar()) >= '0' && current <= '6')
+ current = getchar();
+ }
+ switch (current) {
case UP:
case DOWN:
oldlen = strlen(c->buffer);
- if (!gethistory(current, c->buffer)) continue;
+ if (!gethistory(current == UP, c->buffer)) break;
newlen = strlen(c->buffer);
end = cursor = start + newlen;
if (cursor < end) putchar(*cursor++);
break;
}
+ }
+ switch (current) {
+ case FORWARD:
+ while (cursor != end && *cursor != ' ') putchar(*cursor++);
+ 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'));
break;
- default:
- ungetc(current, stdin);
}
break;
- case BACKSPACE:
+
case DEL:
- if (cursor == start) continue;
+ if (cursor == start) break;
memmove(cursor - 1, cursor, end - cursor);
--cursor;
*--end = '\0';
break;
}
- puts("\r");
+ putchar('\n');
}
while (*start == ' ') ++start;
-enum {
- CTRLC = '\003',
- CTRLD,
- BACKSPACE = '\010',
- CLEAR = '\014',
- ESCAPE = '\033',
- FORWARD = 'f',
- BACKWARD = 'b',
- ARROW = '[',
- UP = 'A',
- DOWN,
- RIGHT,
- LEFT,
- DEL = '\177',
-};
-
int stringinput(struct context *c);
int scriptinput(struct context *c);
int userinput(struct context *c);
+++ /dev/null
-#include <string.h>
-#include <termios.h>
-
-#include "job.h"
-#include "utils.h"
-
-#define MAXJOBS 100
-
-struct joblink {
- struct job job;
- struct joblink *next;
-};
-
-static struct {
- struct joblink entries[MAXJOBS + 1], *active, *free;
-} jobs;
-
-void initjobs(void) {
- size_t i;
-
- for (i = 0; i < MAXJOBS - 1; ++i) jobs.entries[i].next = &jobs.entries[i + 1];
- jobs.free = jobs.entries;
-}
-
-struct job *pushjob(struct job *job) {
- struct joblink *p;
-
- if (!jobs.free) {
- note("Unable to %s, exceeds %d job limit",
- job->suspended ? "suspend job" : "place job in background", MAXJOBS);
- return NULL;
- }
-
- (p = jobs.free)->job = *job;
- jobs.free = p->next;
- p->next = jobs.active;
- jobs.active = p;
-
- return &p->job;
-}
-
-struct job *pulljob(void) {
- struct joblink *p;
-
- if (!jobs.active) return NULL;
-
- p = jobs.active;
- jobs.active = p->next;
- p->next = jobs.free;
- jobs.free = p;
-
- return &p->job;
-}
-
-struct job *peekjob(void) {
- return jobs.active ? &jobs.active->job : NULL;
-}
-
-struct job *peeksuspendedjob(void) {
- struct joblink *p;
-
- for (p = jobs.active; p; p = p->next) if (p->job.suspended) return &p->job;
-
- note("No suspended job to run in background");
-
- return NULL;
-}
-
-struct job *searchjobid(pid_t id) {
- struct joblink *p;
-
- for (p = jobs.active; p; p = p->next) if (p->job.id == id) return &p->job;
-
- return NULL;
-}
-
-struct job *deletejobid(pid_t id) {
- struct joblink *p, *prev;
-
- for (prev = NULL, p = jobs.active; p; prev = p, p = p->next) if (p->job.id == id) break;
- if (!p) return NULL;
-
- if (prev) prev->next = p->next; else jobs.active = p->next;
- p->next = jobs.free;
- jobs.free = p;
-
- return &p->job;
-}
+++ /dev/null
-struct job {
- pid_t id;
- struct termios config;
- int suspended;
-};
-
-void initjobs(void);
-struct job *pushjob(struct job *job);
-struct job *pulljob(void);
-struct job *peekjob(void);
-struct job *peeksuspendedjob(void);
-struct job *searchjobid(pid_t id);
-struct job *deletejobid(pid_t id);
#include "utils.h"
int parse(struct context *c) {
- int globbing, e, offset;
+ int globbing, e, offset, globflags;
+ size_t prevsublen, sublen;
char *stlend, *p, *end, *env, term, **sub;
long l;
- size_t sublen;
static glob_t globs;
if (!c->b) {
c->r = c->redirects;
c->r->mode = NONE;
c->prev = c->current;
- globbing = 0;
+ prevsublen = globbing = 0;
for (*c->t = c->b; *c->b; ++c->b) switch (*c->b) {
case '<':
*c->t = c->b;
} else if (!c->alias && c->t == c->tokens && (sub = getalias(*c->tokens)) || globbing) {
if (globbing) {
- switch (glob(*c->t, GLOB_APPEND | GLOB_MARK, NULL, &globs)) {
+ globflags = GLOB_MARK;
+ if (prevsublen) globflags |= GLOB_APPEND;
+ switch (glob(*c->t, globflags, NULL, &globs)) {
case GLOB_NOMATCH:
note("No matches found for %s", *c->t);
return quit(c);
case GLOB_NOSPACE:
fatal("Memory allocation");
}
- sublen = globs.gl_matchc;
- sub = globs.gl_pathv + globs.gl_pathc - sublen;
+ sublen = globs.gl_pathc - prevsublen;
+ sub = globs.gl_pathv + prevsublen;
+ prevsublen = globs.gl_pathc;
globbing = 0;
} else for (sublen = 0; sub[sublen]; ++sublen);
#include <sys/wait.h>
#include <termios.h>
#include <unistd.h>
+#include <stdio.h> // XXX
#include "builtin.h"
#include "context.h"
-#include "job.h"
+#include "bg.h"
#include "fg.h"
#include "parse.h"
#include "utils.h"
int islist, ispipe, ispipestart, ispipeend;
char *path;
pid_t cpid, jobid;
- struct job *p, job;
+ static pid_t pipeid;
- setsigchld(&sigchld);
+ setsigchld(&actbg);
if (!parse(c)) return 0;
- setsigchld(&sigdfl);
-
+ setsigchld(&actdefault);
islist = c->prev.term > BG || c->current.term > BG;
if (c->t) {
+ if (c->current.term == BG && fullbg()) {
+ note("Unable to place job in background, too many background jobs",
+ c->current.name);
+ return quit(c);
+ }
if (!(path = getpath(c->current.name))) {
note("Couldn't find `%s' command", c->current.name);
- if (c->prev.term == PIPE) closepipe(c->prev);
+ if (c->prev.term == PIPE) {
+ killpg(pipeid, SIGKILL);
+ }
return quit(c);
}
if (!ispipestart) closepipe(c->prev);
return quit(c);
}
- if ((jobid = cpid = fork()) == -1) {
+ if ((cpid = fork()) == -1) {
note("Unable to fork child process");
return quit(c);
} else if (cpid == 0) {
}
exec(path, c);
}
- if (!ispipestart) {
- closepipe(c->prev);
- if (!(p = (ispipeend ? pulljob : peekjob)())) {
- note("Unable to %s pipeline job from background",
- ispipeend ? "remove" : "get");
- return quit(c);
- }
- jobid = p->id;
- }
+ if (ispipestart) pipeid = cpid;
+ jobid = pipeid;
} else if (!c->r && isbuiltin(c->tokens)) cpid = 0;
else if ((jobid = cpid = fork()) == -1) {
note("Unable to fork child process");
}
return quit(c);
}
- job = (struct job){.id = jobid, .config = canonical};
if (ispipestart || c->current.term == BG) {
- if (!pushjob(&job)) {
- if (ispipestart) closepipe(c->current);
- return quit(c);
- }
- } else if (c->current.term != PIPE) {
- if (!setfg(job)) return quit(c);
- waitfg(job);
+ pushid(jobid);
+ return 1;
}
+ if (c->current.term != PIPE && !runfg(jobid)) return quit(c);
}
if (status != EXIT_SUCCESS) {
} else if (c->current.term == OR) return clear(c);
} else {
if (islist) {
- if (c->prev.term == PIPE) closepipe(c->prev);
+ if (c->prev.term == PIPE) killpg(pipeid, SIGKILL);
note("Expected command");
return quit(c);
}
- if (c->r) return 1;
+ if (!c->r) return 1;
if ((cpid = fork()) == -1) {
note("Unable to fork child process");
#include <termios.h>
#include <unistd.h>
+#include "bg.h"
#include "context.h"
-#include "history.h"
-#include "job.h"
#include "fg.h"
+#include "history.h"
#include "options.h"
int argcount, status;
va_start(args, fmt);
(errno ? vwarn : vwarnx)(fmt, args);
va_end(args);
- putc('\r', stderr);
errno = 0;
}
va_start(args, fmt);
(errno ? vwarn : vwarnx)(fmt, args);
va_end(args);
- putc('\r', stderr);
exit(EXIT_FAILURE);
}
note("Unable to update $SHLVL$ environment variable");
initfg();
- initjobs();
+ initbg();
if (interactive) inithistory();
}
void deinit(void) {
if (interactive) deinithistory();
+ deinitbg();
deinitfg();
}