]> Dogcows Code - chaz/tar/blob - src/misc.c
tar: don't crash if getcwd fails
[chaz/tar] / src / misc.c
1 /* Miscellaneous functions, not really specific to GNU tar.
2
3 Copyright (C) 1988, 1992, 1994, 1995, 1996, 1997, 1999, 2000, 2001,
4 2003, 2004, 2005, 2006, 2007, 2009, 2010 Free Software Foundation, Inc.
5
6 This program is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; either version 3, or (at your option) any later
9 version.
10
11 This program is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
14 Public License for more details.
15
16 You should have received a copy of the GNU General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
19
20 #include <system.h>
21 #include <rmt.h>
22 #include "common.h"
23 #include <quotearg.h>
24 #include <save-cwd.h>
25 #include <xgetcwd.h>
26 #include <unlinkdir.h>
27 #include <utimens.h>
28
29 #if HAVE_STROPTS_H
30 # include <stropts.h>
31 #endif
32 #if HAVE_SYS_FILIO_H
33 # include <sys/filio.h>
34 #endif
35
36 #ifndef DOUBLE_SLASH_IS_DISTINCT_ROOT
37 # define DOUBLE_SLASH_IS_DISTINCT_ROOT 0
38 #endif
39
40 \f
41 /* Handling strings. */
42
43 /* Assign STRING to a copy of VALUE if not zero, or to zero. If
44 STRING was nonzero, it is freed first. */
45 void
46 assign_string (char **string, const char *value)
47 {
48 if (*string)
49 free (*string);
50 *string = value ? xstrdup (value) : 0;
51 }
52
53 /* Allocate a copy of the string quoted as in C, and returns that. If
54 the string does not have to be quoted, it returns a null pointer.
55 The allocated copy should normally be freed with free() after the
56 caller is done with it.
57
58 This is used in one context only: generating the directory file in
59 incremental dumps. The quoted string is not intended for human
60 consumption; it is intended only for unquote_string. The quoting
61 is locale-independent, so that users needn't worry about locale
62 when reading directory files. This means that we can't use
63 quotearg, as quotearg is locale-dependent and is meant for human
64 consumption. */
65 char *
66 quote_copy_string (const char *string)
67 {
68 const char *source = string;
69 char *destination = 0;
70 char *buffer = 0;
71 int copying = 0;
72
73 while (*source)
74 {
75 int character = *source++;
76
77 switch (character)
78 {
79 case '\n': case '\\':
80 if (!copying)
81 {
82 size_t length = (source - string) - 1;
83
84 copying = 1;
85 buffer = xmalloc (length + 2 + 2 * strlen (source) + 1);
86 memcpy (buffer, string, length);
87 destination = buffer + length;
88 }
89 *destination++ = '\\';
90 *destination++ = character == '\\' ? '\\' : 'n';
91 break;
92
93 default:
94 if (copying)
95 *destination++ = character;
96 break;
97 }
98 }
99 if (copying)
100 {
101 *destination = '\0';
102 return buffer;
103 }
104 return 0;
105 }
106
107 /* Takes a quoted C string (like those produced by quote_copy_string)
108 and turns it back into the un-quoted original. This is done in
109 place. Returns 0 only if the string was not properly quoted, but
110 completes the unquoting anyway.
111
112 This is used for reading the saved directory file in incremental
113 dumps. It is used for decoding old `N' records (demangling names).
114 But also, it is used for decoding file arguments, would they come
115 from the shell or a -T file, and for decoding the --exclude
116 argument. */
117 int
118 unquote_string (char *string)
119 {
120 int result = 1;
121 char *source = string;
122 char *destination = string;
123
124 /* Escape sequences other than \\ and \n are no longer generated by
125 quote_copy_string, but accept them for backwards compatibility,
126 and also because unquote_string is used for purposes other than
127 parsing the output of quote_copy_string. */
128
129 while (*source)
130 if (*source == '\\')
131 switch (*++source)
132 {
133 case '\\':
134 *destination++ = '\\';
135 source++;
136 break;
137
138 case 'a':
139 *destination++ = '\a';
140 source++;
141 break;
142
143 case 'b':
144 *destination++ = '\b';
145 source++;
146 break;
147
148 case 'f':
149 *destination++ = '\f';
150 source++;
151 break;
152
153 case 'n':
154 *destination++ = '\n';
155 source++;
156 break;
157
158 case 'r':
159 *destination++ = '\r';
160 source++;
161 break;
162
163 case 't':
164 *destination++ = '\t';
165 source++;
166 break;
167
168 case 'v':
169 *destination++ = '\v';
170 source++;
171 break;
172
173 case '?':
174 *destination++ = 0177;
175 source++;
176 break;
177
178 case '0':
179 case '1':
180 case '2':
181 case '3':
182 case '4':
183 case '5':
184 case '6':
185 case '7':
186 {
187 int value = *source++ - '0';
188
189 if (*source < '0' || *source > '7')
190 {
191 *destination++ = value;
192 break;
193 }
194 value = value * 8 + *source++ - '0';
195 if (*source < '0' || *source > '7')
196 {
197 *destination++ = value;
198 break;
199 }
200 value = value * 8 + *source++ - '0';
201 *destination++ = value;
202 break;
203 }
204
205 default:
206 result = 0;
207 *destination++ = '\\';
208 if (*source)
209 *destination++ = *source++;
210 break;
211 }
212 else if (source != destination)
213 *destination++ = *source++;
214 else
215 source++, destination++;
216
217 if (source != destination)
218 *destination = '\0';
219 return result;
220 }
221
222 /* Zap trailing slashes. */
223 char *
224 zap_slashes (char *name)
225 {
226 char *q;
227
228 if (!name || *name == 0)
229 return name;
230 q = name + strlen (name) - 1;
231 while (q > name && ISSLASH (*q))
232 *q-- = '\0';
233 return name;
234 }
235
236 /* Normalize FILE_NAME by removing redundant slashes and "."
237 components, including redundant trailing slashes. Leave ".."
238 alone, as it may be significant in the presence of symlinks and on
239 platforms where "/.." != "/". Destructive version: modifies its
240 argument. */
241 static void
242 normalize_filename_x (char *file_name)
243 {
244 char *name = file_name + FILE_SYSTEM_PREFIX_LEN (file_name);
245 char *p;
246 char const *q;
247 char c;
248
249 /* Don't squeeze leading "//" to "/", on hosts where they're distinct. */
250 name += (DOUBLE_SLASH_IS_DISTINCT_ROOT
251 && ISSLASH (*name) && ISSLASH (name[1]) && ! ISSLASH (name[2]));
252
253 /* Omit redundant leading "." components. */
254 for (q = p = name; (*p = *q) == '.' && ISSLASH (q[1]); p += !*q)
255 for (q += 2; ISSLASH (*q); q++)
256 continue;
257
258 /* Copy components from Q to P, omitting redundant slashes and
259 internal "." components. */
260 while ((*p++ = c = *q++) != '\0')
261 if (ISSLASH (c))
262 while (ISSLASH (q[*q == '.']))
263 q += (*q == '.') + 1;
264
265 /* Omit redundant trailing "." component and slash. */
266 if (2 < p - name)
267 {
268 p -= p[-2] == '.' && ISSLASH (p[-3]);
269 p -= 2 < p - name && ISSLASH (p[-2]);
270 p[-1] = '\0';
271 }
272 }
273
274 /* Normalize NAME by removing redundant slashes and "." components,
275 including redundant trailing slashes. Return a normalized
276 newly-allocated copy. */
277
278 char *
279 normalize_filename (const char *name)
280 {
281 char *copy = xstrdup (name);
282 normalize_filename_x (copy);
283 return copy;
284 }
285
286 \f
287 void
288 replace_prefix (char **pname, const char *samp, size_t slen,
289 const char *repl, size_t rlen)
290 {
291 char *name = *pname;
292 size_t nlen = strlen (name);
293 if (nlen > slen && memcmp (name, samp, slen) == 0 && ISSLASH (name[slen]))
294 {
295 if (rlen > slen)
296 {
297 name = xrealloc (name, nlen - slen + rlen + 1);
298 *pname = name;
299 }
300 memmove (name + rlen, name + slen, nlen - slen + 1);
301 memcpy (name, repl, rlen);
302 }
303 }
304
305 \f
306 /* Handling numbers. */
307
308 /* Output fraction and trailing digits appropriate for a nanoseconds
309 count equal to NS, but don't output unnecessary '.' or trailing
310 zeros. */
311
312 void
313 code_ns_fraction (int ns, char *p)
314 {
315 if (ns == 0)
316 *p = '\0';
317 else
318 {
319 int i = 9;
320 *p++ = '.';
321
322 while (ns % 10 == 0)
323 {
324 ns /= 10;
325 i--;
326 }
327
328 p[i] = '\0';
329
330 for (;;)
331 {
332 p[--i] = '0' + ns % 10;
333 if (i == 0)
334 break;
335 ns /= 10;
336 }
337 }
338 }
339
340 char const *
341 code_timespec (struct timespec t, char sbuf[TIMESPEC_STRSIZE_BOUND])
342 {
343 time_t s = t.tv_sec;
344 int ns = t.tv_nsec;
345 char *np;
346 bool negative = s < 0;
347
348 /* ignore invalid values of ns */
349 if (BILLION <= ns || ns < 0)
350 ns = 0;
351
352 if (negative && ns != 0)
353 {
354 s++;
355 ns = BILLION - ns;
356 }
357
358 np = umaxtostr (negative ? - (uintmax_t) s : (uintmax_t) s, sbuf + 1);
359 if (negative)
360 *--np = '-';
361 code_ns_fraction (ns, sbuf + UINTMAX_STRSIZE_BOUND);
362 return np;
363 }
364 \f
365 /* File handling. */
366
367 /* Saved names in case backup needs to be undone. */
368 static char *before_backup_name;
369 static char *after_backup_name;
370
371 /* Return 1 if FILE_NAME is obviously "." or "/". */
372 static bool
373 must_be_dot_or_slash (char const *file_name)
374 {
375 file_name += FILE_SYSTEM_PREFIX_LEN (file_name);
376
377 if (ISSLASH (file_name[0]))
378 {
379 for (;;)
380 if (ISSLASH (file_name[1]))
381 file_name++;
382 else if (file_name[1] == '.'
383 && ISSLASH (file_name[2 + (file_name[2] == '.')]))
384 file_name += 2 + (file_name[2] == '.');
385 else
386 return ! file_name[1];
387 }
388 else
389 {
390 while (file_name[0] == '.' && ISSLASH (file_name[1]))
391 {
392 file_name += 2;
393 while (ISSLASH (*file_name))
394 file_name++;
395 }
396
397 return ! file_name[0] || (file_name[0] == '.' && ! file_name[1]);
398 }
399 }
400
401 /* Some implementations of rmdir let you remove '.' or '/'.
402 Report an error with errno set to zero for obvious cases of this;
403 otherwise call rmdir. */
404 static int
405 safer_rmdir (const char *file_name)
406 {
407 if (must_be_dot_or_slash (file_name))
408 {
409 errno = 0;
410 return -1;
411 }
412
413 return rmdir (file_name);
414 }
415
416 /* Remove FILE_NAME, returning 1 on success. If FILE_NAME is a directory,
417 then if OPTION is RECURSIVE_REMOVE_OPTION is set remove FILE_NAME
418 recursively; otherwise, remove it only if it is empty. If FILE_NAME is
419 a directory that cannot be removed (e.g., because it is nonempty)
420 and if OPTION is WANT_DIRECTORY_REMOVE_OPTION, then return -1.
421 Return 0 on error, with errno set; if FILE_NAME is obviously the working
422 directory return zero with errno set to zero. */
423 int
424 remove_any_file (const char *file_name, enum remove_option option)
425 {
426 /* Try unlink first if we cannot unlink directories, as this saves
427 us a system call in the common case where we're removing a
428 non-directory. */
429 bool try_unlink_first = cannot_unlink_dir ();
430
431 if (try_unlink_first)
432 {
433 if (unlink (file_name) == 0)
434 return 1;
435
436 /* POSIX 1003.1-2001 requires EPERM when attempting to unlink a
437 directory without appropriate privileges, but many Linux
438 kernels return the more-sensible EISDIR. */
439 if (errno != EPERM && errno != EISDIR)
440 return 0;
441 }
442
443 if (safer_rmdir (file_name) == 0)
444 return 1;
445
446 switch (errno)
447 {
448 case ENOTDIR:
449 return !try_unlink_first && unlink (file_name) == 0;
450
451 case 0:
452 case EEXIST:
453 #if defined ENOTEMPTY && ENOTEMPTY != EEXIST
454 case ENOTEMPTY:
455 #endif
456 switch (option)
457 {
458 case ORDINARY_REMOVE_OPTION:
459 break;
460
461 case WANT_DIRECTORY_REMOVE_OPTION:
462 return -1;
463
464 case RECURSIVE_REMOVE_OPTION:
465 {
466 char *directory = savedir (file_name);
467 char const *entry;
468 size_t entrylen;
469
470 if (! directory)
471 return 0;
472
473 for (entry = directory;
474 (entrylen = strlen (entry)) != 0;
475 entry += entrylen + 1)
476 {
477 char *file_name_buffer = new_name (file_name, entry);
478 int r = remove_any_file (file_name_buffer,
479 RECURSIVE_REMOVE_OPTION);
480 int e = errno;
481 free (file_name_buffer);
482
483 if (! r)
484 {
485 free (directory);
486 errno = e;
487 return 0;
488 }
489 }
490
491 free (directory);
492 return safer_rmdir (file_name) == 0;
493 }
494 }
495 break;
496 }
497
498 return 0;
499 }
500
501 /* Check if FILE_NAME already exists and make a backup of it right now.
502 Return success (nonzero) only if the backup is either unneeded, or
503 successful. For now, directories are considered to never need
504 backup. If THIS_IS_THE_ARCHIVE is nonzero, this is the archive and
505 so, we do not have to backup block or character devices, nor remote
506 entities. */
507 bool
508 maybe_backup_file (const char *file_name, bool this_is_the_archive)
509 {
510 struct stat file_stat;
511
512 assign_string (&before_backup_name, file_name);
513
514 /* A run situation may exist between Emacs or other GNU programs trying to
515 make a backup for the same file simultaneously. If theoretically
516 possible, real problems are unlikely. Doing any better would require a
517 convention, GNU-wide, for all programs doing backups. */
518
519 assign_string (&after_backup_name, 0);
520
521 /* Check if we really need to backup the file. */
522
523 if (this_is_the_archive && _remdev (file_name))
524 return true;
525
526 if (stat (file_name, &file_stat))
527 {
528 if (errno == ENOENT)
529 return true;
530
531 stat_error (file_name);
532 return false;
533 }
534
535 if (S_ISDIR (file_stat.st_mode))
536 return true;
537
538 if (this_is_the_archive
539 && (S_ISBLK (file_stat.st_mode) || S_ISCHR (file_stat.st_mode)))
540 return true;
541
542 after_backup_name = find_backup_file_name (file_name, backup_type);
543 if (! after_backup_name)
544 xalloc_die ();
545
546 if (rename (before_backup_name, after_backup_name) == 0)
547 {
548 if (verbose_option)
549 fprintf (stdlis, _("Renaming %s to %s\n"),
550 quote_n (0, before_backup_name),
551 quote_n (1, after_backup_name));
552 return true;
553 }
554 else
555 {
556 /* The backup operation failed. */
557 int e = errno;
558 ERROR ((0, e, _("%s: Cannot rename to %s"),
559 quotearg_colon (before_backup_name),
560 quote_n (1, after_backup_name)));
561 assign_string (&after_backup_name, 0);
562 return false;
563 }
564 }
565
566 /* Try to restore the recently backed up file to its original name.
567 This is usually only needed after a failed extraction. */
568 void
569 undo_last_backup (void)
570 {
571 if (after_backup_name)
572 {
573 if (rename (after_backup_name, before_backup_name) != 0)
574 {
575 int e = errno;
576 ERROR ((0, e, _("%s: Cannot rename to %s"),
577 quotearg_colon (after_backup_name),
578 quote_n (1, before_backup_name)));
579 }
580 if (verbose_option)
581 fprintf (stdlis, _("Renaming %s back to %s\n"),
582 quote_n (0, after_backup_name),
583 quote_n (1, before_backup_name));
584 assign_string (&after_backup_name, 0);
585 }
586 }
587
588 /* Depending on DEREF, apply either stat or lstat to (NAME, BUF). */
589 int
590 deref_stat (bool deref, char const *name, struct stat *buf)
591 {
592 return deref ? stat (name, buf) : lstat (name, buf);
593 }
594
595 /* Set FD's (i.e., FILE's) access time to TIMESPEC[0]. If that's not
596 possible to do by itself, set its access and data modification
597 times to TIMESPEC[0] and TIMESPEC[1], respectively. */
598 int
599 set_file_atime (int fd, char const *file, struct timespec const timespec[2])
600 {
601 #ifdef _FIOSATIME
602 if (0 <= fd)
603 {
604 struct timeval timeval;
605 timeval.tv_sec = timespec[0].tv_sec;
606 timeval.tv_usec = timespec[0].tv_nsec / 1000;
607 if (ioctl (fd, _FIOSATIME, &timeval) == 0)
608 return 0;
609 }
610 #endif
611
612 return gl_futimens (fd, file, timespec);
613 }
614
615 /* A description of a working directory. */
616 struct wd
617 {
618 char const *name;
619 int saved;
620 struct saved_cwd saved_cwd;
621 };
622
623 /* A vector of chdir targets. wd[0] is the initial working directory. */
624 static struct wd *wd;
625
626 /* The number of working directories in the vector. */
627 static size_t wd_count;
628
629 /* The allocated size of the vector. */
630 static size_t wd_alloc;
631
632 int
633 chdir_count ()
634 {
635 if (wd_count == 0)
636 return wd_count;
637 return wd_count - 1;
638 }
639
640 /* DIR is the operand of a -C option; add it to vector of chdir targets,
641 and return the index of its location. */
642 int
643 chdir_arg (char const *dir)
644 {
645 if (wd_count == wd_alloc)
646 {
647 if (wd_alloc == 0)
648 {
649 wd_alloc = 2;
650 wd = xmalloc (sizeof *wd * wd_alloc);
651 }
652 else
653 wd = x2nrealloc (wd, &wd_alloc, sizeof *wd);
654
655 if (! wd_count)
656 {
657 wd[wd_count].name = ".";
658 wd[wd_count].saved = 0;
659 wd_count++;
660 }
661 }
662
663 /* Optimize the common special case of the working directory,
664 or the working directory as a prefix. */
665 if (dir[0])
666 {
667 while (dir[0] == '.' && ISSLASH (dir[1]))
668 for (dir += 2; ISSLASH (*dir); dir++)
669 continue;
670 if (! dir[dir[0] == '.'])
671 return wd_count - 1;
672 }
673
674 wd[wd_count].name = dir;
675 wd[wd_count].saved = 0;
676 return wd_count++;
677 }
678
679 /* Change to directory I. If I is 0, change to the initial working
680 directory; otherwise, I must be a value returned by chdir_arg. */
681 void
682 chdir_do (int i)
683 {
684 static int previous;
685
686 if (previous != i)
687 {
688 struct wd *prev = &wd[previous];
689 struct wd *curr = &wd[i];
690
691 if (! prev->saved)
692 {
693 int err = 0;
694 prev->saved = 1;
695 if (save_cwd (&prev->saved_cwd) != 0)
696 err = errno;
697 else if (0 <= prev->saved_cwd.desc)
698 {
699 /* Make sure we still have at least one descriptor available. */
700 int fd1 = prev->saved_cwd.desc;
701 int fd2 = dup (fd1);
702 if (0 <= fd2)
703 close (fd2);
704 else if (errno == EMFILE)
705 {
706 /* Force restore_cwd to use chdir_long. */
707 close (fd1);
708 prev->saved_cwd.desc = -1;
709 prev->saved_cwd.name = xgetcwd ();
710 if (! prev->saved_cwd.name)
711 err = errno;
712 }
713 else
714 err = errno;
715 }
716
717 if (err)
718 FATAL_ERROR ((0, err, _("Cannot save working directory")));
719 }
720
721 if (curr->saved)
722 {
723 if (restore_cwd (&curr->saved_cwd))
724 FATAL_ERROR ((0, 0, _("Cannot change working directory")));
725 }
726 else
727 {
728 if (i && ! ISSLASH (curr->name[0]))
729 chdir_do (i - 1);
730 if (chdir (curr->name) != 0)
731 chdir_fatal (curr->name);
732 }
733
734 previous = i;
735 }
736 }
737 \f
738 void
739 close_diag (char const *name)
740 {
741 if (ignore_failed_read_option)
742 close_warn (name);
743 else
744 close_error (name);
745 }
746
747 void
748 open_diag (char const *name)
749 {
750 if (ignore_failed_read_option)
751 open_warn (name);
752 else
753 open_error (name);
754 }
755
756 void
757 read_diag_details (char const *name, off_t offset, size_t size)
758 {
759 if (ignore_failed_read_option)
760 read_warn_details (name, offset, size);
761 else
762 read_error_details (name, offset, size);
763 }
764
765 void
766 readlink_diag (char const *name)
767 {
768 if (ignore_failed_read_option)
769 readlink_warn (name);
770 else
771 readlink_error (name);
772 }
773
774 void
775 savedir_diag (char const *name)
776 {
777 if (ignore_failed_read_option)
778 savedir_warn (name);
779 else
780 savedir_error (name);
781 }
782
783 void
784 seek_diag_details (char const *name, off_t offset)
785 {
786 if (ignore_failed_read_option)
787 seek_warn_details (name, offset);
788 else
789 seek_error_details (name, offset);
790 }
791
792 void
793 stat_diag (char const *name)
794 {
795 if (ignore_failed_read_option)
796 stat_warn (name);
797 else
798 stat_error (name);
799 }
800
801 void
802 file_removed_diag (const char *name, bool top_level,
803 void (*diagfn) (char const *name))
804 {
805 if (!top_level && errno == ENOENT)
806 {
807 WARNOPT (WARN_FILE_REMOVED,
808 (0, 0, _("%s: File removed before we read it"),
809 quotearg_colon (name)));
810 set_exit_status (TAREXIT_DIFFERS);
811 }
812 else
813 diagfn (name);
814 }
815
816 void
817 dir_removed_diag (const char *name, bool top_level,
818 void (*diagfn) (char const *name))
819 {
820 if (!top_level && errno == ENOENT)
821 {
822 WARNOPT (WARN_FILE_REMOVED,
823 (0, 0, _("%s: Directory removed before we read it"),
824 quotearg_colon (name)));
825 set_exit_status (TAREXIT_DIFFERS);
826 }
827 else
828 diagfn (name);
829 }
830
831 void
832 write_fatal_details (char const *name, ssize_t status, size_t size)
833 {
834 write_error_details (name, status, size);
835 fatal_exit ();
836 }
837
838 /* Fork, aborting if unsuccessful. */
839 pid_t
840 xfork (void)
841 {
842 pid_t p = fork ();
843 if (p == (pid_t) -1)
844 call_arg_fatal ("fork", _("child process"));
845 return p;
846 }
847
848 /* Create a pipe, aborting if unsuccessful. */
849 void
850 xpipe (int fd[2])
851 {
852 if (pipe (fd) < 0)
853 call_arg_fatal ("pipe", _("interprocess channel"));
854 }
855
856 /* Return PTR, aligned upward to the next multiple of ALIGNMENT.
857 ALIGNMENT must be nonzero. The caller must arrange for ((char *)
858 PTR) through ((char *) PTR + ALIGNMENT - 1) to be addressable
859 locations. */
860
861 static inline void *
862 ptr_align (void *ptr, size_t alignment)
863 {
864 char *p0 = ptr;
865 char *p1 = p0 + alignment - 1;
866 return p1 - (size_t) p1 % alignment;
867 }
868
869 /* Return the address of a page-aligned buffer of at least SIZE bytes.
870 The caller should free *PTR when done with the buffer. */
871
872 void *
873 page_aligned_alloc (void **ptr, size_t size)
874 {
875 size_t alignment = getpagesize ();
876 size_t size1 = size + alignment;
877 if (size1 < size)
878 xalloc_die ();
879 *ptr = xmalloc (size1);
880 return ptr_align (*ptr, alignment);
881 }
882
883 \f
884
885 struct namebuf
886 {
887 char *buffer; /* directory, `/', and directory member */
888 size_t buffer_size; /* allocated size of name_buffer */
889 size_t dir_length; /* length of directory part in buffer */
890 };
891
892 namebuf_t
893 namebuf_create (const char *dir)
894 {
895 namebuf_t buf = xmalloc (sizeof (*buf));
896 buf->buffer_size = strlen (dir) + 2;
897 buf->buffer = xmalloc (buf->buffer_size);
898 strcpy (buf->buffer, dir);
899 buf->dir_length = strlen (buf->buffer);
900 if (!ISSLASH (buf->buffer[buf->dir_length - 1]))
901 buf->buffer[buf->dir_length++] = DIRECTORY_SEPARATOR;
902 return buf;
903 }
904
905 void
906 namebuf_free (namebuf_t buf)
907 {
908 free (buf->buffer);
909 free (buf);
910 }
911
912 char *
913 namebuf_name (namebuf_t buf, const char *name)
914 {
915 size_t len = strlen (name);
916 while (buf->dir_length + len + 1 >= buf->buffer_size)
917 buf->buffer = x2realloc (buf->buffer, &buf->buffer_size);
918 strcpy (buf->buffer + buf->dir_length, name);
919 return buf->buffer;
920 }
This page took 0.07174 seconds and 5 git commands to generate.