Coverage Report

Created: 2023-11-27 06:25

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