Add support for nested conditionals

The previous behavior was to support nested #ifdef and #ifndef, but to
return to unconditional parsing after the next #endif, independently of
the number of previously parsed conditions.

E.g. after "#ifdef A / #ifdef B / #endif" the following lines were
unconditially parsed again, independently of A and/or B being defined.

The new behavior is to count the level of false conditions and to only
restart parsing of calendar entries when the corresponding number of
#endif tokens have been seen.

In addition to the above, an #else directive has been added, to toggle
between parsing and ignoring of the following lines.

No validation of the correct use of the condition directives is made.
#endif without prior #define or #ifndef is ignored and #else toggles
between parsing and skipping of entries.

The MFC period has been set to 1 month to allow for a review of the
changes and for a discussion, whether these modifications should not
be merged at all.

No correct input file is parsed differently than before, but if calendar
data files are published that use these new features, those data files
will not parse correctly on prior versions of this program.

MFC after:	1 month
This commit is contained in:
Stefan Eßer 2020-10-28 14:48:58 +00:00
parent bdc0cb4e2c
commit 19b5c30754
2 changed files with 67 additions and 40 deletions

View File

@ -28,7 +28,7 @@
.\" @(#)calendar.1 8.1 (Berkeley) 6/29/93
.\" $FreeBSD$
.\"
.Dd October 25, 2020
.Dd October 28, 2020
.Dt CALENDAR 1
.Os
.Sh NAME
@ -198,7 +198,13 @@ file is preprocessed by a limited subset of
.Xr cpp 1
internally, allowing the inclusion of shared files such as
lists of company holidays or meetings.
This limited subset consists of \fB#include #ifndef #endif\fR and \fB#define\fR.
This limited subset consists of \fB#include #ifdef #ifndef #else
#endif\fR and \fB#define\fR. Conditions can be nested, but not check
for matching begin and end directives is performed.
Included files are parsed in their own scope.
They have no read or write access to condition variables defined in
an outer scope and open conditional blocks are implicitly closed.
.Pp
If the shared file is not referenced by a full pathname,
.Nm
searches in the current (or home) directory first, and then in the
@ -346,9 +352,12 @@ double-check the start and end time of solar and lunar events.
.Sh BUGS
The
.Nm
internal cpp does not support nested conditions and will continue
parsing of the input file on the next #endif even in nested contexts.
It does only recognise #include, #define, #ifdef and #ifndef.
does only recognise the cpp directives #include, #define, #ifdef,
#ifndef and #else.
It supports nested conditions, but does not perform any validation
on the correct use and nesting of conditions.
#endif without prior #ifdef or #define is ignored and #else outside
a conditional section skips input lines up to the next #endif.
.Pp
There is no possibility to properly specify the local position
needed for solar and lunar calculations.

View File

@ -140,16 +140,64 @@ cal_fopen(const char *file)
}
static int
token(char *line, FILE *out, bool *skip)
token(char *line, FILE *out, int *skip)
{
char *walk, c, a;
if (strncmp(line, "endif", 5) == 0) {
*skip = false;
if (*skip > 0)
--*skip;
return (T_OK);
}
if (*skip)
if (strncmp(line, "ifdef", 5) == 0) {
walk = line + 5;
trimlr(&walk);
if (*walk == '\0') {
warnx("Expecting arguments after #ifdef");
return (T_ERR);
}
if (*skip != 0 || definitions == NULL || sl_find(definitions, walk) == NULL)
++*skip;
return (T_OK);
}
if (strncmp(line, "ifndef", 6) == 0) {
walk = line + 6;
trimlr(&walk);
if (*walk == '\0') {
warnx("Expecting arguments after #ifndef");
return (T_ERR);
}
if (*skip != 0 || (definitions != NULL && sl_find(definitions, walk) != NULL))
++*skip;
return (T_OK);
}
if (strncmp(line, "else", 4) == 0) {
walk = line + 4;
trimlr(&walk);
if (*walk != '\0') {
warnx("Expecting no arguments after #else");
return (T_ERR);
}
if (*skip == 0)
*skip = 1;
else if (*skip == 1)
*skip = 0;
return (T_OK);
}
if (*skip != 0)
return (T_OK);
if (strncmp(line, "include", 7) == 0) {
@ -212,36 +260,6 @@ token(char *line, FILE *out, bool *skip)
return (T_OK);
}
if (strncmp(line, "ifdef", 5) == 0) {
walk = line + 5;
trimlr(&walk);
if (*walk == '\0') {
warnx("Expecting arguments after #ifdef");
return (T_ERR);
}
if (definitions == NULL || sl_find(definitions, walk) == NULL)
*skip = true;
return (T_OK);
}
if (strncmp(line, "ifndef", 6) == 0) {
walk = line + 6;
trimlr(&walk);
if (*walk == '\0') {
warnx("Expecting arguments after #ifndef");
return (T_ERR);
}
if (definitions != NULL && sl_find(definitions, walk) != NULL)
*skip = true;
return (T_OK);
}
return (T_PROCESS);
}
@ -269,7 +287,7 @@ cal_parse(FILE *in, FILE *out)
int month[MAXCOUNT];
int day[MAXCOUNT];
int year[MAXCOUNT];
bool skip = false;
int skip = 0;
char dbuf[80];
char *pp, p;
struct tm tm;
@ -299,7 +317,7 @@ cal_parse(FILE *in, FILE *out)
}
}
if (skip)
if (skip != 0)
continue;
buf = line;