1/* input_scrub.c - layer between app and the rest of the world 2 Copyright (C) 1987 Free Software Foundation, Inc. 3 4This file is part of GAS, the GNU Assembler. 5 6GAS is free software; you can redistribute it and/or modify 7it under the terms of the GNU General Public License as published by 8the Free Software Foundation; either version 1, or (at your option) 9any later version. 10 11GAS is distributed in the hope that it will be useful, 12but WITHOUT ANY WARRANTY; without even the implied warranty of 13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14GNU General Public License for more details. 15 16You should have received a copy of the GNU General Public License 17along with GAS; see the file COPYING. If not, write to 18the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ 19 20#include <stdlib.h> 21#include <string.h> 22#include <errno.h> 23#include <libc.h> 24#ifdef NeXT_MOD /* .include feature */ 25#include <sys/file.h> 26#include <sys/param.h> 27#endif /* NeXT_MOD .include feature */ 28#include "as.h" 29#include "read.h" 30#include "input-file.h" 31#include "input-scrub.h" 32#include "app.h" 33#include "xmalloc.h" 34#include "messages.h" 35 36/* 37 * O/S independent module to supply buffers of sanitised source code 38 * to rest of assembler. We get raw input data of some length. 39 * Also looks after line numbers, for e.g. error messages. 40 * This module used to do the sanitising, but now a pre-processor program 41 * (app) does that job so this module is degenerate. 42 * Now input is pre-sanitised, so we only worry about finding the 43 * last partial line. A buffer of full lines is returned to caller. 44 * The last partial line begins the next buffer we build and return to caller. 45 * The buffer returned to caller is preceeded by BEFORE_STRING and followed 46 * by AFTER_STRING. The last character before AFTER_STRING is a newline. 47 */ 48 49/* 50 * We expect the following sanitation has already been done. 51 * 52 * No comments, reduce a comment to a space. 53 * Reduce a tab to a space unless it is 1st char of line. 54 * All multiple tabs and spaces collapsed into 1 char. Tab only 55 * legal if 1st char of line. 56 * # line file statements converted to .line x;.file y; statements. 57 * Escaped newlines at end of line: remove them but add as many newlines 58 * to end of statement as you removed in the middle, to synch line numbers. 59 */ 60 61#define BEFORE_STRING ("\n") 62#define AFTER_STRING ("\0") /* memcpy of 0 chars might choke. */ 63#define BEFORE_SIZE (1) 64#define AFTER_SIZE (1) /* includes the \0 */ 65 66static char * buffer_start; /* -> 1st char of full buffer area. */ 67static char * partial_where; /* -> after last full line in buffer. */ 68static int partial_size; /* >=0. Number of chars in partial line in buffer. */ 69static char save_source [AFTER_SIZE]; 70 /* Because we need AFTER_STRING just after last */ 71 /* full line, it clobbers 1st part of partial */ 72 /* line. So we preserve 1st part of partial */ 73 /* line here. */ 74static int buffer_length; /* What is the largest size buffer that */ 75 /* input_file_give_next_buffer() could */ 76 /* return to us? */ 77 78/* 79We never have more than one source file open at once. 80We may, however, read more than 1 source file in an assembly. 81NULL means we have no file open right now. 82*/ 83 84 85/* 86We must track the physical file and line number for error messages. 87We also track a "logical" file and line number corresponding to (C?) 88compiler source line numbers. 89Whenever we open a file we must fill in physical_input_file. So if it is NULL 90we have not opened any files yet. 91*/ 92 93int doing_include = FALSE; /* TRUE when we are processing a .include */ 94 95char *physical_input_file = NULL; 96char *logical_input_file = NULL; 97char *layout_file = NULL; 98char *input_dir = NULL; 99 100line_numberT physical_input_line = 0; 101line_numberT logical_input_line = 0; 102line_numberT layout_line = 0; 103 104 105void 106input_scrub_begin( 107void) 108{ 109 know( strlen(BEFORE_STRING) == BEFORE_SIZE ); 110 know( strlen(AFTER_STRING) + 1 == AFTER_SIZE ); 111 112 input_file_begin (); 113 114 buffer_length = input_file_buffer_size (); 115 116 buffer_start = xmalloc ((size_t)(BEFORE_SIZE + buffer_length + buffer_length + AFTER_SIZE)); 117 memcpy(buffer_start, BEFORE_STRING, (int)BEFORE_SIZE); 118 119 /* Line number things. */ 120 logical_input_line = 0; 121 logical_input_file = (char *)NULL; 122 physical_input_file = NULL; /* No file read yet. */ 123 do_scrub_begin(); 124} 125 126void 127input_scrub_end( 128void) 129{ 130 input_file_end (); 131} 132 133char * /* Return start of caller's part of buffer. */ 134input_scrub_new_file( 135char *filename) 136{ 137 input_file_open (filename, !flagseen['f']); 138 physical_input_file = filename[0] ? filename : "{standard input}"; 139 physical_input_line = 0; 140 141 if (filename[0]) 142 { 143 char *p; 144 int len; 145 146 p = strrchr(filename, '/'); 147 if (p != NULL && p[1] != '\0') 148 { 149 len = p - filename + 1; 150 input_dir = xmalloc(len + 1); 151 strncpy(input_dir, filename, len); 152 } 153 } 154 155 partial_size = 0; 156 return (buffer_start + BEFORE_SIZE); 157} 158 159/* 160 * input_scrub_next_buffer() 161 * 162 * The input parameter **bufp is used to return the address of a new or the 163 * previous buffer containing the characters to parse. 164 * 165 * This uses the static variables declared in this file (previouly set up by 166 * input_scrub_begin() and previous calls to itself). The buffer is created 167 * with twice the buffer_length plus the "BEFORE" and "AFTER" bytes. 168 * So there maybe some characters from a partial line following the last 169 * newline in the buffer. 170 * 171 * It returns a pointer into the buffer as the buffer_limit to the last 172 * character of the last line in the buffer (before the last newline, where the 173 * parsing stops). 174 */ 175char * 176input_scrub_next_buffer( 177char **bufp) 178{ 179 register char * limit; /* -> just after last char of buffer. */ 180 int give_next_size; 181 182 if (partial_size) 183 { 184 memcpy(buffer_start + BEFORE_SIZE, partial_where, (int)partial_size); 185 memcpy(buffer_start + BEFORE_SIZE, save_source, (int)AFTER_SIZE); 186 } 187get_more: 188 limit = input_file_give_next_buffer( 189 buffer_start + BEFORE_SIZE + partial_size, 190 &give_next_size); 191 if (limit) 192 { 193 register char * p; /* Find last newline. */ 194 for (p = limit; * -- p != '\n'; ) 195 { 196 } 197 ++ p; 198 if (p <= buffer_start + BEFORE_SIZE) 199 { 200 int new; 201 202 new = limit - (buffer_start + BEFORE_SIZE + partial_size); 203 partial_size += new; 204 205 /* 206 * If there is enough room left in this buffer for what 207 * input_file_give_next_buffer() will need don't reallocate as we 208 * could run out of memory needlessly. 209 */ 210 if((BEFORE_SIZE + buffer_length * 2) - (limit - buffer_start) > 211 give_next_size) 212 goto get_more; 213 214 buffer_length = buffer_length * 2; 215 buffer_start = xrealloc (buffer_start, 216 (size_t)(BEFORE_SIZE + buffer_length + 217 buffer_length + AFTER_SIZE)); 218 *bufp = buffer_start + BEFORE_SIZE; 219 goto get_more; 220 } 221 partial_where = p; 222 partial_size = limit - p; 223 memcpy(save_source, partial_where, (int)AFTER_SIZE); 224 memcpy(partial_where, AFTER_STRING, (int)AFTER_SIZE); 225 } 226 else 227 { 228 partial_where = 0; 229 if (partial_size > 0) 230 { 231 as_warn( "Partial line at end of file ignored" ); 232 } 233 } 234 return (partial_where); 235} 236 237/* 238 * The remaining part of this file deals with line numbers, error 239 * messages and so on. 240 */ 241 242 243/* 244 * seen_at_least_1_file() returns TRUE if we opened any file. 245 */ 246int 247seen_at_least_1_file( 248void) 249{ 250 return (physical_input_file != NULL); 251} 252 253void 254bump_line_counters( 255void) 256{ 257 ++ physical_input_line; 258 ++ logical_input_line; 259} 260 261/* 262 * new_logical_line() 263 * 264 * Tells us what the new logical line number and file are. 265 * If the line_number is <0, we don't change the current logical line number. 266 * If the fname is NULL, we don't change the current logical file name. 267 */ 268void 269new_logical_line( 270char *fname, /* DON'T destroy it! We point to it! */ 271int line_number) 272{ 273 if ( fname ) 274 { 275 logical_input_file = fname; 276 } 277 if ( line_number >= 0 ) 278 { 279 logical_input_line = line_number; 280 } 281} 282 283/* 284 * a s _ w h e r e ( ) 285 * 286 * Write a line to stderr locating where we are in reading 287 * input source files. 288 * As a sop to the debugger of AS, pretty-print the offending line. 289 */ 290void 291as_where( 292void) 293{ 294 char *p; 295 line_numberT line; 296 297 if (physical_input_file) 298 { /* we tried to read SOME source */ 299 if (input_file_is_open()) 300 { /* we can still read lines from source */ 301 p = logical_input_file ? logical_input_file : physical_input_file; 302 line = logical_input_line ? logical_input_line : physical_input_line; 303 fprintf(stderr,"%s:%u:", p, line); 304 } 305 else 306 { 307 p = logical_input_file ? logical_input_file : physical_input_file; 308 line = logical_input_line ? logical_input_line : physical_input_line; 309 if(layout_line != 0) 310 fprintf (stderr,"%s:%u:", layout_file, layout_line); 311 else 312 fprintf (stderr,"%s:unknown:", p); 313 } 314 } 315 else 316 { 317 } 318} 319 320void 321as_file_and_line( 322char **file_ret, 323unsigned int *line_ret) 324{ 325 *file_ret = NULL; 326 *line_ret = 0; 327 if (physical_input_file) 328 { /* we tried to read SOME source */ 329 *file_ret = logical_input_file ? 330 logical_input_file : physical_input_file; 331 if (input_file_is_open()) 332 { /* we can still read lines from source */ 333 *line_ret = logical_input_line ? 334 logical_input_line : physical_input_line; 335 } 336 } 337} 338 339/* 340 * as_where_ProjectBuilder() returns the fileName, directory, and line number 341 * to be used to tell ProjectBuilder where the error is. Note that the '/' 342 * between fileName and directory does not appear in what is returned. 343 */ 344void 345as_where_ProjectBuilder( 346char **fileName, 347char **directory, 348int *line) 349{ 350 char *p, *q; 351 static char directory_buf[MAXPATHLEN]; 352 353 getwd(directory_buf); 354 *fileName = NULL; 355 *directory = directory_buf; 356 *line = 0; 357 358 if(physical_input_file){ 359 p = logical_input_file ? 360 logical_input_file : physical_input_file; 361 if(input_file_is_open()){ 362 *line = logical_input_line ? 363 logical_input_line : physical_input_line; 364 } 365 *fileName = p; 366 q = strrchr(p, '/'); 367 if(q == NULL) 368 return; 369 *fileName = p + 1; 370 strncat(directory_buf, p, q - p); 371 } 372} 373 374/* 375 * a s _ p e r r o r 376 * 377 * Like perror(3), but with more info. 378 */ 379void 380as_perror( 381char *gripe, /* Unpunctuated error theme. */ 382char *filename) 383{ 384 fprintf (stderr,"as:file(%s) %s! ", 385 filename, gripe 386 ); 387 if (errno > sys_nerr) 388 { 389 fprintf (stderr, "Unknown error #%d.", errno); 390 } 391 else 392 { 393 fprintf (stderr, "%s.", sys_errlist [errno]); 394 } 395 (void)putc('\n', stderr); 396 errno = 0; /* After reporting, clear it. */ 397 if (input_file_is_open()) /* RMS says don't mention line # if not needed. */ 398 { 399 as_where(); 400 } 401 bad_error = 1; 402} 403 404 405#ifdef NeXT_MOD /* .include feature */ 406/* DJA -- added for .include pseudo op support */ 407char * 408find_an_include_file( 409char *no_path_name) 410{ 411 char name_buffer [MAXPATHLEN]; 412 register struct directory_stack * the_path_pointer; 413 register char * whole_file_name; 414 415 /* 416 * figure out what directory the file name is in. 417 */ 418 whole_file_name = no_path_name; 419 if (access(whole_file_name, R_OK)) 420 { 421 whole_file_name = name_buffer; 422 if (no_path_name[0] != '/' && input_dir != NULL) 423 { 424 if (strlen (input_dir) + strlen (no_path_name) >= MAXPATHLEN) 425 as_fatal ("include file name too long: \"%s%s\"", input_dir, no_path_name); 426 else 427 { 428 strcpy (whole_file_name, input_dir); 429 strcat (whole_file_name, no_path_name); 430 if (!access(whole_file_name, R_OK)) 431 goto found; 432 } 433 } 434 the_path_pointer = include; 435 while (the_path_pointer) 436 { 437 if (strlen (the_path_pointer->fname) + (strlen (no_path_name)) >= MAXPATHLEN) 438 as_fatal ("include file name too long: \"%s%s\"", the_path_pointer->fname, no_path_name); 439 else 440 { 441 *whole_file_name = '\0'; 442 strcpy (whole_file_name, the_path_pointer->fname); 443 strcat (whole_file_name, "/"); 444 strcat (whole_file_name, no_path_name); 445 if (!access(whole_file_name, R_OK)) 446 goto found; 447 } 448 the_path_pointer = the_path_pointer->next; 449 } 450 the_path_pointer = include_defaults; 451 while (the_path_pointer->fname != NULL) 452 { 453 if (strlen (the_path_pointer->fname) + (strlen (no_path_name)) >= MAXPATHLEN) 454 as_fatal ("include file name too long: \"%s%s\"", the_path_pointer->fname, no_path_name); 455 else 456 { 457 *whole_file_name = '\0'; 458 strcpy (whole_file_name, the_path_pointer->fname); 459 strcat (whole_file_name, "/"); 460 strcat (whole_file_name, no_path_name); 461 if (!access(whole_file_name, R_OK)) 462 goto found; 463 } 464 the_path_pointer++; 465 } 466 as_fatal ("Couldn't find the include file: \"%s\"", no_path_name); 467 return (NULL); 468 } 469found: 470 return (whole_file_name); 471} 472 473void 474read_an_include_file( 475char *no_path_name) 476{ 477 char * buffer; 478 char * last_buffer_limit; 479 char * last_buffer_start; 480 int last_doing_include; 481 FILE * last_f_in; 482 char * last_file_name; 483 char * last_input_line_pointer; 484 char * last_logical_input_file; 485 line_numberT last_logical_input_line; 486 int last_partial_size; 487 char * last_partial_where; 488 char * last_physical_input_file; 489 line_numberT last_physical_input_line; 490 char last_save_source [AFTER_SIZE]; 491 int last_buffer_length; 492#if 0 493 char * last_save_buffer; 494#endif 495 scrub_context_data scrub_context; 496 register char * whole_file_name; 497 498 /* 499 * figure out what directory the file name is in. 500 */ 501 whole_file_name = find_an_include_file (no_path_name); 502 503 /* 504 * save a copy of the file state for a recursive call to read a file 505 */ 506 last_buffer_limit = buffer_limit; 507 last_buffer_start = buffer_start; 508 last_doing_include = doing_include; 509 last_f_in = f_in; 510 last_file_name = file_name; 511 last_input_line_pointer = input_line_pointer; 512 last_logical_input_file = logical_input_file; 513 last_logical_input_line = logical_input_line; 514 last_partial_size = partial_size; 515 last_partial_where = partial_where; 516 last_physical_input_file = physical_input_file; 517 last_physical_input_line = physical_input_line; 518 memcpy(last_save_source, save_source, sizeof (save_source)); 519 last_buffer_length = buffer_length; 520 save_scrub_context (&scrub_context); 521 /* 522 * set up for another file 523 */ 524 partial_size = 0; 525 doing_include = TRUE; 526 input_scrub_begin (); 527 buffer = input_scrub_new_file (whole_file_name); 528 if (f_in != (FILE *)0) 529 read_a_source_file(buffer); 530 531 xfree (buffer_start); 532 /* 533 * restore the file state 534 */ 535 buffer_limit = last_buffer_limit; 536 buffer_start = last_buffer_start; 537 doing_include = last_doing_include; 538 f_in = last_f_in; 539 file_name = last_file_name; 540 input_line_pointer = last_input_line_pointer; 541 logical_input_file = last_logical_input_file; 542 logical_input_line = last_logical_input_line; 543 partial_size = last_partial_size; 544 partial_where = last_partial_where; 545 physical_input_file = last_physical_input_file; 546 physical_input_line = last_physical_input_line; 547 memcpy(save_source, last_save_source, sizeof (save_source)); 548 buffer_length = last_buffer_length; 549 restore_scrub_context (&scrub_context); 550} /* read_an_include_file */ 551#endif /* NeXT_MOD .include feature */ 552