|
| 1 | +/* ************************************************************************** */ |
| 2 | +/* */ |
| 3 | +/* ::: :::::::: */ |
| 4 | +/* casescan.h :+: :+: :+: */ |
| 5 | +/* +:+ +:+ +:+ */ |
| 6 | +/* By: dlesieur <dlesieur@student.42.fr> +#+ +:+ +#+ */ |
| 7 | +/* +#+#+#+#+#+ +#+ */ |
| 8 | +/* Created: 2026/09/01 00:00:00 by dlesieur #+# #+# */ |
| 9 | +/* Updated: 2026/09/01 00:00:00 by dlesieur ### ########.fr */ |
| 10 | +/* */ |
| 11 | +/* ************************************************************************** */ |
| 12 | + |
| 13 | +/* Case-aware $(...) span scanning (issue #95). A case pattern's closing |
| 14 | + `)` is unbalanced by design (`a*)` needs no opening paren), so every |
| 15 | + scanner that walks a command substitution looking for its matching `)` |
| 16 | + by counting parens ends the span at the first pattern. This automaton |
| 17 | + is the one shared implementation of the fix: it tracks `case ... esac` |
| 18 | + regions (only in command position, so `echo case` cannot arm it) and |
| 19 | + reports a `)` at the depth where the innermost open `case` started as |
| 20 | + a pattern terminator instead of a closer. Balanced pattern parens |
| 21 | + `(a*)`, extglob `@(a|b)`, body subshells and nested substitutions all |
| 22 | + raised the depth first, so they still close normally. |
| 23 | + Users: the lexer's tokenize_subshell, the word reparser's |
| 24 | + reparse_envvar_paren, the expander's find_cmd_sub_end. The cmdsub fast |
| 25 | + path's csf_skip_csub stays naive on purpose: a mis-scan there makes |
| 26 | + eligibility reject and fork, which is correct by construction. */ |
| 27 | + |
| 28 | +#ifndef CASESCAN_H |
| 29 | +# define CASESCAN_H |
| 30 | + |
| 31 | +# include <stdbool.h> |
| 32 | + |
| 33 | +/* Unclosed-`case` tracking depth; nesting beyond it falls back to plain |
| 34 | + paren counting (the pre-#95 behaviour). */ |
| 35 | +# define CASESCAN_MAX 32 |
| 36 | + |
| 37 | +typedef struct s_casescan |
| 38 | +{ |
| 39 | + int len; /* input length, or -1 for NUL-terminated input */ |
| 40 | + int ncase; /* how many `case` are open */ |
| 41 | + int at[CASESCAN_MAX]; /* paren depth each open case started at */ |
| 42 | + bool cmdpos; /* the next word sits in command position */ |
| 43 | +} t_casescan; |
| 44 | + |
| 45 | +void casescan_init(t_casescan *cs, int len); |
| 46 | + |
| 47 | +/* Feed the UNQUOTED, UNESCAPED character at s[*i]; the caller handles |
| 48 | + quotes and backslash itself (clearing cs->cmdpos when it does). |
| 49 | + Advances *i past what was consumed (one char, or a whole keyword) and |
| 50 | + returns the paren-depth delta to apply: +1, -1 or 0. */ |
| 51 | +int casescan_step(t_casescan *cs, const char *s, int *i, int depth); |
| 52 | + |
| 53 | +#endif |
0 commit comments