Commit | Line | Data |
---|---|---|
f640bcb3 RK |
1 | /* |
2 | * This file is part of DisOrder | |
3 | * Copyright (C) 2008 Richard Kettlewell | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation; either version 2 of the License, or | |
8 | * (at your option) any later version. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, but | |
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | * General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with this program; if not, write to the Free Software | |
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 | |
18 | * USA | |
19 | */ | |
20 | ||
21 | /** @file lib/macros-builtin.c | |
22 | * @brief Built-in expansions | |
23 | * | |
24 | * This is a grab-bag of non-domain-specific expansions. Note that | |
25 | * documentation will be generated from the comments at the head of | |
26 | * each function. | |
27 | */ | |
28 | ||
29 | #include <config.h> | |
30 | #include "types.h" | |
31 | ||
32 | #include <stdio.h> | |
33 | #include <string.h> | |
34 | #include <errno.h> | |
35 | #include <assert.h> | |
36 | #include <unistd.h> | |
37 | ||
38 | #include "macros.h" | |
39 | #include "sink.h" | |
40 | #include "syscalls.h" | |
41 | #include "log.h" | |
42 | #include "wstat.h" | |
43 | #include "kvp.h" | |
44 | ||
45 | /** @brief Return 1 if @p s is 'true' else 0 */ | |
46 | int mx_str2bool(const char *s) { | |
47 | return !strcmp(s, "true"); | |
48 | } | |
49 | ||
50 | /** @brief Return "true" if @p n is nonzero else "false" */ | |
51 | const char *mx_bool2str(int n) { | |
52 | return n ? "true" : "false"; | |
53 | } | |
54 | ||
55 | /* @include{TEMPLATE}@ | |
56 | * | |
57 | * Includes TEMPLATE as if its text were substituted for the @include | |
58 | * expansion. TODO | |
59 | */ | |
60 | static int exp_include(int attribute((unused)) nargs, | |
61 | char attribute((unused)) **args, | |
62 | struct sink attribute((unused)) *output, | |
63 | void attribute((unused)) *u) { | |
64 | assert(!"TODO implement search path"); | |
65 | } | |
66 | ||
67 | /* @include{COMMAND} | |
68 | * | |
69 | * Executes COMMAND via the shell (using "sh -c") and copies its | |
70 | * standard output to the template output. The shell command output | |
71 | * is not expanded or modified in any other way. | |
72 | * | |
73 | * The shell command's standard error is copied to the error log. | |
74 | * | |
75 | * If the shell exits nonzero then this is reported to the error log | |
76 | * but otherwise no special action is taken. | |
77 | */ | |
78 | static int exp_shell(int attribute((unused)) nargs, | |
79 | char **args, | |
80 | struct sink *output, | |
81 | void attribute((unused)) *u) { | |
82 | int w, p[2], n; | |
83 | char buffer[4096]; | |
84 | pid_t pid; | |
85 | ||
86 | xpipe(p); | |
87 | if(!(pid = xfork())) { | |
88 | exitfn = _exit; | |
89 | xclose(p[0]); | |
90 | xdup2(p[1], 1); | |
91 | xclose(p[1]); | |
92 | execlp("sh", "sh", "-c", args[0], (char *)0); | |
93 | fatal(errno, "error executing sh"); | |
94 | } | |
95 | xclose(p[1]); | |
96 | while((n = read(p[0], buffer, sizeof buffer))) { | |
97 | if(n < 0) { | |
98 | if(errno == EINTR) | |
99 | continue; | |
100 | else | |
101 | fatal(errno, "error reading from pipe"); | |
102 | } | |
103 | if(output->write(output, buffer, n) < 0) | |
104 | return -1; | |
105 | } | |
106 | xclose(p[0]); | |
107 | while((n = waitpid(pid, &w, 0)) < 0 && errno == EINTR) | |
108 | ; | |
109 | if(n < 0) | |
110 | fatal(errno, "error calling waitpid"); | |
111 | if(w) | |
112 | error(0, "shell command '%s' %s", args[0], wstat(w)); | |
113 | return 0; | |
114 | } | |
115 | ||
116 | /* @if{CONDITION}{IF-TRUE}{IF-FALSE} | |
117 | * | |
118 | * If CONDITION is "true" then evaluates to IF-TRUE. Otherwise | |
119 | * evaluates to IF-FALSE. The IF-FALSE part is optional. | |
120 | */ | |
121 | static int exp_if(int nargs, | |
122 | const struct mx_node **args, | |
123 | struct sink *output, | |
124 | void *u) { | |
125 | char *s; | |
126 | int rc; | |
127 | ||
128 | if((rc = mx_expandstr(args[0], &s, u))) | |
129 | return rc; | |
130 | if(mx_str2bool(s)) | |
131 | return mx_expand(args[1], output, u); | |
132 | else if(nargs > 2) | |
133 | return mx_expand(args[2], output, u); | |
134 | else | |
135 | return 0; | |
136 | } | |
137 | ||
138 | /* @and{BRANCH}{BRANCH}... | |
139 | * | |
140 | * Expands to "true" if all the branches are "true" otherwise to "false". If | |
141 | * there are no brances then the result is "true". Only as many branches as | |
142 | * necessary to compute the answer are evaluated (starting from the first one), | |
143 | * so if later branches have side effects they may not take place. | |
144 | */ | |
145 | static int exp_and(int nargs, | |
146 | const struct mx_node **args, | |
147 | struct sink *output, | |
148 | void *u) { | |
149 | int n, result = 1, rc; | |
150 | char *s; | |
151 | ||
152 | for(n = 0; n < nargs; ++n) { | |
153 | if((rc = mx_expandstr(args[n], &s, u))) | |
154 | return rc; | |
155 | if(!mx_str2bool(s)) { | |
156 | result = 0; | |
157 | break; | |
158 | } | |
159 | } | |
160 | if(sink_writes(output, mx_bool2str(result)) < 0) | |
161 | return -1; | |
162 | else | |
163 | return 0; | |
164 | } | |
165 | ||
166 | /* @or{BRANCH}{BRANCH}... | |
167 | * | |
168 | * Expands to "true" if any of the branches are "true" otherwise to "false". | |
169 | * If there are no brances then the result is "false". Only as many branches | |
170 | * as necessary to compute the answer are evaluated (starting from the first | |
171 | * one), so if later branches have side effects they may not take place. | |
172 | */ | |
173 | static int exp_or(int nargs, | |
174 | const struct mx_node **args, | |
175 | struct sink *output, | |
176 | void *u) { | |
177 | int n, result = 0, rc; | |
178 | char *s; | |
179 | ||
180 | for(n = 0; n < nargs; ++n) { | |
181 | if((rc = mx_expandstr(args[n], &s, u))) | |
182 | return rc; | |
183 | if(mx_str2bool(s)) { | |
184 | result = 1; | |
185 | break; | |
186 | } | |
187 | } | |
188 | if(sink_writes(output, mx_bool2str(result)) < 0) | |
189 | return -1; | |
190 | else | |
191 | return 0; | |
192 | } | |
193 | ||
194 | /* @not{CONDITION} | |
195 | * | |
196 | * Expands to "true" unless CONDITION is "true" in which case "false". | |
197 | */ | |
198 | static int exp_not(int attribute((unused)) nargs, | |
199 | char **args, | |
200 | struct sink *output, | |
201 | void attribute((unused)) *u) { | |
202 | if(sink_writes(output, mx_bool2str(!mx_str2bool(args[0]))) < 0) | |
203 | return -1; | |
204 | else | |
205 | return 0; | |
206 | } | |
207 | ||
208 | /* @#{...} | |
209 | * | |
210 | * Expands to nothing. The argument(s) are not fully evaluated, and no side | |
211 | * effects occur. | |
212 | */ | |
213 | static int exp_comment(int attribute((unused)) nargs, | |
214 | const struct mx_node attribute((unused)) **args, | |
215 | struct sink attribute((unused)) *output, | |
216 | void attribute((unused)) *u) { | |
217 | return 0; | |
218 | } | |
219 | ||
220 | /* @urlquote{STRING} | |
221 | * | |
222 | * URL-quotes a string, i.e. replaces any characters not safe to use unquoted | |
223 | * in a URL with %-encoded form. | |
224 | */ | |
225 | static int exp_urlquote(int attribute((unused)) nargs, | |
226 | char **args, | |
227 | struct sink *output, | |
228 | void attribute((unused)) *u) { | |
229 | if(sink_writes(output, urlencodestring(args[0])) < 0) | |
230 | return -1; | |
231 | else | |
232 | return 0; | |
233 | } | |
234 | ||
235 | void mx_register_builtin(void) { | |
236 | mx_register("include", 1, 1, exp_include); | |
237 | mx_register("shell", 1, 1, exp_shell); | |
238 | mx_magic_register("if", 2, 3, exp_if); | |
239 | mx_magic_register("and", 0, INT_MAX, exp_and); | |
240 | mx_magic_register("or", 0, INT_MAX, exp_or); | |
241 | mx_register("not", 1, 1, exp_not); | |
242 | mx_magic_register("#", 0, INT_MAX, exp_comment); | |
243 | mx_register("urlquote", 1, 1, exp_urlquote); | |
244 | /* TODO: eq */ | |
245 | /* TODO: ne */ | |
246 | /* TODO: define */ | |
247 | /* TODO: discard */ | |
248 | } | |
249 | ||
250 | /* | |
251 | Local Variables: | |
252 | c-basic-offset:2 | |
253 | comment-column:40 | |
254 | fill-column:79 | |
255 | indent-tabs-mode:nil | |
256 | End: | |
257 | */ |