Hi! ----
Attached is a sample source code for a |wordexp()| implementation which works with both current Solaris /usr/bin/ksh and ksh93. I did some tests with it but due lack of Sun's test suite I cannot say whether this is 100% correct. The following sources files are attached (use "munpack" to extract them): - "mywordexp3_main.c" contains the test application's |main()| function - "mywordexp3_own.c" contains my own hacked version of |wordexp()| (called |mywordexp()|/|mywordfree()|, based on http://cvs.opensolaris.org/source/xref/on/usr/src/lib/libc/port/regex/wordexp.c) - "mywordexp3_sys.c" calls the system's native |wordexp()| implementation for comparisation To compile the code simply do a % cc -Xt -g -xs mywordexp3_main.c mywordexp3_own.c mywordexp3_sys.c -o mywordexp3 # Usage is simply % mywordexp3 'expression' # do not forget the '' around the expression to make sure the calling shell can't mess around with the string. Comments/suggestions/ideas welcome... ---- Bye, Roland -- __ . . __ (o.\ \/ /.o) roland.mainz at nrubsig.org \__\/\/__/ MPEG specialist, C&&JAVA&&Sun&&Unix programmer /O /==\ O\ TEL +49 641 7950090 (;O/ \/ \O;) -------------- next part -------------- #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> int main(int ac, char **av) { test_own(av[1]); test_sys(av[1]); return EXIT_SUCCESS; } -------------- next part -------------- #include <stdio.h> #include <unistd.h> #include <limits.h> #include <fcntl.h> #include <limits.h> #include <stdlib.h> #include <string.h> #include <sys/wait.h> #include <unistd.h> #include <stdio.h> #include <errno.h> #define INITIAL 8 /* initial pathv allocation */ #define BUFSZ 256 /* allocation unit of the line buffer */ typedef struct wordexp_t { size_t we_wordc; /* Count of paths matched by pattern */ char **we_wordv; /* List of matched pathnames */ size_t we_offs; /* # of slots reserved in we_pathv */ /* following are internal to the implementation */ char **we_wordp; /* we_pathv + we_offs */ int we_wordn; /* # of elements allocated */ } wordexp_t; /* * wordexp flags. */ #define WRDE_APPEND 0x0001 /* append to existing wordexp_t */ #define WRDE_DOOFFS 0x0002 /* use we_offs */ #define WRDE_NOCMD 0x0004 /* don't allow $() */ #define WRDE_REUSE 0x0008 #define WRDE_SHOWERR 0x0010 /* don't 2>/dev/null */ #define WRDE_UNDEF 0x0020 /* set -u */ /* * wordexp errors. */ #define WRDE_ERRNO (2) /* error in "errno" */ #define WRDE_BADCHAR (3) /* shell syntax character */ #define WRDE_BADVAL (4) /* undefined variable expanded */ #define WRDE_CMDSUB (5) /* prohibited $() */ #define WRDE_NOSPACE (6) /* no memory */ #define WRDE_SYNTAX (7) /* bad syntax */ #define WRDE_NOSYS (8) /* function not supported (XPG4) */ static int append(wordexp_t *, char *); /* * Do word expansion. * We just pass our arguments to shell with -E option. Note that the * underlying shell must recognize the -E option, and do the right thing * with it. */ int mywordexp(const char *word, wordexp_t *wp, int flags) { char *args[10]; const char *path; wordexp_t wptmp; size_t si; int i; pid_t pid; char *line, *eob, *cp; /* word from shell */ int rv = WRDE_ERRNO; int status; int pv[2]; /* pipe from shell stdout */ FILE *fp; /* pipe read stream */ int serrno, tmpalloc; const char *ksh_path = "/bin/bash"; /* * Do absolute minimum neccessary for the REUSE flag. Eventually * want to be able to actually avoid excessive malloc calls. */ if (flags & WRDE_REUSE) wordfree(wp); /* * Initialize wordexp_t * * XPG requires that the struct pointed to by wp not be modified * unless wordexp() either succeeds, or fails on WRDE_NOSPACE. * So we work with wptmp, and only copy wptmp to wp if one of the * previously mentioned conditions is satisfied. */ wptmp = *wp; /* * Man page says: * 2. All of the calls must set WRDE_DOOFFS, or all must not * set it. * Therefore, if it's not set, we_offs will always be reset. */ if ((flags & WRDE_DOOFFS) == 0) wptmp.we_offs = 0; /* * If we get APPEND|REUSE, how should we do? * allocating buffer anyway to avoid segfault. */ tmpalloc = 0; if ((flags & WRDE_APPEND) == 0 || (flags & WRDE_REUSE)) { wptmp.we_wordc = 0; wptmp.we_wordn = wptmp.we_offs + INITIAL; wptmp.we_wordv = (char **)malloc( sizeof (char *) * wptmp.we_wordn); if (wptmp.we_wordv == NULL) return (WRDE_NOSPACE); wptmp.we_wordp = wptmp.we_wordv + wptmp.we_offs; for (si = 0; si < wptmp.we_offs; si++) wptmp.we_wordv[si] = NULL; tmpalloc = 1; } /* * Set up pipe from shell stdout to "fp" for us */ if (pipe(pv) < 0) goto cleanup; /* * Fork/exec shell */ if ((pid = fork()) == -1) { serrno = errno; (void) close(pv[0]); (void) close(pv[1]); errno = serrno; goto cleanup; } if (pid == 0) { /* child */ char buff[80+strlen(word)]; /* Needs C99 */ int i; (void) dup2(pv[1], 1); (void) close(pv[0]); (void) close(pv[1]); path = ksh_path; i=0; buff[0]='\0'; if (flags & WRDE_UNDEF) strcat(buff, "set -o nounset ; "); if (flags & WRDE_NOCMD) strcat(buff, "set -o noexec ; "); if ((flags & WRDE_SHOWERR) == 0) { /* The newline ('\n') is neccesary to make sure that * the redirection to /dev/null is already active in * the case the printf below contains a syntax * error... */ strcat(buff, "exec 2>/dev/null\n"); } sprintf(&buff[strlen(buff)], "printf \"%%s\\000\" %s", word); args[i++] = strrchr(path, '/') + 1; args[i++] = "-c"; args[i++] = buff; args[i++] = NULL; // fprintf(stderr, "buff='%s'\n", buff); (void) execv(path, args); _exit(127); } (void) close(pv[1]); if ((fp = fdopen(pv[0], "r")) == NULL) { serrno = errno; (void) close(pv[0]); errno = serrno; goto wait_cleanup; } /* * Read words from shell, separated with '\0'. * Since there is no way to disable IFS splitting, * it would be possible to separate the output with '\n'. */ cp = line = malloc(BUFSZ); if (line == NULL) { (void) fclose(fp); rv = WRDE_NOSPACE; goto wait_cleanup; } eob = line + BUFSZ; rv = 0; while ((i = getc(fp)) != EOF) { *cp++ = (char)i; if (i == '\0') { cp = line; if ((rv = append(&wptmp, cp)) != 0) { break; } } if (cp == eob) { size_t bs = (eob - line); char *nl; if ((nl = realloc(line, bs + BUFSZ)) == NULL) { rv = WRDE_NOSPACE; break; } line = nl; cp = line + bs; eob = cp + BUFSZ; } } wptmp.we_wordp[wptmp.we_wordc] = NULL; free(line); (void) fclose(fp); /* kill shell if still writing */ wait_cleanup: if (waitpid(pid, &status, 0) == -1) rv = WRDE_ERRNO; else if (rv == 0) rv = WEXITSTATUS(status); /* shell WRDE_* status */ cleanup: if (rv == 0) *wp = wptmp; else { if (tmpalloc) wordfree(&wptmp); } /* * Map ksh errors to wordexp() errors */ if (rv == 4) rv = WRDE_CMDSUB; else if (rv == 5) rv = WRDE_BADVAL; else if (rv == 6) rv = WRDE_SYNTAX; return (rv); } /* * Append a word to the wordexp_t structure, growing it as neccessary. */ static int append(wordexp_t *wp, char *str) { char *cp; char **nwp; /* * We will be adding one entry and later adding * one more NULL. So we need 2 more free slots. */ if ((wp->we_wordp + wp->we_wordc) == (wp->we_wordv + wp->we_wordn - 1)) { nwp = realloc(wp->we_wordv, (wp->we_wordn + INITIAL) * sizeof (char *)); if (nwp == NULL) return (WRDE_NOSPACE); wp->we_wordn += INITIAL; wp->we_wordv = nwp; wp->we_wordp = wp->we_wordv + wp->we_offs; } if ((cp = strdup(str)) == NULL) return (WRDE_NOSPACE); wp->we_wordp[wp->we_wordc++] = cp; return (0); } /* * Free all space owned by wordexp_t. */ void mywordfree(wordexp_t *wp) { size_t i; if (wp->we_wordv == NULL) return; for (i = wp->we_offs; i < wp->we_offs + wp->we_wordc; i++) free(wp->we_wordv[i]); free((void *)wp->we_wordv); wp->we_wordc = 0; wp->we_wordv = NULL; } void test_own(const char *testexpr) { wordexp_t p; char **w; int i; int err; if ((err=mywordexp(testexpr, &p, 0)) != 0) { fprintf(stdout, "## mywordexp() failed, err=%d, errno=%d.\n", err, errno); return; } printf("ownres=|"); w = p.we_wordv; for (i=0; i<p.we_wordc; i++) printf("|%s|", w[i]); wordfree(&p); printf("|\n"); } -------------- next part -------------- #include <stdio.h> #include <unistd.h> #include <stdio.h> #include <errno.h> #include <wordexp.h> void test_sys(const char *testexpr) { wordexp_t p; char **w; int i; int err; if ((err=wordexp(testexpr, &p, 0)) != 0) { fprintf(stdout, "## wordexp() failed, err=%d.\n", err); return; } printf("sysres=|"); w = p.we_wordv; for (i=0; i<p.we_wordc; i++) printf("|%s|", w[i]); wordfree(&p); printf("|\n"); }
