Coverage Report

Created: 2022-12-08 06:09

/src/gnupg/common/miscellaneous.c
Line
Count
Source (jump to first uncovered line)
1
/* miscellaneous.c - Stuff not fitting elsewhere
2
 *  Copyright (C) 2003, 2006 Free Software Foundation, Inc.
3
 *
4
 * This file is part of GnuPG.
5
 *
6
 * This file is free software; you can redistribute it and/or modify
7
 * it under the terms of either
8
 *
9
 *   - the GNU Lesser General Public License as published by the Free
10
 *     Software Foundation; either version 3 of the License, or (at
11
 *     your option) any later version.
12
 *
13
 * or
14
 *
15
 *   - the GNU General Public License as published by the Free
16
 *     Software Foundation; either version 2 of the License, or (at
17
 *     your option) any later version.
18
 *
19
 * or both in parallel, as here.
20
 *
21
 * This file is distributed in the hope that it will be useful,
22
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24
 * GNU General Public License for more details.
25
 *
26
 * You should have received a copy of the GNU General Public License
27
 * along with this program; if not, see <https://www.gnu.org/licenses/>.
28
 */
29
30
#include <config.h>
31
#include <stdlib.h>
32
#include <limits.h>
33
#include <errno.h>
34
35
#include "util.h"
36
#include "iobuf.h"
37
#include "i18n.h"
38
39
/* Used by libgcrypt for logging.  */
40
static void
41
my_gcry_logger (void *dummy, int level, const char *fmt, va_list arg_ptr)
42
0
{
43
0
  (void)dummy;
44
45
  /* Map the log levels.  */
46
0
  switch (level)
47
0
    {
48
0
    case GCRY_LOG_CONT: level = GPGRT_LOGLVL_CONT; break;
49
0
    case GCRY_LOG_INFO: level = GPGRT_LOGLVL_INFO; break;
50
0
    case GCRY_LOG_WARN: level = GPGRT_LOGLVL_WARN; break;
51
0
    case GCRY_LOG_ERROR:level = GPGRT_LOGLVL_ERROR; break;
52
0
    case GCRY_LOG_FATAL:level = GPGRT_LOGLVL_FATAL; break;
53
0
    case GCRY_LOG_BUG:  level = GPGRT_LOGLVL_BUG; break;
54
0
    case GCRY_LOG_DEBUG:level = GPGRT_LOGLVL_DEBUG; break;
55
0
    default:            level = GPGRT_LOGLVL_ERROR; break;
56
0
    }
57
0
  log_logv (level, fmt, arg_ptr);
58
0
}
59
60
61
/* This function is called by libgcrypt on a fatal error.  */
62
static void
63
my_gcry_fatalerror_handler (void *opaque, int rc, const char *text)
64
0
{
65
0
  (void)opaque;
66
67
0
  log_fatal ("libgcrypt problem: %s\n", text ? text : gpg_strerror (rc));
68
0
  abort ();
69
0
}
70
71
72
/* This function is called by libgcrypt if it ran out of core and
73
   there is no way to return that error to the caller.  We do our own
74
   function here to make use of our logging functions. */
75
static int
76
my_gcry_outofcore_handler (void *opaque, size_t req_n, unsigned int flags)
77
0
{
78
0
  static int been_here;  /* Used to protect against recursive calls. */
79
80
0
  (void)opaque;
81
82
0
  if (!been_here)
83
0
    {
84
0
      been_here = 1;
85
0
      if ( (flags & 1) )
86
0
        log_fatal (_("out of core in secure memory "
87
0
                     "while allocating %lu bytes"), (unsigned long)req_n);
88
0
      else
89
0
        log_fatal (_("out of core while allocating %lu bytes"),
90
0
                   (unsigned long)req_n);
91
0
    }
92
0
  return 0; /* Let libgcrypt call its own fatal error handler.
93
               Actually this will turn out to be
94
               my_gcry_fatalerror_handler. */
95
0
}
96
97
98
/* Setup libgcrypt to use our own logging functions.  Should be used
99
   early at startup. */
