android_external_toybox/lib/commas.c
Elliott Hughes 0d754b823c dd: iflags, oflags, fix ^C, fix the fundamental loop.
Investigating why the toybox tar tests fail on Android with toybox dd, I
realized I was rewriting a part of dd I'd rewritten before!

This is a re-send of my 2019-02-22 patch, rebased against the current
ToT...

This patch was originally motivated because after suggesting to the author of
https://stackoverflow.com/questions/17157820/access-vdsolinux/54797221#54797221
that he could tell dd to work in bytes rather than blocks, I realized
that our dd doesn't actually support that. But the rewrite of the main
loop is necessary to fix the incorrect output from the dd calls in the
tar test.

Without this patch, `yes | dd bs=65536 count=1 > fweep` basically gives
random output, based on how many bytes the pipe feels like giving you in
your first read. (As far as I know, dd *without* bs= was fine, but I
can't guarantee that that's true, just that I haven't seen it fail.)

Also switch to TAGGED_ARRAY and comma_* for conv rather than add two more
copies of an undesired idiom. It turned out -- contrary to the belief of
cp(1) -- that comma_scan isn't suitable for this because of its magic
handling of "no" prefixes. (It's actually harmless in cp because none
of the --preserve options begin with "no", but some dd options do.) To
this end, comma_remove is a less-magic comma_scan.

I've also changed an `if` to a `while` because other implementations
allow things like `--preserve=mode,mode` or `conv=sync,sync`. (If we
decide this is a bug rather than a feature, we should at least fix the
error message to be clear that we're rejecting the *duplication*, not
the option itself.)

I've also fixed the ^C behavior by simply adding a direct SIGINT handler
rather than trying to be clever inside the read loop (which is why we
weren't handling the SIGINT until the read returned).

I've also removed `strstarteq` and just added the '=' to each literal
when calling regular `strstart`.

Plus basic tests.
2019-07-10 01:54:01 -05:00

120 lines
2.8 KiB
C

/* commas.c - Deal with comma separated lists
*
* Copyright 2018 Rob Landley <rob@landley.net>
*/
#include "toys.h"
// Traverse arg_list of csv, calling callback on each value
void comma_args(struct arg_list *al, void *data, char *err,
char *(*callback)(void *data, char *str, int len))
{
char *next, *arg;
int len;
if (CFG_TOYBOX_DEBUG && !err) err = "INTERNAL";
while (al) {
arg = al->arg;
while ((next = comma_iterate(&arg, &len)))
if ((next = callback(data, next, len)))
error_exit("%s '%s'\n%*c", err, al->arg,
(int)(5+strlen(toys.which->name)+strlen(err)+next-al->arg), '^');
al = al->next;
}
}
// Realloc *old with oldstring,newstring
void comma_collate(char **old, char *new)
{
char *temp, *atold = *old;
// Only add a comma if old string didn't end with one
if (atold && *atold) {
char *comma = ",";
if (atold[strlen(atold)-1] == ',') comma = "";
temp = xmprintf("%s%s%s", atold, comma, new);
} else temp = xstrdup(new);
free (atold);
*old = temp;
}
// iterate through strings in a comma separated list.
// returns start of next entry or NULL if none
// sets *len to length of entry (not including comma)
// advances *list to start of next entry
char *comma_iterate(char **list, int *len)
{
char *start = *list, *end;
if (!*list || !**list) return 0;
if (!(end = strchr(*list, ','))) {
*len = strlen(*list);
*list = 0;
} else *list += (*len = end-start)+1;
return start;
}
// Check all instances of opt and "no"opt in optlist, return true if opt
// found and last instance wasn't no. If clean, remove each instance from list.
int comma_scan(char *optlist, char *opt, int clean)
{
int optlen = strlen(opt), len, no, got = 0;
if (optlist) for (;;) {
char *s = comma_iterate(&optlist, &len);
if (!s) break;
no = 2*(*s == 'n' && s[1] == 'o');
if (optlen == len-no && !strncmp(opt, s+no, optlen)) {
got = !no;
if (clean) {
if (optlist) memmove(s, optlist, strlen(optlist)+1);
else *s = 0;
}
}
}
return got;
}
// return true if all scanlist options enabled in optlist
int comma_scanall(char *optlist, char *scanlist)
{
int i = 1;
while (scanlist && *scanlist) {
char *opt = comma_iterate(&scanlist, &i), *s = xstrndup(opt, i);
i = comma_scan(optlist, s, 0);
free(s);
if (!i) break;
}
return i;
}
// Returns true and removes `opt` from `optlist` if present, false otherwise.
// Doesn't have the magic "no" behavior of comma_scan.
int comma_remove(char *optlist, char *opt)
{
int optlen = strlen(opt), len, got = 0;
if (optlist) for (;;) {
char *s = comma_iterate(&optlist, &len);
if (!s) break;
if (optlen == len && !strncmp(opt, s, optlen)) {
got = 1;
if (optlist) memmove(s, optlist, strlen(optlist)+1);
else *s = 0;
}
}
return got;
}