Coverage Report

Created: 2024-09-11 06:44

/src/tmux/arguments.c
Line
Count
Source (jump to first uncovered line)
1
/* $OpenBSD$ */
2
3
/*
4
 * Copyright (c) 2010 Nicholas Marriott <nicholas.marriott@gmail.com>
5
 *
6
 * Permission to use, copy, modify, and distribute this software for any
7
 * purpose with or without fee is hereby granted, provided that the above
8
 * copyright notice and this permission notice appear in all copies.
9
 *
10
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15
 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16
 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
 */
18
19
#include <sys/types.h>
20
21
#include <ctype.h>
22
#include <stdlib.h>
23
#include <string.h>
24
#include <unistd.h>
25
26
#include "tmux.h"
27
28
/*
29
 * Manipulate command arguments.
30
 */
31
32
/* List of argument values. */
33
TAILQ_HEAD(args_values, args_value);
34
35
/* Single arguments flag. */
36
struct args_entry {
37
  u_char       flag;
38
  struct args_values   values;
39
  u_int      count;
40
41
  int      flags;
42
0
#define ARGS_ENTRY_OPTIONAL_VALUE 0x1
43
44
  RB_ENTRY(args_entry)   entry;
45
};
46
47
/* Parsed argument flags and values. */
48
struct args {
49
  struct args_tree   tree;
50
  u_int      count;
51
  struct args_value *values;
52
};
53
54
/* Prepared command state. */
55
struct args_command_state {
56
  struct cmd_list   *cmdlist;
57
  char      *cmd;
58
  struct cmd_parse_input   pi;
59
};
60
61
static struct args_entry  *args_find(struct args *, u_char);
62
63
static int  args_cmp(struct args_entry *, struct args_entry *);
64
RB_GENERATE_STATIC(args_tree, args_entry, entry, args_cmp);
65
66
/* Arguments tree comparison function. */
67
static int
68
args_cmp(struct args_entry *a1, struct args_entry *a2)
69
0
{
70
0
  return (a1->flag - a2->flag);
71
0
}
72
73
/* Find a flag in the arguments tree. */
74
static struct args_entry *
75
args_find(struct args *args, u_char flag)
76
0
{
77
0
  struct args_entry entry;
78
79
0
  entry.flag = flag;
80
0
  return (RB_FIND(args_tree, &args->tree, &entry));
81
0
}
82
83
/* Copy value. */
84
static void
85
args_copy_value(struct args_value *to, struct args_value *from)
86
0
{
87
0
  to->type = from->type;
88
0
  switch (from->type) {
89
0
  case ARGS_NONE:
90
0
    break;
91
0
  case ARGS_COMMANDS:
92
0
    to->cmdlist = from->cmdlist;
93
0
    to->cmdlist->references++;
94
0
    break;
95
0
  case ARGS_STRING:
96
0
    to->string = xstrdup(from->string);
97
0
    break;
98
0
  }
99
0
}
100
101
/* Type to string. */
102
static const char *
103
args_type_to_string (enum args_type type)
104
0
{
105
0
  switch (type)
106
0
  {
107
0
  case ARGS_NONE:
108
0
    return "NONE";
109
0
  case ARGS_STRING:
110
0
    return "STRING";
111
0
  case ARGS_COMMANDS:
112
0
    return "COMMANDS";
113
0
  }
114
0
  return "INVALID";
115
0
}
116
117
/* Get value as string. */
118
static const char *
119
args_value_as_string(struct args_value *value)
120
0
{
121
0
  switch (value->type) {
122
0
  case ARGS_NONE:
123
0
    return ("");
124
0
  case ARGS_COMMANDS:
125
0
    if (value->cached == NULL)
126
0
      value->cached = cmd_list_print(value->cmdlist, 0);
127
0
    return (value->cached);
128
0
  case ARGS_STRING:
129
0
    return (value->string);
130
0
  }
131
0
  fatalx("unexpected argument type");
132
0
}
133
134
/* Create an empty arguments set. */
135
struct args *
136
args_create(void)
137
0
{
138
0
  struct args  *args;
139
140
0
  args = xcalloc(1, sizeof *args);
141
0
  RB_INIT(&args->tree);
142
0
  return (args);
143
0
}
144
145
/* Parse a single flag. */
146
static int
147
args_parse_flag_argument(struct args_value *values, u_int count, char **cause,
148
    struct args *args, u_int *i, const char *string, int flag,
149
    int optional_argument)
