diff --git a/.gitignore b/.gitignore index 0e05920..98eb6f9 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ bake +lex.yy.c diff --git a/bake.1 b/bake.1 index 2420b18..d53d084 100644 --- a/bake.1 +++ b/bake.1 @@ -24,7 +24,7 @@ Options must always be put first, and short options may be merged together, nume \fB\-n \-\-dry\-run\fP, don't execute anything \fB\-l \-\-list\fP, lists available shell commands \fB\-s \-\-select\fP <\FBn\fP>, selects Nth shell command -\fB\-x \-\-expunge\fP, Removes what's specified in the expunge block +\fB\-x \-\-expunge\fP, Removes what's specified in the expunge block .PP Macros @@ -34,15 +34,17 @@ done by a leading backslash, which are NOT subtracted. These macros will expand to their counterpart before execution. .TP -.B @FILENAME, @NAME, $@ +.B @FILE, @FILENAME, @NAME, $@ returns target\-file (abc.x.txt) .TP .B @SHORT, $* returns target\-file without suffix (abc.x.txt \-> abc.x) +supports choice syntax, @SHORT:1 .TP .B @ARGS, $+ returns .B arguments +, supports choice syntax, @ARGS:0 returns the first argument, @ARGS:N so on. .TP .B @LINE returns the line number diff --git a/bake.l b/bake.l new file mode 100644 index 0000000..43dee33 --- /dev/null +++ b/bake.l @@ -0,0 +1,176 @@ +/* cbake.l @BAKE flex @FILE && cc -Wall -Wextra -std=c99 -D_GNU_SOURCE -o @SHORT lex.yy.c @ARGS -lfl @STOP */ +/* TODO: implement expunge, color */ +%{ +#include + +#undef ECHO +#define ECHO do { fprintf(stdout, yytext); if (g_pipe) { fprintf(g_pipe, yytext); } } while (0) +#define CHAR(c) do { fputc(c, stdout); if (g_pipe) { fputc(c, g_pipe); } } while (0) +#define STRING(s) do { fputs(s, stdout); if (g_pipe) { fputs(s, g_pipe); } } while (0) +#define FORMAT(...) do { fprintf(stdout, __VA_ARGS__); if (g_pipe) { fprintf(g_pipe, __VA_ARGS__); } } while (0) +#define FWRITE(str, len) do { fwrite(str, 1, len, stdout); if (g_pipe) { fwrite(str, 1, len, g_pipe); } } while (0) + +/* input from main to lexer */ +FILE * g_pipe; +char * g_filename; +int g_ac; +char ** g_av; +int g_select = 1; +/* for the lexers eyes only */ +int line = 1, nth = 0, expunge_depth = 0, first_nl, tmpline; + +extern void root(char * filename); +extern void args(int n); +extern void shorten(char * filename, int n); +%} + +SPACE [ \t\r\v\f] +MACROS (@BAKE|@FILENAME|@FILE|@NAME|@SHORT|@ARGS|@LINE|@STOP|$@|$*|$+) + +%x FOUND PADDING STOP +%option nodefault noinput nounput noyywrap +%% + +@BAKE[[:space:]] { bake: + first_nl = 1; + + if (yytext[yyleng-1] == '\n') { ++line; } + if (!g_select) { ; } + else if (g_select < 0) { BEGIN FOUND; printf("\n%s:%d:s%d: ", g_filename, line, ++nth); } + else if (!--g_select) { BEGIN FOUND; } +} + +\n { ++line; } +. {;} + +{ + @BAKE[[:space:]]|@STOP { BEGIN INITIAL; yyless(0); if (first_nl) { CHAR('\n'); } if (!g_select) { return 0; } } + @FILENAME|@FILE|@NAME|$@ { STRING(g_filename); } + @SHORT:[[:digit:]]+ { shorten(g_filename, atoi(strrchr(yytext, ':')+1)); } + @SHORT|$\* { shorten(g_filename, 1); } + @ARGS:[[:digit:]]+ { args(atoi(strrchr(yytext, ':')+1)); } + @ARGS|$\+ { args(-1); } + @LINE { FORMAT("%d", line); } + @\{ { ++expunge_depth; } + \} { if (!expunge_depth--) { ECHO; } } + \\\n { BEGIN PADDING; ++line; CHAR(' '); } + \\{MACROS} { STRING(yytext + 1); } + \n { CHAR('\n'); ++line; if (first_nl) { BEGIN STOP; first_nl = 0; tmpline = 0; } } + {SPACE} { BEGIN PADDING; CHAR(' '); } + . { ECHO; } +} + +{ + {SPACE} { ; } + .|\n { yyless(0); BEGIN FOUND; } +} + +{ + @BAKE[[:space:]] { line += tmpline; goto bake; } + @STOP { BEGIN FOUND; yyless(0); } + \n { ++tmpline; yymore(); } + .|\\@ { yymore(); } +} + +%% + +void root(char * filename) { + char * path, * terminator; + if (!(path = realpath(filename, NULL))) { return; } + if ((terminator = strrchr(path, '/'))) { + *terminator = '\0'; + chroot(path); + } + free(path); +} + +void args(int n) { + if (n < 0) { for (int i = 0; i < g_ac; ++i) { STRING(g_av[i]); if (i + 1 < g_ac) { CHAR(' '); } } } + else if (n < g_ac) { STRING(g_av[n]); } +} + +void shorten(char * filename, int n) { + char * end = filename + strlen(filename); + while (n && (end = memrchr(filename, '.', end - filename))) { --n; } + if (!end) { + fprintf(stderr, " context error: Argument out of range.\n"); + /* Ensures consistency. @SHORT will always return *something* that isn't filename */ + STRING("idiot"); + return; + } + FWRITE(filename, end - filename); +} + +void help(void) { fputs("see bake(1) - \"Buy high. Sell low.\"\n", stderr); } + +int main (int ac, char ** av) { + int run = 1; + char * av0 = av[0]; + FILE * fp; + + /* supports long/short, -allinone, (-X ... -X=... -X) */ + while (++av, --ac) { + size_t i; + if (av[0][0] != '-') { goto start; } + if (av[0][1] == '-') { + if (av[0][2] == '\0') { ++av, --ac; goto start; } + if (!strcmp(av[0]+2, "dry-run")) { i = strlen(av[0]); goto opt_dry_run; } + if (!strcmp(av[0]+2, "select" )) { if (!ac-1 || isdigit(av[1][0])) { goto opt_arg; } + ++av, --ac; i = strlen(av[0]); goto opt_select; } + if (!strcmp(av[0]+2, "list" )) { i = strlen(av[0]); goto opt_list; } + if (!strcmp(av[0]+2, "help" )) { goto opt_help; } + goto opt_default; + } + for (i = 1; i < strlen(av[0]); ++i) { + switch (av[0][i]) { + opt_dry_run: case 'n': run = 0; break; + case 's': + /* Covers cases -s -s */ + if (isdigit(av[0][i+1])) { g_select = atoi(av[0]+i+1); } + else if (ac > 1 && isdigit(av[1][0])) { ++av, --ac; opt_select: g_select = atoi(av[0]); } + else { g_select = 0; } + if (!g_select) { fprintf(stderr, "%s: Invalid argument for -s\n", av0); return 1; } + i = strlen(av[0]); + break; + opt_list: case 'l': run = 0; g_select = -1; break; + opt_help: case 'h': help(); return 0; + opt_default: default: fprintf(stderr, "%s: Unknown option '%s'\n", av0, av[0]); return 1; + opt_arg: fprintf(stderr, "%s: Argument missing for '%s'\n", av0, av[0]); return 1; + } + } + } + + start: + if (!ac) { fprintf(stderr, "%s: Missing filename\n", av0); return 1; } + if (!g_select) { goto out_of_range; } + + g_filename = av[0]; + root(g_filename); + { /* ensures the filename doesn't have a relative path that would misdirect the command within the new root */ + char * tmp = strrchr(g_filename, '/'); + if (tmp) { g_filename = tmp+1; } + } + + /* open and prepare ac, av */ + if (!(yyin = fp = fopen(g_filename, "rb"))) + { fprintf(stderr, "%s: '%s' %s\n", av0, g_filename, strerror(errno)); return 1; } + g_ac = --ac, g_av = ++av; + + /* Prepares our UNIX pipe for input */ + if (run) { + g_pipe = popen("/bin/sh -e", "w"); + if (!g_pipe) { fprintf(stderr, "%s: %s\n", av0, strerror(errno)); return 1; } + } + + if (g_select > 0) { fprintf(stderr, "%s: ", av0); fflush(stderr); } + yylex(); fflush(stdout); + fclose(fp); + if (g_select > 0) { pclose(g_pipe); goto out_of_range; } + + if (!run) { return 0; } + fprintf(stderr, "output: "); fflush(stderr); + run = pclose(g_pipe); /* repurposed run */ + if (run) { printf("%s: Exit code %d\n", av0, run); } + return run; + out_of_range: fprintf(stderr, "%s: <%d> Out of range\n", av0, g_select); return 1; +} diff --git a/install.sh b/install.sh index 29da975..f591f0e 100755 --- a/install.sh +++ b/install.sh @@ -2,14 +2,12 @@ # source install TARGET=${TARGET:-/usr/local} -INSTALL=${INSTALL:-bake shake} +INSTALL=${INSTALL:-bake} -cd $(dirname "$(readlink -f "$0")")/src -chmod +x shake +cd "$(dirname "$(readlink -f $0)")" -./shake bake.c -s $@ && \ +./shake bake.l -s $@ && \ mkdir $TARGET/bin $TARGET/man/man1 -p && \ install -m 755 $INSTALL $TARGET/bin -gzip -c bake.1 > $TARGET/man/man1/bake.1.gz && \ -ln -f -s $TARGET/man/man1/bake.1.gz $TARGET/man/man1/shake.1.gz +gzip -c bake.1 > $TARGET/man/man1/bake.1.gz diff --git a/source/shake b/shake similarity index 97% rename from source/shake rename to shake index cfeda79..8407a54 100755 --- a/source/shake +++ b/shake @@ -79,6 +79,8 @@ if [[ -n $line ]]; then line=${line//@FILENAME/$input_file} line=${line//@SHORT/${input_file%.*}} line=${line//@ARGS/$@} + line=${line//@NAME/$input_file} + line=${line//@FILE/$input_file} line=$(echo "$line" | sed 's/@STOP.*//') echo -e "${BOLD}${GREEN}$0${NORMAL}: ${line#*${MARK}}" diff --git a/source/bake.c b/source/bake.c deleted file mode 100644 index bf65e43..0000000 --- a/source/bake.c +++ /dev/null @@ -1,392 +0,0 @@ -/* @BAKE cc -std=c99 -O2 -Wall -Wextra -Wpedantic -Wno-implicit-fallthrough -o @SHORT @FILENAME @ARGS @STOP */ -/* @BAKE cc -std=c99 -O2 -Wall -Wextra -Wpedantic -Wno-implicit-fallthrough -o @SHORT $+ @STOP */ -#define _GNU_SOURCE - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define BUFFER_SIZE (1 << 12) - -#define START "@" "BAKE" " " -#define STOP "@" "STOP" - -#define AUTONOMOUS_COMPILE 0 - -#define ENABLE_COLOR 1 - -#if ENABLE_COLOR == 1 -# define RED "\033[91m" -# define GREEN "\033[92m" -# define YELLOW "\033[93m" -# define BOLD "\033[1m" -# define RESET "\033[0m" -#elif ENABLE_COLOR -# define RED "\033[91;5m" -# define GREEN "\033[96;7m" -# define YELLOW "\033[94m" -# define BOLD "\033[1;4m" -# define RESET "\033[0m" -#else -# define RED -# define GREEN -# define YELLOW -# define BOLD -# define RESET -#endif - -#define ARRLEN(x) (sizeof (x) / sizeof (x [0])) - -__attribute__((__section__(".text"))) static volatile char autonomous_compile [] = - "@BAKE cc -std=c99 -O2 -Wall -Wextra -Wpedantic -Wno-implicit-fallthrough -o @SHORT @FILENAME.c @ARGS @STOP"; - -static void -swap (char * a, char * b) { - *a ^= *b; - *b ^= *a; - *a ^= *b; -} - -static int -root (char ** rootp) { - char x [1] = {'\0'}; - char * root = *rootp; - size_t len = strlen (root); - int ret; - while (len && root [len] != '/') { --len; } - if (!len) { return 0; } - swap (root + len, x); - ret = chdir (root); - swap (root + len, x); - *rootp += len + 1; - return ret; -} - -static char * -shorten (char * filename) { - size_t i, last, len; - static char sh [FILENAME_MAX]; - len = strlen (filename); - for (last = i = 0; i < len; ++i) { - if (filename [i] == '.') { last = i; } - } - last = last ? last : i; - strncpy (sh, filename, last); - sh [last] = '\0'; - return sh; -} - -static char * -all_args (size_t argc, char ** argv) { - static char buffer [BUFFER_SIZE] = {0}; - if (argc > 0) { - size_t i, len = argc; - for (i = 0; i < argc; ++i) { len += strlen (argv [i]); } - buffer [len] = '\0'; - for (len = 0, i = 0; i < argc; ++i) { - strcpy (buffer + len, argv [i]); - len += strlen (argv [i]) + 1; - if (i + 1 < argc) { buffer [len - 1] = ' '; } - }} - return buffer; -} - -static size_t -lines (char * buffer, size_t off) { - size_t line = 1; - char * end = buffer + off; - while (buffer < end) { - if (*buffer == '\n') { ++line; } - ++buffer; - } - return line; -} - -static char * -expand_buffer(char * expanded, char * buffer, size_t length, char ** pairs, size_t count) { - size_t old, new, i, f, off = 0; - /* I need to test the bounds checking here, it'll crash normally if this provision doesn't do anything, though. */ - length &= (1 << 12) - 1; - for (f = 0; f < length; ++f) { - for (i = 0; i < count; i += 2) { - old = strlen (pairs [i]); - new = strlen (pairs [i + 1]); - if (memcmp (buffer + f - off, pairs [i], old) == 0) { - if (f && buffer [f - off - 1] == '\\') - { --f; --off; break; } - memcpy (expanded + f, pairs [i + 1], new); - f += new; - length += new - old; - off += new - old; - break; - }} - expanded [f] = buffer [f - off]; - } - expanded [f] = '\0'; - return expanded; -} - -static char * -expand (char * buffer, size_t length, char ** pairs, size_t count) { - static char expanded [BUFFER_SIZE] = {0}; - return expand_buffer (expanded, buffer, length, pairs, count); -} - -static char * -expunge (char * to, char * from, size_t length, char ** pair, size_t count) { - size_t i, f; - (void)count; /* count isn't actually used... */ - static char expunge_me [FILENAME_MAX] = {0}; - for (i = 0; i < length; ++i) { - if (memcmp(from + i, pair[0], strlen(pair[0])) == 0) { - if (from[i - 1] == '\\') { --i; memmove(from + i, from + i + 1, length - i); i+=1+strlen(pair[0]); --length; continue; } - for (f = i + strlen(pair[0]); f < length; ++f) { - if (memcmp(from + f, pair[1], strlen(pair[1])) == 0) { - if (from[f - 1] == '\\') { --f; memmove(from + f, from + f + 1, length - f); --length; continue; } - memmove(to + i, from + i + strlen(pair[0]), length - i - strlen(pair[0])); - memmove(to + f - strlen(pair[1]), from + f - 1, length - f); - /* memcpy(to + f - 2, " ", strlen(pair[1])); */ - memcpy(expunge_me, to + i, f - i - 1 - strlen(pair[1])); - goto end; - } - } - } - } -end: - return expunge_me; -} - -static char * -getmap (char * filename, size_t * length) { - char * buffer = NULL; - int fd = open (filename, O_RDONLY); - if (fd != -1) { - struct stat s; - if (!fstat (fd, &s) - && s.st_mode & S_IFREG - && s.st_size) { - *length = (size_t) s.st_size; - buffer = (char *) mmap (NULL, s.st_size, PROT_READ, MAP_SHARED, fd, 0); - } - close (fd); - } - return buffer; -} - -# define color_printf(...) color_fprintf (stdout, __VA_ARGS__) -/* not perfect, too simple, doesn't work with a var, only a literal. */ -# define color_fputs(fp, msg) color_fprintf (fp, msg "\n") -# define color_puts(msg) color_fputs (stdout, msg) - -static int color = ENABLE_COLOR; - -static void -color_fprintf (FILE * fp, char * format, ...) { - va_list ap; - char * buf; - - va_start (ap, format); - - if (color) { - vfprintf (fp, format, ap); - va_end (ap); - return; - } - vasprintf (&buf, format, ap); - - if (buf) { - char * expanded, * colors [] = { - YELLOW, "", - GREEN, "", - RED, "", - BOLD, "", - RESET, "" - }; - size_t count = ARRLEN (colors); - expanded = expand (buf, strlen (buf), colors, count - 2); - expanded = expand (expanded, strlen (buf), colors + count - 2, 2); - fwrite (expanded, strlen (expanded), 1, fp); - } - - free (buf); - va_end (ap); -} - -/* -- */ - -int main (int argc, char ** argv) { - void help (void); - int off, run = 1, list = 0, ex = 0, select_input, select = 1; - size_t length, begin = 0, end = 0; - pid_t pid; - char line [10], expanded [BUFFER_SIZE], * buffer, * expandedp, * args, * shortened, * filename = NULL, - * paren [] = { "@{", "}" }, * expunge_me = NULL, * bake_begin = START, * bake_end = STOP; - - /* opts */ - for (off = 1; off < argc; ++off) { - if (argv [off][0] != '-') { filename = argv [off]; ++off; break; } - if (argv [off][1] != '-') { - while (*(++argv [off])) - switch (argv [off][0]) { - select: case 's': - select = atoi (argv [off] + 1); - if (!select) { - ++off; - if (off >= argc) { help (); } - select = atoi (argv [off]); - } - if (select) { goto next; } - default: case 'h': help (); - case 'l': list = 1; - case 'n': run = 0; break; - case 'c': color = 0; break; - case 'x': ex = 1; break; - case 'q': fclose(stderr); fclose(stdout); break; - } - continue; - } - argv[off] += 2; - if (strcmp(argv[off], "select") == 0) { goto select; } - else if (strcmp(argv[off], "list") == 0) { list = 1; run = 0; } - else if (strcmp(argv[off], "dry-run") == 0) { run = 0; } - else if (strcmp(argv[off], "color") == 0) { color = 0; } - else if (strcmp(argv[off], "expunge") == 0) { ex = 1; } - else if (strcmp(argv[off], "quiet") == 0) { fclose(stderr); fclose(stdout); } - else if (strcmp(argv[off], "help") == 0) { help(); } - next:; - } - if (argc == 1 || !filename) { help (); } - select_input = select; - /* roots to directory of filename and mutates filename with the change */ - root (&filename); - - buffer = getmap (filename, &length); - if (!buffer) { - color_fprintf (stderr, RED "%s" RESET ": Could not access '" BOLD "%s" RESET "'\n", argv [0], filename); - return 1; - } - for (begin = 0; (list || select) && begin < length - strlen (bake_begin); ++begin) { - if (memcmp (buffer + begin, bake_begin, strlen (bake_begin)) == 0) { - size_t stop = begin; - end = begin; - again: while (end < length && buffer[end] != '\n') { ++end; } - if (buffer[end - 1] == '\\') { ++end; goto again; } - while (stop < length - strlen(bake_end)) { - if (memcmp(buffer + stop, bake_end, strlen(bake_end)) == 0) { - if (stop && buffer[stop - 1] == '\\') { ++stop; continue; } - end = stop; - break; - } - ++stop; - } - if (list) { - color_printf (GREEN "%d,%d" RESET ": " BOLD, select++, lines (buffer, begin)); - fwrite (buffer + begin, 1, end - begin, stdout); - color_puts (RESET); - } else { --select; } - }} - begin += strlen (bake_begin) - 1; - - if (list) { return 0; } - if (end < begin) { - color_fprintf (stderr, RED "%s" RESET ": " BOLD "%d" RESET " is out of range\n", argv [0], select_input); - return 1; - } - - /* expansion */ - - args = all_args (argc - off, argv + off); - shortened = shorten (filename); - char * pair [] = { - "@FILENAME", filename, - "@FILE", filename, - "$@", filename, - "@SHORT", shortened, - "$*", shortened, - "@ARGS", args, - "$+", args, - "@LINE", line - }; - snprintf (line, 16, "%lu", lines (buffer, begin)); - expandedp = expand (buffer + begin, end - begin, pair, ARRLEN (pair)); - memcpy(expanded, expandedp, BUFFER_SIZE); - expunge_me = expunge (expanded, expanded, strlen(expanded), paren, ARRLEN (paren)); - munmap (buffer, length); - - /* print and execute */ - color_fprintf (stderr, GREEN "%s" RESET ": " BOLD "%s" RESET, argv [0], expanded); - if (expanded[strlen(expanded)] != '\n') { puts(""); } - - if (ex) { - color_fprintf (stderr, GREEN "%s" RESET ": removing '%s'\n", argv [0], expunge_me); - char * jin = ".c"; - if (expunge_me - && run - && /* just in case */ - memcmp(expunge_me + strlen(expunge_me) - strlen(jin), jin, strlen(jin)) != 0) - { remove(expunge_me); } - return 0; - } - - if (!run) { return 0; } - - color_fprintf (stderr, GREEN "output" RESET ":\n"); - fflush(stdout); - - if ((pid = fork ()) == 0) { - execl ("/bin/sh", "sh", "-c", expanded, NULL); - return 0; /* execl overwrites the process anyways */ - } - - if (pid == -1) { - color_fprintf (stderr, GREEN "%s" RESET ": %s, %s\n", - argv [0], "Fork Error", strerror (errno)); - return 1; - } - - /* reuse of run as status return */ - if (waitpid (pid, &run, 0) < 0) { - color_fprintf (stderr, GREEN "%s" RESET ": " RED "%s" RESET ", %s\n", - argv [0], "Wait PID Error", strerror (errno)); - return 1; - } - if (!WIFEXITED (run)) { - return 1; - } - - return WEXITSTATUS (run); -} - -void help (void) { - char * help = - BOLD "bake [-chln] [-s ] [ARGS...]; version 20240804\n\n" RESET - GREEN "Bake" RESET " is a simple tool meant to execute embedded shell commands within\n" - "any file. It executes with /bin/sh the command after a \"" BOLD "@BAKE" RESET " \" to\n" - "the end of the line (a UNIX newline: '\\n').\n\n" - "It expands some macros,\n" - YELLOW "\t@NAME " RESET "- filename\n" - YELLOW "\t@SHORT " RESET "- shortened filename\n" - YELLOW "\t@ARGS " RESET "- other arguments to the program\n" - YELLOW "\t@LINE " RESET "- line number at the selected " BOLD "@BAKE" RESET "\n\n" - "All macros can be exempted by prefixing them with a backslash,\n" - "which'll be subtracted in the expansion. multi-line commands may be\n" - "done by a leading backslash, which are NOT subtracted.\n\n" - "It has five options, this message (-h, --help); prevents the execution\n" - "of the shell command (-n, --dry-run); disable color (-c, --color); list\n" - "(-l, --list) and select (-s, --select ) which respectively lists\n" - "all " BOLD "@BAKE" RESET " commands and select & run the Nth command.\n\n" - "It roots the shell execution in the directory of the given file.\n" - "Expunge with @{filename...}, using (-x, --expunge), \\@{} or @{\\}}.\n\n" - "Licensed under the public domain.\n"; - color_printf ("%s", help); - exit (1); -} diff --git a/source/bake2.c b/source/bake2.c deleted file mode 100644 index 539b211..0000000 --- a/source/bake2.c +++ /dev/null @@ -1,192 +0,0 @@ -/* @BAKE cc -I. -std=c99 -O2 -Wall -Wextra -Wpedantic -Wno-parentheses -Wno-implicit-fallthrough -o @SHORT sds.c @FILENAME @ARGS @STOP */ -#define _GNU_SOURCE - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#define ARRLEN(x) (sizeof (x) / sizeof (x [0])) - -#define START "@" "BAKE" " " -#define STOP "@" "STOP" - -#define ENABLE_COLOR 1 - -#include "color.h" - -static int map(char * filename, char ** buffer, size_t * buffer_length) { - int err = 0, fd = open(filename, O_RDONLY); - if (fd != -1) { - struct stat s; - if (!fstat(fd, &s) && s.st_mode & S_IFREG && s.st_size) { - *buffer_length = (size_t) s.st_size; - *buffer = (char *) mmap(NULL, s.st_size, PROT_READ, MAP_SHARED, fd, 0); - } else { - err = 1; - } - close(fd); - } - return err; -} - -void root(const char * filename) { - char * string = strdupa(filename), * terminator = strrchr(string, '/'); - if (terminator) { - terminator = '\0'; - chdir(string); - } -} - -void err(char * msg) { fprintf(stderr, "%s\n", msg); abort(); } - -void help(void) { abort(); } - - -void selection_list(char * buffer, size_t buffer_length, sds ** rlist, size_t * rlist_length) { - sds * list = malloc(0); - size_t list_length = 0; - - char * search = buffer; - char * start; - while (start = memmem(search, buffer_length, START, strlen(START))) { - buffer_length -= start - buffer + strlen(START); - search = start + strlen(START); - char * end = memmem(search, buffer_length, STOP, strlen(STOP)); - if (!end) { end = memchr(search, '\n', buffer_length);} - if (start && end) { - list = realloc(list, ++list_length * sizeof(sds)); - list[list_length-1] = sdsnewlen(start,end-start); - } - } - - *rlist = list; - *rlist_length = list_length; -} - -int main(int argc, char ** argv) { - char * filename = NULL; - int run = 1, select = 0, olist = 0; - - for (int off = 1; off < argc; ++off) { - if (argv [off][0] != '-') { filename = argv [off]; ++off; break; } - if (argv [off][1] != '-') { - while (*(++argv [off])) - switch (argv [off][0]) { - select: case 's': - select = atoi(argv [off] + 1); - if (!select) { - ++off; - if (off >= argc) { help(); } - select = atoi(argv [off]); - } - if (select) { goto next; } - default: case 'h': help(); - case 'l': olist = 1; - case 'n': run = 0; break; - /* case 'c': color = 0; break; */ - /* case 'x': ex = 1; break; */ - case 'q': fclose(stderr); fclose(stdout); break; - } - continue; - } - argv[off] += 2; - if (strcmp(argv[off], "select") == 0) { goto select; } - else if (strcmp(argv[off], "list") == 0) { olist = 1; run = 0; } - else if (strcmp(argv[off], "dry-run") == 0) { run = 0; } - /* else if (strcmp(argv[off], "color") == 0) { color = 0; } */ - /* else if (strcmp(argv[off], "expunge") == 0) { ex = 1; } */ - else if (strcmp(argv[off], "quiet") == 0) { fclose(stderr); fclose(stdout); } - else if (strcmp(argv[off], "help") == 0) { help(); } - next:; - } - - if (argc == 1 || !filename) { help(); } - - root(filename); - - char * buffer = NULL; - size_t buffer_length = 0; - - if (map(filename, &buffer, &buffer_length) || !buffer) { err("Could not access file"); } - - /* select */ - sds * list; - size_t list_length; - selection_list(buffer, buffer_length, &list, &list_length); - - if (olist) { - for (size_t i = 0; i < list_length; ++i) { - printf("%s\n", list[i]); - }} - - for (size_t i = 0; i < list_length; ++i) { - if (olist || (size_t) select != i) { sdsfree(list[i]); } - } - - sds command = list[select]; - free(list); - - if (olist) { return 0; } - - /* expand */ - - char * macro [] = { - "$*", - "$+", - "$@", - "@ARGS", - "@FILE", - "@FILENAME", - "@LINE" - "@SHORT", - }; - - char * search; - size_t search_length = sdslen(command); - for (size_t i = 0; i < ARRLEN(macro); ++i) { - search = command; - while (search = memmem(search, search_length, macro[i], strlen(macro[i]))) { - search += strlen(macro[i]); - printf("Found %s\n", macro[i]); - } - } - - /* execute */ - printf("command: '%s'\n", command); - sdsfree(command); - - if (!run) { return 0; } - - fprintf (stderr, GREEN "output" RESET ":\n"); - - pid_t pid; - if ((pid = fork ()) == 0) { - execl ("/bin/sh", "sh", "-c", command, NULL); - return 0; /* execl overwrites the process anyways */ - } - - if (pid == -1) { - fprintf (stderr, GREEN "%s" RESET ": %s, %s\n", argv [0], "Fork Error", strerror (errno)); - return 1; - } - - /* reuse of run as status return */ - if (waitpid (pid, &run, 0) < 0) { - fprintf (stderr, GREEN "%s" RESET ": " RED "%s" RESET ", %s\n", argv [0], "Wait PID Error", strerror (errno)); - return 1; - } - - if (!WIFEXITED (run)) { - return 1; - } - - return WEXITSTATUS (run); -} diff --git a/source/color.h b/source/color.h deleted file mode 100644 index 769d2ad..0000000 --- a/source/color.h +++ /dev/null @@ -1,56 +0,0 @@ -/* static int color = ENABLE_COLOR; */ - -#if ENABLE_COLOR -# define RED "\033[91m" -# define GREEN "\033[92m" -# define YELLOW "\033[93m" -# define BOLD "\033[1m" -# define RESET "\033[0m" -#else -# define RED -# define GREEN -# define YELLOW -# define BOLD -# define RESET -#endif - -#if 0 - -# define color_printf(...) color_fprintf (stdout, __VA_ARGS__) -/* not perfect, too simple, doesn't work with a var, only a literal. */ -# define color_fputs(fp, msg) color_fprintf (fp, msg "\n") -# define color_puts(msg) color_fputs (stdout, msg) - -static void -color_fprintf (FILE * fp, char * format, ...) { - va_list ap; - char * buf; - - va_start (ap, format); - - if (color) { - vfprintf (fp, format, ap); - va_end (ap); - return; - } - vasprintf (&buf, format, ap); - - if (buf) { - /* char * expanded, * colors [] = { */ - /* YELLOW, "", */ - /* GREEN, "", */ - /* RED, "", */ - /* BOLD, "", */ - /* RESET, "" */ - /* }; */ - /* size_t count = ARRLEN (colors); */ - /* expanded = expand (buf, strlen (buf), colors, count - 2); */ - /* expanded = expand (expanded, strlen (buf), colors + count - 2, 2); */ - /* fwrite (expanded, strlen (expanded), 1, fp); */ - } - - free (buf); - va_end (ap); -} - -#endif diff --git a/source/sds.c b/source/sds.c deleted file mode 100644 index 3a7eae7..0000000 --- a/source/sds.c +++ /dev/null @@ -1,1328 +0,0 @@ -/* SDSLib 2.0 -- A C dynamic strings library - * - * Copyright (c) 2006-2015, Salvatore Sanfilippo - * Copyright (c) 2015, Oran Agra - * Copyright (c) 2015, Redis Labs, Inc - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include -#include -#include -#include -#include -#include "sds.h" -#include "sdsalloc.h" - -const char *SDS_NOINIT = "SDS_NOINIT"; - -static inline int sdsHdrSize(char type) { - switch(type&SDS_TYPE_MASK) { - case SDS_TYPE_5: - return sizeof(struct sdshdr5); - case SDS_TYPE_8: - return sizeof(struct sdshdr8); - case SDS_TYPE_16: - return sizeof(struct sdshdr16); - case SDS_TYPE_32: - return sizeof(struct sdshdr32); - case SDS_TYPE_64: - return sizeof(struct sdshdr64); - } - return 0; -} - -static inline char sdsReqType(size_t string_size) { - if (string_size < 1<<5) - return SDS_TYPE_5; - if (string_size < 1<<8) - return SDS_TYPE_8; - if (string_size < 1<<16) - return SDS_TYPE_16; -#if (LONG_MAX == LLONG_MAX) - if (string_size < 1ll<<32) - return SDS_TYPE_32; - return SDS_TYPE_64; -#else - return SDS_TYPE_32; -#endif -} - -/* Create a new sds string with the content specified by the 'init' pointer - * and 'initlen'. - * If NULL is used for 'init' the string is initialized with zero bytes. - * If SDS_NOINIT is used, the buffer is left uninitialized; - * - * The string is always null-terminated (all the sds strings are, always) so - * even if you create an sds string with: - * - * mystring = sdsnewlen("abc",3); - * - * You can print the string with printf() as there is an implicit \0 at the - * end of the string. However the string is binary safe and can contain - * \0 characters in the middle, as the length is stored in the sds header. */ -sds sdsnewlen(const void *init, size_t initlen) { - void *sh; - sds s; - char type = sdsReqType(initlen); - /* Empty strings are usually created in order to append. Use type 8 - * since type 5 is not good at this. */ - if (type == SDS_TYPE_5 && initlen == 0) type = SDS_TYPE_8; - int hdrlen = sdsHdrSize(type); - unsigned char *fp; /* flags pointer. */ - - sh = s_malloc(hdrlen+initlen+1); - if (sh == NULL) return NULL; - if (init==SDS_NOINIT) - init = NULL; - else if (!init) - memset(sh, 0, hdrlen+initlen+1); - s = (char*)sh+hdrlen; - fp = ((unsigned char*)s)-1; - switch(type) { - case SDS_TYPE_5: { - *fp = type | (initlen << SDS_TYPE_BITS); - break; - } - case SDS_TYPE_8: { - SDS_HDR_VAR(8,s); - sh->len = initlen; - sh->alloc = initlen; - *fp = type; - break; - } - case SDS_TYPE_16: { - SDS_HDR_VAR(16,s); - sh->len = initlen; - sh->alloc = initlen; - *fp = type; - break; - } - case SDS_TYPE_32: { - SDS_HDR_VAR(32,s); - sh->len = initlen; - sh->alloc = initlen; - *fp = type; - break; - } - case SDS_TYPE_64: { - SDS_HDR_VAR(64,s); - sh->len = initlen; - sh->alloc = initlen; - *fp = type; - break; - } - } - if (initlen && init) - memcpy(s, init, initlen); - s[initlen] = '\0'; - return s; -} - -/* Create an empty (zero length) sds string. Even in this case the string - * always has an implicit null term. */ -sds sdsempty(void) { - return sdsnewlen("",0); -} - -/* Create a new sds string starting from a null terminated C string. */ -sds sdsnew(const char *init) { - size_t initlen = (init == NULL) ? 0 : strlen(init); - return sdsnewlen(init, initlen); -} - -/* Duplicate an sds string. */ -sds sdsdup(const sds s) { - return sdsnewlen(s, sdslen(s)); -} - -/* Free an sds string. No operation is performed if 's' is NULL. */ -void sdsfree(sds s) { - if (s == NULL) return; - s_free((char*)s-sdsHdrSize(s[-1])); -} - -/* Set the sds string length to the length as obtained with strlen(), so - * considering as content only up to the first null term character. - * - * This function is useful when the sds string is hacked manually in some - * way, like in the following example: - * - * s = sdsnew("foobar"); - * s[2] = '\0'; - * sdsupdatelen(s); - * printf("%d\n", sdslen(s)); - * - * The output will be "2", but if we comment out the call to sdsupdatelen() - * the output will be "6" as the string was modified but the logical length - * remains 6 bytes. */ -void sdsupdatelen(sds s) { - size_t reallen = strlen(s); - sdssetlen(s, reallen); -} - -/* Modify an sds string in-place to make it empty (zero length). - * However all the existing buffer is not discarded but set as free space - * so that next append operations will not require allocations up to the - * number of bytes previously available. */ -void sdsclear(sds s) { - sdssetlen(s, 0); - s[0] = '\0'; -} - -/* Enlarge the free space at the end of the sds string so that the caller - * is sure that after calling this function can overwrite up to addlen - * bytes after the end of the string, plus one more byte for nul term. - * - * Note: this does not change the *length* of the sds string as returned - * by sdslen(), but only the free buffer space we have. */ -sds sdsMakeRoomFor(sds s, size_t addlen) { - void *sh, *newsh; - size_t avail = sdsavail(s); - size_t len, newlen, reqlen; - char type, oldtype = s[-1] & SDS_TYPE_MASK; - int hdrlen; - - /* Return ASAP if there is enough space left. */ - if (avail >= addlen) return s; - - len = sdslen(s); - sh = (char*)s-sdsHdrSize(oldtype); - reqlen = newlen = (len+addlen); - if (newlen < SDS_MAX_PREALLOC) - newlen *= 2; - else - newlen += SDS_MAX_PREALLOC; - - type = sdsReqType(newlen); - - /* Don't use type 5: the user is appending to the string and type 5 is - * not able to remember empty space, so sdsMakeRoomFor() must be called - * at every appending operation. */ - if (type == SDS_TYPE_5) type = SDS_TYPE_8; - - hdrlen = sdsHdrSize(type); - assert(hdrlen + newlen + 1 > reqlen); /* Catch size_t overflow */ - if (oldtype==type) { - newsh = s_realloc(sh, hdrlen+newlen+1); - if (newsh == NULL) return NULL; - s = (char*)newsh+hdrlen; - } else { - /* Since the header size changes, need to move the string forward, - * and can't use realloc */ - newsh = s_malloc(hdrlen+newlen+1); - if (newsh == NULL) return NULL; - memcpy((char*)newsh+hdrlen, s, len+1); - s_free(sh); - s = (char*)newsh+hdrlen; - s[-1] = type; - sdssetlen(s, len); - } - sdssetalloc(s, newlen); - return s; -} - -/* Reallocate the sds string so that it has no free space at the end. The - * contained string remains not altered, but next concatenation operations - * will require a reallocation. - * - * After the call, the passed sds string is no longer valid and all the - * references must be substituted with the new pointer returned by the call. */ -sds sdsRemoveFreeSpace(sds s) { - void *sh, *newsh; - char type, oldtype = s[-1] & SDS_TYPE_MASK; - int hdrlen, oldhdrlen = sdsHdrSize(oldtype); - size_t len = sdslen(s); - size_t avail = sdsavail(s); - sh = (char*)s-oldhdrlen; - - /* Return ASAP if there is no space left. */ - if (avail == 0) return s; - - /* Check what would be the minimum SDS header that is just good enough to - * fit this string. */ - type = sdsReqType(len); - hdrlen = sdsHdrSize(type); - - /* If the type is the same, or at least a large enough type is still - * required, we just realloc(), letting the allocator to do the copy - * only if really needed. Otherwise if the change is huge, we manually - * reallocate the string to use the different header type. */ - if (oldtype==type || type > SDS_TYPE_8) { - newsh = s_realloc(sh, oldhdrlen+len+1); - if (newsh == NULL) return NULL; - s = (char*)newsh+oldhdrlen; - } else { - newsh = s_malloc(hdrlen+len+1); - if (newsh == NULL) return NULL; - memcpy((char*)newsh+hdrlen, s, len+1); - s_free(sh); - s = (char*)newsh+hdrlen; - s[-1] = type; - sdssetlen(s, len); - } - sdssetalloc(s, len); - return s; -} - -/* Return the total size of the allocation of the specified sds string, - * including: - * 1) The sds header before the pointer. - * 2) The string. - * 3) The free buffer at the end if any. - * 4) The implicit null term. - */ -size_t sdsAllocSize(sds s) { - size_t alloc = sdsalloc(s); - return sdsHdrSize(s[-1])+alloc+1; -} - -/* Return the pointer of the actual SDS allocation (normally SDS strings - * are referenced by the start of the string buffer). */ -void *sdsAllocPtr(sds s) { - return (void*) (s-sdsHdrSize(s[-1])); -} - -/* Increment the sds length and decrements the left free space at the - * end of the string according to 'incr'. Also set the null term - * in the new end of the string. - * - * This function is used in order to fix the string length after the - * user calls sdsMakeRoomFor(), writes something after the end of - * the current string, and finally needs to set the new length. - * - * Note: it is possible to use a negative increment in order to - * right-trim the string. - * - * Usage example: - * - * Using sdsIncrLen() and sdsMakeRoomFor() it is possible to mount the - * following schema, to cat bytes coming from the kernel to the end of an - * sds string without copying into an intermediate buffer: - * - * oldlen = sdslen(s); - * s = sdsMakeRoomFor(s, BUFFER_SIZE); - * nread = read(fd, s+oldlen, BUFFER_SIZE); - * ... check for nread <= 0 and handle it ... - * sdsIncrLen(s, nread); - */ -void sdsIncrLen(sds s, ssize_t incr) { - unsigned char flags = s[-1]; - size_t len; - switch(flags&SDS_TYPE_MASK) { - case SDS_TYPE_5: { - unsigned char *fp = ((unsigned char*)s)-1; - unsigned char oldlen = SDS_TYPE_5_LEN(flags); - assert((incr > 0 && oldlen+incr < 32) || (incr < 0 && oldlen >= (unsigned int)(-incr))); - *fp = SDS_TYPE_5 | ((oldlen+incr) << SDS_TYPE_BITS); - len = oldlen+incr; - break; - } - case SDS_TYPE_8: { - SDS_HDR_VAR(8,s); - assert((incr >= 0 && sh->alloc-sh->len >= incr) || (incr < 0 && sh->len >= (unsigned int)(-incr))); - len = (sh->len += incr); - break; - } - case SDS_TYPE_16: { - SDS_HDR_VAR(16,s); - assert((incr >= 0 && sh->alloc-sh->len >= incr) || (incr < 0 && sh->len >= (unsigned int)(-incr))); - len = (sh->len += incr); - break; - } - case SDS_TYPE_32: { - SDS_HDR_VAR(32,s); - assert((incr >= 0 && sh->alloc-sh->len >= (unsigned int)incr) || (incr < 0 && sh->len >= (unsigned int)(-incr))); - len = (sh->len += incr); - break; - } - case SDS_TYPE_64: { - SDS_HDR_VAR(64,s); - assert((incr >= 0 && sh->alloc-sh->len >= (uint64_t)incr) || (incr < 0 && sh->len >= (uint64_t)(-incr))); - len = (sh->len += incr); - break; - } - default: len = 0; /* Just to avoid compilation warnings. */ - } - s[len] = '\0'; -} - -/* Grow the sds to have the specified length. Bytes that were not part of - * the original length of the sds will be set to zero. - * - * if the specified length is smaller than the current length, no operation - * is performed. */ -sds sdsgrowzero(sds s, size_t len) { - size_t curlen = sdslen(s); - - if (len <= curlen) return s; - s = sdsMakeRoomFor(s,len-curlen); - if (s == NULL) return NULL; - - /* Make sure added region doesn't contain garbage */ - memset(s+curlen,0,(len-curlen+1)); /* also set trailing \0 byte */ - sdssetlen(s, len); - return s; -} - -/* Append the specified binary-safe string pointed by 't' of 'len' bytes to the - * end of the specified sds string 's'. - * - * After the call, the passed sds string is no longer valid and all the - * references must be substituted with the new pointer returned by the call. */ -sds sdscatlen(sds s, const void *t, size_t len) { - size_t curlen = sdslen(s); - - s = sdsMakeRoomFor(s,len); - if (s == NULL) return NULL; - memcpy(s+curlen, t, len); - sdssetlen(s, curlen+len); - s[curlen+len] = '\0'; - return s; -} - -/* Append the specified null termianted C string to the sds string 's'. - * - * After the call, the passed sds string is no longer valid and all the - * references must be substituted with the new pointer returned by the call. */ -sds sdscat(sds s, const char *t) { - return sdscatlen(s, t, strlen(t)); -} - -/* Append the specified sds 't' to the existing sds 's'. - * - * After the call, the modified sds string is no longer valid and all the - * references must be substituted with the new pointer returned by the call. */ -sds sdscatsds(sds s, const sds t) { - return sdscatlen(s, t, sdslen(t)); -} - -/* Destructively modify the sds string 's' to hold the specified binary - * safe string pointed by 't' of length 'len' bytes. */ -sds sdscpylen(sds s, const char *t, size_t len) { - if (sdsalloc(s) < len) { - s = sdsMakeRoomFor(s,len-sdslen(s)); - if (s == NULL) return NULL; - } - memcpy(s, t, len); - s[len] = '\0'; - sdssetlen(s, len); - return s; -} - -/* Like sdscpylen() but 't' must be a null-terminated string so that the length - * of the string is obtained with strlen(). */ -sds sdscpy(sds s, const char *t) { - return sdscpylen(s, t, strlen(t)); -} - -/* Helper for sdscatlonglong() doing the actual number -> string - * conversion. 's' must point to a string with room for at least - * SDS_LLSTR_SIZE bytes. - * - * The function returns the length of the null-terminated string - * representation stored at 's'. */ -#define SDS_LLSTR_SIZE 21 -int sdsll2str(char *s, long long value) { - char *p, aux; - unsigned long long v; - size_t l; - - /* Generate the string representation, this method produces - * an reversed string. */ - if (value < 0) { - /* Since v is unsigned, if value==LLONG_MIN then - * -LLONG_MIN will overflow. */ - if (value != LLONG_MIN) { - v = -value; - } else { - v = ((unsigned long long)LLONG_MAX) + 1; - } - } else { - v = value; - } - - p = s; - do { - *p++ = '0'+(v%10); - v /= 10; - } while(v); - if (value < 0) *p++ = '-'; - - /* Compute length and add null term. */ - l = p-s; - *p = '\0'; - - /* Reverse the string. */ - p--; - while(s < p) { - aux = *s; - *s = *p; - *p = aux; - s++; - p--; - } - return l; -} - -/* Identical sdsll2str(), but for unsigned long long type. */ -int sdsull2str(char *s, unsigned long long v) { - char *p, aux; - size_t l; - - /* Generate the string representation, this method produces - * an reversed string. */ - p = s; - do { - *p++ = '0'+(v%10); - v /= 10; - } while(v); - - /* Compute length and add null term. */ - l = p-s; - *p = '\0'; - - /* Reverse the string. */ - p--; - while(s < p) { - aux = *s; - *s = *p; - *p = aux; - s++; - p--; - } - return l; -} - -/* Create an sds string from a long long value. It is much faster than: - * - * sdscatprintf(sdsempty(),"%lld\n", value); - */ -sds sdsfromlonglong(long long value) { - char buf[SDS_LLSTR_SIZE]; - int len = sdsll2str(buf,value); - - return sdsnewlen(buf,len); -} - -/* Like sdscatprintf() but gets va_list instead of being variadic. */ -sds sdscatvprintf(sds s, const char *fmt, va_list ap) { - va_list cpy; - char staticbuf[1024], *buf = staticbuf, *t; - size_t buflen = strlen(fmt)*2; - int bufstrlen; - - /* We try to start using a static buffer for speed. - * If not possible we revert to heap allocation. */ - if (buflen > sizeof(staticbuf)) { - buf = s_malloc(buflen); - if (buf == NULL) return NULL; - } else { - buflen = sizeof(staticbuf); - } - - /* Alloc enough space for buffer and \0 after failing to - * fit the string in the current buffer size. */ - while(1) { - va_copy(cpy,ap); - bufstrlen = vsnprintf(buf, buflen, fmt, cpy); - va_end(cpy); - if (bufstrlen < 0) { - if (buf != staticbuf) s_free(buf); - return NULL; - } - if (((size_t)bufstrlen) >= buflen) { - if (buf != staticbuf) s_free(buf); - buflen = ((size_t)bufstrlen) + 1; - buf = s_malloc(buflen); - if (buf == NULL) return NULL; - continue; - } - break; - } - - /* Finally concat the obtained string to the SDS string and return it. */ - t = sdscatlen(s, buf, bufstrlen); - if (buf != staticbuf) s_free(buf); - return t; -} - -/* Append to the sds string 's' a string obtained using printf-alike format - * specifier. - * - * After the call, the modified sds string is no longer valid and all the - * references must be substituted with the new pointer returned by the call. - * - * Example: - * - * s = sdsnew("Sum is: "); - * s = sdscatprintf(s,"%d+%d = %d",a,b,a+b). - * - * Often you need to create a string from scratch with the printf-alike - * format. When this is the need, just use sdsempty() as the target string: - * - * s = sdscatprintf(sdsempty(), "... your format ...", args); - */ -sds sdscatprintf(sds s, const char *fmt, ...) { - va_list ap; - char *t; - va_start(ap, fmt); - t = sdscatvprintf(s,fmt,ap); - va_end(ap); - return t; -} - -/* This function is similar to sdscatprintf, but much faster as it does - * not rely on sprintf() family functions implemented by the libc that - * are often very slow. Moreover directly handling the sds string as - * new data is concatenated provides a performance improvement. - * - * However this function only handles an incompatible subset of printf-alike - * format specifiers: - * - * %s - C String - * %S - SDS string - * %i - signed int - * %I - 64 bit signed integer (long long, int64_t) - * %u - unsigned int - * %U - 64 bit unsigned integer (unsigned long long, uint64_t) - * %% - Verbatim "%" character. - */ -sds sdscatfmt(sds s, char const *fmt, ...) { - size_t initlen = sdslen(s); - const char *f = fmt; - long i; - va_list ap; - - /* To avoid continuous reallocations, let's start with a buffer that - * can hold at least two times the format string itself. It's not the - * best heuristic but seems to work in practice. */ - s = sdsMakeRoomFor(s, initlen + strlen(fmt)*2); - va_start(ap,fmt); - f = fmt; /* Next format specifier byte to process. */ - i = initlen; /* Position of the next byte to write to dest str. */ - while(*f) { - char next, *str; - size_t l; - long long num; - unsigned long long unum; - - /* Make sure there is always space for at least 1 char. */ - if (sdsavail(s)==0) { - s = sdsMakeRoomFor(s,1); - } - - switch(*f) { - case '%': - next = *(f+1); - if (next == '\0') break; - f++; - switch(next) { - case 's': - case 'S': - str = va_arg(ap,char*); - l = (next == 's') ? strlen(str) : sdslen(str); - if (sdsavail(s) < l) { - s = sdsMakeRoomFor(s,l); - } - memcpy(s+i,str,l); - sdsinclen(s,l); - i += l; - break; - case 'i': - case 'I': - if (next == 'i') - num = va_arg(ap,int); - else - num = va_arg(ap,long long); - { - char buf[SDS_LLSTR_SIZE]; - l = sdsll2str(buf,num); - if (sdsavail(s) < l) { - s = sdsMakeRoomFor(s,l); - } - memcpy(s+i,buf,l); - sdsinclen(s,l); - i += l; - } - break; - case 'u': - case 'U': - if (next == 'u') - unum = va_arg(ap,unsigned int); - else - unum = va_arg(ap,unsigned long long); - { - char buf[SDS_LLSTR_SIZE]; - l = sdsull2str(buf,unum); - if (sdsavail(s) < l) { - s = sdsMakeRoomFor(s,l); - } - memcpy(s+i,buf,l); - sdsinclen(s,l); - i += l; - } - break; - default: /* Handle %% and generally %. */ - s[i++] = next; - sdsinclen(s,1); - break; - } - break; - default: - s[i++] = *f; - sdsinclen(s,1); - break; - } - f++; - } - va_end(ap); - - /* Add null-term */ - s[i] = '\0'; - return s; -} - -/* Remove the part of the string from left and from right composed just of - * contiguous characters found in 'cset', that is a null terminated C string. - * - * After the call, the modified sds string is no longer valid and all the - * references must be substituted with the new pointer returned by the call. - * - * Example: - * - * s = sdsnew("AA...AA.a.aa.aHelloWorld :::"); - * s = sdstrim(s,"Aa. :"); - * printf("%s\n", s); - * - * Output will be just "HelloWorld". - */ -sds sdstrim(sds s, const char *cset) { - char *end, *sp, *ep; - size_t len; - - sp = s; - ep = end = s+sdslen(s)-1; - while(sp <= end && strchr(cset, *sp)) sp++; - while(ep > sp && strchr(cset, *ep)) ep--; - len = (ep-sp)+1; - if (s != sp) memmove(s, sp, len); - s[len] = '\0'; - sdssetlen(s,len); - return s; -} - -/* Turn the string into a smaller (or equal) string containing only the - * substring specified by the 'start' and 'end' indexes. - * - * start and end can be negative, where -1 means the last character of the - * string, -2 the penultimate character, and so forth. - * - * The interval is inclusive, so the start and end characters will be part - * of the resulting string. - * - * The string is modified in-place. - * - * Example: - * - * s = sdsnew("Hello World"); - * sdsrange(s,1,-1); => "ello World" - */ -void sdsrange(sds s, ssize_t start, ssize_t end) { - size_t newlen, len = sdslen(s); - - if (len == 0) return; - if (start < 0) { - start = len+start; - if (start < 0) start = 0; - } - if (end < 0) { - end = len+end; - if (end < 0) end = 0; - } - newlen = (start > end) ? 0 : (end-start)+1; - if (newlen != 0) { - if (start >= (ssize_t)len) { - newlen = 0; - } else if (end >= (ssize_t)len) { - end = len-1; - newlen = (end-start)+1; - } - } - if (start && newlen) memmove(s, s+start, newlen); - s[newlen] = 0; - sdssetlen(s,newlen); -} - -/* Apply tolower() to every character of the sds string 's'. */ -void sdstolower(sds s) { - size_t len = sdslen(s), j; - - for (j = 0; j < len; j++) s[j] = tolower(s[j]); -} - -/* Apply toupper() to every character of the sds string 's'. */ -void sdstoupper(sds s) { - size_t len = sdslen(s), j; - - for (j = 0; j < len; j++) s[j] = toupper(s[j]); -} - -/* Compare two sds strings s1 and s2 with memcmp(). - * - * Return value: - * - * positive if s1 > s2. - * negative if s1 < s2. - * 0 if s1 and s2 are exactly the same binary string. - * - * If two strings share exactly the same prefix, but one of the two has - * additional characters, the longer string is considered to be greater than - * the smaller one. */ -int sdscmp(const sds s1, const sds s2) { - size_t l1, l2, minlen; - int cmp; - - l1 = sdslen(s1); - l2 = sdslen(s2); - minlen = (l1 < l2) ? l1 : l2; - cmp = memcmp(s1,s2,minlen); - if (cmp == 0) return l1>l2? 1: (l1". - * - * After the call, the modified sds string is no longer valid and all the - * references must be substituted with the new pointer returned by the call. */ -sds sdscatrepr(sds s, const char *p, size_t len) { - s = sdscatlen(s,"\"",1); - while(len--) { - switch(*p) { - case '\\': - case '"': - s = sdscatprintf(s,"\\%c",*p); - break; - case '\n': s = sdscatlen(s,"\\n",2); break; - case '\r': s = sdscatlen(s,"\\r",2); break; - case '\t': s = sdscatlen(s,"\\t",2); break; - case '\a': s = sdscatlen(s,"\\a",2); break; - case '\b': s = sdscatlen(s,"\\b",2); break; - default: - if (isprint(*p)) - s = sdscatprintf(s,"%c",*p); - else - s = sdscatprintf(s,"\\x%02x",(unsigned char)*p); - break; - } - p++; - } - return sdscatlen(s,"\"",1); -} - -/* Helper function for sdssplitargs() that returns non zero if 'c' - * is a valid hex digit. */ -int is_hex_digit(char c) { - return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || - (c >= 'A' && c <= 'F'); -} - -/* Helper function for sdssplitargs() that converts a hex digit into an - * integer from 0 to 15 */ -int hex_digit_to_int(char c) { - switch(c) { - case '0': return 0; - case '1': return 1; - case '2': return 2; - case '3': return 3; - case '4': return 4; - case '5': return 5; - case '6': return 6; - case '7': return 7; - case '8': return 8; - case '9': return 9; - case 'a': case 'A': return 10; - case 'b': case 'B': return 11; - case 'c': case 'C': return 12; - case 'd': case 'D': return 13; - case 'e': case 'E': return 14; - case 'f': case 'F': return 15; - default: return 0; - } -} - -/* Split a line into arguments, where every argument can be in the - * following programming-language REPL-alike form: - * - * foo bar "newline are supported\n" and "\xff\x00otherstuff" - * - * The number of arguments is stored into *argc, and an array - * of sds is returned. - * - * The caller should free the resulting array of sds strings with - * sdsfreesplitres(). - * - * Note that sdscatrepr() is able to convert back a string into - * a quoted string in the same format sdssplitargs() is able to parse. - * - * The function returns the allocated tokens on success, even when the - * input string is empty, or NULL if the input contains unbalanced - * quotes or closed quotes followed by non space characters - * as in: "foo"bar or "foo' - */ -sds *sdssplitargs(const char *line, int *argc) { - const char *p = line; - char *current = NULL; - char **vector = NULL; - - *argc = 0; - while(1) { - /* skip blanks */ - while(*p && isspace(*p)) p++; - if (*p) { - /* get a token */ - int inq=0; /* set to 1 if we are in "quotes" */ - int insq=0; /* set to 1 if we are in 'single quotes' */ - int done=0; - - if (current == NULL) current = sdsempty(); - while(!done) { - if (inq) { - if (*p == '\\' && *(p+1) == 'x' && - is_hex_digit(*(p+2)) && - is_hex_digit(*(p+3))) - { - unsigned char byte; - - byte = (hex_digit_to_int(*(p+2))*16)+ - hex_digit_to_int(*(p+3)); - current = sdscatlen(current,(char*)&byte,1); - p += 3; - } else if (*p == '\\' && *(p+1)) { - char c; - - p++; - switch(*p) { - case 'n': c = '\n'; break; - case 'r': c = '\r'; break; - case 't': c = '\t'; break; - case 'b': c = '\b'; break; - case 'a': c = '\a'; break; - default: c = *p; break; - } - current = sdscatlen(current,&c,1); - } else if (*p == '"') { - /* closing quote must be followed by a space or - * nothing at all. */ - if (*(p+1) && !isspace(*(p+1))) goto err; - done=1; - } else if (!*p) { - /* unterminated quotes */ - goto err; - } else { - current = sdscatlen(current,p,1); - } - } else if (insq) { - if (*p == '\\' && *(p+1) == '\'') { - p++; - current = sdscatlen(current,"'",1); - } else if (*p == '\'') { - /* closing quote must be followed by a space or - * nothing at all. */ - if (*(p+1) && !isspace(*(p+1))) goto err; - done=1; - } else if (!*p) { - /* unterminated quotes */ - goto err; - } else { - current = sdscatlen(current,p,1); - } - } else { - switch(*p) { - case ' ': - case '\n': - case '\r': - case '\t': - case '\0': - done=1; - break; - case '"': - inq=1; - break; - case '\'': - insq=1; - break; - default: - current = sdscatlen(current,p,1); - break; - } - } - if (*p) p++; - } - /* add the token to the vector */ - vector = s_realloc(vector,((*argc)+1)*sizeof(char*)); - vector[*argc] = current; - (*argc)++; - current = NULL; - } else { - /* Even on empty input string return something not NULL. */ - if (vector == NULL) vector = s_malloc(sizeof(void*)); - return vector; - } - } - -err: - while((*argc)--) - sdsfree(vector[*argc]); - s_free(vector); - if (current) sdsfree(current); - *argc = 0; - return NULL; -} - -/* Modify the string substituting all the occurrences of the set of - * characters specified in the 'from' string to the corresponding character - * in the 'to' array. - * - * For instance: sdsmapchars(mystring, "ho", "01", 2) - * will have the effect of turning the string "hello" into "0ell1". - * - * The function returns the sds string pointer, that is always the same - * as the input pointer since no resize is needed. */ -sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen) { - size_t j, i, l = sdslen(s); - - for (j = 0; j < l; j++) { - for (i = 0; i < setlen; i++) { - if (s[j] == from[i]) { - s[j] = to[i]; - break; - } - } - } - return s; -} - -/* Join an array of C strings using the specified separator (also a C string). - * Returns the result as an sds string. */ -sds sdsjoin(char **argv, int argc, char *sep) { - sds join = sdsempty(); - int j; - - for (j = 0; j < argc; j++) { - join = sdscat(join, argv[j]); - if (j != argc-1) join = sdscat(join,sep); - } - return join; -} - -/* Like sdsjoin, but joins an array of SDS strings. */ -sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen) { - sds join = sdsempty(); - int j; - - for (j = 0; j < argc; j++) { - join = sdscatsds(join, argv[j]); - if (j != argc-1) join = sdscatlen(join,sep,seplen); - } - return join; -} - -/* Wrappers to the allocators used by SDS. Note that SDS will actually - * just use the macros defined into sdsalloc.h in order to avoid to pay - * the overhead of function calls. Here we define these wrappers only for - * the programs SDS is linked to, if they want to touch the SDS internals - * even if they use a different allocator. */ -void *sds_malloc(size_t size) { return s_malloc(size); } -void *sds_realloc(void *ptr, size_t size) { return s_realloc(ptr,size); } -void sds_free(void *ptr) { s_free(ptr); } - -#if defined(SDS_TEST_MAIN) -#include -#include "testhelp.h" -#include "limits.h" - -#define UNUSED(x) (void)(x) -int sdsTest(void) { - { - sds x = sdsnew("foo"), y; - - test_cond("Create a string and obtain the length", - sdslen(x) == 3 && memcmp(x,"foo\0",4) == 0) - - sdsfree(x); - x = sdsnewlen("foo",2); - test_cond("Create a string with specified length", - sdslen(x) == 2 && memcmp(x,"fo\0",3) == 0) - - x = sdscat(x,"bar"); - test_cond("Strings concatenation", - sdslen(x) == 5 && memcmp(x,"fobar\0",6) == 0); - - x = sdscpy(x,"a"); - test_cond("sdscpy() against an originally longer string", - sdslen(x) == 1 && memcmp(x,"a\0",2) == 0) - - x = sdscpy(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk"); - test_cond("sdscpy() against an originally shorter string", - sdslen(x) == 33 && - memcmp(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\0",33) == 0) - - sdsfree(x); - x = sdscatprintf(sdsempty(),"%d",123); - test_cond("sdscatprintf() seems working in the base case", - sdslen(x) == 3 && memcmp(x,"123\0",4) == 0) - - sdsfree(x); - x = sdscatprintf(sdsempty(),"a%cb",0); - test_cond("sdscatprintf() seems working with \\0 inside of result", - sdslen(x) == 3 && memcmp(x,"a\0""b\0",4) == 0) - - { - sdsfree(x); - char etalon[1024*1024]; - for (size_t i = 0; i < sizeof(etalon); i++) { - etalon[i] = '0'; - } - x = sdscatprintf(sdsempty(),"%0*d",(int)sizeof(etalon),0); - test_cond("sdscatprintf() can print 1MB", - sdslen(x) == sizeof(etalon) && memcmp(x,etalon,sizeof(etalon)) == 0) - } - - sdsfree(x); - x = sdsnew("--"); - x = sdscatfmt(x, "Hello %s World %I,%I--", "Hi!", LLONG_MIN,LLONG_MAX); - test_cond("sdscatfmt() seems working in the base case", - sdslen(x) == 60 && - memcmp(x,"--Hello Hi! World -9223372036854775808," - "9223372036854775807--",60) == 0) - printf("[%s]\n",x); - - sdsfree(x); - x = sdsnew("--"); - x = sdscatfmt(x, "%u,%U--", UINT_MAX, ULLONG_MAX); - test_cond("sdscatfmt() seems working with unsigned numbers", - sdslen(x) == 35 && - memcmp(x,"--4294967295,18446744073709551615--",35) == 0) - - sdsfree(x); - x = sdsnew(" x "); - sdstrim(x," x"); - test_cond("sdstrim() works when all chars match", - sdslen(x) == 0) - - sdsfree(x); - x = sdsnew(" x "); - sdstrim(x," "); - test_cond("sdstrim() works when a single char remains", - sdslen(x) == 1 && x[0] == 'x') - - sdsfree(x); - x = sdsnew("xxciaoyyy"); - sdstrim(x,"xy"); - test_cond("sdstrim() correctly trims characters", - sdslen(x) == 4 && memcmp(x,"ciao\0",5) == 0) - - y = sdsdup(x); - sdsrange(y,1,1); - test_cond("sdsrange(...,1,1)", - sdslen(y) == 1 && memcmp(y,"i\0",2) == 0) - - sdsfree(y); - y = sdsdup(x); - sdsrange(y,1,-1); - test_cond("sdsrange(...,1,-1)", - sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0) - - sdsfree(y); - y = sdsdup(x); - sdsrange(y,-2,-1); - test_cond("sdsrange(...,-2,-1)", - sdslen(y) == 2 && memcmp(y,"ao\0",3) == 0) - - sdsfree(y); - y = sdsdup(x); - sdsrange(y,2,1); - test_cond("sdsrange(...,2,1)", - sdslen(y) == 0 && memcmp(y,"\0",1) == 0) - - sdsfree(y); - y = sdsdup(x); - sdsrange(y,1,100); - test_cond("sdsrange(...,1,100)", - sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0) - - sdsfree(y); - y = sdsdup(x); - sdsrange(y,100,100); - test_cond("sdsrange(...,100,100)", - sdslen(y) == 0 && memcmp(y,"\0",1) == 0) - - sdsfree(y); - sdsfree(x); - x = sdsnew("foo"); - y = sdsnew("foa"); - test_cond("sdscmp(foo,foa)", sdscmp(x,y) > 0) - - sdsfree(y); - sdsfree(x); - x = sdsnew("bar"); - y = sdsnew("bar"); - test_cond("sdscmp(bar,bar)", sdscmp(x,y) == 0) - - sdsfree(y); - sdsfree(x); - x = sdsnew("aar"); - y = sdsnew("bar"); - test_cond("sdscmp(bar,bar)", sdscmp(x,y) < 0) - - sdsfree(y); - sdsfree(x); - x = sdsnewlen("\a\n\0foo\r",7); - y = sdscatrepr(sdsempty(),x,sdslen(x)); - test_cond("sdscatrepr(...data...)", - memcmp(y,"\"\\a\\n\\x00foo\\r\"",15) == 0) - - { - char *p; - int step = 10, j, i; - - sdsfree(x); - sdsfree(y); - x = sdsnew("0"); - test_cond("sdsnew() free/len buffers", sdslen(x) == 1 && sdsavail(x) == 0); - - /* Run the test a few times in order to hit the first two - * SDS header types. */ - for (i = 0; i < 10; i++) { - int oldlen = sdslen(x); - x = sdsMakeRoomFor(x,step); - int type = x[-1]&SDS_TYPE_MASK; - - test_cond("sdsMakeRoomFor() len", sdslen(x) == oldlen); - if (type != SDS_TYPE_5) { - test_cond("sdsMakeRoomFor() free", sdsavail(x) >= step); - } - p = x+oldlen; - for (j = 0; j < step; j++) { - p[j] = 'A'+j; - } - sdsIncrLen(x,step); - } - test_cond("sdsMakeRoomFor() content", - memcmp("0ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJ",x,101) == 0); - test_cond("sdsMakeRoomFor() final length",sdslen(x)==101); - - sdsfree(x); - } - } - test_report() - return 0; -} -#endif - -#ifdef SDS_TEST_MAIN -int main(void) { - return sdsTest(); -} -#endif diff --git a/source/sds.h b/source/sds.h deleted file mode 100644 index adcc12c..0000000 --- a/source/sds.h +++ /dev/null @@ -1,274 +0,0 @@ -/* SDSLib 2.0 -- A C dynamic strings library - * - * Copyright (c) 2006-2015, Salvatore Sanfilippo - * Copyright (c) 2015, Oran Agra - * Copyright (c) 2015, Redis Labs, Inc - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __SDS_H -#define __SDS_H - -#define SDS_MAX_PREALLOC (1024*1024) -extern const char *SDS_NOINIT; - -#include -#include -#include - -typedef char *sds; - -/* Note: sdshdr5 is never used, we just access the flags byte directly. - * However is here to document the layout of type 5 SDS strings. */ -struct __attribute__ ((__packed__)) sdshdr5 { - unsigned char flags; /* 3 lsb of type, and 5 msb of string length */ - char buf[]; -}; -struct __attribute__ ((__packed__)) sdshdr8 { - uint8_t len; /* used */ - uint8_t alloc; /* excluding the header and null terminator */ - unsigned char flags; /* 3 lsb of type, 5 unused bits */ - char buf[]; -}; -struct __attribute__ ((__packed__)) sdshdr16 { - uint16_t len; /* used */ - uint16_t alloc; /* excluding the header and null terminator */ - unsigned char flags; /* 3 lsb of type, 5 unused bits */ - char buf[]; -}; -struct __attribute__ ((__packed__)) sdshdr32 { - uint32_t len; /* used */ - uint32_t alloc; /* excluding the header and null terminator */ - unsigned char flags; /* 3 lsb of type, 5 unused bits */ - char buf[]; -}; -struct __attribute__ ((__packed__)) sdshdr64 { - uint64_t len; /* used */ - uint64_t alloc; /* excluding the header and null terminator */ - unsigned char flags; /* 3 lsb of type, 5 unused bits */ - char buf[]; -}; - -#define SDS_TYPE_5 0 -#define SDS_TYPE_8 1 -#define SDS_TYPE_16 2 -#define SDS_TYPE_32 3 -#define SDS_TYPE_64 4 -#define SDS_TYPE_MASK 7 -#define SDS_TYPE_BITS 3 -#define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (void*)((s)-(sizeof(struct sdshdr##T))); -#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T)))) -#define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS) - -static inline size_t sdslen(const sds s) { - unsigned char flags = s[-1]; - switch(flags&SDS_TYPE_MASK) { - case SDS_TYPE_5: - return SDS_TYPE_5_LEN(flags); - case SDS_TYPE_8: - return SDS_HDR(8,s)->len; - case SDS_TYPE_16: - return SDS_HDR(16,s)->len; - case SDS_TYPE_32: - return SDS_HDR(32,s)->len; - case SDS_TYPE_64: - return SDS_HDR(64,s)->len; - } - return 0; -} - -static inline size_t sdsavail(const sds s) { - unsigned char flags = s[-1]; - switch(flags&SDS_TYPE_MASK) { - case SDS_TYPE_5: { - return 0; - } - case SDS_TYPE_8: { - SDS_HDR_VAR(8,s); - return sh->alloc - sh->len; - } - case SDS_TYPE_16: { - SDS_HDR_VAR(16,s); - return sh->alloc - sh->len; - } - case SDS_TYPE_32: { - SDS_HDR_VAR(32,s); - return sh->alloc - sh->len; - } - case SDS_TYPE_64: { - SDS_HDR_VAR(64,s); - return sh->alloc - sh->len; - } - } - return 0; -} - -static inline void sdssetlen(sds s, size_t newlen) { - unsigned char flags = s[-1]; - switch(flags&SDS_TYPE_MASK) { - case SDS_TYPE_5: - { - unsigned char *fp = ((unsigned char*)s)-1; - *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS); - } - break; - case SDS_TYPE_8: - SDS_HDR(8,s)->len = newlen; - break; - case SDS_TYPE_16: - SDS_HDR(16,s)->len = newlen; - break; - case SDS_TYPE_32: - SDS_HDR(32,s)->len = newlen; - break; - case SDS_TYPE_64: - SDS_HDR(64,s)->len = newlen; - break; - } -} - -static inline void sdsinclen(sds s, size_t inc) { - unsigned char flags = s[-1]; - switch(flags&SDS_TYPE_MASK) { - case SDS_TYPE_5: - { - unsigned char *fp = ((unsigned char*)s)-1; - unsigned char newlen = SDS_TYPE_5_LEN(flags)+inc; - *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS); - } - break; - case SDS_TYPE_8: - SDS_HDR(8,s)->len += inc; - break; - case SDS_TYPE_16: - SDS_HDR(16,s)->len += inc; - break; - case SDS_TYPE_32: - SDS_HDR(32,s)->len += inc; - break; - case SDS_TYPE_64: - SDS_HDR(64,s)->len += inc; - break; - } -} - -/* sdsalloc() = sdsavail() + sdslen() */ -static inline size_t sdsalloc(const sds s) { - unsigned char flags = s[-1]; - switch(flags&SDS_TYPE_MASK) { - case SDS_TYPE_5: - return SDS_TYPE_5_LEN(flags); - case SDS_TYPE_8: - return SDS_HDR(8,s)->alloc; - case SDS_TYPE_16: - return SDS_HDR(16,s)->alloc; - case SDS_TYPE_32: - return SDS_HDR(32,s)->alloc; - case SDS_TYPE_64: - return SDS_HDR(64,s)->alloc; - } - return 0; -} - -static inline void sdssetalloc(sds s, size_t newlen) { - unsigned char flags = s[-1]; - switch(flags&SDS_TYPE_MASK) { - case SDS_TYPE_5: - /* Nothing to do, this type has no total allocation info. */ - break; - case SDS_TYPE_8: - SDS_HDR(8,s)->alloc = newlen; - break; - case SDS_TYPE_16: - SDS_HDR(16,s)->alloc = newlen; - break; - case SDS_TYPE_32: - SDS_HDR(32,s)->alloc = newlen; - break; - case SDS_TYPE_64: - SDS_HDR(64,s)->alloc = newlen; - break; - } -} - -sds sdsnewlen(const void *init, size_t initlen); -sds sdsnew(const char *init); -sds sdsempty(void); -sds sdsdup(const sds s); -void sdsfree(sds s); -sds sdsgrowzero(sds s, size_t len); -sds sdscatlen(sds s, const void *t, size_t len); -sds sdscat(sds s, const char *t); -sds sdscatsds(sds s, const sds t); -sds sdscpylen(sds s, const char *t, size_t len); -sds sdscpy(sds s, const char *t); - -sds sdscatvprintf(sds s, const char *fmt, va_list ap); -#ifdef __GNUC__ -sds sdscatprintf(sds s, const char *fmt, ...) - __attribute__((format(printf, 2, 3))); -#else -sds sdscatprintf(sds s, const char *fmt, ...); -#endif - -sds sdscatfmt(sds s, char const *fmt, ...); -sds sdstrim(sds s, const char *cset); -void sdsrange(sds s, ssize_t start, ssize_t end); -void sdsupdatelen(sds s); -void sdsclear(sds s); -int sdscmp(const sds s1, const sds s2); -sds *sdssplitlen(const char *s, ssize_t len, const char *sep, int seplen, int *count); -void sdsfreesplitres(sds *tokens, int count); -void sdstolower(sds s); -void sdstoupper(sds s); -sds sdsfromlonglong(long long value); -sds sdscatrepr(sds s, const char *p, size_t len); -sds *sdssplitargs(const char *line, int *argc); -sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen); -sds sdsjoin(char **argv, int argc, char *sep); -sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen); - -/* Low level functions exposed to the user API */ -sds sdsMakeRoomFor(sds s, size_t addlen); -void sdsIncrLen(sds s, ssize_t incr); -sds sdsRemoveFreeSpace(sds s); -size_t sdsAllocSize(sds s); -void *sdsAllocPtr(sds s); - -/* Export the allocator used by SDS to the program using SDS. - * Sometimes the program SDS is linked to, may use a different set of - * allocators, but may want to allocate or free things that SDS will - * respectively free or allocate. */ -void *sds_malloc(size_t size); -void *sds_realloc(void *ptr, size_t size); -void sds_free(void *ptr); - -#ifdef REDIS_TEST -int sdsTest(int argc, char *argv[]); -#endif - -#endif diff --git a/source/sdsalloc.h b/source/sdsalloc.h deleted file mode 100644 index f43023c..0000000 --- a/source/sdsalloc.h +++ /dev/null @@ -1,42 +0,0 @@ -/* SDSLib 2.0 -- A C dynamic strings library - * - * Copyright (c) 2006-2015, Salvatore Sanfilippo - * Copyright (c) 2015, Oran Agra - * Copyright (c) 2015, Redis Labs, Inc - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/* SDS allocator selection. - * - * This file is used in order to change the SDS allocator at compile time. - * Just define the following defines to what you want to use. Also add - * the include of your alternate allocator if needed (not needed in order - * to use the default libc allocator). */ - -#define s_malloc malloc -#define s_realloc realloc -#define s_free free diff --git a/test.a.txt b/test.a.txt new file mode 100644 index 0000000..ee1f9ea Binary files /dev/null and b/test.a.txt differ