URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
Compare Revisions
- This comparison shows the changes necessary to convert path
/or1k/trunk/linux/uClibc/libc/misc/wordexp
- from Rev 1325 to Rev 1765
- ↔ Reverse comparison
Rev 1325 → Rev 1765
/wordexp.c
0,0 → 1,2244
/* vi: set sw=4 ts=4: */ |
/* POSIX.2 wordexp implementation. |
Copyright (C) 1997, 1998, 1999 Free Software Foundation, Inc. |
This file is part of the GNU C Library. |
Contributed by Tim Waugh <tim@cyberelk.demon.co.uk>. |
|
The GNU C Library is free software; you can redistribute it and/or |
modify it under the terms of the GNU Library General Public License as |
published by the Free Software Foundation; either version 2 of the |
License, or (at your option) any later version. |
|
The GNU C Library is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
Library General Public License for more details. |
|
You should have received a copy of the GNU Library General Public |
License along with the GNU C Library; see the file COPYING.LIB. If not, |
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
Boston, MA 02111-1307, USA. */ |
|
#define _GNU_SOURCE |
#include <sys/cdefs.h> |
#include <sys/types.h> |
#include <sys/wait.h> |
#include <fcntl.h> |
#include <paths.h> |
#include <stdio.h> |
#include <stdlib.h> |
#include <string.h> |
#include <unistd.h> |
#include <pwd.h> |
#include <errno.h> |
#include <assert.h> |
#include <fnmatch.h> |
#include <glob.h> |
#include <wordexp.h> |
|
#define __WORDEXP_FULL |
//#undef __WORDEXP_FULL |
|
/* |
* This is a recursive-descent-style word expansion routine. |
*/ |
|
/* These variables are defined and initialized in the startup code. */ |
//extern int __libc_argc; |
//extern char **__libc_argv; |
|
/* FIXME!!!! */ |
int __libc_argc; |
char **__libc_argv; |
|
/* Some forward declarations */ |
static int parse_dollars(char **word, size_t * word_length, |
size_t * max_length, const char *words, |
size_t * offset, int flags, wordexp_t * pwordexp, |
const char *ifs, const char *ifs_white, |
int quoted); |
static int parse_backtick(char **word, size_t * word_length, |
size_t * max_length, const char *words, |
size_t * offset, int flags, wordexp_t * pwordexp, |
const char *ifs, const char *ifs_white); |
static int parse_dquote(char **word, size_t * word_length, |
size_t * max_length, const char *words, |
size_t * offset, int flags, wordexp_t * pwordexp, |
const char *ifs, const char *ifs_white); |
|
|
|
/* The w_*() functions manipulate word lists. */ |
#define W_CHUNK (100) |
|
/* Result of w_newword will be ignored if it's the last word. */ |
static inline char *w_newword(size_t * actlen, size_t * maxlen) |
{ |
*actlen = *maxlen = 0; |
return NULL; |
} |
|
/* Add a character to the buffer, allocating room for it if needed. */ |
static inline char *w_addchar(char *buffer, size_t * actlen, |
size_t * maxlen, char ch) |
/* (lengths exclude trailing zero) */ |
{ |
|
if (*actlen == *maxlen) { |
char *old_buffer = buffer; |
assert(buffer == NULL || *maxlen != 0); |
*maxlen += W_CHUNK; |
buffer = realloc(buffer, 1 + *maxlen); |
if (buffer == NULL) |
free(old_buffer); |
} |
|
if (buffer != NULL) { |
buffer[*actlen] = ch; |
buffer[++(*actlen)] = '\0'; |
} |
|
return buffer; |
} |
|
#define MAX( a, b ) ( ( ( a ) > ( b ) ) ? ( a ) : ( b ) ) |
static char *w_addmem(char *buffer, size_t * actlen, size_t * maxlen, |
const char *str, size_t len) |
{ |
/* Add a string to the buffer, allocating room for it if needed. |
*/ |
if (*actlen + len > *maxlen) { |
char *old_buffer = buffer; |
assert(buffer == NULL || *maxlen != 0); |
*maxlen += MAX(2 * len, W_CHUNK); |
buffer = realloc(old_buffer, 1 + *maxlen); |
if (buffer == NULL) |
free(old_buffer); |
} |
|
if (buffer != NULL) { |
*((char *) mempcpy(&buffer[*actlen], str, len)) = '\0'; |
*actlen += len; |
} |
return buffer; |
} |
|
/* Add a string to the buffer, allocating room for it if needed. */ |
static char *w_addstr(char *buffer, size_t * actlen, size_t * maxlen, |
const char *str) |
/* (lengths exclude trailing zero) */ |
{ |
size_t len; |
assert(str != NULL); /* w_addstr only called from this file */ |
len = strlen(str); |
|
return w_addmem(buffer, actlen, maxlen, str, len); |
} |
|
/* Add a word to the wordlist */ |
static int w_addword(wordexp_t * pwordexp, char *word) |
{ |
size_t num_p; |
char **new_wordv; |
|
/* Internally, NULL acts like "". Convert NULLs to "" before |
* the caller sees them. |
*/ |
if (word == NULL) { |
word = strdup(""); |
if (word == NULL) |
goto no_space; |
} |
|
num_p = 2 + pwordexp->we_wordc + pwordexp->we_offs; |
new_wordv = realloc(pwordexp->we_wordv, sizeof(char *) * num_p); |
if (new_wordv != NULL) { |
pwordexp->we_wordv = new_wordv; |
pwordexp->we_wordv[pwordexp->we_offs + pwordexp->we_wordc++] = word; |
pwordexp->we_wordv[pwordexp->we_offs + pwordexp->we_wordc] = NULL; |
return 0; |
} |
|
no_space: |
return WRDE_NOSPACE; |
} |
|
/* The parse_*() functions should leave *offset being the offset in 'words' |
* to the last character processed. |
*/ |
static int |
parse_backslash(char **word, size_t * word_length, size_t * max_length, |
const char *words, size_t * offset) |
{ |
/* We are poised _at_ a backslash, not in quotes */ |
|
switch (words[1 + *offset]) { |
case 0: |
/* Backslash is last character of input words */ |
return WRDE_SYNTAX; |
|
case '\n': |
++(*offset); |
break; |
|
default: |
*word = w_addchar(*word, word_length, max_length, words[1 + *offset]); |
if (*word == NULL) |
return WRDE_NOSPACE; |
|
++(*offset); |
break; |
} |
|
return 0; |
} |
|
static int |
parse_qtd_backslash(char **word, size_t * word_length, size_t * max_length, |
const char *words, size_t * offset) |
{ |
/* We are poised _at_ a backslash, inside quotes */ |
|
switch (words[1 + *offset]) { |
case 0: |
/* Backslash is last character of input words */ |
return WRDE_SYNTAX; |
|
case '\n': |
++(*offset); |
break; |
|
case '$': |
case '`': |
case '"': |
case '\\': |
*word = |
w_addchar(*word, word_length, max_length, words[1 + *offset]); |
if (*word == NULL) |
return WRDE_NOSPACE; |
|
++(*offset); |
break; |
|
default: |
*word = w_addchar(*word, word_length, max_length, words[*offset]); |
if (*word != NULL) |
*word = |
w_addchar(*word, word_length, max_length, |
words[1 + *offset]); |
|
if (*word == NULL) |
return WRDE_NOSPACE; |
|
++(*offset); |
break; |
} |
|
return 0; |
} |
|
static int |
parse_tilde(char **word, size_t * word_length, size_t * max_length, |
const char *words, size_t * offset, size_t wordc) |
{ |
/* We are poised _at_ a tilde */ |
size_t i; |
|
if (*word_length != 0) { |
if (!((*word)[*word_length - 1] == '=' && wordc == 0)) { |
if (!((*word)[*word_length - 1] == ':' |
&& strchr(*word, '=') && wordc == 0)) { |
*word = w_addchar(*word, word_length, max_length, '~'); |
return *word ? 0 : WRDE_NOSPACE; |
} |
} |
} |
|
for (i = 1 + *offset; words[i]; i++) { |
if (words[i] == ':' || words[i] == '/' || words[i] == ' ' || |
words[i] == '\t' || words[i] == 0) |
break; |
|
if (words[i] == '\\') { |
*word = w_addchar(*word, word_length, max_length, '~'); |
return *word ? 0 : WRDE_NOSPACE; |
} |
} |
|
if (i == 1 + *offset) { |
/* Tilde appears on its own */ |
uid_t uid; |
struct passwd pwd, *tpwd; |
int buflen = 1000; |
char *home; |
char *buffer; |
int result; |
|
/* POSIX.2 says ~ expands to $HOME and if HOME is unset the |
results are unspecified. We do a lookup on the uid if |
HOME is unset. */ |
|
home = getenv("HOME"); |
if (home != NULL) { |
*word = w_addstr(*word, word_length, max_length, home); |
if (*word == NULL) |
return WRDE_NOSPACE; |
} else { |
uid = getuid(); |
buffer = alloca(buflen); |
|
while ((result = getpwuid_r(uid, &pwd, buffer, buflen, &tpwd)) |
!= 0 && errno == ERANGE) |
{ |
buflen += 1000; |
buffer = alloca(buflen); |
} |
|
if (result == 0 && tpwd != NULL && pwd.pw_dir != NULL) { |
*word = w_addstr(*word, word_length, max_length, pwd.pw_dir); |
if (*word == NULL) |
return WRDE_NOSPACE; |
} else { |
*word = w_addchar(*word, word_length, max_length, '~'); |
if (*word == NULL) |
return WRDE_NOSPACE; |
} |
} |
} else { |
/* Look up user name in database to get home directory */ |
char *user = strndup(&words[1 + *offset], i - (1 + *offset)); |
struct passwd pwd, *tpwd; |
int buflen = 1000; |
char *buffer = alloca(buflen); |
int result; |
|
while ((result = getpwnam_r(user, &pwd, buffer, buflen, &tpwd)) != 0 |
&& errno == ERANGE) { |
buflen += 1000; |
buffer = alloca(buflen); |
} |
|
if (result == 0 && tpwd != NULL && pwd.pw_dir) |
*word = w_addstr(*word, word_length, max_length, pwd.pw_dir); |
else { |
/* (invalid login name) */ |
*word = w_addchar(*word, word_length, max_length, '~'); |
if (*word != NULL) |
*word = w_addstr(*word, word_length, max_length, user); |
} |
|
*offset = i - 1; |
} |
return *word ? 0 : WRDE_NOSPACE; |
} |
|
|
static int |
do_parse_glob(const char *glob_word, char **word, size_t * word_length, |
size_t * max_length, wordexp_t * pwordexp, const char *ifs, |
const char *ifs_white) |
{ |
int error; |
int match; |
glob_t globbuf; |
|
error = glob(glob_word, GLOB_NOCHECK, NULL, &globbuf); |
|
if (error != 0) { |
/* We can only run into memory problems. */ |
assert(error == GLOB_NOSPACE); |
return WRDE_NOSPACE; |
} |
|
if (ifs && !*ifs) { |
/* No field splitting allowed. */ |
assert(globbuf.gl_pathv[0] != NULL); |
*word = w_addstr(*word, word_length, max_length, globbuf.gl_pathv[0]); |
for (match = 1; match < globbuf.gl_pathc && *word != NULL; ++match) { |
*word = w_addchar(*word, word_length, max_length, ' '); |
if (*word != NULL) |
*word = w_addstr(*word, word_length, max_length, |
globbuf.gl_pathv[match]); |
} |
|
globfree(&globbuf); |
return *word ? 0 : WRDE_NOSPACE; |
} |
|
assert(ifs == NULL || *ifs != '\0'); |
if (*word != NULL) { |
free(*word); |
*word = w_newword(word_length, max_length); |
} |
|
for (match = 0; match < globbuf.gl_pathc; ++match) { |
char *matching_word = strdup(globbuf.gl_pathv[match]); |
|
if (matching_word == NULL || w_addword(pwordexp, matching_word)) { |
globfree(&globbuf); |
return WRDE_NOSPACE; |
} |
} |
|
globfree(&globbuf); |
return 0; |
} |
|
static int |
parse_glob(char **word, size_t * word_length, size_t * max_length, |
const char *words, size_t * offset, int flags, |
wordexp_t * pwordexp, const char *ifs, const char *ifs_white) |
{ |
/* We are poised just after a '*', a '[' or a '?'. */ |
int error = WRDE_NOSPACE; |
int quoted = 0; /* 1 if singly-quoted, 2 if doubly */ |
int i; |
wordexp_t glob_list; /* List of words to glob */ |
|
glob_list.we_wordc = 0; |
glob_list.we_wordv = NULL; |
glob_list.we_offs = 0; |
for (; words[*offset] != '\0'; ++*offset) { |
if ((ifs && strchr(ifs, words[*offset])) || |
(!ifs && strchr(" \t\n", words[*offset]))) |
/* Reached IFS */ |
break; |
|
/* Sort out quoting */ |
if (words[*offset] == '\'') { |
if (quoted == 0) { |
quoted = 1; |
continue; |
} else if (quoted == 1) { |
quoted = 0; |
continue; |
} |
} else if (words[*offset] == '"') { |
if (quoted == 0) { |
quoted = 2; |
continue; |
} else if (quoted == 2) { |
quoted = 0; |
continue; |
} |
} |
|
/* Sort out other special characters */ |
if (quoted != 1 && words[*offset] == '$') { |
error = parse_dollars(word, word_length, max_length, words, |
offset, flags, &glob_list, ifs, |
ifs_white, quoted == 2); |
if (error) |
goto tidy_up; |
|
continue; |
} else if (words[*offset] == '\\') { |
if (quoted) |
error = parse_qtd_backslash(word, word_length, max_length, |
words, offset); |
else |
error = parse_backslash(word, word_length, max_length, |
words, offset); |
|
if (error) |
goto tidy_up; |
|
continue; |
} |
|
*word = w_addchar(*word, word_length, max_length, words[*offset]); |
if (*word == NULL) |
goto tidy_up; |
} |
|
/* Don't forget to re-parse the character we stopped at. */ |
--*offset; |
|
/* Glob the words */ |
error = w_addword(&glob_list, *word); |
*word = w_newword(word_length, max_length); |
for (i = 0; error == 0 && i < glob_list.we_wordc; i++) |
error = do_parse_glob(glob_list.we_wordv[i], word, word_length, |
max_length, pwordexp, ifs, ifs_white); |
|
/* Now tidy up */ |
tidy_up: |
wordfree(&glob_list); |
return error; |
} |
|
static int |
parse_squote(char **word, size_t * word_length, size_t * max_length, |
const char *words, size_t * offset) |
{ |
/* We are poised just after a single quote */ |
for (; words[*offset]; ++(*offset)) { |
if (words[*offset] != '\'') { |
*word = w_addchar(*word, word_length, max_length, words[*offset]); |
if (*word == NULL) |
return WRDE_NOSPACE; |
} else |
return 0; |
} |
|
/* Unterminated string */ |
return WRDE_SYNTAX; |
} |
|
#ifdef __WORDEXP_FULL |
static int eval_expr(char *expr, long int *result); |
|
static char *_itoa(unsigned long long int value, char *buflim) |
{ |
sprintf(buflim, "%llu", value); |
return buflim; |
} |
|
/* Functions to evaluate an arithmetic expression */ |
static int eval_expr_val(char **expr, long int *result) |
{ |
int sgn = +1; |
char *digit; |
|
/* Skip white space */ |
for (digit = *expr; digit && *digit && isspace(*digit); ++digit); |
|
switch (*digit) { |
case '(': |
|
/* Scan for closing paren */ |
for (++digit; **expr && **expr != ')'; ++(*expr)); |
|
/* Is there one? */ |
if (!**expr) |
return WRDE_SYNTAX; |
|
*(*expr)++ = 0; |
|
if (eval_expr(digit, result)) |
return WRDE_SYNTAX; |
|
return 0; |
|
case '+': /* Positive value */ |
++digit; |
break; |
|
case '-': /* Negative value */ |
++digit; |
sgn = -1; |
break; |
|
default: |
if (!isdigit(*digit)) |
return WRDE_SYNTAX; |
} |
|
*result = 0; |
for (; *digit && isdigit(*digit); ++digit) |
*result = (*result * 10) + (*digit - '0'); |
|
*expr = digit; |
*result *= sgn; |
return 0; |
} |
|
static int eval_expr_multdiv(char **expr, long int *result) |
{ |
long int arg; |
|
/* Read a Value */ |
if (eval_expr_val(expr, result) != 0) |
return WRDE_SYNTAX; |
|
while (**expr) { |
/* Skip white space */ |
for (; *expr && **expr && isspace(**expr); ++(*expr)); |
|
if (**expr == '*') { |
++(*expr); |
if (eval_expr_val(expr, &arg) != 0) |
return WRDE_SYNTAX; |
|
*result *= arg; |
} else if (**expr == '/') { |
++(*expr); |
if (eval_expr_val(expr, &arg) != 0) |
return WRDE_SYNTAX; |
|
*result /= arg; |
} else |
break; |
} |
|
return 0; |
} |
|
static int eval_expr(char *expr, long int *result) |
{ |
long int arg; |
|
/* Read a Multdiv */ |
if (eval_expr_multdiv(&expr, result) != 0) |
return WRDE_SYNTAX; |
|
while (*expr) { |
/* Skip white space */ |
for (; expr && *expr && isspace(*expr); ++expr); |
|
if (*expr == '+') { |
++expr; |
if (eval_expr_multdiv(&expr, &arg) != 0) |
return WRDE_SYNTAX; |
|
*result += arg; |
} else if (*expr == '-') { |
++expr; |
if (eval_expr_multdiv(&expr, &arg) != 0) |
return WRDE_SYNTAX; |
|
*result -= arg; |
} else |
break; |
} |
|
return 0; |
} |
|
static int |
parse_arith(char **word, size_t * word_length, size_t * max_length, |
const char *words, size_t * offset, int flags, int bracket) |
{ |
/* We are poised just after "$((" or "$[" */ |
int error; |
int paren_depth = 1; |
size_t expr_length; |
size_t expr_maxlen; |
char *expr; |
|
expr = w_newword(&expr_length, &expr_maxlen); |
for (; words[*offset]; ++(*offset)) { |
switch (words[*offset]) { |
case '$': |
error = parse_dollars(&expr, &expr_length, &expr_maxlen, |
words, offset, flags, NULL, NULL, NULL, |
1); |
/* The ``1'' here is to tell parse_dollars not to |
* split the fields. |
*/ |
if (error) { |
free(expr); |
return error; |
} |
break; |
|
case '`': |
(*offset)++; |
error = parse_backtick(&expr, &expr_length, &expr_maxlen, |
words, offset, flags, NULL, NULL, NULL); |
/* The first NULL here is to tell parse_backtick not to |
* split the fields. |
*/ |
if (error) { |
free(expr); |
return error; |
} |
break; |
|
case '\\': |
error = parse_qtd_backslash(&expr, &expr_length, &expr_maxlen, |
words, offset); |
if (error) { |
free(expr); |
return error; |
} |
/* I think that a backslash within an |
* arithmetic expansion is bound to |
* cause an error sooner or later anyway though. |
*/ |
break; |
|
case ')': |
if (--paren_depth == 0) { |
char result[21]; /* 21 = ceil(log10(2^64)) + 1 */ |
long int numresult = 0; |
long long int convertme; |
|
if (bracket || words[1 + *offset] != ')') { |
free(expr); |
return WRDE_SYNTAX; |
} |
|
++(*offset); |
|
/* Go - evaluate. */ |
if (*expr && eval_expr(expr, &numresult) != 0) { |
free(expr); |
return WRDE_SYNTAX; |
} |
|
if (numresult < 0) { |
convertme = -numresult; |
*word = w_addchar(*word, word_length, max_length, '-'); |
if (!*word) { |
free(expr); |
return WRDE_NOSPACE; |
} |
} else |
convertme = numresult; |
|
result[20] = '\0'; |
*word = w_addstr(*word, word_length, max_length, |
_itoa(convertme, &result[20])); |
free(expr); |
return *word ? 0 : WRDE_NOSPACE; |
} |
expr = |
w_addchar(expr, &expr_length, &expr_maxlen, |
words[*offset]); |
if (expr == NULL) |
return WRDE_NOSPACE; |
|
break; |
|
case ']': |
if (bracket && paren_depth == 1) { |
char result[21]; /* 21 = ceil(log10(2^64)) + 1 */ |
long int numresult = 0; |
|
/* Go - evaluate. */ |
if (*expr && eval_expr(expr, &numresult) != 0) { |
free(expr); |
return WRDE_SYNTAX; |
} |
|
result[20] = '\0'; |
*word = w_addstr(*word, word_length, max_length, |
_itoa(numresult, &result[20])); |
free(expr); |
return *word ? 0 : WRDE_NOSPACE; |
} |
|
free(expr); |
return WRDE_SYNTAX; |
|
case '\n': |
case ';': |
case '{': |
case '}': |
free(expr); |
return WRDE_BADCHAR; |
|
case '(': |
++paren_depth; |
default: |
expr = |
w_addchar(expr, &expr_length, &expr_maxlen, |
words[*offset]); |
if (expr == NULL) |
return WRDE_NOSPACE; |
} |
} |
|
/* Premature end */ |
free(expr); |
return WRDE_SYNTAX; |
} |
|
/* Function called by child process in exec_comm() */ |
static void |
exec_comm_child(char *comm, int *fildes, int showerr, int noexec) |
{ |
const char *args[4] = { _PATH_BSHELL, "-c", comm, NULL }; |
|
/* Execute the command, or just check syntax? */ |
if (noexec) |
args[1] = "-nc"; |
|
/* Redirect output. */ |
dup2(fildes[1], 1); |
close(fildes[1]); |
|
/* Redirect stderr to /dev/null if we have to. */ |
if (showerr == 0) { |
int fd; |
|
close(2); |
fd = open(_PATH_DEVNULL, O_WRONLY); |
if (fd >= 0 && fd != 2) { |
dup2(fd, 2); |
close(fd); |
} |
} |
|
/* Make sure the subshell doesn't field-split on our behalf. */ |
unsetenv("IFS"); |
|
close(fildes[0]); |
execve(_PATH_BSHELL, (char *const *) args, __environ); |
|
/* Bad. What now? */ |
abort(); |
} |
|
/* Function to execute a command and retrieve the results */ |
/* pwordexp contains NULL if field-splitting is forbidden */ |
static int |
exec_comm(char *comm, char **word, size_t * word_length, |
size_t * max_length, int flags, wordexp_t * pwordexp, |
const char *ifs, const char *ifs_white) |
{ |
int fildes[2]; |
int bufsize = 128; |
int buflen; |
int i; |
int status = 0; |
size_t maxnewlines = 0; |
char *buffer; |
pid_t pid; |
|
/* Don't fork() unless necessary */ |
if (!comm || !*comm) |
return 0; |
|
if (pipe(fildes)) |
/* Bad */ |
return WRDE_NOSPACE; |
|
if ((pid = fork()) < 0) { |
/* Bad */ |
close(fildes[0]); |
close(fildes[1]); |
return WRDE_NOSPACE; |
} |
|
if (pid == 0) |
exec_comm_child(comm, fildes, (flags & WRDE_SHOWERR), 0); |
|
/* Parent */ |
|
close(fildes[1]); |
buffer = alloca(bufsize); |
|
if (!pwordexp) |
/* Quoted - no field splitting */ |
{ |
while (1) { |
if ((buflen = read(fildes[0], buffer, bufsize)) < 1) { |
if (waitpid(pid, &status, WNOHANG) == 0) |
continue; |
if ((buflen = read(fildes[0], buffer, bufsize)) < 1) |
break; |
} |
|
maxnewlines += buflen; |
|
*word = w_addmem(*word, word_length, max_length, buffer, buflen); |
if (*word == NULL) |
goto no_space; |
} |
} else |
/* Not quoted - split fields */ |
{ |
int copying = 0; |
|
/* 'copying' is: |
* 0 when searching for first character in a field not IFS white space |
* 1 when copying the text of a field |
* 2 when searching for possible non-whitespace IFS |
* 3 when searching for non-newline after copying field |
*/ |
|
while (1) { |
if ((buflen = read(fildes[0], buffer, bufsize)) < 1) { |
if (waitpid(pid, &status, WNOHANG) == 0) |
continue; |
if ((buflen = read(fildes[0], buffer, bufsize)) < 1) |
break; |
} |
|
for (i = 0; i < buflen; ++i) { |
if (strchr(ifs, buffer[i]) != NULL) { |
/* Current character is IFS */ |
if (strchr(ifs_white, buffer[i]) == NULL) { |
/* Current character is IFS but not whitespace */ |
if (copying == 2) { |
/* current character |
* | |
* V |
* eg: text<space><comma><space>moretext |
* |
* So, strip whitespace IFS (like at the start) |
*/ |
copying = 0; |
continue; |
} |
|
copying = 0; |
/* fall through and delimit field.. */ |
} else { |
if (buffer[i] == '\n') { |
/* Current character is (IFS) newline */ |
|
/* If copying a field, this is the end of it, |
but maybe all that's left is trailing newlines. |
So start searching for a non-newline. */ |
if (copying == 1) |
copying = 3; |
|
continue; |
} else { |
/* Current character is IFS white space, but |
not a newline */ |
|
/* If not either copying a field or searching |
for non-newline after a field, ignore it */ |
if (copying != 1 && copying != 3) |
continue; |
|
/* End of field (search for non-ws IFS afterwards) */ |
copying = 2; |
} |
} |
|
/* First IFS white space (non-newline), or IFS non-whitespace. |
* Delimit the field. Nulls are converted by w_addword. */ |
if (w_addword(pwordexp, *word) == WRDE_NOSPACE) |
goto no_space; |
|
*word = w_newword(word_length, max_length); |
|
maxnewlines = 0; |
/* fall back round the loop.. */ |
} else { |
/* Not IFS character */ |
|
if (copying == 3) { |
/* Nothing but (IFS) newlines since the last field, |
so delimit it here before starting new word */ |
if (w_addword(pwordexp, *word) == WRDE_NOSPACE) |
goto no_space; |
|
*word = w_newword(word_length, max_length); |
} |
|
copying = 1; |
|
if (buffer[i] == '\n') /* happens if newline not in IFS */ |
maxnewlines++; |
else |
maxnewlines = 0; |
|
*word = w_addchar(*word, word_length, max_length, |
buffer[i]); |
if (*word == NULL) |
goto no_space; |
} |
} |
} |
} |
|
/* Chop off trailing newlines (required by POSIX.2) */ |
/* Ensure we don't go back further than the beginning of the |
substitution (i.e. remove maxnewlines bytes at most) */ |
while (maxnewlines-- != 0 && |
*word_length > 0 && (*word)[*word_length - 1] == '\n') { |
(*word)[--*word_length] = '\0'; |
|
/* If the last word was entirely newlines, turn it into a new word |
* which can be ignored if there's nothing following it. */ |
if (*word_length == 0) { |
free(*word); |
*word = w_newword(word_length, max_length); |
break; |
} |
} |
|
close(fildes[0]); |
|
/* Check for syntax error (re-execute but with "-n" flag) */ |
if (buflen < 1 && status != 0) { |
if ((pid = fork()) < 0) { |
/* Bad */ |
return WRDE_NOSPACE; |
} |
|
if (pid == 0) { |
fildes[0] = fildes[1] = -1; |
exec_comm_child(comm, fildes, 0, 1); |
} |
|
if (waitpid(pid, &status, 0) == pid && status != 0) |
return WRDE_SYNTAX; |
} |
|
return 0; |
|
no_space: |
kill(pid, SIGKILL); |
waitpid(pid, NULL, 0); |
close(fildes[0]); |
return WRDE_NOSPACE; |
} |
|
static int |
parse_comm(char **word, size_t * word_length, size_t * max_length, |
const char *words, size_t * offset, int flags, |
wordexp_t * pwordexp, const char *ifs, const char *ifs_white) |
{ |
/* We are poised just after "$(" */ |
int paren_depth = 1; |
int error = 0; |
int quoted = 0; /* 1 for singly-quoted, 2 for doubly-quoted */ |
size_t comm_length; |
size_t comm_maxlen; |
char *comm = w_newword(&comm_length, &comm_maxlen); |
|
for (; words[*offset]; ++(*offset)) { |
switch (words[*offset]) { |
case '\'': |
if (quoted == 0) |
quoted = 1; |
else if (quoted == 1) |
quoted = 0; |
|
break; |
|
case '"': |
if (quoted == 0) |
quoted = 2; |
else if (quoted == 2) |
quoted = 0; |
|
break; |
|
case ')': |
if (!quoted && --paren_depth == 0) { |
/* Go -- give script to the shell */ |
if (comm) { |
error = exec_comm(comm, word, word_length, max_length, |
flags, pwordexp, ifs, ifs_white); |
free(comm); |
} |
|
return error; |
} |
|
/* This is just part of the script */ |
break; |
|
case '(': |
if (!quoted) |
++paren_depth; |
} |
|
comm = w_addchar(comm, &comm_length, &comm_maxlen, words[*offset]); |
if (comm == NULL) |
return WRDE_NOSPACE; |
} |
|
/* Premature end */ |
if (comm) |
free(comm); |
|
return WRDE_SYNTAX; |
} |
|
static int |
parse_backtick(char **word, size_t * word_length, size_t * max_length, |
const char *words, size_t * offset, int flags, |
wordexp_t * pwordexp, const char *ifs, |
const char *ifs_white) |
{ |
/* We are poised just after "`" */ |
int error; |
int squoting = 0; |
size_t comm_length; |
size_t comm_maxlen; |
char *comm = w_newword(&comm_length, &comm_maxlen); |
|
for (; words[*offset]; ++(*offset)) { |
switch (words[*offset]) { |
case '`': |
/* Go -- give the script to the shell */ |
error = exec_comm(comm, word, word_length, max_length, flags, |
pwordexp, ifs, ifs_white); |
free(comm); |
return error; |
|
case '\\': |
if (squoting) { |
error = parse_qtd_backslash(&comm, &comm_length, &comm_maxlen, |
words, offset); |
|
if (error) { |
free(comm); |
return error; |
} |
|
break; |
} |
|
++(*offset); |
error = parse_backslash(&comm, &comm_length, &comm_maxlen, words, |
offset); |
|
if (error) { |
free(comm); |
return error; |
} |
|
break; |
|
case '\'': |
squoting = 1 - squoting; |
default: |
comm = w_addchar(comm, &comm_length, &comm_maxlen, |
words[*offset]); |
if (comm == NULL) |
return WRDE_NOSPACE; |
} |
} |
|
/* Premature end */ |
free(comm); |
return WRDE_SYNTAX; |
} |
|
static int |
parse_param(char **word, size_t * word_length, size_t * max_length, |
const char *words, size_t * offset, int flags, |
wordexp_t * pwordexp, const char *ifs, const char *ifs_white, |
int quoted) |
{ |
/* We are poised just after "$" */ |
enum action { |
ACT_NONE, |
ACT_RP_SHORT_LEFT = '#', |
ACT_RP_LONG_LEFT = 'L', |
ACT_RP_SHORT_RIGHT = '%', |
ACT_RP_LONG_RIGHT = 'R', |
ACT_NULL_ERROR = '?', |
ACT_NULL_SUBST = '-', |
ACT_NONNULL_SUBST = '+', |
ACT_NULL_ASSIGN = '=' |
}; |
size_t env_length; |
size_t env_maxlen; |
size_t pat_length; |
size_t pat_maxlen; |
size_t start = *offset; |
char *env; |
char *pattern; |
char *value = NULL; |
enum action action = ACT_NONE; |
int depth = 0; |
int colon_seen = 0; |
int seen_hash = 0; |
int free_value = 0; |
int pattern_is_quoted = 0; /* 1 for singly-quoted, 2 for doubly-quoted */ |
int error; |
int special = 0; |
char buffer[21]; |
int brace = words[*offset] == '{'; |
|
env = w_newword(&env_length, &env_maxlen); |
pattern = w_newword(&pat_length, &pat_maxlen); |
|
if (brace) |
++ * offset; |
|
/* First collect the parameter name. */ |
|
if (words[*offset] == '#') { |
seen_hash = 1; |
if (!brace) |
goto envsubst; |
++*offset; |
} |
|
if (isalpha(words[*offset]) || words[*offset] == '_') { |
/* Normal parameter name. */ |
do { |
env = w_addchar(env, &env_length, &env_maxlen, words[*offset]); |
if (env == NULL) |
goto no_space; |
} |
while (isalnum(words[++*offset]) || words[*offset] == '_'); |
} else if (isdigit(words[*offset])) { |
/* Numeric parameter name. */ |
special = 1; |
do { |
env = w_addchar(env, &env_length, &env_maxlen, words[*offset]); |
if (env == NULL) |
goto no_space; |
if (!brace) |
goto envsubst; |
} |
while (isdigit(words[++*offset])); |
} else if (strchr("*@$", words[*offset]) != NULL) { |
/* Special parameter. */ |
special = 1; |
env = w_addchar(env, &env_length, &env_maxlen, words[*offset]); |
if (env == NULL) |
goto no_space; |
++*offset; |
} else { |
if (brace) |
goto syntax; |
} |
|
if (brace) { |
/* Check for special action to be applied to the value. */ |
switch (words[*offset]) { |
case '}': |
/* Evaluate. */ |
goto envsubst; |
|
case '#': |
action = ACT_RP_SHORT_LEFT; |
if (words[1 + *offset] == '#') { |
++*offset; |
action = ACT_RP_LONG_LEFT; |
} |
break; |
|
case '%': |
action = ACT_RP_SHORT_RIGHT; |
if (words[1 + *offset] == '%') { |
++*offset; |
action = ACT_RP_LONG_RIGHT; |
} |
break; |
|
case ':': |
if (strchr("-=?+", words[1 + *offset]) == NULL) |
goto syntax; |
|
colon_seen = 1; |
action = words[++*offset]; |
break; |
|
case '-': |
case '=': |
case '?': |
case '+': |
action = words[*offset]; |
break; |
|
default: |
goto syntax; |
} |
|
/* Now collect the pattern, but don't expand it yet. */ |
++*offset; |
for (; words[*offset]; ++(*offset)) { |
switch (words[*offset]) { |
case '{': |
if (!pattern_is_quoted) |
++depth; |
break; |
|
case '}': |
if (!pattern_is_quoted) { |
if (depth == 0) |
goto envsubst; |
--depth; |
} |
break; |
|
case '\\': |
if (pattern_is_quoted) |
/* Quoted; treat as normal character. */ |
break; |
|
/* Otherwise, it's an escape: next character is literal. */ |
if (words[++*offset] == '\0') |
goto syntax; |
|
pattern = w_addchar(pattern, &pat_length, &pat_maxlen, '\\'); |
if (pattern == NULL) |
goto no_space; |
|
break; |
|
case '\'': |
if (pattern_is_quoted == 0) |
pattern_is_quoted = 1; |
else if (pattern_is_quoted == 1) |
pattern_is_quoted = 0; |
|
break; |
|
case '"': |
if (pattern_is_quoted == 0) |
pattern_is_quoted = 2; |
else if (pattern_is_quoted == 2) |
pattern_is_quoted = 0; |
|
break; |
} |
|
pattern = w_addchar(pattern, &pat_length, &pat_maxlen, |
words[*offset]); |
if (pattern == NULL) |
goto no_space; |
} |
} |
|
/* End of input string -- remember to reparse the character that we |
* stopped at. */ |
--(*offset); |
|
envsubst: |
if (words[start] == '{' && words[*offset] != '}') |
goto syntax; |
|
if (env == NULL) { |
if (seen_hash) { |
/* $# expands to the number of positional parameters */ |
buffer[20] = '\0'; |
value = _itoa(__libc_argc - 1, &buffer[20]); |
seen_hash = 0; |
} else { |
/* Just $ on its own */ |
*offset = start - 1; |
*word = w_addchar(*word, word_length, max_length, '$'); |
return *word ? 0 : WRDE_NOSPACE; |
} |
} |
/* Is it a numeric parameter? */ |
else if (isdigit(env[0])) { |
int n = atoi(env); |
|
if (n >= __libc_argc) |
/* Substitute NULL. */ |
value = NULL; |
else |
/* Replace with appropriate positional parameter. */ |
value = __libc_argv[n]; |
} |
/* Is it a special parameter? */ |
else if (special) { |
/* Is it `$$'? */ |
if (*env == '$') { |
buffer[20] = '\0'; |
value = _itoa(getpid(), &buffer[20]); |
} |
/* Is it `${#*}' or `${#@}'? */ |
else if ((*env == '*' || *env == '@') && seen_hash) { |
buffer[20] = '\0'; |
value = _itoa(__libc_argc > 0 ? __libc_argc - 1 : 0, |
&buffer[20]); |
*word = w_addstr(*word, word_length, max_length, value); |
free(env); |
if (pattern) |
free(pattern); |
return *word ? 0 : WRDE_NOSPACE; |
} |
/* Is it `$*' or `$@' (unquoted) ? */ |
else if (*env == '*' || (*env == '@' && !quoted)) { |
size_t plist_len = 0; |
int p; |
char *end; |
|
/* Build up value parameter by parameter (copy them) */ |
for (p = 1; __libc_argv[p]; ++p) |
plist_len += strlen(__libc_argv[p]) + 1; /* for space */ |
value = malloc(plist_len); |
if (value == NULL) |
goto no_space; |
end = value; |
*end = 0; |
for (p = 1; __libc_argv[p]; ++p) { |
if (p > 1) |
*end++ = ' '; |
end = stpcpy(end, __libc_argv[p]); |
} |
|
free_value = 1; |
} else { |
/* Must be a quoted `$@' */ |
assert(*env == '@' && quoted); |
|
/* Each parameter is a separate word ("$@") */ |
if (__libc_argc == 2) |
value = __libc_argv[1]; |
else if (__libc_argc > 2) { |
int p; |
|
/* Append first parameter to current word. */ |
value = w_addstr(*word, word_length, max_length, |
__libc_argv[1]); |
if (value == NULL || w_addword(pwordexp, value)) |
goto no_space; |
|
for (p = 2; __libc_argv[p + 1]; p++) { |
char *newword = strdup(__libc_argv[p]); |
|
if (newword == NULL || w_addword(pwordexp, newword)) |
goto no_space; |
} |
|
/* Start a new word with the last parameter. */ |
*word = w_newword(word_length, max_length); |
value = __libc_argv[p]; |
} else { |
free(env); |
free(pattern); |
return 0; |
} |
} |
} else |
value = getenv(env); |
|
if (value == NULL && (flags & WRDE_UNDEF)) { |
/* Variable not defined. */ |
error = WRDE_BADVAL; |
goto do_error; |
} |
|
if (action != ACT_NONE) { |
int expand_pattern = 0; |
|
/* First, find out if we need to expand pattern (i.e. if we will |
* use it). */ |
switch (action) { |
case ACT_RP_SHORT_LEFT: |
case ACT_RP_LONG_LEFT: |
case ACT_RP_SHORT_RIGHT: |
case ACT_RP_LONG_RIGHT: |
/* Always expand for these. */ |
expand_pattern = 1; |
break; |
|
case ACT_NULL_ERROR: |
case ACT_NULL_SUBST: |
case ACT_NULL_ASSIGN: |
if (!value || (!*value && colon_seen)) |
/* If param is unset, or set but null and a colon has been seen, |
the expansion of the pattern will be needed. */ |
expand_pattern = 1; |
|
break; |
|
case ACT_NONNULL_SUBST: |
/* Expansion of word will be needed if parameter is set and not null, |
or set null but no colon has been seen. */ |
if (value && (*value || !colon_seen)) |
expand_pattern = 1; |
|
break; |
|
default: |
assert(!"Unrecognised action!"); |
} |
|
if (expand_pattern) { |
/* We need to perform tilde expansion, parameter expansion, |
command substitution, and arithmetic expansion. We also |
have to be a bit careful with wildcard characters, as |
pattern might be given to fnmatch soon. To do this, we |
convert quotes to escapes. */ |
|
char *expanded; |
size_t exp_len; |
size_t exp_maxl; |
char *p; |
int quoted = 0; /* 1: single quotes; 2: double */ |
|
expanded = w_newword(&exp_len, &exp_maxl); |
for (p = pattern; p && *p; p++) { |
size_t offset; |
|
switch (*p) { |
case '"': |
if (quoted == 2) |
quoted = 0; |
else if (quoted == 0) |
quoted = 2; |
else |
break; |
|
continue; |
|
case '\'': |
if (quoted == 1) |
quoted = 0; |
else if (quoted == 0) |
quoted = 1; |
else |
break; |
|
continue; |
|
case '*': |
case '?': |
if (quoted) { |
/* Convert quoted wildchar to escaped wildchar. */ |
expanded = w_addchar(expanded, &exp_len, |
&exp_maxl, '\\'); |
|
if (expanded == NULL) |
goto no_space; |
} |
break; |
|
case '$': |
offset = 0; |
error = parse_dollars(&expanded, &exp_len, &exp_maxl, p, |
&offset, flags, NULL, NULL, NULL, 1); |
if (error) { |
if (free_value) |
free(value); |
|
if (expanded) |
free(expanded); |
|
goto do_error; |
} |
|
p += offset; |
continue; |
|
case '~': |
if (quoted || exp_len) |
break; |
|
offset = 0; |
error = parse_tilde(&expanded, &exp_len, &exp_maxl, p, |
&offset, 0); |
if (error) { |
if (free_value) |
free(value); |
|
if (expanded) |
free(expanded); |
|
goto do_error; |
} |
|
p += offset; |
continue; |
|
case '\\': |
expanded = w_addchar(expanded, &exp_len, &exp_maxl, '\\'); |
++p; |
assert(*p); /* checked when extracted initially */ |
if (expanded == NULL) |
goto no_space; |
} |
|
expanded = w_addchar(expanded, &exp_len, &exp_maxl, *p); |
|
if (expanded == NULL) |
goto no_space; |
} |
|
if (pattern) |
free(pattern); |
|
pattern = expanded; |
} |
|
switch (action) { |
case ACT_RP_SHORT_LEFT: |
case ACT_RP_LONG_LEFT: |
case ACT_RP_SHORT_RIGHT: |
case ACT_RP_LONG_RIGHT: |
{ |
char *p; |
char c; |
char *end; |
|
if (value == NULL || pattern == NULL || *pattern == '\0') |
break; |
|
end = value + strlen(value); |
|
switch (action) { |
case ACT_RP_SHORT_LEFT: |
for (p = value; p <= end; ++p) { |
c = *p; |
*p = '\0'; |
if (fnmatch(pattern, value, 0) != FNM_NOMATCH) { |
*p = c; |
if (free_value) { |
char *newval = strdup(p); |
|
if (newval == NULL) { |
free(value); |
goto no_space; |
} |
free(value); |
value = newval; |
} else |
value = p; |
break; |
} |
*p = c; |
} |
|
break; |
|
case ACT_RP_LONG_LEFT: |
for (p = end; p >= value; --p) { |
c = *p; |
*p = '\0'; |
if (fnmatch(pattern, value, 0) != FNM_NOMATCH) { |
*p = c; |
if (free_value) { |
char *newval = strdup(p); |
|
if (newval == NULL) { |
free(value); |
goto no_space; |
} |
free(value); |
value = newval; |
} else |
value = p; |
break; |
} |
*p = c; |
} |
|
break; |
|
case ACT_RP_SHORT_RIGHT: |
for (p = end; p >= value; --p) { |
if (fnmatch(pattern, p, 0) != FNM_NOMATCH) { |
char *newval; |
|
newval = malloc(p - value + 1); |
|
if (newval == NULL) { |
if (free_value) |
free(value); |
goto no_space; |
} |
|
*(char *) mempcpy(newval, value, p - value) = '\0'; |
if (free_value) |
free(value); |
value = newval; |
free_value = 1; |
break; |
} |
} |
|
break; |
|
case ACT_RP_LONG_RIGHT: |
for (p = value; p <= end; ++p) { |
if (fnmatch(pattern, p, 0) != FNM_NOMATCH) { |
char *newval; |
|
newval = malloc(p - value + 1); |
|
if (newval == NULL) { |
if (free_value) |
free(value); |
goto no_space; |
} |
|
*(char *) mempcpy(newval, value, p - value) = '\0'; |
if (free_value) |
free(value); |
value = newval; |
free_value = 1; |
break; |
} |
} |
|
break; |
|
default: |
break; |
} |
|
break; |
} |
|
case ACT_NULL_ERROR: |
if (value && *value) |
/* Substitute parameter */ |
break; |
|
error = 0; |
if (!colon_seen && value) |
/* Substitute NULL */ |
; |
else if (*pattern) |
fprintf(stderr, "%s: %s\n", env, pattern); |
else { |
fprintf(stderr, "%s: parameter null or not set\n", env); |
error = WRDE_BADVAL; |
} |
|
if (free_value) |
free(value); |
goto do_error; |
|
case ACT_NULL_SUBST: |
if (value && *value) |
/* Substitute parameter */ |
break; |
|
if (free_value && value) |
free(value); |
|
if (!colon_seen && value) |
/* Substitute NULL */ |
goto success; |
|
value = pattern ? strdup(pattern) : pattern; |
free_value = 1; |
|
if (pattern && !value) |
goto no_space; |
|
break; |
|
case ACT_NONNULL_SUBST: |
if (value && (*value || !colon_seen)) { |
if (free_value && value) |
free(value); |
|
value = pattern ? strdup(pattern) : pattern; |
free_value = 1; |
|
if (pattern && !value) |
goto no_space; |
|
break; |
} |
|
/* Substitute NULL */ |
if (free_value) |
free(value); |
goto success; |
|
case ACT_NULL_ASSIGN: |
if (value && *value) |
/* Substitute parameter */ |
break; |
|
if (!colon_seen && value) { |
/* Substitute NULL */ |
if (free_value) |
free(value); |
goto success; |
} |
|
if (free_value && value) |
free(value); |
|
value = pattern ? strdup(pattern) : pattern; |
free_value = 1; |
|
if (pattern && !value) |
goto no_space; |
|
setenv(env, value, 1); |
break; |
|
default: |
assert(!"Unrecognised action!"); |
} |
} |
|
free(env); |
env = NULL; |
free(pattern); |
pattern = NULL; |
|
if (seen_hash) { |
char param_length[21]; |
|
param_length[20] = '\0'; |
*word = w_addstr(*word, word_length, max_length, |
_itoa(value ? strlen(value) : 0, |
¶m_length[20])); |
if (free_value) { |
assert(value != NULL); |
free(value); |
} |
|
return *word ? 0 : WRDE_NOSPACE; |
} |
|
if (value == NULL) |
return 0; |
|
if (quoted || !pwordexp) { |
/* Quoted - no field split */ |
*word = w_addstr(*word, word_length, max_length, value); |
if (free_value) |
free(value); |
|
return *word ? 0 : WRDE_NOSPACE; |
} else { |
/* Need to field-split */ |
char *value_copy = strdup(value); /* Don't modify value */ |
char *field_begin = value_copy; |
int seen_nonws_ifs = 0; |
|
if (free_value) |
free(value); |
|
if (value_copy == NULL) |
goto no_space; |
|
do { |
char *field_end = field_begin; |
char *next_field; |
|
/* If this isn't the first field, start a new word */ |
if (field_begin != value_copy) { |
if (w_addword(pwordexp, *word) == WRDE_NOSPACE) { |
free(value_copy); |
goto no_space; |
} |
|
*word = w_newword(word_length, max_length); |
} |
|
/* Skip IFS whitespace before the field */ |
field_begin += strspn(field_begin, ifs_white); |
|
if (!seen_nonws_ifs && *field_begin == 0) |
/* Nothing but whitespace */ |
break; |
|
/* Search for the end of the field */ |
field_end = field_begin + strcspn(field_begin, ifs); |
|
/* Set up pointer to the character after end of field and |
skip whitespace IFS after it. */ |
next_field = field_end + strspn(field_end, ifs_white); |
|
/* Skip at most one non-whitespace IFS character after the field */ |
seen_nonws_ifs = 0; |
if (*next_field && strchr(ifs, *next_field)) { |
seen_nonws_ifs = 1; |
next_field++; |
} |
|
/* Null-terminate it */ |
*field_end = 0; |
|
/* Tag a copy onto the current word */ |
*word = w_addstr(*word, word_length, max_length, field_begin); |
|
if (*word == NULL && *field_begin != '\0') { |
free(value_copy); |
goto no_space; |
} |
|
field_begin = next_field; |
} |
while (seen_nonws_ifs || *field_begin); |
|
free(value_copy); |
} |
|
return 0; |
|
success: |
error = 0; |
goto do_error; |
|
no_space: |
error = WRDE_NOSPACE; |
goto do_error; |
|
syntax: |
error = WRDE_SYNTAX; |
|
do_error: |
if (env) |
free(env); |
|
if (pattern) |
free(pattern); |
|
return error; |
} |
#else |
static inline int |
parse_backtick(char **word, size_t * word_length, size_t * max_length, |
const char *words, size_t * offset, int flags, |
wordexp_t * pwordexp, const char *ifs, |
const char *ifs_white) |
{ |
return 0; |
} |
#endif |
|
static int |
parse_dollars(char **word, size_t * word_length, size_t * max_length, |
const char *words, size_t * offset, int flags, |
wordexp_t * pwordexp, const char *ifs, const char *ifs_white, |
int quoted) |
{ |
/* We are poised _at_ "$" */ |
switch (words[1 + *offset]) { |
case '"': |
case '\'': |
case 0: |
*word = w_addchar(*word, word_length, max_length, '$'); |
return *word ? 0 : WRDE_NOSPACE; |
|
#ifdef __WORDEXP_FULL |
case '(': |
if (words[2 + *offset] == '(') { |
/* Differentiate between $((1+3)) and $((echo);(ls)) */ |
int i = 3 + *offset; |
int depth = 0; |
|
while (words[i] && !(depth == 0 && words[i] == ')')) { |
if (words[i] == '(') |
++depth; |
else if (words[i] == ')') |
--depth; |
|
++i; |
} |
|
if (words[i] == ')' && words[i + 1] == ')') { |
(*offset) += 3; |
/* Call parse_arith -- 0 is for "no brackets" */ |
return parse_arith(word, word_length, max_length, words, |
offset, flags, 0); |
} |
} |
|
if (flags & WRDE_NOCMD) |
return WRDE_CMDSUB; |
|
(*offset) += 2; |
return parse_comm(word, word_length, max_length, words, offset, |
flags, quoted ? NULL : pwordexp, ifs, ifs_white); |
|
case '[': |
(*offset) += 2; |
/* Call parse_arith -- 1 is for "brackets" */ |
return parse_arith(word, word_length, max_length, words, offset, |
flags, 1); |
|
case '{': |
default: |
++(*offset); /* parse_param needs to know if "{" is there */ |
return parse_param(word, word_length, max_length, words, offset, |
flags, pwordexp, ifs, ifs_white, quoted); |
#else |
default: |
++(*offset); /* parse_param needs to know if "{" is there */ |
return 0; |
#endif |
} |
} |
|
static int |
parse_dquote(char **word, size_t * word_length, size_t * max_length, |
const char *words, size_t * offset, int flags, |
wordexp_t * pwordexp, const char *ifs, const char *ifs_white) |
{ |
/* We are poised just after a double-quote */ |
int error; |
|
for (; words[*offset]; ++(*offset)) { |
switch (words[*offset]) { |
case '"': |
return 0; |
|
case '$': |
error = parse_dollars(word, word_length, max_length, words, offset, |
flags, pwordexp, ifs, ifs_white, 1); |
/* The ``1'' here is to tell parse_dollars not to |
* split the fields. It may need to, however ("$@"). |
*/ |
if (error) |
return error; |
|
break; |
|
case '`': |
if (flags & WRDE_NOCMD) |
return WRDE_CMDSUB; |
|
++(*offset); |
error = parse_backtick(word, word_length, max_length, words, |
offset, flags, NULL, NULL, NULL); |
/* The first NULL here is to tell parse_backtick not to |
* split the fields. |
*/ |
if (error) |
return error; |
|
break; |
|
case '\\': |
error = parse_qtd_backslash(word, word_length, max_length, words, |
offset); |
|
if (error) |
return error; |
|
break; |
|
default: |
*word = w_addchar(*word, word_length, max_length, words[*offset]); |
if (*word == NULL) |
return WRDE_NOSPACE; |
} |
} |
|
/* Unterminated string */ |
return WRDE_SYNTAX; |
} |
|
/* |
* wordfree() is to be called after pwordexp is finished with. |
*/ |
|
void wordfree(wordexp_t * pwordexp) |
{ |
|
/* wordexp can set pwordexp to NULL */ |
if (pwordexp && pwordexp->we_wordv) { |
char **wordv = pwordexp->we_wordv; |
|
for (wordv += pwordexp->we_offs; *wordv; ++wordv) |
free(*wordv); |
|
free(pwordexp->we_wordv); |
pwordexp->we_wordv = NULL; |
} |
} |
|
/* |
* wordexp() |
*/ |
|
int wordexp(const char *words, wordexp_t * we, int flags) |
{ |
size_t words_offset; |
size_t word_length; |
size_t max_length; |
char *word = w_newword(&word_length, &max_length); |
int error; |
char *ifs; |
char ifs_white[4]; |
wordexp_t old_word = *we; |
|
if (flags & WRDE_REUSE) { |
/* Minimal implementation of WRDE_REUSE for now */ |
wordfree(we); |
old_word.we_wordv = NULL; |
} |
|
if ((flags & WRDE_APPEND) == 0) { |
we->we_wordc = 0; |
|
if (flags & WRDE_DOOFFS) { |
we->we_wordv = calloc(1 + we->we_offs, sizeof(char *)); |
if (we->we_wordv == NULL) { |
error = WRDE_NOSPACE; |
goto do_error; |
} |
} else { |
we->we_wordv = calloc(1, sizeof(char *)); |
if (we->we_wordv == NULL) { |
error = WRDE_NOSPACE; |
goto do_error; |
} |
|
we->we_offs = 0; |
} |
} |
|
/* Find out what the field separators are. |
* There are two types: whitespace and non-whitespace. |
*/ |
ifs = getenv("IFS"); |
|
if (!ifs) |
/* IFS unset - use <space><tab><newline>. */ |
ifs = strcpy(ifs_white, " \t\n"); |
else { |
char *ifsch = ifs; |
char *whch = ifs_white; |
|
/* Start off with no whitespace IFS characters */ |
ifs_white[0] = '\0'; |
|
while (*ifsch != '\0') { |
if ((*ifsch == ' ') || (*ifsch == '\t') || (*ifsch == '\n')) { |
/* Whitespace IFS. See first whether it is already in our |
collection. */ |
char *runp = ifs_white; |
|
while (runp < whch && *runp != '\0' && *runp != *ifsch) |
++runp; |
|
if (runp == whch) |
*whch++ = *ifsch; |
} |
|
++ifsch; |
} |
*whch = '\0'; |
} |
|
for (words_offset = 0; words[words_offset]; ++words_offset) |
switch (words[words_offset]) { |
case '\\': |
error = parse_backslash(&word, &word_length, &max_length, words, |
&words_offset); |
|
if (error) |
goto do_error; |
|
break; |
|
case '$': |
error = parse_dollars(&word, &word_length, &max_length, words, |
&words_offset, flags, we, ifs, ifs_white, |
0); |
|
if (error) |
goto do_error; |
|
break; |
|
case '`': |
if (flags & WRDE_NOCMD) { |
error = WRDE_CMDSUB; |
goto do_error; |
} |
|
++words_offset; |
error = parse_backtick(&word, &word_length, &max_length, words, |
&words_offset, flags, we, ifs, |
ifs_white); |
|
if (error) |
goto do_error; |
|
break; |
|
case '"': |
++words_offset; |
error = parse_dquote(&word, &word_length, &max_length, words, |
&words_offset, flags, we, ifs, ifs_white); |
|
if (error) |
goto do_error; |
|
if (!word_length) { |
error = w_addword(we, NULL); |
|
if (error) |
return error; |
} |
|
break; |
|
case '\'': |
++words_offset; |
error = parse_squote(&word, &word_length, &max_length, words, |
&words_offset); |
|
if (error) |
goto do_error; |
|
if (!word_length) { |
error = w_addword(we, NULL); |
|
if (error) |
return error; |
} |
|
break; |
|
case '~': |
error = parse_tilde(&word, &word_length, &max_length, words, |
&words_offset, we->we_wordc); |
|
if (error) |
goto do_error; |
|
break; |
|
case '*': |
case '[': |
case '?': |
error = parse_glob(&word, &word_length, &max_length, words, |
&words_offset, flags, we, ifs, ifs_white); |
|
if (error) |
goto do_error; |
|
break; |
|
default: |
/* Is it a word separator? */ |
if (strchr(" \t", words[words_offset]) == NULL) { |
char ch = words[words_offset]; |
|
/* Not a word separator -- but is it a valid word char? */ |
if (strchr("\n|&;<>(){}", ch)) { |
/* Fail */ |
error = WRDE_BADCHAR; |
goto do_error; |
} |
|
/* "Ordinary" character -- add it to word */ |
word = w_addchar(word, &word_length, &max_length, ch); |
if (word == NULL) { |
error = WRDE_NOSPACE; |
goto do_error; |
} |
|
break; |
} |
|
/* If a word has been delimited, add it to the list. */ |
if (word != NULL) { |
error = w_addword(we, word); |
if (error) |
goto do_error; |
} |
|
word = w_newword(&word_length, &max_length); |
} |
|
/* End of string */ |
|
/* There was a word separator at the end */ |
if (word == NULL) /* i.e. w_newword */ |
return 0; |
|
/* There was no field separator at the end */ |
return w_addword(we, word); |
|
do_error: |
/* Error: |
* free memory used (unless error is WRDE_NOSPACE), and |
* set we members back to what they were. |
*/ |
|
if (word != NULL) |
free(word); |
|
if (error == WRDE_NOSPACE) |
return WRDE_NOSPACE; |
|
if ((flags & WRDE_APPEND) == 0) |
wordfree(we); |
|
*we = old_word; |
return error; |
} |
/Makefile
0,0 → 1,39
# Makefile for uClibc |
# |
# Copyright (C) 2003 Erik Andersen <andersen@uclibc.org> |
# |
# This program is free software; you can redistribute it and/or modify it under |
# the terms of the GNU Library General Public License as published by the Free |
# Software Foundation; either version 2 of the License, or (at your option) any |
# later version. |
# |
# This program is distributed in the hope that it will be useful, but WITHOUT |
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
# FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more |
# details. |
# |
# You should have received a copy of the GNU Library General Public License |
# along with this program; if not, write to the Free Software Foundation, Inc., |
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
TOPDIR=../../../ |
include $(TOPDIR)Rules.mak |
|
CSRC=wordexp.c |
COBJS=$(patsubst %.c,%.o, $(CSRC)) |
OBJS=$(COBJS) |
|
all: $(OBJS) $(LIBC) |
|
$(LIBC): ar-target |
|
ar-target: $(OBJS) |
$(AR) $(ARFLAGS) $(LIBC) $(OBJS) |
|
$(COBJS): %.o : %.c |
$(CC) $(CFLAGS) -c $< -o $@ |
$(STRIPTOOL) -x -R .note -R .comment $*.o |
|
clean: |
$(RM) *.[oa] *~ core |
|