150
0
{
151
0
  struct args_value *argument, *new;
152
0
  const char    *s;
153
154
0
  new = xcalloc(1, sizeof *new);
155
0
  if (*string != '\0') {
156
0
    new->type = ARGS_STRING;
157
0
    new->string = xstrdup(string);
158
0
    goto out;
159
0
  }
160
161
0
  if (*i == count)
162
0
    argument = NULL;
163
0
  else {
164
0
    argument = &values[*i];
165
0
    if (argument->type != ARGS_STRING) {
166
0
      xasprintf(cause, "-%c argument must be a string", flag);
167
0
      args_free_value(new);
168
0
      free(new);
169
0
      return (-1);
170
0
    }
171
0
  }
172
0
  if (argument == NULL) {
173
0
    args_free_value(new);
174
0
    free(new);
175
0
    if (optional_argument) {
176
0
      log_debug("%s: -%c (optional)", __func__, flag);
177
0
      args_set(args, flag, NULL, ARGS_ENTRY_OPTIONAL_VALUE);
178
0
      return (0); /* either - or end */
179
0
    }
180
0
    xasprintf(cause, "-%c expects an argument", flag);
181
0
    return (-1);
182
0
  }
183
0
  args_copy_value(new, argument);
184
0
  (*i)++;
185
186
0
out:
187
0
  s = args_value_as_string(new);
188
0
  log_debug("%s: -%c = %s", __func__, flag, s);
189
0
  args_set(args, flag, new, 0);
190
0
  return (0);
191
0
}
192
193
/* Parse flags argument. */
194
static int
195
args_parse_flags(const struct args_parse *parse, struct args_value *values,
196
    u_int count, char **cause, struct args *args, u_int *i)
197
0
{
198
0
  struct args_value *value;
199
0
  u_char       flag;
200
0
  const char    *found, *string;
201
0
  int      optional_argument;
202
203
0
  value = &values[*i];
204
0
  if (value->type != ARGS_STRING)
205
0
    return (1);
206
207
0
  string = value->string;
208
0
  log_debug("%s: next %s", __func__, string);
209
0
  if (*string++ != '-' || *string == '\0')
210
0
    return (1);
211
0
  (*i)++;
212
0
  if (string[0] == '-' && string[1] == '\0')
213
0
    return (1);
214
215
0
  for (;;) {
216
0
    flag = *string++;
217
0
    if (flag == '\0')
218
0
      return (0);
219
0
    if (flag == '?')
220
0
      return (-1);
221
0
    if (!isalnum(flag)) {
222
0
      xasprintf(cause, "invalid flag -%c", flag);
223
0
      return (-1);
224
0
    }
225
226
0
    found = strchr(parse->template, flag);
227
0
    if (found == NULL) {
228
0
      xasprintf(cause, "unknown flag -%c", flag);
229
0
      return (-1);
230
0
    }
231
0
    if (found[1] != ':') {
232
0
      log_debug("%s: -%c", __func__, flag);
233
0
      args_set(args, flag, NULL, 0);
234
0
      continue;
235
0
    }
236
0
    optional_argument = (found[2] == ':');
237
0
    return (args_parse_flag_argument(values, count, cause, args, i,
238
0
        string, flag, optional_argument));
239
0
  }
240
0
}
241
242
/* Parse arguments into a new argument set. */
243
struct args *
244
args_parse(const struct args_parse *parse, struct args_value *values,
245
    u_int count, char **cause)
