1d0e51869Samw /* 2d0e51869Samw * CDDL HEADER START 3d0e51869Samw * 4d0e51869Samw * The contents of this file are subject to the terms of the 5d0e51869Samw * Common Development and Distribution License (the "License"). 6d0e51869Samw * You may not use this file except in compliance with the License. 7d0e51869Samw * 8d0e51869Samw * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9d0e51869Samw * or http://www.opensolaris.org/os/licensing. 10d0e51869Samw * See the License for the specific language governing permissions 11d0e51869Samw * and limitations under the License. 12d0e51869Samw * 13d0e51869Samw * When distributing Covered Code, include this CDDL HEADER in each 14d0e51869Samw * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15d0e51869Samw * If applicable, add the following below this CDDL HEADER, with the 16d0e51869Samw * fields enclosed by brackets "[]" replaced with your own identifying 17d0e51869Samw * information: Portions Copyright [yyyy] [name of copyright owner] 18d0e51869Samw * 19d0e51869Samw * CDDL HEADER END 20d0e51869Samw */ 21d0e51869Samw 22d0e51869Samw /* 23*a0b6e447SAlan Wright * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24d0e51869Samw * Use is subject to license terms. 25d0e51869Samw */ 26d0e51869Samw 27d0e51869Samw #include <errno.h> 28d0e51869Samw #include <stdarg.h> 29d0e51869Samw #include "ndrgen.h" 30d0e51869Samw #include "y.tab.h" 31d0e51869Samw 32d0e51869Samw /* 33d0e51869Samw * C-like lexical analysis. 34d0e51869Samw * 35d0e51869Samw * 1. Define a "struct node" 36d0e51869Samw * 2. Define a "struct symbol" that encapsulates a struct node. 37d0e51869Samw * 3. Define a "struct integer" that encapsulates a struct node. 38d0e51869Samw * 4. Set the YACC stack type in the grammar: 39d0e51869Samw * %{ 40d0e51869Samw * #define YYSTYPE struct node * 41d0e51869Samw * %} 42d0e51869Samw * 5. Define %token's in the grammer for IDENTIFIER, STRING and INTEGER. 43d0e51869Samw * Using "_KW" as a suffix for keyword tokens, i.e. "struct" is 44d0e51869Samw * "%token STRUCT_KW": 45d0e51869Samw * // atomic values 46d0e51869Samw * %token INTEGER STRING IDENTIFIER 47d0e51869Samw * // keywords 48d0e51869Samw * %token STRUCT_KW CASE_KW 49d0e51869Samw * // operators 50d0e51869Samw * %token PLUS MINUS ASSIGN ARROW 51d0e51869Samw * // overloaded tokens (++ --, < > <= >=, == !=, += -= *= ...) 52d0e51869Samw * %token INCOP RELOP EQUOP ASSOP 53d0e51869Samw * 6. It's easiest to use the yacc(1) generated token numbers for node 54d0e51869Samw * labels. For node labels that are not actually part of the grammer, 55d0e51869Samw * use a %token with an L_ prefix: 56d0e51869Samw * // node labels (can't be generated by lex) 57d0e51869Samw * %token L_LT L_LTE L_GT L_GTE L_EQU L_NEQ 58d0e51869Samw * 7. Call set_lex_input() before parsing. 59d0e51869Samw */ 60d0e51869Samw 61d0e51869Samw #define SQ '\'' 62d0e51869Samw #define DQ '"' 63d0e51869Samw 64d0e51869Samw #define isquote(c) ((c) == SQ || (c) == DQ) 65d0e51869Samw #define iswhite(c) ((c) == ' ' || (c) == '\t' || (c) == '\n' || (c) == '\f') 66d0e51869Samw 67d0e51869Samw #define is_between(c, l, u) ((l) <= (c) && (c) <= (u)) 68d0e51869Samw #define is_white(c) ((c) == ' ' || c == '\r' || c == '\t' || c == '\f') 69d0e51869Samw #define is_lower(c) is_between((c), 'a', 'z') 70d0e51869Samw #define is_upper(c) is_between((c), 'A', 'Z') 71d0e51869Samw #define is_alpha(c) (is_lower(c) || is_upper(c)) 72d0e51869Samw #define is_digit(c) is_between((c), '0', '9') 73d0e51869Samw #define is_sstart(c) (is_alpha(c) || (c) == '_') 74d0e51869Samw #define is_sfollow(c) (is_sstart(c) || is_digit(c)) 75d0e51869Samw #define is_xdigit(c) \ 76d0e51869Samw (is_digit(c) || is_between((c), 'A', 'F') || is_between((c), 'a', 'f')) 77d0e51869Samw 78d0e51869Samw ndr_symbol_t *symbol_list; 79d0e51869Samw static ndr_integer_t *integer_list; 80d0e51869Samw static FILE *lex_infp; 81d0e51869Samw static ndr_symbol_t *file_name; 82d0e51869Samw int line_number; 83d0e51869Samw int n_compile_error; 84d0e51869Samw 85d0e51869Samw static int lex_at_bol; 86d0e51869Samw 87d0e51869Samw /* In yacc(1) generated parser */ 88d0e51869Samw extern struct node *yylval; 89d0e51869Samw 90d0e51869Samw /* 91d0e51869Samw * The keywtab[] and optable[] could be external to this lex 92d0e51869Samw * and it would all still work. 93d0e51869Samw */ 94d0e51869Samw static ndr_keyword_t keywtable[] = { 95d0e51869Samw { "struct", STRUCT_KW, 0 }, 96d0e51869Samw { "union", UNION_KW, 0 }, 97d0e51869Samw { "typedef", TYPEDEF_KW, 0 }, 98d0e51869Samw 99d0e51869Samw { "interface", INTERFACE_KW, 0 }, 100d0e51869Samw { "uuid", UUID_KW, 0 }, 101d0e51869Samw { "_no_reorder", _NO_REORDER_KW, 0 }, 102d0e51869Samw { "extern", EXTERN_KW, 0 }, 103d0e51869Samw { "reference", REFERENCE_KW, 0 }, 104d0e51869Samw 105d0e51869Samw { "align", ALIGN_KW, 0 }, 106d0e51869Samw { "operation", OPERATION_KW, 0 }, 107d0e51869Samw { "in", IN_KW, 0 }, 108d0e51869Samw { "out", OUT_KW, 0 }, 109d0e51869Samw 110d0e51869Samw { "string", STRING_KW, 0 }, 111d0e51869Samw { "size_is", SIZE_IS_KW, 0 }, 112d0e51869Samw { "length_is", LENGTH_IS_KW, 0 }, 113d0e51869Samw 114d0e51869Samw { "switch_is", SWITCH_IS_KW, 0 }, 115d0e51869Samw { "case", CASE_KW, 0 }, 116d0e51869Samw { "default", DEFAULT_KW, 0 }, 117d0e51869Samw 118d0e51869Samw { "transmit_as", TRANSMIT_AS_KW, 0 }, 119d0e51869Samw { "arg_is", ARG_IS_KW, 0 }, 120d0e51869Samw 121d0e51869Samw { "char", BASIC_TYPE, 1 }, 122d0e51869Samw { "uchar", BASIC_TYPE, 1 }, 123d0e51869Samw { "wchar", BASIC_TYPE, 2 }, 124d0e51869Samw { "short", BASIC_TYPE, 2 }, 125d0e51869Samw { "ushort", BASIC_TYPE, 2 }, 126d0e51869Samw { "long", BASIC_TYPE, 4 }, 127d0e51869Samw { "ulong", BASIC_TYPE, 4 }, 128d0e51869Samw {0} 129d0e51869Samw }; 130d0e51869Samw 131d0e51869Samw static ndr_keyword_t optable[] = { 132d0e51869Samw { "{", LC, 0 }, 133d0e51869Samw { "}", RC, 0 }, 134d0e51869Samw { "(", LP, 0 }, 135d0e51869Samw { ")", RP, 0 }, 136d0e51869Samw { "[", LB, 0 }, 137d0e51869Samw { "]", RB, 0 }, 138d0e51869Samw { "*", STAR, 0 }, 139*a0b6e447SAlan Wright { "/", DIV, 0 }, 140*a0b6e447SAlan Wright { "%", MOD, 0 }, 141*a0b6e447SAlan Wright { "-", MINUS, 0 }, 142*a0b6e447SAlan Wright { "+", PLUS, 0 }, 143*a0b6e447SAlan Wright { "&", AND, 0 }, 144*a0b6e447SAlan Wright { "|", OR, 0 }, 145*a0b6e447SAlan Wright { "^", XOR, 0 }, 146d0e51869Samw { ";", SEMI, 0 }, 147d0e51869Samw {0} 148d0e51869Samw }; 149d0e51869Samw 150d0e51869Samw static int getch(FILE *fp); 151d0e51869Samw static ndr_integer_t *int_enter(long); 152*a0b6e447SAlan Wright static ndr_symbol_t *sym_enter(char *); 153d0e51869Samw static ndr_symbol_t *sym_find(char *); 154d0e51869Samw static int str_to_sv(char *, char *sv[]); 155d0e51869Samw 156d0e51869Samw /* 157d0e51869Samw * Enter the symbols for keyword. 158d0e51869Samw */ 159d0e51869Samw static void 160d0e51869Samw keyw_tab_init(ndr_keyword_t kwtable[]) 161d0e51869Samw { 162d0e51869Samw int i; 163d0e51869Samw ndr_keyword_t *kw; 164d0e51869Samw ndr_symbol_t *sym; 165d0e51869Samw 166d0e51869Samw for (i = 0; kwtable[i].name; i++) { 167d0e51869Samw kw = &kwtable[i]; 168d0e51869Samw 169d0e51869Samw sym = sym_enter(kw->name); 170d0e51869Samw sym->kw = kw; 171d0e51869Samw } 172d0e51869Samw } 173d0e51869Samw 174d0e51869Samw void 175d0e51869Samw set_lex_input(FILE *fp, char *name) 176d0e51869Samw { 177d0e51869Samw keyw_tab_init(keywtable); 178d0e51869Samw keyw_tab_init(optable); 179d0e51869Samw 180d0e51869Samw lex_infp = fp; 181d0e51869Samw file_name = sym_enter(name); 182d0e51869Samw line_number = 1; 183d0e51869Samw lex_at_bol = 1; 184d0e51869Samw } 185d0e51869Samw 186d0e51869Samw static int 187d0e51869Samw getch(FILE *fp) 188d0e51869Samw { 189d0e51869Samw return (getc(fp)); 190d0e51869Samw } 191d0e51869Samw 192d0e51869Samw int 193d0e51869Samw yylex(void) 194d0e51869Samw { 195d0e51869Samw char lexeme[512]; 196d0e51869Samw char *p = lexeme; 197d0e51869Samw FILE *fp = lex_infp; 198d0e51869Samw int c, xc; 199d0e51869Samw ndr_symbol_t *sym; 200d0e51869Samw ndr_integer_t *intg; 201d0e51869Samw 202d0e51869Samw top: 203d0e51869Samw p = lexeme; 204d0e51869Samw 205d0e51869Samw c = getch(fp); 206d0e51869Samw if (c == EOF) 207d0e51869Samw return (EOF); 208d0e51869Samw 209d0e51869Samw if (c == '\n') { 210d0e51869Samw line_number++; 211d0e51869Samw lex_at_bol = 1; 212d0e51869Samw goto top; 213d0e51869Samw } 214d0e51869Samw 215d0e51869Samw /* 216d0e51869Samw * Handle preprocessor lines. This just notes 217d0e51869Samw * which file we're processing. 218d0e51869Samw */ 219d0e51869Samw if (c == '#' && lex_at_bol) { 220d0e51869Samw char *sv[10]; 221d0e51869Samw int sc; 222d0e51869Samw 223d0e51869Samw while ((c = getch(fp)) != EOF && c != '\n') 224d0e51869Samw *p++ = c; 225d0e51869Samw 226d0e51869Samw *p = 0; 227d0e51869Samw /* note: no ungetc() of newline, we don't want to count it */ 228d0e51869Samw 229d0e51869Samw if (*lexeme != ' ') { 230d0e51869Samw /* not a line we know */ 231d0e51869Samw goto top; 232d0e51869Samw } 233d0e51869Samw 234d0e51869Samw sc = str_to_sv(lexeme, sv); 235d0e51869Samw if (sc < 2) 236d0e51869Samw goto top; 237d0e51869Samw 238d0e51869Samw file_name = sym_enter(sv[1]); 239d0e51869Samw line_number = atoi(sv[0]); /* for next input line */ 240d0e51869Samw lex_at_bol = 1; 241d0e51869Samw goto top; 242d0e51869Samw } 243d0e51869Samw 244d0e51869Samw lex_at_bol = 0; 245d0e51869Samw 246d0e51869Samw /* 247d0e51869Samw * Skip white space 248d0e51869Samw */ 249d0e51869Samw if (is_white(c)) 250d0e51869Samw goto top; 251d0e51869Samw 252d0e51869Samw /* 253d0e51869Samw * Symbol? Might be a keyword or just an identifier 254d0e51869Samw */ 255d0e51869Samw if (is_sstart(c)) { 256d0e51869Samw /* we got a symbol */ 257d0e51869Samw do { 258d0e51869Samw *p++ = c; 259d0e51869Samw c = getch(fp); 260d0e51869Samw } while (is_sfollow(c)); 261d0e51869Samw (void) ungetc(c, fp); 262d0e51869Samw *p = 0; 263d0e51869Samw 264d0e51869Samw sym = sym_enter(lexeme); 265d0e51869Samw 266d0e51869Samw yylval = &sym->s_node; 267d0e51869Samw 268d0e51869Samw if (sym->kw) { 269d0e51869Samw return (sym->kw->token); 270d0e51869Samw } else { 271d0e51869Samw return (IDENTIFIER); 272d0e51869Samw } 273d0e51869Samw } 274d0e51869Samw 275d0e51869Samw /* 276d0e51869Samw * Integer constant? 277d0e51869Samw */ 278d0e51869Samw if (is_digit(c)) { 279d0e51869Samw /* we got a number */ 280d0e51869Samw *p++ = c; 281d0e51869Samw if (c == '0') { 282d0e51869Samw c = getch(fp); 283d0e51869Samw if (c == 'x' || c == 'X') { 284d0e51869Samw /* handle hex specially */ 285d0e51869Samw do { 286d0e51869Samw *p++ = c; 287d0e51869Samw c = getch(fp); 288d0e51869Samw } while (is_xdigit(c)); 289d0e51869Samw goto convert_icon; 290d0e51869Samw } else if (c == 'b' || c == 'B' || 291d0e51869Samw c == 'd' || c == 'D' || 292d0e51869Samw c == 'o' || c == 'O') { 293d0e51869Samw do { 294d0e51869Samw *p++ = c; 295d0e51869Samw c = getch(fp); 296d0e51869Samw } while (is_digit(c)); 297d0e51869Samw goto convert_icon; 298d0e51869Samw } 299d0e51869Samw (void) ungetc(c, fp); 300d0e51869Samw } 301d0e51869Samw /* could be anything */ 302d0e51869Samw c = getch(fp); 303d0e51869Samw while (is_digit(c)) { 304d0e51869Samw *p++ = c; 305d0e51869Samw c = getch(fp); 306d0e51869Samw } 307d0e51869Samw 308d0e51869Samw convert_icon: 309d0e51869Samw *p = 0; 310d0e51869Samw (void) ungetc(c, fp); 311d0e51869Samw 312d0e51869Samw intg = int_enter(strtol(lexeme, 0, 0)); 313d0e51869Samw yylval = &intg->s_node; 314d0e51869Samw 315d0e51869Samw return (INTEGER); 316d0e51869Samw } 317d0e51869Samw 318d0e51869Samw /* Could handle strings. We don't seem to need them yet */ 319d0e51869Samw 320d0e51869Samw yylval = 0; /* operator tokens have no value */ 321d0e51869Samw xc = getch(fp); /* get look-ahead for two-char lexemes */ 322d0e51869Samw 323d0e51869Samw lexeme[0] = c; 324d0e51869Samw lexeme[1] = xc; 325d0e51869Samw lexeme[2] = 0; 326d0e51869Samw 327d0e51869Samw /* 328d0e51869Samw * Look for to-end-of-line comment 329d0e51869Samw */ 330d0e51869Samw if (c == '/' && xc == '/') { 331d0e51869Samw /* eat the comment */ 332d0e51869Samw while ((c = getch(fp)) != EOF && c != '\n') 333d0e51869Samw ; 334d0e51869Samw (void) ungetc(c, fp); /* put back newline */ 335d0e51869Samw goto top; 336d0e51869Samw } 337d0e51869Samw 338d0e51869Samw /* 339d0e51869Samw * Look for multi-line comment 340d0e51869Samw */ 341d0e51869Samw if (c == '/' && xc == '*') { 342d0e51869Samw /* eat the comment */ 343d0e51869Samw xc = -1; 344d0e51869Samw while ((c = getch(fp)) != EOF) { 345d0e51869Samw if (xc == '*' && c == '/') { 346d0e51869Samw /* that's it */ 347d0e51869Samw break; 348d0e51869Samw } 349d0e51869Samw xc = c; 350d0e51869Samw if (c == '\n') 351d0e51869Samw line_number++; 352d0e51869Samw } 353d0e51869Samw goto top; 354d0e51869Samw } 355d0e51869Samw 356d0e51869Samw /* 357d0e51869Samw * Use symbol table lookup for two-character and 358d0e51869Samw * one character operator tokens. 359d0e51869Samw */ 360d0e51869Samw sym = sym_find(lexeme); 361d0e51869Samw if (sym) { 362d0e51869Samw /* there better be a keyword attached */ 363d0e51869Samw yylval = &sym->s_node; 364d0e51869Samw return (sym->kw->token); 365d0e51869Samw } 366d0e51869Samw 367d0e51869Samw /* Try a one-character form */ 368d0e51869Samw (void) ungetc(xc, fp); 369d0e51869Samw lexeme[1] = 0; 370d0e51869Samw sym = sym_find(lexeme); 371d0e51869Samw if (sym) { 372d0e51869Samw /* there better be a keyword attached */ 373d0e51869Samw yylval = &sym->s_node; 374d0e51869Samw return (sym->kw->token); 375d0e51869Samw } 376d0e51869Samw 377*a0b6e447SAlan Wright if (is_between(c, ' ', '~')) 378*a0b6e447SAlan Wright compile_error("unrecognized character: 0x%02x (%c)", c, c); 379*a0b6e447SAlan Wright else 380*a0b6e447SAlan Wright compile_error("unrecognized character: 0x%02x", c); 381d0e51869Samw goto top; 382d0e51869Samw } 383d0e51869Samw 384d0e51869Samw static ndr_symbol_t * 385d0e51869Samw sym_find(char *name) 386d0e51869Samw { 387d0e51869Samw ndr_symbol_t **pp; 388d0e51869Samw ndr_symbol_t *p; 389d0e51869Samw 390d0e51869Samw for (pp = &symbol_list; (p = *pp) != 0; pp = &p->next) { 391d0e51869Samw if (strcmp(p->name, name) == 0) 392d0e51869Samw return (p); 393d0e51869Samw } 394d0e51869Samw 395d0e51869Samw return (0); 396d0e51869Samw } 397d0e51869Samw 398*a0b6e447SAlan Wright static ndr_symbol_t * 399d0e51869Samw sym_enter(char *name) 400d0e51869Samw { 401d0e51869Samw ndr_symbol_t **pp; 402d0e51869Samw ndr_symbol_t *p; 403d0e51869Samw 404d0e51869Samw for (pp = &symbol_list; (p = *pp) != 0; pp = &p->next) { 405d0e51869Samw if (strcmp(p->name, name) == 0) 406d0e51869Samw return (p); 407d0e51869Samw } 408d0e51869Samw 409d0e51869Samw p = ndr_alloc(1, sizeof (ndr_symbol_t)); 410d0e51869Samw 411d0e51869Samw if ((p->name = strdup(name)) == NULL) 412d0e51869Samw fatal_error("%s", strerror(ENOMEM)); 413d0e51869Samw 414d0e51869Samw p->s_node.label = IDENTIFIER; 415d0e51869Samw p->s_node.n_sym = p; 416d0e51869Samw 417d0e51869Samw *pp = p; 418d0e51869Samw 419d0e51869Samw return (p); 420d0e51869Samw } 421d0e51869Samw 422d0e51869Samw static ndr_integer_t * 423d0e51869Samw int_enter(long value) 424d0e51869Samw { 425d0e51869Samw ndr_integer_t **pp; 426d0e51869Samw ndr_integer_t *p; 427d0e51869Samw 428d0e51869Samw for (pp = &integer_list; (p = *pp) != 0; pp = &p->next) { 429d0e51869Samw if (p->value == value) 430d0e51869Samw return (p); 431d0e51869Samw } 432d0e51869Samw 433d0e51869Samw p = ndr_alloc(1, sizeof (ndr_integer_t)); 434d0e51869Samw 435d0e51869Samw p->value = value; 436d0e51869Samw p->s_node.label = INTEGER; 437d0e51869Samw p->s_node.n_int = value; 438d0e51869Samw 439d0e51869Samw *pp = p; 440d0e51869Samw 441d0e51869Samw return (p); 442d0e51869Samw } 443d0e51869Samw 444d0e51869Samw void * 445d0e51869Samw ndr_alloc(size_t nelem, size_t elsize) 446d0e51869Samw { 447d0e51869Samw void *p; 448d0e51869Samw 449d0e51869Samw if ((p = calloc(nelem, elsize)) == NULL) { 450d0e51869Samw fatal_error("%s", strerror(ENOMEM)); 451d0e51869Samw /* NOTREACHED */ 452d0e51869Samw } 453d0e51869Samw 454d0e51869Samw return (p); 455d0e51869Samw } 456d0e51869Samw 457d0e51869Samw /* 458d0e51869Samw * The input context (filename, line number) is maintained by the 459d0e51869Samw * lexical analysis, and we generally want such info reported for 460d0e51869Samw * errors in a consistent manner. 461d0e51869Samw */ 462d0e51869Samw void 463d0e51869Samw compile_error(const char *fmt, ...) 464d0e51869Samw { 465d0e51869Samw char buf[NDLBUFSZ]; 466d0e51869Samw va_list ap; 467d0e51869Samw 468d0e51869Samw va_start(ap, fmt); 469d0e51869Samw (void) vsnprintf(buf, NDLBUFSZ, fmt, ap); 470d0e51869Samw va_end(ap); 471d0e51869Samw 472d0e51869Samw (void) fprintf(stderr, "ndrgen: compile error: %s:%d: %s\n", 473d0e51869Samw file_name->name, line_number, buf); 474d0e51869Samw 475d0e51869Samw n_compile_error++; 476d0e51869Samw } 477d0e51869Samw 478d0e51869Samw void 479d0e51869Samw fatal_error(const char *fmt, ...) 480d0e51869Samw { 481d0e51869Samw char buf[NDLBUFSZ]; 482d0e51869Samw va_list ap; 483d0e51869Samw 484d0e51869Samw va_start(ap, fmt); 485d0e51869Samw (void) vsnprintf(buf, NDLBUFSZ, fmt, ap); 486d0e51869Samw va_end(ap); 487d0e51869Samw 488d0e51869Samw (void) fprintf(stderr, "ndrgen: fatal error: %s\n", buf); 489d0e51869Samw exit(1); 490d0e51869Samw } 491d0e51869Samw 492d0e51869Samw /* 493d0e51869Samw * Setup nodes for the lexical analyzer. 494d0e51869Samw */ 495d0e51869Samw struct node * 496d0e51869Samw n_cons(int label, ...) 497d0e51869Samw { 498d0e51869Samw ndr_node_t *np; 499d0e51869Samw va_list ap; 500d0e51869Samw 501d0e51869Samw np = ndr_alloc(1, sizeof (ndr_node_t)); 502d0e51869Samw 503d0e51869Samw va_start(ap, label); 504d0e51869Samw np->label = label; 505d0e51869Samw np->n_arg[0] = va_arg(ap, void *); 506d0e51869Samw np->n_arg[1] = va_arg(ap, void *); 507d0e51869Samw np->n_arg[2] = va_arg(ap, void *); 508d0e51869Samw va_end(ap); 509d0e51869Samw 510d0e51869Samw np->line_number = line_number; 511d0e51869Samw np->file_name = file_name; 512d0e51869Samw 513d0e51869Samw return (np); 514d0e51869Samw } 515d0e51869Samw 516d0e51869Samw /* 517d0e51869Samw * list: item 518d0e51869Samw * | list item ={ n_splice($1, $2); } 519d0e51869Samw * ; 520d0e51869Samw */ 521d0e51869Samw void 522d0e51869Samw n_splice(struct node *np1, struct node *np2) 523d0e51869Samw { 524d0e51869Samw while (np1->n_next) 525d0e51869Samw np1 = np1->n_next; 526d0e51869Samw 527d0e51869Samw np1->n_next = np2; 528d0e51869Samw } 529d0e51869Samw 530d0e51869Samw /* 531d0e51869Samw * Convert a string of words to a vector of strings. 532d0e51869Samw * Returns the number of words. 533d0e51869Samw */ 534d0e51869Samw static int 535d0e51869Samw str_to_sv(char *buf, char *sv[]) 536d0e51869Samw { 537d0e51869Samw char **pp = sv; 538d0e51869Samw char *p = buf; 539d0e51869Samw char *q = buf; 540d0e51869Samw int in_word = 0; 541d0e51869Samw int c; 542d0e51869Samw 543d0e51869Samw for (;;) { 544d0e51869Samw c = *p++; 545d0e51869Samw if (c == 0) 546d0e51869Samw break; 547d0e51869Samw 548d0e51869Samw if (!in_word) { 549d0e51869Samw if (iswhite(c)) 550d0e51869Samw continue; 551d0e51869Samw 552d0e51869Samw *pp++ = q; 553d0e51869Samw in_word = 1; 554d0e51869Samw } 555d0e51869Samw 556d0e51869Samw if (isquote(c)) { 557d0e51869Samw int qc = c; 558d0e51869Samw 559d0e51869Samw while (((c = *p++) != 0) && (c != qc)) 560d0e51869Samw *q++ = c; 561d0e51869Samw if (c == 0) 562d0e51869Samw break; 563d0e51869Samw } else if (iswhite(c)) { 564d0e51869Samw /* end of word */ 565d0e51869Samw *q++ = 0; 566d0e51869Samw in_word = 0; 567d0e51869Samw } else { 568d0e51869Samw /* still inside word */ 569d0e51869Samw *q++ = c; 570d0e51869Samw } 571d0e51869Samw } 572d0e51869Samw 573d0e51869Samw if (in_word) 574d0e51869Samw *q++ = 0; 575d0e51869Samw 576d0e51869Samw *pp = (char *)0; 577d0e51869Samw return (pp - sv); 578d0e51869Samw } 579