Coverage Report

Created: 2026-08-13 06:07

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
24.5k
#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
248k
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);
arguments.c:args_tree_RB_REMOVE
Line
Count
Source
64
RB_GENERATE_STATIC(args_tree, args_entry, entry, args_cmp);
arguments.c:args_tree_RB_REMOVE_COLOR
Line
Count
Source
64
RB_GENERATE_STATIC(args_tree, args_entry, entry, args_cmp);
arguments.c:args_tree_RB_FIND
Line
Count
Source
64
RB_GENERATE_STATIC(args_tree, args_entry, entry, args_cmp);
arguments.c:args_tree_RB_INSERT
Line
Count
Source
64
RB_GENERATE_STATIC(args_tree, args_entry, entry, args_cmp);
65
248k
66
248k
/* Arguments tree comparison function. */
67
248k
static int
68
248k
args_cmp(struct args_entry *a1, struct args_entry *a2)
69
248k
{
70
96.6k
  return (a1->flag - a2->flag);
71
96.6k
}
72
73
/* Find a flag in the arguments tree. */
74
static struct args_entry *
75
args_find(struct args *args, u_char flag)
76
28.9k
{
77
28.9k
  struct args_entry entry;
78
79
28.9k
  entry.flag = flag;
80
28.9k
  return (RB_FIND(args_tree, &args->tree, &entry));
81
28.9k
}
82
83
/* Copy value. */
84
static void
85
args_copy_value(struct args_value *to, struct args_value *from)
86
25.0k
{
87
25.0k
  to->type = from->type;
88
25.0k
  switch (from->type) {
89
0
  case ARGS_NONE:
90
0
    break;
91
993
  case ARGS_COMMANDS:
92
993
    to->cmdlist = from->cmdlist;
93
993
    to->cmdlist->references++;
94
993
    break;
95
24.1k
  case ARGS_STRING:
96
24.1k
    to->string = xstrdup(from->string);
97
24.1k
    break;
98
25.0k
  }
99
25.0k
}
100
101
/* Type to string. */
102
static const char *
103
args_type_to_string (enum args_type type)
104
24.3k
{
105
24.3k
  switch (type)
106
24.3k
  {
107
0
  case ARGS_NONE:
108
0
    return "NONE";
109
23.3k
  case ARGS_STRING:
110
23.3k
    return "STRING";
111
1.05k
  case ARGS_COMMANDS:
112
1.05k
    return "COMMANDS";
113
24.3k
  }
114
0
  return "INVALID";
115
24.3k
}
116
117
/* Get value as string. */
118
static const char *
119
args_value_as_string(struct args_value *value)
120
1.20M
{
121
1.20M
  switch (value->type) {
122
0
  case ARGS_NONE:
123
0
    return ("");
124
1.05k
  case ARGS_COMMANDS:
125
1.05k
    if (value->cached == NULL)
126
1.05k
      value->cached = cmd_list_print(value->cmdlist, 0);
127
1.05k
    return (value->cached);
128
1.19M
  case ARGS_STRING:
129
1.19M
    return (value->string);
130
1.20M
  }
131
0
  fatalx("unexpected argument type");
132
1.20M
}
133
134
/* Create an empty arguments set. */
135
struct args *
136
args_create(void)
137
7.66k
{
138
7.66k
  struct args  *args;
139
140
7.66k
  args = xcalloc(1, sizeof *args);
141
7.66k
  RB_INIT(&args->tree);
142
7.66k
  return (args);
143
7.66k
}
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
3.19k
{
151
3.19k
  struct args_value *argument, *new;
152
3.19k
  const char    *s, *as;
153
154
3.19k
  new = xcalloc(1, sizeof *new);
155
3.19k
  if (*string != '\0') {
156
2.38k
    new->type = ARGS_STRING;
157
2.38k
    new->string = xstrdup(string);
158
2.38k
    goto out;
159
2.38k
  }
160
161
804
  if (*i == count)
162
7
    argument = NULL;
163
797
  else {
164
797
    argument = &values[*i];
165
797
    if (argument->type != ARGS_STRING) {
166
1
      xasprintf(cause, "-%c argument must be a string", flag);
167
1
      args_free_value(new);
168
1
      free(new);
169
1
      return (-1);
170
1
    }
171
797
  }
172
803
  if (argument == NULL) {
173
7
    args_free_value(new);
174
7
    free(new);
175
7
    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
7
    xasprintf(cause, "-%c expects an argument", flag);
181
7
    return (-1);
182
7
  }
183
184
796
  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
796
  args_copy_value(new, argument);
195
796
  (*i)++;
196
197
3.18k
out:
198
3.18k
  s = args_value_as_string(new);
199
3.18k
  log_debug("%s: -%c = %s", __func__, flag, s);
200
201
3.18k
  args_set(args, flag, new, 0);
202
3.18k
  return (0);
203
796
}
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
9.80k
{
210
9.80k
  struct args_value *value;
211
9.80k
  u_char       flag;
212
9.80k
  const char    *found, *string;
213
9.80k
  int      optional_argument;
214
215
9.80k
  value = &values[*i];
216
9.80k
  if (value->type != ARGS_STRING)
217
664
    return (1);
218
219
9.14k
  string = value->string;
220
9.14k
  log_debug("%s: next %s", __func__, string);
221
9.14k
  if (*string++ != '-' || *string == '\0')
222
4.15k
    return (1);
223
4.99k
  (*i)++;
224
4.99k
  if (string[0] == '-' && string[1] == '\0')
225
213
    return (1);
226
227
24.8k
  for (;;) {
228
24.8k
    flag = *string++;
229
24.8k
    if (flag == '\0')
230
1.56k
      return (0);
231
23.3k
    if (flag == '?')
232
1
      return (-1);
233
23.3k
    if (!isalnum(flag)) {
234
21
      xasprintf(cause, "invalid flag -%c", flag);
235
21
      return (-1);
236
21
    }
237
238
23.3k
    found = strchr(parse->template, flag);
239
23.3k
    if (found == NULL) {
240
4
      xasprintf(cause, "unknown flag -%c", flag);
241
4
      return (-1);
242
4
    }
243
23.3k
    if (found[1] != ':') {
244
20.1k
      log_debug("%s: -%c", __func__, flag);
245
20.1k
      args_set(args, flag, NULL, 0);
246
20.1k
      continue;
247
20.1k
    }
248
3.19k
    optional_argument = (found[2] == ':');
249
3.19k
    return (args_parse_flag_argument(values, count, cause, args, i,
250
3.19k
        string, flag, optional_argument));
251
23.3k
  }
252
4.77k
}
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
7.66k
{
259
7.66k
  struct args   *args;
260
7.66k
  u_int      i;
261
7.66k
  enum args_parse_type   type;
262
7.66k
  struct args_value *value, *new;
263
7.66k
  const char    *s;
264
7.66k
  int      stop;
265
266
7.66k
  if (count == 0)
267
0
    return (args_create());
268
269
7.66k
  args = args_create();
270
12.4k
  for (i = 1; i < count; /* nothing */) {
271
9.80k
    stop = args_parse_flags(parse, values, count, cause, args, &i);
272
9.80k
    if (stop == -1) {
273
34
      args_free(args);
274
34
      return (NULL);
275
34
    }
276
9.77k
    if (stop == 1)
277
5.03k
      break;
278
9.77k
  }
279
7.63k
  log_debug("%s: flags end at %u of %u", __func__, i, count);
280
7.63k
  if (i != count) {
281
29.1k
    for (/* nothing */; i < count; i++) {
282
24.3k
      value = &values[i];
283
284
24.3k
      s = args_value_as_string(value);
285
24.3k
      log_debug("%s: %u = %s (type %s)", __func__, i, s,
286
24.3k
          args_type_to_string (value->type));
287
288
24.3k
      if (parse->cb != NULL) {
289
23.1k
        type = parse->cb(args, args->count, cause);
290
23.1k
        if (type == ARGS_PARSE_INVALID) {
291
0
          args_free(args);
292
0
          return (NULL);
293
0
        }
294
23.1k
      } else
295
1.22k
        type = ARGS_PARSE_STRING;
296
297
24.3k
      args->values = xrecallocarray(args->values,
298
24.3k
          args->count, args->count + 1, sizeof *args->values);
299
24.3k
      new = &args->values[args->count++];
300
301
24.3k
      switch (type) {
302
0
      case ARGS_PARSE_INVALID:
303
0
        fatalx("unexpected argument type");
304
17.5k
      case ARGS_PARSE_STRING:
305
17.5k
        if (value->type != ARGS_STRING) {
306
61
          xasprintf(cause,
307
61
              "argument %u must be \"string\"",
308
61
              args->count);
309
61
          args_free(args);
310
61
          return (NULL);
311
61
        }
312
17.4k
        args_copy_value(new, value);
313
17.4k
        break;
314
6.82k
      case ARGS_PARSE_COMMANDS_OR_STRING:
315
6.82k
        args_copy_value(new, value);
316
6.82k
        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
24.3k
      }
328
24.3k
    }
329
4.81k
  }
330
331
7.57k
  if (parse->lower != -1 && args->count < (u_int)parse->lower) {
332
196
    xasprintf(cause,
333
196
        "too few arguments (need at least %u)",
334
196
        parse->lower);
335
196
    args_free(args);
336
196
    return (NULL);
337
196
  }
338
7.37k
  if (parse->upper != -1 && args->count > (u_int)parse->upper) {
339
98
    xasprintf(cause,
340
98
        "too many arguments (need at most %u)",
341
98
        parse->upper);
342
98
    args_free(args);
343
98
    return (NULL);
344
98
  }
345
7.27k
  return (args);
346
7.37k
}
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
69.1k
{
414
69.1k
  switch (value->type) {
415
69
  case ARGS_NONE:
416
69
    break;
417
66.6k
  case ARGS_STRING:
418
66.6k
    free(value->string);
419
66.6k
    break;
420
2.45k
  case ARGS_COMMANDS:
421
2.45k
    cmd_list_free(value->cmdlist);
422
2.45k
    break;
423
69.1k
  }
424
69.1k
  free(value->cached);
425
69.1k
}
426
427
/* Free values. */
428
void
429
args_free_values(struct args_value *values, u_int count)
430
7.65k
{
431
7.65k
  u_int i;
432
433
32.0k
  for (i = 0; i < count; i++)
434
24.3k
    args_free_value(&values[i]);
435
7.65k
}
436
437
/* Free an arguments set. */
438
void
439
args_free(struct args *args)
440
7.65k
{
441
7.65k
  struct args_entry *entry;
442
7.65k
  struct args_entry *entry1;
443
7.65k
  struct args_value *value;
444
7.65k
  struct args_value *value1;
445
446
7.65k
  args_free_values(args->values, args->count);
447
7.65k
  free(args->values);
448
449
19.6k
  RB_FOREACH_SAFE(entry, args_tree, &args->tree, entry1) {
450
19.6k
    RB_REMOVE(args_tree, &args->tree, entry);
451
19.6k
    TAILQ_FOREACH_SAFE(value, &entry->values, entry, value1) {
452
3.18k
      TAILQ_REMOVE(&entry->values, value, entry);
453
3.18k
      args_free_value(value);
454
3.18k
      free(value);
455
3.18k
    }
456
19.6k
    free(entry);
457
19.6k
  }
458
459
7.65k
  free(args);
460
7.65k
}
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
64.4k
{
507
64.4k
  va_list  ap;
508
64.4k
  char  *s;
509
64.4k
  size_t   slen;
510
511
64.4k
  va_start(ap, fmt);
512
64.4k
  slen = xvasprintf(&s, fmt, ap);
513
64.4k
  va_end(ap);
514
515
64.4k
  *len += slen;
516
64.4k
  *buf = xrealloc(*buf, *len);
517
518
64.4k
  strlcat(*buf, s, *len);
519
64.4k
  free(s);
520
64.4k
}
521
522
/* Add value to string. */
523
static void
524
args_print_add_value(char **buf, size_t *len, struct args_value *value)
525
28.2k
{
526
28.2k
  char  *expanded = NULL;
527
528
28.2k
  if (**buf != '\0')
529
19.3k
    args_print_add(buf, len, " ");
530
531
28.2k
  switch (value->type) {
532
0
  case ARGS_NONE:
533
0
    break;
534
5.73k
  case ARGS_COMMANDS:
535
5.73k
    expanded = cmd_list_print(value->cmdlist, 0);
536
5.73k
    args_print_add(buf, len, "{ %s }", expanded);
537
5.73k
    break;
538
22.4k
  case ARGS_STRING:
539
22.4k
    expanded = args_escape(value->string);
540
22.4k
    args_print_add(buf, len, "%s", expanded);
541
22.4k
    break;
542
28.2k
  }
543
28.2k
  free(expanded);
544
28.2k
}
545
546
/* Print a set of arguments. */
547
char *
548
args_print(struct args *args)
549
13.6k
{
550
13.6k
  size_t       len;
551
13.6k
  char      *buf;
552
13.6k
  u_int      i, j;
553
13.6k
  struct args_entry *entry;
554
13.6k
  struct args_entry *last = NULL;
555
13.6k
  struct args_value *value;
556
557
13.6k
  len = 1;
558
13.6k
  buf = xcalloc(1, len);
559
560
  /* Process the flags first. */
561
13.6k
  RB_FOREACH(entry, args_tree, &args->tree) {
562
11.6k
    if (entry->flags & ARGS_ENTRY_OPTIONAL_VALUE)
563
0
      continue;
564
11.6k
    if (!TAILQ_EMPTY(&entry->values))
565
1.51k
      continue;
566
567
10.0k
    if (*buf == '\0')
568
2.34k
      args_print_add(&buf, &len, "-");
569
21.5k
    for (j = 0; j < entry->count; j++)
570
11.4k
      args_print_add(&buf, &len, "%c", entry->flag);
571
10.0k
  }
572
573
  /* Then the flags with arguments. */
574
13.6k
  RB_FOREACH(entry, args_tree, &args->tree) {
575
11.6k
    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
11.6k
    if (TAILQ_EMPTY(&entry->values))
584
10.0k
      continue;
585
3.07k
    TAILQ_FOREACH(value, &entry->values, entry) {
586
3.07k
      if (*buf != '\0')
587
2.71k
        args_print_add(&buf, &len, " -%c", entry->flag);
588
364
      else
589
364
        args_print_add(&buf, &len, "-%c", entry->flag);
590
3.07k
      args_print_add_value(&buf, &len, value);
591
3.07k
    }
592
1.51k
    last = entry;
593
1.51k
  }
594
13.6k
  if (last && (last->flags & ARGS_ENTRY_OPTIONAL_VALUE))
595
0
    args_print_add(&buf, &len, " --");
596
597
  /* And finally the argument vector. */
598
38.7k
  for (i = 0; i < args->count; i++)
599
25.1k
    args_print_add_value(&buf, &len, &args->values[i]);
600
601
13.6k
  return (buf);
602
13.6k
}
603
604
/* Escape an argument. */
605
char *
606
args_escape(const char *s)
607
22.5k
{
608
22.5k
  static const char  dquoted[] = " #';${}%";
609
22.5k
  static const char  squoted[] = " \"";
610
22.5k
  char      *escaped, *result;
611
22.5k
  int      flags, quotes = 0;
612
613
22.5k
  if (*s == '\0') {
614
3.20k
    xasprintf(&result, "''");
615
3.20k
    return (result);
616
3.20k
  }
617
19.3k
  if (s[strcspn(s, dquoted)] != '\0')
618
4.83k
    quotes = '"';
619
14.4k
  else if (s[strcspn(s, squoted)] != '\0')
620
715
    quotes = '\'';
621
622
19.3k
  if (s[0] != ' ' &&
623
19.0k
      s[1] == '\0' &&
624
13.2k
      (quotes != 0 || s[0] == '~')) {
625
3.20k
    xasprintf(&escaped, "\\%c", s[0]);
626
3.20k
    return (escaped);
627
3.20k
  }
628
629
16.1k
  flags = VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL;
630
16.1k
  if (quotes == '"')
631
2.29k
    flags |= VIS_DQ;
632
16.1k
  utf8_stravis(&escaped, s, flags);
633
634
16.1k
  if (quotes == '\'')
635
282
    xasprintf(&result, "'%s'", escaped);
636
15.8k
  else if (quotes == '"') {
637
2.29k
    if (*escaped == '~')
638
450
      xasprintf(&result, "\"\\%s\"", escaped);
639
1.84k
    else
640
1.84k
      xasprintf(&result, "\"%s\"", escaped);
641
13.5k
  } else {
642
13.5k
    if (*escaped == '~')
643
236
      xasprintf(&result, "\\%s", escaped);
644
13.2k
    else
645
13.2k
      result = xstrdup(escaped);
646
13.5k
  }
647
16.1k
  free(escaped);
648
16.1k
  return (result);
649
19.3k
}
650
651
/* Return if an argument is present. */
652
int
653
args_has(struct args *args, u_char flag)
654
5.69k
{
655
5.69k
  struct args_entry *entry;
656
657
5.69k
  entry = args_find(args, flag);
658
5.69k
  if (entry == NULL)
659
5.33k
    return (0);
660
363
  return (entry->count);
661
5.69k
}
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
23.3k
{
667
23.3k
  struct args_entry *entry;
668
669
23.3k
  entry = args_find(args, flag);
670
23.3k
  if (entry == NULL) {
671
19.6k
    entry = xcalloc(1, sizeof *entry);
672
19.6k
    entry->flag = flag;
673
19.6k
    entry->count = 1;
674
19.6k
    entry->flags = flags;
675
19.6k
    TAILQ_INIT(&entry->values);
676
19.6k
    RB_INSERT(args_tree, &args->tree, entry);
677
19.6k
  } else
678
3.62k
    entry->count++;
679
23.3k
  if (value != NULL && value->type != ARGS_NONE)
680
3.18k
    TAILQ_INSERT_TAIL(&entry->values, value, entry);
681
20.1k
  else
682
20.1k
    free(value);
683
23.3k
}
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
1.17M
{
745
1.17M
  if (idx >= args->count)
746
0
    return (NULL);
747
1.17M
  return (args_value_as_string(&args->values[idx]));
748
1.17M
}
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
}