246
0
{
247
0
  struct args   *args;
248
0
  u_int      i;
249
0
  enum args_parse_type   type;
250
0
  struct args_value *value, *new;
251
0
  const char    *s;
252
0
  int      stop;
253
254
0
  if (count == 0)
255
0
    return (args_create());
256
257
0
  args = args_create();
258
0
  for (i = 1; i < count; /* nothing */) {
259
0
    stop = args_parse_flags(parse, values, count, cause, args, &i);
260
0
    if (stop == -1) {
261
0
      args_free(args);
262
0
      return (NULL);
263
0
    }
264
0
    if (stop == 1)
265
0
      break;
266
0
  }
267
0
  log_debug("%s: flags end at %u of %u", __func__, i, count);
268
0
  if (i != count) {
269
0
    for (/* nothing */; i < count; i++) {
270
0
      value = &values[i];
271
272
0
      s = args_value_as_string(value);
273
0
      log_debug("%s: %u = %s (type %s)", __func__, i, s,
274
0
          args_type_to_string (value->type));
275
276
0
      if (parse->cb != NULL) {
277
0
        type = parse->cb(args, args->count, cause);
278
0
        if (type == ARGS_PARSE_INVALID) {
279
0
          args_free(args);
280
0
          return (NULL);
281
0
        }
282
0
      } else
283
0
        type = ARGS_PARSE_STRING;
284
285
0
      args->values = xrecallocarray(args->values,
286
0
          args->count, args->count + 1, sizeof *args->values);
287
0
      new = &args->values[args->count++];
288
289
0
      switch (type) {
290
0
      case ARGS_PARSE_INVALID:
291
0
        fatalx("unexpected argument type");
292
0
      case ARGS_PARSE_STRING:
293
0
        if (value->type != ARGS_STRING) {
294
0
          xasprintf(cause,
295
0
              "argument %u must be \"string\"",
296
0
              args->count);
297
0
          args_free(args);
298
0
          return (NULL);
299
0
        }
300
0
        args_copy_value(new, value);
301
0
        break;
302
0
      case ARGS_PARSE_COMMANDS_OR_STRING:
303
0
        args_copy_value(new, value);
304
0
        break;
305
0
      case ARGS_PARSE_COMMANDS:
306
0
        if (value->type != ARGS_COMMANDS) {
307
0
          xasprintf(cause,
308
0
              "argument %u must be { commands }",
309
0
              args->count);
310
0
          args_free(args);
311
0
          return (NULL);
312
0
        }
313
0
        args_copy_value(new, value);
314
0
        break;
315
0
      }
316
0
    }
317
0
  }
318
319
0
  if (parse->lower != -1 && args->count < (u_int)parse->lower) {
320
0
    xasprintf(cause,
321
0
        "too few arguments (need at least %u)",
322
0
        parse->lower);
323
0
    args_free(args);
324
0
    return (NULL);
325
0
  }
326
0
  if (parse->upper != -1 && args->count > (u_int)parse->upper) {
327
0
    xasprintf(cause,
328
0
        "too many arguments (need at most %u)",
329
0
        parse->upper);
330
0
    args_free(args);
331
0
    return (NULL);
332
0
  }
333
0
  return (args);
334
0
}
335
336
/* Copy and expand a value. */
337
static void
338
args_copy_copy_value(struct args_value *to, struct args_value *from, int argc,
339
    char **argv)