100
void
101
setup_libgcrypt_logging (void)
102
0
{
103
0
  gcry_set_log_handler (my_gcry_logger, NULL);
104
0
  gcry_set_fatalerror_handler (my_gcry_fatalerror_handler, NULL);
105
0
  gcry_set_outofcore_handler (my_gcry_outofcore_handler, NULL);
106
0
}
107
108
109
/* Print an out of core message and let the process die.  The printed
110
 * error is taken from ERRNO.  */
111
void
112
xoutofcore (void)
113
0
{
114
0
  gpg_error_t err = gpg_error_from_syserror ();
115
0
  log_fatal (_("error allocating enough memory: %s\n"), gpg_strerror (err));
116
0
  abort (); /* Never called; just to make the compiler happy.  */
117
0
}
118
119
120
/* Wrapper around gpgrt_reallocarray.   */
121
void *
122
xreallocarray (void *a, size_t oldnmemb, size_t nmemb, size_t size)
123
0
{
124
0
  void *p = gpgrt_reallocarray (a, oldnmemb, nmemb, size);
125
0
  if (!p)
126
0
    xoutofcore ();
127
0
  return p;
128
0
}
129
130
131
/* A wrapper around gcry_cipher_algo_name to return the string
132
   "AES-128" instead of "AES".  Given that we have an alias in
133
   libgcrypt for it, it does not harm to too much to return this other
134
   string.  Some users complained that we print "AES" but "AES192"
135
   and "AES256".  We can't fix that in libgcrypt but it is pretty
136
   safe to do it in an application. */
137
const char *
138
gnupg_cipher_algo_name (int algo)
139
0
{
140
0
  const char *s;
141
142
0
  s = gcry_cipher_algo_name (algo);
143
0
  if (!strcmp (s, "AES"))
144
0
    s = "AES128";
145
0
  return s;
146
0
}
147
148
149
void
150
obsolete_option (const char *configname, unsigned int configlineno,
151
                 const char *name)
152
0
{
153
0
  if (configname)
154
0
    log_info (_("%s:%u: obsolete option \"%s\" - it has no effect\n"),
155
0
              configname, configlineno, name);
156
0
  else
157
0
    log_info (_("WARNING: \"%s%s\" is an obsolete option - it has no effect\n"),
158
0
              "--", name);
159
0
}
160
161
162
/* Decide whether the filename is stdout or a real filename and return
163
 * an appropriate string.  */
164
const char *
165
print_fname_stdout (const char *s)
166
0
{
167
0
    if( !s || (*s == '-' && !s[1]) )
168
0
  return "[stdout]";
169
0
    return s;
170
0
}
171
172
173
/* Decide whether the filename is stdin or a real filename and return
174
 * an appropriate string.  */
175
const char *
176
print_fname_stdin (const char *s)
177
0
{
178
0
    if( !s || (*s == '-' && !s[1]) )
179
0
  return "[stdin]";
180
0
    return s;
181
0
}
182
183
184
static int
185
do_print_utf8_buffer (estream_t stream,
186
                      const void *buffer, size_t length,
187
                      const char *delimiters, size_t *bytes_written)
188
0
{
189
0
  const char *p = buffer;
190
0
  size_t i;
191
192
  /* We can handle plain ascii simpler, so check for it first. */
193
0
  for (i=0; i < length; i++ )
194
0
    {
195
0
      if ( (p[i] & 0x80) )
196
0
        break;
197
0
    }
198
0
  if (i < length)
199
0
    {
200
0
      int delim = delimiters? *delimiters : 0;
201
0
      char *buf;
202
0
      int ret;
203
204
      /*(utf8 conversion already does the control character quoting). */
205
0
      buf = utf8_to_native (p, length, delim);
206
0
      if (bytes_written)
207
0
        *bytes_written = strlen (buf);
208
0
      ret = es_fputs (buf, stream);
209
0
      xfree (buf);
210
0
      return ret == EOF? ret : (int)i;
211
0
    }
212
0
  else
213
0
    return es_write_sanitized (stream, p, length, delimiters, bytes_written);
214
0
}
215
216
217
void
218
print_utf8_buffer3 (estream_t stream, const void *p, size_t n,
219
                    const char *delim)
