aboutsummaryrefslogtreecommitdiff
path: root/posix/wordexp.c
diff options
context:
space:
mode:
Diffstat (limited to 'posix/wordexp.c')
-rw-r--r--posix/wordexp.c263
1 files changed, 175 insertions, 88 deletions
diff --git a/posix/wordexp.c b/posix/wordexp.c
index 302cf0884f..fd6ce113b2 100644
--- a/posix/wordexp.c
+++ b/posix/wordexp.c
@@ -174,8 +174,8 @@ w_addword (wordexp_t *pwordexp, char *word)
if (new_wordv != NULL)
{
pwordexp->we_wordv = new_wordv;
- pwordexp->we_wordv[pwordexp->we_wordc++] = word;
- pwordexp->we_wordv[pwordexp->we_wordc] = NULL;
+ pwordexp->we_wordv[pwordexp->we_offs + pwordexp->we_wordc++] = word;
+ pwordexp->we_wordv[pwordexp->we_offs + pwordexp->we_wordc] = NULL;
return 0;
}
@@ -299,35 +299,51 @@ parse_tilde (char **word, size_t *word_length, size_t *max_length,
uid_t uid;
struct passwd pwd, *tpwd;
int buflen = 1000;
- char* buffer = __alloca (buflen);
+ char* home;
+ char* buffer;
int result;
- uid = __getuid ();
-
- while ((result = __getpwuid_r (uid, &pwd, buffer, buflen, &tpwd)) != 0
- && errno == ERANGE)
- {
- buflen += 1000;
- buffer = __alloca (buflen);
- }
+ /* 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. */
- if (result == 0 && tpwd != NULL && pwd.pw_dir != NULL)
+ home = getenv ("HOME");
+ if (home != NULL)
{
- *word = w_addstr (*word, word_length, max_length, pwd.pw_dir);
+ *word = w_addstr (*word, word_length, max_length, home);
if (*word == NULL)
return WRDE_NOSPACE;
}
else
{
- *word = w_addchar (*word, word_length, max_length, '~');
- if (*word == NULL)
- return WRDE_NOSPACE;
+ 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 - *offset);
+ char *user = __strndup (&words[1 + *offset], i - (1 + *offset));
struct passwd pwd, *tpwd;
int buflen = 1000;
char* buffer = __alloca (buflen);
@@ -806,6 +822,44 @@ parse_arith (char **word, size_t *word_length, size_t *max_length,
return WRDE_SYNTAX;
}
+/* Function called by child process in exec_comm() */
+static void
+internal_function
+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
@@ -818,6 +872,8 @@ exec_comm (char *comm, char **word, size_t *word_length, size_t *max_length,
int bufsize = 128;
int buflen;
int i;
+ int status = 0;
+ size_t maxnewlines = 0;
char *buffer;
pid_t pid;
@@ -838,36 +894,7 @@ exec_comm (char *comm, char **word, size_t *word_length, size_t *max_length,
}
if (pid == 0)
- {
- /* Child */
- const char *args[4] = { _PATH_BSHELL, "-c", comm, NULL };
-
- /* Redirect output. */
- __dup2 (fildes[1], 1);
- __close (fildes[1]);
-
- /* Redirect stderr to /dev/null if we have to. */
- if ((flags & WRDE_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 ();
- }
+ exec_comm_child(comm, fildes, (flags & WRDE_SHOWERR), 0);
/* Parent */
@@ -875,18 +902,20 @@ exec_comm (char *comm, char **word, size_t *word_length, size_t *max_length,
buffer = __alloca (bufsize);
if (!pwordexp)
- { /* Quoted - no field splitting */
-
+ /* Quoted - no field splitting */
+ {
while (1)
{
if ((buflen = __read (fildes[0], buffer, bufsize)) < 1)
{
- if (__waitpid (pid, NULL, WNOHANG) == 0)
+ 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;
@@ -900,15 +929,16 @@ exec_comm (char *comm, char **word, size_t *word_length, size_t *max_length,
* 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, NULL, WNOHANG) == 0)
+ if (__waitpid (pid, &status, WNOHANG) == 0)
continue;
- if ((__read (fildes[0], buffer, bufsize)) < 1)
+ if ((buflen = __read (fildes[0], buffer, bufsize)) < 1)
break;
}
@@ -938,29 +968,64 @@ exec_comm (char *comm, char **word, size_t *word_length, size_t *max_length,
}
else
{
- /* Current character is IFS white space */
+ if (buffer[i] == '\n')
+ {
+ /* Current character is (IFS) newline */
- /* If not copying a field, ignore it */
- if (copying != 1)
- continue;
+ /* 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;
- /* End of field (search for non-ws IFS afterwards) */
- copying = 2;
+ 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, or IFS non-whitespace.
+ /* 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)
@@ -970,8 +1035,11 @@ exec_comm (char *comm, char **word, size_t *word_length, size_t *max_length,
}
}
- /* Bash chops off trailing newlines, which seems sensible. */
- while (*word_length > 0 && (*word)[*word_length - 1] == '\n')
+ /* 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';
@@ -986,6 +1054,26 @@ exec_comm (char *comm, char **word, size_t *word_length, size_t *max_length,
}
__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:
@@ -2109,7 +2197,6 @@ wordfree (wordexp_t *pwordexp)
int
wordexp (const char *words, wordexp_t *pwordexp, int flags)
{
- size_t wordv_offset;
size_t words_offset;
size_t word_length;
size_t max_length;
@@ -2117,42 +2204,41 @@ wordexp (const char *words, wordexp_t *pwordexp, int flags)
int error;
char *ifs;
char ifs_white[4];
- char **old_wordv = pwordexp->we_wordv;
- size_t old_wordc = (flags & WRDE_REUSE) ? pwordexp->we_wordc : 0;
+ wordexp_t old_word = *pwordexp;
if (flags & WRDE_REUSE)
{
/* Minimal implementation of WRDE_REUSE for now */
wordfree (pwordexp);
- old_wordv = NULL;
+ old_word.we_wordv = NULL;
}
- if (flags & WRDE_DOOFFS)
+ if ((flags & WRDE_APPEND) == 0)
{
- pwordexp->we_wordv = calloc (1 + pwordexp->we_offs, sizeof (char *));
- if (pwordexp->we_wordv == NULL)
+ pwordexp->we_wordc = 0;
+
+ if (flags & WRDE_DOOFFS)
{
- error = WRDE_NOSPACE;
- goto do_error;
+ pwordexp->we_wordv = calloc (1 + pwordexp->we_offs, sizeof (char *));
+ if (pwordexp->we_wordv == NULL)
+ {
+ error = WRDE_NOSPACE;
+ goto do_error;
+ }
}
- }
- else
- {
- pwordexp->we_wordv = calloc (1, sizeof (char *));
- if (pwordexp->we_wordv == NULL)
+ else
{
- error = WRDE_NOSPACE;
- goto do_error;
- }
+ pwordexp->we_wordv = calloc (1, sizeof (char *));
+ if (pwordexp->we_wordv == NULL)
+ {
+ error = WRDE_NOSPACE;
+ goto do_error;
+ }
- pwordexp->we_offs = 0;
+ pwordexp->we_offs = 0;
+ }
}
- if ((flags & WRDE_APPEND) == 0)
- pwordexp->we_wordc = 0;
-
- wordv_offset = pwordexp->we_offs + pwordexp->we_wordc;
-
/* Find out what the field separators are.
* There are two types: whitespace and non-whitespace.
*/
@@ -2333,7 +2419,7 @@ wordexp (const char *words, wordexp_t *pwordexp, int flags)
do_error:
/* Error:
* free memory used (unless error is WRDE_NOSPACE), and
- * set we_wordc and wd_wordv back to what they were.
+ * set pwordexp members back to what they were.
*/
if (word != NULL)
@@ -2342,8 +2428,9 @@ do_error:
if (error == WRDE_NOSPACE)
return WRDE_NOSPACE;
- wordfree (pwordexp);
- pwordexp->we_wordv = old_wordv;
- pwordexp->we_wordc = old_wordc;
+ if ((flags & WRDE_APPEND) == 0)
+ wordfree (pwordexp);
+
+ *pwordexp = old_word;
return error;
}