340
0
{
341
0
  char  *s, *expanded;
342
0
  int  i;
343
344
0
  to->type = from->type;
345
0
  switch (from->type) {
346
0
  case ARGS_NONE:
347
0
    break;
348
0
  case ARGS_STRING:
349
0
    expanded = xstrdup(from->string);
350
0
    for (i = 0; i < argc; i++) {
351
0
      s = cmd_template_replace(expanded, argv[i], i + 1);
352
0
      free(expanded);
353
0
      expanded = s;
354
0
    }
355
0
    to->string = expanded;
356
0
    break;
357
0
  case ARGS_COMMANDS:
358
0
    to->cmdlist = cmd_list_copy(from->cmdlist, argc, argv);
359
0
    break;
360
0
  }
361
0
}
362
363
/* Copy an arguments set. */
364
struct args *
365
args_copy(struct args *args, int argc, char **argv)
366
0
{
367
0
  struct args   *new_args;
368
0
  struct args_entry *entry;
369
0
  struct args_value *value, *new_value;
370
0
  u_int      i;
371
372
0
  cmd_log_argv(argc, argv, "%s", __func__);
373
374
0
  new_args = args_create();
375
0
  RB_FOREACH(entry, args_tree, &args->tree) {
376
0
    if (TAILQ_EMPTY(&entry->values)) {
377
0
      for (i = 0; i < entry->count; i++)
378
0
        args_set(new_args, entry->flag, NULL, 0);
379
0
      continue;
380
0
    }
381
0
    TAILQ_FOREACH(value, &entry->values, entry) {
382
0
      new_value = xcalloc(1, sizeof *new_value);
383
0
      args_copy_copy_value(new_value, value, argc, argv);
384
0
      args_set(new_args, entry->flag, new_value, 0);
385
0
    }
386
0
  }
387
0
  if (args->count == 0)
388
0
    return (new_args);
389
0
  new_args->count = args->count;
390
0
  new_args->values = xcalloc(args->count, sizeof *new_args->values);
391
0
  for (i = 0; i < args->count; i++) {
392
0
    new_value = &new_args->values[i];
393
0
    args_copy_copy_value(new_value, &args->values[i], argc, argv);
394
0
  }
395
0
  return (new_args);
396
0
}
397
398
/* Free a value. */
399
void
400
args_free_value(struct args_value *value)
401
0
{
402
0
  switch (value->type) {
403
0
  case ARGS_NONE:
404
0
    break;
405
0
  case ARGS_STRING:
406
0
    free(value->string);
407
0
    break;
408
0
  case ARGS_COMMANDS:
409
0
    cmd_list_free(value->cmdlist);
410
0
    break;
411
0
  }
412
0
  free(value->cached);
413
0
}
414
415
/* Free values. */
416
void
417
args_free_values(struct args_value *values, u_int count)
418
0
{
419
0
  u_int i;
420
421
0
  for (i = 0; i < count; i++)
422
0
    args_free_value(&values[i]);
423
0
}
424
425
/* Free an arguments set. */
426
void
427
args_free(struct args *args)
428
0
{
429
0
  struct args_entry *entry;
430
0
  struct args_entry *entry1;
431
0
  struct args_value *value;
432
0
  struct args_value *value1;
433
434
0
  args_free_values(args->values, args->count);
435
0
  free(args->values);
436
437
0
  RB_FOREACH_SAFE(entry, args_tree, &args->tree, entry1) {
438
0
    RB_REMOVE(args_tree, &args->tree, entry);
439
0
    TAILQ_FOREACH_SAFE(value, &entry->values, entry, value1) {
440
0
      TAILQ_REMOVE(&entry->values, value, entry);
441
0
      args_free_value(value);
442
0
      free(value);
443
0
    }
444
0
    free(entry);
445
0
  }
446
447
0
  free(args);
448
0
}
449
450
/* Convert arguments to vector. */
451
void
452
args_to_vector(struct args *args, int *argc, char ***argv)
453
0
{
454
0
  char  *s;
455
0
  u_int  i;
456
457
0
  *argc = 0;
458
0
  *argv = NULL;
459
460
0
  for (i = 0; i < args->count; i++) {
461
0
    switch (args->values[i].type) {
462
0
    case ARGS_NONE:
463
0
      break;
464
0
    case ARGS_STRING:
465
0
      cmd_append_argv(argc, argv, args->values[i].string);
466
0
      break;
467
0
    case ARGS_COMMANDS:
468
0
      s = cmd_list_print(args->values[i].cmdlist, 0);
469
0
      cmd_append_argv(argc, argv, s);
470
0
      free(s);
471
0
      break;
472
0
    }
473
0
  }
474
0
}
475
476
/* Convert arguments from vector. */
477
struct args_value *
478
args_from_vector(int argc, char **argv)
479
0
{
480
0
  struct args_value *values;
481
0
  int      i;
482
483
0
  values = xcalloc(argc, sizeof *values);
484
0
  for (i = 0; i < argc; i++) {
485
0
    values[i].type = ARGS_STRING;
486
0
    values[i].string = xstrdup(argv[i]);
487
0
  }
488
0
  return (values);
489
0
}
490
491
/* Add to string. */
492
static void printflike(3, 4)
493
args_print_add(char **buf, size_t *len, const char *fmt, ...)
494
0
{
495
0
  va_list  ap;
496
0
  char  *s;
497
0
  size_t   slen;
498
499
0
  va_start(ap, fmt);
500
0
  slen = xvasprintf(&s, fmt, ap);
501
0
  va_end(ap);
502
503
0
  *len += slen;
504
0
  *buf = xrealloc(*buf, *len);
505
506
0
  strlcat(*buf, s, *len);
507
0
  free(s);
508
0
}
509
510
/* Add value to string. */
511
static void
512
args_print_add_value(char **buf, size_t *len, struct args_value *value)
513
0
{
514
0
  char  *expanded = NULL;
515
516
0
  if (**buf != '\0')
517
0
    args_print_add(buf, len, " ");
518
519
0
  switch (value->type) {
520
0
  case ARGS_NONE:
521
0
    break;
522
0
  case ARGS_COMMANDS:
523
0
    expanded = cmd_list_print(value->cmdlist, 0);
524
0
    args_print_add(buf, len, "{ %s }", expanded);
525
0
    break;
526
0
  case ARGS_STRING:
527
0
    expanded = args_escape(value->string);
528
0
    args_print_add(buf, len, "%s", expanded);
529
0
    break;
530
0
  }
531
0
  free(expanded);
532
0
}
533
534
/* Print a set of arguments. */
535
char *
536
args_print(struct args *args)
537
0
{
538
0
  size_t       len;
539
0
  char      *buf;
540
0
  u_int      i, j;
541
0
  struct args_entry *entry;
542
0
  struct args_entry *last = NULL;
543
0
  struct args_value *value;
544
545
0
  len = 1;
546
0
  buf = xcalloc(1, len);
547
548
  /* Process the flags first. */
549
0
  RB_FOREACH(entry, args_tree, &args->tree) {
550
0
    if (entry->flags & ARGS_ENTRY_OPTIONAL_VALUE)
551
0
      continue;
552
0
    if (!TAILQ_EMPTY(&entry->values))
553
0
      continue;
554
555
0
    if (*buf == '\0')
556
0
      args_print_add(&buf, &len, "-");
557
0
    for (j = 0; j < entry->count; j++)
558
0
      args_print_add(&buf, &len, "%c", entry->flag);
559
0
  }
560
561
  /* Then the flags with arguments. */
562
0
  RB_FOREACH(entry, args_tree, &args->tree) {
563
0
    if (entry->flags & ARGS_ENTRY_OPTIONAL_VALUE) {
564
0
      if (*buf != '\0')
565
0
        args_print_add(&buf, &len, " -%c", entry->flag);
566
0
      else
567
0
        args_print_add(&buf, &len, "-%c", entry->flag);
568
0
      last = entry;
569
0
      continue;
570
0
    }
571
0
    if (TAILQ_EMPTY(&entry->values))
572
0
      continue;
573
0
    TAILQ_FOREACH(value, &entry->values, entry) {
574
0
      if (*buf != '\0')
575
0
        args_print_add(&buf, &len, " -%c", entry->flag);
576
0
      else
577
0
        args_print_add(&buf, &len, "-%c", entry->flag);
578
0
      args_print_add_value(&buf, &len, value);
579
0
    }
580
0
    last = entry;
581
0
  }
582
0
  if (last && (last->flags & ARGS_ENTRY_OPTIONAL_VALUE))
583
0
    args_print_add(&buf, &len, " --");
584
585
  /* And finally the argument vector. */
586
0
  for (i = 0; i < args->count; i++)
587
0
    args_print_add_value(&buf, &len, &args->values[i]);
588
589
0
  return (buf);
590
0
}
591
592
/* Escape an argument. */
593
char *
594
args_escape(const char *s)
595
0
{
596
0
  static const char  dquoted[] = " #';${}%";
597
0
  static const char  squoted[] = " \"";
598
0
  char      *escaped, *result;
599
0
  int      flags, quotes = 0;
600
601
0
  if (*s == '\0') {
602
0
    xasprintf(&result, "''");
603
0
    return (result);
604
0
  }
605
0
  if (s[strcspn(s, dquoted)] != '\0')
606
0
    quotes = '"';
607
0
  else if (s[strcspn(s, squoted)] != '\0')
608
0
    quotes = '\'';
609
610
0
  if (s[0] != ' ' &&
611
0
      s[1] == '\0' &&
612
0
      (quotes != 0 || s[0] == '~')) {
613
0
    xasprintf(&escaped, "\\%c", s[0]);
614
0
    return (escaped);
615
0
  }
616
617
0
  flags = VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL;
618
0
  if (quotes == '"')
619
0
    flags |= VIS_DQ;
620
0
  utf8_stravis(&escaped, s, flags);
621
622
0
  if (quotes == '\'')
623
0
    xasprintf(&result, "'%s'", escaped);
624
0
  else if (quotes == '"') {
625
0
    if (*escaped == '~')
626
0
      xasprintf(&result, "\"\\%s\"", escaped);
627
0
    else
628
0
      xasprintf(&result, "\"%s\"", escaped);
629
0
  } else {
630
0
    if (*escaped == '~')
631
0
      xasprintf(&result, "\\%s", escaped);
632
0
    else
633
0
      result = xstrdup(escaped);
634
0
  }
635
0
  free(escaped);
636
0
  return (result);
637
0
}
638
639
/* Return if an argument is present. */
640
int
641
args_has(struct args *args, u_char flag)
642
0
{
643
0
  struct args_entry *entry;
644
645
0
  entry = args_find(args, flag);
646
0
  if (entry == NULL)
647
0
    return (0);
648
0
  return (entry->count);
649
0
}
650
651
/* Set argument value in the arguments tree. */
652
void
653
args_set(struct args *args, u_char flag, struct args_value *value, int flags)
654
0
{
655
0
  struct args_entry *entry;
656
657
0
  entry = args_find(args, flag);
658
0
  if (entry == NULL) {
659
0
    entry = xcalloc(1, sizeof *entry);
660
0
    entry->flag = flag;
661
0
    entry->count = 1;
662
0
    entry->flags = flags;
663
0
    TAILQ_INIT(&entry->values);
664
0
    RB_INSERT(args_tree, &args->tree, entry);
665
0
  } else
666
0
    entry->count++;
667
0
  if (value != NULL && value->type != ARGS_NONE)
668
0
    TAILQ_INSERT_TAIL(&entry->values, value, entry);
669
0
  else
670
0
    free(value);
671
0
}
672
673
/* Get argument value. Will be NULL if it isn't present. */
674
const char *
675
args_get(struct args *args, u_char flag)
676
0
{
677
0
  struct args_entry *entry;
678
679
0
  if ((entry = args_find(args, flag)) == NULL)
680
0
    return (NULL);
681
0
  if (TAILQ_EMPTY(&entry->values))
682
0
    return (NULL);
683
0
  return (TAILQ_LAST(&entry->values, args_values)->string);
684
0
}
685
686
/* Get first argument. */
687
u_char
688
args_first(struct args *args, struct args_entry **entry)
689
0
{
690
0
  *entry = RB_MIN(args_tree, &args->tree);
691
0
  if (*entry == NULL)
692
0
    return (0);
693
0
  return ((*entry)->flag);
694
0
}
695
696
/* Get next argument. */
697
u_char
698
args_next(struct args_entry **entry)
699
0
{
700
0
  *entry = RB_NEXT(args_tree, &args->tree, *entry);
701
0
  if (*entry == NULL)
702
0
    return (0);
703
0
  return ((*entry)->flag);
704
0
}
705
706
/* Get argument count. */
707
u_int
708
args_count(struct args *args)
709
0
{
710
0
  return (args->count);
711
0
}
712
713
/* Get argument values. */
714
struct args_value *
715
args_values(struct args *args)
716
0
{
717
0
  return (args->values);
718
0
}
719
720
/* Get argument value. */
721
struct args_value *
722
args_value(struct args *args, u_int idx)
723
0
{
724
0
  if (idx >= args->count)
725
0
    return (NULL);
726
0
  return (&args->values[idx]);
727
0
}
728
729
/* Return argument as string. */
730
const char *
731
args_string(struct args *args, u_int idx)
732
0
{
733
0
  if (idx >= args->count)
734
0
    return (NULL);
735
0
  return (args_value_as_string(&args->values[idx]));
736
0
}
737
738
/* Make a command now. */
739
struct cmd_list *
740
args_make_commands_now(struct cmd *self, struct cmdq_item *item, u_int idx,
741
    int expand)