220
0
{
221
0
  do_print_utf8_buffer (stream, p, n, delim, NULL);
222
0
}
223
224
225
void
226
print_utf8_buffer2 (estream_t stream, const void *p, size_t n, int delim)
227
0
{
228
0
  char tmp[2];
229
230
0
  tmp[0] = delim;
231
0
  tmp[1] = 0;
232
0
  do_print_utf8_buffer (stream, p, n, tmp, NULL);
233
0
}
234
235
236
void
237
print_utf8_buffer (estream_t stream, const void *p, size_t n)
238
0
{
239
0
  do_print_utf8_buffer (stream, p, n, NULL, NULL);
240
0
}
241
242
243
void
244
print_utf8_string (estream_t stream, const char *p)
245
0
{
246
0
  if (!p)
247
0
    p = "";
248
0
  do_print_utf8_buffer (stream, p, strlen (p), NULL, NULL);
249
0
}
250
251
252
/* Write LENGTH bytes of BUFFER to FP as a hex encoded string.
253
   RESERVED must be 0. */
254
void
255
print_hexstring (FILE *fp, const void *buffer, size_t length, int reserved)
256
0
{
257
0
#define tohex(n) ((n) < 10 ? ((n) + '0') : (((n) - 10) + 'A'))
258
0
  const unsigned char *s;
259
260
0
  (void)reserved;
261
262
0
  for (s = buffer; length; s++, length--)
263
0
    {
264
0
      putc ( tohex ((*s>>4)&15), fp);
265
0
      putc ( tohex (*s&15), fp);
266
0
    }
267
0
#undef tohex
268
0
}
269
270
271
/* Create a string from the buffer P_ARG of length N which is suitable
272
 * for printing.  Caller must release the created string using xfree.
273
 * On error ERRNO is set and NULL returned.  Errors are only possible
274
 * due to malloc failure.  */
275
char *
276
try_make_printable_string (const void *p_arg, size_t n, int delim)
277
6.24k
{
278
6.24k
  const unsigned char *p = p_arg;
279
6.24k
  size_t save_n, buflen;
280
6.24k
  const unsigned char *save_p;
281
6.24k
  char *buffer, *d;
282
283
  /* First count length. */
284
52.0k
  for (save_n = n, save_p = p, buflen=1 ; n; n--, p++ )
285
45.8k
    {
286
45.8k
      if ( *p < 0x20 || *p == 0x7f || *p == delim  || (delim && *p=='\\'))
287
9.86k
        {
288
9.86k
          if ( *p=='\n' || *p=='\r' || *p=='\f'
289
9.86k
               || *p=='\v' || *p=='\b' || !*p )
290
5.52k
            buflen += 2;
291
4.34k
          else
292
4.34k
            buflen += 5;
293
9.86k
  }
294
35.9k
      else
295
35.9k
        buflen++;
296
45.8k
    }
297
6.24k
  p = save_p;
298
6.24k
  n = save_n;
299
  /* And now make the string */
300
6.24k
  d = buffer = xtrymalloc (buflen);
301
52.0k
  for ( ; n; n--, p++ )
302
45.8k
    {
303
45.8k
      if (*p < 0x20 || *p == 0x7f || *p == delim || (delim && *p=='\\')) {
304
9.86k
        *d++ = '\\';
305
9.86k
        if( *p == '\n' )
306
344
          *d++ = 'n';
307
9.52k
        else if( *p == '\r' )
308
518
          *d++ = 'r';
309
9.00k
        else if( *p == '\f' )
310
255
          *d++ = 'f';
311
8.74k
        else if( *p == '\v' )
312
288
          *d++ = 'v';
313
8.46k
        else if( *p == '\b' )
314
317
          *d++ = 'b';
315
8.14k
        else if( !*p )
316
3.80k
          *d++ = '0';
317
4.34k
        else {
318
4.34k
          sprintf(d, "x%02x", *p );
319
4.34k
          d += 3;
320
4.34k
        }
321
9.86k
      }
322
35.9k
      else
323
35.9k
        *d++ = *p;
324
45.8k
    }
325
6.24k
  *d = 0;
326
6.24k
  return buffer;
327
6.24k
}
328
329
330
/* Same as try_make_printable_string but terminates the process on
331
 * memory shortage.  */
