summaryrefslogtreecommitdiffabout
path: root/shared.c
authorJohan Herland <johan@herland.net>2010-09-30 18:15:14 (UTC)
committer Lars Hjemli <hjemli@gmail.com>2010-11-09 15:37:39 (UTC)
commit1415f3f3e017d0123e850707c55cb7e5e5887406 (patch) (unidiff)
tree9036ea6ce534e40ff35394359332a9a628276c0f /shared.c
parente0c6f23789e4893781bcd5af2281d468991ccf3a (diff)
downloadcgit-1415f3f3e017d0123e850707c55cb7e5e5887406.zip
cgit-1415f3f3e017d0123e850707c55cb7e5e5887406.tar.gz
cgit-1415f3f3e017d0123e850707c55cb7e5e5887406.tar.bz2
ui-log: Fix filecount/linecount when path limit is in effect
When using ui-log with path limits, the listing of commits enables parent rewriting in Git's internal log machinery. This did not work well together with cgit_diff_commit() which is used to generate the filecount and linecount numbers for each commit in the log view. cgit_diff_commit() would operate without any path limits, and would therefore process the full diff between the commits shown (which, because of parent rewriting, is not the same as processing the diff for the commit itself). Additionally, the bottom commit in the log view would (again, because of parent rewriting) have zero parents, causing us to process the entire diff between the empty tree and that commit. Since path limits were not in effect, this would (in large projects) reports thousands of files and millions of lines changed in that bottom commit. This patch fixes the issue by applying the same path limit to cgit_diff_commit() as is applied to the rest of the log view. The result is that the filecount/linecount now only reflects the diff as it pertains to the given path limit. Signed-off-by: Johan Herland <johan@herland.net> Signed-off-by: Lars Hjemli <hjemli@gmail.com>
Diffstat (limited to 'shared.c') (more/less context) (ignore whitespace changes)
-rw-r--r--shared.c4
1 files changed, 2 insertions, 2 deletions
diff --git a/shared.c b/shared.c
index 72ac140..765cd27 100644
--- a/shared.c
+++ b/shared.c
@@ -85,426 +85,426 @@ struct cgit_repo *cgit_get_repoinfo(const char *url)
85 85
86void *cgit_free_commitinfo(struct commitinfo *info) 86void *cgit_free_commitinfo(struct commitinfo *info)
87{ 87{
88 free(info->author); 88 free(info->author);
89 free(info->author_email); 89 free(info->author_email);
90 free(info->committer); 90 free(info->committer);
91 free(info->committer_email); 91 free(info->committer_email);
92 free(info->subject); 92 free(info->subject);
93 free(info->msg); 93 free(info->msg);
94 free(info->msg_encoding); 94 free(info->msg_encoding);
95 free(info); 95 free(info);
96 return NULL; 96 return NULL;
97} 97}
98 98
99char *trim_end(const char *str, char c) 99char *trim_end(const char *str, char c)
100{ 100{
101 int len; 101 int len;
102 char *s, *t; 102 char *s, *t;
103 103
104 if (str == NULL) 104 if (str == NULL)
105 return NULL; 105 return NULL;
106 t = (char *)str; 106 t = (char *)str;
107 len = strlen(t); 107 len = strlen(t);
108 while(len > 0 && t[len - 1] == c) 108 while(len > 0 && t[len - 1] == c)
109 len--; 109 len--;
110 110
111 if (len == 0) 111 if (len == 0)
112 return NULL; 112 return NULL;
113 113
114 c = t[len]; 114 c = t[len];
115 t[len] = '\0'; 115 t[len] = '\0';
116 s = xstrdup(t); 116 s = xstrdup(t);
117 t[len] = c; 117 t[len] = c;
118 return s; 118 return s;
119} 119}
120 120
121char *strlpart(char *txt, int maxlen) 121char *strlpart(char *txt, int maxlen)
122{ 122{
123 char *result; 123 char *result;
124 124
125 if (!txt) 125 if (!txt)
126 return txt; 126 return txt;
127 127
128 if (strlen(txt) <= maxlen) 128 if (strlen(txt) <= maxlen)
129 return txt; 129 return txt;
130 result = xmalloc(maxlen + 1); 130 result = xmalloc(maxlen + 1);
131 memcpy(result, txt, maxlen - 3); 131 memcpy(result, txt, maxlen - 3);
132 result[maxlen-1] = result[maxlen-2] = result[maxlen-3] = '.'; 132 result[maxlen-1] = result[maxlen-2] = result[maxlen-3] = '.';
133 result[maxlen] = '\0'; 133 result[maxlen] = '\0';
134 return result; 134 return result;
135} 135}
136 136
137char *strrpart(char *txt, int maxlen) 137char *strrpart(char *txt, int maxlen)
138{ 138{
139 char *result; 139 char *result;
140 140
141 if (!txt) 141 if (!txt)
142 return txt; 142 return txt;
143 143
144 if (strlen(txt) <= maxlen) 144 if (strlen(txt) <= maxlen)
145 return txt; 145 return txt;
146 result = xmalloc(maxlen + 1); 146 result = xmalloc(maxlen + 1);
147 memcpy(result + 3, txt + strlen(txt) - maxlen + 4, maxlen - 3); 147 memcpy(result + 3, txt + strlen(txt) - maxlen + 4, maxlen - 3);
148 result[0] = result[1] = result[2] = '.'; 148 result[0] = result[1] = result[2] = '.';
149 return result; 149 return result;
150} 150}
151 151
152void cgit_add_ref(struct reflist *list, struct refinfo *ref) 152void cgit_add_ref(struct reflist *list, struct refinfo *ref)
153{ 153{
154 size_t size; 154 size_t size;
155 155
156 if (list->count >= list->alloc) { 156 if (list->count >= list->alloc) {
157 list->alloc += (list->alloc ? list->alloc : 4); 157 list->alloc += (list->alloc ? list->alloc : 4);
158 size = list->alloc * sizeof(struct refinfo *); 158 size = list->alloc * sizeof(struct refinfo *);
159 list->refs = xrealloc(list->refs, size); 159 list->refs = xrealloc(list->refs, size);
160 } 160 }
161 list->refs[list->count++] = ref; 161 list->refs[list->count++] = ref;
162} 162}
163 163
164struct refinfo *cgit_mk_refinfo(const char *refname, const unsigned char *sha1) 164struct refinfo *cgit_mk_refinfo(const char *refname, const unsigned char *sha1)
165{ 165{
166 struct refinfo *ref; 166 struct refinfo *ref;
167 167
168 ref = xmalloc(sizeof (struct refinfo)); 168 ref = xmalloc(sizeof (struct refinfo));
169 ref->refname = xstrdup(refname); 169 ref->refname = xstrdup(refname);
170 ref->object = parse_object(sha1); 170 ref->object = parse_object(sha1);
171 switch (ref->object->type) { 171 switch (ref->object->type) {
172 case OBJ_TAG: 172 case OBJ_TAG:
173 ref->tag = cgit_parse_tag((struct tag *)ref->object); 173 ref->tag = cgit_parse_tag((struct tag *)ref->object);
174 break; 174 break;
175 case OBJ_COMMIT: 175 case OBJ_COMMIT:
176 ref->commit = cgit_parse_commit((struct commit *)ref->object); 176 ref->commit = cgit_parse_commit((struct commit *)ref->object);
177 break; 177 break;
178 } 178 }
179 return ref; 179 return ref;
180} 180}
181 181
182int cgit_refs_cb(const char *refname, const unsigned char *sha1, int flags, 182int cgit_refs_cb(const char *refname, const unsigned char *sha1, int flags,
183 void *cb_data) 183 void *cb_data)
184{ 184{
185 struct reflist *list = (struct reflist *)cb_data; 185 struct reflist *list = (struct reflist *)cb_data;
186 struct refinfo *info = cgit_mk_refinfo(refname, sha1); 186 struct refinfo *info = cgit_mk_refinfo(refname, sha1);
187 187
188 if (info) 188 if (info)
189 cgit_add_ref(list, info); 189 cgit_add_ref(list, info);
190 return 0; 190 return 0;
191} 191}
192 192
193void cgit_diff_tree_cb(struct diff_queue_struct *q, 193void cgit_diff_tree_cb(struct diff_queue_struct *q,
194 struct diff_options *options, void *data) 194 struct diff_options *options, void *data)
195{ 195{
196 int i; 196 int i;
197 197
198 for (i = 0; i < q->nr; i++) { 198 for (i = 0; i < q->nr; i++) {
199 if (q->queue[i]->status == 'U') 199 if (q->queue[i]->status == 'U')
200 continue; 200 continue;
201 ((filepair_fn)data)(q->queue[i]); 201 ((filepair_fn)data)(q->queue[i]);
202 } 202 }
203} 203}
204 204
205static int load_mmfile(mmfile_t *file, const unsigned char *sha1) 205static int load_mmfile(mmfile_t *file, const unsigned char *sha1)
206{ 206{
207 enum object_type type; 207 enum object_type type;
208 208
209 if (is_null_sha1(sha1)) { 209 if (is_null_sha1(sha1)) {
210 file->ptr = (char *)""; 210 file->ptr = (char *)"";
211 file->size = 0; 211 file->size = 0;
212 } else { 212 } else {
213 file->ptr = read_sha1_file(sha1, &type, 213 file->ptr = read_sha1_file(sha1, &type,
214 (unsigned long *)&file->size); 214 (unsigned long *)&file->size);
215 } 215 }
216 return 1; 216 return 1;
217} 217}
218 218
219/* 219/*
220 * Receive diff-buffers from xdiff and concatenate them as 220 * Receive diff-buffers from xdiff and concatenate them as
221 * needed across multiple callbacks. 221 * needed across multiple callbacks.
222 * 222 *
223 * This is basically a copy of xdiff-interface.c/xdiff_outf(), 223 * This is basically a copy of xdiff-interface.c/xdiff_outf(),
224 * ripped from git and modified to use globals instead of 224 * ripped from git and modified to use globals instead of
225 * a special callback-struct. 225 * a special callback-struct.
226 */ 226 */
227char *diffbuf = NULL; 227char *diffbuf = NULL;
228int buflen = 0; 228int buflen = 0;
229 229
230int filediff_cb(void *priv, mmbuffer_t *mb, int nbuf) 230int filediff_cb(void *priv, mmbuffer_t *mb, int nbuf)
231{ 231{
232 int i; 232 int i;
233 233
234 for (i = 0; i < nbuf; i++) { 234 for (i = 0; i < nbuf; i++) {
235 if (mb[i].ptr[mb[i].size-1] != '\n') { 235 if (mb[i].ptr[mb[i].size-1] != '\n') {
236 /* Incomplete line */ 236 /* Incomplete line */
237 diffbuf = xrealloc(diffbuf, buflen + mb[i].size); 237 diffbuf = xrealloc(diffbuf, buflen + mb[i].size);
238 memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size); 238 memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size);
239 buflen += mb[i].size; 239 buflen += mb[i].size;
240 continue; 240 continue;
241 } 241 }
242 242
243 /* we have a complete line */ 243 /* we have a complete line */
244 if (!diffbuf) { 244 if (!diffbuf) {
245 ((linediff_fn)priv)(mb[i].ptr, mb[i].size); 245 ((linediff_fn)priv)(mb[i].ptr, mb[i].size);
246 continue; 246 continue;
247 } 247 }
248 diffbuf = xrealloc(diffbuf, buflen + mb[i].size); 248 diffbuf = xrealloc(diffbuf, buflen + mb[i].size);
249 memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size); 249 memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size);
250 ((linediff_fn)priv)(diffbuf, buflen + mb[i].size); 250 ((linediff_fn)priv)(diffbuf, buflen + mb[i].size);
251 free(diffbuf); 251 free(diffbuf);
252 diffbuf = NULL; 252 diffbuf = NULL;
253 buflen = 0; 253 buflen = 0;
254 } 254 }
255 if (diffbuf) { 255 if (diffbuf) {
256 ((linediff_fn)priv)(diffbuf, buflen); 256 ((linediff_fn)priv)(diffbuf, buflen);
257 free(diffbuf); 257 free(diffbuf);
258 diffbuf = NULL; 258 diffbuf = NULL;
259 buflen = 0; 259 buflen = 0;
260 } 260 }
261 return 0; 261 return 0;
262} 262}
263 263
264int cgit_diff_files(const unsigned char *old_sha1, 264int cgit_diff_files(const unsigned char *old_sha1,
265 const unsigned char *new_sha1, unsigned long *old_size, 265 const unsigned char *new_sha1, unsigned long *old_size,
266 unsigned long *new_size, int *binary, int context, 266 unsigned long *new_size, int *binary, int context,
267 int ignorews, linediff_fn fn) 267 int ignorews, linediff_fn fn)
268{ 268{
269 mmfile_t file1, file2; 269 mmfile_t file1, file2;
270 xpparam_t diff_params; 270 xpparam_t diff_params;
271 xdemitconf_t emit_params; 271 xdemitconf_t emit_params;
272 xdemitcb_t emit_cb; 272 xdemitcb_t emit_cb;
273 273
274 if (!load_mmfile(&file1, old_sha1) || !load_mmfile(&file2, new_sha1)) 274 if (!load_mmfile(&file1, old_sha1) || !load_mmfile(&file2, new_sha1))
275 return 1; 275 return 1;
276 276
277 *old_size = file1.size; 277 *old_size = file1.size;
278 *new_size = file2.size; 278 *new_size = file2.size;
279 279
280 if ((file1.ptr && buffer_is_binary(file1.ptr, file1.size)) || 280 if ((file1.ptr && buffer_is_binary(file1.ptr, file1.size)) ||
281 (file2.ptr && buffer_is_binary(file2.ptr, file2.size))) { 281 (file2.ptr && buffer_is_binary(file2.ptr, file2.size))) {
282 *binary = 1; 282 *binary = 1;
283 if (file1.size) 283 if (file1.size)
284 free(file1.ptr); 284 free(file1.ptr);
285 if (file2.size) 285 if (file2.size)
286 free(file2.ptr); 286 free(file2.ptr);
287 return 0; 287 return 0;
288 } 288 }
289 289
290 memset(&diff_params, 0, sizeof(diff_params)); 290 memset(&diff_params, 0, sizeof(diff_params));
291 memset(&emit_params, 0, sizeof(emit_params)); 291 memset(&emit_params, 0, sizeof(emit_params));
292 memset(&emit_cb, 0, sizeof(emit_cb)); 292 memset(&emit_cb, 0, sizeof(emit_cb));
293 diff_params.flags = XDF_NEED_MINIMAL; 293 diff_params.flags = XDF_NEED_MINIMAL;
294 if (ignorews) 294 if (ignorews)
295 diff_params.flags |= XDF_IGNORE_WHITESPACE; 295 diff_params.flags |= XDF_IGNORE_WHITESPACE;
296 emit_params.ctxlen = context > 0 ? context : 3; 296 emit_params.ctxlen = context > 0 ? context : 3;
297 emit_params.flags = XDL_EMIT_FUNCNAMES; 297 emit_params.flags = XDL_EMIT_FUNCNAMES;
298 emit_cb.outf = filediff_cb; 298 emit_cb.outf = filediff_cb;
299 emit_cb.priv = fn; 299 emit_cb.priv = fn;
300 xdl_diff(&file1, &file2, &diff_params, &emit_params, &emit_cb); 300 xdl_diff(&file1, &file2, &diff_params, &emit_params, &emit_cb);
301 if (file1.size) 301 if (file1.size)
302 free(file1.ptr); 302 free(file1.ptr);
303 if (file2.size) 303 if (file2.size)
304 free(file2.ptr); 304 free(file2.ptr);
305 return 0; 305 return 0;
306} 306}
307 307
308void cgit_diff_tree(const unsigned char *old_sha1, 308void cgit_diff_tree(const unsigned char *old_sha1,
309 const unsigned char *new_sha1, 309 const unsigned char *new_sha1,
310 filepair_fn fn, const char *prefix, int ignorews) 310 filepair_fn fn, const char *prefix, int ignorews)
311{ 311{
312 struct diff_options opt; 312 struct diff_options opt;
313 int ret; 313 int ret;
314 int prefixlen; 314 int prefixlen;
315 315
316 diff_setup(&opt); 316 diff_setup(&opt);
317 opt.output_format = DIFF_FORMAT_CALLBACK; 317 opt.output_format = DIFF_FORMAT_CALLBACK;
318 opt.detect_rename = 1; 318 opt.detect_rename = 1;
319 opt.rename_limit = ctx.cfg.renamelimit; 319 opt.rename_limit = ctx.cfg.renamelimit;
320 DIFF_OPT_SET(&opt, RECURSIVE); 320 DIFF_OPT_SET(&opt, RECURSIVE);
321 if (ignorews) 321 if (ignorews)
322 DIFF_XDL_SET(&opt, IGNORE_WHITESPACE); 322 DIFF_XDL_SET(&opt, IGNORE_WHITESPACE);
323 opt.format_callback = cgit_diff_tree_cb; 323 opt.format_callback = cgit_diff_tree_cb;
324 opt.format_callback_data = fn; 324 opt.format_callback_data = fn;
325 if (prefix) { 325 if (prefix) {
326 opt.nr_paths = 1; 326 opt.nr_paths = 1;
327 opt.paths = &prefix; 327 opt.paths = &prefix;
328 prefixlen = strlen(prefix); 328 prefixlen = strlen(prefix);
329 opt.pathlens = &prefixlen; 329 opt.pathlens = &prefixlen;
330 } 330 }
331 diff_setup_done(&opt); 331 diff_setup_done(&opt);
332 332
333 if (old_sha1 && !is_null_sha1(old_sha1)) 333 if (old_sha1 && !is_null_sha1(old_sha1))
334 ret = diff_tree_sha1(old_sha1, new_sha1, "", &opt); 334 ret = diff_tree_sha1(old_sha1, new_sha1, "", &opt);
335 else 335 else
336 ret = diff_root_tree_sha1(new_sha1, "", &opt); 336 ret = diff_root_tree_sha1(new_sha1, "", &opt);
337 diffcore_std(&opt); 337 diffcore_std(&opt);
338 diff_flush(&opt); 338 diff_flush(&opt);
339} 339}
340 340
341void cgit_diff_commit(struct commit *commit, filepair_fn fn) 341void cgit_diff_commit(struct commit *commit, filepair_fn fn, const char *prefix)
342{ 342{
343 unsigned char *old_sha1 = NULL; 343 unsigned char *old_sha1 = NULL;
344 344
345 if (commit->parents) 345 if (commit->parents)
346 old_sha1 = commit->parents->item->object.sha1; 346 old_sha1 = commit->parents->item->object.sha1;
347 cgit_diff_tree(old_sha1, commit->object.sha1, fn, NULL, 347 cgit_diff_tree(old_sha1, commit->object.sha1, fn, prefix,
348 ctx.qry.ignorews); 348 ctx.qry.ignorews);
349} 349}
350 350
351int cgit_parse_snapshots_mask(const char *str) 351int cgit_parse_snapshots_mask(const char *str)
352{ 352{
353 const struct cgit_snapshot_format *f; 353 const struct cgit_snapshot_format *f;
354 static const char *delim = " \t,:/|;"; 354 static const char *delim = " \t,:/|;";
355 int tl, sl, rv = 0; 355 int tl, sl, rv = 0;
356 356
357 /* favor legacy setting */ 357 /* favor legacy setting */
358 if(atoi(str)) 358 if(atoi(str))
359 return 1; 359 return 1;
360 for(;;) { 360 for(;;) {
361 str += strspn(str,delim); 361 str += strspn(str,delim);
362 tl = strcspn(str,delim); 362 tl = strcspn(str,delim);
363 if (!tl) 363 if (!tl)
364 break; 364 break;
365 for (f = cgit_snapshot_formats; f->suffix; f++) { 365 for (f = cgit_snapshot_formats; f->suffix; f++) {
366 sl = strlen(f->suffix); 366 sl = strlen(f->suffix);
367 if((tl == sl && !strncmp(f->suffix, str, tl)) || 367 if((tl == sl && !strncmp(f->suffix, str, tl)) ||
368 (tl == sl-1 && !strncmp(f->suffix+1, str, tl-1))) { 368 (tl == sl-1 && !strncmp(f->suffix+1, str, tl-1))) {
369 rv |= f->bit; 369 rv |= f->bit;
370 break; 370 break;
371 } 371 }
372 } 372 }
373 str += tl; 373 str += tl;
374 } 374 }
375 return rv; 375 return rv;
376} 376}
377 377
378int cgit_open_filter(struct cgit_filter *filter) 378int cgit_open_filter(struct cgit_filter *filter)
379{ 379{
380 380
381 filter->old_stdout = chk_positive(dup(STDOUT_FILENO), 381 filter->old_stdout = chk_positive(dup(STDOUT_FILENO),
382 "Unable to duplicate STDOUT"); 382 "Unable to duplicate STDOUT");
383 chk_zero(pipe(filter->pipe_fh), "Unable to create pipe to subprocess"); 383 chk_zero(pipe(filter->pipe_fh), "Unable to create pipe to subprocess");
384 filter->pid = chk_non_negative(fork(), "Unable to create subprocess"); 384 filter->pid = chk_non_negative(fork(), "Unable to create subprocess");
385 if (filter->pid == 0) { 385 if (filter->pid == 0) {
386 close(filter->pipe_fh[1]); 386 close(filter->pipe_fh[1]);
387 chk_non_negative(dup2(filter->pipe_fh[0], STDIN_FILENO), 387 chk_non_negative(dup2(filter->pipe_fh[0], STDIN_FILENO),
388 "Unable to use pipe as STDIN"); 388 "Unable to use pipe as STDIN");
389 execvp(filter->cmd, filter->argv); 389 execvp(filter->cmd, filter->argv);
390 die("Unable to exec subprocess %s: %s (%d)", filter->cmd, 390 die("Unable to exec subprocess %s: %s (%d)", filter->cmd,
391 strerror(errno), errno); 391 strerror(errno), errno);
392 } 392 }
393 close(filter->pipe_fh[0]); 393 close(filter->pipe_fh[0]);
394 chk_non_negative(dup2(filter->pipe_fh[1], STDOUT_FILENO), 394 chk_non_negative(dup2(filter->pipe_fh[1], STDOUT_FILENO),
395 "Unable to use pipe as STDOUT"); 395 "Unable to use pipe as STDOUT");
396 close(filter->pipe_fh[1]); 396 close(filter->pipe_fh[1]);
397 return 0; 397 return 0;
398} 398}
399 399
400int cgit_close_filter(struct cgit_filter *filter) 400int cgit_close_filter(struct cgit_filter *filter)
401{ 401{
402 chk_non_negative(dup2(filter->old_stdout, STDOUT_FILENO), 402 chk_non_negative(dup2(filter->old_stdout, STDOUT_FILENO),
403 "Unable to restore STDOUT"); 403 "Unable to restore STDOUT");
404 close(filter->old_stdout); 404 close(filter->old_stdout);
405 if (filter->pid < 0) 405 if (filter->pid < 0)
406 return 0; 406 return 0;
407 waitpid(filter->pid, &filter->exitstatus, 0); 407 waitpid(filter->pid, &filter->exitstatus, 0);
408 if (WIFEXITED(filter->exitstatus) && !WEXITSTATUS(filter->exitstatus)) 408 if (WIFEXITED(filter->exitstatus) && !WEXITSTATUS(filter->exitstatus))
409 return 0; 409 return 0;
410 die("Subprocess %s exited abnormally", filter->cmd); 410 die("Subprocess %s exited abnormally", filter->cmd);
411} 411}
412 412
413/* Read the content of the specified file into a newly allocated buffer, 413/* Read the content of the specified file into a newly allocated buffer,
414 * zeroterminate the buffer and return 0 on success, errno otherwise. 414 * zeroterminate the buffer and return 0 on success, errno otherwise.
415 */ 415 */
416int readfile(const char *path, char **buf, size_t *size) 416int readfile(const char *path, char **buf, size_t *size)
417{ 417{
418 int fd, e; 418 int fd, e;
419 struct stat st; 419 struct stat st;
420 420
421 fd = open(path, O_RDONLY); 421 fd = open(path, O_RDONLY);
422 if (fd == -1) 422 if (fd == -1)
423 return errno; 423 return errno;
424 if (fstat(fd, &st)) { 424 if (fstat(fd, &st)) {
425 e = errno; 425 e = errno;
426 close(fd); 426 close(fd);
427 return e; 427 return e;
428 } 428 }
429 if (!S_ISREG(st.st_mode)) { 429 if (!S_ISREG(st.st_mode)) {
430 close(fd); 430 close(fd);
431 return EISDIR; 431 return EISDIR;
432 } 432 }
433 *buf = xmalloc(st.st_size + 1); 433 *buf = xmalloc(st.st_size + 1);
434 *size = read_in_full(fd, *buf, st.st_size); 434 *size = read_in_full(fd, *buf, st.st_size);
435 e = errno; 435 e = errno;
436 (*buf)[*size] = '\0'; 436 (*buf)[*size] = '\0';
437 close(fd); 437 close(fd);
438 return (*size == st.st_size ? 0 : e); 438 return (*size == st.st_size ? 0 : e);
439} 439}
440 440
441int is_token_char(char c) 441int is_token_char(char c)
442{ 442{
443 return isalnum(c) || c == '_'; 443 return isalnum(c) || c == '_';
444} 444}
445 445
446/* Replace name with getenv(name), return pointer to zero-terminating char 446/* Replace name with getenv(name), return pointer to zero-terminating char
447 */ 447 */
448char *expand_macro(char *name, int maxlength) 448char *expand_macro(char *name, int maxlength)
449{ 449{
450 char *value; 450 char *value;
451 int len; 451 int len;
452 452
453 len = 0; 453 len = 0;
454 value = getenv(name); 454 value = getenv(name);
455 if (value) { 455 if (value) {
456 len = strlen(value); 456 len = strlen(value);
457 if (len > maxlength) 457 if (len > maxlength)
458 len = maxlength; 458 len = maxlength;
459 strncpy(name, value, len); 459 strncpy(name, value, len);
460 } 460 }
461 return name + len; 461 return name + len;
462} 462}
463 463
464#define EXPBUFSIZE (1024 * 8) 464#define EXPBUFSIZE (1024 * 8)
465 465
466/* Replace all tokens prefixed by '$' in the specified text with the 466/* Replace all tokens prefixed by '$' in the specified text with the
467 * value of the named environment variable. 467 * value of the named environment variable.
468 * NB: the return value is a static buffer, i.e. it must be strdup'd 468 * NB: the return value is a static buffer, i.e. it must be strdup'd
469 * by the caller. 469 * by the caller.
470 */ 470 */
471char *expand_macros(const char *txt) 471char *expand_macros(const char *txt)
472{ 472{
473 static char result[EXPBUFSIZE]; 473 static char result[EXPBUFSIZE];
474 char *p, *start; 474 char *p, *start;
475 int len; 475 int len;
476 476
477 p = result; 477 p = result;
478 start = NULL; 478 start = NULL;
479 while (p < result + EXPBUFSIZE - 1 && txt && *txt) { 479 while (p < result + EXPBUFSIZE - 1 && txt && *txt) {
480 *p = *txt; 480 *p = *txt;
481 if (start) { 481 if (start) {
482 if (!is_token_char(*txt)) { 482 if (!is_token_char(*txt)) {
483 if (p - start > 0) { 483 if (p - start > 0) {
484 *p = '\0'; 484 *p = '\0';
485 len = result + EXPBUFSIZE - start - 1; 485 len = result + EXPBUFSIZE - start - 1;
486 p = expand_macro(start, len) - 1; 486 p = expand_macro(start, len) - 1;
487 } 487 }
488 start = NULL; 488 start = NULL;
489 txt--; 489 txt--;
490 } 490 }
491 p++; 491 p++;
492 txt++; 492 txt++;
493 continue; 493 continue;
494 } 494 }
495 if (*txt == '$') { 495 if (*txt == '$') {
496 start = p; 496 start = p;
497 txt++; 497 txt++;
498 continue; 498 continue;
499 } 499 }
500 p++; 500 p++;
501 txt++; 501 txt++;
502 } 502 }
503 *p = '\0'; 503 *p = '\0';
504 if (start && p - start > 0) { 504 if (start && p - start > 0) {
505 len = result + EXPBUFSIZE - start - 1; 505 len = result + EXPBUFSIZE - start - 1;
506 p = expand_macro(start, len); 506 p = expand_macro(start, len);
507 *p = '\0'; 507 *p = '\0';
508 } 508 }
509 return result; 509 return result;
510} 510}