742
0
{
743
0
  struct args_command_state *state;
744
0
  char        *error;
745
0
  struct cmd_list     *cmdlist;
746
747
0
  state = args_make_commands_prepare(self, item, idx, NULL, 0, expand);
748
0
  cmdlist = args_make_commands(state, 0, NULL, &error);
749
0
  if (cmdlist == NULL) {
750
0
    cmdq_error(item, "%s", error);
751
0
    free(error);
752
0
  }
753
0
  else
754
0
    cmdlist->references++;
755
0
  args_make_commands_free(state);
756
0
  return (cmdlist);
757
0
}
758
759
/* Save bits to make a command later. */
760
struct args_command_state *
761
args_make_commands_prepare(struct cmd *self, struct cmdq_item *item, u_int idx,
762
    const char *default_command, int wait, int expand)
763
0
{
764
0
  struct args     *args = cmd_get_args(self);
765
0
  struct cmd_find_state   *target = cmdq_get_target(item);
766
0
  struct client     *tc = cmdq_get_target_client(item);
767
0
  struct args_value   *value;
768
0
  struct args_command_state *state;
769
0
  const char      *cmd;
770
0
  const char      *file;
771
772
0
  state = xcalloc(1, sizeof *state);
773
774
0
  if (idx < args->count) {
775
0
    value = &args->values[idx];
776
0
    if (value->type == ARGS_COMMANDS) {
777
0
      state->cmdlist = value->cmdlist;
778
0
      state->cmdlist->references++;
779
0
      return (state);
780
0
    }
781
0
    cmd = value->string;
782
0
  } else {
783
0
    if (default_command == NULL)
784
0
      fatalx("argument out of range");
785
0
    cmd = default_command;
786
0
  }
787
788
789
0
  if (expand)
790
0
    state->cmd = format_single_from_target(item, cmd);
791
0
  else
792
0
    state->cmd = xstrdup(cmd);
793
0
  log_debug("%s: %s", __func__, state->cmd);
794
795
0
  if (wait)
796
0
    state->pi.item = item;
797
0
  cmd_get_source(self, &file, &state->pi.line);
798
0
  if (file != NULL)
799
0
    state->pi.file = xstrdup(file);
800
0
  state->pi.c = tc;
801
0
  if (state->pi.c != NULL)
802
0
    state->pi.c->references++;
803
0
  cmd_find_copy_state(&state->pi.fs, target);
804
805
0
  return (state);
806
0
}
807
808
/* Return argument as command. */
809
struct cmd_list *
810
args_make_commands(struct args_command_state *state, int argc, char **argv,
811
    char **error)
