1*20c29e2bSop /* $OpenBSD: scanopt.c,v 1.7 2024/11/09 18:03:44 op Exp $ */ 2a58c1ecbStedu 3a58c1ecbStedu /* flex - tool to generate fast lexical analyzers */ 4a58c1ecbStedu 5a58c1ecbStedu /* Copyright (c) 1990 The Regents of the University of California. */ 6a58c1ecbStedu /* All rights reserved. */ 7a58c1ecbStedu 8a58c1ecbStedu /* This code is derived from software contributed to Berkeley by */ 9a58c1ecbStedu /* Vern Paxson. */ 10a58c1ecbStedu 11a58c1ecbStedu /* The United States Government has rights in this work pursuant */ 12a58c1ecbStedu /* to contract no. DE-AC03-76SF00098 between the United States */ 13a58c1ecbStedu /* Department of Energy and the University of California. */ 14a58c1ecbStedu 15a58c1ecbStedu /* This file is part of flex. */ 16a58c1ecbStedu 17a58c1ecbStedu /* Redistribution and use in source and binary forms, with or without */ 18a58c1ecbStedu /* modification, are permitted provided that the following conditions */ 19a58c1ecbStedu /* are met: */ 20a58c1ecbStedu 21a58c1ecbStedu /* 1. Redistributions of source code must retain the above copyright */ 22a58c1ecbStedu /* notice, this list of conditions and the following disclaimer. */ 23a58c1ecbStedu /* 2. Redistributions in binary form must reproduce the above copyright */ 24a58c1ecbStedu /* notice, this list of conditions and the following disclaimer in the */ 25a58c1ecbStedu /* documentation and/or other materials provided with the distribution. */ 26a58c1ecbStedu 27a58c1ecbStedu /* Neither the name of the University nor the names of its contributors */ 28a58c1ecbStedu /* may be used to endorse or promote products derived from this software */ 29a58c1ecbStedu /* without specific prior written permission. */ 30a58c1ecbStedu 31a58c1ecbStedu /* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR */ 32a58c1ecbStedu /* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED */ 33a58c1ecbStedu /* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR */ 34a58c1ecbStedu /* PURPOSE. */ 35a58c1ecbStedu 36a58c1ecbStedu #include "flexdef.h" 37a58c1ecbStedu #include "scanopt.h" 38a58c1ecbStedu 39a58c1ecbStedu 40a58c1ecbStedu /* Internal structures */ 41a58c1ecbStedu 42a58c1ecbStedu #ifdef HAVE_STRCASECMP 43a58c1ecbStedu #define STRCASECMP(a,b) strcasecmp(a,b) 44a58c1ecbStedu #else 45a58c1ecbStedu static int STRCASECMP PROTO ((const char *, const char *)); 46a58c1ecbStedu 47*20c29e2bSop static int STRCASECMP (const char *a, const char *b) 48a58c1ecbStedu { 49d9622a3dSmmcc while (tolower ((u_char)*a++) == tolower ((u_char)*b++)) ; 50a58c1ecbStedu return b - a; 51a58c1ecbStedu } 52a58c1ecbStedu #endif 53a58c1ecbStedu 54a58c1ecbStedu #define ARG_NONE 0x01 55a58c1ecbStedu #define ARG_REQ 0x02 56a58c1ecbStedu #define ARG_OPT 0x04 57a58c1ecbStedu #define IS_LONG 0x08 58a58c1ecbStedu 59a58c1ecbStedu struct _aux { 60a58c1ecbStedu int flags; /* The above hex flags. */ 61a58c1ecbStedu int namelen; /* Length of the actual option word, e.g., "--file[=foo]" is 4 */ 62a58c1ecbStedu int printlen; /* Length of entire string, e.g., "--file[=foo]" is 12 */ 63a58c1ecbStedu }; 64a58c1ecbStedu 65a58c1ecbStedu 66a58c1ecbStedu struct _scanopt_t { 67a58c1ecbStedu const optspec_t *options; /* List of options. */ 68a58c1ecbStedu struct _aux *aux; /* Auxiliary data about options. */ 69a58c1ecbStedu int optc; /* Number of options. */ 70a58c1ecbStedu int argc; /* Number of args. */ 71a58c1ecbStedu char **argv; /* Array of strings. */ 72a58c1ecbStedu int index; /* Used as: argv[index][subscript]. */ 73a58c1ecbStedu int subscript; 74a58c1ecbStedu char no_err_msg; /* If true, do not print errors. */ 75a58c1ecbStedu char has_long; 76a58c1ecbStedu char has_short; 77a58c1ecbStedu }; 78a58c1ecbStedu 79a58c1ecbStedu /* Accessor functions. These WOULD be one-liners, but portability calls. */ 80a58c1ecbStedu static const char *NAME PROTO ((struct _scanopt_t *, int)); 81a58c1ecbStedu static int PRINTLEN PROTO ((struct _scanopt_t *, int)); 82a58c1ecbStedu static int RVAL PROTO ((struct _scanopt_t *, int)); 83a58c1ecbStedu static int FLAGS PROTO ((struct _scanopt_t *, int)); 84a58c1ecbStedu static const char *DESC PROTO ((struct _scanopt_t *, int)); 85a58c1ecbStedu static int scanopt_err PROTO ((struct _scanopt_t *, int, int, int)); 86a58c1ecbStedu static int matchlongopt PROTO ((char *, char **, int *, char **, int *)); 87a58c1ecbStedu static int find_opt 88a58c1ecbStedu PROTO ((struct _scanopt_t *, int, char *, int, int *, int *opt_offset)); 89a58c1ecbStedu 90*20c29e2bSop static const char *NAME (struct _scanopt_t *s, int i) 91a58c1ecbStedu { 92a58c1ecbStedu return s->options[i].opt_fmt + 93a58c1ecbStedu ((s->aux[i].flags & IS_LONG) ? 2 : 1); 94a58c1ecbStedu } 95a58c1ecbStedu 96*20c29e2bSop static int PRINTLEN (struct _scanopt_t *s, int i) 97a58c1ecbStedu { 98a58c1ecbStedu return s->aux[i].printlen; 99a58c1ecbStedu } 100a58c1ecbStedu 101*20c29e2bSop static int RVAL (struct _scanopt_t *s, int i) 102a58c1ecbStedu { 103a58c1ecbStedu return s->options[i].r_val; 104a58c1ecbStedu } 105a58c1ecbStedu 106*20c29e2bSop static int FLAGS (struct _scanopt_t *s, int i) 107a58c1ecbStedu { 108a58c1ecbStedu return s->aux[i].flags; 109a58c1ecbStedu } 110a58c1ecbStedu 111*20c29e2bSop static const char *DESC (struct _scanopt_t *s, int i) 112a58c1ecbStedu { 113a58c1ecbStedu return s->options[i].desc ? s->options[i].desc : ""; 114a58c1ecbStedu } 115a58c1ecbStedu 116a58c1ecbStedu #ifndef NO_SCANOPT_USAGE 117a58c1ecbStedu static int get_cols PROTO ((void)); 118a58c1ecbStedu 119*20c29e2bSop static int get_cols (void) 120a58c1ecbStedu { 121a58c1ecbStedu char *env; 122a58c1ecbStedu int cols = 80; /* default */ 123a58c1ecbStedu 124a58c1ecbStedu #ifdef HAVE_NCURSES_H 125a58c1ecbStedu initscr (); 126a58c1ecbStedu endwin (); 127a58c1ecbStedu if (COLS > 0) 128a58c1ecbStedu return COLS; 129a58c1ecbStedu #endif 130a58c1ecbStedu 131a58c1ecbStedu if ((env = getenv ("COLUMNS")) != NULL) 132a58c1ecbStedu cols = atoi (env); 133a58c1ecbStedu 134a58c1ecbStedu return cols; 135a58c1ecbStedu } 136a58c1ecbStedu #endif 137a58c1ecbStedu 138a58c1ecbStedu /* Macro to check for NULL before assigning a value. */ 139a58c1ecbStedu #define SAFE_ASSIGN(ptr,val) \ 140a58c1ecbStedu do{ \ 141a58c1ecbStedu if((ptr)!=NULL) \ 142a58c1ecbStedu *(ptr) = val; \ 143a58c1ecbStedu }while(0) 144a58c1ecbStedu 145a58c1ecbStedu /* Macro to assure we reset subscript whenever we adjust s->index.*/ 146a58c1ecbStedu #define INC_INDEX(s,n) \ 147a58c1ecbStedu do{ \ 148a58c1ecbStedu (s)->index += (n); \ 149a58c1ecbStedu (s)->subscript= 0; \ 150a58c1ecbStedu }while(0) 151a58c1ecbStedu 152*20c29e2bSop scanopt_t *scanopt_init (const optspec_t *options, int argc, char **argv, 153*20c29e2bSop int flags) 154a58c1ecbStedu { 155a58c1ecbStedu int i; 156a58c1ecbStedu struct _scanopt_t *s; 157a58c1ecbStedu s = (struct _scanopt_t *) malloc (sizeof (struct _scanopt_t)); 158a58c1ecbStedu 159a58c1ecbStedu s->options = options; 160a58c1ecbStedu s->optc = 0; 161a58c1ecbStedu s->argc = argc; 162a58c1ecbStedu s->argv = (char **) argv; 163a58c1ecbStedu s->index = 1; 164a58c1ecbStedu s->subscript = 0; 165a58c1ecbStedu s->no_err_msg = (flags & SCANOPT_NO_ERR_MSG); 166a58c1ecbStedu s->has_long = 0; 167a58c1ecbStedu s->has_short = 0; 168a58c1ecbStedu 169a58c1ecbStedu /* Determine option count. (Find entry with all zeros). */ 170a58c1ecbStedu s->optc = 0; 171a58c1ecbStedu while (options[s->optc].opt_fmt 172a58c1ecbStedu || options[s->optc].r_val || options[s->optc].desc) 173a58c1ecbStedu s->optc++; 174a58c1ecbStedu 175a58c1ecbStedu /* Build auxiliary data */ 176a58c1ecbStedu s->aux = (struct _aux *) malloc (s->optc * sizeof (struct _aux)); 177a58c1ecbStedu 178a58c1ecbStedu for (i = 0; i < s->optc; i++) { 17973902435Smmcc const u_char *p, *pname; 180a58c1ecbStedu const struct optspec_t *opt; 181a58c1ecbStedu struct _aux *aux; 182a58c1ecbStedu 183a58c1ecbStedu opt = s->options + i; 184a58c1ecbStedu aux = s->aux + i; 185a58c1ecbStedu 186a58c1ecbStedu aux->flags = ARG_NONE; 187a58c1ecbStedu 188a58c1ecbStedu if (opt->opt_fmt[0] == '-' && opt->opt_fmt[1] == '-') { 189a58c1ecbStedu aux->flags |= IS_LONG; 19073902435Smmcc pname = (const u_char *)(opt->opt_fmt + 2); 191a58c1ecbStedu s->has_long = 1; 192a58c1ecbStedu } 193a58c1ecbStedu else { 19473902435Smmcc pname = (const u_char *)(opt->opt_fmt + 1); 195a58c1ecbStedu s->has_short = 1; 196a58c1ecbStedu } 197a58c1ecbStedu aux->printlen = strlen (opt->opt_fmt); 198a58c1ecbStedu 199a58c1ecbStedu aux->namelen = 0; 200a58c1ecbStedu for (p = pname + 1; *p; p++) { 201a58c1ecbStedu /* detect required arg */ 202a58c1ecbStedu if (*p == '=' || isspace (*p) 203a58c1ecbStedu || !(aux->flags & IS_LONG)) { 204a58c1ecbStedu if (aux->namelen == 0) 205a58c1ecbStedu aux->namelen = p - pname; 206a58c1ecbStedu aux->flags |= ARG_REQ; 207a58c1ecbStedu aux->flags &= ~ARG_NONE; 208a58c1ecbStedu } 209a58c1ecbStedu /* detect optional arg. This overrides required arg. */ 210a58c1ecbStedu if (*p == '[') { 211a58c1ecbStedu if (aux->namelen == 0) 212a58c1ecbStedu aux->namelen = p - pname; 213a58c1ecbStedu aux->flags &= ~(ARG_REQ | ARG_NONE); 214a58c1ecbStedu aux->flags |= ARG_OPT; 215a58c1ecbStedu break; 216a58c1ecbStedu } 217a58c1ecbStedu } 218a58c1ecbStedu if (aux->namelen == 0) 219a58c1ecbStedu aux->namelen = p - pname; 220a58c1ecbStedu } 221a58c1ecbStedu return (scanopt_t *) s; 222a58c1ecbStedu } 223a58c1ecbStedu 224a58c1ecbStedu #ifndef NO_SCANOPT_USAGE 225a58c1ecbStedu /* these structs are for scanopt_usage(). */ 226a58c1ecbStedu struct usg_elem { 227a58c1ecbStedu int idx; 228a58c1ecbStedu struct usg_elem *next; 229a58c1ecbStedu struct usg_elem *alias; 230a58c1ecbStedu }; 231a58c1ecbStedu typedef struct usg_elem usg_elem; 232a58c1ecbStedu 233a58c1ecbStedu 234a58c1ecbStedu /* Prints a usage message based on contents of optlist. 235a58c1ecbStedu * Parameters: 236a58c1ecbStedu * scanner - The scanner, already initialized with scanopt_init(). 237a58c1ecbStedu * fp - The file stream to write to. 238a58c1ecbStedu * usage - Text to be prepended to option list. 239a58c1ecbStedu * Return: Always returns 0 (zero). 240a58c1ecbStedu * The output looks something like this: 241a58c1ecbStedu 242a58c1ecbStedu [indent][option, alias1, alias2...][indent][description line1 243a58c1ecbStedu description line2...] 244a58c1ecbStedu */ 245*20c29e2bSop int scanopt_usage (scanopt_t *scanner, FILE *fp, const char *usage) 246a58c1ecbStedu { 247a58c1ecbStedu struct _scanopt_t *s; 248a58c1ecbStedu int i, columns, indent = 2; 249a58c1ecbStedu usg_elem *byr_val = NULL; /* option indices sorted by r_val */ 250a58c1ecbStedu usg_elem *store; /* array of preallocated elements. */ 251a58c1ecbStedu int store_idx = 0; 252a58c1ecbStedu usg_elem *ue; 253a58c1ecbStedu int maxlen[2]; 254a58c1ecbStedu int desccol = 0; 255a58c1ecbStedu int print_run = 0; 256a58c1ecbStedu 257a58c1ecbStedu maxlen[0] = 0; 258a58c1ecbStedu maxlen[1] = 0; 259a58c1ecbStedu 260a58c1ecbStedu s = (struct _scanopt_t *) scanner; 261a58c1ecbStedu 262a58c1ecbStedu if (usage) { 263a58c1ecbStedu fprintf (fp, "%s\n", usage); 264a58c1ecbStedu } 265a58c1ecbStedu else { 266a58c1ecbStedu /* Find the basename of argv[0] */ 267a58c1ecbStedu const char *p; 268a58c1ecbStedu 269a58c1ecbStedu p = s->argv[0] + strlen (s->argv[0]); 270a58c1ecbStedu while (p != s->argv[0] && *p != '/') 271a58c1ecbStedu --p; 272a58c1ecbStedu if (*p == '/') 273a58c1ecbStedu p++; 274a58c1ecbStedu 275a58c1ecbStedu fprintf (fp, _("Usage: %s [OPTIONS]...\n"), p); 276a58c1ecbStedu } 277a58c1ecbStedu fprintf (fp, "\n"); 278a58c1ecbStedu 279a58c1ecbStedu /* Sort by r_val and string. Yes, this is O(n*n), but n is small. */ 280a58c1ecbStedu store = (usg_elem *) malloc (s->optc * sizeof (usg_elem)); 281a58c1ecbStedu for (i = 0; i < s->optc; i++) { 282a58c1ecbStedu 283a58c1ecbStedu /* grab the next preallocate node. */ 284a58c1ecbStedu ue = store + store_idx++; 285a58c1ecbStedu ue->idx = i; 286a58c1ecbStedu ue->next = ue->alias = NULL; 287a58c1ecbStedu 288a58c1ecbStedu /* insert into list. */ 289a58c1ecbStedu if (!byr_val) 290a58c1ecbStedu byr_val = ue; 291a58c1ecbStedu else { 292a58c1ecbStedu int found_alias = 0; 293a58c1ecbStedu usg_elem **ue_curr, **ptr_if_no_alias = NULL; 294a58c1ecbStedu 295a58c1ecbStedu ue_curr = &byr_val; 296a58c1ecbStedu while (*ue_curr) { 297a58c1ecbStedu if (RVAL (s, (*ue_curr)->idx) == 298a58c1ecbStedu RVAL (s, ue->idx)) { 299a58c1ecbStedu /* push onto the alias list. */ 300a58c1ecbStedu ue_curr = &((*ue_curr)->alias); 301a58c1ecbStedu found_alias = 1; 302a58c1ecbStedu break; 303a58c1ecbStedu } 304a58c1ecbStedu if (!ptr_if_no_alias 305a58c1ecbStedu && 306a58c1ecbStedu STRCASECMP (NAME (s, (*ue_curr)->idx), 307a58c1ecbStedu NAME (s, ue->idx)) > 0) { 308a58c1ecbStedu ptr_if_no_alias = ue_curr; 309a58c1ecbStedu } 310a58c1ecbStedu ue_curr = &((*ue_curr)->next); 311a58c1ecbStedu } 312a58c1ecbStedu if (!found_alias && ptr_if_no_alias) 313a58c1ecbStedu ue_curr = ptr_if_no_alias; 314a58c1ecbStedu ue->next = *ue_curr; 315a58c1ecbStedu *ue_curr = ue; 316a58c1ecbStedu } 317a58c1ecbStedu } 318a58c1ecbStedu 319a58c1ecbStedu #if 0 320a58c1ecbStedu if (1) { 321a58c1ecbStedu printf ("ORIGINAL:\n"); 322a58c1ecbStedu for (i = 0; i < s->optc; i++) 323a58c1ecbStedu printf ("%2d: %s\n", i, NAME (s, i)); 324a58c1ecbStedu printf ("SORTED:\n"); 325a58c1ecbStedu ue = byr_val; 326a58c1ecbStedu while (ue) { 327a58c1ecbStedu usg_elem *ue2; 328a58c1ecbStedu 329a58c1ecbStedu printf ("%2d: %s\n", ue->idx, NAME (s, ue->idx)); 330a58c1ecbStedu for (ue2 = ue->alias; ue2; ue2 = ue2->next) 331a58c1ecbStedu printf (" +---> %2d: %s\n", ue2->idx, 332a58c1ecbStedu NAME (s, ue2->idx)); 333a58c1ecbStedu ue = ue->next; 334a58c1ecbStedu } 335a58c1ecbStedu } 336a58c1ecbStedu #endif 337a58c1ecbStedu 338a58c1ecbStedu /* Now build each row of output. */ 339a58c1ecbStedu 340a58c1ecbStedu /* first pass calculate how much room we need. */ 341a58c1ecbStedu for (ue = byr_val; ue; ue = ue->next) { 342a58c1ecbStedu usg_elem *ap; 343a58c1ecbStedu int len = 0; 344a58c1ecbStedu int nshort = 0, nlong = 0; 345a58c1ecbStedu 346a58c1ecbStedu 347a58c1ecbStedu #define CALC_LEN(i) do {\ 348a58c1ecbStedu if(FLAGS(s,i) & IS_LONG) \ 349a58c1ecbStedu len += (nlong++||nshort) ? 2+PRINTLEN(s,i) : PRINTLEN(s,i);\ 350a58c1ecbStedu else\ 351a58c1ecbStedu len += (nshort++||nlong)? 2+PRINTLEN(s,i) : PRINTLEN(s,i);\ 352a58c1ecbStedu }while(0) 353a58c1ecbStedu 354a58c1ecbStedu if (!(FLAGS (s, ue->idx) & IS_LONG)) 355a58c1ecbStedu CALC_LEN (ue->idx); 356a58c1ecbStedu 357a58c1ecbStedu /* do short aliases first. */ 358a58c1ecbStedu for (ap = ue->alias; ap; ap = ap->next) { 359a58c1ecbStedu if (FLAGS (s, ap->idx) & IS_LONG) 360a58c1ecbStedu continue; 361a58c1ecbStedu CALC_LEN (ap->idx); 362a58c1ecbStedu } 363a58c1ecbStedu 364a58c1ecbStedu if (FLAGS (s, ue->idx) & IS_LONG) 365a58c1ecbStedu CALC_LEN (ue->idx); 366a58c1ecbStedu 367a58c1ecbStedu /* repeat the above loop, this time for long aliases. */ 368a58c1ecbStedu for (ap = ue->alias; ap; ap = ap->next) { 369a58c1ecbStedu if (!(FLAGS (s, ap->idx) & IS_LONG)) 370a58c1ecbStedu continue; 371a58c1ecbStedu CALC_LEN (ap->idx); 372a58c1ecbStedu } 373a58c1ecbStedu 374a58c1ecbStedu if (len > maxlen[0]) 375a58c1ecbStedu maxlen[0] = len; 376a58c1ecbStedu 377a58c1ecbStedu /* It's much easier to calculate length for description column! */ 378a58c1ecbStedu len = strlen (DESC (s, ue->idx)); 379a58c1ecbStedu if (len > maxlen[1]) 380a58c1ecbStedu maxlen[1] = len; 381a58c1ecbStedu } 382a58c1ecbStedu 383a58c1ecbStedu /* Determine how much room we have, and how much we will allocate to each col. 384a58c1ecbStedu * Do not address pathological cases. Output will just be ugly. */ 385a58c1ecbStedu columns = get_cols () - 1; 386a58c1ecbStedu if (maxlen[0] + maxlen[1] + indent * 2 > columns) { 387a58c1ecbStedu /* col 0 gets whatever it wants. we'll wrap the desc col. */ 388a58c1ecbStedu maxlen[1] = columns - (maxlen[0] + indent * 2); 389a58c1ecbStedu if (maxlen[1] < 14) /* 14 is arbitrary lower limit on desc width. */ 390a58c1ecbStedu maxlen[1] = INT_MAX; 391a58c1ecbStedu } 392a58c1ecbStedu desccol = maxlen[0] + indent * 2; 393a58c1ecbStedu 394a58c1ecbStedu #define PRINT_SPACES(fp,n)\ 395a58c1ecbStedu do{\ 396a58c1ecbStedu int _n;\ 397a58c1ecbStedu _n=(n);\ 398a58c1ecbStedu while(_n-- > 0)\ 399a58c1ecbStedu fputc(' ',(fp));\ 400a58c1ecbStedu }while(0) 401a58c1ecbStedu 402a58c1ecbStedu 403a58c1ecbStedu /* Second pass (same as above loop), this time we print. */ 404a58c1ecbStedu /* Sloppy hack: We iterate twice. The first time we print short and long options. 405a58c1ecbStedu The second time we print those lines that have ONLY long options. */ 406a58c1ecbStedu while (print_run++ < 2) { 407a58c1ecbStedu for (ue = byr_val; ue; ue = ue->next) { 408a58c1ecbStedu usg_elem *ap; 409a58c1ecbStedu int nwords = 0, nchars = 0, has_short = 0; 410a58c1ecbStedu 411a58c1ecbStedu /* TODO: get has_short schtick to work */ 412a58c1ecbStedu has_short = !(FLAGS (s, ue->idx) & IS_LONG); 413a58c1ecbStedu for (ap = ue->alias; ap; ap = ap->next) { 414a58c1ecbStedu if (!(FLAGS (s, ap->idx) & IS_LONG)) { 415a58c1ecbStedu has_short = 1; 416a58c1ecbStedu break; 417a58c1ecbStedu } 418a58c1ecbStedu } 419a58c1ecbStedu if ((print_run == 1 && !has_short) || 420a58c1ecbStedu (print_run == 2 && has_short)) 421a58c1ecbStedu continue; 422a58c1ecbStedu 423a58c1ecbStedu PRINT_SPACES (fp, indent); 424a58c1ecbStedu nchars += indent; 425a58c1ecbStedu 426a58c1ecbStedu /* Print, adding a ", " between aliases. */ 427a58c1ecbStedu #define PRINT_IT(i) do{\ 428a58c1ecbStedu if(nwords++)\ 429a58c1ecbStedu nchars+=fprintf(fp,", ");\ 430a58c1ecbStedu nchars+=fprintf(fp,"%s",s->options[i].opt_fmt);\ 431a58c1ecbStedu }while(0) 432a58c1ecbStedu 433a58c1ecbStedu if (!(FLAGS (s, ue->idx) & IS_LONG)) 434a58c1ecbStedu PRINT_IT (ue->idx); 435a58c1ecbStedu 436a58c1ecbStedu /* print short aliases first. */ 437a58c1ecbStedu for (ap = ue->alias; ap; ap = ap->next) { 438a58c1ecbStedu if (!(FLAGS (s, ap->idx) & IS_LONG)) 439a58c1ecbStedu PRINT_IT (ap->idx); 440a58c1ecbStedu } 441a58c1ecbStedu 442a58c1ecbStedu 443a58c1ecbStedu if (FLAGS (s, ue->idx) & IS_LONG) 444a58c1ecbStedu PRINT_IT (ue->idx); 445a58c1ecbStedu 446a58c1ecbStedu /* repeat the above loop, this time for long aliases. */ 447a58c1ecbStedu for (ap = ue->alias; ap; ap = ap->next) { 448a58c1ecbStedu if (FLAGS (s, ap->idx) & IS_LONG) 449a58c1ecbStedu PRINT_IT (ap->idx); 450a58c1ecbStedu } 451a58c1ecbStedu 452a58c1ecbStedu /* pad to desccol */ 453a58c1ecbStedu PRINT_SPACES (fp, desccol - nchars); 454a58c1ecbStedu 455a58c1ecbStedu /* Print description, wrapped to maxlen[1] columns. */ 456a58c1ecbStedu if (1) { 457a58c1ecbStedu const char *pstart; 458a58c1ecbStedu 459a58c1ecbStedu pstart = DESC (s, ue->idx); 460a58c1ecbStedu while (1) { 461a58c1ecbStedu int n = 0; 462a58c1ecbStedu const char *lastws = NULL, *p; 463a58c1ecbStedu 464a58c1ecbStedu p = pstart; 465a58c1ecbStedu 466a58c1ecbStedu while (*p && n < maxlen[1] 467a58c1ecbStedu && *p != '\n') { 46873902435Smmcc if (isspace ((u_char)(*p)) 469a58c1ecbStedu || *p == '-') lastws = 470a58c1ecbStedu p; 471a58c1ecbStedu n++; 472a58c1ecbStedu p++; 473a58c1ecbStedu } 474a58c1ecbStedu 475a58c1ecbStedu if (!*p) { /* hit end of desc. done. */ 476a58c1ecbStedu fprintf (fp, "%s\n", 477a58c1ecbStedu pstart); 478a58c1ecbStedu break; 479a58c1ecbStedu } 480a58c1ecbStedu else if (*p == '\n') { /* print everything up to here then wrap. */ 481a58c1ecbStedu fprintf (fp, "%.*s\n", n, 482a58c1ecbStedu pstart); 483a58c1ecbStedu PRINT_SPACES (fp, desccol); 484a58c1ecbStedu pstart = p + 1; 485a58c1ecbStedu continue; 486a58c1ecbStedu } 487a58c1ecbStedu else { /* we hit the edge of the screen. wrap at space if possible. */ 488a58c1ecbStedu if (lastws) { 489a58c1ecbStedu fprintf (fp, 490a58c1ecbStedu "%.*s\n", 491a58c1ecbStedu (int)(lastws - pstart), 492a58c1ecbStedu pstart); 493a58c1ecbStedu pstart = 494a58c1ecbStedu lastws + 1; 495a58c1ecbStedu } 496a58c1ecbStedu else { 497a58c1ecbStedu fprintf (fp, 498a58c1ecbStedu "%.*s\n", 499a58c1ecbStedu n, 500a58c1ecbStedu pstart); 501a58c1ecbStedu pstart = p + 1; 502a58c1ecbStedu } 503a58c1ecbStedu PRINT_SPACES (fp, desccol); 504a58c1ecbStedu continue; 505a58c1ecbStedu } 506a58c1ecbStedu } 507a58c1ecbStedu } 508a58c1ecbStedu } 509a58c1ecbStedu } /* end while */ 510a58c1ecbStedu free (store); 511a58c1ecbStedu return 0; 512a58c1ecbStedu } 513a58c1ecbStedu #endif /* no scanopt_usage */ 514a58c1ecbStedu 515a58c1ecbStedu 516*20c29e2bSop static int scanopt_err (struct _scanopt_t *s, int opt_offset, 517*20c29e2bSop int is_short, int err) 518a58c1ecbStedu { 519a58c1ecbStedu const char *optname = ""; 520a58c1ecbStedu char optchar[2]; 521a58c1ecbStedu 522a58c1ecbStedu if (!s->no_err_msg) { 523a58c1ecbStedu 524a58c1ecbStedu if (s->index > 0 && s->index < s->argc) { 525a58c1ecbStedu if (is_short) { 526a58c1ecbStedu optchar[0] = 527a58c1ecbStedu s->argv[s->index][s->subscript]; 528a58c1ecbStedu optchar[1] = '\0'; 529a58c1ecbStedu optname = optchar; 530a58c1ecbStedu } 531a58c1ecbStedu else { 532a58c1ecbStedu optname = s->argv[s->index]; 533a58c1ecbStedu } 534a58c1ecbStedu } 535a58c1ecbStedu 536a58c1ecbStedu fprintf (stderr, "%s: ", s->argv[0]); 537a58c1ecbStedu switch (err) { 538a58c1ecbStedu case SCANOPT_ERR_ARG_NOT_ALLOWED: 539a58c1ecbStedu fprintf (stderr, 540a58c1ecbStedu _ 541a58c1ecbStedu ("option `%s' doesn't allow an argument\n"), 542a58c1ecbStedu optname); 543a58c1ecbStedu break; 544a58c1ecbStedu case SCANOPT_ERR_ARG_NOT_FOUND: 545a58c1ecbStedu fprintf (stderr, 546a58c1ecbStedu _("option `%s' requires an argument\n"), 547a58c1ecbStedu optname); 548a58c1ecbStedu break; 549a58c1ecbStedu case SCANOPT_ERR_OPT_AMBIGUOUS: 550a58c1ecbStedu fprintf (stderr, _("option `%s' is ambiguous\n"), 551a58c1ecbStedu optname); 552a58c1ecbStedu break; 553a58c1ecbStedu case SCANOPT_ERR_OPT_UNRECOGNIZED: 554a58c1ecbStedu fprintf (stderr, _("Unrecognized option `%s'\n"), 555a58c1ecbStedu optname); 556a58c1ecbStedu break; 557a58c1ecbStedu default: 558a58c1ecbStedu fprintf (stderr, _("Unknown error=(%d)\n"), err); 559a58c1ecbStedu break; 560a58c1ecbStedu } 561a58c1ecbStedu } 562a58c1ecbStedu return err; 563a58c1ecbStedu } 564a58c1ecbStedu 565a58c1ecbStedu 566a58c1ecbStedu /* Internal. Match str against the regex ^--([^=]+)(=(.*))? 567a58c1ecbStedu * return 1 if *looks* like a long option. 568a58c1ecbStedu * 'str' is the only input argument, the rest of the arguments are output only. 569a58c1ecbStedu * optname will point to str + 2 570a58c1ecbStedu * 571a58c1ecbStedu */ 572*20c29e2bSop static int matchlongopt (char *str, char **optname, int *optlen, 573*20c29e2bSop char **arg, int *arglen) 574a58c1ecbStedu { 575a58c1ecbStedu char *p; 576a58c1ecbStedu 577a58c1ecbStedu *optname = *arg = (char *) 0; 578a58c1ecbStedu *optlen = *arglen = 0; 579a58c1ecbStedu 580a58c1ecbStedu /* Match regex /--./ */ 581a58c1ecbStedu p = str; 582a58c1ecbStedu if (p[0] != '-' || p[1] != '-' || !p[2]) 583a58c1ecbStedu return 0; 584a58c1ecbStedu 585a58c1ecbStedu p += 2; 586a58c1ecbStedu *optname = (char *) p; 587a58c1ecbStedu 588a58c1ecbStedu /* find the end of optname */ 589a58c1ecbStedu while (*p && *p != '=') 590a58c1ecbStedu ++p; 591a58c1ecbStedu 592a58c1ecbStedu *optlen = p - *optname; 593a58c1ecbStedu 594a58c1ecbStedu if (!*p) 595a58c1ecbStedu /* an option with no '=...' part. */ 596a58c1ecbStedu return 1; 597a58c1ecbStedu 598a58c1ecbStedu 599a58c1ecbStedu /* We saw an '=' char. The rest of p is the arg. */ 600a58c1ecbStedu p++; 601a58c1ecbStedu *arg = p; 602a58c1ecbStedu while (*p) 603a58c1ecbStedu ++p; 604a58c1ecbStedu *arglen = p - *arg; 605a58c1ecbStedu 606a58c1ecbStedu return 1; 607a58c1ecbStedu } 608a58c1ecbStedu 609a58c1ecbStedu 610a58c1ecbStedu /* Internal. Look up long or short option by name. 611a58c1ecbStedu * Long options must match a non-ambiguous prefix, or exact match. 612a58c1ecbStedu * Short options must be exact. 613a58c1ecbStedu * Return boolean true if found and no error. 614a58c1ecbStedu * Error stored in err_code or zero if no error. */ 615*20c29e2bSop static int find_opt (struct _scanopt_t *s, int lookup_long, char *optstart, 616*20c29e2bSop int len, int *err_code, int *opt_offset) 617a58c1ecbStedu { 618a58c1ecbStedu int nmatch = 0, lastr_val = 0, i; 619a58c1ecbStedu 620a58c1ecbStedu *err_code = 0; 621a58c1ecbStedu *opt_offset = -1; 622a58c1ecbStedu 623a58c1ecbStedu if (!optstart) 624a58c1ecbStedu return 0; 625a58c1ecbStedu 626a58c1ecbStedu for (i = 0; i < s->optc; i++) { 627a58c1ecbStedu char *optname; 628a58c1ecbStedu 629a58c1ecbStedu optname = 630a58c1ecbStedu (char *) (s->options[i].opt_fmt + 631a58c1ecbStedu (lookup_long ? 2 : 1)); 632a58c1ecbStedu 633a58c1ecbStedu if (lookup_long && (s->aux[i].flags & IS_LONG)) { 634a58c1ecbStedu if (len > s->aux[i].namelen) 635a58c1ecbStedu continue; 636a58c1ecbStedu 637a58c1ecbStedu if (strncmp (optname, optstart, len) == 0) { 638a58c1ecbStedu nmatch++; 639a58c1ecbStedu *opt_offset = i; 640a58c1ecbStedu 641a58c1ecbStedu /* exact match overrides all. */ 642a58c1ecbStedu if (len == s->aux[i].namelen) { 643a58c1ecbStedu nmatch = 1; 644a58c1ecbStedu break; 645a58c1ecbStedu } 646a58c1ecbStedu 647a58c1ecbStedu /* ambiguity is ok between aliases. */ 648a58c1ecbStedu if (lastr_val 649a58c1ecbStedu && lastr_val == 650a58c1ecbStedu s->options[i].r_val) nmatch--; 651a58c1ecbStedu lastr_val = s->options[i].r_val; 652a58c1ecbStedu } 653a58c1ecbStedu } 654a58c1ecbStedu else if (!lookup_long && !(s->aux[i].flags & IS_LONG)) { 655a58c1ecbStedu if (optname[0] == optstart[0]) { 656a58c1ecbStedu nmatch++; 657a58c1ecbStedu *opt_offset = i; 658a58c1ecbStedu } 659a58c1ecbStedu } 660a58c1ecbStedu } 661a58c1ecbStedu 662a58c1ecbStedu if (nmatch == 0) { 663a58c1ecbStedu *err_code = SCANOPT_ERR_OPT_UNRECOGNIZED; 664a58c1ecbStedu *opt_offset = -1; 665a58c1ecbStedu } 666a58c1ecbStedu else if (nmatch > 1) { 667a58c1ecbStedu *err_code = SCANOPT_ERR_OPT_AMBIGUOUS; 668a58c1ecbStedu *opt_offset = -1; 669a58c1ecbStedu } 670a58c1ecbStedu 671a58c1ecbStedu return *err_code ? 0 : 1; 672a58c1ecbStedu } 673a58c1ecbStedu 674a58c1ecbStedu 675*20c29e2bSop int scanopt (scanopt_t *svoid, char **arg, int *optindex) 676a58c1ecbStedu { 677a58c1ecbStedu char *optname = NULL, *optarg = NULL, *pstart; 678a58c1ecbStedu int namelen = 0, arglen = 0; 679a58c1ecbStedu int errcode = 0, has_next; 680a58c1ecbStedu const optspec_t *optp; 681a58c1ecbStedu struct _scanopt_t *s; 682a58c1ecbStedu struct _aux *auxp; 683a58c1ecbStedu int is_short; 684a58c1ecbStedu int opt_offset = -1; 685a58c1ecbStedu 686a58c1ecbStedu s = (struct _scanopt_t *) svoid; 687a58c1ecbStedu 688a58c1ecbStedu /* Normalize return-parameters. */ 689a58c1ecbStedu SAFE_ASSIGN (arg, NULL); 690a58c1ecbStedu SAFE_ASSIGN (optindex, s->index); 691a58c1ecbStedu 692a58c1ecbStedu if (s->index >= s->argc) 693a58c1ecbStedu return 0; 694a58c1ecbStedu 695a58c1ecbStedu /* pstart always points to the start of our current scan. */ 696a58c1ecbStedu pstart = s->argv[s->index] + s->subscript; 697a58c1ecbStedu if (!pstart) 698a58c1ecbStedu return 0; 699a58c1ecbStedu 700a58c1ecbStedu if (s->subscript == 0) { 701a58c1ecbStedu 702a58c1ecbStedu /* test for exact match of "--" */ 703a58c1ecbStedu if (pstart[0] == '-' && pstart[1] == '-' && !pstart[2]) { 704a58c1ecbStedu SAFE_ASSIGN (optindex, s->index + 1); 705a58c1ecbStedu INC_INDEX (s, 1); 706a58c1ecbStedu return 0; 707a58c1ecbStedu } 708a58c1ecbStedu 709a58c1ecbStedu /* Match an opt. */ 710a58c1ecbStedu if (matchlongopt 711a58c1ecbStedu (pstart, &optname, &namelen, &optarg, &arglen)) { 712a58c1ecbStedu 713a58c1ecbStedu /* it LOOKS like an opt, but is it one?! */ 714a58c1ecbStedu if (!find_opt 715a58c1ecbStedu (s, 1, optname, namelen, &errcode, 716a58c1ecbStedu &opt_offset)) { 717a58c1ecbStedu scanopt_err (s, opt_offset, 0, errcode); 718a58c1ecbStedu return errcode; 719a58c1ecbStedu } 720a58c1ecbStedu /* We handle this below. */ 721a58c1ecbStedu is_short = 0; 722a58c1ecbStedu 723a58c1ecbStedu /* Check for short opt. */ 724a58c1ecbStedu } 725a58c1ecbStedu else if (pstart[0] == '-' && pstart[1]) { 726a58c1ecbStedu /* Pass through to below. */ 727a58c1ecbStedu is_short = 1; 728a58c1ecbStedu s->subscript++; 729a58c1ecbStedu pstart++; 730a58c1ecbStedu } 731a58c1ecbStedu 732a58c1ecbStedu else { 733a58c1ecbStedu /* It's not an option. We're done. */ 734a58c1ecbStedu return 0; 735a58c1ecbStedu } 736a58c1ecbStedu } 737a58c1ecbStedu 738a58c1ecbStedu /* We have to re-check the subscript status because it 739a58c1ecbStedu * may have changed above. */ 740a58c1ecbStedu 741a58c1ecbStedu if (s->subscript != 0) { 742a58c1ecbStedu 743a58c1ecbStedu /* we are somewhere in a run of short opts, 744a58c1ecbStedu * e.g., at the 'z' in `tar -xzf` */ 745a58c1ecbStedu 746a58c1ecbStedu optname = pstart; 747a58c1ecbStedu namelen = 1; 748a58c1ecbStedu is_short = 1; 749a58c1ecbStedu 750a58c1ecbStedu if (!find_opt 751a58c1ecbStedu (s, 0, pstart, namelen, &errcode, &opt_offset)) { 752a58c1ecbStedu return scanopt_err (s, opt_offset, 1, errcode); 753a58c1ecbStedu } 754a58c1ecbStedu 755a58c1ecbStedu optarg = pstart + 1; 756a58c1ecbStedu if (!*optarg) { 757a58c1ecbStedu optarg = NULL; 758a58c1ecbStedu arglen = 0; 759a58c1ecbStedu } 760a58c1ecbStedu else 761a58c1ecbStedu arglen = strlen (optarg); 762a58c1ecbStedu } 763a58c1ecbStedu 764a58c1ecbStedu /* At this point, we have a long or short option matched at opt_offset into 765a58c1ecbStedu * the s->options array (and corresponding aux array). 766a58c1ecbStedu * A trailing argument is in {optarg,arglen}, if any. 767a58c1ecbStedu */ 768a58c1ecbStedu 769a58c1ecbStedu /* Look ahead in argv[] to see if there is something 770a58c1ecbStedu * that we can use as an argument (if needed). */ 771a58c1ecbStedu has_next = s->index + 1 < s->argc 772a58c1ecbStedu && strcmp ("--", s->argv[s->index + 1]) != 0; 773a58c1ecbStedu 774a58c1ecbStedu optp = s->options + opt_offset; 775a58c1ecbStedu auxp = s->aux + opt_offset; 776a58c1ecbStedu 777a58c1ecbStedu /* case: no args allowed */ 778a58c1ecbStedu if (auxp->flags & ARG_NONE) { 779a58c1ecbStedu if (optarg && !is_short) { 780a58c1ecbStedu scanopt_err (s, opt_offset, is_short, errcode = 781a58c1ecbStedu SCANOPT_ERR_ARG_NOT_ALLOWED); 782a58c1ecbStedu INC_INDEX (s, 1); 783a58c1ecbStedu return errcode; 784a58c1ecbStedu } 785a58c1ecbStedu else if (!optarg) 786a58c1ecbStedu INC_INDEX (s, 1); 787a58c1ecbStedu else 788a58c1ecbStedu s->subscript++; 789a58c1ecbStedu return optp->r_val; 790a58c1ecbStedu } 791a58c1ecbStedu 792a58c1ecbStedu /* case: required */ 793a58c1ecbStedu if (auxp->flags & ARG_REQ) { 794a58c1ecbStedu if (!optarg && !has_next) 795a58c1ecbStedu return scanopt_err (s, opt_offset, is_short, 796a58c1ecbStedu SCANOPT_ERR_ARG_NOT_FOUND); 797a58c1ecbStedu 798a58c1ecbStedu if (!optarg) { 799a58c1ecbStedu /* Let the next argv element become the argument. */ 800a58c1ecbStedu SAFE_ASSIGN (arg, s->argv[s->index + 1]); 801a58c1ecbStedu INC_INDEX (s, 2); 802a58c1ecbStedu } 803a58c1ecbStedu else { 804a58c1ecbStedu SAFE_ASSIGN (arg, (char *) optarg); 805a58c1ecbStedu INC_INDEX (s, 1); 806a58c1ecbStedu } 807a58c1ecbStedu return optp->r_val; 808a58c1ecbStedu } 809a58c1ecbStedu 810a58c1ecbStedu /* case: optional */ 811a58c1ecbStedu if (auxp->flags & ARG_OPT) { 812a58c1ecbStedu SAFE_ASSIGN (arg, optarg); 813a58c1ecbStedu INC_INDEX (s, 1); 814a58c1ecbStedu return optp->r_val; 815a58c1ecbStedu } 816a58c1ecbStedu 817a58c1ecbStedu 818a58c1ecbStedu /* Should not reach here. */ 819a58c1ecbStedu return 0; 820a58c1ecbStedu } 821a58c1ecbStedu 822a58c1ecbStedu 823*20c29e2bSop void scanopt_destroy (scanopt_t *svoid) 824a58c1ecbStedu { 825a58c1ecbStedu struct _scanopt_t *s; 826a58c1ecbStedu 827a58c1ecbStedu s = (struct _scanopt_t *) svoid; 828a58c1ecbStedu if (s) { 829a58c1ecbStedu free(s->aux); 830a58c1ecbStedu free (s); 831a58c1ecbStedu } 832a58c1ecbStedu } 833