332
char *
333
make_printable_string (const void *p, size_t n, int delim )
334
0
{
335
0
  char *string = try_make_printable_string (p, n, delim);
336
0
  if (!string)
337
0
    xoutofcore ();
338
0
  return string;
339
0
}
340
341
342
/* Decode the C formatted string SRC and return the result in a newly
343
 * allocated buffer.  In error returns NULL and sets ERRNO. */
344
char *
345
decode_c_string (const char *src)
346
0
{
347
0
  char *buffer, *dst;
348
0
  int val;
349
350
  /* The converted string will never be larger than the original
351
     string.  */
352
0
  buffer = dst = xtrymalloc (strlen (src) + 1);
353
0
  if (!buffer)
354
0
    return NULL;
355
356
0
  while (*src)
357
0
    {
358
0
      if (*src != '\\')
359
0
  {
360
0
    *dst++ = *src++;
361
0
    continue;
362
0
  }
363
364
0
#define DECODE_ONE(_m,_r) case _m: src += 2; *dst++ = _r; break;
365
366
0
      switch (src[1])
367
0
  {
368
0
    DECODE_ONE ('n', '\n');
369
0
    DECODE_ONE ('r', '\r');
370
0
    DECODE_ONE ('f', '\f');
371
0
    DECODE_ONE ('v', '\v');
372
0
    DECODE_ONE ('b', '\b');
373
0
    DECODE_ONE ('t', '\t');
374
0
    DECODE_ONE ('\\', '\\');
375
0
    DECODE_ONE ('\'', '\'');
376
0
    DECODE_ONE ('\"', '\"');
377
378
0
  case 'x':
379
0
          val = hextobyte (src+2);
380
0
          if (val == -1)  /* Bad coding, keep as is. */
381
0
            {
382
0
              *dst++ = *src++;
383
0
              *dst++ = *src++;
384
0
              if (*src)
385
0
                *dst++ = *src++;
386
0
              if (*src)
387
0
                *dst++ = *src++;
388
0
            }
389
0
          else if (!val)
390
0
            {
391
              /* A binary zero is not representable in a C string thus
392
               * we keep the C-escaping.  Note that this will also
393
               * never be larger than the source string.  */
394
0
              *dst++ = '\\';
395
0
              *dst++ = '0';
396
0
              src += 4;
397
0
            }
398
0
          else
399
0
            {
400
0
              *(unsigned char *)dst++ = val;
401
0
              src += 4;
402
0
            }
403
0
    break;
404
405
0
  default: /* Bad coding; keep as is..  */
406
0
          *dst++ = *src++;
407
0
          *dst++ = *src++;
408
0
          break;
409
0
        }
410
0
#undef DECODE_ONE
411
0
    }
412
0
  *dst++ = 0;
413
414
0
  return buffer;
415
0
}
416
417
418
/* Check whether (BUF,LEN) is valid header for an OpenPGP compressed
419
 * packet.  LEN should be at least 6.  */
