X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=gcc%2Ffortran%2Fscanner.c;h=711042ddcb25952cef2107fa5586b988074771a6;hb=507e9ee716fcd51cd3263f43e5496a5d33cc9b98;hp=34959ab92fe1bfad42bc8ff8b046c9cb85ff771b;hpb=c84b470d6105061c09995cc9c68585cf68e52384;p=pf3gnuchains%2Fgcc-fork.git diff --git a/gcc/fortran/scanner.c b/gcc/fortran/scanner.c index 34959ab92fe..711042ddcb2 100644 --- a/gcc/fortran/scanner.c +++ b/gcc/fortran/scanner.c @@ -1,12 +1,13 @@ /* Character scanner. - Copyright (C) 2000, 2001, 2002, 2003 Free Software Foundation, Inc. + Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 + Free Software Foundation, Inc. Contributed by Andy Vaught This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free -Software Foundation; either version 2, or (at your option) any later +Software Foundation; either version 3, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY @@ -15,9 +16,8 @@ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with GCC; see the file COPYING. If not, write to the Free -Software Foundation, 59 Temple Place - Suite 330, Boston, MA -02111-1307, USA. */ +along with GCC; see the file COPYING3. If not see +. */ /* Set of subroutines to (ultimately) return the next character to the various matching subroutines. This file's job is to read files and @@ -42,28 +42,225 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA new characters and do a lot of jumping backwards. */ #include "config.h" -#include -#include -#include -#include - +#include "system.h" #include "gfortran.h" +#include "toplev.h" +#include "debug.h" +#include "flags.h" +#include "cpp.h" /* Structure for holding module and include file search path. */ typedef struct gfc_directorylist { char *path; + bool use_for_modules; struct gfc_directorylist *next; } gfc_directorylist; /* List of include file search directories. */ -static gfc_directorylist *include_dirs; +static gfc_directorylist *include_dirs, *intrinsic_modules_dirs; + +static gfc_file *file_head, *current_file; + +static int continue_flag, end_flag, openmp_flag, gcc_attribute_flag; +static int continue_count, continue_line; +static locus openmp_locus; +static locus gcc_attribute_locus; + +gfc_source_form gfc_current_form; +static gfc_linebuf *line_head, *line_tail; + +locus gfc_current_locus; +const char *gfc_source_file; +static FILE *gfc_src_file; +static gfc_char_t *gfc_src_preprocessor_lines[2]; + +extern int pedantic; + +static struct gfc_file_change +{ + const char *filename; + gfc_linebuf *lb; + int line; +} *file_changes; +size_t file_changes_cur, file_changes_count; +size_t file_changes_allocated; + + +/* Functions dealing with our wide characters (gfc_char_t) and + sequences of such characters. */ + +int +gfc_wide_fits_in_byte (gfc_char_t c) +{ + return (c <= UCHAR_MAX); +} + +static inline int +wide_is_ascii (gfc_char_t c) +{ + return (gfc_wide_fits_in_byte (c) && ((unsigned char) c & ~0x7f) == 0); +} + +int +gfc_wide_is_printable (gfc_char_t c) +{ + return (gfc_wide_fits_in_byte (c) && ISPRINT ((unsigned char) c)); +} + +gfc_char_t +gfc_wide_tolower (gfc_char_t c) +{ + return (wide_is_ascii (c) ? (gfc_char_t) TOLOWER((unsigned char) c) : c); +} + +gfc_char_t +gfc_wide_toupper (gfc_char_t c) +{ + return (wide_is_ascii (c) ? (gfc_char_t) TOUPPER((unsigned char) c) : c); +} + +int +gfc_wide_is_digit (gfc_char_t c) +{ + return (c >= '0' && c <= '9'); +} + +static inline int +wide_atoi (gfc_char_t *c) +{ +#define MAX_DIGITS 20 + char buf[MAX_DIGITS+1]; + int i = 0; + + while (gfc_wide_is_digit(*c) && i < MAX_DIGITS) + buf[i++] = *c++; + buf[i] = '\0'; + return atoi (buf); +} + +size_t +gfc_wide_strlen (const gfc_char_t *str) +{ + size_t i; + + for (i = 0; str[i]; i++) + ; + + return i; +} + +gfc_char_t * +gfc_wide_memset (gfc_char_t *b, gfc_char_t c, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) + b[i] = c; + + return b; +} + +static gfc_char_t * +wide_strcpy (gfc_char_t *dest, const gfc_char_t *src) +{ + gfc_char_t *d; + + for (d = dest; (*d = *src) != '\0'; ++src, ++d) + ; + + return dest; +} + +static gfc_char_t * +wide_strchr (const gfc_char_t *s, gfc_char_t c) +{ + do { + if (*s == c) + { + return CONST_CAST(gfc_char_t *, s); + } + } while (*s++); + return 0; +} + +char * +gfc_widechar_to_char (const gfc_char_t *s, int length) +{ + size_t len, i; + char *res; + + if (s == NULL) + return NULL; + + /* Passing a negative length is used to indicate that length should be + calculated using gfc_wide_strlen(). */ + len = (length >= 0 ? (size_t) length : gfc_wide_strlen (s)); + res = XNEWVEC (char, len + 1); + + for (i = 0; i < len; i++) + { + gcc_assert (gfc_wide_fits_in_byte (s[i])); + res[i] = (unsigned char) s[i]; + } + + res[len] = '\0'; + return res; +} -static gfc_file *first_file, *first_duplicated_file; -static int continue_flag, end_flag; +gfc_char_t * +gfc_char_to_widechar (const char *s) +{ + size_t len, i; + gfc_char_t *res; + + if (s == NULL) + return NULL; + + len = strlen (s); + res = gfc_get_wide_string (len + 1); + + for (i = 0; i < len; i++) + res[i] = (unsigned char) s[i]; + + res[len] = '\0'; + return res; +} + +static int +wide_strncmp (const gfc_char_t *s1, const char *s2, size_t n) +{ + gfc_char_t c1, c2; + + while (n-- > 0) + { + c1 = *s1++; + c2 = *s2++; + if (c1 != c2) + return (c1 > c2 ? 1 : -1); + if (c1 == '\0') + return 0; + } + return 0; +} + +int +gfc_wide_strncasecmp (const gfc_char_t *s1, const char *s2, size_t n) +{ + gfc_char_t c1, c2; -gfc_file *gfc_current_file; + while (n-- > 0) + { + c1 = gfc_wide_tolower (*s1++); + c2 = TOLOWER (*s2++); + if (c1 != c2) + return (c1 > c2 ? 1 : -1); + if (c1 == '\0') + return 0; + } + return 0; +} /* Main scanner initialization. */ @@ -71,10 +268,13 @@ gfc_file *gfc_current_file; void gfc_scanner_init_1 (void) { + file_head = NULL; + line_head = NULL; + line_tail = NULL; + + continue_count = 0; + continue_line = 0; - gfc_current_file = NULL; - first_file = NULL; - first_duplicated_file = NULL; end_flag = 0; } @@ -84,73 +284,84 @@ gfc_scanner_init_1 (void) void gfc_scanner_done_1 (void) { + gfc_linebuf *lb; + gfc_file *f; - linebuf *lp, *lp2; - gfc_file *fp, *fp2; - - for (fp = first_file; fp; fp = fp2) + while(line_head != NULL) { - - if (fp->start != NULL) - { - /* Free linebuf blocks */ - for (fp2 = fp->next; fp2; fp2 = fp2->next) - if (fp->start == fp2->start) - fp2->start = NULL; - - for (lp = fp->start; lp; lp = lp2) - { - lp2 = lp->next; - gfc_free (lp); - } - } - - fp2 = fp->next; - gfc_free (fp); + lb = line_head->next; + gfc_free(line_head); + line_head = lb; } - - for (fp = first_duplicated_file; fp; fp = fp2) + + while(file_head != NULL) { - fp2 = fp->next; - gfc_free (fp); + f = file_head->next; + gfc_free(file_head->filename); + gfc_free(file_head); + file_head = f; } } /* Adds path to the list pointed to by list. */ -void -gfc_add_include_path (const char *path) +static void +add_path_to_list (gfc_directorylist **list, const char *path, + bool use_for_modules, bool head) { gfc_directorylist *dir; const char *p; p = path; - while (*p == ' ' || *p == '\t') /* someone might do 'gfortran "-I include"' */ + while (*p == ' ' || *p == '\t') /* someone might do "-I include" */ if (*p++ == '\0') return; - dir = include_dirs; - if (!dir) + if (head || *list == NULL) { - dir = include_dirs = gfc_getmem (sizeof (gfc_directorylist)); + dir = XCNEW (gfc_directorylist); + if (!head) + *list = dir; } else { + dir = *list; while (dir->next) dir = dir->next; - dir->next = gfc_getmem (sizeof (gfc_directorylist)); + dir->next = XCNEW (gfc_directorylist); dir = dir->next; } - dir->next = NULL; - dir->path = gfc_getmem (strlen (p) + 2); + dir->next = head ? *list : NULL; + if (head) + *list = dir; + dir->use_for_modules = use_for_modules; + dir->path = XCNEWVEC (char, strlen (p) + 2); strcpy (dir->path, p); strcat (dir->path, "/"); /* make '/' last character */ } +void +gfc_add_include_path (const char *path, bool use_for_modules, bool file_dir) +{ + add_path_to_list (&include_dirs, path, use_for_modules, file_dir); + + /* For '#include "..."' these directories are automatically searched. */ + if (!file_dir) + gfc_cpp_add_include_path (xstrdup(path), true); +} + + +void +gfc_add_intrinsic_modules_path (const char *path) +{ + add_path_to_list (&intrinsic_modules_dirs, path, true, false); +} + + /* Release resources allocated for options. */ void @@ -158,7 +369,6 @@ gfc_release_include_path (void) { gfc_directorylist *p; - gfc_free (gfc_option.module_dir); while (include_dirs != NULL) { p = include_dirs; @@ -166,28 +376,32 @@ gfc_release_include_path (void) gfc_free (p->path); gfc_free (p); } -} + while (intrinsic_modules_dirs != NULL) + { + p = intrinsic_modules_dirs; + intrinsic_modules_dirs = intrinsic_modules_dirs->next; + gfc_free (p->path); + gfc_free (p); + } + + gfc_free (gfc_option.module_dir); +} -/* Opens file for reading, searching through the include directories - given if necessary. */ -FILE * -gfc_open_included_file (const char *name) +static FILE * +open_included_file (const char *name, gfc_directorylist *list, bool module) { - char fullname[PATH_MAX]; + char *fullname; gfc_directorylist *p; FILE *f; - f = gfc_open_file (name); - if (f != NULL) - return f; - - for (p = include_dirs; p; p = p->next) + for (p = list; p; p = p->next) { - if (strlen (p->path) + strlen (name) + 1 > PATH_MAX) + if (module && !p->use_for_modules) continue; + fullname = (char *) alloca(strlen (p->path) + strlen (name) + 1); strcpy (fullname, p->path); strcat (fullname, name); @@ -200,25 +414,35 @@ gfc_open_included_file (const char *name) } -/* Return a pointer to the current locus. */ +/* Opens file for reading, searching through the include directories + given if necessary. If the include_cwd argument is true, we try + to open the file in the current directory first. */ -locus * -gfc_current_locus (void) +FILE * +gfc_open_included_file (const char *name, bool include_cwd, bool module) { + FILE *f; - if (gfc_current_file == NULL) - return NULL; - return &gfc_current_file->loc; -} + if (IS_ABSOLUTE_PATH (name)) + return gfc_open_file (name); + if (include_cwd) + { + f = gfc_open_file (name); + if (f != NULL) + return f; + } -/* Let a caller move the current read pointer (backwards). */ + return open_included_file (name, include_dirs, module); +} -void -gfc_set_locus (locus * lp) +FILE * +gfc_open_intrinsic_module (const char *name) { + if (IS_ABSOLUTE_PATH (name)) + return gfc_open_file (name); - gfc_current_file->loc = *lp; + return open_included_file (name, intrinsic_modules_dirs, true); } @@ -227,7 +451,6 @@ gfc_set_locus (locus * lp) int gfc_at_end (void) { - return end_flag; } @@ -237,14 +460,13 @@ gfc_at_end (void) int gfc_at_eof (void) { - if (gfc_at_end ()) return 1; - if (gfc_current_file->start->lines == 0) + if (line_head == NULL) return 1; /* Null file */ - if (gfc_current_file->loc.lp == NULL) + if (gfc_current_locus.lb == NULL) return 1; return 0; @@ -256,14 +478,10 @@ gfc_at_eof (void) int gfc_at_bol (void) { - int i; - if (gfc_at_eof ()) return 1; - i = gfc_current_file->loc.line; - - return gfc_current_file->loc.nextc == gfc_current_file->loc.lp->line[i]; + return (gfc_current_locus.nextc == gfc_current_locus.lb->line); } @@ -272,40 +490,97 @@ gfc_at_bol (void) int gfc_at_eol (void) { - if (gfc_at_eof ()) return 1; - return *gfc_current_file->loc.nextc == '\0'; + return (*gfc_current_locus.nextc == '\0'); +} + +static void +add_file_change (const char *filename, int line) +{ + if (file_changes_count == file_changes_allocated) + { + if (file_changes_allocated) + file_changes_allocated *= 2; + else + file_changes_allocated = 16; + file_changes = XRESIZEVEC (struct gfc_file_change, file_changes, + file_changes_allocated); + } + file_changes[file_changes_count].filename = filename; + file_changes[file_changes_count].lb = NULL; + file_changes[file_changes_count++].line = line; +} + +static void +report_file_change (gfc_linebuf *lb) +{ + size_t c = file_changes_cur; + while (c < file_changes_count + && file_changes[c].lb == lb) + { + if (file_changes[c].filename) + (*debug_hooks->start_source_file) (file_changes[c].line, + file_changes[c].filename); + else + (*debug_hooks->end_source_file) (file_changes[c].line); + ++c; + } + file_changes_cur = c; +} + +void +gfc_start_source_files (void) +{ + /* If the debugger wants the name of the main source file, + we give it. */ + if (debug_hooks->start_end_main_source_file) + (*debug_hooks->start_source_file) (0, gfc_source_file); + + file_changes_cur = 0; + report_file_change (gfc_current_locus.lb); } +void +gfc_end_source_files (void) +{ + report_file_change (NULL); + + if (debug_hooks->start_end_main_source_file) + (*debug_hooks->end_source_file) (0); +} /* Advance the current line pointer to the next line. */ void gfc_advance_line (void) { - locus *locp; - linebuf *lp; - if (gfc_at_end ()) return; - locp = &gfc_current_file->loc; - lp = locp->lp; - if (lp == NULL) - return; - - if (++locp->line >= lp->lines) + if (gfc_current_locus.lb == NULL) { - locp->lp = lp = lp->next; - if (lp == NULL) - return; /* End of this file */ + end_flag = 1; + return; + } - locp->line = 0; + if (gfc_current_locus.lb->next + && !gfc_current_locus.lb->next->dbg_emitted) + { + report_file_change (gfc_current_locus.lb->next); + gfc_current_locus.lb->next->dbg_emitted = true; } - locp->nextc = lp->line[locp->line]; + gfc_current_locus.lb = gfc_current_locus.lb->next; + + if (gfc_current_locus.lb != NULL) + gfc_current_locus.nextc = gfc_current_locus.lb->line; + else + { + gfc_current_locus.nextc = NULL; + end_flag = 1; + } } @@ -318,33 +593,18 @@ gfc_advance_line (void) pointer from being on the wrong line if the current statement ends prematurely. */ -static int +static gfc_char_t next_char (void) { - locus *locp; - int c; - - /* End the current include level, but not if we're in the middle - of processing a continuation. */ - if (gfc_at_eof ()) - { - if (continue_flag != 0 || gfc_at_end ()) - return '\n'; - - if (gfc_current_file->included_by == NULL) - end_flag = 1; - - return '\n'; - } - - locp = &gfc_current_file->loc; - if (locp->nextc == NULL) + gfc_char_t c; + + if (gfc_current_locus.nextc == NULL) return '\n'; - c = *locp->nextc++; + c = *gfc_current_locus.nextc++; if (c == '\0') { - locp->nextc--; /* Stay stuck on this line */ + gfc_current_locus.nextc--; /* Remain on this line. */ c = '\n'; } @@ -352,112 +612,107 @@ next_char (void) } -/* Checks the current line buffer to see if it is an include line. If - so, we load the new file and prepare to read from it. Include - lines happen at a lower level than regular parsing because the - string-matching subroutine is far simpler than the normal one. - - We never return a syntax error because a statement like "include = 5" - is perfectly legal. We return zero if no include was processed or - nonzero if we matched an include. */ +/* Skip a comment. When we come here the parse pointer is positioned + immediately after the comment character. If we ever implement + compiler directives within comments, here is where we parse the + directive. */ -int -gfc_check_include (void) +static void +skip_comment_line (void) { - char c, quote, path[PATH_MAX + 1]; - const char *include; - locus start; - int i; + gfc_char_t c; - include = "include"; + do + { + c = next_char (); + } + while (c != '\n'); - start = *gfc_current_locus (); - gfc_gobble_whitespace (); + gfc_advance_line (); +} - /* Match the 'include' */ - while (*include != '\0') - if (*include++ != gfc_next_char ()) - goto no_include; - gfc_gobble_whitespace (); +int +gfc_define_undef_line (void) +{ + char *tmp; - quote = next_char (); - if (quote != '"' && quote != '\'') - goto no_include; + /* All lines beginning with '#' are either #define or #undef. */ + if (debug_info_level != DINFO_LEVEL_VERBOSE || gfc_peek_ascii_char () != '#') + return 0; - /* Copy the filename */ - for (i = 0;;) + if (wide_strncmp (gfc_current_locus.nextc, "#define ", 8) == 0) { - c = next_char (); - if (c == '\n') - goto no_include; /* No close quote */ - if (c == quote) - break; - - /* This shouldn't happen-- PATH_MAX should be way longer than the - max line length. */ - - if (i >= PATH_MAX) - gfc_internal_error ("Pathname of include file is too long at %C"); - - path[i++] = c; + tmp = gfc_widechar_to_char (&gfc_current_locus.nextc[8], -1); + (*debug_hooks->define) (gfc_linebuf_linenum (gfc_current_locus.lb), + tmp); + gfc_free (tmp); } - path[i] = '\0'; - if (i == 0) - goto no_include; /* No filename! */ + if (wide_strncmp (gfc_current_locus.nextc, "#undef ", 7) == 0) + { + tmp = gfc_widechar_to_char (&gfc_current_locus.nextc[7], -1); + (*debug_hooks->undef) (gfc_linebuf_linenum (gfc_current_locus.lb), + tmp); + gfc_free (tmp); + } - /* At this point, we've got a filename to be included. The rest - of the include line is ignored */ + /* Skip the rest of the line. */ + skip_comment_line (); - gfc_new_file (path, gfc_current_file->form); return 1; - -no_include: - gfc_set_locus (&start); - return 0; } -/* Skip a comment. When we come here the parse pointer is positioned - immediately after the comment character. If we ever implement - compiler directives withing comments, here is where we parse the - directive. */ - -static void -skip_comment_line (void) +/* Return true if GCC$ was matched. */ +static bool +skip_gcc_attribute (locus start) { + bool r = false; char c; + locus old_loc = gfc_current_locus; - do - { - c = next_char (); - } - while (c != '\n'); + if ((c = next_char ()) == 'g' || c == 'G') + if ((c = next_char ()) == 'c' || c == 'C') + if ((c = next_char ()) == 'c' || c == 'C') + if ((c = next_char ()) == '$') + r = true; - gfc_advance_line (); + if (r == false) + gfc_current_locus = old_loc; + else + { + gcc_attribute_flag = 1; + gcc_attribute_locus = old_loc; + gfc_current_locus = start; + } + + return r; } + /* Comment lines are null lines, lines containing only blanks or lines - on which the first nonblank line is a '!'. */ + on which the first nonblank line is a '!'. + Return true if !$ openmp conditional compilation sentinel was + seen. */ -static void +static bool skip_free_comments (void) { locus start; - char c; + gfc_char_t c; + int at_bol; for (;;) { - start = *gfc_current_locus (); + at_bol = gfc_at_bol (); + start = gfc_current_locus; if (gfc_at_eof ()) break; do - { - c = next_char (); - } + c = next_char (); while (gfc_is_whitespace (c)); if (c == '\n') @@ -468,6 +723,59 @@ skip_free_comments (void) if (c == '!') { + /* Keep the !GCC$ line. */ + if (at_bol && skip_gcc_attribute (start)) + return false; + + /* If -fopenmp, we need to handle here 2 things: + 1) don't treat !$omp as comments, but directives + 2) handle OpenMP conditional compilation, where + !$ should be treated as 2 spaces (for initial lines + only if followed by space). */ + if (gfc_option.flag_openmp && at_bol) + { + locus old_loc = gfc_current_locus; + if (next_char () == '$') + { + c = next_char (); + if (c == 'o' || c == 'O') + { + if (((c = next_char ()) == 'm' || c == 'M') + && ((c = next_char ()) == 'p' || c == 'P')) + { + if ((c = next_char ()) == ' ' || c == '\t' + || continue_flag) + { + while (gfc_is_whitespace (c)) + c = next_char (); + if (c != '\n' && c != '!') + { + openmp_flag = 1; + openmp_locus = old_loc; + gfc_current_locus = start; + return false; + } + } + else + gfc_warning_now ("!$OMP at %C starts a commented " + "line as it neither is followed " + "by a space nor is a " + "continuation line"); + } + gfc_current_locus = old_loc; + next_char (); + c = next_char (); + } + if (continue_flag || c == ' ' || c == '\t') + { + gfc_current_locus = old_loc; + next_char (); + openmp_flag = 0; + return true; + } + } + gfc_current_locus = old_loc; + } skip_comment_line (); continue; } @@ -475,24 +783,52 @@ skip_free_comments (void) break; } - gfc_set_locus (&start); + if (openmp_flag && at_bol) + openmp_flag = 0; + + gcc_attribute_flag = 0; + gfc_current_locus = start; + return false; } /* Skip comment lines in fixed source mode. We have the same rules as in skip_free_comment(), except that we can have a 'c', 'C' or '*' - in column 1. and a '!' cannot be in* column 6. */ + in column 1, and a '!' cannot be in column 6. Also, we deal with + lines with 'd' or 'D' in column 1, if the user requested this. */ static void skip_fixed_comments (void) { locus start; int col; - char c; + gfc_char_t c; + + if (! gfc_at_bol ()) + { + start = gfc_current_locus; + if (! gfc_at_eof ()) + { + do + c = next_char (); + while (gfc_is_whitespace (c)); + + if (c == '\n') + gfc_advance_line (); + else if (c == '!') + skip_comment_line (); + } + + if (! gfc_at_bol ()) + { + gfc_current_locus = start; + return; + } + } for (;;) { - start = *gfc_current_locus (); + start = gfc_current_locus; if (gfc_at_eof ()) break; @@ -505,17 +841,104 @@ skip_fixed_comments (void) if (c == '!' || c == 'c' || c == 'C' || c == '*') { + if (skip_gcc_attribute (start)) + { + /* Canonicalize to *$omp. */ + *start.nextc = '*'; + return; + } + + /* If -fopenmp, we need to handle here 2 things: + 1) don't treat !$omp|c$omp|*$omp as comments, but directives + 2) handle OpenMP conditional compilation, where + !$|c$|*$ should be treated as 2 spaces if the characters + in columns 3 to 6 are valid fixed form label columns + characters. */ + if (gfc_current_locus.lb != NULL + && continue_line < gfc_linebuf_linenum (gfc_current_locus.lb)) + continue_line = gfc_linebuf_linenum (gfc_current_locus.lb); + + if (gfc_option.flag_openmp) + { + if (next_char () == '$') + { + c = next_char (); + if (c == 'o' || c == 'O') + { + if (((c = next_char ()) == 'm' || c == 'M') + && ((c = next_char ()) == 'p' || c == 'P')) + { + c = next_char (); + if (c != '\n' + && ((openmp_flag && continue_flag) + || c == ' ' || c == '\t' || c == '0')) + { + do + c = next_char (); + while (gfc_is_whitespace (c)); + if (c != '\n' && c != '!') + { + /* Canonicalize to *$omp. */ + *start.nextc = '*'; + openmp_flag = 1; + gfc_current_locus = start; + return; + } + } + } + } + else + { + int digit_seen = 0; + + for (col = 3; col < 6; col++, c = next_char ()) + if (c == ' ') + continue; + else if (c == '\t') + { + col = 6; + break; + } + else if (c < '0' || c > '9') + break; + else + digit_seen = 1; + + if (col == 6 && c != '\n' + && ((continue_flag && !digit_seen) + || c == ' ' || c == '\t' || c == '0')) + { + gfc_current_locus = start; + start.nextc[0] = ' '; + start.nextc[1] = ' '; + continue; + } + } + } + gfc_current_locus = start; + } skip_comment_line (); continue; } - col = 1; - do + if (gfc_option.flag_d_lines != -1 && (c == 'd' || c == 'D')) { - c = next_char (); - col++; + if (gfc_option.flag_d_lines == 0) + { + skip_comment_line (); + continue; + } + else + *start.nextc = c = ' '; + } + + col = 1; + + while (gfc_is_whitespace (c)) + { + c = next_char (); + col++; } - while (gfc_is_whitespace (c)); if (c == '\n') { @@ -525,6 +948,9 @@ skip_fixed_comments (void) if (col != 6 && c == '!') { + if (gfc_current_locus.lb != NULL + && continue_line < gfc_linebuf_linenum (gfc_current_locus.lb)) + continue_line = gfc_linebuf_linenum (gfc_current_locus.lb); skip_comment_line (); continue; } @@ -532,18 +958,18 @@ skip_fixed_comments (void) break; } - gfc_set_locus (&start); + openmp_flag = 0; + gcc_attribute_flag = 0; + gfc_current_locus = start; } -/* Skips the current line if it is a comment. Assumes that we are at - the start of the current line. */ +/* Skips the current line if it is a comment. */ void gfc_skip_comments (void) { - - if (!gfc_at_bol () || gfc_current_file->form == FORM_FREE) + if (gfc_current_form == FORM_FREE) skip_free_comments (); else skip_fixed_comments (); @@ -557,24 +983,39 @@ gfc_skip_comments (void) line. The in_string flag denotes whether we're inside a character context or not. */ -int +gfc_char_t gfc_next_char_literal (int in_string) { locus old_loc; - int i, c; + int i, prev_openmp_flag; + gfc_char_t c; continue_flag = 0; restart: c = next_char (); if (gfc_at_end ()) - return c; + { + continue_count = 0; + return c; + } - if (gfc_current_file->form == FORM_FREE) + if (gfc_current_form == FORM_FREE) { + bool openmp_cond_flag; if (!in_string && c == '!') { + if (gcc_attribute_flag + && memcmp (&gfc_current_locus, &gcc_attribute_locus, + sizeof (gfc_current_locus)) == 0) + goto done; + + if (openmp_flag + && memcmp (&gfc_current_locus, &openmp_locus, + sizeof (gfc_current_locus)) == 0) + goto done; + /* This line can't be continued */ do { @@ -582,6 +1023,9 @@ restart: } while (c != '\n'); + /* Avoid truncation warnings for comment ending lines. */ + gfc_current_locus.lb->truncated = 0; + goto done; } @@ -589,56 +1033,124 @@ restart: goto done; /* If the next nonblank character is a ! or \n, we've got a - continuation line. */ - old_loc = gfc_current_file->loc; + continuation line. */ + old_loc = gfc_current_locus; c = next_char (); while (gfc_is_whitespace (c)) c = next_char (); /* Character constants to be continued cannot have commentary - after the '&'. */ + after the '&'. */ if (in_string && c != '\n') { - gfc_set_locus (&old_loc); + gfc_current_locus = old_loc; c = '&'; goto done; } if (c != '!' && c != '\n') { - gfc_set_locus (&old_loc); + gfc_current_locus = old_loc; c = '&'; goto done; } + prev_openmp_flag = openmp_flag; continue_flag = 1; if (c == '!') skip_comment_line (); else gfc_advance_line (); + + if (gfc_at_eof()) + goto not_continuation; + + /* We've got a continuation line. If we are on the very next line after + the last continuation, increment the continuation line count and + check whether the limit has been exceeded. */ + if (gfc_linebuf_linenum (gfc_current_locus.lb) == continue_line + 1) + { + if (++continue_count == gfc_option.max_continue_free) + { + if (gfc_notification_std (GFC_STD_GNU) || pedantic) + gfc_warning ("Limit of %d continuations exceeded in " + "statement at %C", gfc_option.max_continue_free); + } + } + + /* Check to see if the continuation line was truncated. */ + if (gfc_option.warn_line_truncation && gfc_current_locus.lb != NULL + && gfc_current_locus.lb->truncated) + { + int maxlen = gfc_option.free_line_length; + gfc_current_locus.lb->truncated = 0; + gfc_current_locus.nextc += maxlen; + gfc_warning_now ("Line truncated at %L", &gfc_current_locus); + gfc_current_locus.nextc -= maxlen; + } + + /* Now find where it continues. First eat any comment lines. */ + openmp_cond_flag = skip_free_comments (); - /* We've got a continuation line and need to find where it continues. - First eat any comment lines. */ - gfc_skip_comments (); + if (gfc_current_locus.lb != NULL + && continue_line < gfc_linebuf_linenum (gfc_current_locus.lb)) + continue_line = gfc_linebuf_linenum (gfc_current_locus.lb); + + if (prev_openmp_flag != openmp_flag) + { + gfc_current_locus = old_loc; + openmp_flag = prev_openmp_flag; + c = '&'; + goto done; + } /* Now that we have a non-comment line, probe ahead for the - first non-whitespace character. If it is another '&', then - reading starts at the next character, otherwise we must back - up to where the whitespace started and resume from there. */ + first non-whitespace character. If it is another '&', then + reading starts at the next character, otherwise we must back + up to where the whitespace started and resume from there. */ - old_loc = *gfc_current_locus (); + old_loc = gfc_current_locus; c = next_char (); while (gfc_is_whitespace (c)) c = next_char (); - if (c != '&') - gfc_set_locus (&old_loc); + if (openmp_flag) + { + for (i = 0; i < 5; i++, c = next_char ()) + { + gcc_assert (gfc_wide_tolower (c) == (unsigned char) "!$omp"[i]); + if (i == 4) + old_loc = gfc_current_locus; + } + while (gfc_is_whitespace (c)) + c = next_char (); + } + if (c != '&') + { + if (in_string) + { + if (gfc_option.warn_ampersand) + gfc_warning_now ("Missing '&' in continued character " + "constant at %C"); + gfc_current_locus.nextc--; + } + /* Both !$omp and !$ -fopenmp continuation lines have & on the + continuation line only optionally. */ + else if (openmp_flag || openmp_cond_flag) + gfc_current_locus.nextc--; + else + { + c = ' '; + gfc_current_locus = old_loc; + goto done; + } + } } - else + else /* Fixed form. */ { /* Fixed form continuation. */ if (!in_string && c == '!') @@ -649,28 +1161,72 @@ restart: c = next_char (); } while (c != '\n'); + + /* Avoid truncation warnings for comment ending lines. */ + gfc_current_locus.lb->truncated = 0; } if (c != '\n') goto done; + /* Check to see if the continuation line was truncated. */ + if (gfc_option.warn_line_truncation && gfc_current_locus.lb != NULL + && gfc_current_locus.lb->truncated) + { + gfc_current_locus.lb->truncated = 0; + gfc_warning_now ("Line truncated at %L", &gfc_current_locus); + } + + prev_openmp_flag = openmp_flag; continue_flag = 1; - old_loc = *gfc_current_locus (); + old_loc = gfc_current_locus; gfc_advance_line (); - gfc_skip_comments (); + skip_fixed_comments (); /* See if this line is a continuation line. */ - for (i = 0; i < 5; i++) + if (openmp_flag != prev_openmp_flag) { - c = next_char (); - if (c != ' ') - goto not_continuation; + openmp_flag = prev_openmp_flag; + goto not_continuation; } + if (!openmp_flag) + for (i = 0; i < 5; i++) + { + c = next_char (); + if (c != ' ') + goto not_continuation; + } + else + for (i = 0; i < 5; i++) + { + c = next_char (); + if (gfc_wide_tolower (c) != (unsigned char) "*$omp"[i]) + goto not_continuation; + } + c = next_char (); - if (c == '0' || c == ' ') + if (c == '0' || c == ' ' || c == '\n') goto not_continuation; + + /* We've got a continuation line. If we are on the very next line after + the last continuation, increment the continuation line count and + check whether the limit has been exceeded. */ + if (gfc_linebuf_linenum (gfc_current_locus.lb) == continue_line + 1) + { + if (++continue_count == gfc_option.max_continue_fixed) + { + if (gfc_notification_std (GFC_STD_GNU) || pedantic) + gfc_warning ("Limit of %d continuations exceeded in " + "statement at %C", + gfc_option.max_continue_fixed); + } + } + + if (gfc_current_locus.lb != NULL + && continue_line < gfc_linebuf_linenum (gfc_current_locus.lb)) + continue_line = gfc_linebuf_linenum (gfc_current_locus.lb); } /* Ready to read first character of continuation line, which might @@ -679,9 +1235,11 @@ restart: not_continuation: c = '\n'; - gfc_set_locus (&old_loc); + gfc_current_locus = old_loc; done: + if (c == '\n') + continue_count = 0; continue_flag = 0; return c; } @@ -692,35 +1250,54 @@ done: parsing character literals, they have to call gfc_next_char_literal(). */ -int +gfc_char_t gfc_next_char (void) { - int c; + gfc_char_t c; do { c = gfc_next_char_literal (0); } - while (gfc_current_file->form == FORM_FIXED && gfc_is_whitespace (c)); + while (gfc_current_form == FORM_FIXED && gfc_is_whitespace (c)); - return TOLOWER (c); + return gfc_wide_tolower (c); } +char +gfc_next_ascii_char (void) +{ + gfc_char_t c = gfc_next_char (); -int + return (gfc_wide_fits_in_byte (c) ? (unsigned char) c + : (unsigned char) UCHAR_MAX); +} + + +gfc_char_t gfc_peek_char (void) { locus old_loc; - int c; + gfc_char_t c; - old_loc = *gfc_current_locus (); + old_loc = gfc_current_locus; c = gfc_next_char (); - gfc_set_locus (&old_loc); + gfc_current_locus = old_loc; return c; } +char +gfc_peek_ascii_char (void) +{ + gfc_char_t c = gfc_peek_char (); + + return (gfc_wide_fits_in_byte (c) ? (unsigned char) c + : (unsigned char) UCHAR_MAX); +} + + /* Recover from an error. We try to get past the current statement and get lined up for the next. The next statement follows a '\n' or a ';'. We also assume that we are not within a character @@ -729,7 +1306,7 @@ gfc_peek_char (void) void gfc_error_recovery (void) { - char c, delim; + gfc_char_t c, delim; if (gfc_at_eof ()) return; @@ -755,21 +1332,17 @@ gfc_error_recovery (void) if (c == delim) break; if (c == '\n') - goto done; + return; if (c == '\\') { c = next_char (); if (c == '\n') - goto done; + return; } } if (gfc_at_eof ()) break; } - -done: - if (c == '\n') - gfc_advance_line (); } @@ -778,296 +1351,796 @@ done: void gfc_gobble_whitespace (void) { + static int linenum = 0; locus old_loc; - int c; + gfc_char_t c; do { - old_loc = *gfc_current_locus (); + old_loc = gfc_current_locus; c = gfc_next_char_literal (0); + /* Issue a warning for nonconforming tabs. We keep track of the line + number because the Fortran matchers will often back up and the same + line will be scanned multiple times. */ + if (!gfc_option.warn_tabs && c == '\t') + { + int cur_linenum = LOCATION_LINE (gfc_current_locus.lb->location); + if (cur_linenum != linenum) + { + linenum = cur_linenum; + gfc_warning_now ("Nonconforming tab character at %C"); + } + } } while (gfc_is_whitespace (c)); - gfc_set_locus (&old_loc); + gfc_current_locus = old_loc; } -/* Load a single line into the buffer. We truncate lines that are too - long. In fixed mode, we expand a tab that occurs within the - statement label region to expand to spaces that leave the next - character in the source region. */ +/* Load a single line into pbuf. -static void -load_line (FILE * input, gfc_source_form form, char *buffer, - char *filename, int linenum) + If pbuf points to a NULL pointer, it is allocated. + We truncate lines that are too long, unless we're dealing with + preprocessor lines or if the option -ffixed-line-length-none is set, + in which case we reallocate the buffer to fit the entire line, if + need be. + In fixed mode, we expand a tab that occurs within the statement + label region to expand to spaces that leave the next character in + the source region. + + If first_char is not NULL, it's a pointer to a single char value holding + the first character of the line, which has already been read by the + caller. This avoids the use of ungetc(). + + load_line returns whether the line was truncated. + + NOTE: The error machinery isn't available at this point, so we can't + easily report line and column numbers consistent with other + parts of gfortran. */ + +static int +load_line (FILE *input, gfc_char_t **pbuf, int *pbuflen, const int *first_char) { - int c, maxlen, i, trunc_flag; + static int linenum = 0, current_line = 1; + int c, maxlen, i, preprocessor_flag, buflen = *pbuflen; + int trunc_flag = 0, seen_comment = 0; + int seen_printable = 0, seen_ampersand = 0; + gfc_char_t *buffer; + bool found_tab = false; + + /* Determine the maximum allowed line length. */ + if (gfc_current_form == FORM_FREE) + maxlen = gfc_option.free_line_length; + else if (gfc_current_form == FORM_FIXED) + maxlen = gfc_option.fixed_line_length; + else + maxlen = 72; - maxlen = (form == FORM_FREE) ? 132 : gfc_option.fixed_line_length; + if (*pbuf == NULL) + { + /* Allocate the line buffer, storing its length into buflen. + Note that if maxlen==0, indicating that arbitrary-length lines + are allowed, the buffer will be reallocated if this length is + insufficient; since 132 characters is the length of a standard + free-form line, we use that as a starting guess. */ + if (maxlen > 0) + buflen = maxlen; + else + buflen = 132; + + *pbuf = gfc_get_wide_string (buflen + 1); + } i = 0; + buffer = *pbuf; + + if (first_char) + c = *first_char; + else + c = getc (input); + + /* In order to not truncate preprocessor lines, we have to + remember that this is one. */ + preprocessor_flag = (c == '#' ? 1 : 0); for (;;) { - c = fgetc (input); - if (c == EOF) break; + if (c == '\n') - break; + { + /* Check for illegal use of ampersand. See F95 Standard 3.3.1.3. */ + if (gfc_current_form == FORM_FREE + && !seen_printable && seen_ampersand) + { + if (pedantic) + gfc_error_now ("'&' not allowed by itself in line %d", + current_line); + else + gfc_warning_now ("'&' not allowed by itself in line %d", + current_line); + } + break; + } - if (c == '\r') - continue; /* Gobble characters */ - if (c == '\0') - continue; + if (c == '\r' || c == '\0') + goto next_char; /* Gobble characters. */ + + if (c == '&') + { + if (seen_ampersand) + { + seen_ampersand = 0; + seen_printable = 1; + } + else + seen_ampersand = 1; + } + + if ((c != '&' && c != '!' && c != ' ') || (c == '!' && !seen_ampersand)) + seen_printable = 1; + + /* Is this a fixed-form comment? */ + if (gfc_current_form == FORM_FIXED && i == 0 + && (c == '*' || c == 'c' || c == 'd')) + seen_comment = 1; + + /* Vendor extension: "1" marks a continuation line. */ + if (found_tab) + { + found_tab = false; + if (c >= '1' && c <= '9') + { + *(buffer-1) = c; + goto next_char; + } + } + + if (gfc_current_form == FORM_FIXED && c == '\t' && i < 6) + { + found_tab = true; - if (form == FORM_FIXED && c == '\t' && i <= 6) - { /* Tab expandsion */ - while (i <= 6) + if (!gfc_option.warn_tabs && seen_comment == 0 + && current_line != linenum) + { + linenum = current_line; + gfc_warning_now ("Nonconforming tab character in column %d " + "of line %d", i+1, linenum); + } + + while (i < 6) { *buffer++ = ' '; i++; } - continue; + goto next_char; } *buffer++ = c; i++; - if (i >= maxlen) - { /* Truncate the rest of the line */ - trunc_flag = 1; - + if (maxlen == 0 || preprocessor_flag) + { + if (i >= buflen) + { + /* Reallocate line buffer to double size to hold the + overlong line. */ + buflen = buflen * 2; + *pbuf = XRESIZEVEC (gfc_char_t, *pbuf, (buflen + 1)); + buffer = (*pbuf) + i; + } + } + else if (i >= maxlen) + { + /* Truncate the rest of the line. */ for (;;) { - c = fgetc (input); + c = getc (input); + if (c == '\r') + continue; + if (c == '\n' || c == EOF) break; - if (gfc_option.warn_line_truncation - && trunc_flag - && !gfc_is_whitespace (c)) - { - gfc_warning_now ("Line %d of %s is being truncated", - linenum, filename); - trunc_flag = 0; - } + trunc_flag = 1; } - ungetc ('\n', input); + c = '\n'; + continue; } + +next_char: + c = getc (input); + } + + /* Pad lines to the selected line length in fixed form. */ + if (gfc_current_form == FORM_FIXED + && gfc_option.fixed_line_length != 0 + && !preprocessor_flag + && c != EOF) + { + while (i++ < maxlen) + *buffer++ = ' '; } *buffer = '\0'; + *pbuflen = buflen; + current_line++; + + return trunc_flag; } -/* Load a file into memory by calling load_line until the file ends. */ +/* Get a gfc_file structure, initialize it and add it to + the file stack. */ + +static gfc_file * +get_file (const char *name, enum lc_reason reason ATTRIBUTE_UNUSED) +{ + gfc_file *f; + + f = XCNEW (gfc_file); + + f->filename = xstrdup (name); + + f->next = file_head; + file_head = f; + + f->up = current_file; + if (current_file != NULL) + f->inclusion_line = current_file->line; + + linemap_add (line_table, reason, false, f->filename, 1); + + return f; +} + + +/* Deal with a line from the C preprocessor. The + initial octothorp has already been seen. */ static void -load_file (FILE * input, gfc_file * fp) +preprocessor_line (gfc_char_t *c) { - char *linep, line[GFC_MAX_LINE + 1]; - int len, linenum; - linebuf *lp; + bool flag[5]; + int i, line; + gfc_char_t *wide_filename; + gfc_file *f; + int escaped, unescape; + char *filename; - fp->start = lp = gfc_getmem (sizeof (linebuf)); + c++; + while (*c == ' ' || *c == '\t') + c++; - linenum = 1; - lp->lines = 0; - lp->start_line = 1; - lp->next = NULL; + if (*c < '0' || *c > '9') + goto bad_cpp_line; - linep = (char *) (lp + 1); + line = wide_atoi (c); - /* Load the file. */ - for (;;) + c = wide_strchr (c, ' '); + if (c == NULL) { - load_line (input, fp->form, line, fp->filename, linenum); - linenum++; + /* No file name given. Set new line number. */ + current_file->line = line; + return; + } - len = strlen (line); + /* Skip spaces. */ + while (*c == ' ' || *c == '\t') + c++; - if (feof (input) && len == 0) - break; + /* Skip quote. */ + if (*c != '"') + goto bad_cpp_line; + ++c; - /* See if we need another linebuf. */ - if (((char *) &lp->line[lp->lines + 2]) > linep - len - 1) + wide_filename = c; + + /* Make filename end at quote. */ + unescape = 0; + escaped = false; + while (*c && ! (!escaped && *c == '"')) + { + if (escaped) + escaped = false; + else if (*c == '\\') { - lp->next = gfc_getmem (sizeof (linebuf)); + escaped = true; + unescape++; + } + ++c; + } - lp->next->start_line = lp->start_line + lp->lines; - lp = lp->next; - lp->lines = 0; + if (! *c) + /* Preprocessor line has no closing quote. */ + goto bad_cpp_line; - linep = (char *) (lp + 1); - } + *c++ = '\0'; - linep = linep - len - 1; - lp->line[lp->lines++] = linep; - strcpy (linep, line); - } -} + /* Undo effects of cpp_quote_string. */ + if (unescape) + { + gfc_char_t *s = wide_filename; + gfc_char_t *d = gfc_get_wide_string (c - wide_filename - unescape); + wide_filename = d; + while (*s) + { + if (*s == '\\') + *d++ = *++s; + else + *d++ = *s; + s++; + } + *d = '\0'; + } -/* Determine the source form from the filename extension. We assume - case insensitivity. */ + /* Get flags. */ -static gfc_source_form -form_from_filename (const char *filename) -{ + flag[1] = flag[2] = flag[3] = flag[4] = false; - static const struct - { - const char *extension; - gfc_source_form form; - } - exttype[] = - { - { - ".f90", FORM_FREE} - , + for (;;) { - ".f95", FORM_FREE} - , + c = wide_strchr (c, ' '); + if (c == NULL) + break; + + c++; + i = wide_atoi (c); + + if (1 <= i && i <= 4) + flag[i] = true; + } + + /* Convert the filename in wide characters into a filename in narrow + characters. */ + filename = gfc_widechar_to_char (wide_filename, -1); + + /* Interpret flags. */ + + if (flag[1]) /* Starting new file. */ { - ".f", FORM_FIXED} - , + f = get_file (filename, LC_RENAME); + add_file_change (f->filename, f->inclusion_line); + current_file = f; + } + + if (flag[2]) /* Ending current file. */ { - ".for", FORM_FIXED} - , + if (!current_file->up + || strcmp (current_file->up->filename, filename) != 0) + { + gfc_warning_now ("%s:%d: file %s left but not entered", + current_file->filename, current_file->line, + filename); + if (unescape) + gfc_free (wide_filename); + gfc_free (filename); + return; + } + + add_file_change (NULL, line); + current_file = current_file->up; + linemap_add (line_table, LC_RENAME, false, current_file->filename, + current_file->line); + } + + /* The name of the file can be a temporary file produced by + cpp. Replace the name if it is different. */ + + if (strcmp (current_file->filename, filename) != 0) { - "", FORM_UNKNOWN} - }; /* sentinel value */ + /* FIXME: we leak the old filename because a pointer to it may be stored + in the linemap. Alternative could be using GC or updating linemap to + point to the new name, but there is no API for that currently. */ + current_file->filename = xstrdup (filename); + } - gfc_source_form f_form; - const char *fileext; - int i; + /* Set new line number. */ + current_file->line = line; + if (unescape) + gfc_free (wide_filename); + gfc_free (filename); + return; + + bad_cpp_line: + gfc_warning_now ("%s:%d: Illegal preprocessor directive", + current_file->filename, current_file->line); + current_file->line++; +} - /* Find end of file name. */ - i = 0; - while ((i < PATH_MAX) && (filename[i] != '\0')) - i++; - /* Improperly terminated or too-long filename. */ - if (i == PATH_MAX) - return FORM_UNKNOWN; +static gfc_try load_file (const char *, const char *, bool); - /* Find last period. */ - while (i >= 0 && (filename[i] != '.')) - i--; +/* include_line()-- Checks a line buffer to see if it is an include + line. If so, we call load_file() recursively to load the included + file. We never return a syntax error because a statement like + "include = 5" is perfectly legal. We return false if no include was + processed or true if we matched an include. */ - /* Did we see a file extension? */ - if (i < 0) - return FORM_UNKNOWN; /* Nope */ +static bool +include_line (gfc_char_t *line) +{ + gfc_char_t quote, *c, *begin, *stop; + char *filename; - /* Get file extension and compare it to others. */ - fileext = &(filename[i]); + c = line; - i = -1; - f_form = FORM_UNKNOWN; - do + if (gfc_option.flag_openmp) { - i++; - if (strcasecmp (fileext, exttype[i].extension) == 0) + if (gfc_current_form == FORM_FREE) { - f_form = exttype[i].form; - break; + while (*c == ' ' || *c == '\t') + c++; + if (*c == '!' && c[1] == '$' && (c[2] == ' ' || c[2] == '\t')) + c += 3; + } + else + { + if ((*c == '!' || *c == 'c' || *c == 'C' || *c == '*') + && c[1] == '$' && (c[2] == ' ' || c[2] == '\t')) + c += 3; } } - while (exttype[i].form != FORM_UNKNOWN); - return f_form; + while (*c == ' ' || *c == '\t') + c++; + + if (gfc_wide_strncasecmp (c, "include", 7)) + return false; + + c += 7; + while (*c == ' ' || *c == '\t') + c++; + + /* Find filename between quotes. */ + + quote = *c++; + if (quote != '"' && quote != '\'') + return false; + + begin = c; + + while (*c != quote && *c != '\0') + c++; + + if (*c == '\0') + return false; + + stop = c++; + + while (*c == ' ' || *c == '\t') + c++; + + if (*c != '\0' && *c != '!') + return false; + + /* We have an include line at this point. */ + + *stop = '\0'; /* It's ok to trash the buffer, as this line won't be + read by anything else. */ + + filename = gfc_widechar_to_char (begin, -1); + load_file (filename, NULL, false); + gfc_free (filename); + return true; } -/* Open a new file and start scanning from that file. Every new file - gets a gfc_file node, even if it is a duplicate file. Returns SUCCESS - if everything went OK, FAILURE otherwise. */ +/* Load a file into memory by calling load_line until the file ends. */ -try -gfc_new_file (const char *filename, gfc_source_form form) +static gfc_try +load_file (const char *realfilename, const char *displayedname, bool initial) { - gfc_file *fp, *fp2; + gfc_char_t *line; + gfc_linebuf *b; + gfc_file *f; FILE *input; - int len; + int len, line_len; + bool first_line; + const char *filename; - len = strlen (filename); - if (len > PATH_MAX) - { - gfc_error_now ("Filename '%s' is too long- ignoring it", filename); - return FAILURE; - } - - fp = gfc_getmem (sizeof (gfc_file)); + filename = displayedname ? displayedname : realfilename; - /* Make sure this file isn't being included recursively. */ - for (fp2 = gfc_current_file; fp2; fp2 = fp2->included_by) - if (strcmp (filename, fp2->filename) == 0) + for (f = current_file; f; f = f->up) + if (strcmp (filename, f->filename) == 0) { - gfc_error_now ("Recursive inclusion of file '%s' at %C- ignoring it", - filename); - gfc_free (fp); + fprintf (stderr, "%s:%d: Error: File '%s' is being included " + "recursively\n", current_file->filename, current_file->line, + filename); return FAILURE; } - /* See if the file has already been included. */ - for (fp2 = first_file; fp2; fp2 = fp2->next) - if (strcmp (filename, fp2->filename) == 0) - { - *fp = *fp2; - fp->next = first_duplicated_file; - first_duplicated_file = fp; - goto init_fp; - } + if (initial) + { + if (gfc_src_file) + { + input = gfc_src_file; + gfc_src_file = NULL; + } + else + input = gfc_open_file (realfilename); + if (input == NULL) + { + gfc_error_now ("Can't open file '%s'", filename); + return FAILURE; + } + } + else + { + input = gfc_open_included_file (realfilename, false, false); + if (input == NULL) + { + fprintf (stderr, "%s:%d: Error: Can't open included file '%s'\n", + current_file->filename, current_file->line, filename); + return FAILURE; + } + } - strcpy (fp->filename, filename); + /* Load the file. */ - if (gfc_current_file == NULL) - input = gfc_open_file (filename); - else - input = gfc_open_included_file (filename); + f = get_file (filename, initial ? LC_RENAME : LC_ENTER); + if (!initial) + add_file_change (f->filename, f->inclusion_line); + current_file = f; + current_file->line = 1; + line = NULL; + line_len = 0; + first_line = true; + + if (initial && gfc_src_preprocessor_lines[0]) + { + preprocessor_line (gfc_src_preprocessor_lines[0]); + gfc_free (gfc_src_preprocessor_lines[0]); + gfc_src_preprocessor_lines[0] = NULL; + if (gfc_src_preprocessor_lines[1]) + { + preprocessor_line (gfc_src_preprocessor_lines[1]); + gfc_free (gfc_src_preprocessor_lines[1]); + gfc_src_preprocessor_lines[1] = NULL; + } + } - if (input == NULL) + for (;;) { - if (gfc_current_file == NULL) - gfc_error_now ("Can't open file '%s'", filename); + int trunc = load_line (input, &line, &line_len, NULL); + + len = gfc_wide_strlen (line); + if (feof (input) && len == 0) + break; + + /* If this is the first line of the file, it can contain a byte + order mark (BOM), which we will ignore: + FF FE is UTF-16 little endian, + FE FF is UTF-16 big endian, + EF BB BF is UTF-8. */ + if (first_line + && ((line_len >= 2 && line[0] == (unsigned char) '\xFF' + && line[1] == (unsigned char) '\xFE') + || (line_len >= 2 && line[0] == (unsigned char) '\xFE' + && line[1] == (unsigned char) '\xFF') + || (line_len >= 3 && line[0] == (unsigned char) '\xEF' + && line[1] == (unsigned char) '\xBB' + && line[2] == (unsigned char) '\xBF'))) + { + int n = line[1] == (unsigned char) '\xBB' ? 3 : 2; + gfc_char_t *new_char = gfc_get_wide_string (line_len); + + wide_strcpy (new_char, &line[n]); + gfc_free (line); + line = new_char; + len -= n; + } + + /* There are three things this line can be: a line of Fortran + source, an include line or a C preprocessor directive. */ + + if (line[0] == '#') + { + /* When -g3 is specified, it's possible that we emit #define + and #undef lines, which we need to pass to the middle-end + so that it can emit correct debug info. */ + if (debug_info_level == DINFO_LEVEL_VERBOSE + && (wide_strncmp (line, "#define ", 8) == 0 + || wide_strncmp (line, "#undef ", 7) == 0)) + ; + else + { + preprocessor_line (line); + continue; + } + } + + /* Preprocessed files have preprocessor lines added before the byte + order mark, so first_line is not about the first line of the file + but the first line that's not a preprocessor line. */ + first_line = false; + + if (include_line (line)) + { + current_file->line++; + continue; + } + + /* Add line. */ + + b = (gfc_linebuf *) gfc_getmem (gfc_linebuf_header_size + + (len + 1) * sizeof (gfc_char_t)); + + b->location + = linemap_line_start (line_table, current_file->line++, 120); + b->file = current_file; + b->truncated = trunc; + wide_strcpy (b->line, line); + + if (line_head == NULL) + line_head = b; else - gfc_error_now ("Can't open file '%s' included at %C", filename); + line_tail->next = b; + + line_tail = b; - gfc_free (fp); - return FAILURE; + while (file_changes_cur < file_changes_count) + file_changes[file_changes_cur++].lb = b; } - /* Decide which form the file will be read in as. */ - if (form != FORM_UNKNOWN) - fp->form = form; - else + /* Release the line buffer allocated in load_line. */ + gfc_free (line); + + fclose (input); + + if (!initial) + add_file_change (NULL, current_file->inclusion_line + 1); + current_file = current_file->up; + linemap_add (line_table, LC_LEAVE, 0, NULL, 0); + return SUCCESS; +} + + +/* Open a new file and start scanning from that file. Returns SUCCESS + if everything went OK, FAILURE otherwise. If form == FORM_UNKNOWN + it tries to determine the source form from the filename, defaulting + to free form. */ + +gfc_try +gfc_new_file (void) +{ + gfc_try result; + + if (gfc_cpp_enabled ()) { - fp->form = form_from_filename (filename); + result = gfc_cpp_preprocess (gfc_source_file); + if (!gfc_cpp_preprocess_only ()) + result = load_file (gfc_cpp_temporary_file (), gfc_source_file, true); + } + else + result = load_file (gfc_source_file, NULL, true); + + gfc_current_locus.lb = line_head; + gfc_current_locus.nextc = (line_head == NULL) ? NULL : line_head->line; - if (fp->form == FORM_UNKNOWN) +#if 0 /* Debugging aid. */ + for (; line_head; line_head = line_head->next) + printf ("%s:%3d %s\n", LOCATION_FILE (line_head->location), + LOCATION_LINE (line_head->location), line_head->line); + + exit (0); +#endif + + return result; +} + +static char * +unescape_filename (const char *ptr) +{ + const char *p = ptr, *s; + char *d, *ret; + int escaped, unescape = 0; + + /* Make filename end at quote. */ + escaped = false; + while (*p && ! (! escaped && *p == '"')) + { + if (escaped) + escaped = false; + else if (*p == '\\') { - fp->form = FORM_FREE; - gfc_warning_now ("Reading file %s as free form", filename); + escaped = true; + unescape++; } + ++p; } - fp->next = first_file; - first_file = fp; + if (!*p || p[1]) + return NULL; - load_file (input, fp); - fclose (input); + /* Undo effects of cpp_quote_string. */ + s = ptr; + d = XCNEWVEC (char, p + 1 - ptr - unescape); + ret = d; -init_fp: - fp->included_by = gfc_current_file; - gfc_current_file = fp; + while (s != p) + { + if (*s == '\\') + *d++ = *++s; + else + *d++ = *s; + s++; + } + *d = '\0'; + return ret; +} - fp->loc.line = 0; - fp->loc.lp = fp->start; - fp->loc.nextc = fp->start->line[0]; - fp->loc.file = fp; +/* For preprocessed files, if the first tokens are of the form # NUM. + handle the directives so we know the original file name. */ - return SUCCESS; +const char * +gfc_read_orig_filename (const char *filename, const char **canon_source_file) +{ + int c, len; + char *dirname, *tmp; + + gfc_src_file = gfc_open_file (filename); + if (gfc_src_file == NULL) + return NULL; + + c = getc (gfc_src_file); + + if (c != '#') + return NULL; + + len = 0; + load_line (gfc_src_file, &gfc_src_preprocessor_lines[0], &len, &c); + + if (wide_strncmp (gfc_src_preprocessor_lines[0], "# 1 \"", 5) != 0) + return NULL; + + tmp = gfc_widechar_to_char (&gfc_src_preprocessor_lines[0][5], -1); + filename = unescape_filename (tmp); + gfc_free (tmp); + if (filename == NULL) + return NULL; + + c = getc (gfc_src_file); + + if (c != '#') + return filename; + + len = 0; + load_line (gfc_src_file, &gfc_src_preprocessor_lines[1], &len, &c); + + if (wide_strncmp (gfc_src_preprocessor_lines[1], "# 1 \"", 5) != 0) + return filename; + + tmp = gfc_widechar_to_char (&gfc_src_preprocessor_lines[1][5], -1); + dirname = unescape_filename (tmp); + gfc_free (tmp); + if (dirname == NULL) + return filename; + + len = strlen (dirname); + if (len < 3 || dirname[len - 1] != '/' || dirname[len - 2] != '/') + { + gfc_free (dirname); + return filename; + } + dirname[len - 2] = '\0'; + set_src_pwd (dirname); + + if (! IS_ABSOLUTE_PATH (filename)) + { + char *p = XCNEWVEC (char, len + strlen (filename)); + + memcpy (p, dirname, len - 2); + p[len - 2] = '/'; + strcpy (p + len - 1, filename); + *canon_source_file = p; + } + + gfc_free (dirname); + return filename; }