1228072Sbapt/* flex - tool to generate fast lexical analyzers */ 2228072Sbapt 3228072Sbapt/* Copyright (c) 1990 The Regents of the University of California. */ 4228072Sbapt/* All rights reserved. */ 5228072Sbapt 6228072Sbapt/* This code is derived from software contributed to Berkeley by */ 7228072Sbapt/* Vern Paxson. */ 8228072Sbapt 9228072Sbapt/* The United States Government has rights in this work pursuant */ 10228072Sbapt/* to contract no. DE-AC03-76SF00098 between the United States */ 11228072Sbapt/* Department of Energy and the University of California. */ 12228072Sbapt 13228072Sbapt/* This file is part of flex. */ 14228072Sbapt 15228072Sbapt/* Redistribution and use in source and binary forms, with or without */ 16228072Sbapt/* modification, are permitted provided that the following conditions */ 17228072Sbapt/* are met: */ 18228072Sbapt 19228072Sbapt/* 1. Redistributions of source code must retain the above copyright */ 20228072Sbapt/* notice, this list of conditions and the following disclaimer. */ 21228072Sbapt/* 2. Redistributions in binary form must reproduce the above copyright */ 22228072Sbapt/* notice, this list of conditions and the following disclaimer in the */ 23228072Sbapt/* documentation and/or other materials provided with the distribution. */ 24228072Sbapt 25228072Sbapt/* Neither the name of the University nor the names of its contributors */ 26228072Sbapt/* may be used to endorse or promote products derived from this software */ 27228072Sbapt/* without specific prior written permission. */ 28228072Sbapt 29228072Sbapt/* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR */ 30228072Sbapt/* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED */ 31228072Sbapt/* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR */ 32228072Sbapt/* PURPOSE. */ 33228072Sbapt 34228072Sbapt#include "flexdef.h" 35228072Sbapt#include "scanopt.h" 36228072Sbapt 37228072Sbapt 38228072Sbapt/* Internal structures */ 39228072Sbapt 40228072Sbapt#ifdef HAVE_STRCASECMP 41228072Sbapt#define STRCASECMP(a,b) strcasecmp(a,b) 42228072Sbapt#else 43228072Sbaptstatic int STRCASECMP PROTO ((const char *, const char *)); 44228072Sbapt 45228072Sbaptstatic int STRCASECMP (a, b) 46228072Sbapt const char *a; 47228072Sbapt const char *b; 48228072Sbapt{ 49228072Sbapt while (tolower (*a++) == tolower (*b++)) ; 50228072Sbapt return b - a; 51228072Sbapt} 52228072Sbapt#endif 53228072Sbapt 54228072Sbapt#define ARG_NONE 0x01 55228072Sbapt#define ARG_REQ 0x02 56228072Sbapt#define ARG_OPT 0x04 57228072Sbapt#define IS_LONG 0x08 58228072Sbapt 59228072Sbaptstruct _aux { 60228072Sbapt int flags; /* The above hex flags. */ 61228072Sbapt int namelen; /* Length of the actual option word, e.g., "--file[=foo]" is 4 */ 62228072Sbapt int printlen; /* Length of entire string, e.g., "--file[=foo]" is 12 */ 63228072Sbapt}; 64228072Sbapt 65228072Sbapt 66228072Sbaptstruct _scanopt_t { 67228072Sbapt const optspec_t *options; /* List of options. */ 68228072Sbapt struct _aux *aux; /* Auxiliary data about options. */ 69228072Sbapt int optc; /* Number of options. */ 70228072Sbapt int argc; /* Number of args. */ 71228072Sbapt char **argv; /* Array of strings. */ 72228072Sbapt int index; /* Used as: argv[index][subscript]. */ 73228072Sbapt int subscript; 74228072Sbapt char no_err_msg; /* If true, do not print errors. */ 75228072Sbapt char has_long; 76228072Sbapt char has_short; 77228072Sbapt}; 78228072Sbapt 79228072Sbapt/* Accessor functions. These WOULD be one-liners, but portability calls. */ 80228072Sbaptstatic const char *NAME PROTO ((struct _scanopt_t *, int)); 81228072Sbaptstatic int PRINTLEN PROTO ((struct _scanopt_t *, int)); 82228072Sbaptstatic int RVAL PROTO ((struct _scanopt_t *, int)); 83228072Sbaptstatic int FLAGS PROTO ((struct _scanopt_t *, int)); 84228072Sbaptstatic const char *DESC PROTO ((struct _scanopt_t *, int)); 85228072Sbaptstatic int scanopt_err PROTO ((struct _scanopt_t *, int, int, int)); 86228072Sbaptstatic int matchlongopt PROTO ((char *, char **, int *, char **, int *)); 87228072Sbaptstatic int find_opt 88228072SbaptPROTO ((struct _scanopt_t *, int, char *, int, int *, int *opt_offset)); 89228072Sbapt 90228072Sbaptstatic const char *NAME (s, i) 91228072Sbapt struct _scanopt_t *s; 92228072Sbapt int i; 93228072Sbapt{ 94228072Sbapt return s->options[i].opt_fmt + 95228072Sbapt ((s->aux[i].flags & IS_LONG) ? 2 : 1); 96228072Sbapt} 97228072Sbapt 98228072Sbaptstatic int PRINTLEN (s, i) 99228072Sbapt struct _scanopt_t *s; 100228072Sbapt int i; 101228072Sbapt{ 102228072Sbapt return s->aux[i].printlen; 103228072Sbapt} 104228072Sbapt 105228072Sbaptstatic int RVAL (s, i) 106228072Sbapt struct _scanopt_t *s; 107228072Sbapt int i; 108228072Sbapt{ 109228072Sbapt return s->options[i].r_val; 110228072Sbapt} 111228072Sbapt 112228072Sbaptstatic int FLAGS (s, i) 113228072Sbapt struct _scanopt_t *s; 114228072Sbapt int i; 115228072Sbapt{ 116228072Sbapt return s->aux[i].flags; 117228072Sbapt} 118228072Sbapt 119228072Sbaptstatic const char *DESC (s, i) 120228072Sbapt struct _scanopt_t *s; 121228072Sbapt int i; 122228072Sbapt{ 123228072Sbapt return s->options[i].desc ? s->options[i].desc : ""; 124228072Sbapt} 125228072Sbapt 126228072Sbapt#ifndef NO_SCANOPT_USAGE 127228072Sbaptstatic int get_cols PROTO ((void)); 128228072Sbapt 129228072Sbaptstatic int get_cols () 130228072Sbapt{ 131228072Sbapt char *env; 132228072Sbapt int cols = 80; /* default */ 133228072Sbapt 134228072Sbapt#ifdef HAVE_NCURSES_H 135228072Sbapt initscr (); 136228072Sbapt endwin (); 137228072Sbapt if (COLS > 0) 138228072Sbapt return COLS; 139228072Sbapt#endif 140228072Sbapt 141228072Sbapt if ((env = getenv ("COLUMNS")) != NULL) 142228072Sbapt cols = atoi (env); 143228072Sbapt 144228072Sbapt return cols; 145228072Sbapt} 146228072Sbapt#endif 147228072Sbapt 148228072Sbapt/* Macro to check for NULL before assigning a value. */ 149228072Sbapt#define SAFE_ASSIGN(ptr,val) \ 150228072Sbapt do{ \ 151228072Sbapt if((ptr)!=NULL) \ 152228072Sbapt *(ptr) = val; \ 153228072Sbapt }while(0) 154228072Sbapt 155228072Sbapt/* Macro to assure we reset subscript whenever we adjust s->index.*/ 156228072Sbapt#define INC_INDEX(s,n) \ 157228072Sbapt do{ \ 158228072Sbapt (s)->index += (n); \ 159228072Sbapt (s)->subscript= 0; \ 160228072Sbapt }while(0) 161228072Sbapt 162228072Sbaptscanopt_t *scanopt_init (options, argc, argv, flags) 163228072Sbapt const optspec_t *options; 164228072Sbapt int argc; 165228072Sbapt char **argv; 166228072Sbapt int flags; 167228072Sbapt{ 168228072Sbapt int i; 169228072Sbapt struct _scanopt_t *s; 170228072Sbapt s = (struct _scanopt_t *) malloc (sizeof (struct _scanopt_t)); 171228072Sbapt 172228072Sbapt s->options = options; 173228072Sbapt s->optc = 0; 174228072Sbapt s->argc = argc; 175228072Sbapt s->argv = (char **) argv; 176228072Sbapt s->index = 1; 177228072Sbapt s->subscript = 0; 178228072Sbapt s->no_err_msg = (flags & SCANOPT_NO_ERR_MSG); 179228072Sbapt s->has_long = 0; 180228072Sbapt s->has_short = 0; 181228072Sbapt 182228072Sbapt /* Determine option count. (Find entry with all zeros). */ 183228072Sbapt s->optc = 0; 184228072Sbapt while (options[s->optc].opt_fmt 185228072Sbapt || options[s->optc].r_val || options[s->optc].desc) 186228072Sbapt s->optc++; 187228072Sbapt 188228072Sbapt /* Build auxiliary data */ 189228072Sbapt s->aux = (struct _aux *) malloc (s->optc * sizeof (struct _aux)); 190228072Sbapt 191228072Sbapt for (i = 0; i < s->optc; i++) { 192250125Sjkim const Char *p, *pname; 193228072Sbapt const struct optspec_t *opt; 194228072Sbapt struct _aux *aux; 195228072Sbapt 196228072Sbapt opt = s->options + i; 197228072Sbapt aux = s->aux + i; 198228072Sbapt 199228072Sbapt aux->flags = ARG_NONE; 200228072Sbapt 201228072Sbapt if (opt->opt_fmt[0] == '-' && opt->opt_fmt[1] == '-') { 202228072Sbapt aux->flags |= IS_LONG; 203250125Sjkim pname = (const Char *)(opt->opt_fmt + 2); 204228072Sbapt s->has_long = 1; 205228072Sbapt } 206228072Sbapt else { 207250125Sjkim pname = (const Char *)(opt->opt_fmt + 1); 208228072Sbapt s->has_short = 1; 209228072Sbapt } 210228072Sbapt aux->printlen = strlen (opt->opt_fmt); 211228072Sbapt 212228072Sbapt aux->namelen = 0; 213228072Sbapt for (p = pname + 1; *p; p++) { 214228072Sbapt /* detect required arg */ 215228072Sbapt if (*p == '=' || isspace (*p) 216228072Sbapt || !(aux->flags & IS_LONG)) { 217228072Sbapt if (aux->namelen == 0) 218228072Sbapt aux->namelen = p - pname; 219228072Sbapt aux->flags |= ARG_REQ; 220228072Sbapt aux->flags &= ~ARG_NONE; 221228072Sbapt } 222228072Sbapt /* detect optional arg. This overrides required arg. */ 223228072Sbapt if (*p == '[') { 224228072Sbapt if (aux->namelen == 0) 225228072Sbapt aux->namelen = p - pname; 226228072Sbapt aux->flags &= ~(ARG_REQ | ARG_NONE); 227228072Sbapt aux->flags |= ARG_OPT; 228228072Sbapt break; 229228072Sbapt } 230228072Sbapt } 231228072Sbapt if (aux->namelen == 0) 232228072Sbapt aux->namelen = p - pname; 233228072Sbapt } 234228072Sbapt return (scanopt_t *) s; 235228072Sbapt} 236228072Sbapt 237228072Sbapt#ifndef NO_SCANOPT_USAGE 238228072Sbapt/* these structs are for scanopt_usage(). */ 239228072Sbaptstruct usg_elem { 240228072Sbapt int idx; 241228072Sbapt struct usg_elem *next; 242228072Sbapt struct usg_elem *alias; 243228072Sbapt}; 244228072Sbapttypedef struct usg_elem usg_elem; 245228072Sbapt 246228072Sbapt 247228072Sbapt/* Prints a usage message based on contents of optlist. 248228072Sbapt * Parameters: 249228072Sbapt * scanner - The scanner, already initialized with scanopt_init(). 250228072Sbapt * fp - The file stream to write to. 251228072Sbapt * usage - Text to be prepended to option list. 252228072Sbapt * Return: Always returns 0 (zero). 253228072Sbapt * The output looks something like this: 254228072Sbapt 255228072Sbapt[indent][option, alias1, alias2...][indent][description line1 256228072Sbapt description line2...] 257228072Sbapt */ 258228072Sbaptint scanopt_usage (scanner, fp, usage) 259228072Sbapt scanopt_t *scanner; 260228072Sbapt FILE *fp; 261228072Sbapt const char *usage; 262228072Sbapt{ 263228072Sbapt struct _scanopt_t *s; 264228072Sbapt int i, columns, indent = 2; 265228072Sbapt usg_elem *byr_val = NULL; /* option indices sorted by r_val */ 266228072Sbapt usg_elem *store; /* array of preallocated elements. */ 267228072Sbapt int store_idx = 0; 268228072Sbapt usg_elem *ue; 269228072Sbapt int maxlen[2]; 270228072Sbapt int desccol = 0; 271228072Sbapt int print_run = 0; 272228072Sbapt 273228072Sbapt maxlen[0] = 0; 274228072Sbapt maxlen[1] = 0; 275228072Sbapt 276228072Sbapt s = (struct _scanopt_t *) scanner; 277228072Sbapt 278228072Sbapt if (usage) { 279228072Sbapt fprintf (fp, "%s\n", usage); 280228072Sbapt } 281228072Sbapt else { 282228072Sbapt /* Find the basename of argv[0] */ 283228072Sbapt const char *p; 284228072Sbapt 285228072Sbapt p = s->argv[0] + strlen (s->argv[0]); 286228072Sbapt while (p != s->argv[0] && *p != '/') 287228072Sbapt --p; 288228072Sbapt if (*p == '/') 289228072Sbapt p++; 290228072Sbapt 291228072Sbapt fprintf (fp, _("Usage: %s [OPTIONS]...\n"), p); 292228072Sbapt } 293228072Sbapt fprintf (fp, "\n"); 294228072Sbapt 295228072Sbapt /* Sort by r_val and string. Yes, this is O(n*n), but n is small. */ 296228072Sbapt store = (usg_elem *) malloc (s->optc * sizeof (usg_elem)); 297228072Sbapt for (i = 0; i < s->optc; i++) { 298228072Sbapt 299228072Sbapt /* grab the next preallocate node. */ 300228072Sbapt ue = store + store_idx++; 301228072Sbapt ue->idx = i; 302228072Sbapt ue->next = ue->alias = NULL; 303228072Sbapt 304228072Sbapt /* insert into list. */ 305228072Sbapt if (!byr_val) 306228072Sbapt byr_val = ue; 307228072Sbapt else { 308228072Sbapt int found_alias = 0; 309228072Sbapt usg_elem **ue_curr, **ptr_if_no_alias = NULL; 310228072Sbapt 311228072Sbapt ue_curr = &byr_val; 312228072Sbapt while (*ue_curr) { 313228072Sbapt if (RVAL (s, (*ue_curr)->idx) == 314228072Sbapt RVAL (s, ue->idx)) { 315228072Sbapt /* push onto the alias list. */ 316228072Sbapt ue_curr = &((*ue_curr)->alias); 317228072Sbapt found_alias = 1; 318228072Sbapt break; 319228072Sbapt } 320228072Sbapt if (!ptr_if_no_alias 321228072Sbapt && 322228072Sbapt STRCASECMP (NAME (s, (*ue_curr)->idx), 323228072Sbapt NAME (s, ue->idx)) > 0) { 324228072Sbapt ptr_if_no_alias = ue_curr; 325228072Sbapt } 326228072Sbapt ue_curr = &((*ue_curr)->next); 327228072Sbapt } 328228072Sbapt if (!found_alias && ptr_if_no_alias) 329228072Sbapt ue_curr = ptr_if_no_alias; 330228072Sbapt ue->next = *ue_curr; 331228072Sbapt *ue_curr = ue; 332228072Sbapt } 333228072Sbapt } 334228072Sbapt 335228072Sbapt#if 0 336228072Sbapt if (1) { 337228072Sbapt printf ("ORIGINAL:\n"); 338228072Sbapt for (i = 0; i < s->optc; i++) 339228072Sbapt printf ("%2d: %s\n", i, NAME (s, i)); 340228072Sbapt printf ("SORTED:\n"); 341228072Sbapt ue = byr_val; 342228072Sbapt while (ue) { 343228072Sbapt usg_elem *ue2; 344228072Sbapt 345228072Sbapt printf ("%2d: %s\n", ue->idx, NAME (s, ue->idx)); 346228072Sbapt for (ue2 = ue->alias; ue2; ue2 = ue2->next) 347228072Sbapt printf (" +---> %2d: %s\n", ue2->idx, 348228072Sbapt NAME (s, ue2->idx)); 349228072Sbapt ue = ue->next; 350228072Sbapt } 351228072Sbapt } 352228072Sbapt#endif 353228072Sbapt 354228072Sbapt /* Now build each row of output. */ 355228072Sbapt 356228072Sbapt /* first pass calculate how much room we need. */ 357228072Sbapt for (ue = byr_val; ue; ue = ue->next) { 358228072Sbapt usg_elem *ap; 359228072Sbapt int len = 0; 360228072Sbapt int nshort = 0, nlong = 0; 361228072Sbapt 362228072Sbapt 363228072Sbapt#define CALC_LEN(i) do {\ 364228072Sbapt if(FLAGS(s,i) & IS_LONG) \ 365228072Sbapt len += (nlong++||nshort) ? 2+PRINTLEN(s,i) : PRINTLEN(s,i);\ 366228072Sbapt else\ 367228072Sbapt len += (nshort++||nlong)? 2+PRINTLEN(s,i) : PRINTLEN(s,i);\ 368228072Sbapt }while(0) 369228072Sbapt 370228072Sbapt if (!(FLAGS (s, ue->idx) & IS_LONG)) 371228072Sbapt CALC_LEN (ue->idx); 372228072Sbapt 373228072Sbapt /* do short aliases first. */ 374228072Sbapt for (ap = ue->alias; ap; ap = ap->next) { 375228072Sbapt if (FLAGS (s, ap->idx) & IS_LONG) 376228072Sbapt continue; 377228072Sbapt CALC_LEN (ap->idx); 378228072Sbapt } 379228072Sbapt 380228072Sbapt if (FLAGS (s, ue->idx) & IS_LONG) 381228072Sbapt CALC_LEN (ue->idx); 382228072Sbapt 383228072Sbapt /* repeat the above loop, this time for long aliases. */ 384228072Sbapt for (ap = ue->alias; ap; ap = ap->next) { 385228072Sbapt if (!(FLAGS (s, ap->idx) & IS_LONG)) 386228072Sbapt continue; 387228072Sbapt CALC_LEN (ap->idx); 388228072Sbapt } 389228072Sbapt 390228072Sbapt if (len > maxlen[0]) 391228072Sbapt maxlen[0] = len; 392228072Sbapt 393228072Sbapt /* It's much easier to calculate length for description column! */ 394228072Sbapt len = strlen (DESC (s, ue->idx)); 395228072Sbapt if (len > maxlen[1]) 396228072Sbapt maxlen[1] = len; 397228072Sbapt } 398228072Sbapt 399228072Sbapt /* Determine how much room we have, and how much we will allocate to each col. 400228072Sbapt * Do not address pathological cases. Output will just be ugly. */ 401228072Sbapt columns = get_cols () - 1; 402228072Sbapt if (maxlen[0] + maxlen[1] + indent * 2 > columns) { 403228072Sbapt /* col 0 gets whatever it wants. we'll wrap the desc col. */ 404228072Sbapt maxlen[1] = columns - (maxlen[0] + indent * 2); 405228072Sbapt if (maxlen[1] < 14) /* 14 is arbitrary lower limit on desc width. */ 406228072Sbapt maxlen[1] = INT_MAX; 407228072Sbapt } 408228072Sbapt desccol = maxlen[0] + indent * 2; 409228072Sbapt 410228072Sbapt#define PRINT_SPACES(fp,n)\ 411228072Sbapt do{\ 412228072Sbapt int _n;\ 413228072Sbapt _n=(n);\ 414228072Sbapt while(_n-- > 0)\ 415228072Sbapt fputc(' ',(fp));\ 416228072Sbapt }while(0) 417228072Sbapt 418228072Sbapt 419228072Sbapt /* Second pass (same as above loop), this time we print. */ 420228072Sbapt /* Sloppy hack: We iterate twice. The first time we print short and long options. 421228072Sbapt The second time we print those lines that have ONLY long options. */ 422228072Sbapt while (print_run++ < 2) { 423228072Sbapt for (ue = byr_val; ue; ue = ue->next) { 424228072Sbapt usg_elem *ap; 425228072Sbapt int nwords = 0, nchars = 0, has_short = 0; 426228072Sbapt 427228072Sbapt/* TODO: get has_short schtick to work */ 428228072Sbapt has_short = !(FLAGS (s, ue->idx) & IS_LONG); 429228072Sbapt for (ap = ue->alias; ap; ap = ap->next) { 430228072Sbapt if (!(FLAGS (s, ap->idx) & IS_LONG)) { 431228072Sbapt has_short = 1; 432228072Sbapt break; 433228072Sbapt } 434228072Sbapt } 435228072Sbapt if ((print_run == 1 && !has_short) || 436228072Sbapt (print_run == 2 && has_short)) 437228072Sbapt continue; 438228072Sbapt 439228072Sbapt PRINT_SPACES (fp, indent); 440228072Sbapt nchars += indent; 441228072Sbapt 442228072Sbapt/* Print, adding a ", " between aliases. */ 443228072Sbapt#define PRINT_IT(i) do{\ 444228072Sbapt if(nwords++)\ 445228072Sbapt nchars+=fprintf(fp,", ");\ 446228072Sbapt nchars+=fprintf(fp,"%s",s->options[i].opt_fmt);\ 447228072Sbapt }while(0) 448228072Sbapt 449228072Sbapt if (!(FLAGS (s, ue->idx) & IS_LONG)) 450228072Sbapt PRINT_IT (ue->idx); 451228072Sbapt 452228072Sbapt /* print short aliases first. */ 453228072Sbapt for (ap = ue->alias; ap; ap = ap->next) { 454228072Sbapt if (!(FLAGS (s, ap->idx) & IS_LONG)) 455228072Sbapt PRINT_IT (ap->idx); 456228072Sbapt } 457228072Sbapt 458228072Sbapt 459228072Sbapt if (FLAGS (s, ue->idx) & IS_LONG) 460228072Sbapt PRINT_IT (ue->idx); 461228072Sbapt 462228072Sbapt /* repeat the above loop, this time for long aliases. */ 463228072Sbapt for (ap = ue->alias; ap; ap = ap->next) { 464228072Sbapt if (FLAGS (s, ap->idx) & IS_LONG) 465228072Sbapt PRINT_IT (ap->idx); 466228072Sbapt } 467228072Sbapt 468228072Sbapt /* pad to desccol */ 469228072Sbapt PRINT_SPACES (fp, desccol - nchars); 470228072Sbapt 471228072Sbapt /* Print description, wrapped to maxlen[1] columns. */ 472228072Sbapt if (1) { 473228072Sbapt const char *pstart; 474228072Sbapt 475228072Sbapt pstart = DESC (s, ue->idx); 476228072Sbapt while (1) { 477228072Sbapt int n = 0; 478228072Sbapt const char *lastws = NULL, *p; 479228072Sbapt 480228072Sbapt p = pstart; 481228072Sbapt 482228072Sbapt while (*p && n < maxlen[1] 483228072Sbapt && *p != '\n') { 484250125Sjkim if (isspace ((Char)(*p)) 485228072Sbapt || *p == '-') lastws = 486228072Sbapt p; 487228072Sbapt n++; 488228072Sbapt p++; 489228072Sbapt } 490228072Sbapt 491228072Sbapt if (!*p) { /* hit end of desc. done. */ 492228072Sbapt fprintf (fp, "%s\n", 493228072Sbapt pstart); 494228072Sbapt break; 495228072Sbapt } 496228072Sbapt else if (*p == '\n') { /* print everything up to here then wrap. */ 497228072Sbapt fprintf (fp, "%.*s\n", n, 498228072Sbapt pstart); 499228072Sbapt PRINT_SPACES (fp, desccol); 500228072Sbapt pstart = p + 1; 501228072Sbapt continue; 502228072Sbapt } 503228072Sbapt else { /* we hit the edge of the screen. wrap at space if possible. */ 504228072Sbapt if (lastws) { 505228072Sbapt fprintf (fp, 506228072Sbapt "%.*s\n", 507250125Sjkim (int)(lastws - pstart), 508228072Sbapt pstart); 509228072Sbapt pstart = 510228072Sbapt lastws + 1; 511228072Sbapt } 512228072Sbapt else { 513228072Sbapt fprintf (fp, 514228072Sbapt "%.*s\n", 515228072Sbapt n, 516228072Sbapt pstart); 517228072Sbapt pstart = p + 1; 518228072Sbapt } 519228072Sbapt PRINT_SPACES (fp, desccol); 520228072Sbapt continue; 521228072Sbapt } 522228072Sbapt } 523228072Sbapt } 524228072Sbapt } 525228072Sbapt } /* end while */ 526228072Sbapt free (store); 527228072Sbapt return 0; 528228072Sbapt} 529228072Sbapt#endif /* no scanopt_usage */ 530228072Sbapt 531228072Sbapt 532228072Sbaptstatic int scanopt_err (s, opt_offset, is_short, err) 533228072Sbapt struct _scanopt_t *s; 534228072Sbapt int opt_offset; 535228072Sbapt int is_short; 536228072Sbapt int err; 537228072Sbapt{ 538228072Sbapt const char *optname = ""; 539228072Sbapt char optchar[2]; 540228072Sbapt const optspec_t *opt = NULL; 541228072Sbapt 542228072Sbapt if (opt_offset >= 0) 543228072Sbapt opt = s->options + opt_offset; 544228072Sbapt 545228072Sbapt if (!s->no_err_msg) { 546228072Sbapt 547228072Sbapt if (s->index > 0 && s->index < s->argc) { 548228072Sbapt if (is_short) { 549228072Sbapt optchar[0] = 550228072Sbapt s->argv[s->index][s->subscript]; 551228072Sbapt optchar[1] = '\0'; 552228072Sbapt optname = optchar; 553228072Sbapt } 554228072Sbapt else { 555228072Sbapt optname = s->argv[s->index]; 556228072Sbapt } 557228072Sbapt } 558228072Sbapt 559228072Sbapt fprintf (stderr, "%s: ", s->argv[0]); 560228072Sbapt switch (err) { 561228072Sbapt case SCANOPT_ERR_ARG_NOT_ALLOWED: 562228072Sbapt fprintf (stderr, 563228072Sbapt _ 564228072Sbapt ("option `%s' doesn't allow an argument\n"), 565228072Sbapt optname); 566228072Sbapt break; 567228072Sbapt case SCANOPT_ERR_ARG_NOT_FOUND: 568228072Sbapt fprintf (stderr, 569228072Sbapt _("option `%s' requires an argument\n"), 570228072Sbapt optname); 571228072Sbapt break; 572228072Sbapt case SCANOPT_ERR_OPT_AMBIGUOUS: 573228072Sbapt fprintf (stderr, _("option `%s' is ambiguous\n"), 574228072Sbapt optname); 575228072Sbapt break; 576228072Sbapt case SCANOPT_ERR_OPT_UNRECOGNIZED: 577228072Sbapt fprintf (stderr, _("Unrecognized option `%s'\n"), 578228072Sbapt optname); 579228072Sbapt break; 580228072Sbapt default: 581228072Sbapt fprintf (stderr, _("Unknown error=(%d)\n"), err); 582228072Sbapt break; 583228072Sbapt } 584228072Sbapt } 585228072Sbapt return err; 586228072Sbapt} 587228072Sbapt 588228072Sbapt 589228072Sbapt/* Internal. Match str against the regex ^--([^=]+)(=(.*))? 590228072Sbapt * return 1 if *looks* like a long option. 591228072Sbapt * 'str' is the only input argument, the rest of the arguments are output only. 592228072Sbapt * optname will point to str + 2 593228072Sbapt * 594228072Sbapt */ 595228072Sbaptstatic int matchlongopt (str, optname, optlen, arg, arglen) 596228072Sbapt char *str; 597228072Sbapt char **optname; 598228072Sbapt int *optlen; 599228072Sbapt char **arg; 600228072Sbapt int *arglen; 601228072Sbapt{ 602228072Sbapt char *p; 603228072Sbapt 604228072Sbapt *optname = *arg = (char *) 0; 605228072Sbapt *optlen = *arglen = 0; 606228072Sbapt 607228072Sbapt /* Match regex /--./ */ 608228072Sbapt p = str; 609228072Sbapt if (p[0] != '-' || p[1] != '-' || !p[2]) 610228072Sbapt return 0; 611228072Sbapt 612228072Sbapt p += 2; 613228072Sbapt *optname = (char *) p; 614228072Sbapt 615228072Sbapt /* find the end of optname */ 616228072Sbapt while (*p && *p != '=') 617228072Sbapt ++p; 618228072Sbapt 619228072Sbapt *optlen = p - *optname; 620228072Sbapt 621228072Sbapt if (!*p) 622228072Sbapt /* an option with no '=...' part. */ 623228072Sbapt return 1; 624228072Sbapt 625228072Sbapt 626228072Sbapt /* We saw an '=' char. The rest of p is the arg. */ 627228072Sbapt p++; 628228072Sbapt *arg = p; 629228072Sbapt while (*p) 630228072Sbapt ++p; 631228072Sbapt *arglen = p - *arg; 632228072Sbapt 633228072Sbapt return 1; 634228072Sbapt} 635228072Sbapt 636228072Sbapt 637228072Sbapt/* Internal. Look up long or short option by name. 638228072Sbapt * Long options must match a non-ambiguous prefix, or exact match. 639228072Sbapt * Short options must be exact. 640228072Sbapt * Return boolean true if found and no error. 641228072Sbapt * Error stored in err_code or zero if no error. */ 642228072Sbaptstatic int find_opt (s, lookup_long, optstart, len, err_code, opt_offset) 643228072Sbapt struct _scanopt_t *s; 644228072Sbapt int lookup_long; 645228072Sbapt char *optstart; 646228072Sbapt int len; 647228072Sbapt int *err_code; 648228072Sbapt int *opt_offset; 649228072Sbapt{ 650228072Sbapt int nmatch = 0, lastr_val = 0, i; 651228072Sbapt 652228072Sbapt *err_code = 0; 653228072Sbapt *opt_offset = -1; 654228072Sbapt 655228072Sbapt if (!optstart) 656228072Sbapt return 0; 657228072Sbapt 658228072Sbapt for (i = 0; i < s->optc; i++) { 659228072Sbapt char *optname; 660228072Sbapt 661228072Sbapt optname = 662228072Sbapt (char *) (s->options[i].opt_fmt + 663228072Sbapt (lookup_long ? 2 : 1)); 664228072Sbapt 665228072Sbapt if (lookup_long && (s->aux[i].flags & IS_LONG)) { 666228072Sbapt if (len > s->aux[i].namelen) 667228072Sbapt continue; 668228072Sbapt 669228072Sbapt if (strncmp (optname, optstart, len) == 0) { 670228072Sbapt nmatch++; 671228072Sbapt *opt_offset = i; 672228072Sbapt 673228072Sbapt /* exact match overrides all. */ 674228072Sbapt if (len == s->aux[i].namelen) { 675228072Sbapt nmatch = 1; 676228072Sbapt break; 677228072Sbapt } 678228072Sbapt 679228072Sbapt /* ambiguity is ok between aliases. */ 680228072Sbapt if (lastr_val 681228072Sbapt && lastr_val == 682228072Sbapt s->options[i].r_val) nmatch--; 683228072Sbapt lastr_val = s->options[i].r_val; 684228072Sbapt } 685228072Sbapt } 686228072Sbapt else if (!lookup_long && !(s->aux[i].flags & IS_LONG)) { 687228072Sbapt if (optname[0] == optstart[0]) { 688228072Sbapt nmatch++; 689228072Sbapt *opt_offset = i; 690228072Sbapt } 691228072Sbapt } 692228072Sbapt } 693228072Sbapt 694228072Sbapt if (nmatch == 0) { 695228072Sbapt *err_code = SCANOPT_ERR_OPT_UNRECOGNIZED; 696228072Sbapt *opt_offset = -1; 697228072Sbapt } 698228072Sbapt else if (nmatch > 1) { 699228072Sbapt *err_code = SCANOPT_ERR_OPT_AMBIGUOUS; 700228072Sbapt *opt_offset = -1; 701228072Sbapt } 702228072Sbapt 703228072Sbapt return *err_code ? 0 : 1; 704228072Sbapt} 705228072Sbapt 706228072Sbapt 707228072Sbaptint scanopt (svoid, arg, optindex) 708228072Sbapt scanopt_t *svoid; 709228072Sbapt char **arg; 710228072Sbapt int *optindex; 711228072Sbapt{ 712228072Sbapt char *optname = NULL, *optarg = NULL, *pstart; 713228072Sbapt int namelen = 0, arglen = 0; 714228072Sbapt int errcode = 0, has_next; 715228072Sbapt const optspec_t *optp; 716228072Sbapt struct _scanopt_t *s; 717228072Sbapt struct _aux *auxp; 718228072Sbapt int is_short; 719228072Sbapt int opt_offset = -1; 720228072Sbapt 721228072Sbapt s = (struct _scanopt_t *) svoid; 722228072Sbapt 723228072Sbapt /* Normalize return-parameters. */ 724228072Sbapt SAFE_ASSIGN (arg, NULL); 725228072Sbapt SAFE_ASSIGN (optindex, s->index); 726228072Sbapt 727228072Sbapt if (s->index >= s->argc) 728228072Sbapt return 0; 729228072Sbapt 730228072Sbapt /* pstart always points to the start of our current scan. */ 731228072Sbapt pstart = s->argv[s->index] + s->subscript; 732228072Sbapt if (!pstart) 733228072Sbapt return 0; 734228072Sbapt 735228072Sbapt if (s->subscript == 0) { 736228072Sbapt 737228072Sbapt /* test for exact match of "--" */ 738228072Sbapt if (pstart[0] == '-' && pstart[1] == '-' && !pstart[2]) { 739228072Sbapt SAFE_ASSIGN (optindex, s->index + 1); 740228072Sbapt INC_INDEX (s, 1); 741228072Sbapt return 0; 742228072Sbapt } 743228072Sbapt 744228072Sbapt /* Match an opt. */ 745228072Sbapt if (matchlongopt 746228072Sbapt (pstart, &optname, &namelen, &optarg, &arglen)) { 747228072Sbapt 748228072Sbapt /* it LOOKS like an opt, but is it one?! */ 749228072Sbapt if (!find_opt 750228072Sbapt (s, 1, optname, namelen, &errcode, 751228072Sbapt &opt_offset)) { 752228072Sbapt scanopt_err (s, opt_offset, 0, errcode); 753228072Sbapt return errcode; 754228072Sbapt } 755228072Sbapt /* We handle this below. */ 756228072Sbapt is_short = 0; 757228072Sbapt 758228072Sbapt /* Check for short opt. */ 759228072Sbapt } 760228072Sbapt else if (pstart[0] == '-' && pstart[1]) { 761228072Sbapt /* Pass through to below. */ 762228072Sbapt is_short = 1; 763228072Sbapt s->subscript++; 764228072Sbapt pstart++; 765228072Sbapt } 766228072Sbapt 767228072Sbapt else { 768228072Sbapt /* It's not an option. We're done. */ 769228072Sbapt return 0; 770228072Sbapt } 771228072Sbapt } 772228072Sbapt 773228072Sbapt /* We have to re-check the subscript status because it 774228072Sbapt * may have changed above. */ 775228072Sbapt 776228072Sbapt if (s->subscript != 0) { 777228072Sbapt 778228072Sbapt /* we are somewhere in a run of short opts, 779228072Sbapt * e.g., at the 'z' in `tar -xzf` */ 780228072Sbapt 781228072Sbapt optname = pstart; 782228072Sbapt namelen = 1; 783228072Sbapt is_short = 1; 784228072Sbapt 785228072Sbapt if (!find_opt 786228072Sbapt (s, 0, pstart, namelen, &errcode, &opt_offset)) { 787228072Sbapt return scanopt_err (s, opt_offset, 1, errcode); 788228072Sbapt } 789228072Sbapt 790228072Sbapt optarg = pstart + 1; 791228072Sbapt if (!*optarg) { 792228072Sbapt optarg = NULL; 793228072Sbapt arglen = 0; 794228072Sbapt } 795228072Sbapt else 796228072Sbapt arglen = strlen (optarg); 797228072Sbapt } 798228072Sbapt 799228072Sbapt /* At this point, we have a long or short option matched at opt_offset into 800228072Sbapt * the s->options array (and corresponding aux array). 801228072Sbapt * A trailing argument is in {optarg,arglen}, if any. 802228072Sbapt */ 803228072Sbapt 804228072Sbapt /* Look ahead in argv[] to see if there is something 805228072Sbapt * that we can use as an argument (if needed). */ 806228072Sbapt has_next = s->index + 1 < s->argc 807228072Sbapt && strcmp ("--", s->argv[s->index + 1]) != 0; 808228072Sbapt 809228072Sbapt optp = s->options + opt_offset; 810228072Sbapt auxp = s->aux + opt_offset; 811228072Sbapt 812228072Sbapt /* case: no args allowed */ 813228072Sbapt if (auxp->flags & ARG_NONE) { 814228072Sbapt if (optarg && !is_short) { 815228072Sbapt scanopt_err (s, opt_offset, is_short, errcode = 816228072Sbapt SCANOPT_ERR_ARG_NOT_ALLOWED); 817228072Sbapt INC_INDEX (s, 1); 818228072Sbapt return errcode; 819228072Sbapt } 820228072Sbapt else if (!optarg) 821228072Sbapt INC_INDEX (s, 1); 822228072Sbapt else 823228072Sbapt s->subscript++; 824228072Sbapt return optp->r_val; 825228072Sbapt } 826228072Sbapt 827228072Sbapt /* case: required */ 828228072Sbapt if (auxp->flags & ARG_REQ) { 829228072Sbapt if (!optarg && !has_next) 830228072Sbapt return scanopt_err (s, opt_offset, is_short, 831228072Sbapt SCANOPT_ERR_ARG_NOT_FOUND); 832228072Sbapt 833228072Sbapt if (!optarg) { 834228072Sbapt /* Let the next argv element become the argument. */ 835228072Sbapt SAFE_ASSIGN (arg, s->argv[s->index + 1]); 836228072Sbapt INC_INDEX (s, 2); 837228072Sbapt } 838228072Sbapt else { 839228072Sbapt SAFE_ASSIGN (arg, (char *) optarg); 840228072Sbapt INC_INDEX (s, 1); 841228072Sbapt } 842228072Sbapt return optp->r_val; 843228072Sbapt } 844228072Sbapt 845228072Sbapt /* case: optional */ 846228072Sbapt if (auxp->flags & ARG_OPT) { 847228072Sbapt SAFE_ASSIGN (arg, optarg); 848228072Sbapt INC_INDEX (s, 1); 849228072Sbapt return optp->r_val; 850228072Sbapt } 851228072Sbapt 852228072Sbapt 853228072Sbapt /* Should not reach here. */ 854228072Sbapt return 0; 855228072Sbapt} 856228072Sbapt 857228072Sbapt 858228072Sbaptint scanopt_destroy (svoid) 859228072Sbapt scanopt_t *svoid; 860228072Sbapt{ 861228072Sbapt struct _scanopt_t *s; 862228072Sbapt 863228072Sbapt s = (struct _scanopt_t *) svoid; 864228072Sbapt if (s) { 865228072Sbapt if (s->aux) 866228072Sbapt free (s->aux); 867228072Sbapt free (s); 868228072Sbapt } 869228072Sbapt return 0; 870228072Sbapt} 871228072Sbapt 872228072Sbapt 873228072Sbapt/* vim:set tabstop=8 softtabstop=4 shiftwidth=4: */ 874