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