420
static int
421
is_openpgp_compressed_packet (unsigned char *buf, size_t len)
422
0
{
423
0
  int c, ctb, pkttype;
424
0
  int lenbytes;
425
426
0
  ctb = *buf++; len--;
427
0
  if (!(ctb & 0x80))
428
0
    return 0; /* Invalid packet.  */
429
430
0
  if ((ctb & 0x40)) /* New style (OpenPGP) CTB.  */
431
0
    {
432
0
      pkttype = (ctb & 0x3f);
433
0
      if (!len)
434
0
        return 0; /* Expected first length octet missing.  */
435
0
      c = *buf++; len--;
436
0
      if (c < 192)
437
0
        ;
438
0
      else if (c < 224)
439
0
        {
440
0
          if (!len)
441
0
            return 0; /* Expected second length octet missing. */
442
0
        }
443
0
      else if (c == 255)
444
0
        {
445
0
          if (len < 4)
446
0
            return 0; /* Expected length octets missing */
447
0
        }
448
0
    }
449
0
  else /* Old style CTB.  */
450
0
    {
451
0
      pkttype = (ctb>>2)&0xf;
452
0
      lenbytes = ((ctb&3)==3)? 0 : (1<<(ctb & 3));
453
0
      if (len < lenbytes)
454
0
        return 0; /* Not enough length bytes.  */
455
0
    }
456
457
0
  return (pkttype == 8);
458
0
}
459
460
461
462
/*
463
 * Check if the file is compressed.
464
 */
465
int
466
is_file_compressed (const char *s, int *ret_rc)
467
0
{
468
0
    iobuf_t a;
469
0
    byte buf[6];
470
0
    int i;
471
0
    int rc = 0;
472
0
    int overflow;
473
474
0
    struct magic_compress_s {
475
0
        size_t len;
476
0
        byte magic[4];
477
0
    } magic[] = {
478
0
        { 3, { 0x42, 0x5a, 0x68, 0x00 } }, /* bzip2 */
479
0
        { 3, { 0x1f, 0x8b, 0x08, 0x00 } }, /* gzip */
480
0
        { 4, { 0x50, 0x4b, 0x03, 0x04 } }, /* (pk)zip */
481
0
    };
482
483
0
    if ( iobuf_is_pipe_filename (s) || !ret_rc )
484
0
        return 0; /* We can't check stdin or no file was given */
485
486
0
    a = iobuf_open( s );
487
0
    if ( a == NULL ) {
488
0
        *ret_rc = gpg_error_from_syserror ();
489
0
        return 0;
490
0
    }
491
0
    iobuf_ioctl (a, IOBUF_IOCTL_NO_CACHE, 1, NULL);
492
493
0
    if ( iobuf_get_filelength( a, &overflow ) < 6 && !overflow) {
494
0
        *ret_rc = 0;
495
0
        goto leave;
496
0
    }
497
498
0
    if ( iobuf_read( a, buf, 6 ) == -1 ) {
499
0
        *ret_rc = a->error;
500
0
        goto leave;
501
0
    }
502
503
0
    for ( i = 0; i < DIM( magic ); i++ ) {
504
0
        if ( !memcmp( buf, magic[i].magic, magic[i].len ) ) {
505
0
            *ret_rc = 0;
506
0
            rc = 1;
507
0
            goto leave;
508
0
        }
509
0
    }
510
511
0
    if (is_openpgp_compressed_packet (buf, 6))
512
0
      {
513
0
        *ret_rc = 0;
514
0
        rc = 1;
515
0
      }
516
517
0
 leave:
518
0
    iobuf_close( a );
519
0
    return rc;
520
0
}
521
522
523
/* Try match against each substring of multistr, delimited by | */
524
int
525
match_multistr (const char *multistr,const char *match)
526
0
{
527
0
  do
528
0
    {
529
0
      size_t seglen = strcspn (multistr,"|");
530
0
      if (!seglen)
531
0
  break;
532
      /* Using the localized strncasecmp! */
533
0
      if (strncasecmp(multistr,match,seglen)==0)
534
0
  return 1;
535
0
      multistr += seglen;
536
0
      if (*multistr == '|')
537
0
  multistr++;
538
0
    }
539
0
  while (*multistr);
540
541
0
  return 0;
542
0
}
543
544
545

546
/* Parse the first portion of the version number S and store it at
547
   NUMBER.  On success, the function returns a pointer into S starting
548
   with the first character, which is not part of the initial number
549
   portion; on failure, NULL is returned.  */
