📄 wordexp.c
字号:
/* 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 intparse_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 intparse_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 intparse_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 intdo_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 intparse_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 intparse_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_FULLstatic 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -