10f66f451Sopenharmony_ci/* commas.c - Deal with comma separated lists 20f66f451Sopenharmony_ci * 30f66f451Sopenharmony_ci * Copyright 2018 Rob Landley <rob@landley.net> 40f66f451Sopenharmony_ci */ 50f66f451Sopenharmony_ci 60f66f451Sopenharmony_ci#include "toys.h" 70f66f451Sopenharmony_ci 80f66f451Sopenharmony_ci// Traverse arg_list of csv, calling callback on each value 90f66f451Sopenharmony_civoid comma_args(struct arg_list *al, void *data, char *err, 100f66f451Sopenharmony_ci char *(*callback)(void *data, char *str, int len)) 110f66f451Sopenharmony_ci{ 120f66f451Sopenharmony_ci char *next, *arg; 130f66f451Sopenharmony_ci int len; 140f66f451Sopenharmony_ci 150f66f451Sopenharmony_ci if (CFG_TOYBOX_DEBUG && !err) err = "INTERNAL"; 160f66f451Sopenharmony_ci 170f66f451Sopenharmony_ci while (al) { 180f66f451Sopenharmony_ci arg = al->arg; 190f66f451Sopenharmony_ci while ((next = comma_iterate(&arg, &len))) 200f66f451Sopenharmony_ci if ((next = callback(data, next, len))) 210f66f451Sopenharmony_ci error_exit("%s '%s'\n%*c", err, al->arg, 220f66f451Sopenharmony_ci (int)(5+strlen(toys.which->name)+strlen(err)+next-al->arg), '^'); 230f66f451Sopenharmony_ci al = al->next; 240f66f451Sopenharmony_ci } 250f66f451Sopenharmony_ci} 260f66f451Sopenharmony_ci 270f66f451Sopenharmony_ci// Realloc *old with oldstring,newstring 280f66f451Sopenharmony_ci 290f66f451Sopenharmony_civoid comma_collate(char **old, char *new) 300f66f451Sopenharmony_ci{ 310f66f451Sopenharmony_ci char *temp, *atold = *old; 320f66f451Sopenharmony_ci 330f66f451Sopenharmony_ci // Only add a comma if old string didn't end with one 340f66f451Sopenharmony_ci if (atold && *atold) { 350f66f451Sopenharmony_ci char *comma = ","; 360f66f451Sopenharmony_ci 370f66f451Sopenharmony_ci if (atold[strlen(atold)-1] == ',') comma = ""; 380f66f451Sopenharmony_ci temp = xmprintf("%s%s%s", atold, comma, new); 390f66f451Sopenharmony_ci } else temp = xstrdup(new); 400f66f451Sopenharmony_ci free (atold); 410f66f451Sopenharmony_ci *old = temp; 420f66f451Sopenharmony_ci} 430f66f451Sopenharmony_ci 440f66f451Sopenharmony_ci// iterate through strings in a comma separated list. 450f66f451Sopenharmony_ci// returns start of next entry or NULL if none 460f66f451Sopenharmony_ci// sets *len to length of entry (not including comma) 470f66f451Sopenharmony_ci// advances *list to start of next entry 480f66f451Sopenharmony_cichar *comma_iterate(char **list, int *len) 490f66f451Sopenharmony_ci{ 500f66f451Sopenharmony_ci char *start = *list, *end; 510f66f451Sopenharmony_ci 520f66f451Sopenharmony_ci if (!*list || !**list) return 0; 530f66f451Sopenharmony_ci 540f66f451Sopenharmony_ci if (!(end = strchr(*list, ','))) { 550f66f451Sopenharmony_ci *len = strlen(*list); 560f66f451Sopenharmony_ci *list = 0; 570f66f451Sopenharmony_ci } else *list += (*len = end-start)+1; 580f66f451Sopenharmony_ci 590f66f451Sopenharmony_ci return start; 600f66f451Sopenharmony_ci} 610f66f451Sopenharmony_ci 620f66f451Sopenharmony_ci// Check all instances of opt and "no"opt in optlist, return true if opt 630f66f451Sopenharmony_ci// found and last instance wasn't no. If clean, remove each instance from list. 640f66f451Sopenharmony_ciint comma_scan(char *optlist, char *opt, int clean) 650f66f451Sopenharmony_ci{ 660f66f451Sopenharmony_ci int optlen = strlen(opt), len, no, got = 0; 670f66f451Sopenharmony_ci 680f66f451Sopenharmony_ci if (optlist) for (;;) { 690f66f451Sopenharmony_ci char *s = comma_iterate(&optlist, &len); 700f66f451Sopenharmony_ci 710f66f451Sopenharmony_ci if (!s) break; 720f66f451Sopenharmony_ci no = 2*(*s == 'n' && s[1] == 'o'); 730f66f451Sopenharmony_ci if (optlen == len-no && !strncmp(opt, s+no, optlen)) { 740f66f451Sopenharmony_ci got = !no; 750f66f451Sopenharmony_ci if (clean) { 760f66f451Sopenharmony_ci if (optlist) memmove(s, optlist, strlen(optlist)+1); 770f66f451Sopenharmony_ci else *s = 0; 780f66f451Sopenharmony_ci } 790f66f451Sopenharmony_ci } 800f66f451Sopenharmony_ci } 810f66f451Sopenharmony_ci 820f66f451Sopenharmony_ci return got; 830f66f451Sopenharmony_ci} 840f66f451Sopenharmony_ci 850f66f451Sopenharmony_ci// return true if all scanlist options enabled in optlist 860f66f451Sopenharmony_ciint comma_scanall(char *optlist, char *scanlist) 870f66f451Sopenharmony_ci{ 880f66f451Sopenharmony_ci int i = 1; 890f66f451Sopenharmony_ci 900f66f451Sopenharmony_ci while (scanlist && *scanlist) { 910f66f451Sopenharmony_ci char *opt = comma_iterate(&scanlist, &i), *s = xstrndup(opt, i); 920f66f451Sopenharmony_ci 930f66f451Sopenharmony_ci i = comma_scan(optlist, s, 0); 940f66f451Sopenharmony_ci free(s); 950f66f451Sopenharmony_ci if (!i) break; 960f66f451Sopenharmony_ci } 970f66f451Sopenharmony_ci 980f66f451Sopenharmony_ci return i; 990f66f451Sopenharmony_ci} 1000f66f451Sopenharmony_ci 1010f66f451Sopenharmony_ci// Returns true and removes `opt` from `optlist` if present, false otherwise. 1020f66f451Sopenharmony_ci// Doesn't have the magic "no" behavior of comma_scan. 1030f66f451Sopenharmony_ciint comma_remove(char *optlist, char *opt) 1040f66f451Sopenharmony_ci{ 1050f66f451Sopenharmony_ci int optlen = strlen(opt), len, got = 0; 1060f66f451Sopenharmony_ci 1070f66f451Sopenharmony_ci if (optlist) for (;;) { 1080f66f451Sopenharmony_ci char *s = comma_iterate(&optlist, &len); 1090f66f451Sopenharmony_ci 1100f66f451Sopenharmony_ci if (!s) break; 1110f66f451Sopenharmony_ci if (optlen == len && !strncmp(opt, s, optlen)) { 1120f66f451Sopenharmony_ci got = 1; 1130f66f451Sopenharmony_ci if (optlist) memmove(s, optlist, strlen(optlist)+1); 1140f66f451Sopenharmony_ci else *s = 0; 1150f66f451Sopenharmony_ci } 1160f66f451Sopenharmony_ci } 1170f66f451Sopenharmony_ci 1180f66f451Sopenharmony_ci return got; 1190f66f451Sopenharmony_ci} 120