550
static const char*
551
parse_version_number (const char *s, int *number)
552
0
{
553
0
  int val = 0;
554
555
0
  if (*s == '0' && digitp (s+1))
556
0
    return NULL; /* Leading zeros are not allowed.  */
557
0
  for (; digitp (s); s++ )
558
0
    {
559
0
      val *= 10;
560
0
      val += *s - '0';
561
0
    }
562
0
  *number = val;
563
0
  return val < 0? NULL : s;
564
0
}
565
566
/* Break up the complete string representation of the version number S,
567
   which is expected to have this format:
568
569
      <major number>.<minor number>.<micro number><patch level>.
570
571
   The major, minor and micro number components will be stored at
572
   MAJOR, MINOR and MICRO. On success, a pointer to the last
573
   component, the patch level, will be returned; on failure, NULL will
574
   be returned.  */
575
static const char *
576
parse_version_string (const char *s, int *major, int *minor, int *micro)
577
0
{
578
0
  s = parse_version_number (s, major);
579
0
  if (!s || *s != '.')
580
0
    return NULL;
581
0
  s++;
582
0
  s = parse_version_number (s, minor);
583
0
  if (!s || *s != '.')
584
0
    return NULL;
585
0
  s++;
586
0
  s = parse_version_number (s, micro);
587
0
  if (!s)
588
0
    return NULL;
589
0
  return s; /* Patchlevel.  */
590
0
}
591
592
/* Return true if version string is at least version B. */
593
int
594
gnupg_compare_version (const char *a, const char *b)
595
0
{
596
0
  int a_major, a_minor, a_micro;
597
0
  int b_major, b_minor, b_micro;
598
0
  const char *a_plvl, *b_plvl;
599
600
0
  if (!a || !b)
601
0
    return 0;
602
603
  /* Parse version A.  */
604
0
  a_plvl = parse_version_string (a, &a_major, &a_minor, &a_micro);
605
0
  if (!a_plvl )
606
0
    return 0; /* Invalid version number.  */
607
608
  /* Parse version B.  */
609
0
  b_plvl = parse_version_string (b, &b_major, &b_minor, &b_micro);
610
0
  if (!b_plvl )
611
0
    return 0; /* Invalid version number.  */
612
613
  /* Compare version numbers.  */
614
0
  return (a_major > b_major
615
0
          || (a_major == b_major && a_minor > b_minor)
616
0
          || (a_major == b_major && a_minor == b_minor
617
0
              && a_micro > b_micro)
618
0
          || (a_major == b_major && a_minor == b_minor
619
0
              && a_micro == b_micro
620
0
              && strcmp (a_plvl, b_plvl) >= 0));
621
0
}
622
623
624

625
/* Parse an --debug style argument.  We allow the use of number values
626
 * in the usual C notation or a string with comma separated keywords.
627
 *
628
 * Returns: 0 on success or -1 and ERRNO set on error.  On success the
629
 *          supplied variable is updated by the parsed flags.
630
 *
631
 * If STRING is NULL the enabled debug flags are printed.
632
 *
633
 * See doc/DETAILS for a summary of used debug options.
634
 */
635
int
636
parse_debug_flag (const char *string, unsigned int *debugvar,
637
                  const struct debug_flags_s *flags)
