From 361a1f9f551188baba430063a91ea4817f3bd72e Mon Sep 17 00:00:00 2001 From: Robert Pengelly Date: Sun, 23 Mar 2025 01:47:09 +0000 Subject: [PATCH] Added conditional support --- read.c | 374 ++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 344 insertions(+), 30 deletions(-) diff --git a/read.c b/read.c index 2a72ad1..81aaa23 100644 --- a/read.c +++ b/read.c @@ -182,21 +182,56 @@ static int include_makefile (const char *filename) { } - fprintf (stderr, "%s: *** Failed to include '%s'. Stop.\n", program_name, filename); + fprintf (stderr, "%s: *** failed to include '%s'. Stop.\n", program_name, filename); return 1; } +struct if_stack { + + struct if_stack *prev; + + int ignoring; + int prev_ignoring; + int has_else; + +}; + +static struct if_stack *cur_if_stack = 0; + +static void add_if_stack (void) { + + struct if_stack *if_stack = xmalloc (sizeof *if_stack); + + if (cur_if_stack) { + if_stack->prev_ignoring = cur_if_stack->ignoring; + } + + if_stack->prev = cur_if_stack; + cur_if_stack = if_stack; + +} + +static char *skip_whitespace (char *p) { + + while (isspace ((int) *p)) { + p++; + } + + return p; + +} + static int read_lbuf (struct linebuf *lbuf, int set_default) { struct nameseq* filenames = 0; int ret; char *cmds, *clean = 0, *depstr = 0, *q; - char *colon, *semicolonp, *commentp; + size_t clean_size = 0, cmds_idx = 0, cmds_sz = 256; + char *colon, *semicolonp, *commentp; long lines_read; - size_t clean_size = 0, cmds_idx = 0, cmds_sz = 256; cmds = xmalloc (cmds_sz); @@ -212,9 +247,296 @@ static int read_lbuf (struct linebuf *lbuf, int set_default) { while ((lines_read = read_line (lbuf))) { char *line = lbuf->start; - char *p, *src, *dst; + int after_else = 0; + + char *p, *src, *dst, *comma, *arg1 = 0, *arg2 = 0; + char ch; + + if (*line == '\0') { + continue; + } + + if (strlen (line) + 1 > clean_size) { + + clean_size = strlen (line) + 1; + + free (clean); + clean = xmalloc (clean_size); + + } + + strcpy (clean, line); + + remove_backslash_newlines (clean); + remove_comment (clean); + + p = clean; + + while (isspace ((int) *p)) { + p++; + } + + if (*p == '\0') { + continue; + } + + /** + * In command lines ifeq, etc. with space/tab before them + * should be treated as real commands, not directives. + */ + if (filenames && p != clean) { + goto is_command; + } + + if (strncmp (p, "else", 4) == 0) { + + p += 4; + + if (!cur_if_stack) { + + fprintf (stderr, "%s: *** extraneous 'else'. Stop.\n", program_name); + exit (EXIT_FAILURE); + + } + + p = skip_whitespace (p); + + if (strncmp (p, "ifeq", 4) == 0 || strncmp (p, "ifneq", 5) == 0 || strncmp (p, "ifdef", 5) == 0 || strncmp (p, "ifndef", 6) == 0) { + after_else = 1; + } else { + + if (*p) { + fprintf (stderr, "%s: extraneous text after 'else' directive\n", program_name); + } + + if (cur_if_stack->has_else) { + + fprintf (stderr, "%s: *** only one 'else' per conditional. Stop.\n", program_name); + exit (EXIT_FAILURE); + + } + + cur_if_stack->ignoring = (cur_if_stack->prev_ignoring || !cur_if_stack->ignoring); + cur_if_stack->has_else = 1; + + continue; + + } + + } + + if (strncmp (p, "ifeq", 4) == 0 || strncmp (p, "ifneq", 5) == 0) { + + int ifneq; + + if (strncmp (p, "ifeq", 4) == 0) { + + ifneq = 0; + p += 4; + + } else { + + ifneq = 1; + p += 5; + + } + + if (isspace ((int) *p)) { + p = skip_whitespace (p); + } + + if (*p == '"' || *p == '\'') { + + ch = *p++; + arg1 = p; + + while (*p != ch) { + + if (*p == '\0') { + goto ifeq_invalid_syntax; + } + + p++; + + } + + *p = '\0'; + p = skip_whitespace (p + 1); + + if (*p != '"' && *p != '\'') { + goto ifeq_invalid_syntax; + } + + ch = *p++; + arg2 = p; + + while (*p != ch) { + + if (*p == '\0') { + goto ifeq_invalid_syntax; + } + + p++; + + } + + *p++ = '\0'; + + } else if (*p == '(') { + + arg1 = ++p; + + if (!(comma = strchr (p, ',')) || !(q = strrchr (p, ')')) || q < comma) { + goto ifeq_invalid_syntax; + } + + *comma = '\0'; + *q = '\0'; + + for (q = comma; q > p && isspace ((int) *(q - 1));) { + *--q = '\0'; + } + + for (p = comma + 1; isspace ((int) *p);) { + p++; + } + + arg2 = p; + + while (*p != '\0') { + p++; + } + + } else { + goto ifeq_invalid_syntax; + } + + p = skip_whitespace (p); + fprintf (stderr, "%s\n", p); + + if (*p || !arg1 || !arg2) { + fprintf (stderr, "%s: extraneous text after '%s' directive\n", program_name, ifneq ? "ifneq" : "ifeq"); + } + + p = variable_expand_line (xstrdup (arg1)); + q = variable_expand_line (xstrdup (arg2)); + + if (after_else) { + cur_if_stack->prev_ignoring |= !cur_if_stack->ignoring; + } else { + add_if_stack (); + } + + if (cur_if_stack->prev_ignoring) { + cur_if_stack->ignoring = 1; + } else { + + int is_equal = (strcmp (p, q) == 0); + cur_if_stack ->ignoring = is_equal ^ (!ifneq); + + } + + free (p); + free (q); + + continue; + + ifeq_invalid_syntax: + + fprintf (stderr, "%s: *** invalid syntax in conditional. Stop.", program_name); + exit (EXIT_FAILURE); + + } + + if (strncmp (p, "ifdef", 5) == 0 || strncmp (p, "ifndef", 6) == 0) { + + struct variable *var; + int ifndef; + + if (strncmp (p, "ifdef", 5) == 0) { + + ifndef = 0; + p += 5; + + } else { + + ifndef = 1; + p += 6; + + } + + if (!isspace ((int) *p)) { + + fprintf (stderr, "%s: %s must be followed by whitespace. Stop.", program_name, ifndef ? "ifndef" : "ifdef"); + exit (EXIT_FAILURE); + + } + + p = line = variable_expand_line (xstrdup (skip_whitespace (p))); + + for (; isspace ((int) *p);) { + p++; + } + + for (q = p + strlen (p); q > p && isspace ((int) *(q - 1));) { + *--q = '\0'; + } + + var = variable_find (p); + + if (after_else) { + cur_if_stack->prev_ignoring |= !cur_if_stack->ignoring; + } else { + add_if_stack (); + } + + if (cur_if_stack->prev_ignoring) { + cur_if_stack->ignoring = 1; + } else { + + int is_defined = 0; + + if (var) { + is_defined = 1; + } + + cur_if_stack->ignoring = is_defined ^ (!ifndef); + + } + + continue; + + } - if (line[0] == '\0') { + if (strncmp (p, "endif", 5) == 0) { + + p = skip_whitespace (p + 5); + + if (!cur_if_stack) { + + fprintf (stderr, "%s: *** extraneous 'endif'. Stop.\n", program_name); + exit (EXIT_FAILURE); + + } else { + + struct if_stack *prev = cur_if_stack->prev; + + free (cur_if_stack); + cur_if_stack = prev; + + } + + if (*p) { + fprintf (stderr, "%s: *** extraneous text after 'endif' directive\n", program_name); + } + + continue; + + } + + is_command: + + if (cur_if_stack && cur_if_stack->ignoring) { continue; } @@ -262,30 +584,6 @@ static int read_lbuf (struct linebuf *lbuf, int set_default) { } - if (strlen (line) + 1 > clean_size) { - - clean_size = strlen (line) + 1; - - free (clean); - clean = xmalloc (clean_size); - - } - - strcpy (clean, line); - - remove_backslash_newlines (clean); - remove_comment (clean); - - p = clean; - - while (isspace ((int) *p)) { - p++; - } - - if (*p == '\0') { - continue; - } - if (strncmp (p, "include", 7) == 0 && (isspace ((int) p[7]) || p[7] == '\0')) { p += 7; @@ -364,7 +662,7 @@ static int read_lbuf (struct linebuf *lbuf, int set_default) { } if (*p) { - fprintf (stderr, "%s: Missing ':' in rule line!\n", program_name); + fprintf (stderr, "%s: missing ':' in rule line!\n", program_name); } free (line); @@ -419,6 +717,22 @@ static int read_lbuf (struct linebuf *lbuf, int set_default) { free (clean); free (cmds); + if (cur_if_stack) { + + struct if_stack *prev; + + for (; cur_if_stack; cur_if_stack = prev) { + + prev = cur_if_stack->prev; + free (cur_if_stack); + + } + + fprintf (stderr, "%s: *** missing 'endif'. Stop.\n", program_name); + exit (EXIT_FAILURE); + + } + return 0; } -- 2.34.1