1*2775Sraf /* 2*2775Sraf * CDDL HEADER START 3*2775Sraf * 4*2775Sraf * The contents of this file are subject to the terms of the 5*2775Sraf * Common Development and Distribution License, Version 1.0 only 6*2775Sraf * (the "License"). You may not use this file except in compliance 7*2775Sraf * with the License. 8*2775Sraf * 9*2775Sraf * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*2775Sraf * or http://www.opensolaris.org/os/licensing. 11*2775Sraf * See the License for the specific language governing permissions 12*2775Sraf * and limitations under the License. 13*2775Sraf * 14*2775Sraf * When distributing Covered Code, include this CDDL HEADER in each 15*2775Sraf * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*2775Sraf * If applicable, add the following below this CDDL HEADER, with the 17*2775Sraf * fields enclosed by brackets "[]" replaced with your own identifying 18*2775Sraf * information: Portions Copyright [yyyy] [name of copyright owner] 19*2775Sraf * 20*2775Sraf * CDDL HEADER END 21*2775Sraf */ 22*2775Sraf /* 23*2775Sraf * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24*2775Sraf * Use is subject to license terms. 25*2775Sraf */ 26*2775Sraf 27*2775Sraf #pragma ident "%Z%%M% %I% %E% SMI" 28*2775Sraf 29*2775Sraf #include <stdio.h> 30*2775Sraf #include <ctype.h> 31*2775Sraf #include <stdlib.h> 32*2775Sraf #include <unistd.h> 33*2775Sraf #include <string.h> 34*2775Sraf #include <dlfcn.h> 35*2775Sraf #include <dirent.h> 36*2775Sraf #include <libgen.h> 37*2775Sraf #include <sys/param.h> 38*2775Sraf #include <errno.h> 39*2775Sraf 40*2775Sraf #include "parser.h" 41*2775Sraf #include "errlog.h" 42*2775Sraf 43*2775Sraf static char const *ARCH_I386 = "i386"; 44*2775Sraf static char const *ARCH_SPARC = "sparc"; 45*2775Sraf static char const *ARCH_SPARCV9 = "sparcv9"; 46*2775Sraf static char const *ARCH_IA64 = "ia64"; 47*2775Sraf static char const *ARCH_AMD64 = "amd64"; 48*2775Sraf static char const *ARCH_ALL = "all"; 49*2775Sraf 50*2775Sraf static int dofiles(const Translator_info *); 51*2775Sraf static int read_spec(const Translator_info *, char *); 52*2775Sraf 53*2775Sraf static int Curlineno; 54*2775Sraf 55*2775Sraf xlator_keyword_t *keywordlist; 56*2775Sraf 57*2775Sraf /* 58*2775Sraf * frontend entry point 59*2775Sraf * returns the number of errors encountered 60*2775Sraf */ 61*2775Sraf int 62*2775Sraf frontend(const Translator_info *T_info) 63*2775Sraf { 64*2775Sraf int retval, i = 0, errors = 0; 65*2775Sraf 66*2775Sraf keywordlist = xlator_init(T_info); 67*2775Sraf if (keywordlist == NULL) { 68*2775Sraf errlog(ERROR, "Error: Unable to get keywordlist\n"); 69*2775Sraf return (1); 70*2775Sraf } 71*2775Sraf 72*2775Sraf if (T_info->ti_verbosity >= STATUS) { 73*2775Sraf errlog(STATUS, "interesting keywords:\n"); 74*2775Sraf while (keywordlist[i].key != NULL) { 75*2775Sraf errlog(STATUS, "\t%s\n", keywordlist[i].key); 76*2775Sraf ++i; 77*2775Sraf }; 78*2775Sraf } 79*2775Sraf 80*2775Sraf retval = xlator_startlib(T_info->ti_liblist); 81*2775Sraf switch (retval) { 82*2775Sraf case XLATOR_SKIP: 83*2775Sraf if (T_info->ti_verbosity >= STATUS) 84*2775Sraf errlog(STATUS, "Skipping %s\n", T_info->ti_liblist); 85*2775Sraf retval = 0; 86*2775Sraf break; 87*2775Sraf 88*2775Sraf case XLATOR_NONFATAL: 89*2775Sraf ++errors; 90*2775Sraf retval = 0; 91*2775Sraf break; 92*2775Sraf 93*2775Sraf case XLATOR_SUCCESS: 94*2775Sraf retval = dofiles(T_info); 95*2775Sraf errors += retval; 96*2775Sraf if ((retval = xlator_endlib()) != XLATOR_SUCCESS) 97*2775Sraf ++errors; 98*2775Sraf retval = 0; 99*2775Sraf break; 100*2775Sraf 101*2775Sraf default: 102*2775Sraf errlog(ERROR | FATAL, 103*2775Sraf "Error: Invalid return code from xlator_startlib()\n"); 104*2775Sraf exit(1); 105*2775Sraf } 106*2775Sraf 107*2775Sraf if ((retval = xlator_end()) != XLATOR_SUCCESS) 108*2775Sraf ++errors; 109*2775Sraf 110*2775Sraf return (errors); 111*2775Sraf } 112*2775Sraf 113*2775Sraf /* 114*2775Sraf * dofiles(const Translator_info *T_info); 115*2775Sraf * iterate through files specified in the command line and process 116*2775Sraf * them one by one 117*2775Sraf * requires spec files to have a ".spec" suffix 118*2775Sraf * returns the number of errors; 119*2775Sraf */ 120*2775Sraf static int 121*2775Sraf dofiles(const Translator_info *T_info) 122*2775Sraf { 123*2775Sraf int nfiles, flen, findex, retval = 0, errors = 0; 124*2775Sraf 125*2775Sraf nfiles = T_info->ti_nfiles; 126*2775Sraf 127*2775Sraf for (findex = 0; findex < nfiles; ++findex) { 128*2775Sraf flen = strlen(filelist[findex]); 129*2775Sraf if ((flen <= 5) || 130*2775Sraf strcmp(&filelist[findex][flen-5], ".spec") != 0) { 131*2775Sraf errlog(ERROR, 132*2775Sraf "Error: File specified does not have the " 133*2775Sraf ".spec extension: %s\n", filelist[findex]); 134*2775Sraf ++errors; 135*2775Sraf continue; 136*2775Sraf }; 137*2775Sraf retval = read_spec(T_info, filelist[findex]); 138*2775Sraf errors += retval; 139*2775Sraf } 140*2775Sraf return (errors); 141*2775Sraf } 142*2775Sraf 143*2775Sraf /* 144*2775Sraf * read_spec - 145*2775Sraf * Given a filename, this function will reads the spec file to 146*2775Sraf * recognize keywords which it passes along with the corresponding 147*2775Sraf * value to the back-end translator to process. The following 148*2775Sraf * back-end interfaces are called: 149*2775Sraf * xlator_startfile 150*2775Sraf * xlator_start_if 151*2775Sraf * xlator_take_kvpair 152*2775Sraf * xlator_end_if 153*2775Sraf * xlator_endfile 154*2775Sraf */ 155*2775Sraf static int 156*2775Sraf read_spec(const Translator_info *T_info, char *spec_filename) 157*2775Sraf { 158*2775Sraf FILE *spec_fp; 159*2775Sraf Meta_info meta_info; 160*2775Sraf char key[BUFSIZ], *value = NULL, *p = NULL; 161*2775Sraf char *buf2 = NULL; 162*2775Sraf int retval = 0, errors = 0, ki = 0; /* keyword indicator */ 163*2775Sraf int start_if_fail = 0, skip_if = 0; 164*2775Sraf int extends_err = 0; 165*2775Sraf 166*2775Sraf meta_info.mi_ext_cnt = 0; /* All info is non-extends */ 167*2775Sraf meta_info.mi_flags = 0; 168*2775Sraf 169*2775Sraf retval = xlator_startfile(spec_filename); 170*2775Sraf 171*2775Sraf switch (retval) { 172*2775Sraf case XLATOR_SKIP: 173*2775Sraf if (T_info->ti_verbosity >= WARNING) 174*2775Sraf errlog(WARNING, "Warning: Skipping %s\n", 175*2775Sraf spec_filename); 176*2775Sraf return (errors); 177*2775Sraf 178*2775Sraf case XLATOR_NONFATAL: 179*2775Sraf errlog(ERROR, "Error in xlator_startfile\n"); 180*2775Sraf ++errors; 181*2775Sraf return (errors); 182*2775Sraf 183*2775Sraf case XLATOR_SUCCESS: 184*2775Sraf break; 185*2775Sraf 186*2775Sraf default: 187*2775Sraf errlog(ERROR, 188*2775Sraf "Error: Invalid return code from xlator_startfile()\n"); 189*2775Sraf ++errors; 190*2775Sraf return (errors); 191*2775Sraf }; 192*2775Sraf 193*2775Sraf /* file processing */ 194*2775Sraf spec_fp = fopen(spec_filename, "r"); 195*2775Sraf if (spec_fp == NULL) { 196*2775Sraf errlog(ERROR, "Error: Unable to open spec file %s: %s\n", 197*2775Sraf spec_filename, strerror(errno)); 198*2775Sraf ++errors; 199*2775Sraf return (errors); 200*2775Sraf } 201*2775Sraf 202*2775Sraf (void) strncpy(meta_info.mi_filename, spec_filename, BUFSIZ); 203*2775Sraf meta_info.mi_line_number = 0; 204*2775Sraf Curlineno = meta_info.mi_line_number; 205*2775Sraf while (meta_info.mi_nlines = readline(&buf2, spec_fp)) { 206*2775Sraf meta_info.mi_line_number += meta_info.mi_nlines; 207*2775Sraf Curlineno = meta_info.mi_line_number; 208*2775Sraf if (!non_empty(buf2)) { 209*2775Sraf free(buf2); 210*2775Sraf buf2 = NULL; 211*2775Sraf continue; 212*2775Sraf } 213*2775Sraf p = realloc(value, sizeof (char)*(strlen(buf2)+1)); 214*2775Sraf if (p == NULL) { 215*2775Sraf errlog(ERROR | FATAL, 216*2775Sraf "Error: Unable to allocate memory for " 217*2775Sraf "value: %d\n", errno); 218*2775Sraf } 219*2775Sraf value = p; 220*2775Sraf split(buf2, key, value); 221*2775Sraf ki = interesting_keyword(keywordlist, key); 222*2775Sraf switch (ki) { 223*2775Sraf case XLATOR_KW_FUNC: /* Function keyword */ 224*2775Sraf case XLATOR_KW_DATA: /* Data keyword */ 225*2775Sraf meta_info.mi_extended = 0; 226*2775Sraf retval = xlator_start_if(meta_info, ki, value); 227*2775Sraf switch (retval) { 228*2775Sraf case XLATOR_FATAL: /* FATAL ERROR */ 229*2775Sraf if (T_info->ti_verbosity >= STATUS) { 230*2775Sraf errlog(STATUS, 231*2775Sraf "Error in xlator_start_if: "); 232*2775Sraf } 233*2775Sraf ++errors; 234*2775Sraf return (errors); 235*2775Sraf case XLATOR_NONFATAL: /* NON-FATAL ERROR */ 236*2775Sraf if (T_info->ti_verbosity >= STATUS) 237*2775Sraf errlog(STATUS, 238*2775Sraf "Error in xlator_start_if\n"); 239*2775Sraf ++errors; 240*2775Sraf start_if_fail = 1; 241*2775Sraf break; 242*2775Sraf case XLATOR_SUCCESS: /* OK */ 243*2775Sraf start_if_fail = 0; 244*2775Sraf extends_err = check4extends(spec_filename, 245*2775Sraf value, T_info->ti_archtoken, spec_fp); 246*2775Sraf switch (extends_err) { 247*2775Sraf case -1: /* Error */ 248*2775Sraf errlog(ERROR, "\"%s\", line %d: " 249*2775Sraf "Error occurred while " 250*2775Sraf "checking for extends clause\n", 251*2775Sraf spec_filename, Curlineno); 252*2775Sraf ++errors; 253*2775Sraf /*FALLTHRU*/ 254*2775Sraf case 0: /* No Extends */ 255*2775Sraf break; 256*2775Sraf case 1: /* Extends */ 257*2775Sraf meta_info.mi_extended = 1; 258*2775Sraf extends_err = do_extends(meta_info, 259*2775Sraf T_info, value); 260*2775Sraf if (extends_err) { 261*2775Sraf errors += extends_err; 262*2775Sraf } 263*2775Sraf break; 264*2775Sraf default: /* Programmer Error */ 265*2775Sraf errlog(ERROR | FATAL, 266*2775Sraf "Error: invalid return from " 267*2775Sraf "check4extends %d\n", extends_err); 268*2775Sraf } 269*2775Sraf break; 270*2775Sraf case XLATOR_SKIP: /* SKIP */ 271*2775Sraf if (T_info->ti_verbosity >= WARNING) 272*2775Sraf errlog(WARNING, "Warning: Skipping " 273*2775Sraf "interface %s\n", value); 274*2775Sraf skip_if = 1; 275*2775Sraf start_if_fail = 0; 276*2775Sraf break; 277*2775Sraf default: 278*2775Sraf /* Invalid Return */ 279*2775Sraf errlog(ERROR | FATAL, 280*2775Sraf "Error: Invalid return code " 281*2775Sraf "from xlator_start_if (): %d\n", retval); 282*2775Sraf } 283*2775Sraf break; 284*2775Sraf case XLATOR_KW_END: /* END keyword */ 285*2775Sraf if (start_if_fail == 0 && skip_if == 0) { 286*2775Sraf retval = xlator_end_if(meta_info, value); 287*2775Sraf if (retval) 288*2775Sraf ++errors; 289*2775Sraf } 290*2775Sraf skip_if = 0; 291*2775Sraf break; 292*2775Sraf case XLATOR_KW_NOTFOUND: 293*2775Sraf if (T_info->ti_verbosity >= TRACING) 294*2775Sraf errlog(TRACING, "uninteresting keyword: %s\n", 295*2775Sraf key); 296*2775Sraf break; 297*2775Sraf default: 298*2775Sraf if (skip_if == 0 && start_if_fail == 0) { 299*2775Sraf retval = xlator_take_kvpair(meta_info, 300*2775Sraf ki, value); 301*2775Sraf if (retval) { 302*2775Sraf if (T_info->ti_verbosity >= STATUS) 303*2775Sraf errlog(STATUS, "Error in " 304*2775Sraf "xlator_take_kvpair\n"); 305*2775Sraf ++errors; 306*2775Sraf } 307*2775Sraf } 308*2775Sraf } 309*2775Sraf free(buf2); 310*2775Sraf buf2 = NULL; 311*2775Sraf } 312*2775Sraf 313*2775Sraf if ((retval = xlator_endfile()) != XLATOR_SUCCESS) { 314*2775Sraf if (T_info->ti_verbosity >= STATUS) 315*2775Sraf errlog(STATUS, "Error in xlator_endfile\n"); 316*2775Sraf ++errors; 317*2775Sraf } 318*2775Sraf free(p); 319*2775Sraf (void) fclose(spec_fp); 320*2775Sraf return (errors); 321*2775Sraf } 322*2775Sraf 323*2775Sraf /* 324*2775Sraf * interesting_keyword(char **keywordlist, const char *key) { 325*2775Sraf * returns the token associated with key if key is found in keywordlist 326*2775Sraf * returns XLATOR_KW_NOTFOUND if key is NOT found in keywordlist 327*2775Sraf * "Function" and "End" are always interesting, return XLATOR_KW_FUNC 328*2775Sraf * and XLATOR_KW_DATA respectively; 329*2775Sraf * "End" is always interesting, return XLATOR_KW_END; 330*2775Sraf * 331*2775Sraf */ 332*2775Sraf int 333*2775Sraf interesting_keyword(xlator_keyword_t *keywordlist, const char *key) 334*2775Sraf { 335*2775Sraf int i = 0; 336*2775Sraf 337*2775Sraf if (strcasecmp(key, "data") == 0) { 338*2775Sraf return (XLATOR_KW_DATA); 339*2775Sraf } 340*2775Sraf if (strcasecmp(key, "function") == 0) { 341*2775Sraf return (XLATOR_KW_FUNC); 342*2775Sraf } 343*2775Sraf 344*2775Sraf if (strcasecmp(key, "end") == 0) 345*2775Sraf return (XLATOR_KW_END); 346*2775Sraf 347*2775Sraf while (keywordlist[i].key != NULL) { 348*2775Sraf if (strcasecmp(keywordlist[i].key, key) == 0) 349*2775Sraf return (keywordlist[i].token); 350*2775Sraf ++i; 351*2775Sraf } 352*2775Sraf return (XLATOR_KW_NOTFOUND); 353*2775Sraf } 354*2775Sraf 355*2775Sraf /* 356*2775Sraf * line_to_buf(char *dest, const char *src) { 357*2775Sraf * appends src to dest, dynamically increasing the size of dest. 358*2775Sraf * replaces the trailing '\' continuation character with a space. 359*2775Sraf * 360*2775Sraf * if src is continuation of dest, dest != NULL, and 361*2775Sraf * the last character in dest before the newline must be a `\' 362*2775Sraf * if src is not continuation of dest, then dest must be NULL 363*2775Sraf */ 364*2775Sraf char * 365*2775Sraf line_to_buf(char *dest, const char *src) 366*2775Sraf { 367*2775Sraf int slen = strlen(src); 368*2775Sraf int dlen; 369*2775Sraf 370*2775Sraf if (dest == NULL) { 371*2775Sraf /* We're being called for the first time */ 372*2775Sraf dest = malloc(sizeof (char) * (slen + 1)); 373*2775Sraf if (dest == NULL) { 374*2775Sraf errlog(ERROR | FATAL, 375*2775Sraf "Error: Unable to allocate memory for dest\n"); 376*2775Sraf } 377*2775Sraf (void) strcpy(dest, src); 378*2775Sraf return (dest); 379*2775Sraf } 380*2775Sraf 381*2775Sraf dlen = strlen(dest); 382*2775Sraf 383*2775Sraf dest = realloc(dest, (size_t)(sizeof (char) * (dlen+slen+1))); 384*2775Sraf if (dest == NULL) { 385*2775Sraf errlog(ERROR | FATAL, 386*2775Sraf "Error: Unable to allocate memory for dest\n"); 387*2775Sraf } 388*2775Sraf 389*2775Sraf if (dlen > 1) { 390*2775Sraf /* 391*2775Sraf * remove continuation character 392*2775Sraf * we replace the '\' from the previous line with a space 393*2775Sraf */ 394*2775Sraf if (dest[dlen-2] == '\\') { 395*2775Sraf dest[dlen-2] = ' '; 396*2775Sraf } 397*2775Sraf } 398*2775Sraf 399*2775Sraf /* join the two strings */ 400*2775Sraf (void) strcat(dest, src); 401*2775Sraf 402*2775Sraf return (dest); 403*2775Sraf } 404*2775Sraf 405*2775Sraf /* 406*2775Sraf * non_empty(const char *str) 407*2775Sraf * assumes str is non null 408*2775Sraf * checks if str is a non empty string 409*2775Sraf * returns 1 if string contains non whitespace 410*2775Sraf * returns 0 if string contains only whitespace 411*2775Sraf */ 412*2775Sraf int 413*2775Sraf non_empty(const char *str) 414*2775Sraf { 415*2775Sraf while (*str != '\0') { 416*2775Sraf if (!isspace(*str)) 417*2775Sraf return (1); 418*2775Sraf ++str; 419*2775Sraf }; 420*2775Sraf return (0); 421*2775Sraf } 422*2775Sraf 423*2775Sraf /* 424*2775Sraf * split(const char *line, char *key, char *value); 425*2775Sraf * splits the line into keyword (key) and value pair 426*2775Sraf */ 427*2775Sraf void 428*2775Sraf split(const char *line, char *key, char *value) 429*2775Sraf { 430*2775Sraf char *p; 431*2775Sraf 432*2775Sraf p = (char *)line; 433*2775Sraf 434*2775Sraf /* skip leading whitespace */ 435*2775Sraf while (isspace(*p)&& *p != '\0') 436*2775Sraf ++p; 437*2775Sraf 438*2775Sraf /* copy keyword from line into key */ 439*2775Sraf while (!isspace(*p) && *p != '\0') 440*2775Sraf *key++ = *p++; 441*2775Sraf 442*2775Sraf *key = '\0'; 443*2775Sraf 444*2775Sraf /* skip whitespace */ 445*2775Sraf while (isspace(*p) && *p != '\0') 446*2775Sraf p++; 447*2775Sraf 448*2775Sraf (void) strcpy(value, p); 449*2775Sraf 450*2775Sraf } 451*2775Sraf 452*2775Sraf /* 453*2775Sraf * check4extends(char *filename, char *value, int arch, FILE *fp) 454*2775Sraf * if no arch keyword is found or there is a MATCHING arch keyword 455*2775Sraf * returns 1 if value is of the form "data|function name extends" 456*2775Sraf * -1 for error 457*2775Sraf * 0 no other keyword after the function name 458*2775Sraf * else 459*2775Sraf * return 0 460*2775Sraf * 461*2775Sraf * filename is used only for error reporting 462*2775Sraf */ 463*2775Sraf int 464*2775Sraf check4extends(const char *filename, const char *value, int arch, FILE *fp) 465*2775Sraf { 466*2775Sraf char fun[BUFSIZ]; 467*2775Sraf char extends[BUFSIZ]; 468*2775Sraf int n; 469*2775Sraf 470*2775Sraf if (arch_match(fp, arch)) { 471*2775Sraf split(value, fun, extends); 472*2775Sraf n = strlen(extends); 473*2775Sraf if (extends[n-1] == '\n') 474*2775Sraf extends[n-1] = '\0'; 475*2775Sraf if (strncasecmp("extends", extends, 7) == 0) { 476*2775Sraf return (1); 477*2775Sraf } else { 478*2775Sraf if (*extends != '\0') { 479*2775Sraf errlog(ERROR, "\"%s\", line %d: Error: " 480*2775Sraf "Trailing garbage after function name\n", 481*2775Sraf filename, Curlineno); 482*2775Sraf return (-1); 483*2775Sraf } 484*2775Sraf } 485*2775Sraf } 486*2775Sraf return (0); 487*2775Sraf } 488*2775Sraf 489*2775Sraf /* 490*2775Sraf * remcomment (char *buf) 491*2775Sraf * replace comments with single whitespace 492*2775Sraf */ 493*2775Sraf /* XXX: There is currently no way to escape a comment character */ 494*2775Sraf void 495*2775Sraf remcomment(char const *buf) 496*2775Sraf { 497*2775Sraf char *p; 498*2775Sraf p = strchr(buf, '#'); 499*2775Sraf if (p) { 500*2775Sraf *p = ' '; 501*2775Sraf *(p+1) = '\0'; 502*2775Sraf } 503*2775Sraf } 504*2775Sraf 505*2775Sraf /* 506*2775Sraf * arch_strtoi() 507*2775Sraf * 508*2775Sraf * input: string 509*2775Sraf * return: XLATOR_I386 if string == ARCH_I386 510*2775Sraf * XLATOR_SPARC if string == ARCH_SPARC 511*2775Sraf * XLATOR_SPARCV9 if string == ARCH_SPARCV9 512*2775Sraf * XLATOR_IA64 if string == ARCH_IA64 513*2775Sraf * XLATOR_AMD64 if string == ARCH_AMD64 514*2775Sraf * XLATOR_ALLARCH if string == ARCH_ALL 515*2775Sraf * 0 if outside the known set {i386, sparc, sparcv9, ia64, amd64}. 516*2775Sraf */ 517*2775Sraf int 518*2775Sraf arch_strtoi(const char *arch_str) 519*2775Sraf { 520*2775Sraf if (arch_str != NULL) { 521*2775Sraf if (strcmp(arch_str, ARCH_I386) == 0) 522*2775Sraf return (XLATOR_I386); 523*2775Sraf else if (strcmp(arch_str, ARCH_SPARC) == 0) 524*2775Sraf return (XLATOR_SPARC); 525*2775Sraf else if (strcmp(arch_str, ARCH_SPARCV9) == 0) 526*2775Sraf return (XLATOR_SPARCV9); 527*2775Sraf else if (strcmp(arch_str, ARCH_IA64) == 0) 528*2775Sraf return (XLATOR_IA64); 529*2775Sraf else if (strcmp(arch_str, ARCH_AMD64) == 0) 530*2775Sraf return (XLATOR_AMD64); 531*2775Sraf else if (strcmp(arch_str, ARCH_ALL) == 0) 532*2775Sraf return (XLATOR_ALLARCH); 533*2775Sraf } else { 534*2775Sraf errlog(ERROR, "\"%s\", line %d: Error: " 535*2775Sraf "arch keyword with no value"); 536*2775Sraf } 537*2775Sraf return (0); 538*2775Sraf } 539*2775Sraf 540*2775Sraf int 541*2775Sraf readline(char **buffer, FILE *fp) 542*2775Sraf { 543*2775Sraf int nlines = 0; 544*2775Sraf int len; 545*2775Sraf char buf[BUFSIZ]; 546*2775Sraf 547*2775Sraf if (fgets(buf, BUFSIZ, fp)) { 548*2775Sraf nlines++; 549*2775Sraf /* replace comments with single whitespace */ 550*2775Sraf remcomment(buf); 551*2775Sraf 552*2775Sraf /* get complete line */ 553*2775Sraf *buffer = line_to_buf(*buffer, buf); /* append buf to buffer */ 554*2775Sraf len = strlen(buf); 555*2775Sraf if (len > 1) { 556*2775Sraf /* handle continuation lines */ 557*2775Sraf while (buf[len-2] == '\\') { 558*2775Sraf if (!fgets(buf, BUFSIZ, fp)) { 559*2775Sraf *buffer = line_to_buf(*buffer, buf); 560*2775Sraf break; 561*2775Sraf } 562*2775Sraf nlines++; 563*2775Sraf len = strlen(buf); 564*2775Sraf *buffer = line_to_buf(*buffer, buf); 565*2775Sraf } 566*2775Sraf } /* end of 'get complete line' */ 567*2775Sraf } 568*2775Sraf return (nlines); 569*2775Sraf } 570