638
639
0
{
640
0
  unsigned long result = 0;
641
0
  int i, j;
642
643
0
  if (!string)
644
0
    {
645
0
      if (debugvar)
646
0
        {
647
0
          log_info ("enabled debug flags:");
648
0
          for (i=0; flags[i].name; i++)
649
0
            if ((*debugvar & flags[i].flag))
650
0
              log_printf (" %s", flags[i].name);
651
0
          log_printf ("\n");
652
0
        }
653
0
      return 0;
654
0
    }
655
656
0
  while (spacep (string))
657
0
    string++;
658
0
  if (*string == '-')
659
0
    {
660
0
      errno = EINVAL;
661
0
      return -1;
662
0
    }
663
664
0
  if (!strcmp (string, "?") || !strcmp (string, "help"))
665
0
    {
666
0
      log_info ("available debug flags:\n");
667
0
      for (i=0; flags[i].name; i++)
668
0
        log_info (" %5u %s\n", flags[i].flag, flags[i].name);
669
0
      if (flags[i].flag != 77)
670
0
        exit (0);
671
0
    }
672
0
  else if (digitp (string))
673
0
    {
674
0
      errno = 0;
675
0
      result = strtoul (string, NULL, 0);
676
0
      if (result == ULONG_MAX && errno == ERANGE)
677
0
        return -1;
678
0
    }
679
0
  else
680
0
    {
681
0
      char **words;
682
0
      words = strtokenize (string, ",");
683
0
      if (!words)
684
0
        return -1;
685
0
      for (i=0; words[i]; i++)
686
0
        {
687
0
          if (*words[i])
688
0
            {
689
0
              for (j=0; flags[j].name; j++)
690
0
                if (!strcmp (words[i], flags[j].name))
691
0
                  {
692
0
                    result |= flags[j].flag;
693
0
                    break;
694
0
                  }
695
0
              if (!flags[j].name)
696
0
                {
697
0
                  if (!strcmp (words[i], "none"))
698
0
                    {
699
0
                      *debugvar = 0;
700
0
                      result = 0;
701
0
                    }
702
0
                  else if (!strcmp (words[i], "all"))
703
0
                    result = ~0;
704
0
                  else
705
0
                    log_info (_("unknown debug flag '%s' ignored\n"), words[i]);
706
0
                }
707
0
            }
708
0
        }
709
0
      xfree (words);
710
0
    }
711
712
0
  *debugvar |= result;
713
0
  return 0;
714
0
}
715
716
717

718
/* Parse an --comaptibility_flags style argument consisting of comma
719
 * separated strings.
720
 *
721
 * Returns: 0 on success or -1 and ERRNO set on error.  On success the
722
 *          supplied variable is updated by the parsed flags.
723
 *
724
 * If STRING is NULL the enabled flags are printed.
725
 */
726
int
727
parse_compatibility_flags (const char *string, unsigned int *flagvar,
728
                           const struct compatibility_flags_s *flags)
729
730
0
{
731
0
  unsigned long result = 0;
732
0
  int i, j;
733
734
0
  if (!string)
735
0
    {
736
0
      if (flagvar)
737
0
        {
738
0
          log_info ("enabled compatibility flags:");
739
0
          for (i=0; flags[i].name; i++)
740
0
            if ((*flagvar & flags[i].flag))
741
0
              log_printf (" %s", flags[i].name);
742
0
          log_printf ("\n");
743
0
        }
744
0
      return 0;
745
0
    }
746
747
0
  while (spacep (string))
748
0
    string++;
749
750
0
  if (!strcmp (string, "?") || !strcmp (string, "help"))
751
0
    {
752
0
      log_info ("available compatibility flags:\n");
753
0
      for (i=0; flags[i].name; i++)
754
0
        log_info (" %s\n", flags[i].name);
755
0
      if (flags[i].flag != 77)
756
0
        exit (0);
757
0
    }
758
0
  else
759
0
    {
760
0
      char **words;
761
0
      words = strtokenize (string, ",");
762
0
      if (!words)
763
0
        return -1;
764
0
      for (i=0; words[i]; i++)
765
0
        {
766
0
          if (*words[i])
767
0
            {
768
0
              for (j=0; flags[j].name; j++)
769
0
                if (!strcmp (words[i], flags[j].name))
770
0
                  {
771
0
                    result |= flags[j].flag;
772
0
                    break;
773
0
                  }
774
0
              if (!flags[j].name)
775
0
                {
776
0
                  if (!strcmp (words[i], "none"))
777
0
                    {
778
0
                      *flagvar = 0;
779
0
                      result = 0;
780
0
                    }
781
0
                  else if (!strcmp (words[i], "all"))
782
0
                    result = ~0;
783
0
                  else
784
0
                    log_info ("unknown compatibility flag '%s' ignored\n",
785
0
                              words[i]);
786
0
                }
787
0
            }
788
0
        }
789
0
      xfree (words);
790
0
    }
791
792
0
  *flagvar |= result;
793
0
  return 0;
794
0
}