812
0
{
813
0
  struct cmd_parse_result *pr;
814
0
  char      *cmd, *new_cmd;
815
0
  int      i;
816
817
0
  if (state->cmdlist != NULL) {
818
0
    if (argc == 0)
819
0
      return (state->cmdlist);
820
0
    return (cmd_list_copy(state->cmdlist, argc, argv));
821
0
  }
822
823
0
  cmd = xstrdup(state->cmd);
824
0
  log_debug("%s: %s", __func__, cmd);
825
0
  cmd_log_argv(argc, argv, __func__);
826
0
  for (i = 0; i < argc; i++) {
827
0
    new_cmd = cmd_template_replace(cmd, argv[i], i + 1);
828
0
    log_debug("%s: %%%u %s: %s", __func__, i + 1, argv[i], new_cmd);
829
0
    free(cmd);
830
0
    cmd = new_cmd;
831
0
  }
832
0
  log_debug("%s: %s", __func__, cmd);
833
834
0
  pr = cmd_parse_from_string(cmd, &state->pi);
835
0
  free(cmd);
836
0
  switch (pr->status) {
837
0
  case CMD_PARSE_ERROR:
838
0
    *error = pr->error;
839
0
    return (NULL);
840
0
  case CMD_PARSE_SUCCESS:
841
0
    return (pr->cmdlist);
842
0
  }
843
0
  fatalx("invalid parse return state");
844
0
}
845
846
/* Free commands state. */
847
void
848
args_make_commands_free(struct args_command_state *state)
849
0
{
850
0
  if (state->cmdlist != NULL)
851
0
    cmd_list_free(state->cmdlist);
852
0
  if (state->pi.c != NULL)
853
0
    server_client_unref(state->pi.c);
854
0
  free((void *)state->pi.file);
855
0
  free(state->cmd);
856
0
  free(state);
857
0
}
858
859
/* Get prepared command. */
860
char *
861
args_make_commands_get_command(struct args_command_state *state)
862
0
{
863
0
  struct cmd  *first;
864
0
  int    n;
865
0
  char    *s;
866
867
0
  if (state->cmdlist != NULL) {
868
0
    first = cmd_list_first(state->cmdlist);
869
0
    if (first == NULL)
870
0
      return (xstrdup(""));
871
0
    return (xstrdup(cmd_get_entry(first)->name));
872
0
  }
873
0
  n = strcspn(state->cmd, " ,");
874
0
  xasprintf(&s, "%.*s", n, state->cmd);
875
0
  return (s);
876
0
}
877
878
/* Get first value in argument. */
879
struct args_value *
880
args_first_value(struct args *args, u_char flag)
881
0
{
882
0
  struct args_entry *entry;
883
884
0
  if ((entry = args_find(args, flag)) == NULL)
885
0
    return (NULL);
886
0
  return (TAILQ_FIRST(&entry->values));
887
0
}
888
889
/* Get next value in argument. */
890
struct args_value *
891
args_next_value(struct args_value *value)
892
0
{
893
0
  return (TAILQ_NEXT(value, entry));
894
0
}
895
896
/* Convert an argument value to a number. */
897
long long
898
args_strtonum(struct args *args, u_char flag, long long minval,
899
    long long maxval, char **cause)
