> And here's that diff. [..] It never hurts to re-read a diff before going to bed. Indeed, I forgot to free argv (execlp uses alloca). The number of things to clean up after one thing fails is almost getting out of hand, and at least the fork() case wasn't handled properly. In this new revision of d_exec, argv and all the fds are checked and freed (only) at the end of the function, if necessary. That's where we jump to, should anything fail.
The rest of the diff is as before. Index: dired.c =================================================================== RCS file: /cvs/src/usr.bin/mg/dired.c,v retrieving revision 1.49 diff -u -p -r1.49 dired.c --- dired.c 29 Aug 2011 11:02:06 -0000 1.49 +++ dired.c 29 Aug 2011 23:27:09 -0000 @@ -21,6 +21,7 @@ #include <fcntl.h> #include <errno.h> #include <libgen.h> +#include <stdarg.h> void dired_init(void); static int dired(int, int); @@ -33,6 +34,7 @@ static int d_expunge(int, int); static int d_copy(int, int); static int d_del(int, int); static int d_rename(int, int); +static int d_exec(int, struct buffer *, const char *, const char *, ...); static int d_shell_command(int, int); static int d_create_directory(int, int); static int d_makename(struct line *, char *, size_t); @@ -483,14 +485,10 @@ reaper(int signo __attribute__((unused)) int d_shell_command(int f, int n) { - char command[512], fname[MAXPATHLEN], buf[BUFSIZ], *bufp, *cp; - int infd, fds[2]; - pid_t pid; - struct sigaction olda, newa; + char command[512], fname[MAXPATHLEN], *bufp; struct buffer *bp; struct mgwin *wp; - FILE *fin; - char sname[NFILEN]; + char sname[NFILEN]; bp = bfind("*Shell Command Output*", TRUE); if (bclear(bp) != TRUE) @@ -506,68 +504,124 @@ d_shell_command(int f, int n) bufp = eread("! on %s: ", command, sizeof(command), EFNEW, sname); if (bufp == NULL) return (ABORT); - infd = open(fname, O_RDONLY); - if (infd == -1) { + + if (d_exec(0, bp, fname, "sh", "-c", command, NULL) != TRUE) + return (ABORT); + + if ((wp = popbuf(bp, WNONE)) == NULL) + return (ABORT); /* XXX - free the buffer?? */ + curwp = wp; + curbp = wp->w_bufp; + return (TRUE); +} + +/* + * Pipe input file to cmd and insert the command's output in the + * given buffer. Each line will be prefixed with the given + * number of spaces. + */ +static int +d_exec(int space, struct buffer *bp, const char *input, const char *cmd, ...) +{ + char buf[BUFSIZ]; + va_list ap; + struct sigaction olda, newa; + char **argv = NULL, *cp; + FILE *fin; + int fds[2] = { -1, -1 }; + int infd = -1; + int ret = (ABORT), n; + pid_t pid; + + if (sigaction(SIGCHLD, NULL, &olda) == -1) + return (ABORT); + + /* Find the number of arguments. */ + va_start(ap, cmd); + for (n = 2; va_arg(ap, char *) != NULL; n++) + ; + va_end(ap); + + /* Allocate and build the argv. */ + if ((argv = calloc(n, sizeof(*argv))) == NULL) { + ewprintf("Can't allocate argv : %s", strerror(errno)); + goto out; + } + + n = 1; + argv[0] = (char *)cmd; + va_start(ap, cmd); + while ((argv[n] = va_arg(ap, char *)) != NULL) + n++; + va_end(ap); + + if (input == NULL) + input = "/dev/null"; + + if ((infd = open(input, O_RDONLY)) == -1) { ewprintf("Can't open input file : %s", strerror(errno)); - return (FALSE); + goto out; } + if (pipe(fds) == -1) { ewprintf("Can't create pipe : %s", strerror(errno)); - close(infd); - return (FALSE); + goto out; } newa.sa_handler = reaper; newa.sa_flags = 0; - if (sigaction(SIGCHLD, &newa, &olda) == -1) { - close(infd); - close(fds[0]); - close(fds[1]); - return (ABORT); + if (sigaction(SIGCHLD, &newa, NULL) == -1) + goto out; + + if ((pid = fork()) == -1) { + ewprintf("Can't fork"); + goto out; } - pid = fork(); + switch (pid) { - case -1: - ewprintf("Can't fork"); - return (ABORT); - case 0: + case 0: /* Child */ close(fds[0]); dup2(infd, STDIN_FILENO); dup2(fds[1], STDOUT_FILENO); dup2(fds[1], STDERR_FILENO); - execl("/bin/sh", "sh", "-c", bufp, (char *)NULL); + execvp(argv[0], argv); exit(1); break; - default: + default: /* Parent */ close(infd); close(fds[1]); - fin = fdopen(fds[0], "r"); - if (fin == NULL) /* "r" is surely a valid mode! */ - panic("can't happen"); + infd = fds[1] = -1; + if ((fin = fdopen(fds[0], "r")) == NULL) + goto out; while (fgets(buf, sizeof(buf), fin) != NULL) { cp = strrchr(buf, '\n'); if (cp == NULL && !feof(fin)) { /* too long a line */ int c; - addlinef(bp, "%s...", buf); + addlinef(bp, "%*s%s...", space, "", buf); while ((c = getc(fin)) != EOF && c != '\n') ; continue; } else if (cp) *cp = '\0'; - addline(bp, buf); + addlinef(bp, "%*s%s", space, "", buf); } fclose(fin); - close(fds[0]); break; } - wp = popbuf(bp, WNONE); - if (wp == NULL) - return (ABORT); /* XXX - free the buffer?? */ - curwp = wp; - curbp = wp->w_bufp; + ret = (TRUE); + +out: if (sigaction(SIGCHLD, &olda, NULL) == -1) ewprintf("Warning, couldn't reset previous signal handler"); - return (TRUE); + if (fds[0] != -1) + close(fds[0]); + if (fds[1] != -1) + close(fds[1]); + if (infd != -1) + close(infd); + if (argv != NULL) + free(argv); + return ret; } /* ARGSUSED */ @@ -595,35 +649,26 @@ d_create_directory(int f, int n) return (showbuffer(bp, curwp, WFFULL | WFMODE)); } -#define NAME_FIELD 8 - static int d_makename(struct line *lp, char *fn, size_t len) { - int i; - char *p, *ep; + int start, nlen; + char *namep; - if (strlcpy(fn, curbp->b_fname, len) >= len) - return (FALSE); - if ((p = lp->l_text) == NULL) + if (d_warpdot(lp, &start) == FALSE) return (ABORT); - ep = lp->l_text + llength(lp); - p++; /* skip action letter, if any */ - for (i = 0; i < NAME_FIELD; i++) { - while (p < ep && isspace(*p)) - p++; - while (p < ep && !isspace(*p)) - p++; - while (p < ep && isspace(*p)) - p++; - if (p == ep) - return (ABORT); - } - if (strlcat(fn, p, len) >= len) - return (FALSE); + namep = &lp->l_text[start]; + nlen = llength(lp) - start; + + if (snprintf(fn, len, "%s%.*s", curbp->b_fname, nlen, namep) >= len) + return (ABORT); /* Name is too long. */ + + /* Return TRUE if the entry is a directory. */ return ((lgetc(lp, 2) == 'd') ? TRUE : FALSE); } +#define NAME_FIELD 9 + static int d_warpdot(struct line *dotp, int *doto) { @@ -637,15 +682,18 @@ d_warpdot(struct line *dotp, int *doto) len = llength(dotp); while (off < len) { if (tp[off++] == ' ') { - if (++field == 9) - break; + if (++field == NAME_FIELD) { + *doto = off; + return (TRUE); + } /* Skip the space. */ while (off < len && tp[off] == ' ') off++; } } - *doto = off; - return (TRUE); + /* We didn't find the field. */ + *doto = 0; + return (FALSE); } static int @@ -683,9 +731,7 @@ struct buffer * dired_(char *dname) { struct buffer *bp; - FILE *dirpipe; - char line[256]; - int len, ret, i; + int len, i; if ((fopen(dname,"r")) == NULL) { if (errno == EACCES) @@ -709,32 +755,15 @@ dired_(char *dname) if (bclear(bp) != TRUE) return (NULL); bp->b_flag |= BFREADONLY; - ret = snprintf(line, sizeof(line), "ls -al %s", dname); - if (ret < 0 || ret >= sizeof(line)) { - ewprintf("Path too long"); - return (NULL); - } - if ((dirpipe = popen(line, "r")) == NULL) { - ewprintf("Problem opening pipe to ls"); - return (NULL); - } - line[0] = line[1] = ' '; - while (fgets(&line[2], sizeof(line) - 2, dirpipe) != NULL) { - line[strcspn(line, "\n")] = '\0'; /* remove ^J */ - (void) addline(bp, line); - } - if (pclose(dirpipe) == -1) { - ewprintf("Problem closing pipe to ls : %s", - strerror(errno)); + + if ((d_exec(2, bp, NULL, "ls", "-al", dname, NULL)) != TRUE) return (NULL); - } /* Find the line with ".." on it. */ bp->b_dotp = bfirstlp(bp); for (i = 0; i < bp->b_lines; i++) { bp->b_dotp = lforw(bp->b_dotp); - d_warpdot(bp->b_dotp, &bp->b_doto); - if (bp->b_doto >= llength(bp->b_dotp)) + if (d_warpdot(bp->b_dotp, &bp->b_doto) == FALSE) continue; if (strcmp(ltext(bp->b_dotp) + bp->b_doto, "..") == 0) break;