Re-enable globbing (I'd accidentaly removed it) and finish off the last
of the job control polishing. Works fine now.
-Erik
diff --git a/lash.c b/lash.c
index 68f6735..e4ca5f6 100644
--- a/lash.c
+++ b/lash.c
@@ -135,6 +135,7 @@
static void mark_closed(int fd);
static void close_all(void);
static void checkjobs(struct jobset *job_list);
+static void remove_job(struct jobset *j_list, struct job *job);
static int get_command(FILE * source, char *command);
static int parse_command(char **command_ptr, struct job *job, int *inbg);
static int run_command(struct job *newjob, int inbg, int outpipe[2]);
@@ -181,6 +182,8 @@
static int argc;
static char **argv;
static struct close_me *close_me_head;
+static int last_return_code;
+static int last_bg_pid;
static unsigned int last_jobid;
static int shell_terminal;
static pid_t shell_pgrp;
@@ -313,8 +316,12 @@
job->stopped_progs = 0;
- if (kill(- job->pgrp, SIGCONT) < 0)
+ if ( (i=kill(- job->pgrp, SIGCONT)) < 0) {
+ if (i == ESRCH) {
+ remove_job(&job_list, job);
+ }
perror_msg("kill (SIGCONT)");
+ }
return EXIT_SUCCESS;
}
@@ -536,6 +543,11 @@
prevjob->next = job->next;
}
+ if (j_list->head)
+ last_jobid = j_list->head->jobid;
+ else
+ last_jobid = 0;
+
free(job);
}
@@ -576,10 +588,15 @@
job->stopped_progs++;
job->progs[prognum].is_stopped = 1;
+#if 0
+ /* Printing this stuff is a pain, since it tends to
+ * overwrite the prompt an inconveinient moments. So
+ * don't do that. */
if (job->stopped_progs == job->num_progs) {
printf(JOB_STATUS_FORMAT, job->jobid, "Stopped",
job->text);
}
+#endif
}
}
@@ -714,14 +731,78 @@
return 0;
}
+static char* itoa(register int i)
+{
+ static char a[7]; /* Max 7 ints */
+ register char *b = a + sizeof(a) - 1;
+ int sign = (i < 0);
+
+ if (sign)
+ i = -i;
+ *b = 0;
+ do
+ {
+ *--b = '0' + (i % 10);
+ i /= 10;
+ }
+ while (i);
+ if (sign)
+ *--b = '-';
+ return b;
+}
+
+char * strsep_space( char *string, int * ix)
+{
+ char *token, *begin;
+
+ begin = string;
+
+ /* Short circuit the trivial case */
+ if ( !string || ! string[*ix])
+ return NULL;
+
+ /* Find the end of the token. */
+ while( string && string[*ix] && !isspace(string[*ix]) ) {
+ (*ix)++;
+ }
+
+ /* Find the end of any whitespace trailing behind
+ * the token and let that be part of the token */
+ while( string && string[*ix] && isspace(string[*ix]) ) {
+ (*ix)++;
+ }
+
+ if (! string && *ix==0) {
+ /* Nothing useful was found */
+ return NULL;
+ }
+
+ token = xmalloc(*ix+1);
+ token[*ix] = '\0';
+ strncpy(token, string, *ix);
+
+ return token;
+}
static int expand_arguments(char *command)
{
- int ix = 0;
+ int total_length=0, length, i, retval, ix = 0;
+ expand_t expand_result;
+ char *tmpcmd, *cmd, *cmd_copy;
+ char *src, *dst, *var;
+ const char *out_of_space = "out of space during expansion";
+ int flags = GLOB_NOCHECK
+#ifdef GLOB_BRACE
+ | GLOB_BRACE
+#endif
+#ifdef GLOB_TILDE
+ | GLOB_TILDE
+#endif
+ ;
/* get rid of the terminating \n */
chomp(command);
-
+
/* Fix up escape sequences to be the Real Thing(tm) */
while( command && command[ix]) {
if (command[ix] == '\\') {
@@ -731,6 +812,139 @@
}
ix++;
}
+ /* Use glob and then fixup environment variables and such */
+
+ /* It turns out that glob is very stupid. We have to feed it one word at a
+ * time since it can't cope with a full string. Here we convert command
+ * (char*) into cmd (char**, one word per string) */
+
+ /* We need a clean copy, so strsep can mess up the copy while
+ * we write stuff into the original (in a minute) */
+ cmd = cmd_copy = strdup(command);
+ *command = '\0';
+ for (ix = 0, tmpcmd = cmd;
+ (tmpcmd = strsep_space(cmd, &ix)) != NULL; cmd += ix, ix=0) {
+ if (*tmpcmd == '\0')
+ break;
+ /* we need to trim() the result for glob! */
+ trim(tmpcmd);
+ retval = glob(tmpcmd, flags, NULL, &expand_result);
+ free(tmpcmd); /* Free mem allocated by strsep_space */
+ if (retval == GLOB_NOSPACE) {
+ /* Mem may have been allocated... */
+ globfree (&expand_result);
+ error_msg(out_of_space);
+ return FALSE;
+ } else if (retval != 0) {
+ /* Some other error. GLOB_NOMATCH shouldn't
+ * happen because of the GLOB_NOCHECK flag in
+ * the glob call. */
+ error_msg("syntax error");
+ return FALSE;
+ } else {
+ /* Convert from char** (one word per string) to a simple char*,
+ * but don't overflow command which is BUFSIZ in length */
+ for (i=0; i < expand_result.gl_pathc; i++) {
+ length=strlen(expand_result.gl_pathv[i]);
+ if (total_length+length+1 >= BUFSIZ) {
+ error_msg(out_of_space);
+ return FALSE;
+ }
+ strcat(command+total_length, " ");
+ total_length+=1;
+ strcat(command+total_length, expand_result.gl_pathv[i]);
+ total_length+=length;
+ }
+ globfree (&expand_result);
+ }
+ }
+ free(cmd_copy);
+ trim(command);
+
+ /* Now do the shell variable substitutions which
+ * wordexp can't do for us, namely $? and $! */
+ src = command;
+ while((dst = strchr(src,'$')) != NULL){
+ var = NULL;
+ switch(*(dst+1)) {
+ case '?':
+ var = itoa(last_return_code);
+ break;
+ case '!':
+ if (last_bg_pid==-1)
+ *(var)='\0';
+ else
+ var = itoa(last_bg_pid);
+ break;
+ /* Everything else like $$, $#, $[0-9], etc. should all be
+ * expanded by wordexp(), so we can in theory skip that stuff
+ * here, but just to be on the safe side (i.e., since uClibc
+ * wordexp doesn't do this stuff yet), lets leave it in for
+ * now. */
+ case '$':
+ var = itoa(getpid());
+ break;
+ case '#':
+ var = itoa(argc-1);
+ break;
+ case '0':case '1':case '2':case '3':case '4':
+ case '5':case '6':case '7':case '8':case '9':
+ {
+ int ixx=*(dst + 1)-48;
+ if (ixx >= argc) {
+ var='\0';
+ } else {
+ var = argv[ixx];
+ }
+ }
+ break;
+
+ }
+ if (var) {
+ /* a single character construction was found, and
+ * already handled in the case statement */
+ src=dst+2;
+ } else {
+ /* Looks like an environment variable */
+ char delim_hold;
+ int num_skip_chars=0;
+ int dstlen = strlen(dst);
+ /* Is this a ${foo} type variable? */
+ if (dstlen >=2 && *(dst+1) == '{') {
+ src=strchr(dst+1, '}');
+ num_skip_chars=1;
+ } else {
+ src=dst+1;
+ while(isalnum(*src) || *src=='_') src++;
+ }
+ if (src == NULL) {
+ src = dst+dstlen;
+ }
+ delim_hold=*src;
+ *src='\0'; /* temporary */
+ var = getenv(dst + 1 + num_skip_chars);
+ *src=delim_hold;
+ src += num_skip_chars;
+ }
+ if (var == NULL) {
+ /* Seems we got an un-expandable variable. So delete it. */
+ var = "";
+ }
+ {
+ int subst_len = strlen(var);
+ int trail_len = strlen(src);
+ if (dst+subst_len+trail_len >= command+BUFSIZ) {
+ error_msg(out_of_space);
+ return FALSE;
+ }
+ /* Move stuff to the end of the string to accommodate
+ * filling the created gap with the new stuff */
+ memmove(dst+subst_len, src, trail_len+1);
+ /* Now copy in the new stuff */
+ memcpy(dst, var, subst_len);
+ src = dst+subst_len;
+ }
+ }
return TRUE;
}
@@ -1078,6 +1292,7 @@
printf("[%d] %d\n", thejob->jobid,
newjob->progs[newjob->num_progs - 1].pid);
last_jobid = newjob->jobid;
+ last_bg_pid=newjob->progs[newjob->num_progs - 1].pid;
} else {
newjob->job_list->fg = thejob;
@@ -1250,6 +1465,8 @@
job_list.fg->running_progs--;
job_list.fg->progs[i].pid = 0;
+ last_return_code=WEXITSTATUS(status);
+
if (!job_list.fg->running_progs) {
/* child exited */
remove_job(&job_list, job_list.fg);
@@ -1322,10 +1539,9 @@
signal(SIGCHLD, SIG_IGN);
/* Put ourselves in our own process group. */
+ setsid();
shell_pgrp = getpid ();
- if (setpgid (shell_pgrp, shell_pgrp) < 0) {
- perror_msg_and_die("Couldn't put the shell in its own process group");
- }
+ setpgid (shell_pgrp, shell_pgrp);
/* Grab control of the terminal. */
tcsetpgrp(shell_terminal, shell_pgrp);
@@ -1345,13 +1561,12 @@
close_me_head = NULL;
job_list.head = NULL;
job_list.fg = NULL;
+ last_return_code=1;
if (argv[0] && argv[0][0] == '-') {
FILE *prof_input;
prof_input = fopen("/etc/profile", "r");
- if (!prof_input) {
- printf( "Couldn't open file '/etc/profile'\n");
- } else {
+ if (prof_input) {
int tmp_fd = fileno(prof_input);
mark_open(tmp_fd);
/* Now run the file */
@@ -1418,4 +1633,3 @@
return (busy_loop(input));
}
-
diff --git a/shell/lash.c b/shell/lash.c
index 68f6735..e4ca5f6 100644
--- a/shell/lash.c
+++ b/shell/lash.c
@@ -135,6 +135,7 @@
static void mark_closed(int fd);
static void close_all(void);
static void checkjobs(struct jobset *job_list);
+static void remove_job(struct jobset *j_list, struct job *job);
static int get_command(FILE * source, char *command);
static int parse_command(char **command_ptr, struct job *job, int *inbg);
static int run_command(struct job *newjob, int inbg, int outpipe[2]);
@@ -181,6 +182,8 @@
static int argc;
static char **argv;
static struct close_me *close_me_head;
+static int last_return_code;
+static int last_bg_pid;
static unsigned int last_jobid;
static int shell_terminal;
static pid_t shell_pgrp;
@@ -313,8 +316,12 @@
job->stopped_progs = 0;
- if (kill(- job->pgrp, SIGCONT) < 0)
+ if ( (i=kill(- job->pgrp, SIGCONT)) < 0) {
+ if (i == ESRCH) {
+ remove_job(&job_list, job);
+ }
perror_msg("kill (SIGCONT)");
+ }
return EXIT_SUCCESS;
}
@@ -536,6 +543,11 @@
prevjob->next = job->next;
}
+ if (j_list->head)
+ last_jobid = j_list->head->jobid;
+ else
+ last_jobid = 0;
+
free(job);
}
@@ -576,10 +588,15 @@
job->stopped_progs++;
job->progs[prognum].is_stopped = 1;
+#if 0
+ /* Printing this stuff is a pain, since it tends to
+ * overwrite the prompt an inconveinient moments. So
+ * don't do that. */
if (job->stopped_progs == job->num_progs) {
printf(JOB_STATUS_FORMAT, job->jobid, "Stopped",
job->text);
}
+#endif
}
}
@@ -714,14 +731,78 @@
return 0;
}
+static char* itoa(register int i)
+{
+ static char a[7]; /* Max 7 ints */
+ register char *b = a + sizeof(a) - 1;
+ int sign = (i < 0);
+
+ if (sign)
+ i = -i;
+ *b = 0;
+ do
+ {
+ *--b = '0' + (i % 10);
+ i /= 10;
+ }
+ while (i);
+ if (sign)
+ *--b = '-';
+ return b;
+}
+
+char * strsep_space( char *string, int * ix)
+{
+ char *token, *begin;
+
+ begin = string;
+
+ /* Short circuit the trivial case */
+ if ( !string || ! string[*ix])
+ return NULL;
+
+ /* Find the end of the token. */
+ while( string && string[*ix] && !isspace(string[*ix]) ) {
+ (*ix)++;
+ }
+
+ /* Find the end of any whitespace trailing behind
+ * the token and let that be part of the token */
+ while( string && string[*ix] && isspace(string[*ix]) ) {
+ (*ix)++;
+ }
+
+ if (! string && *ix==0) {
+ /* Nothing useful was found */
+ return NULL;
+ }
+
+ token = xmalloc(*ix+1);
+ token[*ix] = '\0';
+ strncpy(token, string, *ix);
+
+ return token;
+}
static int expand_arguments(char *command)
{
- int ix = 0;
+ int total_length=0, length, i, retval, ix = 0;
+ expand_t expand_result;
+ char *tmpcmd, *cmd, *cmd_copy;
+ char *src, *dst, *var;
+ const char *out_of_space = "out of space during expansion";
+ int flags = GLOB_NOCHECK
+#ifdef GLOB_BRACE
+ | GLOB_BRACE
+#endif
+#ifdef GLOB_TILDE
+ | GLOB_TILDE
+#endif
+ ;
/* get rid of the terminating \n */
chomp(command);
-
+
/* Fix up escape sequences to be the Real Thing(tm) */
while( command && command[ix]) {
if (command[ix] == '\\') {
@@ -731,6 +812,139 @@
}
ix++;
}
+ /* Use glob and then fixup environment variables and such */
+
+ /* It turns out that glob is very stupid. We have to feed it one word at a
+ * time since it can't cope with a full string. Here we convert command
+ * (char*) into cmd (char**, one word per string) */
+
+ /* We need a clean copy, so strsep can mess up the copy while
+ * we write stuff into the original (in a minute) */
+ cmd = cmd_copy = strdup(command);
+ *command = '\0';
+ for (ix = 0, tmpcmd = cmd;
+ (tmpcmd = strsep_space(cmd, &ix)) != NULL; cmd += ix, ix=0) {
+ if (*tmpcmd == '\0')
+ break;
+ /* we need to trim() the result for glob! */
+ trim(tmpcmd);
+ retval = glob(tmpcmd, flags, NULL, &expand_result);
+ free(tmpcmd); /* Free mem allocated by strsep_space */
+ if (retval == GLOB_NOSPACE) {
+ /* Mem may have been allocated... */
+ globfree (&expand_result);
+ error_msg(out_of_space);
+ return FALSE;
+ } else if (retval != 0) {
+ /* Some other error. GLOB_NOMATCH shouldn't
+ * happen because of the GLOB_NOCHECK flag in
+ * the glob call. */
+ error_msg("syntax error");
+ return FALSE;
+ } else {
+ /* Convert from char** (one word per string) to a simple char*,
+ * but don't overflow command which is BUFSIZ in length */
+ for (i=0; i < expand_result.gl_pathc; i++) {
+ length=strlen(expand_result.gl_pathv[i]);
+ if (total_length+length+1 >= BUFSIZ) {
+ error_msg(out_of_space);
+ return FALSE;
+ }
+ strcat(command+total_length, " ");
+ total_length+=1;
+ strcat(command+total_length, expand_result.gl_pathv[i]);
+ total_length+=length;
+ }
+ globfree (&expand_result);
+ }
+ }
+ free(cmd_copy);
+ trim(command);
+
+ /* Now do the shell variable substitutions which
+ * wordexp can't do for us, namely $? and $! */
+ src = command;
+ while((dst = strchr(src,'$')) != NULL){
+ var = NULL;
+ switch(*(dst+1)) {
+ case '?':
+ var = itoa(last_return_code);
+ break;
+ case '!':
+ if (last_bg_pid==-1)
+ *(var)='\0';
+ else
+ var = itoa(last_bg_pid);
+ break;
+ /* Everything else like $$, $#, $[0-9], etc. should all be
+ * expanded by wordexp(), so we can in theory skip that stuff
+ * here, but just to be on the safe side (i.e., since uClibc
+ * wordexp doesn't do this stuff yet), lets leave it in for
+ * now. */
+ case '$':
+ var = itoa(getpid());
+ break;
+ case '#':
+ var = itoa(argc-1);
+ break;
+ case '0':case '1':case '2':case '3':case '4':
+ case '5':case '6':case '7':case '8':case '9':
+ {
+ int ixx=*(dst + 1)-48;
+ if (ixx >= argc) {
+ var='\0';
+ } else {
+ var = argv[ixx];
+ }
+ }
+ break;
+
+ }
+ if (var) {
+ /* a single character construction was found, and
+ * already handled in the case statement */
+ src=dst+2;
+ } else {
+ /* Looks like an environment variable */
+ char delim_hold;
+ int num_skip_chars=0;
+ int dstlen = strlen(dst);
+ /* Is this a ${foo} type variable? */
+ if (dstlen >=2 && *(dst+1) == '{') {
+ src=strchr(dst+1, '}');
+ num_skip_chars=1;
+ } else {
+ src=dst+1;
+ while(isalnum(*src) || *src=='_') src++;
+ }
+ if (src == NULL) {
+ src = dst+dstlen;
+ }
+ delim_hold=*src;
+ *src='\0'; /* temporary */
+ var = getenv(dst + 1 + num_skip_chars);
+ *src=delim_hold;
+ src += num_skip_chars;
+ }
+ if (var == NULL) {
+ /* Seems we got an un-expandable variable. So delete it. */
+ var = "";
+ }
+ {
+ int subst_len = strlen(var);
+ int trail_len = strlen(src);
+ if (dst+subst_len+trail_len >= command+BUFSIZ) {
+ error_msg(out_of_space);
+ return FALSE;
+ }
+ /* Move stuff to the end of the string to accommodate
+ * filling the created gap with the new stuff */
+ memmove(dst+subst_len, src, trail_len+1);
+ /* Now copy in the new stuff */
+ memcpy(dst, var, subst_len);
+ src = dst+subst_len;
+ }
+ }
return TRUE;
}
@@ -1078,6 +1292,7 @@
printf("[%d] %d\n", thejob->jobid,
newjob->progs[newjob->num_progs - 1].pid);
last_jobid = newjob->jobid;
+ last_bg_pid=newjob->progs[newjob->num_progs - 1].pid;
} else {
newjob->job_list->fg = thejob;
@@ -1250,6 +1465,8 @@
job_list.fg->running_progs--;
job_list.fg->progs[i].pid = 0;
+ last_return_code=WEXITSTATUS(status);
+
if (!job_list.fg->running_progs) {
/* child exited */
remove_job(&job_list, job_list.fg);
@@ -1322,10 +1539,9 @@
signal(SIGCHLD, SIG_IGN);
/* Put ourselves in our own process group. */
+ setsid();
shell_pgrp = getpid ();
- if (setpgid (shell_pgrp, shell_pgrp) < 0) {
- perror_msg_and_die("Couldn't put the shell in its own process group");
- }
+ setpgid (shell_pgrp, shell_pgrp);
/* Grab control of the terminal. */
tcsetpgrp(shell_terminal, shell_pgrp);
@@ -1345,13 +1561,12 @@
close_me_head = NULL;
job_list.head = NULL;
job_list.fg = NULL;
+ last_return_code=1;
if (argv[0] && argv[0][0] == '-') {
FILE *prof_input;
prof_input = fopen("/etc/profile", "r");
- if (!prof_input) {
- printf( "Couldn't open file '/etc/profile'\n");
- } else {
+ if (prof_input) {
int tmp_fd = fileno(prof_input);
mark_open(tmp_fd);
/* Now run the file */
@@ -1418,4 +1633,3 @@
return (busy_loop(input));
}
-