900
0
{
901
0
  const char    *errstr;
902
0
  long long    ll;
903
0
  struct args_entry *entry;
904
0
  struct args_value *value;
905
906
0
  if ((entry = args_find(args, flag)) == NULL) {
907
0
    *cause = xstrdup("missing");
908
0
    return (0);
909
0
  }
910
0
  value = TAILQ_LAST(&entry->values, args_values);
911
0
  if (value == NULL ||
912
0
      value->type != ARGS_STRING ||
913
0
      value->string == NULL) {
914
0
    *cause = xstrdup("missing");
915
0
    return (0);
916
0
  }
917
918
0
  ll = strtonum(value->string, minval, maxval, &errstr);
919
0
  if (errstr != NULL) {
920
0
    *cause = xstrdup(errstr);
921
0
    return (0);
922
0
  }
923
924
0
  *cause = NULL;
925
0
  return (ll);
926
0
}
927
928
/* Convert an argument value to a number, and expand formats. */
929
long long
930
args_strtonum_and_expand(struct args *args, u_char flag, long long minval,
931
    long long maxval, struct cmdq_item *item, char **cause)
932
0
{
933
0
  const char    *errstr;
934
0
  char      *formatted;
935
0
  long long    ll;
936
0
  struct args_entry *entry;
937
0
  struct args_value *value;
938
939
0
  if ((entry = args_find(args, flag)) == NULL) {
940
0
    *cause = xstrdup("missing");
941
0
    return (0);
942
0
  }
943
0
  value = TAILQ_LAST(&entry->values, args_values);
944
0
  if (value == NULL ||
945
0
      value->type != ARGS_STRING ||
946
0
      value->string == NULL) {
947
0
    *cause = xstrdup("missing");
948
0
    return (0);
949
0
  }
950
951
0
  formatted = format_single_from_target(item, value->string);
952
0
  ll = strtonum(formatted, minval, maxval, &errstr);
953
0
  free(formatted);
954
0
  if (errstr != NULL) {
955
0
    *cause = xstrdup(errstr);
956
0
    return (0);
957
0
  }
958
959
0
  *cause = NULL;
960
0
  return (ll);
961
0
}
962
963
/* Convert an argument to a number which may be a percentage. */
964
long long
965
args_percentage(struct args *args, u_char flag, long long minval,
966
    long long maxval, long long curval, char **cause)
