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