Discussion:
[PATCH] Fortran include line fixes and -fdec-include support
Jakub Jelinek
2018-11-12 14:51:16 UTC
Permalink
Hi!

In fortran97.pdf I read:
"Except in a character context, blanks are insignificant and may be used freely throughout the program."
and while we handle that in most cases, we don't allow spaces in INCLUDE
lines in fixed form, while e.g. ifort does.

Another thing, which I haven't touched in the PR except covering it with a
testcase is that we allow INLINE line in fixed form to start even in columns
1 to 6, while ifort rejects that. Is say
include 'omp_lib.h'
valid in fixed form? i in column 6 normally means a continuation line,
though not sure if anything can in a valid program contain nclude
followed by character literal. Shall we reject that, or at least warn that
it won't be portable?

The last thing, biggest part of the patch, is that for legacy DEC
compatibility, the DEC manuals document INCLUDE as a statement, not a line,
the
"An INCLUDE line is not a Fortran statement."
and
"An INCLUDE line shall appear on a single source line where a statement may appear; it shall be
the only nonblank text on this line other than an optional trailing comment. Thus, a statement
label is not allowed."
bullets don't apply, but instead there is:
"The INCLUDE statement takes one of the following forms:"
"An INCLUDE statement can appear anywhere within a scoping unit. The statement
can span more than one source line, but no other statement can appear on the same
line. The source line cannot be labeled."

This means there can be (as can be seen in the following testcases)
continuations in both forms, and in fixed form there can be 0 in column 6.

In order not to duplicate all the handling of continuations, comment
skipping etc., the patch just adjusts the include_line routine so that it
signals if the current line is a possible start of a valid INCLUDE statement
when in -fdec-include mode, and if so, whenever it reads a further line it
retries to parse it using
gfc_next_char/gfc_next_char_literal/gfc_gobble_whitespace APIs as an INCLUDE
stmt. If it is found not to be a valid INCLUDE statement line or set of
lines, it returns 0, if it is valid, it returns 1 together with load_file
like include_line does and clears all the lines containint the INCLUDE
statement. If the reading stops because we don't have enough lines, -1 is
returned and the caller tries again with more lines.

Tested on x86_64-linux, ok for trunk if it passes full bootstrap/regtest?

In addition to the above mentioned question about include in columns 1-6 in
fixed form, another thing is that we support
print *, 'abc''def'
print *, "hij""klm"
which prints abc'def and hij"klm. Shall we support that for INCLUDE lines
and INCLUDE statements too?

2018-11-12 Jakub Jelinek <***@redhat.com>
Mark Eggleston <***@codethink.com>

* lang.opt (fdec-include): New option.
* options.c (set_dec_flags): Set also flag_dec_include.
* scanner.c (include_line): Change return type from bool to int.
In fixed form allow spaces in between include keyword letters.
For -fdec-include, allow in fixed form 0 in column 6. With
-fdec-include return -1 if the parsed line is not full include
statement and it could be successfully completed on continuation
lines.
(include_stmt): New function.
(load_file): Adjust include_line caller. If it returns -1, keep
trying include_stmt until it stops returning -1 whenever adding
further line of input.

* gfortran.dg/include_10.f: New test.
* gfortran.dg/include_10.inc: New file.
* gfortran.dg/include_11.f: New test.
* gfortran.dg/include_12.f: New test.
* gfortran.dg/include_13.f90: New test.
* gfortran.dg/gomp/include_1.f: New test.
* gfortran.dg/gomp/include_1.inc: New file.
* gfortran.dg/gomp/include_2.f90: New test.

--- gcc/fortran/lang.opt.jj 2018-07-18 22:57:15.227785894 +0200
+++ gcc/fortran/lang.opt 2018-11-12 09:35:03.185259773 +0100
@@ -440,6 +440,10 @@ fdec
Fortran Var(flag_dec)
Enable all DEC language extensions.

+fdec-include
+Fortran Var(flag_dec_include)
+Enable legacy parsing of INCLUDE as statement.
+
fdec-intrinsic-ints
Fortran Var(flag_dec_intrinsic_ints)
Enable kind-specific variants of integer intrinsic functions.
--- gcc/fortran/options.c.jj 2018-11-06 18:27:13.828831733 +0100
+++ gcc/fortran/options.c 2018-11-12 09:35:39.515655453 +0100
@@ -68,6 +68,7 @@ set_dec_flags (int value)
flag_dec_intrinsic_ints |= value;
flag_dec_static |= value;
flag_dec_math |= value;
+ flag_dec_include |= value;
}


--- gcc/fortran/scanner.c.jj 2018-05-08 13:56:41.691932534 +0200
+++ gcc/fortran/scanner.c 2018-11-12 15:21:51.249391936 +0100
@@ -2135,14 +2135,18 @@ static bool load_file (const char *, con
/* 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. */
+ "include = 5" is perfectly legal. We return 0 if no include was
+ processed, 1 if we matched an include or -1 if include was
+ partially processed, but will need continuation lines. */

-static bool
+static int
include_line (gfc_char_t *line)
{
gfc_char_t quote, *c, *begin, *stop;
char *filename;
+ const char *include = "include";
+ bool allow_continuation = flag_dec_include;
+ int i;

c = line;

@@ -2158,42 +2162,133 @@ include_line (gfc_char_t *line)
else
{
if ((*c == '!' || *c == 'c' || *c == 'C' || *c == '*')
- && c[1] == '$' && (c[2] == ' ' || c[2] == '\t'))
+ && c[1] == '$' && c[2] == ' ')
c += 3;
}
}

