1 /* $OpenPackages$ */ 2 /* $OpenBSD: lowparse.c,v 1.22 2007/09/22 10:43:38 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 * Files have str == NULL, F != NULL. 56 * Strings have F == NULL, str != NULL. 57 */ 58 struct input_stream { 59 const char *fname; /* Name of file */ 60 unsigned long lineno; /* Line number */ 61 FILE *F; /* Open stream, or NULL if pure string. */ 62 char *str; /* Input string, if F == NULL. */ 63 64 /* Line buffer. */ 65 char *ptr; /* Where we are. */ 66 char *end; /* Don't overdo it. */ 67 }; 68 69 static struct input_stream *current; /* the input_stream being parsed. */ 70 71 static LIST input_stack; /* Stack of input_stream waiting to be parsed 72 * (includes and loop reparses) */ 73 74 /* input_stream ctors. 75 * 76 * obj = new_input_file(filename, filehandle); 77 * Create input stream from filename, filehandle. */ 78 static struct input_stream *new_input_file(const char *, FILE *); 79 /* obj = new_input_string(str, filename, lineno); 80 * Create input stream from str, filename, lineno. */ 81 static struct input_stream *new_input_string(char *, const char *, unsigned long); 82 /* free_input_stream(obj); 83 * Discard consumed input stream, closing files, freeing memory. */ 84 static void free_input_stream(struct input_stream *); 85 86 87 /* Handling basic character reading. 88 * c = read_char(); 89 * New character c from current input stream, or EOF at end of stream. */ 90 #define read_char() \ 91 current->ptr < current->end ? *current->ptr++ : grab_new_line_and_readchar() 92 /* char = grab_new_line_and_readchar(); 93 * Guts for read_char. Grabs a new line off fgetln when we have 94 * consumed the current line and returns the first char, or EOF at end of 95 * stream. */ 96 static int grab_new_line_and_readchar(void); 97 /* c = skip_to_end_of_line(); 98 * Skips to the end of the current line, returns either '\n' or EOF. */ 99 static int skip_to_end_of_line(void); 100 101 102 /* Helper functions to handle basic parsing. */ 103 /* read_logical_line(buffer, firstchar); 104 * Grabs logical line into buffer, the first character has already been 105 * read into firstchar. */ 106 static void read_logical_line(Buffer, int); 107 108 /* firstchar = ParseSkipEmptyLines(buffer); 109 * Scans lines, skipping empty lines. May put some characters into 110 * buffer, returns the first character useful to continue parsing 111 * (e.g., not a backslash or a space. */ 112 static int skip_empty_lines_and_read_char(Buffer); 113 114 static struct input_stream * 115 new_input_file(const char *name, FILE *stream) 116 { 117 struct input_stream *istream; 118 #if 0 119 Lst_AtEnd(&fileNames, name); 120 #endif 121 122 istream = emalloc(sizeof(*istream)); 123 istream->fname = name; 124 istream->str = NULL; 125 /* Naturally enough, we start reading at line 0. */ 126 istream->lineno = 0; 127 istream->F = stream; 128 istream->ptr = istream->end = NULL; 129 return istream; 130 } 131 132 static void 133 free_input_stream(struct input_stream *istream) 134 { 135 if (istream->F && fileno(istream->F) != STDIN_FILENO) 136 (void)fclose(istream->F); 137 free(istream->str); 138 /* Note we can't free the file names yet, as they are embedded in GN 139 * for error reports. */ 140 free(istream); 141 } 142 143 static struct input_stream * 144 new_input_string(char *str, const char *name, unsigned long lineno) 145 { 146 struct input_stream *istream; 147 148 istream = emalloc(sizeof(*istream)); 149 /* No malloc, name is always taken from an already existing istream */ 150 istream->fname = name; 151 istream->F = NULL; 152 /* Strings are used in for loops, so we need to reset the line counter 153 * to an appropriate value. */ 154 istream->lineno = lineno; 155 istream->ptr = istream->str = str; 156 istream->end = str + strlen(str); 157 return istream; 158 } 159 160 161 void 162 Parse_FromString(char *str, unsigned long lineno) 163 { 164 if (DEBUG(FOR)) 165 (void)fprintf(stderr, "%s\n----\n", str); 166 167 if (current != NULL) 168 Lst_Push(&input_stack, current); 169 current = new_input_string(str, current->fname, lineno); 170 } 171 172 173 void 174 Parse_FromFile(const char *name, FILE *stream) 175 { 176 if (current != NULL) 177 Lst_Push(&input_stack, current); 178 current = new_input_file(name, stream); 179 } 180 181 bool 182 Parse_NextFile(void) 183 { 184 if (current != NULL) 185 free_input_stream(current); 186 current = (struct input_stream *)Lst_Pop(&input_stack); 187 return current != NULL; 188 } 189 190 static int 191 grab_new_line_and_readchar(void) 192 { 193 size_t len; 194 195 if (current->F) { 196 current->ptr = fgetln(current->F, &len); 197 if (current->ptr) { 198 current->end = current->ptr + len; 199 return *current->ptr++; 200 } else { 201 current->end = NULL; 202 } 203 } 204 return EOF; 205 } 206 207 static int 208 skip_to_end_of_line(void) 209 { 210 if (current->F) { 211 if (current->end - current->ptr > 1) 212 current->ptr = current->end - 1; 213 if (*current->ptr == '\n') 214 return *current->ptr++; 215 return EOF; 216 } else { 217 int c; 218 219 do { 220 c = read_char(); 221 } while (c != '\n' && c != EOF); 222 return c; 223 } 224 } 225 226 227 char * 228 Parse_ReadNextConditionalLine(Buffer linebuf) 229 { 230 int c; 231 232 /* If first char isn't dot, skip to end of line, handling \ */ 233 while ((c = read_char()) != '.') { 234 for (;c != '\n'; c = read_char()) { 235 if (c == '\\') { 236 c = read_char(); 237 if (c == '\n') 238 current->lineno++; 239 } 240 if (c == EOF) { 241 Parse_Error(PARSE_FATAL, 242 "Unclosed conditional"); 243 return NULL; 244 } 245 } 246 current->lineno++; 247 } 248 249 /* This is the line we need to copy */ 250 return Parse_ReadUnparsedLine(linebuf, "conditional"); 251 } 252 253 static void 254 read_logical_line(Buffer linebuf, int c) 255 { 256 for (;;) { 257 if (c == '\n') { 258 current->lineno++; 259 break; 260 } 261 if (c == EOF) 262 break; 263 Buf_AddChar(linebuf, c); 264 c = read_char(); 265 while (c == '\\') { 266 c = read_char(); 267 if (c == '\n') { 268 Buf_AddSpace(linebuf); 269 current->lineno++; 270 do { 271 c = read_char(); 272 } while (c == ' ' || c == '\t'); 273 } else { 274 Buf_AddChar(linebuf, '\\'); 275 if (c == '\\') { 276 Buf_AddChar(linebuf, '\\'); 277 c = read_char(); 278 } 279 break; 280 } 281 } 282 } 283 } 284 285 char * 286 Parse_ReadUnparsedLine(Buffer linebuf, const char *type) 287 { 288 int c; 289 290 Buf_Reset(linebuf); 291 c = read_char(); 292 if (c == EOF) { 293 Parse_Error(PARSE_FATAL, "Unclosed %s", type); 294 return NULL; 295 } 296 297 /* Handle '\' at beginning of line, since \\n needs special treatment */ 298 while (c == '\\') { 299 c = read_char(); 300 if (c == '\n') { 301 current->lineno++; 302 do { 303 c = read_char(); 304 } while (c == ' ' || c == '\t'); 305 } else { 306 Buf_AddChar(linebuf, '\\'); 307 if (c == '\\') { 308 Buf_AddChar(linebuf, '\\'); 309 c = read_char(); 310 } 311 break; 312 } 313 } 314 read_logical_line(linebuf, c); 315 316 return Buf_Retrieve(linebuf); 317 } 318 319 /* This is a fairly complex function, but without it, we could not skip 320 * blocks of comments without reading them. */ 321 static int 322 skip_empty_lines_and_read_char(Buffer linebuf) 323 { 324 int c; /* the current character */ 325 326 for (;;) { 327 Buf_Reset(linebuf); 328 c = read_char(); 329 /* Strip leading spaces, fold on '\n' */ 330 if (c == ' ') { 331 do { 332 c = read_char(); 333 } while (c == ' ' || c == '\t'); 334 while (c == '\\') { 335 c = read_char(); 336 if (c == '\n') { 337 current->lineno++; 338 do { 339 c = read_char(); 340 } while (c == ' ' || c == '\t'); 341 } else { 342 Buf_AddChar(linebuf, '\\'); 343 if (c == '\\') { 344 Buf_AddChar(linebuf, '\\'); 345 c = read_char(); 346 } 347 if (c == EOF) 348 return '\n'; 349 else 350 return c; 351 } 352 } 353 assert(c != '\t'); 354 } 355 if (c == '#') 356 c = skip_to_end_of_line(); 357 /* Almost identical to spaces, except this occurs after 358 * comments have been taken care of, and we keep the tab 359 * itself. */ 360 if (c == '\t') { 361 Buf_AddChar(linebuf, '\t'); 362 do { 363 c = read_char(); 364 } while (c == ' ' || c == '\t'); 365 while (c == '\\') { 366 c = read_char(); 367 if (c == '\n') { 368 current->lineno++; 369 do { 370 c = read_char(); 371 } while (c == ' ' || c == '\t'); 372 } else { 373 Buf_AddChar(linebuf, '\\'); 374 if (c == '\\') { 375 Buf_AddChar(linebuf, '\\'); 376 c = read_char(); 377 } 378 if (c == EOF) 379 return '\n'; 380 else 381 return c; 382 } 383 } 384 } 385 if (c == '\n') 386 current->lineno++; 387 else 388 return c; 389 } 390 } 391 392 /* Parse_ReadNormalLine removes beginning and trailing blanks (but keeps 393 * the first tab), handles escaped newlines, and skips over uninteresting 394 * lines. 395 * 396 * The line number is incremented, which implies that continuation 397 * lines are numbered with the last line number (we could do better, at a 398 * price). 399 * 400 * Trivial comments are also removed, but we can't do more, as 401 * we don't know which lines are shell commands or not. */ 402 char * 403 Parse_ReadNormalLine(Buffer linebuf) 404 { 405 int c; /* the current character */ 406 407 c = skip_empty_lines_and_read_char(linebuf); 408 409 if (c == EOF) 410 return NULL; 411 else { 412 read_logical_line(linebuf, c); 413 Buf_KillTrailingSpaces(linebuf); 414 return Buf_Retrieve(linebuf); 415 } 416 } 417 418 unsigned long 419 Parse_Getlineno(void) 420 { 421 return current ? current->lineno : 0; 422 } 423 424 const char * 425 Parse_Getfilename(void) 426 { 427 return current ? current->fname : NULL; 428 } 429 430 #ifdef CLEANUP 431 void 432 LowParse_Init(void) 433 { 434 Static_Lst_Init(&input_stack); 435 current = NULL; 436 } 437 438 void 439 LowParse_End(void) 440 { 441 Lst_Destroy(&input_stack, NOFREE); /* Should be empty now */ 442 #if 0 443 Lst_Destroy(&fileNames, (SimpleProc)free); 444 #endif 445 } 446 #endif 447 448 449 void 450 Parse_ReportErrors(void) 451 { 452 if (fatal_errors) { 453 #ifdef CLEANUP 454 while (Parse_NextFile()) 455 ; 456 #endif 457 fprintf(stderr, 458 "Fatal errors encountered -- cannot continue\n"); 459 exit(1); 460 } else 461 assert(current == NULL); 462 } 463