/* grecs - Gray's Extensible Configuration System Copyright (C) 2014-2016 Sergey Poznyakoff Grecs is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. Grecs 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Grecs. If not, see . */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include "grecs.h" #include "wordsplit.h" extern char **environ; char *progname; struct kwd { const char *name; int tok; }; struct kwd bool_keytab[] = { { "append", WRDSF_APPEND }, /*{ "reuse", WRDSF_REUSE },*/ { "undef", WRDSF_UNDEF }, { "novar", WRDSF_NOVAR }, { "nocmd", WRDSF_NOCMD }, { "ws", WRDSF_WS }, { "quote", WRDSF_QUOTE }, { "squote", WRDSF_SQUOTE }, { "dquote", WRDSF_DQUOTE }, { "squeeze_delims", WRDSF_SQUEEZE_DELIMS }, { "return_delims", WRDSF_RETURN_DELIMS }, { "sed", WRDSF_SED_EXPR }, { "debug", WRDSF_SHOWDBG }, { "nosplit", WRDSF_NOSPLIT }, { "keepundef", WRDSF_KEEPUNDEF }, { "warnundef", WRDSF_WARNUNDEF }, { "cescapes", WRDSF_CESCAPES }, { "default", WRDSF_DEFFLAGS }, { "env_kv", WRDSF_ENV_KV }, { "incremental", WRDSF_INCREMENTAL }, { "pathexpand", WRDSF_PATHEXPAND }, { NULL, 0 } }; struct kwd opt_keytab[] = { { "nullglob", WRDSO_NULLGLOB }, { "failglob", WRDSO_FAILGLOB }, { "dotglob", WRDSO_DOTGLOB }, { NULL, 0 } }; struct kwd string_keytab[] = { { "delim", WRDSF_DELIM }, { "comment", WRDSF_COMMENT }, { "escape", WRDSF_ESCAPE }, { NULL, 0 } }; static int kwxlat (struct kwd *kwp, const char *str, int *res) { for (; kwp->name; kwp++) if (strcmp (kwp->name, str) == 0) { *res = kwp->tok; return 0; } return -1; } static void help () { size_t i; printf ("usage: %s [options] [VAR=VALUE...] [-- EXTRA...]\n", progname); printf ("options are:\n"); printf (" [-]trimnl\n"); printf (" [-]plaintext\n"); printf (" -env\n"); printf (" env sys|none|null\n"); putchar ('\n'); for (i = 0; bool_keytab[i].name; i++) printf (" [-]%s\n", bool_keytab[i].name); putchar ('\n'); for (i = 0; string_keytab[i].name; i++) { printf (" -%s\n", string_keytab[i].name); printf (" %s ARG\n", string_keytab[i].name); } printf (" escape-word ARG\n"); printf (" escape-quote ARG\n"); putchar ('\n'); for (i = 0; opt_keytab[i].name; i++) { printf (" [-]%s\n", opt_keytab[i].name); } putchar ('\n'); printf (" -dooffs\n"); printf (" dooffs COUNT ARGS...\n"); exit (0); } void print_qword (const char *word, int plaintext) { static char *qbuf = NULL; static size_t qlen = 0; int quote; size_t size = wordsplit_c_quoted_length (word, 0, "e); if (plaintext) { printf ("%s", word); return; } if (*word == 0) quote = 1; if (size >= qlen) { qlen = size + 1; qbuf = realloc (qbuf, qlen); assert (qbuf != NULL); } wordsplit_c_quote_copy (qbuf, word, 0); qbuf[size] = 0; if (quote) printf ("\"%s\"", qbuf); else printf ("%s", qbuf); } /* Convert environment to K/V form */ static char ** make_env_kv () { size_t i, j, size; char **newenv; /* Count the number of entries */ for (i = 0; environ[i]; i++) ; size = i * 2 + 1; newenv = calloc (size, sizeof (newenv[0])); assert (newenv != NULL); for (i = j = 0; environ[i]; i++) { size_t len = strcspn (environ[i], "="); char *p = malloc (len+1); assert (p != NULL); memcpy (p, environ[i], len); p[len] = 0; newenv[j++] = p; p = strdup (environ[i] + len + 1); assert (p != NULL); newenv[j++] = p; } newenv[j] = NULL; return newenv; } static int wsp_getvar (char **ret, const char *vptr, size_t vlen, void *data) { char **base = data; int i; for (i = 0; base[i]; i++) { size_t l = strcspn (base[i], "="); if (l == vlen && memcmp (base[i], vptr, vlen) == 0) { char *p = strdup (base[i] + vlen + 1); if (p == NULL) return WRDSE_NOSPACE; *ret = p; return WRDSE_OK; } } return WRDSE_UNDEF; } static int cmd_quote (char **ret, const char *str, size_t len, char **argv) { int alen; for (alen = 0; alen < len && !(str[alen] == ' ' || str[alen] == '\t'); alen++) ; for (; alen < len && (str[alen] == ' ' || str[alen] == '\t'); alen++) ; len -= alen; *ret = malloc (len + 1); if (!*ret) return WRDSE_NOSPACE; memcpy (*ret, str + alen, len); (*ret)[len] = 0; return WRDSE_OK; } static int cmd_words (char **ret, const char *str, size_t len, char **argv) { char *p; int i; p = malloc (len + 1); if (!p) return WRDSE_NOSPACE; *ret = p; for (i = 1; argv[i]; i++) { size_t s = strlen (argv[i]); if (i > 1) *p++ = ' '; memcpy (p, argv[i], s); p += s; } *p = 0; return WRDSE_OK; } static int cmd_lines (char **ret, const char *str, size_t len, char **argv) { char *p; int i; p = malloc (len + 1); if (!p) return WRDSE_NOSPACE; *ret = p; for (i = 1; argv[i]; i++) { size_t s = strlen (argv[i]); if (i > 1) *p++ = '\n'; memcpy (p, argv[i], s); p += s; } *p = 0; return WRDSE_OK; } static struct command { char const *name; int (*cmd)(char **ret, const char *str, size_t len, char **argv); } comtab[] = { { "quote", cmd_quote }, { "words", cmd_words }, { "lines", cmd_lines } }; static int wsp_runcmd (char **ret, const char *str, size_t len, char **argv, void *closure) { int i; size_t s = 0; for (i = 0; ; i++) { if (i == sizeof (comtab) / sizeof (comtab[0])) break; if (strcmp (comtab[i].name, argv[0]) == 0) return comtab[i].cmd (ret, str, len, argv); } *ret = NULL; if (grecs_asprintf (ret, &s, "unknown command: %s", argv[0])) return WRDSE_NOSPACE; else return WRDSE_USERERR; } enum env_type { env_none, env_null, env_sys }; struct kwd env_keytab[] = { { "none", env_none }, { "null", env_null }, { "sys", env_sys }, { NULL } }; static void set_escape_string (wordsplit_t *ws, int *wsflags, int q, const char *str) { if (*str == ':') { while (*++str != ':') { int f; switch (*str) { case '+': f = WRDSO_BSKEEP; break; case '0': f = WRDSO_OESC; break; case 'x': f = WRDSO_XESC; break; default: fprintf (stderr, "%s: invalid escape flag near %s\n", progname, str); abort (); } WRDSO_ESC_SET (ws, q, f); } *wsflags |= WRDSF_OPTIONS; ++str; } ws->ws_escape[q] = str; } int main (int argc, char **argv) { char buf[1024], *ptr, *saved_ptr; int i, offarg = 0; int trimnl_option = 0; int plaintext_option = 0; int wsflags = (WRDSF_DEFFLAGS & ~WRDSF_NOVAR) | WRDSF_ENOMEMABRT | WRDSF_SHOWERR; wordsplit_t ws; int next_call = 0; char *fenvbase[128]; size_t fenvidx = 0; size_t fenvmax = sizeof (fenvbase) / sizeof (fenvbase[0]); int use_env = env_sys; int appendc = 0; char **appendv = NULL; progname = argv[0]; ws.ws_options = 0; for (i = 1; i < argc; i++) { char *opt = argv[i]; int negate; int flag; if (opt[0] == '-') { if (opt[1] == '-' && opt[2] == 0) { appendc = argc - i - 1; appendv = argv + i + 1; break; } negate = 1; opt++; } else if (opt[0] == '+') { negate = 0; opt++; } else negate = 0; if (strcmp (opt, "h") == 0 || strcmp (opt, "help") == 0 || strcmp (opt, "-help") == 0) { help (); } if (strcmp (opt, "trimnl") == 0) { trimnl_option = !negate; continue; } if (strcmp (opt, "plaintext") == 0) { plaintext_option = !negate; continue; } if (strcmp (opt, "env") == 0) { if (negate) use_env = env_none; else { i++; if (i == argc) { fprintf (stderr, "%s: missing argument for env\n", progname); exit (1); } if (kwxlat (env_keytab, argv[i], &use_env)) { fprintf (stderr, "%s: invalid argument for env\n", progname); exit (1); } } continue; } if (kwxlat (bool_keytab, opt, &flag) == 0) { if (negate) wsflags &= ~flag; else wsflags |= flag; continue; } if (kwxlat (string_keytab, opt, &flag) == 0) { if (negate) wsflags &= ~flag; else { i++; if (i == argc) { fprintf (stderr, "%s: missing argument for %s\n", progname, opt); exit (1); } switch (flag) { case WRDSF_DELIM: ws.ws_delim = argv[i]; break; case WRDSF_COMMENT: ws.ws_comment = argv[i]; break; case WRDSF_ESCAPE: set_escape_string (&ws, &wsflags, 0, argv[i]); set_escape_string (&ws, &wsflags, 1, argv[i]); break; } wsflags |= flag; } continue; } if (strcmp (opt, "escape-word") == 0 || strcmp (opt, "escape-quote") == 0) { int q = opt[7] == 'q'; i++; if (i == argc) { fprintf (stderr, "%s: missing argument for %s\n", progname, opt); exit (1); } if (!(wsflags & WRDSF_ESCAPE)) { wsflags |= WRDSF_ESCAPE; ws.ws_escape[!q] = NULL; } set_escape_string (&ws, &wsflags, q, argv[i]); continue; } if (strcmp (opt, "dooffs") == 0) { if (negate) wsflags &= ~WRDSF_DOOFFS; else { char *p; i++; if (i == argc) { fprintf (stderr, "%s: missing arguments for %s\n", progname, opt); exit (1); } ws.ws_offs = strtoul (argv[i], &p, 10); if (*p) { fprintf (stderr, "%s: invalid number: %s\n", progname, argv[i]); exit (1); } i++; if (i + ws.ws_offs > argc) { fprintf (stderr, "%s: not enough arguments for %s\n", progname, opt); exit (1); } offarg = i; i += ws.ws_offs - 1; wsflags |= WRDSF_DOOFFS; } continue; } if (kwxlat (opt_keytab, opt, &flag) == 0) { wsflags |= WRDSF_OPTIONS; if (negate) ws.ws_options &= ~flag; else ws.ws_options |= flag; continue; } if (strcmp (opt, "maxwords") == 0) { char *p; wsflags |= WRDSF_OPTIONS; ws.ws_options |= WRDSO_MAXWORDS; i++; if (i == argc) { fprintf (stderr, "%s: missing arguments for %s\n", progname, opt); exit (1); } ws.ws_maxwords = strtoul (argv[i], &p, 10); if (*p) { fprintf (stderr, "%s: invalid number: %s\n", progname, argv[i]); exit (1); } continue; } if (strchr (opt, '=')) { assert (fenvidx < fenvmax - 1); fenvbase[fenvidx++] = opt; continue; } fprintf (stderr, "%s: unrecognized argument: %s\n", progname, opt); exit (1); } if (fenvidx) { fenvbase[fenvidx] = NULL; wsflags |= WRDSF_GETVAR | WRDSF_CLOSURE; ws.ws_getvar = wsp_getvar; ws.ws_closure = fenvbase; } switch (use_env) { case env_null: wsflags |= WRDSF_ENV; ws.ws_env = NULL; break; case env_none: break; case env_sys: wsflags |= WRDSF_ENV; if (wsflags & WRDSF_ENV_KV) ws.ws_env = (const char **) make_env_kv (); else ws.ws_env = (const char **) environ; break; } if (!(wsflags & WRDSF_NOCMD)) ws.ws_command = wsp_runcmd; if (wsflags & WRDSF_INCREMENTAL) trimnl_option = 1; next_call = 0; while ((ptr = fgets (buf, sizeof (buf), stdin))) { int rc; size_t i; if (trimnl_option) { size_t len = strlen (ptr); if (len && ptr[len-1] == '\n') ptr[len-1] = 0; } if (wsflags & WRDSF_INCREMENTAL) { if (next_call) { if (*ptr == 0) ptr = NULL; else free (saved_ptr); } else next_call = 1; if (ptr) { ptr = saved_ptr = strdup (ptr); assert (ptr != NULL); } } rc = wordsplit (ptr, &ws, wsflags); if (rc) { if (!(wsflags & WRDSF_SHOWERR)) wordsplit_perror (&ws); continue; } if (offarg) { for (i = 0; i < ws.ws_offs; i++) ws.ws_wordv[i] = argv[offarg + i]; offarg = 0; } if (appendc) { rc = wordsplit_append (&ws, appendc, appendv); if (rc) { if (!(wsflags & WRDSF_SHOWERR)) wordsplit_perror (&ws); continue; } } wsflags |= WRDSF_REUSE | (ws.ws_flags & WRDSF_ENV); printf ("NF: %lu", (unsigned long) ws.ws_wordc); if (wsflags & WRDSF_DOOFFS) printf (" (%lu)", (unsigned long) ws.ws_offs); putchar ('\n'); for (i = 0; i < ws.ws_offs; i++) { printf ("(%lu): ", (unsigned long) i); print_qword (ws.ws_wordv[i], plaintext_option); putchar ('\n'); } for (; i < ws.ws_offs + ws.ws_wordc; i++) { printf ("%lu: ", (unsigned long) i); print_qword (ws.ws_wordv[i], plaintext_option); putchar ('\n'); } printf ("TOTAL: %lu\n", (unsigned long) ws.ws_wordi); } return 0; }