Coverage Report

Created: 2026-07-30 07:05

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