Line data Source code
1 : /*-
2 : * Copyright (c) 2011-2014 Baptiste Daroussin <bapt@FreeBSD.org>
3 : * Copyright (c) 2011-2012 Julien Laffaye <jlaffaye@FreeBSD.org>
4 : * Copyright (c) 2013 Vsevolod Stakhov <vsevolod@FreeBSD.org>
5 : * All rights reserved.
6 : *
7 : * Redistribution and use in source and binary forms, with or without
8 : * modification, are permitted provided that the following conditions
9 : * are met:
10 : * 1. Redistributions of source code must retain the above copyright
11 : * notice, this list of conditions and the following disclaimer
12 : * in this position and unchanged.
13 : * 2. Redistributions in binary form must reproduce the above copyright
14 : * notice, this list of conditions and the following disclaimer in the
15 : * documentation and/or other materials provided with the distribution.
16 : *
17 : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
18 : * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 : * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 : * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
21 : * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 : * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 : * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 : * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 : * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 : */
28 :
29 : #include <pkg_config.h>
30 :
31 : #include <sys/stat.h>
32 : #include <sys/param.h>
33 : #include <stdio.h>
34 :
35 : #include <assert.h>
36 : #include <errno.h>
37 : #include <fcntl.h>
38 : #include <stdlib.h>
39 : #include <unistd.h>
40 : #include <string.h>
41 : #include <ucl.h>
42 : #include <uthash.h>
43 : #include <utlist.h>
44 : #include <ctype.h>
45 : #include <fnmatch.h>
46 : #include <paths.h>
47 : #include <float.h>
48 : #include <math.h>
49 :
50 : #include <bsd_compat.h>
51 :
52 : #include "pkg.h"
53 : #include "private/event.h"
54 : #include "private/utils.h"
55 :
56 : void
57 824 : sbuf_init(struct sbuf **buf)
58 : {
59 824 : if (*buf == NULL)
60 644 : *buf = sbuf_new_auto();
61 : else
62 180 : sbuf_clear(*buf);
63 824 : }
64 :
65 : int
66 50 : sbuf_set(struct sbuf **buf, const char *str)
67 : {
68 50 : if (*buf == NULL)
69 50 : *buf = sbuf_new_auto();
70 :
71 50 : if (str == NULL)
72 0 : return (-1);
73 :
74 50 : sbuf_cpy(*buf, str);
75 50 : sbuf_finish(*buf);
76 50 : return (0);
77 : }
78 :
79 : void
80 12 : sbuf_reset(struct sbuf *buf)
81 : {
82 12 : if (buf != NULL) {
83 12 : sbuf_clear(buf);
84 12 : sbuf_finish(buf);
85 : }
86 12 : }
87 :
88 : void
89 2302 : sbuf_free(struct sbuf *buf)
90 : {
91 2302 : if (buf != NULL)
92 152 : sbuf_delete(buf);
93 2302 : }
94 :
95 : int
96 75 : mkdirs(const char *_path)
97 : {
98 : char path[MAXPATHLEN];
99 : char *p;
100 :
101 75 : strlcpy(path, _path, sizeof(path));
102 75 : p = path;
103 75 : if (*p == '/')
104 33 : p++;
105 :
106 : for (;;) {
107 149 : if ((p = strchr(p, '/')) != NULL)
108 74 : *p = '\0';
109 :
110 149 : if (mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO) < 0)
111 149 : if (errno != EEXIST && errno != EISDIR) {
112 0 : pkg_emit_errno("mkdir", path);
113 0 : return (EPKG_FATAL);
114 : }
115 :
116 : /* that was the last element of the path */
117 149 : if (p == NULL)
118 75 : break;
119 :
120 74 : *p = '/';
121 74 : p++;
122 74 : }
123 :
124 75 : return (EPKG_OK);
125 : }
126 : int
127 25 : file_to_bufferat(int dfd, const char *path, char **buffer, off_t *sz)
128 : {
129 25 : int fd = -1;
130 : struct stat st;
131 25 : int retcode = EPKG_OK;
132 :
133 25 : assert(path != NULL && path[0] != '\0');
134 25 : assert(buffer != NULL);
135 25 : assert(sz != NULL);
136 :
137 25 : if ((fd = openat(dfd, path, O_RDONLY)) == -1) {
138 0 : pkg_emit_errno("openat", path);
139 0 : retcode = EPKG_FATAL;
140 0 : goto cleanup;
141 : }
142 :
143 25 : if (fstatat(dfd, path, &st, 0) == -1) {
144 0 : pkg_emit_errno("fstatat", path);
145 0 : retcode = EPKG_FATAL;
146 0 : goto cleanup;
147 : }
148 :
149 25 : if ((*buffer = malloc(st.st_size + 1)) == NULL) {
150 0 : pkg_emit_errno("malloc", "");
151 0 : retcode = EPKG_FATAL;
152 0 : goto cleanup;
153 : }
154 :
155 25 : if (read(fd, *buffer, st.st_size) == -1) {
156 0 : pkg_emit_errno("read", path);
157 0 : retcode = EPKG_FATAL;
158 0 : goto cleanup;
159 : }
160 :
161 : cleanup:
162 25 : if (fd >= 0)
163 25 : close(fd);
164 :
165 25 : if (retcode == EPKG_OK) {
166 25 : (*buffer)[st.st_size] = '\0';
167 25 : *sz = st.st_size;
168 : } else {
169 0 : *buffer = NULL;
170 0 : *sz = -1;
171 : }
172 25 : return (retcode);
173 : }
174 :
175 : int
176 0 : file_to_buffer(const char *path, char **buffer, off_t *sz)
177 : {
178 0 : int fd = -1;
179 : struct stat st;
180 0 : int retcode = EPKG_OK;
181 :
182 0 : assert(path != NULL && path[0] != '\0');
183 0 : assert(buffer != NULL);
184 0 : assert(sz != NULL);
185 :
186 0 : if ((fd = open(path, O_RDONLY)) == -1) {
187 0 : pkg_emit_errno("open", path);
188 0 : retcode = EPKG_FATAL;
189 0 : goto cleanup;
190 : }
191 :
192 0 : if (fstat(fd, &st) == -1) {
193 0 : pkg_emit_errno("fstat", path);
194 0 : retcode = EPKG_FATAL;
195 0 : goto cleanup;
196 : }
197 :
198 0 : if ((*buffer = malloc(st.st_size + 1)) == NULL) {
199 0 : pkg_emit_errno("malloc", "");
200 0 : retcode = EPKG_FATAL;
201 0 : goto cleanup;
202 : }
203 :
204 0 : if (read(fd, *buffer, st.st_size) == -1) {
205 0 : pkg_emit_errno("read", path);
206 0 : retcode = EPKG_FATAL;
207 0 : goto cleanup;
208 : }
209 :
210 : cleanup:
211 0 : if (fd >= 0)
212 0 : close(fd);
213 :
214 0 : if (retcode == EPKG_OK) {
215 0 : (*buffer)[st.st_size] = '\0';
216 0 : *sz = st.st_size;
217 : } else {
218 0 : *buffer = NULL;
219 0 : *sz = -1;
220 : }
221 0 : return (retcode);
222 : }
223 :
224 : int
225 8 : format_exec_cmd(char **dest, const char *in, const char *prefix,
226 : const char *plist_file, char *line, int argc, char **argv)
227 : {
228 8 : struct sbuf *buf = sbuf_new_auto();
229 : char path[MAXPATHLEN];
230 : char *cp;
231 : size_t sz;
232 :
233 78 : while (in[0] != '\0') {
234 64 : if (in[0] != '%') {
235 48 : sbuf_putc(buf, in[0]);
236 48 : in++;
237 48 : continue;
238 : }
239 16 : in++;
240 16 : switch(in[0]) {
241 : case 'D':
242 0 : sbuf_cat(buf, prefix);
243 0 : break;
244 : case 'F':
245 0 : if (plist_file == NULL || plist_file[0] == '\0') {
246 0 : pkg_emit_error("No files defined %%F couldn't "
247 : "be expanded, ignoring %s", in);
248 0 : sbuf_finish(buf);
249 0 : sbuf_free(buf);
250 0 : return (EPKG_FATAL);
251 : }
252 0 : sbuf_cat(buf, plist_file);
253 0 : break;
254 : case 'f':
255 0 : if (plist_file == NULL || plist_file[0] == '\0') {
256 0 : pkg_emit_error("No files defined %%f couldn't "
257 : "be expanded, ignoring %s", in);
258 0 : sbuf_finish(buf);
259 0 : sbuf_free(buf);
260 0 : return (EPKG_FATAL);
261 : }
262 0 : if (prefix[strlen(prefix) - 1] == '/')
263 0 : snprintf(path, sizeof(path), "%s%s",
264 : prefix, plist_file);
265 : else
266 0 : snprintf(path, sizeof(path), "%s/%s",
267 : prefix, plist_file);
268 0 : cp = strrchr(path, '/');
269 0 : cp ++;
270 0 : sbuf_cat(buf, cp);
271 0 : break;
272 : case 'B':
273 0 : if (plist_file == NULL || plist_file[0] == '\0') {
274 0 : pkg_emit_error("No files defined %%B couldn't "
275 : "be expanded, ignoring %s", in);
276 0 : sbuf_finish(buf);
277 0 : sbuf_free(buf);
278 0 : return (EPKG_FATAL);
279 : }
280 0 : if (prefix[strlen(prefix) - 1] == '/')
281 0 : snprintf(path, sizeof(path), "%s%s", prefix,
282 : plist_file);
283 : else
284 0 : snprintf(path, sizeof(path), "%s/%s", prefix,
285 : plist_file);
286 0 : cp = strrchr(path, '/');
287 0 : cp[0] = '\0';
288 0 : sbuf_cat(buf, path);
289 0 : break;
290 : case '%':
291 0 : sbuf_putc(buf, '%');
292 0 : break;
293 : case '@':
294 0 : if (line != NULL) {
295 0 : sbuf_cat(buf, line);
296 0 : break;
297 : }
298 :
299 : /*
300 : * no break here because if line is not
301 : * given (default exec) %@ does not
302 : * exists
303 : */
304 : case '#':
305 0 : sbuf_putc(buf, argc);
306 0 : break;
307 : default:
308 16 : if ((sz = strspn(in, "0123456789")) > 0) {
309 16 : int pos = strtol(in, NULL, 10);
310 16 : if (pos > argc) {
311 2 : pkg_emit_error("Requesting argument "
312 : "%%%d while only %d arguments are"
313 : " available", pos, argc);
314 2 : sbuf_finish(buf);
315 2 : sbuf_free(buf);
316 2 : return (EPKG_FATAL);
317 : }
318 14 : sbuf_cat(buf, argv[pos -1]);
319 14 : in += sz -1;
320 14 : break;
321 : }
322 0 : sbuf_putc(buf, '%');
323 0 : sbuf_putc(buf, in[0]);
324 0 : break;
325 : }
326 :
327 14 : in++;
328 : }
329 :
330 6 : sbuf_finish(buf);
331 6 : *dest = strdup(sbuf_data(buf));
332 6 : sbuf_free(buf);
333 :
334 6 : return (EPKG_OK);
335 : }
336 :
337 : int
338 74 : is_dir(const char *path)
339 : {
340 : struct stat st;
341 :
342 74 : return (stat(path, &st) == 0 && S_ISDIR(st.st_mode));
343 : }
344 :
345 : bool
346 18 : string_end_with(const char *path, const char *str)
347 : {
348 : size_t n, s;
349 18 : const char *p = NULL;
350 :
351 18 : s = strlen(str);
352 18 : n = strlen(path);
353 :
354 18 : if (n < s)
355 0 : return (false);
356 :
357 18 : p = &path[n - s];
358 :
359 18 : if (strcmp(p, str) == 0)
360 0 : return (true);
361 :
362 18 : return (false);
363 : }
364 :
365 : bool
366 0 : check_for_hardlink(hardlinks_t *hl, struct stat *st)
367 : {
368 : int absent;
369 :
370 0 : kh_put_hardlinks(hl, st->st_ino, &absent);
371 0 : if (absent == 0)
372 0 : return (true);
373 :
374 0 : return (false);
375 : }
376 :
377 : bool
378 35 : is_valid_abi(const char *arch, bool emit_error) {
379 : const char *myarch, *myarch_legacy;
380 :
381 35 : myarch = pkg_object_string(pkg_config_get("ABI"));
382 35 : myarch_legacy = pkg_object_string(pkg_config_get("ALTABI"));
383 :
384 58 : if (fnmatch(arch, myarch, FNM_CASEFOLD) == FNM_NOMATCH &&
385 46 : strncasecmp(arch, myarch, strlen(myarch)) != 0 &&
386 23 : strncasecmp(arch, myarch_legacy, strlen(myarch_legacy)) != 0) {
387 0 : if (emit_error)
388 0 : pkg_emit_error("wrong architecture: %s instead of %s",
389 : arch, myarch);
390 0 : return (false);
391 : }
392 :
393 35 : return (true);
394 : }
395 :
396 : void
397 0 : set_nonblocking(int fd)
398 : {
399 : int flags;
400 :
401 0 : if ((flags = fcntl(fd, F_GETFL)) == -1)
402 0 : return;
403 0 : if (!(flags & O_NONBLOCK)) {
404 0 : flags |= O_NONBLOCK;
405 0 : fcntl(fd, F_SETFL, flags);
406 : }
407 : }
408 :
409 : void
410 0 : set_blocking(int fd)
411 : {
412 : int flags;
413 :
414 0 : if ((flags = fcntl(fd, F_GETFL)) == -1)
415 0 : return;
416 0 : if (flags & O_NONBLOCK) {
417 0 : flags &= ~O_NONBLOCK;
418 0 : fcntl(fd, F_SETFL, flags);
419 : }
420 : }
421 :
422 : /* Spawn a process from pfunc, returning it's pid. The fds array passed will
423 : * be filled with two descriptors: fds[0] will read from the child process,
424 : * and fds[1] will write to it.
425 : * Similarly, the child process will receive a reading/writing fd set (in
426 : * that same order) as arguments.
427 : */
428 : extern char **environ;
429 : pid_t
430 0 : process_spawn_pipe(FILE *inout[2], const char *command)
431 : {
432 : pid_t pid;
433 : int pipes[4];
434 : char *argv[4];
435 :
436 : /* Parent read/child write pipe */
437 0 : if (pipe(&pipes[0]) == -1)
438 0 : return (-1);
439 :
440 : /* Child read/parent write pipe */
441 0 : if (pipe(&pipes[2]) == -1) {
442 0 : close(pipes[0]);
443 0 : close(pipes[1]);
444 0 : return (-1);
445 : }
446 :
447 0 : argv[0] = __DECONST(char *, "sh");
448 0 : argv[1] = __DECONST(char *, "-c");
449 0 : argv[2] = __DECONST(char *, command);
450 0 : argv[3] = NULL;
451 :
452 0 : pid = fork();
453 0 : if (pid > 0) {
454 : /* Parent process */
455 0 : inout[0] = fdopen(pipes[0], "r");
456 0 : inout[1] = fdopen(pipes[3], "w");
457 :
458 0 : close(pipes[1]);
459 0 : close(pipes[2]);
460 :
461 0 : return (pid);
462 :
463 0 : } else if (pid == 0) {
464 0 : close(pipes[0]);
465 0 : close(pipes[3]);
466 :
467 0 : if (pipes[1] != STDOUT_FILENO) {
468 0 : dup2(pipes[1], STDOUT_FILENO);
469 0 : close(pipes[1]);
470 : }
471 0 : if (pipes[2] != STDIN_FILENO) {
472 0 : dup2(pipes[2], STDIN_FILENO);
473 0 : close(pipes[2]);
474 : }
475 0 : closefrom(STDERR_FILENO + 1);
476 :
477 0 : execve(_PATH_BSHELL, argv, environ);
478 :
479 0 : exit(127);
480 : }
481 :
482 0 : return (-1); /* ? */
483 : }
484 :
485 : static int
486 176 : ucl_file_append_character(unsigned char c, size_t len, void *data)
487 : {
488 : size_t i;
489 176 : FILE *out = data;
490 :
491 352 : for (i = 0; i < len; i++)
492 176 : fprintf(out, "%c", c);
493 :
494 176 : return (0);
495 : }
496 :
497 : static int
498 385 : ucl_file_append_len(const unsigned char *str, size_t len, void *data)
499 : {
500 385 : FILE *out = data;
501 :
502 385 : fprintf(out, "%.*s", (int)len, str);
503 :
504 385 : return (0);
505 : }
506 :
507 : static int
508 11 : ucl_file_append_int(int64_t val, void *data)
509 : {
510 11 : FILE *out = data;
511 :
512 11 : fprintf(out, "%"PRId64, val);
513 :
514 11 : return (0);
515 : }
516 :
517 : static int
518 0 : ucl_file_append_double(double val, void *data)
519 : {
520 0 : FILE *out = data;
521 0 : const double delta = 0.0000001;
522 :
523 0 : if (val == (double)(int)val) {
524 0 : fprintf(out, "%.1lf", val);
525 0 : } else if (fabs(val - (double)(int)val) < delta) {
526 0 : fprintf(out, "%.*lg", DBL_DIG, val);
527 : } else {
528 0 : fprintf(out, "%lf", val);
529 : }
530 :
531 0 : return (0);
532 : }
533 :
534 : static int
535 14188 : ucl_sbuf_append_character(unsigned char c, size_t len, void *data)
536 : {
537 14188 : struct sbuf *buf = data;
538 : size_t i;
539 :
540 28376 : for (i = 0; i < len; i++)
541 14188 : sbuf_putc(buf, c);
542 :
543 14188 : return (0);
544 : }
545 :
546 : static int
547 4421 : ucl_sbuf_append_len(const unsigned char *str, size_t len, void *data)
548 : {
549 4421 : struct sbuf *buf = data;
550 :
551 4421 : sbuf_bcat(buf, str, len);
552 :
553 4421 : return (0);
554 : }
555 :
556 : static int
557 180 : ucl_sbuf_append_int(int64_t val, void *data)
558 : {
559 180 : struct sbuf *buf = data;
560 :
561 180 : sbuf_printf(buf, "%"PRId64, val);
562 :
563 180 : return (0);
564 : }
565 :
566 : static int
567 0 : ucl_sbuf_append_double(double val, void *data)
568 : {
569 0 : struct sbuf *buf = data;
570 0 : const double delta = 0.0000001;
571 :
572 0 : if (val == (double)(int)val) {
573 0 : sbuf_printf(buf, "%.1lf", val);
574 0 : } else if (fabs(val - (double)(int)val) < delta) {
575 0 : sbuf_printf(buf, "%.*lg", DBL_DIG, val);
576 : } else {
577 0 : sbuf_printf(buf, "%lf", val);
578 : }
579 :
580 0 : return (0);
581 : }
582 :
583 : bool
584 11 : ucl_object_emit_file(const ucl_object_t *obj, enum ucl_emitter emit_type,
585 : FILE *out)
586 : {
587 11 : struct ucl_emitter_functions func = {
588 : .ucl_emitter_append_character = ucl_file_append_character,
589 : .ucl_emitter_append_len = ucl_file_append_len,
590 : .ucl_emitter_append_int = ucl_file_append_int,
591 : .ucl_emitter_append_double = ucl_file_append_double
592 : };
593 :
594 11 : if (obj == NULL)
595 0 : return (false);
596 :
597 11 : func.ud = out;
598 :
599 11 : return (ucl_object_emit_full(obj, emit_type, &func));
600 :
601 :
602 : }
603 :
604 : bool
605 149 : ucl_object_emit_sbuf(const ucl_object_t *obj, enum ucl_emitter emit_type,
606 : struct sbuf **buf)
607 : {
608 149 : bool ret = false;
609 149 : struct ucl_emitter_functions func = {
610 : .ucl_emitter_append_character = ucl_sbuf_append_character,
611 : .ucl_emitter_append_len = ucl_sbuf_append_len,
612 : .ucl_emitter_append_int = ucl_sbuf_append_int,
613 : .ucl_emitter_append_double = ucl_sbuf_append_double
614 : };
615 :
616 149 : if (*buf == NULL)
617 0 : *buf = sbuf_new_auto();
618 : else
619 149 : sbuf_clear(*buf);
620 :
621 149 : func.ud = *buf;
622 :
623 149 : ret = ucl_object_emit_full(obj, emit_type, &func);
624 149 : sbuf_finish(*buf);
625 :
626 149 : return (ret);
627 : }
628 :
629 : /* A bit like strsep(), except it accounts for "double" and 'single'
630 : quotes. Unlike strsep(), returns the next arg string, trimmed of
631 : whitespace or enclosing quotes, and updates **args to point at the
632 : character after that. Sets *args to NULL when it has been
633 : completely consumed. Quoted strings run from the first encountered
634 : quotemark to the next one of the same type or the terminating NULL.
635 : Quoted strings can contain the /other/ type of quote mark, which
636 : loses any special significance. There isn't an escape
637 : character. */
638 :
639 : enum parse_states {
640 : START,
641 : ORDINARY_TEXT,
642 : OPEN_SINGLE_QUOTES,
643 : IN_SINGLE_QUOTES,
644 : OPEN_DOUBLE_QUOTES,
645 : IN_DOUBLE_QUOTES,
646 : };
647 :
648 : char *
649 18 : pkg_utils_tokenize(char **args)
650 : {
651 : char *p, *p_start;
652 18 : enum parse_states parse_state = START;
653 :
654 18 : assert(*args != NULL);
655 :
656 54 : for (p = p_start = *args; *p != '\0'; p++) {
657 44 : switch (parse_state) {
658 : case START:
659 18 : if (!isspace(*p)) {
660 18 : if (*p == '"')
661 0 : parse_state = OPEN_DOUBLE_QUOTES;
662 18 : else if (*p == '\'')
663 0 : parse_state = OPEN_SINGLE_QUOTES;
664 : else {
665 18 : parse_state = ORDINARY_TEXT;
666 18 : p_start = p;
667 : }
668 : } else
669 0 : p_start = p;
670 18 : break;
671 : case ORDINARY_TEXT:
672 26 : if (isspace(*p))
673 8 : goto finish;
674 18 : break;
675 : case OPEN_SINGLE_QUOTES:
676 0 : p_start = p;
677 0 : if (*p == '\'')
678 0 : goto finish;
679 :
680 0 : parse_state = IN_SINGLE_QUOTES;
681 0 : break;
682 : case IN_SINGLE_QUOTES:
683 0 : if (*p == '\'')
684 0 : goto finish;
685 0 : break;
686 : case OPEN_DOUBLE_QUOTES:
687 0 : p_start = p;
688 0 : if (*p == '"')
689 0 : goto finish;
690 0 : parse_state = IN_DOUBLE_QUOTES;
691 0 : break;
692 : case IN_DOUBLE_QUOTES:
693 0 : if (*p == '"')
694 0 : goto finish;
695 0 : break;
696 : }
697 : }
698 :
699 : finish:
700 18 : if (*p == '\0')
701 10 : *args = NULL; /* All done */
702 : else {
703 8 : *p = '\0';
704 8 : p++;
705 8 : if (*p == '\0' || parse_state == START)
706 0 : *args = NULL; /* whitespace or nothing left */
707 : else
708 8 : *args = p;
709 : }
710 18 : return (p_start);
711 : }
712 :
713 : int
714 10 : pkg_utils_count_spaces(const char *args)
715 : {
716 : int spaces;
717 : const char *p;
718 :
719 54 : for (spaces = 0, p = args; *p != '\0'; p++)
720 44 : if (isspace(*p))
721 8 : spaces++;
722 :
723 10 : return (spaces);
724 : }
725 :
726 : /* unlike realpath(3), this routine does not expand symbolic links */
727 : char *
728 235 : pkg_absolutepath(const char *src, char *dest, size_t dest_size) {
729 : size_t dest_len, src_len, cur_len;
730 : const char *cur, *next;
731 :
732 235 : src_len = strlen(src);
733 235 : bzero(dest, dest_size);
734 235 : if (src_len != 0 && src[0] != '/') {
735 : /* relative path, we use cwd */
736 0 : if (getcwd(dest, dest_size) == NULL)
737 0 : return (NULL);
738 : }
739 235 : dest_len = strlen(dest);
740 :
741 1283 : for (cur = next = src; next != NULL; cur = next + 1) {
742 1048 : next = strchr(cur, '/');
743 1048 : if (next != NULL)
744 813 : cur_len = next - cur;
745 : else
746 235 : cur_len = strlen(cur);
747 :
748 : /* check for special cases "", "." and ".." */
749 1048 : if (cur_len == 0)
750 251 : continue;
751 797 : else if (cur_len == 1 && cur[0] == '.')
752 0 : continue;
753 797 : else if (cur_len == 2 && cur[0] == '.' && cur[1] == '.') {
754 0 : const char *slash = strrchr(dest, '/');
755 0 : if (slash != NULL) {
756 0 : dest_len = slash - dest;
757 0 : dest[dest_len] = '\0';
758 : }
759 0 : continue;
760 : }
761 :
762 797 : if (dest_len + 1 + cur_len >= dest_size)
763 0 : return (NULL);
764 797 : dest[dest_len++] = '/';
765 797 : (void)memcpy(dest + dest_len, cur, cur_len);
766 797 : dest_len += cur_len;
767 797 : dest[dest_len] = '\0';
768 : }
769 :
770 235 : if (dest_len == 0) {
771 0 : if (strlcpy(dest, "/", dest_size) >= dest_size)
772 0 : return (NULL);
773 : }
774 :
775 235 : return (dest);
776 : }
|