3 * xmlif -- support processing instructions for XML conditionalization
5 * By Eric S. Raymond <esr@thyrsus.com>, 3 Nov 1997 (as sgmlpre)
6 * Enhanced for XML September 2002, Licensed under GPLv2+ since 03/2009
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 * Filter XML according to conditionalizing markup. Argument/value
22 * pairs from the command line are matched against the attributes of
23 * <?xmlif if> and <?xmlif elif> tags. Spans between <?xmlif if> or
24 * <?xmlif elif> and the next conditional processing instruction are
25 * passed through unaltered if there is no attribute mismatch; spans
26 * between <?xmlif if not> and <?xmlif elif not> are passed if there
27 * is at least one attribute mismatch. An attribute mismatch happens
28 * if an attribute occurs in both the command-line arguments and the
29 * tag, but the values do not match. <?xmlif else> inverts the sense
30 * of the current comparison. Value matching is by string equality,
31 * except that "|" is interpreted as an alternation character.
32 * <?xmlif if>, <?xmlif fi>, <?xmlif else> and <?xmlif elif>, and will
33 * all be removed from the output.
35 * This lexer requires flex. Limitations; attributes and values may be
36 * only 16384 (YY_BUF_SIZE) characters long.
44 static char **selections; /* selection tokens */
45 static int nselections; /* number of selections */
46 static ifsense; /* sense of last `if' or unless seen */
47 static char *attribute; /* last attribute scanned */
50 int matched; /* matched at current level */
51 int suppressed; /* suppressed branch? */
54 static struct stack_t head, *end = &head;
56 static void push_level(void)
57 /* create activation record for the current level */
59 struct stack_t *newelt;
62 fprintf(stderr, "{push_level()}");
64 newelt = (struct stack_t *)malloc(sizeof(struct stack_t));
69 end->suppressed = end->up->suppressed;
72 static void pop_level(void)
73 /* delete activation record for the current level */
75 struct stack_t *up = end->up;
78 fprintf(stderr, "{pop_level()}");
87 static void stash_attribute(char *attr)
88 /* stash an attribute away for comparison */
91 fprintf(stderr, "{stash_attribute(%s)}", attr);
93 attribute = strdup(attr);
96 static void end_attribute(void)
97 /* we've seen all the attributes of a conditional, process them now */
103 end->suppressed = (ifsense == !!end->suppressed);
104 for (up = end->up; up->up; up = up->up)
110 if (!end->matched && !end->suppressed)
113 fprintf(stderr,"{end_attribute(ifsense=%d)->%d}", ifsense, end->suppressed);
117 static int value_match(char *value, char *against)
118 /* return TRUE if values match (handles alternation syntax) */
124 fprintf(stderr, "{value_match(%s, %s)}", value, against);
127 for (vp = value; *vp; vp += vn)
129 vn = strcspn(vp, "|");
130 for (ap = against; *ap; ap += an)
132 an = strcspn(ap, "|");
133 if (an == vn && memcmp(ap, vp, an) == 0)
145 static int suppress(char *attr, char *value)
146 /* does a given attribute/value pair enable inclusion? */
151 for (i = 0; i < nselections; i++)
153 int eqoffset = strcspn(selections[i], "=");
155 if (strncasecmp(selections[i], attr, eqoffset) == 0)
157 /* attribute matches; enable (0) or lock in suppression (-1) */
158 res = value_match(value, selections[i] + eqoffset + 1) ? 0 : -1;
163 res = 1; /* no match -- suppress but don't lock it in */
166 fprintf(stderr, "{suppress(%s, %s)->%d}", attr, value, res);
171 static void process_value(char *val)
172 /* process value in context of stashed attribute */
174 /* if pred has been set to -1 by a mismatch, latch it there */
175 if (end->suppressed > -1)
176 end->suppressed = suppress(attribute, val);
179 static void process_else()
180 /* process <?xmlif else> tag */
182 end->suppressed = end->matched;
184 fprintf(stderr, "{else -> %d}", end->suppressed);
191 ATTRIBUTE [a-z][a-z0-9]*
198 %option batch never-interactive fast 8bit
201 <INITIAL>\<\?xmlif{WS}if{WS}not{WS} {BEGIN(attrib); ifsense = FALSE; push_level();}
202 <INITIAL>\<\?xmlif{WS}if{WS} {BEGIN(attrib); ifsense = TRUE; push_level();}
203 <INITIAL>\<\?xmlif{WS}elif{WS}not{WS} {BEGIN(attrib); ifsense = FALSE;}
204 <INITIAL>\<\?xmlif{WS}elif{WS} {BEGIN(attrib); ifsense = TRUE;}
205 <INITIAL>\<\?xmlif{WS}else\?> {process_else();}
207 <INITIAL><\?xmlif{WS}fi\?> {pop_level();}
209 <attrib>{ATTRIBUTE} {stash_attribute(yytext);}
210 <attrib>= {BEGIN(val);}
211 <attrib>\?\> {BEGIN(INITIAL); end_attribute();}
212 <val>{DSTRING}|{SSTRING} {
213 yytext[strlen(yytext)-1]='\0';
214 process_value(yytext+1);
219 "xmlif: > where value expected\n");
224 if (!end->suppressed)
233 int yywrap() {exit(0);};
235 main(int argc, char *argv[])
239 selections = argv + 1;
240 nselections = argc - 1;
242 for (i = 0; i < nselections; i++)
243 if (strchr(selections[i], '=') == 0)
245 if (!strcmp(selections[i], "--help"))
247 printf ("usage: xmlif attrib=value..\n");
251 if (!strcmp(selections[i], "--version"))
253 printf ("xmlif - xmlto version %s\n", VERSION);
257 fprintf(stderr, "xmlif: malformed argument %d\n", i);
265 The following sets edit modes for GNU EMACS
271 /* xmlif.l ends here */