- while (*c == ' ' || *c == '\t')
- c++;
+ if (gfc_current_form == FORM_FREE)
+ {
+ while (*c == ' ' || *c == '\t')
+ c++;
+ if (gfc_wide_strncasecmp (c, "include", 7))
+ {
+ if (!allow_continuation)
+ return 0;
+ for (i = 0; i < 7; ++i)
+ {
+ gfc_char_t c1 = gfc_wide_tolower (*c);
+ if (c1 != (unsigned char) include[i])
+ break;
+ c++;
+ }
+ if (i == 0 || *c != '&')
+ return 0;
+ c++;
+ while (*c == ' ' || *c == '\t')
+ c++;
+ if (*c == '\0' || *c == '!')
+ return -1;
+ return 0;
+ }
+
+ c += 7;
+ }
+ else
+ {
+ while (*c == ' ' || *c == '\t')
+ c++;
+ if (flag_dec_include && *c == '0' && c - line == 5)
+ {
+ c++;
+ while (*c == ' ' || *c == '\t')
+ c++;
+ }
+ if (c - line < 6)
+ allow_continuation = false;
+ for (i = 0; i < 7; ++i)
+ {
+ gfc_char_t c1 = gfc_wide_tolower (*c);
+ if (c1 != (unsigned char) include[i])
+ break;
+ c++;
+ while (*c == ' ' || *c == '\t')
+ c++;
+ }
+ if (!allow_continuation)
+ {
+ if (i != 7)
+ return 0;
+ }
+ else if (i != 7)
+ {
+ if (i == 0)
+ return 0;

- if (gfc_wide_strncasecmp (c, "include", 7))
- return false;
+ /* At the end of line or comment this might be continued. */
+ if (*c == '\0' || *c == '!')
+ return -1;
+
+ return 0;
+ }
+ }

- c += 7;
while (*c == ' ' || *c == '\t')
c++;

/* Find filename between quotes. */
-
+
quote = *c++;
if (quote != '"' && quote != '\'')
- return false;
+ {
+ if (allow_continuation)
+ {
+ if (gfc_current_form == FORM_FREE)
+ {
+ if (quote == '&')
+ {
+ while (*c == ' ' || *c == '\t')
+ c++;
+ if (*c == '\0' || *c == '!')
+ return -1;
+ }
+ }
+ else if (quote == '\0' || quote == '!')
+ return -1;
+ }
+ return 0;
+ }

begin = c;

+ bool cont = false;
while (*c != quote && *c != '\0')
- c++;
+ {
+ if (allow_continuation && gfc_current_form == FORM_FREE)
+ {
+ if (*c == '&')
+ cont = true;
+ else if (*c != ' ' && *c != '\t')
+ cont = false;
+ }
+ c++;
+ }

if (*c == '\0')
- return false;
+ {
+ if (allow_continuation
+ && (cont || gfc_current_form != FORM_FREE))
+ return -1;
+ return 0;
+ }

stop = c++;
-
+
while (*c == ' ' || *c == '\t')
c++;

if (*c != '\0' && *c != '!')
- return false;
+ return 0;

/* We have an include line at this point. */

@@ -2205,9 +2300,130 @@ include_line (gfc_char_t *line)
exit (FATAL_EXIT_CODE);

free (filename);
- return true;
+ return 1;
}

+/* Similarly, but try to parse an INCLUDE statement, using gfc_next_char etc.
+ APIs. Return 1 if recognized as valid INCLUDE statement and load_file has
+ been called, 0 if it is not a valid INCLUDE statement and -1 if eof has
+ been encountered while parsing it. */
+static int
+include_stmt (gfc_linebuf *b)
+{
+ int ret = 0, i, length;
+ const char *include = "include";
+ gfc_char_t c, quote = 0;
+ locus str_locus;
+ char *filename;
+
+ continue_flag = 0;
+ end_flag = 0;
+ gcc_attribute_flag = 0;
+ openmp_flag = 0;
+ openacc_flag = 0;
+ continue_count = 0;
+ continue_line = 0;
+ gfc_current_locus.lb = b;
+ gfc_current_locus.nextc = b->line;
+
+ gfc_skip_comments ();
+ gfc_gobble_whitespace ();
+
+ for (i = 0; i < 7; i++)
+ {
+ c = gfc_next_char ();
+ if (c != (unsigned char) include[i])
+ {
+ if (gfc_current_form == FORM_FIXED
+ && i == 0
+ && c == '0'
+ && gfc_current_locus.nextc == b->line + 6)
+ {
+ gfc_gobble_whitespace ();
+ i--;
+ continue;
+ }
+ gcc_assert (i != 0);
+ if (c == '\n')
+ {
+ gfc_advance_line ();
+ gfc_skip_comments ();
+ if (gfc_at_eof ())
+ ret = -1;
+ }
+ goto do_ret;
+ }
+ }
+ gfc_gobble_whitespace ();
+
+ c = gfc_next_char ();
+ if (c == '\'' || c == '"')
+ quote = c;
+ else
+ {
+ if (c == '\n')
+ {
+ gfc_advance_line ();
+ gfc_skip_comments ();
+ if (gfc_at_eof ())
+ ret = -1;
+ }
+ goto do_ret;
+ }
+
+ str_locus = gfc_current_locus;
+ length = 0;
+ do
+ {
+ c = gfc_next_char_literal (INSTRING_NOWARN);
+ if (c == quote)
+ break;
+ if (c == '\n')
+ {
+ gfc_advance_line ();
+ gfc_skip_comments ();
+ if (gfc_at_eof ())
+ ret = -1;
+ goto do_ret;
+ }
+ length++;
+ }
+ while (1);
+
+ gfc_gobble_whitespace ();
+ c = gfc_next_char ();
+ if (c != '\n')
+ goto do_ret;
+
+ gfc_current_locus = str_locus;
+ ret = 1;
+ filename = XNEWVEC (char, length + 1);
+ for (i = 0; i < length; i++)
+ {
+ c = gfc_next_char_literal (INSTRING_WARN);
+ gcc_assert (gfc_wide_fits_in_byte (c));
+ filename[i] = (unsigned char) c;
+ }
+ filename[length] = '\0';
+ if (!load_file (filename, NULL, false))
+ exit (FATAL_EXIT_CODE);
+
+ free (filename);
+
+do_ret:
+ continue_flag = 0;
+ end_flag = 0;
+ gcc_attribute_flag = 0;
+ openmp_flag = 0;
+ openacc_flag = 0;
+ continue_count = 0;
+ continue_line = 0;
+ memset (&gfc_current_locus, '\0', sizeof (locus));
+ memset (&openmp_locus, '\0', sizeof (locus));
+ memset (&openacc_locus, '\0', sizeof (locus));
+ memset (&gcc_attribute_locus, '\0', sizeof (locus));
+ return ret;
+}

/* Load a file into memory by calling load_line until the file ends. */

