1 /* $OpenPackages$ */ 2 /* $OpenBSD: lowparse.c,v 1.17 2002/12/29 17:29:35 espie Exp $ */ 3 4 /* low-level parsing functions. */ 5 6 /* 7 * Copyright (c) 1999,2000 Marc Espie. 8 * 9 * Extensive code changes for the OpenBSD project. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD 24 * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 #include <assert.h> 34 #include <stddef.h> 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <string.h> 38 #include <unistd.h> 39 #include "config.h" 40 #include "defines.h" 41 #include "buf.h" 42 #include "lowparse.h" 43 #include "error.h" 44 #include "lst.h" 45 #include "memory.h" 46 47 /* XXX check whether we can free filenames at the end, for a proper 48 * definition of `end'. */ 49 50 #if 0 51 static LIST fileNames; /* file names to free at end */ 52 #endif 53 54 /* Input stream structure, file or string. */ 55 typedef struct { 56 const char *fname; /* Name of file */ 57 unsigned long lineno; /* Line number */ 58 FILE *F; /* Open stream, or NULL if pure string. */ 59 char *str; /* Input string, if F == NULL. */ 60 61 /* Line buffer. */ 62 char *ptr; /* Where we are. */ 63 char *end; /* Don't overdo it. */ 64 } IFile; 65 66 static IFile *current; /* IFile being parsed. */ 67 68 static LIST input_stack; /* Stack of IFiles waiting to be parsed 69 * (includes and loop reparses) */ 70 71 /* IFile ctors. 72 * 73 * obj = new_ifile(filename, filehandle); 74 * Create input object from filename, filehandle. */ 75 static IFile *new_ifile(const char *, FILE *); 76 /* obj = new_istring(str, filename, lineno); 77 * Create input object from str, filename, lineno. */ 78 static IFile *new_istring(char *, const char *, unsigned long); 79 /* free_ifile(obj); 80 * Discard consumed input object, closing streams, freeing memory. */ 81 static void free_ifile(IFile *); 82 83 84 /* Handling basic character reading. 85 * c = ParseReadc(); 86 * New character c from current input stream, or EOF at end of stream. */ 87 #define ParseReadc() current->ptr < current->end ? *current->ptr++ : newline() 88 /* len = newline(); 89 * Guts for ParseReadc. Grabs a new line off fgetln when we have 90 * consumed the current line and returns its length. Or EOF at end of 91 * stream. */ 92 static int newline(void); 93 /* c = skiptoendofline(); 94 * Skips to the end of the current line, returns either '\n' or EOF. */ 95 static int skiptoendofline(void); 96 97 98 /* Helper functions to handle basic parsing. */ 99 /* ParseFoldLF(buffer, firstchar); 100 * Grabs logical line into buffer, the first character has already been 101 * read into firstchar. */ 102 static void ParseFoldLF(Buffer, int); 103 104 /* firstchar = ParseSkipEmptyLines(buffer); 105 * Scans lines, skipping empty lines. May put some characters into 106 * buffer, returns the first character useful to continue parsing 107 * (e.g., not a backslash or a space. */ 108 static int ParseSkipEmptyLines(Buffer); 109 110 static IFile * 111 new_ifile(name, stream) 112 const char *name; 113 FILE *stream; 114 { 115 IFile *ifile; 116 #if 0 117 Lst_AtEnd(&fileNames, name); 118 #endif 119 120 ifile = emalloc(sizeof(*ifile)); 121 ifile->fname = name; 122 ifile->str = NULL; 123 /* Naturally enough, we start reading at line 0. */ 124 ifile->lineno = 0; 125 ifile->F = stream; 126 ifile->ptr = ifile->end = NULL; 127 return ifile; 128 } 129 130 static void 131 free_ifile(ifile) 132 IFile *ifile; 133 { 134 if (ifile->F && fileno(ifile->F) != STDIN_FILENO) 135 (void)fclose(ifile->F); 136 free(ifile->str); 137 /* Note we can't free the file names yet, as they are embedded in GN for 138 * error reports. */ 139 free(ifile); 140 } 141 142 static IFile * 143 new_istring(str, name, lineno) 144 char *str; 145 const char *name; 146 unsigned long lineno; 147 { 148 IFile *ifile; 149 150 ifile = emalloc(sizeof(*ifile)); 151 /* No malloc, name is always taken from an already existing ifile */ 152 ifile->fname = name; 153 ifile->F = NULL; 154 /* Strings are used in for loops, so we need to reset the line counter 155 * to an appropriate value. */ 156 ifile->lineno = lineno; 157 ifile->ptr = ifile->str = str; 158 ifile->end = str + strlen(str); 159 return ifile; 160 } 161 162 163 void 164 Parse_FromString(str, lineno) 165 char *str; 166 unsigned long lineno; 167 { 168 if (DEBUG(FOR)) 169 (void)fprintf(stderr, "%s\n----\n", str); 170 171 if (current != NULL) 172 Lst_Push(&input_stack, current); 173 current = new_istring(str, current->fname, lineno); 174 } 175 176 177 void 178 Parse_FromFile(name, stream) 179 const char *name; 180 FILE *stream; 181 { 182 if (current != NULL) 183 Lst_Push(&input_stack, current); 184 current = new_ifile(name, stream); 185 } 186 187 bool 188 Parse_NextFile() 189 { 190 if (current != NULL) 191 free_ifile(current); 192 current = (IFile *)Lst_Pop(&input_stack); 193 return current != NULL; 194 } 195 196 static int 197 newline() 198 { 199 size_t len; 200 201 if (current->F) { 202 current->ptr = fgetln(current->F, &len); 203 if (current->ptr) { 204 current->end = current->ptr + len; 205 return *current->ptr++; 206 } else { 207 current->end = NULL; 208 } 209 } 210 return EOF; 211 } 212 213 static int 214 skiptoendofline() 215 { 216 if (current->F) { 217 if (current->end - current->ptr > 1) 218 current->ptr = current->end - 1; 219 if (*current->ptr == '\n') 220 return *current->ptr++; 221 return EOF; 222 } else { 223 int c; 224 225 do { 226 c = ParseReadc(); 227 } while (c != '\n' && c != EOF); 228 return c; 229 } 230 } 231 232 233 char * 234 Parse_ReadNextConditionalLine(linebuf) 235 Buffer linebuf; 236 { 237 int c; 238 239 /* If first char isn't dot, skip to end of line, handling \ */ 240 while ((c = ParseReadc()) != '.') { 241 for (;c != '\n'; c = ParseReadc()) { 242 if (c == '\\') { 243 c = ParseReadc(); 244 if (c == '\n') 245 current->lineno++; 246 } 247 if (c == EOF) { 248 Parse_Error(PARSE_FATAL, "Unclosed conditional"); 249 return NULL; 250 } 251 } 252 current->lineno++; 253 } 254 255 /* This is the line we need to copy */ 256 return Parse_ReadUnparsedLine(linebuf, "conditional"); 257 } 258 259 static void 260 ParseFoldLF(linebuf, c) 261 Buffer linebuf; 262 int c; 263 { 264 for (;;) { 265 if (c == '\n') { 266 current->lineno++; 267 break; 268 } 269 if (c == EOF) 270 break; 271 Buf_AddChar(linebuf, c); 272 c = ParseReadc(); 273 while (c == '\\') { 274 c = ParseReadc(); 275 if (c == '\n') { 276 Buf_AddSpace(linebuf); 277 current->lineno++; 278 do { 279 c = ParseReadc(); 280 } while (c == ' ' || c == '\t'); 281 } else { 282 Buf_AddChar(linebuf, '\\'); 283 if (c == '\\') { 284 Buf_AddChar(linebuf, '\\'); 285 c = ParseReadc(); 286 } 287 break; 288 } 289 } 290 } 291 } 292 293 char * 294 Parse_ReadUnparsedLine(linebuf, type) 295 Buffer linebuf; 296 const char *type; 297 { 298 int c; 299 300 Buf_Reset(linebuf); 301 c = ParseReadc(); 302 if (c == EOF) { 303 Parse_Error(PARSE_FATAL, "Unclosed %s", type); 304 return NULL; 305 } 306 307 /* Handle '\' at beginning of line, since \\n needs special treatment */ 308 while (c == '\\') { 309 c = ParseReadc(); 310 if (c == '\n') { 311 current->lineno++; 312 do { 313 c = ParseReadc(); 314 } while (c == ' ' || c == '\t'); 315 } else { 316 Buf_AddChar(linebuf, '\\'); 317 if (c == '\\') { 318 Buf_AddChar(linebuf, '\\'); 319 c = ParseReadc(); 320 } 321 break; 322 } 323 } 324 ParseFoldLF(linebuf, c); 325 326 return Buf_Retrieve(linebuf); 327 } 328 329 /* This is a fairly complex function, but without it, we could not skip 330 * blocks of comments without reading them. */ 331 static int 332 ParseSkipEmptyLines(linebuf) 333 Buffer linebuf; 334 { 335 int c; /* the current character */ 336 337 for (;;) { 338 Buf_Reset(linebuf); 339 c = ParseReadc(); 340 /* Strip leading spaces, fold on '\n' */ 341 if (c == ' ') { 342 do { 343 c = ParseReadc(); 344 } while (c == ' ' || c == '\t'); 345 while (c == '\\') { 346 c = ParseReadc(); 347 if (c == '\n') { 348 current->lineno++; 349 do { 350 c = ParseReadc(); 351 } while (c == ' ' || c == '\t'); 352 } else { 353 Buf_AddChar(linebuf, '\\'); 354 if (c == '\\') { 355 Buf_AddChar(linebuf, '\\'); 356 c = ParseReadc(); 357 } 358 if (c == EOF) 359 return '\n'; 360 else 361 return c; 362 } 363 } 364 assert(c != '\t'); 365 } 366 if (c == '#') 367 c = skiptoendofline(); 368 /* Almost identical to spaces, except this occurs after comments 369 * have been taken care of, and we keep the tab itself. */ 370 if (c == '\t') { 371 Buf_AddChar(linebuf, '\t'); 372 do { 373 c = ParseReadc(); 374 } while (c == ' ' || c == '\t'); 375 while (c == '\\') { 376 c = ParseReadc(); 377 if (c == '\n') { 378 current->lineno++; 379 do { 380 c = ParseReadc(); 381 } while (c == ' ' || c == '\t'); 382 } else { 383 Buf_AddChar(linebuf, '\\'); 384 if (c == '\\') { 385 Buf_AddChar(linebuf, '\\'); 386 c = ParseReadc(); 387 } 388 if (c == EOF) 389 return '\n'; 390 else 391 return c; 392 return c; 393 } 394 } 395 } 396 if (c == '\n') 397 current->lineno++; 398 else 399 return c; 400 } 401 } 402 403 /* Parse_ReadNormalLine removes beginning and trailing blanks (but keeps 404 * the first tab), handles escaped newlines, and skip over uninteresting 405 * lines. 406 * 407 * The line number is advanced, which implies that continuation 408 * lines are numbered with the last line no (we could do better, at a 409 * price). 410 * 411 * Trivial comments are also removed, but we can't do more, as 412 * we don't know which lines are shell commands or not. */ 413 char * 414 Parse_ReadNormalLine(linebuf) 415 Buffer linebuf; 416 { 417 int c; /* the current character */ 418 419 c = ParseSkipEmptyLines(linebuf); 420 421 if (c == EOF) 422 return NULL; 423 else { 424 ParseFoldLF(linebuf, c); 425 Buf_KillTrailingSpaces(linebuf); 426 return Buf_Retrieve(linebuf); 427 } 428 } 429 430 unsigned long 431 Parse_Getlineno() 432 { 433 return current ? current->lineno : 0; 434 } 435 436 const char * 437 Parse_Getfilename() 438 { 439 return current ? current->fname : NULL; 440 } 441 442 #ifdef CLEANUP 443 void 444 LowParse_Init() 445 { 446 Static_Lst_Init(&input_stack); 447 current = NULL; 448 } 449 450 void 451 LowParse_End() 452 { 453 Lst_Destroy(&input_stack, NOFREE); /* Should be empty now */ 454 #if 0 455 Lst_Destroy(&fileNames, (SimpleProc)free); 456 #endif 457 } 458 #endif 459 460 461 void 462 Parse_ReportErrors() 463 { 464 if (fatal_errors) { 465 #ifdef CLEANUP 466 while (Parse_NextFile()) 467 ; 468 #endif 469 fprintf(stderr, "Fatal errors encountered -- cannot continue\n"); 470 exit(1); 471 } else 472 assert(current == NULL); 473 } 474