967
0
{
968
0
  const char    *value;
969
0
  struct args_entry *entry;
970
971
0
  if ((entry = args_find(args, flag)) == NULL) {
972
0
    *cause = xstrdup("missing");
973
0
    return (0);
974
0
  }
975
0
  if (TAILQ_EMPTY(&entry->values)) {
976
0
    *cause = xstrdup("empty");
977
0
    return (0);
978
0
  }
979
0
  value = TAILQ_LAST(&entry->values, args_values)->string;
980
0
  return (args_string_percentage(value, minval, maxval, curval, cause));
981
0
}
982
983
/* Convert a string to a number which may be a percentage. */
984
long long
985
args_string_percentage(const char *value, long long minval, long long maxval,
986
    long long curval, char **cause)
987
0
{
988
0
  const char  *errstr;
989
0
  long long  ll;
990
0
  size_t     valuelen = strlen(value);
991
0
  char    *copy;
992
993
0
  if (valuelen == 0) {
994
0
    *cause = xstrdup("empty");
995
0
    return (0);
996
0
  }
997
0
  if (value[valuelen - 1] == '%') {
998
0
    copy = xstrdup(value);
999
0
    copy[valuelen - 1] = '\0';
1000
1001
0
    ll = strtonum(copy, 0, 100, &errstr);
1002
0
    free(copy);
1003
0
    if (errstr != NULL) {
1004
0
      *cause = xstrdup(errstr);
1005
0
      return (0);
1006
0
    }
1007
0
    ll = (curval * ll) / 100;
1008
0
    if (ll < minval) {
1009
0
      *cause = xstrdup("too small");
1010
0
      return (0);
1011
0
    }
1012
0
    if (ll > maxval) {
1013
0
      *cause = xstrdup("too large");
1014
0
      return (0);
1015
0
    }
1016
0
  } else {
1017
0
    ll = strtonum(value, minval, maxval, &errstr);
1018
0
    if (errstr != NULL) {
1019
0
      *cause = xstrdup(errstr);
1020
0
      return (0);
1021
0
    }
1022
0
  }
1023
1024
0
  *cause = NULL;
1025
0
  return (ll);
1026
0
}
1027
1028
/*
1029
 * Convert an argument to a number which may be a percentage, and expand
1030
 * formats.
1031
 */
1032
long long
1033
args_percentage_and_expand(struct args *args, u_char flag, long long minval,
1034
    long long maxval, long long curval, struct cmdq_item *item, char **cause)
1035
0
{
1036
0
  const char    *value;
1037
0
  struct args_entry *entry;
1038
1039
0
  if ((entry = args_find(args, flag)) == NULL) {
1040
0
    *cause = xstrdup("missing");
1041
0
    return (0);
1042
0
  }
1043
0
  if (TAILQ_EMPTY(&entry->values)) {
1044
0
    *cause = xstrdup("empty");
1045
0
    return (0);
1046
0
  }
1047
0
  value = TAILQ_LAST(&entry->values, args_values)->string;
1048
0
  return (args_string_percentage_and_expand(value, minval, maxval, curval,
1049
0
        item, cause));
1050
0
}
1051
1052
/*
1053
 * Convert a string to a number which may be a percentage, and expand formats.
1054
 */
1055
long long
1056
args_string_percentage_and_expand(const char *value, long long minval,
1057
    long long maxval, long long curval, struct cmdq_item *item, char **cause)
1058
0
{
1059
0
  const char  *errstr;
1060
0
  long long  ll;
1061
0
  size_t     valuelen = strlen(value);
1062
0
  char    *copy, *f;
1063
1064
0
  if (value[valuelen - 1] == '%') {
1065
0
    copy = xstrdup(value);
1066
0
    copy[valuelen - 1] = '\0';
1067
1068
0
    f = format_single_from_target(item, copy);
1069
0
    ll = strtonum(f, 0, 100, &errstr);
1070
0
    free(f);
1071
0
    free(copy);
1072
0
    if (errstr != NULL) {
1073
0
      *cause = xstrdup(errstr);
1074
0
      return (0);
1075
0
    }
1076
0
    ll = (curval * ll) / 100;
1077
0
    if (ll < minval) {
1078
0
      *cause = xstrdup("too small");
1079
0
      return (0);
1080
0
    }
1081
0
    if (ll > maxval) {
1082
0
      *cause = xstrdup("too large");
1083
0
      return (0);
1084
0
    }
1085
0
  } else {
1086
0
    f = format_single_from_target(item, value);
1087
0
    ll = strtonum(f, minval, maxval, &errstr);
1088
0
    free(f);
1089
0
    if (errstr != NULL) {
1090
0
      *cause = xstrdup(errstr);
1091
0
      return (0);
1092
0
    }
1093
0
  }
1094
1095
0
  *cause = NULL;
1096
0
  return (ll);
1097
0
}