From 7142994367871a0271b98242e72323362898f7d8 Mon Sep 17 00:00:00 2001 From: DTB Date: Fri, 26 Apr 2024 19:08:48 -0600 Subject: [PATCH] pg(1): start work on a quoted strtok(3) and internal environment --- src/pg.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 77 insertions(+), 11 deletions(-) diff --git a/src/pg.c b/src/pg.c index dfa21f9..5a912aa 100644 --- a/src/pg.c +++ b/src/pg.c @@ -6,7 +6,7 @@ * the terms of the GNU Affero General Public License as published by the Free * Software Foundation, either version 3 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 Affero General Public License for more @@ -42,6 +42,70 @@ static FILE *input; static char *prompt = ": "; static char *program_name = "pg"; +static char *permute_out(char *str, size_t index){ + size_t j; + + for(j = index; str[j - 1] != '\0'; ++j) + str[j - 1] = str[j]; + + return str; +} + +/* strtok(3p), but supports double-quotes and escapes (but only for escaping + * quotes). UTF-8 is safe only in str. Unmatched quotes in str are considered + * literal. The behavior of strtok_quoted when '"' or '\\' are in sep is + * undefined. */ +/* TODO: Seems to only ever return NULL. */ +static char *strtok_quoted(char *str, char *sep){ + static char *s; + size_t i; + size_t j; + + if(str != NULL) + s = str; + + while(strchr(sep, *s) == NULL) + if(*++s == '\0') + return NULL; /* no remaining characters except seperators */ + + { + char in_escape; + int in_quotes; + + in_escape = 0; + in_quotes = -1; + for(i = 0; s[i] != '\0'; ++i) + switch(s[i]){ + case '\\': + if(in_escape) + permute_out(s, i--); + in_escape = !in_escape; + break; + case '"': + if(in_escape){ + s[i] = s[i - 1]; + permute_out(s, i--); + }else if(in_quotes != -1){ + permute_out(s, in_quotes); + --i; + permute_out(s, i--); + in_quotes = -1; + }else + in_quotes = i; + break; + case '\0': + return s; + default: + if(!in_escape && strchr(sep, s[i]) != NULL){ + s[i] = '\0'; + return s; + } + } + } + + return s; +} + /* Page at most l bytes from f without consideration of a buffer (print them to * stdout). */ static int pg_b_u(FILE *f, size_t l){ @@ -66,8 +130,8 @@ static int pg_l_u(FILE *f, size_t l, char nl){ return c; } -static int cmd_quit(int argc, char **argv){ return EX_UNAVAILABLE; } -static int cmd_default_page(int argc, char **argv){ +static int cmd_quit(int argc, char **argv, char **envp){return EX_UNAVAILABLE;} +static int cmd_default_page(int argc, char **argv, char **envp){ if(argc > 1) /* This shouldn't be possible. */ return EX_USAGE; @@ -82,11 +146,11 @@ static int cmd_default_page(int argc, char **argv){ else return EX_SOFTWARE; } -static int cmd_page_down_lines(int argc, char **argv){ +static int cmd_page_down_lines(int argc, char **argv, char **envp){ switch(argc){ case 1: - return cmd_default_page(argc, argv); + return cmd_default_page(argc, argv, envp); case 2: /* not implemented */ default: fprintf(stderr, "Usage: %s" /*" (lines)"*/ "\n", argv[0]); @@ -95,13 +159,15 @@ static int cmd_page_down_lines(int argc, char **argv){ } /* A CmdMap must be NULL-terminated. */ -static struct CmdMap{ char *name; int (*fn)(int, char **); } builtins[] = { +static struct CmdMap{ char *name; int (*fn)(int, char **, char **); } +builtins[] = { { "", cmd_default_page }, { "+", cmd_page_down_lines }, /* don't make the user feel trapped */ - { "exit", cmd_quit }, { "q", cmd_quit }, { ":q", cmd_quit }, - { ":q!", cmd_quit }, { "quit", cmd_quit }, { "ZZ", cmd_quit }, + { ":q", cmd_quit }, { ":q!", cmd_quit }, + { "exit", cmd_quit }, { "q", cmd_quit }, { "Q", cmd_quit }, + { "quit", cmd_quit }, { "ZZ", cmd_quit }, { NULL, NULL } }; @@ -109,7 +175,7 @@ static struct CmdMap{ char *name; int (*fn)(int, char **); } builtins[] = { #define ARGV_MAX 10 /* Find and execute the command in the command map, given a corresponding * command line. */ -static int cmdline_exec(struct CmdMap *map, char *cmdline){ +static int cmdline_exec(struct CmdMap *map, char *cmdline, char **envp){ /* Command line word splitting is naive and based on whitespace ONLY; no * fancy quoting or escaping here. Adding that would (ideally) entail * replacing strtok(3) with something specific to this task. */ @@ -128,7 +194,7 @@ static int cmdline_exec(struct CmdMap *map, char *cmdline){ for(; map->name != NULL; map = &map[1]) if(strcmp(map->name, argv[0]) == 0) - return map->fn(argc, argv); + return map->fn(argc, argv, envp); fprintf(stderr, "%s: %s: not found\n", program_name, argv[0]); return EX_USAGE; @@ -182,7 +248,7 @@ int main(int argc, char *argv[]){ return EX_OK; { int r; - switch((r = cmdline_exec(builtins, (char *)cmd))){ + switch((r = cmdline_exec(builtins, (char *)cmd, NULL))){ case EX_OK: case EX_USAGE: break; case EX_UNAVAILABLE: return EX_OK; default: return r;