+\f
+char *
+tar_getcwd (void)
+{
+ static char *cwd;
+ namebuf_t nbuf;
+ int i;
+
+ if (!cwd)
+ cwd = xgetcwd ();
+ nbuf = namebuf_create (cwd);
+ for (i = 1; i <= chdir_current; i++)
+ namebuf_add_dir (nbuf, wd[i].name);
+ return namebuf_finish (nbuf);
+}
+\f
+void
+close_diag (char const *name)
+{
+ if (ignore_failed_read_option)
+ close_warn (name);
+ else
+ close_error (name);
+}
+
+void
+open_diag (char const *name)
+{
+ if (ignore_failed_read_option)
+ open_warn (name);
+ else
+ open_error (name);
+}
+
+void
+read_diag_details (char const *name, off_t offset, size_t size)
+{
+ if (ignore_failed_read_option)
+ read_warn_details (name, offset, size);
+ else
+ read_error_details (name, offset, size);
+}
+
+void
+readlink_diag (char const *name)
+{
+ if (ignore_failed_read_option)
+ readlink_warn (name);
+ else
+ readlink_error (name);
+}
+
+void
+savedir_diag (char const *name)
+{
+ if (ignore_failed_read_option)
+ savedir_warn (name);
+ else
+ savedir_error (name);
+}
+
+void
+seek_diag_details (char const *name, off_t offset)
+{
+ if (ignore_failed_read_option)
+ seek_warn_details (name, offset);
+ else
+ seek_error_details (name, offset);
+}
+
+void
+stat_diag (char const *name)
+{
+ if (ignore_failed_read_option)
+ stat_warn (name);
+ else
+ stat_error (name);
+}
+
+void
+file_removed_diag (const char *name, bool top_level,
+ void (*diagfn) (char const *name))
+{
+ if (!top_level && errno == ENOENT)
+ {
+ WARNOPT (WARN_FILE_REMOVED,
+ (0, 0, _("%s: File removed before we read it"),
+ quotearg_colon (name)));
+ set_exit_status (TAREXIT_DIFFERS);
+ }
+ else
+ diagfn (name);
+}
+
+void
+write_fatal_details (char const *name, ssize_t status, size_t size)
+{
+ write_error_details (name, status, size);
+ fatal_exit ();
+}
+
+/* Fork, aborting if unsuccessful. */
+pid_t
+xfork (void)
+{
+ pid_t p = fork ();
+ if (p == (pid_t) -1)
+ call_arg_fatal ("fork", _("child process"));
+ return p;
+}
+
+/* Create a pipe, aborting if unsuccessful. */
+void
+xpipe (int fd[2])
+{
+ if (pipe (fd) < 0)
+ call_arg_fatal ("pipe", _("interprocess channel"));
+}
+
+/* Return PTR, aligned upward to the next multiple of ALIGNMENT.
+ ALIGNMENT must be nonzero. The caller must arrange for ((char *)
+ PTR) through ((char *) PTR + ALIGNMENT - 1) to be addressable
+ locations. */
+
+static inline void *
+ptr_align (void *ptr, size_t alignment)
+{
+ char *p0 = ptr;
+ char *p1 = p0 + alignment - 1;
+ return p1 - (size_t) p1 % alignment;
+}
+
+/* Return the address of a page-aligned buffer of at least SIZE bytes.
+ The caller should free *PTR when done with the buffer. */
+
+void *
+page_aligned_alloc (void **ptr, size_t size)
+{
+ size_t alignment = getpagesize ();
+ size_t size1 = size + alignment;
+ if (size1 < size)
+ xalloc_die ();
+ *ptr = xmalloc (size1);
+ return ptr_align (*ptr, alignment);
+}
+
+\f
+
+struct namebuf
+{
+ char *buffer; /* directory, '/', and directory member */
+ size_t buffer_size; /* allocated size of name_buffer */
+ size_t dir_length; /* length of directory part in buffer */
+};
+
+namebuf_t
+namebuf_create (const char *dir)
+{
+ namebuf_t buf = xmalloc (sizeof (*buf));
+ buf->buffer_size = strlen (dir) + 2;
+ buf->buffer = xmalloc (buf->buffer_size);
+ strcpy (buf->buffer, dir);
+ buf->dir_length = strlen (buf->buffer);
+ if (!ISSLASH (buf->buffer[buf->dir_length - 1]))
+ buf->buffer[buf->dir_length++] = DIRECTORY_SEPARATOR;
+ return buf;
+}
+
+void
+namebuf_free (namebuf_t buf)
+{
+ free (buf->buffer);
+ free (buf);
+}
+
+char *
+namebuf_name (namebuf_t buf, const char *name)
+{
+ size_t len = strlen (name);
+ while (buf->dir_length + len + 1 >= buf->buffer_size)
+ buf->buffer = x2realloc (buf->buffer, &buf->buffer_size);
+ strcpy (buf->buffer + buf->dir_length, name);
+ return buf->buffer;
+}
+
+void
+namebuf_add_dir (namebuf_t buf, const char *name)
+{
+ static char dirsep[] = { DIRECTORY_SEPARATOR, 0 };
+ if (!ISSLASH (buf->buffer[buf->dir_length - 1]))
+ {
+ namebuf_name (buf, dirsep);
+ buf->dir_length++;
+ }
+ namebuf_name (buf, name);
+ buf->dir_length += strlen (name);
+}
+
+char *
+namebuf_finish (namebuf_t buf)
+{
+ char *res = buf->buffer;
+
+ if (ISSLASH (buf->buffer[buf->dir_length - 1]))
+ buf->buffer[buf->dir_length] = 0;
+ free (buf);
+ return res;
+}
+
+/* Return the filenames in directory NAME, relative to the chdir_fd.
+ If the directory does not exist, report error if MUST_EXIST is
+ true.
+
+ Return NULL on errors.
+*/
+char *
+tar_savedir (const char *name, int must_exist)
+{
+ char *ret = NULL;
+ DIR *dir = NULL;
+ int fd = openat (chdir_fd, name, open_read_flags | O_DIRECTORY);
+ if (fd < 0)
+ {
+ if (!must_exist && errno == ENOENT)
+ return NULL;
+ open_error (name);
+ }
+ else if (! ((dir = fdopendir (fd))
+ && (ret = streamsavedir (dir))))
+ savedir_error (name);
+
+ if (dir ? closedir (dir) != 0 : 0 <= fd && close (fd) != 0)
+ savedir_error (name);
+
+ return ret;
+}