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");
}

Reply via email to