From 19b5c30754899bab78ac267597b3d8dd2d0fbb61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20E=C3=9Fer?= Date: Wed, 28 Oct 2020 14:48:58 +0000 Subject: [PATCH] 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 --- usr.bin/calendar/calendar.1 | 19 +++++--- usr.bin/calendar/io.c | 88 ++++++++++++++++++++++--------------- 2 files changed, 67 insertions(+), 40 deletions(-) diff --git a/usr.bin/calendar/calendar.1 b/usr.bin/calendar/calendar.1 index cf2db36ca4de..240c74d571fe 100644 --- a/usr.bin/calendar/calendar.1 +++ b/usr.bin/calendar/calendar.1 @@ -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. diff --git a/usr.bin/calendar/io.c b/usr.bin/calendar/io.c index 25cf9b5d451a..6efbc5ca72a5 100644 --- a/usr.bin/calendar/io.c +++ b/usr.bin/calendar/io.c @@ -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;