@@ -2215,7 +2431,7 @@ static bool
load_file (const char *realfilename, const char *displayedname, bool initial)
{
gfc_char_t *line;
- gfc_linebuf *b;
+ gfc_linebuf *b, *include_b = NULL;
gfc_file *f;
FILE *input;
int len, line_len;
@@ -2318,6 +2534,7 @@ load_file (const char *realfilename, con
for (;;)
{
int trunc = load_line (input, &line, &line_len, NULL);
+ int inc_line;

len = gfc_wide_strlen (line);
if (feof (input) && len == 0)
@@ -2366,11 +2583,12 @@ load_file (const char *realfilename, con
}

/* Preprocessed files have preprocessor lines added before the byte
- order mark, so first_line is not about the first line of the file
+ 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))
+ inc_line = include_line (line);
+ if (inc_line > 0)
{
current_file->line++;
continue;
@@ -2403,6 +2621,36 @@ load_file (const char *realfilename, con

while (file_changes_cur < file_changes_count)
file_changes[file_changes_cur++].lb = b;
+
+ if (flag_dec_include)
+ {
+ if (include_b && b != include_b)
+ {
+ int inc_line2 = include_stmt (include_b);
+ if (inc_line2 == 0)
+ include_b = NULL;
+ else if (inc_line2 > 0)
+ {
+ do
+ {
+ if (gfc_current_form == FORM_FIXED)
+ {
+ for (gfc_char_t *p = include_b->line; *p; p++)
+ *p = ' ';
+ }
+ else
+ include_b->line[0] = '\0';
+ if (include_b == b)
+ break;
+ include_b = include_b->next;
+ }
+ while (1);
+ include_b = NULL;
+ }
+ }
+ if (inc_line == -1 && !include_b)
+ include_b = b;
+ }
}

/* Release the line buffer allocated in load_line. */
--- gcc/testsuite/gfortran.dg/include_10.f.jj 2018-11-12 12:21:50.886637849 +0100
+++ gcc/testsuite/gfortran.dg/include_10.f 2018-11-12 12:10:22.115007990 +0100
@@ -0,0 +1,11 @@
+c { dg-do compile }
+ subroutine foo
+ implicit none
+ include 'include_10.inc'
+ i = 1
+ end subroutine foo
+ subroutine bar
+ implicit none
+ i n cl UD e'include_10.inc'
+ i = 1
+ end subroutine bar
--- gcc/testsuite/gfortran.dg/include_10.inc.jj 2018-11-12 12:21:53.946587330 +0100
+++ gcc/testsuite/gfortran.dg/include_10.inc 2018-11-12 12:07:29.694854414 +0100
@@ -0,0 +1 @@
+ integer i
--- gcc/testsuite/gfortran.dg/include_11.f.jj 2018-11-12 12:38:34.883081270 +0100
+++ gcc/testsuite/gfortran.dg/include_11.f 2018-11-12 12:38:28.239190805 +0100
@@ -0,0 +1,20 @@
+c { dg-do compile }
+ subroutine foo
+ implicit none
+c We used to accept following in fixed mode. Shall we at least
+c warn about it?
+include 'include_10.inc'
+ i = 1
+ end subroutine foo
+ subroutine bar
+c Likewise here.
+ implicit none
+ include'include_10.inc'
+ i = 1
+ end subroutine bar
+ subroutine baz
+c And here.
+ implicit none
+ include 'include_10.inc'
+ i = 1
+ end subroutine baz
--- gcc/testsuite/gfortran.dg/include_12.f.jj 2018-11-12 14:36:42.542803497 +0100
+++ gcc/testsuite/gfortran.dg/include_12.f 2018-11-12 14:45:36.201055286 +0100
@@ -0,0 +1,49 @@
+c { dg-do compile }
+c { dg-options "-fdec-include" }
+ subroutine foo
+ implicit none
+ 0include 'include_10.inc'
+ i = 1
+ end subroutine foo
+ subroutine bar
+ implicit none
+ i
+ ;n
+ +c
+
+c some comment
+
+ ll
+C comment line
+ uu
+ DD
+ ee'include_10.inc'
+ i = 1
+ end subroutine bar
+ subroutine baz
+ implicit none
+ 0include
+ + 'include_10.inc'
+ i = 1
+ end subroutine baz
+ subroutine qux
+ implicit none
+ i n C lude 'inc
+* another comment line
+ &lude_10.inc'
+ i = 1
+ end subroutine qux
+ subroutine quux
+ implicit none
+ 0inc
+ 1lud
+ 2e '
+ 3include_10.inc'
+ i = 1
+ end subroutine quux
+ program include_12
+ implicit none
+ include
+! comment
+ +'include_10.inc'
+ end program
--- gcc/testsuite/gfortran.dg/include_13.f90.jj 2018-11-12 14:38:05.488443863 +0100
+++ gcc/testsuite/gfortran.dg/include_13.f90 2018-11-12 15:04:42.921254180 +0100
@@ -0,0 +1,32 @@
+! { dg-do compile }
+! { dg-options "-fdec" }
+subroutine foo
+ implicit none
+ incl& ! comment1
+&u&
+ &de & ! comment2
+'include&
+ &_10.inc'
+ i = 1
+end subroutine foo
+subroutine bar
+ implicit none
+include &
+
+! comment3
+
+"include_10.inc"
+ i = 1
+end subroutine bar
+subroutine baz
+ implicit none
+ include&
+&'include_10.&
+&inc'
+ i = 1
+end subroutine baz
+subroutine qux
+ implicit none
+ include '&
+include_10.inc'
+end subroutine qux
--- gcc/testsuite/gfortran.dg/gomp/include_1.f.jj 2018-11-12 15:24:27.455829721 +0100
+++ gcc/testsuite/gfortran.dg/gomp/include_1.f 2018-11-12 15:26:02.044278080 +0100
@@ -0,0 +1,49 @@
+c { dg-do compile }
+c { dg-options "-fopenmp -fdec" }
+ subroutine foo
+ implicit none
+c$ 0include 'include_1.inc'
+ i = 1
+ end subroutine foo
+ subroutine bar
+ implicit none
+ i
+C$ ;n
+ +c
+
+c some comment
+
+*$ ll
+C comment line
+ uu
+ DD
+ ee'include_1.inc'
+ i = 1
+ end subroutine bar
+ subroutine baz
+ implicit none
+ 0include
+ + 'include_1.inc'
+ i = 1
+ end subroutine baz
+ subroutine qux
+ implicit none
+!$ i n C lude 'inc
+* another comment line
+ &lude_1.inc'
+ i = 1
+ end subroutine qux
+ subroutine quux
+ implicit none
+C$ 0inc
+*$ 1lud
+c$ 2e '
+!$ 3include_1.inc'
+ i = 1
+ end subroutine quux
+ program include_12
+ implicit none
+ include
+! comment
+c$ +'include_1.inc'
+ end program
--- gcc/testsuite/gfortran.dg/gomp/include_1.inc.jj 2018-11-12 15:24:30.471780253 +0100
+++ gcc/testsuite/gfortran.dg/gomp/include_1.inc 2018-11-12 12:07:29.694854414 +0100
@@ -0,0 +1 @@
+ integer i
--- gcc/testsuite/gfortran.dg/gomp/include_2.f90.jj 2018-11-12 15:24:33.556729656 +0100
+++ gcc/testsuite/gfortran.dg/gomp/include_2.f90 2018-11-12 15:26:10.480139412 +0100
@@ -0,0 +1,32 @@
+! { dg-do compile }
+! { dg-options "-fopenmp -fdec-include" }
+subroutine foo
+ implicit none
+!$ incl& ! comment1
+!$ &u&
+!$ &de & ! comment2
+!$ 'include&
+ &_1.inc'
+ i = 1
+end subroutine foo
+subroutine bar
+ implicit none
+!$ include &
+
+! comment3
+
+!$ "include_1.inc"
+ i = 1
+end subroutine bar
+subroutine baz
+ implicit none
+!$ include&
+!$ &'include_1.&
+!$ &inc'
+ i = 1
+end subroutine baz
+subroutine qux
+ implicit none
+!$ include '&
+include_1.inc'
+end subroutine qux

Jakub
Fritz Reese
2018-11-12 23:24:24 UTC
Permalink
Post by Jakub Jelinek
"Except in a character context, blanks are insignificant and may be used freely throughout the program."
and while we handle that in most cases, we don't allow spaces in INCLUDE
lines in fixed form, while e.g. ifort does.
I agree with fixing this unconditionally.
Post by Jakub Jelinek
Another thing, which I haven't touched in the PR except covering it with a
testcase is that we allow INLINE line in fixed form to start even in columns
1 to 6, while ifort rejects that. Is say
include 'omp_lib.h'
valid in fixed form? i in column 6 normally means a continuation line,
though not sure if anything can in a valid program contain nclude
followed by character literal. Shall we reject that, or at least warn that
it won't be portable?
AFAICT this is unambiguous so I would certainly suggest adding such a
warning (enabled by default).
Post by Jakub Jelinek
The last thing, biggest part of the patch, is that for legacy DEC
compatibility, the DEC manuals document INCLUDE as a statement, not a line,
[...]
This means there can be (as can be seen in the following testcases)
continuations in both forms, and in fixed form there can be 0 in column 6.
Makes sense to me. I concur with adding -fdec-include to support this
under -fdec.

If we are going to warn for the above and re-do the include matching
anyway, I wonder if we should have also a specific error message for a
labeled include statement? For example,

10 include 'include_10.inc'

Will result in the generic 'Unclassifiable statement' error, but ifort
gives "Label on INCLUDE is invalid."
Post by Jakub Jelinek
In order not to duplicate all the handling of continuations, comment
skipping etc., the patch just adjusts the include_line routine so that it
signals if the current line is a possible start of a valid INCLUDE statement
when in -fdec-include mode, and if so, whenever it reads a further line it
retries to parse it using
gfc_next_char/gfc_next_char_literal/gfc_gobble_whitespace APIs as an INCLUDE
stmt. If it is found not to be a valid INCLUDE statement line or set of
lines, it returns 0, if it is valid, it returns 1 together with load_file
like include_line does and clears all the lines containint the INCLUDE
statement. If the reading stops because we don't have enough lines, -1 is
returned and the caller tries again with more lines.
LGTM.
Post by Jakub Jelinek
In addition to the above mentioned question about include in columns 1-6 in
fixed form, another thing is that we support
print *, 'abc''def'
print *, "hij""klm"
which prints abc'def and hij"klm. Shall we support that for INCLUDE lines
and INCLUDE statements too?
It appears ifort does also support this. I see no reason not to, as
the feature should be straightforward to implement.
Post by Jakub Jelinek
Tested on x86_64-linux, ok for trunk if it passes full bootstrap/regtest?
With the above additions it looks ok to me, but I must defer to an
official Fortran reviewer.

---
Fritz
Mark Eggleston
2018-11-13 10:06:39 UTC
Permalink
Jakub,

I've applied this patch and tried it out.

The following fail to compile:

In free form with or without the optional initial & on the continuation
line:

subroutine one()
  include &
  &"include_4.inc'
  integer(i4) :: i
end subroutine one

In fixed form:

      subroutine one()
      integer include
      include
     +"include_16.inc'
      write (*,*) include
      end subroutine one

Both give "Error: Unclassifiable statement at (1)" with 1 at the start
of the include line.

These cases are not covered by the test cases in the patch.

Is there any particular reason that include line continuation is
isolated in -fdec-include? Is there a specific restriction regarding
include lines in the Fortran standard such that continuations in free
form and fixed form can't be used with include lines?

regards,

Mark Eggleston
Post by Jakub Jelinek
Hi!
"Except in a character context, blanks are insignificant and may be used freely throughout the program."
and while we handle that in most cases, we don't allow spaces in INCLUDE
lines in fixed form, while e.g. ifort does.
Another thing, which I haven't touched in the PR except covering it with a
testcase is that we allow INLINE line in fixed form to start even in columns
1 to 6, while ifort rejects that. Is say
include 'omp_lib.h'
valid in fixed form? i in column 6 normally means a continuation line,
though not sure if anything can in a valid program contain nclude
followed by character literal. Shall we reject that, or at least warn that
it won't be portable?
The last thing, biggest part of the patch, is that for legacy DEC
compatibility, the DEC manuals document INCLUDE as a statement, not a line,
the
"An INCLUDE line is not a Fortran statement."
and
"An INCLUDE line shall appear on a single source line where a statement may appear; it shall be
the only nonblank text on this line other than an optional trailing comment. Thus, a statement
label is not allowed."
"The INCLUDE statement takes one of the following forms:"
"An INCLUDE statement can appear anywhere within a scoping unit. The statement
can span more than one source line, but no other statement can appear on the same
line. The source line cannot be labeled."
This means there can be (as can be seen in the following testcases)
continuations in both forms, and in fixed form there can be 0 in column 6.
In order not to duplicate all the handling of continuations, comment
skipping etc., the patch just adjusts the include_line routine so that it
signals if the current line is a possible start of a valid INCLUDE statement
when in -fdec-include mode, and if so, whenever it reads a further line it
retries to parse it using
gfc_next_char/gfc_next_char_literal/gfc_gobble_whitespace APIs as an INCLUDE
stmt. If it is found not to be a valid INCLUDE statement line or set of
lines, it returns 0, if it is valid, it returns 1 together with load_file
like include_line does and clears all the lines containint the INCLUDE
statement. If the reading stops because we don't have enough lines, -1 is
returned and the caller tries again with more lines.
Tested on x86_64-linux, ok for trunk if it passes full bootstrap/regtest?
In addition to the above mentioned question about include in columns 1-6 in
fixed form, another thing is that we support
print *, 'abc''def'
print *, "hij""klm"
which prints abc'def and hij"klm. Shall we support that for INCLUDE lines
and INCLUDE statements too?
* lang.opt (fdec-include): New option.
* options.c (set_dec_flags): Set also flag_dec_include.
* scanner.c (include_line): Change return type from bool to int.
In fixed form allow spaces in between include keyword letters.
For -fdec-include, allow in fixed form 0 in column 6. With
-fdec-include return -1 if the parsed line is not full include
statement and it could be successfully completed on continuation
lines.
(include_stmt): New function.
(load_file): Adjust include_line caller. If it returns -1, keep
trying include_stmt until it stops returning -1 whenever adding
further line of input.
* gfortran.dg/include_10.f: New test.
* gfortran.dg/include_10.inc: New file.
* gfortran.dg/include_11.f: New test.
* gfortran.dg/include_12.f: New test.
* gfortran.dg/include_13.f90: New test.
* gfortran.dg/gomp/include_1.f: New test.
* gfortran.dg/gomp/include_1.inc: New file.
* gfortran.dg/gomp/include_2.f90: New test.
--- gcc/fortran/lang.opt.jj 2018-07-18 22:57:15.227785894 +0200
+++ gcc/fortran/lang.opt 2018-11-12 09:35:03.185259773 +0100
@@ -440,6 +440,10 @@ fdec
Fortran Var(flag_dec)
Enable all DEC language extensions.
+fdec-include
+Fortran Var(flag_dec_include)
+Enable legacy parsing of INCLUDE as statement.
+
fdec-intrinsic-ints
Fortran Var(flag_dec_intrinsic_ints)
Enable kind-specific variants of integer intrinsic functions.
--- gcc/fortran/options.c.jj 2018-11-06 18:27:13.828831733 +0100
+++ gcc/fortran/options.c 2018-11-12 09:35:39.515655453 +0100
@@ -68,6 +68,7 @@ set_dec_flags (int value)
flag_dec_intrinsic_ints |= value;
flag_dec_static |= value;
flag_dec_math |= value;
+ flag_dec_include |= value;
}
--- gcc/fortran/scanner.c.jj 2018-05-08 13:56:41.691932534 +0200
+++ gcc/fortran/scanner.c 2018-11-12 15:21:51.249391936 +0100
@@ -2135,14 +2135,18 @@ static bool load_file (const char *, con
/* 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. */
+ "include = 5" is perfectly legal. We return 0 if no include was
+ processed, 1 if we matched an include or -1 if include was
+ partially processed, but will need continuation lines. */
-static bool
+static int
include_line (gfc_char_t *line)
{
gfc_char_t quote, *c, *begin, *stop;
char *filename;
+ const char *include = "include";
+ bool allow_continuation = flag_dec_include;
+ int i;
c = line;
@@ -2158,42 +2162,133 @@ include_line (gfc_char_t *line)
else
{
if ((*c == '!' || *c == 'c' || *c == 'C' || *c == '*')
- && c[1] == '$' && (c[2] == ' ' || c[2] == '\t'))
+ && c[1] == '$' && c[2] == ' ')
c += 3;
}
}
- while (*c == ' ' || *c == '\t')
- c++;
+ if (gfc_current_form == FORM_FREE)
+ {
+ while (*c == ' ' || *c == '\t')
+ c++;
+ if (gfc_wide_strncasecmp (c, "include", 7))
+ {
+ if (!allow_continuation)
+ return 0;
+ for (i = 0; i < 7; ++i)
+ {
+ gfc_char_t c1 = gfc_wide_tolower (*c);
+ if (c1 != (unsigned char) include[i])
+ break;
+ c++;
+ }
+ if (i == 0 || *c != '&')
+ return 0;
+ c++;
+ while (*c == ' ' || *c == '\t')
+ c++;
+ if (*c == '\0' || *c == '!')
+ return -1;
+ return 0;
+ }
+
+ c += 7;
+ }
+ else
+ {
+ while (*c == ' ' || *c == '\t')
+ c++;
+ if (flag_dec_include && *c == '0' && c - line == 5)
+ {
+ c++;
+ while (*c == ' ' || *c == '\t')
+ c++;
+ }
+ if (c - line < 6)
+ allow_continuation = false;
+ for (i = 0; i < 7; ++i)
+ {
+ gfc_char_t c1 = gfc_wide_tolower (*c);
+ if (c1 != (unsigned char) include[i])
+ break;
+ c++;
+ while (*c == ' ' || *c == '\t')
+ c++;
+ }
+ if (!allow_continuation)
+ {
+ if (i != 7)
+ return 0;
+ }
+ else if (i != 7)
+ {
+ if (i == 0)
+ return 0;
- if (gfc_wide_strncasecmp (c, "include", 7))
- return false;
+ /* At the end of line or comment this might be continued. */
+ if (*c == '\0' || *c == '!')
+ return -1;
+
+ return 0;
+ }
+ }
- c += 7;
while (*c == ' ' || *c == '\t')
c++;
/* Find filename between quotes. */
-
+
quote = *c++;
if (quote != '"' && quote != '\'')
- return false;
+ {
+ if (allow_continuation)
+ {
+ if (gfc_current_form == FORM_FREE)
+ {
+ if (quote == '&')
+ {
+ while (*c == ' ' || *c == '\t')
+ c++;
+ if (*c == '\0' || *c == '!')
+ return -1;
+ }
+ }
+ else if (quote == '\0' || quote == '!')
+ return -1;
+ }
+ return 0;
+ }
begin = c;
+ bool cont = false;
while (*c != quote && *c != '\0')
- c++;
+ {
+ if (allow_continuation && gfc_current_form == FORM_FREE)
+ {
+ if (*c == '&')
+ cont = true;
+ else if (*c != ' ' && *c != '\t')
+ cont = false;
+ }
+ c++;
+ }
if (*c == '\0')
- return false;
+ {
+ if (allow_continuation
+ && (cont || gfc_current_form != FORM_FREE))
+ return -1;
+ return 0;
+ }
stop = c++;
-
+
while (*c == ' ' || *c == '\t')
c++;
if (*c != '\0' && *c != '!')
- return false;
+ return 0;
/* We have an include line at this point. */
@@ -2205,9 +2300,130 @@ include_line (gfc_char_t *line)
exit (FATAL_EXIT_CODE);
free (filename);
- return true;
+ return 1;
}
+/* Similarly, but try to parse an INCLUDE statement, using gfc_next_char etc.
+ APIs. Return 1 if recognized as valid INCLUDE statement and load_file has
+ been called, 0 if it is not a valid INCLUDE statement and -1 if eof has
+ been encountered while parsing it. */
+static int
+include_stmt (gfc_linebuf *b)
+{
+ int ret = 0, i, length;
+ const char *include = "include";
+ gfc_char_t c, quote = 0;
+ locus str_locus;
+ char *filename;
+
+ continue_flag = 0;
+ end_flag = 0;
+ gcc_attribute_flag = 0;
+ openmp_flag = 0;
+ openacc_flag = 0;
+ continue_count = 0;
+ continue_line = 0;
+ gfc_current_locus.lb = b;
+ gfc_current_locus.nextc = b->line;
+
+ gfc_skip_comments ();
+ gfc_gobble_whitespace ();
+
+ for (i = 0; i < 7; i++)
+ {
+ c = gfc_next_char ();
+ if (c != (unsigned char) include[i])
+ {
+ if (gfc_current_form == FORM_FIXED
+ && i == 0
+ && c == '0'
+ && gfc_current_locus.nextc == b->line + 6)
+ {
+ gfc_gobble_whitespace ();
+ i--;
+ continue;
+ }
+ gcc_assert (i != 0);
+ if (c == '\n')
+ {
+ gfc_advance_line ();
+ gfc_skip_comments ();
+ if (gfc_at_eof ())
+ ret = -1;
+ }
+ goto do_ret;
+ }
+ }
+ gfc_gobble_whitespace ();
+
+ c = gfc_next_char ();
+ if (c == '\'' || c == '"')
+ quote = c;
+ else
+ {
+ if (c == '\n')
+ {
+ gfc_advance_line ();
+ gfc_skip_comments ();
+ if (gfc_at_eof ())
+ ret = -1;
+ }
+ goto do_ret;
+ }
+
+ str_locus = gfc_current_locus;
+ length = 0;
+ do
+ {
+ c = gfc_next_char_literal (INSTRING_NOWARN);
+ if (c == quote)
+ break;
+ if (c == '\n')
+ {
+ gfc_advance_line ();
+ gfc_skip_comments ();
+ if (gfc_at_eof ())
+ ret = -1;
+ goto do_ret;
+ }
+ length++;
+ }
+ while (1);
+
+ gfc_gobble_whitespace ();
+ c = gfc_next_char ();
+ if (c != '\n')
+ goto do_ret;
+
+ gfc_current_locus = str_locus;
+ ret = 1;
+ filename = XNEWVEC (char, length + 1);
+ for (i = 0; i < length; i++)
+ {
+ c = gfc_next_char_literal (INSTRING_WARN);
+ gcc_assert (gfc_wide_fits_in_byte (c));
+ filename[i] = (unsigned char) c;
+ }
+ filename[length] = '\0';
+ if (!load_file (filename, NULL, false))
+ exit (FATAL_EXIT_CODE);
+
+ free (filename);
+
+ continue_flag = 0;
+ end_flag = 0;
+ gcc_attribute_flag = 0;
+ openmp_flag = 0;
+ openacc_flag = 0;
+ continue_count = 0;
+ continue_line = 0;
+ memset (&gfc_current_locus, '\0', sizeof (locus));
+ memset (&openmp_locus, '\0', sizeof (locus));
+ memset (&openacc_locus, '\0', sizeof (locus));
+ memset (&gcc_attribute_locus, '\0', sizeof (locus));
+ return ret;
+}
/* Load a file into memory by calling load_line until the file ends. */
@@ -2215,7 +2431,7 @@ static bool
load_file (const char *realfilename, const char *displayedname, bool initial)
{
gfc_char_t *line;
- gfc_linebuf *b;
+ gfc_linebuf *b, *include_b = NULL;
gfc_file *f;
FILE *input;
int len, line_len;
@@ -2318,6 +2534,7 @@ load_file (const char *realfilename, con
for (;;)
{
int trunc = load_line (input, &line, &line_len, NULL);
+ int inc_line;
len = gfc_wide_strlen (line);
if (feof (input) && len == 0)
@@ -2366,11 +2583,12 @@ load_file (const char *realfilename, con
}
/* Preprocessed files have preprocessor lines added before the byte
- order mark, so first_line is not about the first line of the file
+ 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))
+ inc_line = include_line (line);
+ if (inc_line > 0)
{
current_file->line++;
continue;
@@ -2403,6 +2621,36 @@ load_file (const char *realfilename, con
while (file_changes_cur < file_changes_count)
file_changes[file_changes_cur++].lb = b;
+
+ if (flag_dec_include)
+ {
+ if (include_b && b != include_b)
+ {
+ int inc_line2 = include_stmt (include_b);
+ if (inc_line2 == 0)
+ include_b = NULL;
+ else if (inc_line2 > 0)
+ {
+ do
+ {
+ if (gfc_current_form == FORM_FIXED)
+ {
+ for (gfc_char_t *p = include_b->line; *p; p++)
+ *p = ' ';
+ }
+ else
+ include_b->line[0] = '\0';
+ if (include_b == b)
+ break;
+ include_b = include_b->next;
+ }
+ while (1);
+ include_b = NULL;
+ }
+ }
+ if (inc_line == -1 && !include_b)
+ include_b = b;
+ }
}
/* Release the line buffer allocated in load_line. */
--- gcc/testsuite/gfortran.dg/include_10.f.jj 2018-11-12 12:21:50.886637849 +0100
+++ gcc/testsuite/gfortran.dg/include_10.f 2018-11-12 12:10:22.115007990 +0100
@@ -0,0 +1,11 @@
+c { dg-do compile }
+ subroutine foo
+ implicit none
+ include 'include_10.inc'
+ i = 1
+ end subroutine foo
+ subroutine bar
+ implicit none
+ i n cl UD e'include_10.inc'
+ i = 1
+ end subroutine bar
--- gcc/testsuite/gfortran.dg/include_10.inc.jj 2018-11-12 12:21:53.946587330 +0100
+++ gcc/testsuite/gfortran.dg/include_10.inc 2018-11-12 12:07:29.694854414 +0100
@@ -0,0 +1 @@
+ integer i
--- gcc/testsuite/gfortran.dg/include_11.f.jj 2018-11-12 12:38:34.883081270 +0100
+++ gcc/testsuite/gfortran.dg/include_11.f 2018-11-12 12:38:28.239190805 +0100
@@ -0,0 +1,20 @@
+c { dg-do compile }
+ subroutine foo
+ implicit none
+c We used to accept following in fixed mode. Shall we at least
+c warn about it?
+include 'include_10.inc'
+ i = 1
+ end subroutine foo
+ subroutine bar
+c Likewise here.
+ implicit none
+ include'include_10.inc'
+ i = 1
+ end subroutine bar
+ subroutine baz
+c And here.
+ implicit none
+ include 'include_10.inc'
+ i = 1
+ end subroutine baz
--- gcc/testsuite/gfortran.dg/include_12.f.jj 2018-11-12 14:36:42.542803497 +0100
+++ gcc/testsuite/gfortran.dg/include_12.f 2018-11-12 14:45:36.201055286 +0100
@@ -0,0 +1,49 @@
+c { dg-do compile }
+c { dg-options "-fdec-include" }
+ subroutine foo
+ implicit none
+ 0include 'include_10.inc'
+ i = 1
+ end subroutine foo
+ subroutine bar
+ implicit none
+ i
+ ;n
+ +c
+
+c some comment
+
+ ll
+C comment line
+ uu
+ DD
+ ee'include_10.inc'
+ i = 1
+ end subroutine bar
+ subroutine baz
+ implicit none
+ 0include
+ + 'include_10.inc'
+ i = 1
+ end subroutine baz
+ subroutine qux
+ implicit none
+ i n C lude 'inc
+* another comment line
+ &lude_10.inc'
+ i = 1
+ end subroutine qux
+ subroutine quux
+ implicit none
+ 0inc
+ 1lud
+ 2e '
+ 3include_10.inc'
+ i = 1
+ end subroutine quux
+ program include_12
+ implicit none
+ include
+! comment
+ +'include_10.inc'
+ end program
--- gcc/testsuite/gfortran.dg/include_13.f90.jj 2018-11-12 14:38:05.488443863 +0100
+++ gcc/testsuite/gfortran.dg/include_13.f90 2018-11-12 15:04:42.921254180 +0100
@@ -0,0 +1,32 @@
+! { dg-do compile }
+! { dg-options "-fdec" }
+subroutine foo
+ implicit none
+ incl& ! comment1
+&u&
+ &de & ! comment2
+'include&
+ &_10.inc'
+ i = 1
+end subroutine foo
+subroutine bar
+ implicit none
+include &
+
+! comment3
+
+"include_10.inc"
+ i = 1
+end subroutine bar
+subroutine baz
+ implicit none
+ include&
+&'include_10.&
+&inc'
+ i = 1
+end subroutine baz
+subroutine qux
+ implicit none
+ include '&
+include_10.inc'
+end subroutine qux
--- gcc/testsuite/gfortran.dg/gomp/include_1.f.jj 2018-11-12 15:24:27.455829721 +0100
+++ gcc/testsuite/gfortran.dg/gomp/include_1.f 2018-11-12 15:26:02.044278080 +0100
@@ -0,0 +1,49 @@
+c { dg-do compile }
+c { dg-options "-fopenmp -fdec" }
+ subroutine foo
+ implicit none
+c$ 0include 'include_1.inc'
+ i = 1
+ end subroutine foo
+ subroutine bar
+ implicit none
+ i
+C$ ;n
+ +c
+
+c some comment
+
+*$ ll
+C comment line
+ uu
+ DD
+ ee'include_1.inc'
+ i = 1
+ end subroutine bar
+ subroutine baz
+ implicit none
+ 0include
+ + 'include_1.inc'
+ i = 1
+ end subroutine baz
+ subroutine qux
+ implicit none
+!$ i n C lude 'inc
+* another comment line
+ &lude_1.inc'
+ i = 1
+ end subroutine qux
+ subroutine quux
+ implicit none
+C$ 0inc
+*$ 1lud
+c$ 2e '
+!$ 3include_1.inc'
+ i = 1
+ end subroutine quux
+ program include_12
+ implicit none
+ include
+! comment
+c$ +'include_1.inc'
+ end program
--- gcc/testsuite/gfortran.dg/gomp/include_1.inc.jj 2018-11-12 15:24:30.471780253 +0100
+++ gcc/testsuite/gfortran.dg/gomp/include_1.inc 2018-11-12 12:07:29.694854414 +0100
@@ -0,0 +1 @@
+ integer i
--- gcc/testsuite/gfortran.dg/gomp/include_2.f90.jj 2018-11-12 15:24:33.556729656 +0100
+++ gcc/testsuite/gfortran.dg/gomp/include_2.f90 2018-11-12 15:26:10.480139412 +0100
@@ -0,0 +1,32 @@
+! { dg-do compile }
+! { dg-options "-fopenmp -fdec-include" }
+subroutine foo
+ implicit none
+!$ incl& ! comment1
+!$ &u&
+!$ &de & ! comment2
+!$ 'include&
+ &_1.inc'
+ i = 1
+end subroutine foo
+subroutine bar
+ implicit none
+!$ include &
+
+! comment3
+
+!$ "include_1.inc"
+ i = 1
+end subroutine bar
+subroutine baz
+ implicit none
+!$ include&
+!$ &'include_1.&
+!$ &inc'
+ i = 1
+end subroutine baz
+subroutine qux
+ implicit none
+!$ include '&
+include_1.inc'
+end subroutine qux
Jakub
--
https://www.codethink.co.uk/privacy.html
Jakub Jelinek
2018-11-13 10:53:40 UTC
Permalink
Post by Mark Eggleston
I've applied this patch and tried it out.
That is IMHO correct, it fails to compile with ifort as well:
https://fortran.godbolt.org/z/Aav6dv

The problem is in mixing quotes, " vs. '
If I fix that, it works (changed the testcases to use the include file
I had in the patch):

! { dg-do compile }
! { dg-options "-fdec" }
subroutine quux
implicit none
include &
&'include_10.inc'
i = 1
end subroutine quux
subroutine quuz
implicit none
include &
&"include_10.inc"
i = 1
end subroutine quuz

c { dg-do compile }
c { dg-options "-fdec-include" }
subroutine quuz
implicit none
integer include
include
+"include_10.inc"
i = 1
include
+ = 2
write (*,*) include
end subroutine quuz
subroutine corge
implicit none
include
+'include_10.inc'
i = 1
end subroutine corge

both compile. I can include these snippets into the testcases.
Post by Mark Eggleston
Is there any particular reason that include line continuation is isolated in
-fdec-include? Is there a specific restriction regarding include lines in
the Fortran standard such that continuations in free form and fixed form
can't be used with include lines?
My reading of the Fortran standard says it is invalid, but I am not a good
Fortran language lawyer, so I'm open to be convinced otherwise.

The two bullets that I think say it are:
"An INCLUDE line is not a Fortran statement."
and
"An INCLUDE line shall appear on a single source line where a statement may appear; it shall be
the only nonblank text on this line other than an optional trailing comment. Thus, a statement
label is not allowed."

If there are continuations, it is not a single source line anymore, I don't
see anywhere a word that all the continuations together with the initial
source line are considered a single source line for the purposes of the rest
of the standard.

And in the DEC manual, it explicitly says that INCLUDE is a statement and
that it can be continued.

Jakub
Jakub Jelinek
2018-11-20 17:02:47 UTC
Permalink
Hi!

I'd like to ping this patch, ok for trunk?
Post by Jakub Jelinek
* lang.opt (fdec-include): New option.
* options.c (set_dec_flags): Set also flag_dec_include.
* scanner.c (include_line): Change return type from bool to int.
In fixed form allow spaces in between include keyword letters.
For -fdec-include, allow in fixed form 0 in column 6. With
-fdec-include return -1 if the parsed line is not full include
statement and it could be successfully completed on continuation
lines.
(include_stmt): New function.
(load_file): Adjust include_line caller. If it returns -1, keep
trying include_stmt until it stops returning -1 whenever adding
further line of input.
* gfortran.dg/include_10.f: New test.
* gfortran.dg/include_10.inc: New file.
* gfortran.dg/include_11.f: New test.
* gfortran.dg/include_12.f: New test.
* gfortran.dg/include_13.f90: New test.
* gfortran.dg/gomp/include_1.f: New test.
* gfortran.dg/gomp/include_1.inc: New file.
* gfortran.dg/gomp/include_2.f90: New test.
Jakub
Thomas Koenig
2018-11-21 07:31:17 UTC
Permalink
Hi Jakub,
Post by Jakub Jelinek
I'd like to ping this patch, ok for trunk?
OK. Thanks for the patch!

Before 9.0 is released, we should also document the flag
(and the extension it supports) in the manual, and note it
in changes.html and on the Wiki. Would you also do that?

Regards

Thomas
Jakub Jelinek
2018-11-21 08:25:29 UTC
Permalink
Post by Thomas Koenig
Post by Jakub Jelinek
I'd like to ping this patch, ok for trunk?
OK. Thanks for the patch!
Thanks.
Post by Thomas Koenig
Before 9.0 is released, we should also document the flag
(and the extension it supports) in the manual, and note it
in changes.html and on the Wiki. Would you also do that?
Like this? Ok for trunk/wwwdocs?

2018-11-21 Jakub Jelinek <***@redhat.com>

* invoke.texi (-fdec-include): Document.

--- gcc/fortran/invoke.texi.jj 2018-08-26 22:42:19.907823618 +0200
+++ gcc/fortran/invoke.texi 2018-11-21 09:14:21.449174232 +0100
@@ -119,7 +119,7 @@ by type. Explanations are in the follow
@gccoptlist{-fall-intrinsics -fbackslash -fcray-pointer -fd-lines-as-code @gol
-fd-lines-as-comments @gol
-fdec -fdec-structure -fdec-intrinsic-ints -fdec-static -fdec-math @gol
--fdefault-double-8 -fdefault-integer-8 -fdefault-real-8 @gol
+-fdec-include -fdefault-double-8 -fdefault-integer-8 -fdefault-real-8 @gol
-fdefault-real-10 -fdefault-real-16 -fdollar-ok -ffixed-line-length-@var{n} @gol
-ffixed-line-length-none -ffree-form -ffree-line-length-@var{n} @gol
-ffree-line-length-none -fimplicit-none -finteger-4-integer-8 @gol
@@ -277,6 +277,12 @@ functions (e.g. TAND, ATAND, etc...) for
Enable DEC-style STATIC and AUTOMATIC attributes to explicitly specify
the storage of variables and other objects.

+@item -fdec-include
+@opindex @code{fdec-include}
+Enable parsing of INCLUDE as a statement in addition to parsing it as
+INCLUDE line. When parsed as INCLUDE statement, INCLUDE does not have to
+be on a single line and can use line continuations.
+
@item -fdollar-ok
@opindex @code{fdollar-ok}
@cindex @code{$}


Jakub
Thomas Koenig
2018-11-21 19:31:33 UTC
Permalink
Hi Jakub,
Post by Jakub Jelinek
Post by Thomas Koenig
Before 9.0 is released, we should also document the flag
(and the extension it supports) in the manual, and note it
in changes.html and on the Wiki. Would you also do that?
Like this? Ok for trunk/wwwdocs?
OK for trunk (and I don't think you need my OK for wwwdocs, but
you have it anyway :-)

Regards

Thomas

Loading...