summaryrefslogtreecommitdiffabout
Side-by-side diff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--doc/.cvsignore2
-rw-r--r--doc/Makefile.am20
-rw-r--r--doc/cpio.1340
-rw-r--r--doc/cpio.info493
-rw-r--r--doc/cpio.texi563
-rw-r--r--doc/mt.1126
-rw-r--r--src/.cvsignore11
-rw-r--r--src/Makefile.am81
-rw-r--r--src/alloca.c495
-rw-r--r--src/argmatch.c87
-rw-r--r--src/bcopy.c19
-rw-r--r--src/copyin.c1605
-rw-r--r--src/copyout.c827
-rw-r--r--src/copypass.c482
-rw-r--r--src/cpio.h69
-rw-r--r--src/cpiohdr.h90
-rw-r--r--src/defer.c46
-rw-r--r--src/defer.h25
-rw-r--r--src/dirname.c70
-rw-r--r--src/dstring.c118
-rw-r--r--src/dstring.h49
-rw-r--r--src/error.c109
-rw-r--r--src/extern.h198
-rw-r--r--src/filemode.c255
-rw-r--r--src/filetypes.h84
-rw-r--r--src/fnmatch.c200
-rw-r--r--src/fnmatch.h67
-rw-r--r--src/getopt.c765
-rw-r--r--src/getopt.h129
-rw-r--r--src/getopt1.c180
-rw-r--r--src/gettext.h68
-rw-r--r--src/global.c204
-rw-r--r--src/idcache.c210
-rw-r--r--src/main.c550
-rw-r--r--src/makepath.c311
-rw-r--r--src/mkdir.c100
-rw-r--r--src/mt.c366
-rw-r--r--src/rmt.c473
-rw-r--r--src/rmt.h98
-rw-r--r--src/rtapelib.c601
-rw-r--r--src/safe-stat.h1
-rw-r--r--src/strdup.c43
-rw-r--r--src/strerror.c63
-rw-r--r--src/stripslash.c43
-rw-r--r--src/system.h146
-rw-r--r--src/tar.c528
-rw-r--r--src/tar.h112
-rw-r--r--src/tarhdr.h62
-rw-r--r--src/userspec.c265
-rw-r--r--src/util.c1376
-rw-r--r--src/xmalloc.c103
-rw-r--r--src/xstrdup.c36
52 files changed, 13364 insertions, 0 deletions
diff --git a/doc/.cvsignore b/doc/.cvsignore
new file mode 100644
index 0000000..3dda729
--- a/dev/null
+++ b/doc/.cvsignore
@@ -0,0 +1,2 @@
+Makefile.in
+Makefile
diff --git a/doc/Makefile.am b/doc/Makefile.am
new file mode 100644
index 0000000..c3ec682
--- a/dev/null
+++ b/doc/Makefile.am
@@ -0,0 +1,20 @@
+# This file is part of GNU cpio
+# Copyright (C) 2004 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+info_TEXINFOS = cpio.texi
+man_MANS = cpio.1 mt.1
+EXTRA_DIST = $(man_MANS)
diff --git a/doc/cpio.1 b/doc/cpio.1
new file mode 100644
index 0000000..5051f8d
--- a/dev/null
+++ b/doc/cpio.1
@@ -0,0 +1,340 @@
+.TH CPIO 1L \" -*- nroff -*-
+.SH NAME
+cpio \- copy files to and from archives
+.SH SYNOPSIS
+.B cpio
+{\-o|\-\-create} [\-0acvABLV] [\-C bytes] [\-H format] [\-M message]
+[\-O [[user@]host:]archive] [\-F [[user@]host:]archive]
+[\-\-file=[[user@]host:]archive] [\-\-format=format] [\-\-message=message]
+[\-\-null] [\-\-reset-access-time] [\-\-verbose] [\-\-dot] [\-\-append]
+[\-\-block-size=blocks] [\-\-dereference] [\-\-io-size=bytes] [\-\-quiet]
+[\-\-force\-local] [\-\-rsh-command=command] [\-\-help] [\-\-version]
+< name-list [> archive]
+
+.B cpio
+{\-i|\-\-extract} [\-bcdfmnrtsuvBSV] [\-C bytes] [\-E file] [\-H format]
+[\-M message] [\-R [user][:.][group]] [\-I [[user@]host:]archive]
+[\-F [[user@]host:]archive] [\-\-file=[[user@]host:]archive]
+[\-\-make-directories] [\-\-nonmatching] [\-\-preserve-modification-time]
+[\-\-numeric-uid-gid] [\-\-rename] [\-t|\-\-list] [\-\-swap-bytes] [\-\-swap] [\-\-dot]
+[\-\-unconditional] [\-\-verbose] [\-\-block-size=blocks] [\-\-swap-halfwords]
+[\-\-io-size=bytes] [\-\-pattern-file=file] [\-\-format=format]
+[\-\-owner=[user][:.][group]] [\-\-no-preserve-owner] [\-\-message=message]
+[\-\-force\-local] [\-\-no\-absolute\-filenames] [\-\-sparse]
+[\-\-only\-verify\-crc] [\-\-quiet] [\-\-rsh-command=command] [\-\-help]
+[\-\-version] [pattern...] [< archive]
+
+.B cpio
+{\-p|\-\-pass-through} [\-0adlmuvLV] [\-R [user][:.][group]]
+[\-\-null] [\-\-reset-access-time] [\-\-make-directories] [\-\-link] [\-\-quiet]
+[\-\-preserve-modification-time] [\-\-unconditional] [\-\-verbose] [\-\-dot]
+[\-\-dereference] [\-\-owner=[user][:.][group]] [\-\-no-preserve-owner]
+[\-\-sparse] [\-\-help] [\-\-version] destination-directory < name-list
+.SH DESCRIPTION
+This manual page
+documents the GNU version of
+.BR cpio .
+.B cpio
+copies files into or out of a cpio or tar archive, which is a file that
+contains other files plus information about them, such as their
+file name, owner, timestamps, and access permissions. The archive can
+be another file on the disk, a magnetic tape, or a pipe.
+.B cpio
+has three operating modes.
+.PP
+In copy-out mode,
+.B cpio
+copies files into an archive. It reads a list of filenames, one per
+line, on the standard input, and writes the archive onto the standard
+output. A typical way to generate the list of filenames is with the
+.B find
+command; you should give
+.B find
+the \-depth option to minimize problems with permissions on
+directories that are unwritable or not searchable.
+.PP
+In copy-in mode,
+.B cpio
+copies files out of an archive or lists the archive contents. It
+reads the archive from the standard input. Any non-option command
+line arguments are shell globbing patterns; only files in the archive
+whose names match one or more of those patterns are copied from the
+archive. Unlike in the shell, an initial `.' in a filename does
+match a wildcard at the start of a pattern, and a `/' in a filename
+can match wildcards. If no patterns are given, all files are
+extracted.
+.PP
+In copy-pass mode,
+.B cpio
+copies files from one directory tree to another, combining the
+copy-out and copy-in steps without actually using an archive.
+It reads the list of files to copy from the standard input; the
+directory into which it will copy them is given as a non-option
+argument.
+.PP
+.B cpio
+supports the following archive formats: binary, old ASCII, new
+ASCII, crc, HPUX binary, HPUX old ASCII, old tar, and POSIX.1 tar.
+The binary format
+is obsolete because it encodes information about the files in a way
+that is not portable between different machine architectures.
+The old ASCII format is portable between different machine architectures,
+but should not be used on file systems with more than 65536 i-nodes.
+The new ASCII format is portable between different machine architectures
+and can be used on any size file system, but is not supported by all
+versions of
+.BR cpio ;
+currently, it is only supported by GNU and Unix System V R4.
+The crc format is
+like the new ASCII format, but also contains a checksum for each file
+which
+.B cpio
+calculates when creating an archive
+and verifies when the file is extracted from the archive.
+The HPUX formats are provided for compatibility with HPUX's cpio which
+stores device files differently.
+.PP
+The tar format is provided for compatability with
+the
+.B tar
+program. It can not be used to archive files with names
+longer than 100 characters, and can not be used to archive "special"
+(block or character devices) files.
+The POSIX.1 tar format can not be used to archive files with names longer
+than 255 characters (less unless they have a "/" in just the right place).
+.PP
+By default,
+.B cpio
+creates binary format archives, for compatibility with
+older
+.B cpio
+programs.
+When extracting from archives,
+.B cpio
+automatically recognizes which kind of archive it is reading and can
+read archives created on machines with a different byte-order.
+.PP
+Some of the options to
+.B cpio
+apply only to certain operating modes; see the SYNOPSIS section for a
+list of which options are allowed in which modes.
+.SS OPTIONS
+.TP
+.I "\-0, \-\-null"
+In copy-out and copy-pass modes, read a list of filenames terminated
+by a null character instead of a newline, so that files whose names
+contain newlines can be archived. GNU
+.B find
+is one way to produce a list of null-terminated filenames.
+.TP
+.I "\-a, \-\-reset-access-time"
+Reset the access times of files after reading them, so that it does
+not look like they have just been read.
+.TP
+.I "\-A, \-\-append"
+Append to an existing archive. Only works in copy-out mode. The
+archive must be a disk file specified with the
+.I \-O
+or
+.I "\-F (\-\-file)"
+option.
+.TP
+.I "\-b, \-\-swap"
+In copy-in mode, swap both halfwords of words and bytes of halfwords
+in the data. Equivalent to
+.IR "\-sS" .
+Use this option to convert 32-bit integers between big-endian and
+little-endian machines.
+.TP
+.I "\-B"
+Set the I/O block size to 5120 bytes. Initially the block size is 512
+bytes.
+.TP
+.I "\-\-block-size=BLOCK-SIZE"
+Set the I/O block size to BLOCK-SIZE * 512 bytes.
+.TP
+.I "\-c"
+Use the old portable (ASCII) archive format.
+.TP
+.I "\-C IO-SIZE, \-\-io-size=IO-SIZE"
+Set the I/O block size to IO-SIZE bytes.
+.TP
+.I "\-d, \-\-make-directories"
+Create leading directories where needed.
+.TP
+.I "\-E FILE, \-\-pattern-file=FILE"
+In copy-in mode, read additional patterns specifying filenames to
+extract or list from FILE. The lines of FILE are treated as if they
+had been non-option arguments to
+.BR cpio .
+.TP
+.I "\-f, \-\-nonmatching"
+Only copy files that do not match any of the given patterns.
+.TP
+.I "\-F, \-\-file=archive"
+Archive filename to use instead of standard input or output. To use a
+tape drive on another machine as the archive, use a filename that
+starts with `HOSTNAME:'. The hostname can be preceded by a
+username and an `@' to access the remote tape drive as that user, if
+you have permission to do so (typically an entry in that user's
+`~/.rhosts' file).
+.TP
+.I "\-\-force-local"
+With
+.IR \-F ,
+.IR \-I ,
+or
+.IR \-O ,
+take the archive file name to be a local file even if it contains a
+colon, which would ordinarily indicate a remote host name.
+.TP
+.I "\-H FORMAT, \-\-format=FORMAT"
+Use archive format FORMAT. The valid formats are listed below;
+the same names are also recognized in all-caps. The default in
+copy-in mode is to automatically detect the archive format, and in
+copy-out mode is "bin".
+.RS
+.IP bin
+The obsolete binary format.
+.IP odc
+The old (POSIX.1) portable format.
+.IP newc
+The new (SVR4) portable format, which supports file systems having
+more than 65536 i-nodes.
+.IP crc
+The new (SVR4) portable format with a checksum added.
+.IP tar
+The old tar format.
+.IP ustar
+The POSIX.1 tar format. Also recognizes GNU
+.B tar
+archives, which are similar but not identical.
+.IP hpbin
+The obsolete binary format used by HPUX's cpio (which stores device files
+differently).
+.IP hpodc
+The portable format used by HPUX's cpio (which stores device files differently).
+.RE
+.TP
+.I "\-i, \-\-extract"
+Run in copy-in mode.
+.TP
+.I "\-I archive"
+Archive filename to use instead of standard input. To use a
+tape drive on another machine as the archive, use a filename that
+starts with `HOSTNAME:'. The hostname can be preceded by a
+username and an `@' to access the remote tape drive as that user, if
+you have permission to do so (typically an entry in that user's
+`~/.rhosts' file).
+.TP
+.I \-k
+Ignored; for compatibility with other versions of
+.BR cpio .
+.TP
+.I "\-l, \-\-link"
+Link files instead of copying them, when possible.
+.TP
+.I "\-L, \-\-dereference"
+Dereference symbolic links (copy the files that they point to instead
+of copying the links).
+.TP
+.I "\-m, \-\-preserve-modification-time"
+Retain previous file modification times when creating files.
+.TP
+.I "\-M MESSAGE, \-\-message=MESSAGE"
+Print MESSAGE when the end of a volume of the backup media (such as a
+tape or a floppy disk) is reached, to prompt the user to insert a new
+volume. If MESSAGE contains the string "%d", it is replaced by the
+current volume number (starting at 1).
+.TP
+.I "\-n, \-\-numeric-uid-gid"
+In the verbose table of contents listing, show numeric UID and GID
+instead of translating them into names.
+Also extracts tar archives using the numeric UID and GID instead of the
+user/group names.
+.RB ( cpio
+archives are always extracted using the numeric UID and GID.)
+.TP
+.I " \-\-no-absolute-filenames"
+In copy-in mode, create all files relative to the current directory,
+even if they have an absolute file name in the archive.
+.TP
+.I " \-\-no-preserve-owner"
+In copy-in mode and copy-pass mode, do not change the ownership of the
+files; leave them owned by the user extracting them. This is the
+default for non-root users, so that users on System V don't
+inadvertantly give away files.
+.TP
+.I "\-o, \-\-create"
+Run in copy-out mode.
+.TP
+.I "\-O archive"
+Archive filename to use instead of standard output. To use a tape
+drive on another machine as the archive, use a filename that starts
+with `HOSTNAME:'. The hostname can be preceded by a username and an
+`@' to access the remote tape drive as that user, if you have
+permission to do so (typically an entry in that user's `~/.rhosts'
+file).
+.TP
+.I " \-\-only-verify-crc"
+When reading a CRC format archive in copy-in mode, only verify the
+CRC's of each file in the archive, don't actually extract the files.
+.TP
+.I "\-p, \-\-pass-through"
+Run in copy-pass mode.
+.TP
+.I "\-\-quiet"
+Do not print the number of blocks copied.
+.TP
+.I "\-r, \-\-rename"
+Interactively rename files.
+.TP
+.I "\-R [user][:.][group], \-\-owner [user][:.][group]"
+In copy-out and copy-pass modes, set the ownership of all files created
+to the specified user and/or group. Either the user or the group, or
+both, must be present. If the group is omitted but the ":" or "."
+separator is given, use the given user's login group. Only the
+super-user can change files' ownership.
+.TP
+.I "\-\-rsh-command=COMMAND"
+Notifies
+.B mt
+that it should use COMMAND to communicate with remote devices instead of
+.I /usr/bin/ssh
+or
+.IR /usr/bin/rsh .
+.TP
+.I "\-\-sparse"
+In copy-in and copy-pass modes, write files with large blocks of zeros
+as sparse files.
+.TP
+.I "\-s, \-\-swap-bytes"
+In copy-in mode, swap the bytes of each halfword (pair of bytes) in the
+files.
+.TP
+.I "\-S, \-\-swap-halfwords"
+In copy-in mode, swap the halfwords of each word (4 bytes) in the
+files.
+.TP
+.I "\-t, \-\-list"
+Print a table of contents of the input.
+.TP
+.I "\-u, \-\-unconditional"
+Replace all files, without asking whether to replace existing newer
+files with older files.
+.TP
+.I "\-v, \-\-verbose"
+List the files processed, or with
+.IR \-t ,
+give an `ls \-l' style table of contents listing. In a verbose table
+of contents of a ustar archive, user and group names in the archive
+that do not exist on the local system are replaced by the names that
+correspond locally to the numeric UID and GID stored in the archive.
+.TP
+.I "\-V \-\-dot"
+Print a "." for each file processed.
+.TP
+.I "\-\-version"
+Print the
+.B cpio
+program version number and exit.
diff --git a/doc/cpio.info b/doc/cpio.info
new file mode 100644
index 0000000..19a75f4
--- a/dev/null
+++ b/doc/cpio.info
@@ -0,0 +1,493 @@
+This is cpio.info, produced by makeinfo version 4.6 from cpio.texi.
+
+START-INFO-DIR-ENTRY
+* cpio: (cpio). Making tape (or disk) archives.
+END-INFO-DIR-ENTRY
+
+ This file documents GNU cpio 2.5.
+
+ Copyright (C) 1995, 2001, 2002 Free Software Foundation, Inc.
+
+ Permission is granted to make and distribute verbatim copies of this
+manual provided the copyright notice and this permission notice are
+preserved on all copies.
+
+ Permission is granted to copy and distribute modified versions of
+this manual under the conditions for verbatim copying, provided that
+the entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+
+ Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for modified
+versions, except that this permission notice may be stated in a
+translation approved by the Foundation.
+
+
+File: cpio.info, Node: Top, Next: Introduction, Prev: (dir), Up: (dir)
+
+
+
+GNU cpio is a tool for creating and extracting archives, or copying
+files from one place to another. It handles a number of cpio formats as
+well as reading and writing tar files. This is the first edition of the
+GNU cpio documentation and is consistant with GNU cpio 2.5.
+
+* Menu:
+
+* Introduction::
+* Tutorial:: Getting started.
+* Invoking `cpio':: How to invoke `cpio'.
+* Media:: Using tapes and other archive media.
+* Concept Index:: Concept index.
+
+ --- The Detailed Node Listing ---
+
+Invoking cpio
+
+* Copy-out mode::
+* Copy-in mode::
+* Copy-pass mode::
+* Options::
+
+
+File: cpio.info, Node: Introduction, Next: Tutorial, Prev: Top, Up: Top
+
+Introduction
+************
+
+GNU cpio copies files into or out of a cpio or tar archive, The archive
+can be another file on the disk, a magnetic tape, or a pipe.
+
+ GNU cpio supports the following archive formats: binary, old ASCII,
+new ASCII, crc, HPUX binary, HPUX old ASCII, old tar, and POSIX.1 tar.
+The tar format is provided for compatability with the tar program. By
+default, cpio creates binary format archives, for compatibility with
+older cpio programs. When extracting from archives, cpio automatically
+recognizes which kind of archive it is reading and can read archives
+created on machines with a different byte-order.
+
+
+File: cpio.info, Node: Tutorial, Next: Invoking `cpio', Prev: Introduction, Up: Top
+
+Tutorial
+********
+
+GNU cpio performs three primary functions. Copying files to an
+archive, Extracting files from an archive, and passing files to another
+directory tree. An archive can be a file on disk, one or more floppy
+disks, or one or more tapes.
+
+ When creating an archive, cpio takes the list of files to be
+processed from the standard input, and then sends the archive to the
+standard output, or to the device defined by the `-F' option. *Note
+Copy-out mode::. Usually find or ls is used to provide this list to
+the standard input. In the following example you can see the
+possibilities for archiving the contents of a single directory.
+
+ % ls | cpio -ov > directory.cpio
+
+ The `-o' option creates the archive, and the `-v' option prints the
+names of the files archived as they are added. Notice that the options
+can be put together after a single `-' or can be placed separately on
+the command line. The `>' redirects the cpio output to the file
+`directory.cpio'.
+
+ If you wanted to archive an entire directory tree, the find command
+can provide the file list to cpio:
+
+ % find . -print -depth | cpio -ov > tree.cpio
+
+ This will take all the files in the current directory, the
+directories below and place them in the archive tree.cpio. Again the
+`-o' creates an archive, and the `-v' option shows you the name of the
+files as they are archived. *Note Copy-out mode::. Using the `.' in
+the find statement will give you more flexibility when doing restores,
+as it will save file names with a relative path vice a hard wired,
+absolute path. The `-depth' option forces `find' to print of the
+entries in a directory before printing the directory itself. This
+limits the effects of restrictive directory permissions by printing the
+directory entries in a directory before the directory name itself.
+
+ Extracting an archive requires a bit more thought because cpio will
+not create directories by default. Another characteristic, is it will
+not overwrite existing files unless you tell it to.
+
+ % cpio -iv < directory.cpio
+
+ This will retrieve the files archived in the file directory.cpio and
+place them in the present directory. The `-i' option extracts the
+archive and the `-v' shows the file names as they are extracted. If
+you are dealing with an archived directory tree, you need to use the
+`-d' option to create directories as necessary, something like:
+
+ % cpio -idv < tree.cpio
+
+ This will take the contents of the archive tree.cpio and extract it
+to the current directory. If you try to extract the files on top of
+files of the same name that already exist (and have the same or later
+modification time) cpio will not extract the file unless told to do so
+by the -u option. *Note Copy-in mode::.
+
+ In copy-pass mode, cpio copies files from one directory tree to
+another, combining the copy-out and copy-in steps without actually
+using an archive. It reads the list of files to copy from the standard
+input; the directory into which it will copy them is given as a
+non-option argument. *Note Copy-pass mode::.
+
+ % find . -depth -print0 | cpio --null -pvd new-dir
+
+ The example shows copying the files of the present directory, and
+sub-directories to a new directory called new-dir. Some new options are
+the `-print0' available with GNU find, combined with the `--null'
+option of cpio. These two options act together to send file names
+between find and cpio, even if special characters are embedded in the
+file names. Another is `-p', which tells cpio to pass the files it
+finds to the directory `new-dir'.
+
+
+File: cpio.info, Node: Invoking `cpio', Next: Media, Prev: Tutorial, Up: Top
+
+Invoking cpio
+*************
+
+* Menu:
+
+* Copy-out mode::
+* Copy-in mode::
+* Copy-pass mode::
+* Options::
+
+
+File: cpio.info, Node: Copy-out mode, Next: Copy-in mode, Prev: Invoking `cpio', Up: Invoking `cpio'
+
+Copy-out mode
+=============
+
+In copy-out mode, cpio copies files into an archive. It reads a list
+of filenames, one per line, on the standard input, and writes the
+archive onto the standard output. A typical way to generate the list
+of filenames is with the find command; you should give find the -depth
+option to minimize problems with permissions on directories that are
+unreadable. *Note Options::.
+
+ cpio {-o|--create} [-0acvABLV] [-C bytes] [-H format]
+ [-M message] [-O [[user@]host:]archive] [-F [[user@]host:]archive]
+ [--file=[[user@]host:]archive] [--format=format]
+ [--message=message][--null] [--reset-access-time] [--verbose]
+ [--dot] [--append] [--block-size=blocks] [--dereference]
+ [--io-size=bytes] [--rsh-command=command] [--help] [--version]
+ < name-list [> archive]
+
+
+File: cpio.info, Node: Copy-in mode, Next: Copy-pass mode, Prev: Copy-out mode, Up: Invoking `cpio'
+
+Copy-in mode
+============
+
+In copy-in mode, cpio copies files out of an archive or lists the
+archive contents. It reads the archive from the standard input. Any
+non-option command line arguments are shell globbing patterns; only
+files in the archive whose names match one or more of those patterns are
+copied from the archive. Unlike in the shell, an initial `.' in a
+filename does match a wildcard at the start of a pattern, and a `/' in a
+filename can match wildcards. If no patterns are given, all files are
+extracted. *Note Options::.
+
+ cpio {-i|--extract} [-bcdfmnrtsuvBSV] [-C bytes] [-E file]
+ [-H format] [-M message] [-R [user][:.][group]]
+ [-I [[user@]host:]archive] [-F [[user@]host:]archive]
+ [--file=[[user@]host:]archive] [--make-directories]
+ [--nonmatching] [--preserve-modification-time]
+ [--numeric-uid-gid] [--rename] [--list] [--swap-bytes] [--swap]
+ [--dot] [--unconditional] [--verbose] [--block-size=blocks]
+ [--swap-halfwords] [--io-size=bytes] [--pattern-file=file]
+ [--format=format] [--owner=[user][:.][group]]
+ [--no-preserve-owner] [--message=message] [--help] [--version]
+ [-no-absolute-filenames] [--sparse] [-only-verify-crc] [-quiet]
+ [--rsh-command=command] [pattern...] [< archive]
+
+
+File: cpio.info, Node: Copy-pass mode, Next: Options, Prev: Copy-in mode, Up: Invoking `cpio'
+
+Copy-pass mode
+==============
+
+In copy-pass mode, cpio copies files from one directory tree to
+another, combining the copy-out and copy-in steps without actually
+using an archive. It reads the list of files to copy from the standard
+input; the directory into which it will copy them is given as a
+non-option argument. *Note Options::.
+
+ cpio {-p|--pass-through} [-0adlmuvLV] [-R [user][:.][group]]
+ [--null] [--reset-access-time] [--make-directories] [--link]
+ [--preserve-modification-time] [--unconditional] [--verbose]
+ [--dot] [--dereference] [--owner=[user][:.][group]] [--sparse]
+ [--no-preserve-owner] [--help] [--version] destination-directory
+ < name-list
+
+
+File: cpio.info, Node: Options, Prev: Copy-pass mode, Up: Invoking `cpio'
+
+Options
+=======
+
+`-0, --null'
+ Read a list of filenames terminated by a null character, instead
+ of a newline, so that files whose names contain newlines can be
+ archived. GNU find is one way to produce a list of
+ null-terminated filenames. This option may be used in copy-out
+ and copy-pass modes.
+
+`-a, --reset-access-time'
+ Reset the access times of files after reading them, so that it
+ does not look like they have just been read.
+
+`-A, --append'
+ Append to an existing archive. Only works in copy-out mode. The
+ archive must be a disk file specified with the -O or -F (-file)
+ option.
+
+`-b, --swap'
+ Swap both halfwords of words and bytes of halfwords in the data.
+ Equivalent to -sS. This option may be used in copy-in mode. Use
+ this option to convert 32-bit integers between big-endian and
+ little-endian machines.
+
+`-B'
+ Set the I/O block size to 5120 bytes. Initially the block size is
+ 512 bytes.
+
+`--block-size=BLOCK-SIZE'
+ Set the I/O block size to BLOCK-SIZE * 512 bytes.
+
+`-c'
+ Use the old portable (ASCII) archive format.
+
+`-C IO-SIZE, --io-size=IO-SIZE'
+ Set the I/O block size to IO-SIZE bytes.
+
+`-d, --make-directories'
+ Create leading directories where needed.
+
+`-E FILE, --pattern-file=FILE'
+ Read additional patterns specifying filenames to extract or list
+ from FILE. The lines of FILE are treated as if they had been
+ non-option arguments to cpio. This option is used in copy-in mode,
+
+`-f, --nonmatching'
+ Only copy files that do not match any of the given patterns.
+
+`-F, --file=archive'
+ Archive filename to use instead of standard input or output. To
+ use a tape drive on another machine as the archive, use a filename
+ that starts with `HOSTNAME:'. The hostname can be preceded by a
+ username and an `@' to access the remote tape drive as that user,
+ if you have permission to do so (typically an entry in that user's
+ `~/.rhosts' file).
+
+`--force-local'
+ With -F, -I, or -O, take the archive file name to be a local file
+ even if it contains a colon, which would ordinarily indicate a
+ remote host name.
+
+`-H FORMAT, --format=FORMAT'
+ Use archive format FORMAT. The valid formats are listed below;
+ the same names are also recognized in all-caps. The default in
+ copy-in mode is to automatically detect the archive format, and in
+ copy-out mode is `bin'.
+
+ `bin'
+ The obsolete binary format.
+
+ `odc'
+ The old (POSIX.1) portable format.
+
+ `newc'
+ The new (SVR4) portable format, which supports file systems
+ having more than 65536 i-nodes.
+
+ `crc'
+ The new (SVR4) portable format with a checksum added.
+
+ `tar'
+ The old tar format.
+
+ `ustar'
+ The POSIX.1 tar format. Also recognizes GNU tar archives,
+ which are similar but not identical.
+
+ `hpbin'
+ The obsolete binary format used by HPUX's cpio (which stores
+ device files differently).
+
+ `hpodc'
+ The portable format used by HPUX's cpio (which stores device
+ files differently).
+
+`-i, --extract'
+ Run in copy-in mode. *Note Copy-in mode::.
+
+`-I archive'
+ Archive filename to use instead of standard input. To use a tape
+ drive on another machine as the archive, use a filename that
+ starts with `HOSTNAME:'. The hostname can be preceded by a
+ username and an `@' to access the remote tape drive as that user,
+ if you have permission to do so (typically an entry in that user's
+ `~/.rhosts' file).
+
+`-k'
+ Ignored; for compatibility with other versions of cpio.
+
+`-l, --link'
+ Link files instead of copying them, when possible.
+
+`-L, --dereference'
+ Copy the file that a symbolic link points to, rather than the
+ symbolic link itself.
+
+`-m, --preserve-modification-time'
+ Retain previous file modification times when creating files.
+
+`-M MESSAGE, --message=MESSAGE'
+ Print MESSAGE when the end of a volume of the backup media (such
+ as a tape or a floppy disk) is reached, to prompt the user to
+ insert a new volume. If MESSAGE contains the string "%d", it is
+ replaced by the current volume number (starting at 1).
+
+`-n, --numeric-uid-gid'
+ Show numeric UID and GID instead of translating them into names
+ when using the `--verbose option'.
+
+`--no-absolute-filenames'
+ Create all files relative to the current directory in copy-in
+ mode, even if they have an absolute file name in the archive.
+
+`--no-preserve-owner'
+ Do not change the ownership of the files; leave them owned by the
+ user extracting them. This is the default for non-root users, so
+ that users on System V don't inadvertantly give away files. This
+ option can be used in copy-in mode and copy-pass mode
+
+`-o, --create'
+ Run in copy-out mode. *Note Copy-out mode::.
+
+`-O archive'
+ Archive filename to use instead of standard output. To use a tape
+ drive on another machine as the archive, use a filename that
+ starts with `HOSTNAME:'. The hostname can be preceded by a
+ username and an `@' to access the remote tape drive as that user,
+ if you have permission to do so (typically an entry in that user's
+ `~/.rhosts' file).
+
+`--only-verify-crc'
+ Verify the CRC's of each file in the archive, when reading a CRC
+ format archive. Don't actually extract the files.
+
+`-p, --pass-through'
+ Run in copy-pass mode. *Note Copy-pass mode::.
+
+`--quiet'
+ Do not print the number of blocks copied.
+
+`-r, --rename'
+ Interactively rename files.
+
+`-R [user][:.][group], --owner [user][:.][group]'
+ Set the ownership of all files created to the specified user and/or
+ group in copy-out and copy-pass modes. Either the user, the
+ group, or both, must be present. If the group is omitted but the
+ ":" or "." separator is given, use the given user's login group.
+ Only the super-user can change files' ownership.
+
+`--rsh-command=COMMAND'
+ Notifies cpio that is should use COMMAND to communicate with remote
+ devices.
+
+`-s, --swap-bytes'
+ Swap the bytes of each halfword (pair of bytes) in the files.This
+ option can be used in copy-in mode.
+
+`-S, --swap-halfwords'
+ Swap the halfwords of each word (4 bytes) in the files. This
+ option may be used in copy-in mode.
+
+`--sparse'
+ Write files with large blocks of zeros as sparse files. This
+ option is used in copy-in and copy-pass modes.
+
+`-t, --list'
+ Print a table of contents of the input.
+
+`-u, --unconditional'
+ Replace all files, without asking whether to replace existing
+ newer files with older files.
+
+`-v, --verbose'
+ List the files processed, or with `-t', give an `ls -l' style
+ table of contents listing. In a verbose table of contents of a
+ ustar archive, user and group names in the archive that do not
+ exist on the local system are replaced by the names that
+ correspond locally to the numeric UID and GID stored in the
+ archive.
+
+`-V --dot'
+ Print a `.' for each file processed.
+
+`--version'
+ Print the cpio program version number and exit.
+
+
+File: cpio.info, Node: Media, Next: Concept Index, Prev: Invoking `cpio', Up: Top
+
+Magnetic Media
+**************
+
+Archives are usually written on removable media-tape cartridges, mag
+tapes, or floppy disks.
+
+ The amount of data a tape or disk holds depends not only on its size,
+but also on how it is formatted. A 2400 foot long reel of mag tape
+holds 40 megabytes of data when formated at 1600 bits per inch. The
+physically smaller EXABYTE tape cartridge holds 2.3 gigabytes.
+
+ Magnetic media are re-usable-once the archive on a tape is no longer
+needed, the archive can be erased and the tape or disk used over. Media
+quality does deteriorate with use, however. Most tapes or disks should
+be disgarded when they begin to produce data errors.
+
+ Magnetic media are written and erased using magnetic fields, and
+should be protected from such fields to avoid damage to stored data.
+Sticking a floppy disk to a filing cabinet using a magnet is probably
+not a good idea.
+
+
+File: cpio.info, Node: Concept Index, Prev: Media, Up: Top
+
+Concept Index
+*************
+
+* Menu:
+
+* command line options: Invoking `cpio'.
+* copying directory structures: Tutorial.
+* creating a cpio archive: Tutorial.
+* extracting a cpio archive: Tutorial.
+* invoking cpio: Invoking `cpio'.
+* magnetic media: Media.
+* passing directory structures: Tutorial.
+
+
+
+Tag Table:
+Node: Top938
+Node: Introduction1657
+Node: Tutorial2369
+Node: Invoking `cpio'6038
+Node: Copy-out mode6227
+Node: Copy-in mode7153
+Node: Copy-pass mode8531
+Node: Options9324
+Node: Media16592
+Node: Concept Index17575
+
+End Tag Table
diff --git a/doc/cpio.texi b/doc/cpio.texi
new file mode 100644
index 0000000..ae534f4
--- a/dev/null
+++ b/doc/cpio.texi
@@ -0,0 +1,563 @@
+\input texinfo @c -*-texinfo-*-
+@c %**start of header
+@setfilename cpio.info
+@settitle cpio
+@setchapternewpage off
+@set VERSION GNU cpio 2.5
+@set RELEASEDATE June 2002
+@c %**end of header
+
+@ifinfo
+@format
+START-INFO-DIR-ENTRY
+* cpio: (cpio). Making tape (or disk) archives.
+END-INFO-DIR-ENTRY
+@end format
+@end ifinfo
+
+@ifinfo
+This file documents @value{VERSION}.
+
+Copyright (C) 1995, 2001, 2002 Free Software Foundation, Inc.
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+@ignore
+Permission is granted to process this file through TeX and print the
+results, provided the printed document carries copying permission
+notice identical to this one except for the removal of this paragraph
+
+
+@end ignore
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the entire
+resulting derived work is distributed under the terms of a permission
+notice identical to this one.
+
+Permission is granted to copy and distribute translations of this manual
+into another language, under the above conditions for modified versions,
+except that this permission notice may be stated in a translation approved
+by the Foundation.
+@end ifinfo
+
+
+@titlepage
+@title GNU CPIO
+@subtitle @value{VERSION} @value{RELEASEDATE}
+@author by Robert Carleton
+@c copyright page
+@page
+@vskip 0pt plus 1filll
+Copyright @copyright{} 1995, 2001, 2002 Free Software Foundation, Inc.
+@sp 2
+This is the first edition of the GNU cpio documentation,@*
+and is consistent with @value{VERSION}.@*
+@sp 2
+Published by the Free Software Foundation @*
+59 Temple Place - Suite 330, @*
+Boston, MA 02111-1307, USA @*
+
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the entire
+resulting derived work is distributed under the terms of a permission
+notice identical to this one.
+
+Permission is granted to copy and distribute translations of this manual
+into another language, under the above conditions for modified versions,
+except that this permission notice may be stated in a translation
+approved by the Free Software Foundation.
+@end titlepage
+
+@ifinfo
+@node Top, Introduction, (dir), (dir)
+@comment node-name, next, previous, up
+@top
+
+GNU cpio is a tool for creating and extracting archives, or copying
+files from one place to another. It handles a number of cpio formats as
+well as reading and writing tar files. This is the first edition of the
+GNU cpio documentation and is consistant with @value{VERSION}.
+
+@menu
+* Introduction::
+* Tutorial:: Getting started.
+* Invoking `cpio':: How to invoke `cpio'.
+* Media:: Using tapes and other archive media.
+* Concept Index:: Concept index.
+
+ --- The Detailed Node Listing ---
+
+Invoking cpio
+
+* Copy-out mode::
+* Copy-in mode::
+* Copy-pass mode::
+* Options::
+@end menu
+
+@end ifinfo
+
+@node Introduction, Tutorial, Top, Top
+@comment node-name, next, previous, up
+@chapter Introduction
+
+GNU cpio copies files into or out of a cpio or tar archive, The archive
+can be another file on the disk, a magnetic tape, or a pipe.
+
+GNU cpio supports the following archive formats: binary, old ASCII, new
+ASCII, crc, HPUX binary, HPUX old ASCII, old tar, and POSIX.1 tar. The
+tar format is provided for compatability with the tar program. By
+default, cpio creates binary format archives, for compatibility with
+older cpio programs. When extracting from archives, cpio automatically
+recognizes which kind of archive it is reading and can read archives
+created on machines with a different byte-order.
+
+@node Tutorial, Invoking `cpio', Introduction, Top
+@comment node-name, next, previous, up
+@chapter Tutorial
+@cindex creating a cpio archive
+@cindex extracting a cpio archive
+@cindex copying directory structures
+@cindex passing directory structures
+
+
+GNU cpio performs three primary functions. Copying files to an
+archive, Extracting files from an archive, and passing files to another
+directory tree. An archive can be a file on disk, one or more floppy
+disks, or one or more tapes.
+
+When creating an archive, cpio takes the list of files to be processed
+from the standard input, and then sends the archive to the standard
+output, or to the device defined by the @samp{-F} option.
+@xref{Copy-out mode}. Usually find or ls is used to provide this list
+to the standard input. In the following example you can see the
+possibilities for archiving the contents of a single directory.
+
+
+@example
+@cartouche
+% ls | cpio -ov > directory.cpio
+@end cartouche
+@end example
+
+The @samp{-o} option creates the archive, and the @samp{-v} option
+prints the names of the files archived as they are added. Notice that
+the options can be put together after a single @samp{-} or can be placed
+separately on the command line. The @samp{>} redirects the cpio output
+to the file @samp{directory.cpio}.
+
+
+If you wanted to archive an entire directory tree, the find command can
+provide the file list to cpio:
+
+
+@example
+@cartouche
+% find . -print -depth | cpio -ov > tree.cpio
+@end cartouche
+@end example
+
+
+This will take all the files in the current directory, the directories
+below and place them in the archive tree.cpio. Again the @samp{-o}
+creates an archive, and the @samp{-v} option shows you the name of the
+files as they are archived. @xref{Copy-out mode}. Using the `.' in the
+find statement will give you more flexibility when doing restores, as it
+will save file names with a relative path vice a hard wired, absolute
+path. The @samp{-depth} option forces @samp{find} to print of the
+entries in a directory before printing the directory itself. This
+limits the effects of restrictive directory permissions by printing the
+directory entries in a directory before the directory name itself.
+
+
+
+
+Extracting an archive requires a bit more thought because cpio will not
+create directories by default. Another characteristic, is it will not
+overwrite existing files unless you tell it to.
+
+
+@example
+@cartouche
+% cpio -iv < directory.cpio
+@end cartouche
+@end example
+
+This will retrieve the files archived in the file directory.cpio and
+place them in the present directory. The @samp{-i} option extracts the
+archive and the @samp{-v} shows the file names as they are extracted.
+If you are dealing with an archived directory tree, you need to use the
+@samp{-d} option to create directories as necessary, something like:
+
+@example
+@cartouche
+% cpio -idv < tree.cpio
+@end cartouche
+@end example
+
+This will take the contents of the archive tree.cpio and extract it to
+the current directory. If you try to extract the files on top of files
+of the same name that already exist (and have the same or later
+modification time) cpio will not extract the file unless told to do so
+by the -u option. @xref{Copy-in mode}.
+
+
+In copy-pass mode, cpio copies files from one directory tree to another,
+combining the copy-out and copy-in steps without actually using an
+archive. It reads the list of files to copy from the standard input;
+the directory into which it will copy them is given as a non-option
+argument. @xref{Copy-pass mode}.
+
+@example
+@cartouche
+% find . -depth -print0 | cpio --null -pvd new-dir
+@end cartouche
+@end example
+
+
+The example shows copying the files of the present directory, and
+sub-directories to a new directory called new-dir. Some new options are
+the @samp{-print0} available with GNU find, combined with the
+@samp{--null} option of cpio. These two options act together to send
+file names between find and cpio, even if special characters are
+embedded in the file names. Another is @samp{-p}, which tells cpio to
+pass the files it finds to the directory @samp{new-dir}.
+
+@node Invoking `cpio', Media, Tutorial, Top
+@comment node-name, next, previous, up
+@chapter Invoking cpio
+@cindex invoking cpio
+@cindex command line options
+
+@menu
+* Copy-out mode::
+* Copy-in mode::
+* Copy-pass mode::
+* Options::
+@end menu
+
+@node Copy-out mode, Copy-in mode, Invoking `cpio', Invoking `cpio'
+@comment node-name, next, previous, up
+@section Copy-out mode
+
+In copy-out mode, cpio copies files into an archive. It reads a list
+of filenames, one per line, on the standard input, and writes the
+archive onto the standard output. A typical way to generate the list
+of filenames is with the find command; you should give find the -depth
+option to minimize problems with permissions on directories that are
+unreadable.
+@xref{Options}.
+
+@example
+cpio @{-o|--create@} [-0acvABLV] [-C bytes] [-H format]
+[-M message] [-O [[user@@]host:]archive] [-F [[user@@]host:]archive]
+[--file=[[user@@]host:]archive] [--format=format]
+[--message=message][--null] [--reset-access-time] [--verbose]
+[--dot] [--append] [--block-size=blocks] [--dereference]
+[--io-size=bytes] [--rsh-command=command] [--help] [--version]
+< name-list [> archive]
+@end example
+
+@node Copy-in mode, Copy-pass mode, Copy-out mode, Invoking `cpio'
+@comment node-name, next, previous, up
+@section Copy-in mode
+
+In copy-in mode, cpio copies files out of an archive or lists the
+archive contents. It reads the archive from the standard input. Any
+non-option command line arguments are shell globbing patterns; only
+files in the archive whose names match one or more of those patterns are
+copied from the archive. Unlike in the shell, an initial `.' in a
+filename does match a wildcard at the start of a pattern, and a `/' in a
+filename can match wildcards. If no patterns are given, all files are
+extracted. @xref{Options}.
+
+@example
+cpio @{-i|--extract@} [-bcdfmnrtsuvBSV] [-C bytes] [-E file]
+[-H format] [-M message] [-R [user][:.][group]]
+[-I [[user@@]host:]archive] [-F [[user@@]host:]archive]
+[--file=[[user@@]host:]archive] [--make-directories]
+[--nonmatching] [--preserve-modification-time]
+[--numeric-uid-gid] [--rename] [--list] [--swap-bytes] [--swap]
+[--dot] [--unconditional] [--verbose] [--block-size=blocks]
+[--swap-halfwords] [--io-size=bytes] [--pattern-file=file]
+[--format=format] [--owner=[user][:.][group]]
+[--no-preserve-owner] [--message=message] [--help] [--version]
+[-no-absolute-filenames] [--sparse] [-only-verify-crc] [-quiet]
+[--rsh-command=command] [pattern...] [< archive]
+@end example
+
+@node Copy-pass mode, Options, Copy-in mode, Invoking `cpio'
+@comment node-name, next, previous, up
+@section Copy-pass mode
+
+In copy-pass mode, cpio copies files from one directory tree to
+another, combining the copy-out and copy-in steps without actually
+using an archive. It reads the list of files to copy from the
+standard input; the directory into which it will copy them is given as
+a non-option argument.
+@xref{Options}.
+
+@example
+cpio @{-p|--pass-through@} [-0adlmuvLV] [-R [user][:.][group]]
+[--null] [--reset-access-time] [--make-directories] [--link]
+[--preserve-modification-time] [--unconditional] [--verbose]
+[--dot] [--dereference] [--owner=[user][:.][group]] [--sparse]
+[--no-preserve-owner] [--help] [--version] destination-directory
+< name-list
+@end example
+
+
+
+@node Options, , Copy-pass mode, Invoking `cpio'
+@comment node-name, next, previous, up
+@section Options
+
+
+@table @code
+
+
+@item -0, --null
+Read a list of filenames terminated by a null character, instead of a
+newline, so that files whose names contain newlines can be archived.
+GNU find is one way to produce a list of null-terminated filenames.
+This option may be used in copy-out and copy-pass modes.
+
+@item -a, --reset-access-time
+Reset the access times of files after reading them, so
+that it does not look like they have just been read.
+
+@item -A, --append
+Append to an existing archive. Only works in copy-out
+mode. The archive must be a disk file specified with
+the -O or -F (--file) option.
+
+@item -b, --swap
+Swap both halfwords of words and bytes of halfwords in the data.
+Equivalent to -sS. This option may be used in copy-in mode. Use this
+option to convert 32-bit integers between big-endian and little-endian
+machines.
+
+@item -B
+Set the I/O block size to 5120 bytes. Initially the
+block size is 512 bytes.
+
+@item --block-size=BLOCK-SIZE
+Set the I/O block size to BLOCK-SIZE * 512 bytes.
+
+@item -c
+Use the old portable (ASCII) archive format.
+
+@item -C IO-SIZE, --io-size=IO-SIZE
+Set the I/O block size to IO-SIZE bytes.
+
+@item -d, --make-directories
+Create leading directories where needed.
+
+@item -E FILE, --pattern-file=FILE
+Read additional patterns specifying filenames to extract or list from
+FILE. The lines of FILE are treated as if they had been non-option
+arguments to cpio. This option is used in copy-in mode,
+
+@item -f, --nonmatching
+Only copy files that do not match any of the given
+patterns.
+
+@item -F, --file=archive
+Archive filename to use instead of standard input or output. To use a
+tape drive on another machine as the archive, use a filename that starts
+with `HOSTNAME:'. The hostname can be preceded by a username and an
+`@@' to access the remote tape drive as that user, if you have
+permission to do so (typically an entry in that user's `~/.rhosts'
+file).
+
+@item --force-local
+With -F, -I, or -O, take the archive file name to be a
+local file even if it contains a colon, which would
+ordinarily indicate a remote host name.
+
+@item -H FORMAT, --format=FORMAT
+Use archive format FORMAT. The valid formats are listed below; the same
+names are also recognized in all-caps. The default in copy-in mode is
+to automatically detect the archive format, and in copy-out mode is
+@samp{bin}.
+
+@table @samp
+@item bin
+The obsolete binary format.
+
+@item odc
+The old (POSIX.1) portable format.
+
+@item newc
+The new (SVR4) portable format, which supports file systems having more
+than 65536 i-nodes.
+
+@item crc
+The new (SVR4) portable format with a checksum added.
+
+@item tar
+The old tar format.
+
+@item ustar
+The POSIX.1 tar format. Also recognizes GNU tar archives, which are
+similar but not identical.
+
+@item hpbin
+The obsolete binary format used by HPUX's cpio (which stores device
+files differently).
+
+@item hpodc
+The portable format used by HPUX's cpio (which stores device files
+differently).
+@end table
+
+@item -i, --extract
+Run in copy-in mode.
+@xref{Copy-in mode}.
+
+@item -I archive
+Archive filename to use instead of standard input. To use a tape drive
+on another machine as the archive, use a filename that starts with
+`HOSTNAME:'. The hostname can be preceded by a username and an `@@' to
+access the remote tape drive as that user, if you have permission to do
+so (typically an entry in that user's `~/.rhosts' file).
+
+@item -k
+Ignored; for compatibility with other versions of cpio.
+
+@item -l, --link
+Link files instead of copying them, when possible.
+
+@item -L, --dereference
+Copy the file that a symbolic link points to, rather than the symbolic
+link itself.
+
+@item -m, --preserve-modification-time
+Retain previous file modification times when creating files.
+
+@item -M MESSAGE, --message=MESSAGE
+Print MESSAGE when the end of a volume of the backup media (such as a
+tape or a floppy disk) is reached, to prompt the user to insert a new
+volume. If MESSAGE contains the string "%d", it is replaced by the
+current volume number (starting at 1).
+
+@item -n, --numeric-uid-gid
+Show numeric UID and GID instead of translating them into names when using the
+@samp{--verbose option}.
+
+@item --no-absolute-filenames
+Create all files relative to the current directory in copy-in mode, even
+if they have an absolute file name in the archive.
+
+@item --no-preserve-owner
+Do not change the ownership of the files; leave them owned by the user
+extracting them. This is the default for non-root users, so that users
+on System V don't inadvertantly give away files. This option can be
+used in copy-in mode and copy-pass mode
+
+@item -o, --create
+Run in copy-out mode.
+@xref{Copy-out mode}.
+
+@item -O archive
+Archive filename to use instead of standard output. To use a tape drive
+on another machine as the archive, use a filename that starts with
+`HOSTNAME:'. The hostname can be preceded by a username and an `@@' to
+access the remote tape drive as that user, if you have permission to do
+so (typically an entry in that user's `~/.rhosts' file).
+
+@item --only-verify-crc
+Verify the CRC's of each file in the archive, when reading a CRC format
+archive. Don't actually extract the files.
+
+@item -p, --pass-through
+Run in copy-pass mode.
+@xref{Copy-pass mode}.
+
+@item --quiet
+Do not print the number of blocks copied.
+
+@item -r, --rename
+Interactively rename files.
+
+@item -R [user][:.][group], --owner [user][:.][group]
+Set the ownership of all files created to the specified user and/or
+group in copy-out and copy-pass modes. Either the user, the group, or
+both, must be present. If the group is omitted but the ":" or "."
+separator is given, use the given user's login group. Only the
+super-user can change files' ownership.
+
+@item --rsh-command=COMMAND
+Notifies cpio that is should use COMMAND to communicate with remote
+devices.
+
+@item -s, --swap-bytes
+Swap the bytes of each halfword (pair of bytes) in the files.This option
+can be used in copy-in mode.
+
+@item -S, --swap-halfwords
+Swap the halfwords of each word (4 bytes) in the files. This option may
+be used in copy-in mode.
+
+@item --sparse
+Write files with large blocks of zeros as sparse files. This option is
+used in copy-in and copy-pass modes.
+
+@item -t, --list
+Print a table of contents of the input.
+
+@item -u, --unconditional
+Replace all files, without asking whether to replace
+existing newer files with older files.
+
+@item -v, --verbose
+List the files processed, or with @samp{-t}, give an @samp{ls -l} style
+table of contents listing. In a verbose table of contents of a ustar
+archive, user and group names in the archive that do not exist on the
+local system are replaced by the names that correspond locally to the
+numeric UID and GID stored in the archive.
+
+@item -V --dot
+Print a @kbd{.} for each file processed.
+
+@item --version
+Print the cpio program version number and exit.
+@end table
+
+
+@node Media, Concept Index, Invoking `cpio', Top
+@comment node-name, next, previous, up
+@chapter Magnetic Media
+@cindex magnetic media
+
+Archives are usually written on removable media--tape cartridges, mag
+tapes, or floppy disks.
+
+The amount of data a tape or disk holds depends not only on its size,
+but also on how it is formatted. A 2400 foot long reel of mag tape
+holds 40 megabytes of data when formated at 1600 bits per inch. The
+physically smaller EXABYTE tape cartridge holds 2.3 gigabytes.
+
+Magnetic media are re-usable--once the archive on a tape is no longer
+needed, the archive can be erased and the tape or disk used over. Media
+quality does deteriorate with use, however. Most tapes or disks should
+be disgarded when they begin to produce data errors.
+
+Magnetic media are written and erased using magnetic fields, and should
+be protected from such fields to avoid damage to stored data. Sticking
+a floppy disk to a filing cabinet using a magnet is probably not a good
+idea.
+
+
+@node Concept Index, , Media, Top
+@comment node-name, next, previous, up
+@unnumbered Concept Index
+@printindex cp
+@contents
+@bye
diff --git a/doc/mt.1 b/doc/mt.1
new file mode 100644
index 0000000..ee10add
--- a/dev/null
+++ b/doc/mt.1
@@ -0,0 +1,126 @@
+.TH MT 1L \" -*- nroff -*-
+.SH NAME
+mt \- control magnetic tape drive operation
+.SH SYNOPSIS
+.B mt
+[\-V] [\-f device] [\-\-file=device] [\-\-rsh-command=command] [\-\-version]
+operation [count]
+.SH DESCRIPTION
+This manual page
+documents the GNU version of
+.BR mt .
+.B mt
+performs the given
+.IR operation ,
+which must be one of the tape operations listed below, on a tape
+drive.
+.PP
+The default tape device to operate on is taken from the file
+.I /usr/include/sys/mtio.h
+when
+.B mt
+is compiled. It can be overridden by giving a device file name in
+the environment variable
+.BR TAPE
+or by a command line option (see below), which also overrides the
+environment variable.
+.PP
+The device must be either a character special file or a
+remote tape drive. To use a tape drive on another machine as the
+archive, use a filename that starts with `HOSTNAME:'. The
+hostname can be preceded by a username and an `@' to access the remote
+tape drive as that user, if you have permission to do so (typically an
+entry in that user's `~/.rhosts' file).
+.PP
+The available operations are listed below. Unique abbreviations are
+accepted. Not all operations are available on all systems, or work on
+all types of tape drives.
+Some operations optionally take a repeat count, which can be given
+after the operation name and defaults to 1.
+.IP "eof, weof"
+Write
+.I count
+EOF marks at current position.
+.IP fsf
+Forward space
+.I count
+files.
+The tape is positioned on the first block of the next file.
+.IP bsf
+Backward space
+.I count
+files.
+The tape is positioned on the first block of the next file.
+.IP fsr
+Forward space
+.I count
+records.
+.IP bsr
+Backward space
+.I count
+records.
+.IP bsfm
+Backward space
+.I count
+file marks.
+The tape is positioned on the beginning-of-the-tape side of
+the file mark.
+.IP fsfm
+Forward space
+.I count
+file marks.
+The tape is positioned on the beginning-of-the-tape side of
+the file mark.
+.IP asf
+Absolute space to file number
+.IR count .
+Equivalent to rewind followed by fsf
+.IR count .
+.IP seek
+Seek to block number
+.IR count .
+.IP eom
+Space to the end of the recorded media on the tape
+(for appending files onto tapes).
+.IP rewind
+Rewind the tape.
+.IP "offline, rewoffl"
+Rewind the tape and, if applicable, unload the tape.
+.IP status
+Print status information about the tape unit.
+.IP retension
+Rewind the tape, then wind it to the end of the reel,
+then rewind it again.
+.IP erase
+Erase the tape.
+.PP
+.B mt
+exits with a status of 0 if the operation succeeded, 1 if the
+operation or device name given was invalid, or 2 if the operation
+failed.
+.SS OPTIONS
+.TP
+.I "\-f, \-\-file=device"
+Use
+.I device
+as the file name of the tape drive to operate on.
+To use a
+tape drive on another machine, use a filename that
+starts with `HOSTNAME:'. The hostname can be preceded by a
+username and an `@' to access the remote tape drive as that user, if
+you have permission to do so (typically an entry in that user's
+`~/.rhosts' file).
+.TP
+.I "\-\-rsh-command=command"
+Notifies
+.B mt
+that it should use
+.I command
+to communicate with remote devices instead of
+.I /usr/bin/ssh
+or
+.IR /usr/bin/rsh .
+.TP
+.I "\-V, \-\-version"
+Print the version number of
+.BR mt .
diff --git a/src/.cvsignore b/src/.cvsignore
new file mode 100644
index 0000000..729dc3f
--- a/dev/null
+++ b/src/.cvsignore
@@ -0,0 +1,11 @@
+Makefile.in
+Makefile
+.deps
+.libs
+cpio
+mt
+rmt
+.gdbinit
+*.tar.gz
+*.tar.bz2
+localedir.h
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..1e5da40
--- a/dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,81 @@
+# This file is part of GNU cpio
+# Copyright (C) 2003, 2004 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+bin_PROGRAMS=cpio @CPIO_MT_PROG@
+libexec_PROGRAMS=@CPIO_RMT_PROG@
+EXTRA_PROGRAMS=mt rmt
+
+cpio_SOURCES = \
+ copyin.c\
+ copyout.c\
+ copypass.c\
+ defer.c\
+ dstring.c\
+ global.c\
+ main.c\
+ tar.c\
+ util.c\
+ error.c\
+ filemode.c\
+ dirname.c\
+ idcache.c\
+ makepath.c\
+ xmalloc.c\
+ stripslash.c\
+ userspec.c\
+ xstrdup.c
+
+noinst_HEADERS =\
+ cpio.h\
+ cpiohdr.h\
+ tar.h\
+ tarhdr.h\
+ defer.h\
+ dstring.h\
+ extern.h\
+ filetypes.h\
+ gettext.h\
+ system.h\
+ rmt.h\
+ safe-stat.h\
+ fnmatch.h\
+ getopt.h
+
+cpio_LDADD = @LIBOBJS@
+
+mt_SOURCES = \
+ mt.c argmatch.c
+mt_LDADD = @LIBOBJS@
+
+rmt_SOURCES = rmt.c
+rmt_LDADD = @LIBOBJS@
+
+EXTRA_DIST=\
+ getopt.c\
+ getopt1.c\
+ bcopy.c\
+ fnmatch.c\
+ mkdir.c\
+ strdup.c
+
+localedir = $(datadir)/locale
+
+DISTCLEANFILES = localedir.h
+localedir.h : Makefile
+ echo '#define LOCALEDIR "$(localedir)"' >$@
+
+mt.o main.o: localedir.h
diff --git a/src/alloca.c b/src/alloca.c
new file mode 100644
index 0000000..7061cec
--- a/dev/null
+++ b/src/alloca.c
@@ -0,0 +1,495 @@
+/* alloca.c -- allocate automatically reclaimed memory
+ (Mostly) portable public-domain implementation -- D A Gwyn
+
+ This implementation of the PWB library alloca function,
+ which is used to allocate space off the run-time stack so
+ that it is automatically reclaimed upon procedure exit,
+ was inspired by discussions with J. Q. Johnson of Cornell.
+ J.Otto Tennant <jot@cray.com> contributed the Cray support.
+
+ There are some preprocessor constants that can
+ be defined when compiling for your specific system, for
+ improved efficiency; however, the defaults should be okay.
+
+ The general concept of this implementation is to keep
+ track of all alloca-allocated blocks, and reclaim any
+ that are found to be deeper in the stack than the current
+ invocation. This heuristic does not reclaim storage as
+ soon as it becomes invalid, but it will do so eventually.
+
+ As a special case, alloca(0) reclaims storage without
+ allocating any. It is a good idea to use alloca(0) in
+ your main control loop, etc. to force garbage collection. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef emacs
+#include "blockinput.h"
+#endif
+
+/* If compiling with GCC 2, this file's not needed. */
+#if !defined (__GNUC__) || __GNUC__ < 2
+
+/* If someone has defined alloca as a macro,
+ there must be some other way alloca is supposed to work. */
+#ifndef alloca
+
+#ifdef emacs
+#ifdef static
+/* actually, only want this if static is defined as ""
+ -- this is for usg, in which emacs must undefine static
+ in order to make unexec workable
+ */
+#ifndef STACK_DIRECTION
+you
+lose
+-- must know STACK_DIRECTION at compile-time
+#endif /* STACK_DIRECTION undefined */
+#endif /* static */
+#endif /* emacs */
+
+/* If your stack is a linked list of frames, you have to
+ provide an "address metric" ADDRESS_FUNCTION macro. */
+
+#if defined (CRAY) && defined (CRAY_STACKSEG_END)
+long i00afunc ();
+#define ADDRESS_FUNCTION(arg) (char *) i00afunc (&(arg))
+#else
+#define ADDRESS_FUNCTION(arg) &(arg)
+#endif
+
+#if __STDC__
+typedef void *pointer;
+#else
+typedef char *pointer;
+#endif
+
+#define NULL 0
+
+/* Different portions of Emacs need to call different versions of
+ malloc. The Emacs executable needs alloca to call xmalloc, because
+ ordinary malloc isn't protected from input signals. On the other
+ hand, the utilities in lib-src need alloca to call malloc; some of
+ them are very simple, and don't have an xmalloc routine.
+
+ Non-Emacs programs expect this to call use xmalloc.
+
+ Callers below should use malloc. */
+
+#ifndef emacs
+#define malloc xmalloc
+#endif
+extern pointer malloc ();
+
+/* Define STACK_DIRECTION if you know the direction of stack
+ growth for your system; otherwise it will be automatically
+ deduced at run-time.
+
+ STACK_DIRECTION > 0 => grows toward higher addresses
+ STACK_DIRECTION < 0 => grows toward lower addresses
+ STACK_DIRECTION = 0 => direction of growth unknown */
+
+#ifndef STACK_DIRECTION
+#define STACK_DIRECTION 0 /* Direction unknown. */
+#endif
+
+#if STACK_DIRECTION != 0
+
+#define STACK_DIR STACK_DIRECTION /* Known at compile-time. */
+
+#else /* STACK_DIRECTION == 0; need run-time code. */
+
+static int stack_dir; /* 1 or -1 once known. */
+#define STACK_DIR stack_dir
+
+static void
+find_stack_direction ()
+{
+ static char *addr = NULL; /* Address of first `dummy', once known. */
+ auto char dummy; /* To get stack address. */
+
+ if (addr == NULL)
+ { /* Initial entry. */
+ addr = ADDRESS_FUNCTION (dummy);
+
+ find_stack_direction (); /* Recurse once. */
+ }
+ else
+ {
+ /* Second entry. */
+ if (ADDRESS_FUNCTION (dummy) > addr)
+ stack_dir = 1; /* Stack grew upward. */
+ else
+ stack_dir = -1; /* Stack grew downward. */
+ }
+}
+
+#endif /* STACK_DIRECTION == 0 */
+
+/* An "alloca header" is used to:
+ (a) chain together all alloca'ed blocks;
+ (b) keep track of stack depth.
+
+ It is very important that sizeof(header) agree with malloc
+ alignment chunk size. The following default should work okay. */
+
+#ifndef ALIGN_SIZE
+#define ALIGN_SIZE sizeof(double)
+#endif
+
+typedef union hdr
+{
+ char align[ALIGN_SIZE]; /* To force sizeof(header). */
+ struct
+ {
+ union hdr *next; /* For chaining headers. */
+ char *deep; /* For stack depth measure. */
+ } h;
+} header;
+
+static header *last_alloca_header = NULL; /* -> last alloca header. */
+
+/* Return a pointer to at least SIZE bytes of storage,
+ which will be automatically reclaimed upon exit from
+ the procedure that called alloca. Originally, this space
+ was supposed to be taken from the current stack frame of the
+ caller, but that method cannot be made to work for some
+ implementations of C, for example under Gould's UTX/32. */
+
+pointer
+alloca (size)
+ unsigned size;
+{
+ auto char probe; /* Probes stack depth: */
+ register char *depth = ADDRESS_FUNCTION (probe);
+
+#if STACK_DIRECTION == 0
+ if (STACK_DIR == 0) /* Unknown growth direction. */
+ find_stack_direction ();
+#endif
+
+ /* Reclaim garbage, defined as all alloca'd storage that
+ was allocated from deeper in the stack than currently. */
+
+ {
+ register header *hp; /* Traverses linked list. */
+
+#ifdef emacs
+ BLOCK_INPUT;
+#endif
+
+ for (hp = last_alloca_header; hp != NULL;)
+ if ((STACK_DIR > 0 && hp->h.deep > depth)
+ || (STACK_DIR < 0 && hp->h.deep < depth))
+ {
+ register header *np = hp->h.next;
+
+ free ((pointer) hp); /* Collect garbage. */
+
+ hp = np; /* -> next header. */
+ }
+ else
+ break; /* Rest are not deeper. */
+
+ last_alloca_header = hp; /* -> last valid storage. */
+
+#ifdef emacs
+ UNBLOCK_INPUT;
+#endif
+ }
+
+ if (size == 0)
+ return NULL; /* No allocation required. */
+
+ /* Allocate combined header + user data storage. */
+
+ {
+ register pointer new = malloc (sizeof (header) + size);
+ /* Address of header. */
+
+ if (new == 0)
+ abort();
+
+ ((header *) new)->h.next = last_alloca_header;
+ ((header *) new)->h.deep = depth;
+
+ last_alloca_header = (header *) new;
+
+ /* User storage begins just after header. */
+
+ return (pointer) ((char *) new + sizeof (header));
+ }
+}
+
+#if defined (CRAY) && defined (CRAY_STACKSEG_END)
+
+#ifdef DEBUG_I00AFUNC
+#include <stdio.h>
+#endif
+
+#ifndef CRAY_STACK
+#define CRAY_STACK
+#ifndef CRAY2
+/* Stack structures for CRAY-1, CRAY X-MP, and CRAY Y-MP */
+struct stack_control_header
+ {
+ long shgrow:32; /* Number of times stack has grown. */
+ long shaseg:32; /* Size of increments to stack. */
+ long shhwm:32; /* High water mark of stack. */
+ long shsize:32; /* Current size of stack (all segments). */
+ };
+
+/* The stack segment linkage control information occurs at
+ the high-address end of a stack segment. (The stack
+ grows from low addresses to high addresses.) The initial
+ part of the stack segment linkage control information is
+ 0200 (octal) words. This provides for register storage
+ for the routine which overflows the stack. */
+
+struct stack_segment_linkage
+ {
+ long ss[0200]; /* 0200 overflow words. */
+ long sssize:32; /* Number of words in this segment. */
+ long ssbase:32; /* Offset to stack base. */
+ long:32;
+ long sspseg:32; /* Offset to linkage control of previous
+ segment of stack. */
+ long:32;
+ long sstcpt:32; /* Pointer to task common address block. */
+ long sscsnm; /* Private control structure number for
+ microtasking. */
+ long ssusr1; /* Reserved for user. */
+ long ssusr2; /* Reserved for user. */
+ long sstpid; /* Process ID for pid based multi-tasking. */
+ long ssgvup; /* Pointer to multitasking thread giveup. */
+ long sscray[7]; /* Reserved for Cray Research. */
+ long ssa0;
+ long ssa1;
+ long ssa2;
+ long ssa3;
+ long ssa4;
+ long ssa5;
+ long ssa6;
+ long ssa7;
+ long sss0;
+ long sss1;
+ long sss2;
+ long sss3;
+ long sss4;
+ long sss5;
+ long sss6;
+ long sss7;
+ };
+
+#else /* CRAY2 */
+/* The following structure defines the vector of words
+ returned by the STKSTAT library routine. */
+struct stk_stat
+ {
+ long now; /* Current total stack size. */
+ long maxc; /* Amount of contiguous space which would
+ be required to satisfy the maximum
+ stack demand to date. */
+ long high_water; /* Stack high-water mark. */
+ long overflows; /* Number of stack overflow ($STKOFEN) calls. */
+ long hits; /* Number of internal buffer hits. */
+ long extends; /* Number of block extensions. */
+ long stko_mallocs; /* Block allocations by $STKOFEN. */
+ long underflows; /* Number of stack underflow calls ($STKRETN). */
+ long stko_free; /* Number of deallocations by $STKRETN. */
+ long stkm_free; /* Number of deallocations by $STKMRET. */
+ long segments; /* Current number of stack segments. */
+ long maxs; /* Maximum number of stack segments so far. */
+ long pad_size; /* Stack pad size. */
+ long current_address; /* Current stack segment address. */
+ long current_size; /* Current stack segment size. This
+ number is actually corrupted by STKSTAT to
+ include the fifteen word trailer area. */
+ long initial_address; /* Address of initial segment. */
+ long initial_size; /* Size of initial segment. */
+ };
+
+/* The following structure describes the data structure which trails
+ any stack segment. I think that the description in 'asdef' is
+ out of date. I only describe the parts that I am sure about. */
+
+struct stk_trailer
+ {
+ long this_address; /* Address of this block. */
+ long this_size; /* Size of this block (does not include
+ this trailer). */
+ long unknown2;
+ long unknown3;
+ long link; /* Address of trailer block of previous
+ segment. */
+ long unknown5;
+ long unknown6;
+ long unknown7;
+ long unknown8;
+ long unknown9;
+ long unknown10;
+ long unknown11;
+ long unknown12;
+ long unknown13;
+ long unknown14;
+ };
+
+#endif /* CRAY2 */
+#endif /* not CRAY_STACK */
+
+#ifdef CRAY2
+/* Determine a "stack measure" for an arbitrary ADDRESS.
+ I doubt that "lint" will like this much. */
+
+static long
+i00afunc (long *address)
+{
+ struct stk_stat status;
+ struct stk_trailer *trailer;
+ long *block, size;
+ long result = 0;
+
+ /* We want to iterate through all of the segments. The first
+ step is to get the stack status structure. We could do this
+ more quickly and more directly, perhaps, by referencing the
+ $LM00 common block, but I know that this works. */
+
+ STKSTAT (&status);
+
+ /* Set up the iteration. */
+
+ trailer = (struct stk_trailer *) (status.current_address
+ + status.current_size
+ - 15);
+
+ /* There must be at least one stack segment. Therefore it is
+ a fatal error if "trailer" is null. */
+
+ if (trailer == 0)
+ abort ();
+
+ /* Discard segments that do not contain our argument address. */
+
+ while (trailer != 0)
+ {
+ block = (long *) trailer->this_address;
+ size = trailer->this_size;
+ if (block == 0 || size == 0)
+ abort ();
+ trailer = (struct stk_trailer *) trailer->link;
+ if ((block <= address) && (address < (block + size)))
+ break;
+ }
+
+ /* Set the result to the offset in this segment and add the sizes
+ of all predecessor segments. */
+
+ result = address - block;
+
+ if (trailer == 0)
+ {
+ return result;
+ }
+
+ do
+ {
+ if (trailer->this_size <= 0)
+ abort ();
+ result += trailer->this_size;
+ trailer = (struct stk_trailer *) trailer->link;
+ }
+ while (trailer != 0);
+
+ /* We are done. Note that if you present a bogus address (one
+ not in any segment), you will get a different number back, formed
+ from subtracting the address of the first block. This is probably
+ not what you want. */
+
+ return (result);
+}
+
+#else /* not CRAY2 */
+/* Stack address function for a CRAY-1, CRAY X-MP, or CRAY Y-MP.
+ Determine the number of the cell within the stack,
+ given the address of the cell. The purpose of this
+ routine is to linearize, in some sense, stack addresses
+ for alloca. */
+
+static long
+i00afunc (long address)
+{
+ long stkl = 0;
+
+ long size, pseg, this_segment, stack;
+ long result = 0;
+
+ struct stack_segment_linkage *ssptr;
+
+ /* Register B67 contains the address of the end of the
+ current stack segment. If you (as a subprogram) store
+ your registers on the stack and find that you are past
+ the contents of B67, you have overflowed the segment.
+
+ B67 also points to the stack segment linkage control
+ area, which is what we are really interested in. */
+
+ stkl = CRAY_STACKSEG_END ();
+ ssptr = (struct stack_segment_linkage *) stkl;
+
+ /* If one subtracts 'size' from the end of the segment,
+ one has the address of the first word of the segment.
+
+ If this is not the first segment, 'pseg' will be
+ nonzero. */
+
+ pseg = ssptr->sspseg;
+ size = ssptr->sssize;
+
+ this_segment = stkl - size;
+
+ /* It is possible that calling this routine itself caused
+ a stack overflow. Discard stack segments which do not
+ contain the target address. */
+
+ while (!(this_segment <= address && address <= stkl))
+ {
+#ifdef DEBUG_I00AFUNC
+ fprintf (stderr, "%011o %011o %011o\n", this_segment, address, stkl);
+#endif
+ if (pseg == 0)
+ break;
+ stkl = stkl - pseg;
+ ssptr = (struct stack_segment_linkage *) stkl;
+ size = ssptr->sssize;
+ pseg = ssptr->sspseg;
+ this_segment = stkl - size;
+ }
+
+ result = address - this_segment;
+
+ /* If you subtract pseg from the current end of the stack,
+ you get the address of the previous stack segment's end.
+ This seems a little convoluted to me, but I'll bet you save
+ a cycle somewhere. */
+
+ while (pseg != 0)
+ {
+#ifdef DEBUG_I00AFUNC
+ fprintf (stderr, "%011o %011o\n", pseg, size);
+#endif
+ stkl = stkl - pseg;
+ ssptr = (struct stack_segment_linkage *) stkl;
+ size = ssptr->sssize;
+ pseg = ssptr->sspseg;
+ result += size;
+ }
+ return (result);
+}
+
+#endif /* not CRAY2 */
+#endif /* CRAY */
+
+#endif /* no alloca */
+#endif /* not GCC version 2 */
diff --git a/src/argmatch.c b/src/argmatch.c
new file mode 100644
index 0000000..d89699b
--- a/dev/null
+++ b/src/argmatch.c
@@ -0,0 +1,87 @@
+/* argmatch.c -- find a match for a string in an array
+ Copyright (C) 1990 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+/* Written by David MacKenzie <djm@ai.mit.edu> */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#ifdef STDC_HEADERS
+#include <string.h>
+#endif
+
+extern char *program_name;
+
+/* If ARG is an unambiguous match for an element of the
+ null-terminated array OPTLIST, return the index in OPTLIST
+ of the matched element, else -1 if it does not match any element
+ or -2 if it is ambiguous (is a prefix of more than one element). */
+
+int
+argmatch (arg, optlist)
+ char *arg;
+ char **optlist;
+{
+ int i; /* Temporary index in OPTLIST. */
+ int arglen; /* Length of ARG. */
+ int matchind = -1; /* Index of first nonexact match. */
+ int ambiguous = 0; /* If nonzero, multiple nonexact match(es). */
+
+ arglen = strlen (arg);
+
+ /* Test all elements for either exact match or abbreviated matches. */
+ for (i = 0; optlist[i]; i++)
+ {
+ if (!strncmp (optlist[i], arg, arglen))
+ {
+ if (strlen (optlist[i]) == arglen)
+ /* Exact match found. */
+ return i;
+ else if (matchind == -1)
+ /* First nonexact match found. */
+ matchind = i;
+ else
+ /* Second nonexact match found. */
+ ambiguous = 1;
+ }
+ }
+ if (ambiguous)
+ return -2;
+ else
+ return matchind;
+}
+
+/* Error reporting for argmatch.
+ KIND is a description of the type of entity that was being matched.
+ VALUE is the invalid value that was given.
+ PROBLEM is the return value from argmatch. */
+
+void
+invalid_arg (kind, value, problem)
+ char *kind;
+ char *value;
+ int problem;
+{
+ fprintf (stderr, "%s: ", program_name);
+ if (problem == -1)
+ fprintf (stderr, "invalid");
+ else /* Assume -2. */
+ fprintf (stderr, "ambiguous");
+ fprintf (stderr, " %s `%s'\n", kind, value);
+}
diff --git a/src/bcopy.c b/src/bcopy.c
new file mode 100644
index 0000000..a8991c5
--- a/dev/null
+++ b/src/bcopy.c
@@ -0,0 +1,19 @@
+/* bcopy.c -- copy memory.
+ Copy LENGTH bytes from SOURCE to DEST. Does not null-terminate.
+ In the public domain.
+ By David MacKenzie <djm@gnu.ai.mit.edu>. */
+
+void
+bcopy (source, dest, length)
+ char *source, *dest;
+ unsigned length;
+{
+ if (source < dest)
+ /* Moving from low mem to hi mem; start at end. */
+ for (source += length, dest += length; length; --length)
+ *--dest = *--source;
+ else if (source != dest)
+ /* Moving from hi mem to low mem; start at beginning. */
+ for (; length; --length)
+ *dest++ = *source++;
+}
diff --git a/src/copyin.c b/src/copyin.c
new file mode 100644
index 0000000..17e6744
--- a/dev/null
+++ b/src/copyin.c
@@ -0,0 +1,1605 @@
+/* copyin.c - extract or list a cpio archive
+ Copyright (C) 1990,1991,1992,2001,2002,2003 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#if defined(HAVE_CONFIG_H)
+# include <config.h>
+#endif
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "filetypes.h"
+#include "system.h"
+#include "cpiohdr.h"
+#include "dstring.h"
+#include "extern.h"
+#include "defer.h"
+#include "rmt.h"
+#ifndef FNM_PATHNAME
+#include <fnmatch.h>
+#endif
+
+#ifndef HAVE_LCHOWN
+#define lchown chown
+#endif
+
+static void read_pattern_file ();
+static void tape_skip_padding ();
+static void defer_copyin ();
+static void create_defered_links ();
+static void create_final_defers ();
+static int create_defered_links_to_skipped ();
+static int query_rename();
+static void list_file();
+static void copyin_file();
+static int try_existing_file();
+static void copyin_regular_file();
+static void copyin_directory();
+static void copyin_device();
+static void copyin_link();
+
+/* Return 16-bit integer I with the bytes swapped. */
+#define swab_short(i) ((((i) << 8) & 0xff00) | (((i) >> 8) & 0x00ff))
+
+/* Read the header, including the name of the file, from file
+ descriptor IN_DES into FILE_HDR. */
+
+void
+read_in_header (file_hdr, in_des)
+ struct new_cpio_header *file_hdr;
+ int in_des;
+{
+ long bytes_skipped = 0; /* Bytes of junk found before magic number. */
+
+ /* Search for a valid magic number. */
+
+ if (archive_format == arf_unknown)
+ {
+ char tmpbuf[512];
+ int check_tar;
+ int peeked_bytes;
+
+ while (archive_format == arf_unknown)
+ {
+ peeked_bytes = tape_buffered_peek (tmpbuf, in_des, 512);
+ if (peeked_bytes < 6)
+ error (1, 0, _("premature end of archive"));
+
+ if (!strncmp (tmpbuf, "070701", 6))
+ archive_format = arf_newascii;
+ else if (!strncmp (tmpbuf, "070707", 6))
+ archive_format = arf_oldascii;
+ else if (!strncmp (tmpbuf, "070702", 6))
+ {
+ archive_format = arf_crcascii;
+ crc_i_flag = TRUE;
+ }
+ else if ((*((unsigned short *) tmpbuf) == 070707) ||
+ (*((unsigned short *) tmpbuf) == swab_short ((unsigned short) 070707)))
+ archive_format = arf_binary;
+ else if (peeked_bytes >= 512
+ && (check_tar = is_tar_header (tmpbuf)))
+ {
+ if (check_tar == 2)
+ archive_format = arf_ustar;
+ else
+ archive_format = arf_tar;
+ }
+ else
+ {
+ tape_buffered_read ((char *) tmpbuf, in_des, 1L);
+ ++bytes_skipped;
+ }
+ }
+ }
+
+ if (archive_format == arf_tar || archive_format == arf_ustar)
+ {
+ if (append_flag)
+ last_header_start = input_bytes - io_block_size +
+ (in_buff - input_buffer);
+ if (bytes_skipped > 0)
+ error (0, 0, _("warning: skipped %ld bytes of junk"), bytes_skipped);
+ read_in_tar_header (file_hdr, in_des);
+ return;
+ }
+
+ file_hdr->c_tar_linkname = NULL;
+
+ tape_buffered_read ((char *) file_hdr, in_des, 6L);
+ while (1)
+ {
+ if (append_flag)
+ last_header_start = input_bytes - io_block_size
+ + (in_buff - input_buffer) - 6;
+ if (archive_format == arf_newascii
+ && !strncmp ((char *) file_hdr, "070701", 6))
+ {
+ if (bytes_skipped > 0)
+ error (0, 0, _("warning: skipped %ld bytes of junk"), bytes_skipped);
+ read_in_new_ascii (file_hdr, in_des);
+ break;
+ }
+ if (archive_format == arf_crcascii
+ && !strncmp ((char *) file_hdr, "070702", 6))
+ {
+ if (bytes_skipped > 0)
+ error (0, 0, _("warning: skipped %ld bytes of junk"), bytes_skipped);
+ read_in_new_ascii (file_hdr, in_des);
+ break;
+ }
+ if ( (archive_format == arf_oldascii || archive_format == arf_hpoldascii)
+ && !strncmp ((char *) file_hdr, "070707", 6))
+ {
+ if (bytes_skipped > 0)
+ error (0, 0, _("warning: skipped %ld bytes of junk"), bytes_skipped);
+ read_in_old_ascii (file_hdr, in_des);
+ break;
+ }
+ if ( (archive_format == arf_binary || archive_format == arf_hpbinary)
+ && (file_hdr->c_magic == 070707
+ || file_hdr->c_magic == swab_short ((unsigned short) 070707)))
+ {
+ /* Having to skip 1 byte because of word alignment is normal. */
+ if (bytes_skipped > 0)
+ error (0, 0, _("warning: skipped %ld bytes of junk"), bytes_skipped);
+ read_in_binary (file_hdr, in_des);
+ break;
+ }
+ bytes_skipped++;
+ bcopy ((char *) file_hdr + 1, (char *) file_hdr, 5);
+ tape_buffered_read ((char *) file_hdr + 5, in_des, 1L);
+ }
+}
+
+/* Fill in FILE_HDR by reading an old-format ASCII format cpio header from
+ file descriptor IN_DES, except for the magic number, which is
+ already filled in. */
+
+void
+read_in_old_ascii (file_hdr, in_des)
+ struct new_cpio_header *file_hdr;
+ int in_des;
+{
+ char ascii_header[78];
+ unsigned long dev;
+ unsigned long rdev;
+
+ tape_buffered_read (ascii_header, in_des, 70L);
+ ascii_header[70] = '\0';
+ sscanf (ascii_header,
+ "%6lo%6lo%6lo%6lo%6lo%6lo%6lo%11lo%6lo%11lo",
+ &dev, &file_hdr->c_ino,
+ &file_hdr->c_mode, &file_hdr->c_uid, &file_hdr->c_gid,
+ &file_hdr->c_nlink, &rdev, &file_hdr->c_mtime,
+ &file_hdr->c_namesize, &file_hdr->c_filesize);
+ file_hdr->c_dev_maj = major (dev);
+ file_hdr->c_dev_min = minor (dev);
+ file_hdr->c_rdev_maj = major (rdev);
+ file_hdr->c_rdev_min = minor (rdev);
+
+ /* Read file name from input. */
+ if (file_hdr->c_name != NULL)
+ free (file_hdr->c_name);
+ file_hdr->c_name = (char *) xmalloc (file_hdr->c_namesize + 1);
+ tape_buffered_read (file_hdr->c_name, in_des, (long) file_hdr->c_namesize);
+#ifndef __MSDOS__
+ /* HP/UX cpio creates archives that look just like ordinary archives,
+ but for devices it sets major = 0, minor = 1, and puts the
+ actual major/minor number in the filesize field. See if this
+ is an HP/UX cpio archive, and if so fix it. We have to do this
+ here because process_copy_in() assumes filesize is always 0
+ for devices. */
+ switch (file_hdr->c_mode & CP_IFMT)
+ {
+ case CP_IFCHR:
+ case CP_IFBLK:
+#ifdef CP_IFSOCK
+ case CP_IFSOCK:
+#endif
+#ifdef CP_IFIFO
+ case CP_IFIFO:
+#endif
+ if (file_hdr->c_filesize != 0
+ && file_hdr->c_rdev_maj == 0
+ && file_hdr->c_rdev_min == 1)
+ {
+ file_hdr->c_rdev_maj = major (file_hdr->c_filesize);
+ file_hdr->c_rdev_min = minor (file_hdr->c_filesize);
+ file_hdr->c_filesize = 0;
+ }
+ break;
+ default:
+ break;
+ }
+#endif /* __MSDOS__ */
+}
+
+/* Fill in FILE_HDR by reading a new-format ASCII format cpio header from
+ file descriptor IN_DES, except for the magic number, which is
+ already filled in. */
+
+void
+read_in_new_ascii (file_hdr, in_des)
+ struct new_cpio_header *file_hdr;
+ int in_des;
+{
+ char ascii_header[112];
+
+ tape_buffered_read (ascii_header, in_des, 104L);
+ ascii_header[104] = '\0';
+ sscanf (ascii_header,
+ "%8lx%8lx%8lx%8lx%8lx%8lx%8lx%8lx%8lx%8lx%8lx%8lx%8lx",
+ &file_hdr->c_ino, &file_hdr->c_mode, &file_hdr->c_uid,
+ &file_hdr->c_gid, &file_hdr->c_nlink, &file_hdr->c_mtime,
+ &file_hdr->c_filesize, &file_hdr->c_dev_maj, &file_hdr->c_dev_min,
+ &file_hdr->c_rdev_maj, &file_hdr->c_rdev_min, &file_hdr->c_namesize,
+ &file_hdr->c_chksum);
+ /* Read file name from input. */
+ if (file_hdr->c_name != NULL)
+ free (file_hdr->c_name);
+ file_hdr->c_name = (char *) xmalloc (file_hdr->c_namesize);
+ tape_buffered_read (file_hdr->c_name, in_des, (long) file_hdr->c_namesize);
+
+ /* In SVR4 ASCII format, the amount of space allocated for the header
+ is rounded up to the next long-word, so we might need to drop
+ 1-3 bytes. */
+ tape_skip_padding (in_des, file_hdr->c_namesize + 110);
+}
+
+/* Fill in FILE_HDR by reading a binary format cpio header from
+ file descriptor IN_DES, except for the first 6 bytes (the magic
+ number, device, and inode number), which are already filled in. */
+
+void
+read_in_binary (file_hdr, in_des)
+ struct new_cpio_header *file_hdr;
+ int in_des;
+{
+ struct old_cpio_header short_hdr;
+
+ /* Copy the data into the short header, then later transfer
+ it into the argument long header. */
+ short_hdr.c_dev = ((struct old_cpio_header *) file_hdr)->c_dev;
+ short_hdr.c_ino = ((struct old_cpio_header *) file_hdr)->c_ino;
+ tape_buffered_read (((char *) &short_hdr) + 6, in_des, 20L);
+
+ /* If the magic number is byte swapped, fix the header. */
+ if (file_hdr->c_magic == swab_short ((unsigned short) 070707))
+ {
+ static int warned = 0;
+
+ /* Alert the user that they might have to do byte swapping on
+ the file contents. */
+ if (warned == 0)
+ {
+ error (0, 0, _("warning: archive header has reverse byte-order"));
+ warned = 1;
+ }
+ swab_array ((char *) &short_hdr, 13);
+ }
+
+ file_hdr->c_dev_maj = major (short_hdr.c_dev);
+ file_hdr->c_dev_min = minor (short_hdr.c_dev);
+ file_hdr->c_ino = short_hdr.c_ino;
+ file_hdr->c_mode = short_hdr.c_mode;
+ file_hdr->c_uid = short_hdr.c_uid;
+ file_hdr->c_gid = short_hdr.c_gid;
+ file_hdr->c_nlink = short_hdr.c_nlink;
+ file_hdr->c_rdev_maj = major (short_hdr.c_rdev);
+ file_hdr->c_rdev_min = minor (short_hdr.c_rdev);
+ file_hdr->c_mtime = (unsigned long) short_hdr.c_mtimes[0] << 16
+ | short_hdr.c_mtimes[1];
+
+ file_hdr->c_namesize = short_hdr.c_namesize;
+ file_hdr->c_filesize = (unsigned long) short_hdr.c_filesizes[0] << 16
+ | short_hdr.c_filesizes[1];
+
+ /* Read file name from input. */
+ if (file_hdr->c_name != NULL)
+ free (file_hdr->c_name);
+ file_hdr->c_name = (char *) xmalloc (file_hdr->c_namesize);
+ tape_buffered_read (file_hdr->c_name, in_des, (long) file_hdr->c_namesize);
+
+ /* In binary mode, the amount of space allocated in the header for
+ the filename is `c_namesize' rounded up to the next short-word,
+ so we might need to drop a byte. */
+ if (file_hdr->c_namesize % 2)
+ tape_toss_input (in_des, 1L);
+
+#ifndef __MSDOS__
+ /* HP/UX cpio creates archives that look just like ordinary archives,
+ but for devices it sets major = 0, minor = 1, and puts the
+ actual major/minor number in the filesize field. See if this
+ is an HP/UX cpio archive, and if so fix it. We have to do this
+ here because process_copy_in() assumes filesize is always 0
+ for devices. */
+ switch (file_hdr->c_mode & CP_IFMT)
+ {
+ case CP_IFCHR:
+ case CP_IFBLK:
+#ifdef CP_IFSOCK
+ case CP_IFSOCK:
+#endif
+#ifdef CP_IFIFO
+ case CP_IFIFO:
+#endif
+ if (file_hdr->c_filesize != 0
+ && file_hdr->c_rdev_maj == 0
+ && file_hdr->c_rdev_min == 1)
+ {
+ file_hdr->c_rdev_maj = major (file_hdr->c_filesize);
+ file_hdr->c_rdev_min = minor (file_hdr->c_filesize);
+ file_hdr->c_filesize = 0;
+ }
+ break;
+ default:
+ break;
+ }
+#endif /* __MSDOS__ */
+}
+
+/* Exchange the bytes of each element of the array of COUNT shorts
+ starting at PTR. */
+
+void
+swab_array (ptr, count)
+ char *ptr;
+ int count;
+{
+ char tmp;
+
+ while (count-- > 0)
+ {
+ tmp = *ptr;
+ *ptr = *(ptr + 1);
+ ++ptr;
+ *ptr = tmp;
+ ++ptr;
+ }
+}
+
+/* Current time for verbose table. */
+static time_t current_time;
+
+/* Read the collection from standard input and create files
+ in the file system. */
+
+void
+process_copy_in ()
+{
+ char done = FALSE; /* True if trailer reached. */
+ FILE *tty_in; /* Interactive file for rename option. */
+ FILE *tty_out; /* Interactive file for rename option. */
+ FILE *rename_in; /* Batch file for rename option. */
+ struct stat file_stat; /* Output file stat record. */
+ struct new_cpio_header file_hdr; /* Output header information. */
+ int in_file_des; /* Input file descriptor. */
+ char skip_file; /* Flag for use with patterns. */
+ int i; /* Loop index variable. */
+
+ /* Initialize the copy in. */
+ if (pattern_file_name)
+ {
+ read_pattern_file ();
+ }
+ file_hdr.c_name = NULL;
+
+ if (rename_batch_file)
+ {
+ rename_in = fopen (rename_batch_file, "r");
+ if (rename_in == NULL)
+ {
+ error (2, errno, CONSOLE);
+ }
+ }
+ else if (rename_flag)
+ {
+ /* Open interactive file pair for rename operation. */
+ tty_in = fopen (CONSOLE, "r");
+ if (tty_in == NULL)
+ {
+ error (2, errno, CONSOLE);
+ }
+ tty_out = fopen (CONSOLE, "w");
+ if (tty_out == NULL)
+ {
+ error (2, errno, CONSOLE);
+ }
+ }
+
+ /* Get date and time if needed for processing the table option. */
+ if (table_flag && verbose_flag)
+ {
+ time (&current_time);
+ }
+
+#ifdef __MSDOS__
+ setmode (archive_des, O_BINARY);
+#endif
+ /* Check whether the input file might be a tape. */
+ in_file_des = archive_des;
+ if (_isrmt (in_file_des))
+ {
+ input_is_special = 1;
+ input_is_seekable = 0;
+ }
+ else
+ {
+ if (fstat (in_file_des, &file_stat))
+ error (1, errno, _("standard input is closed"));
+ input_is_special =
+#ifdef S_ISBLK
+ S_ISBLK (file_stat.st_mode) ||
+#endif
+ S_ISCHR (file_stat.st_mode);
+ input_is_seekable = S_ISREG (file_stat.st_mode);
+ }
+ output_is_seekable = TRUE;
+
+ /* While there is more input in the collection, process the input. */
+ while (!done)
+ {
+ swapping_halfwords = swapping_bytes = FALSE;
+
+ /* Start processing the next file by reading the header. */
+ read_in_header (&file_hdr, in_file_des);
+
+#ifdef DEBUG_CPIO
+ if (debug_flag)
+ {
+ struct new_cpio_header *h;
+ h = &file_hdr;
+ fprintf (stderr,
+ "magic = 0%o, ino = %d, mode = 0%o, uid = %d, gid = %d\n",
+ h->c_magic, h->c_ino, h->c_mode, h->c_uid, h->c_gid);
+ fprintf (stderr,
+ "nlink = %d, mtime = %d, filesize = %d, dev_maj = 0x%x\n",
+ h->c_nlink, h->c_mtime, h->c_filesize, h->c_dev_maj);
+ fprintf (stderr,
+ "dev_min = 0x%x, rdev_maj = 0x%x, rdev_min = 0x%x, namesize = %d\n",
+ h->c_dev_min, h->c_rdev_maj, h->c_rdev_min, h->c_namesize);
+ fprintf (stderr,
+ "chksum = %d, name = \"%s\", tar_linkname = \"%s\"\n",
+ h->c_chksum, h->c_name,
+ h->c_tar_linkname ? h->c_tar_linkname : "(null)" );
+
+ }
+#endif
+ /* Is this the header for the TRAILER file? */
+ if (strcmp ("TRAILER!!!", file_hdr.c_name) == 0)
+ {
+ done = TRUE;
+ break;
+ }
+
+ /* Do we have to ignore absolute paths, and if so, does the filename
+ have an absolute path? */
+ if (no_abs_paths_flag && file_hdr.c_name && file_hdr.c_name [0] == '/')
+ {
+ char *p;
+
+ p = file_hdr.c_name;
+ while (*p == '/')
+ ++p;
+ if (*p == '\0')
+ {
+ strcpy (file_hdr.c_name, ".");
+ }
+ else
+ {
+ /* Debian hack: file_hrd.c_name is sometimes set to
+ point to static memory by code in tar.c. This
+ causes a segfault. Therefore, memmove is used
+ instead of freeing and reallocating. (Reported by
+ Horst Knobloch.) This bug has been reported to
+ "bug-gnu-utils@prep.ai.mit.edu". (99/1/6) -BEM */
+ (void)memmove (file_hdr.c_name, p, (size_t)(strlen (p) + 1));
+ }
+ }
+
+ /* Does the file name match one of the given patterns? */
+ if (num_patterns <= 0)
+ skip_file = FALSE;
+ else
+ {
+ skip_file = copy_matching_files;
+ for (i = 0; i < num_patterns
+ && skip_file == copy_matching_files; i++)
+ {
+ if (fnmatch (save_patterns[i], file_hdr.c_name, 0) == 0)
+ skip_file = !copy_matching_files;
+ }
+ }
+
+ if (skip_file)
+ {
+ /* If we're skipping a file with links, there might be other
+ links that we didn't skip, and this file might have the
+ data for the links. If it does, we'll copy in the data
+ to the links, but not to this file. */
+ if (file_hdr.c_nlink > 1 && (archive_format == arf_newascii
+ || archive_format == arf_crcascii) )
+ {
+ if (create_defered_links_to_skipped(&file_hdr, in_file_des) < 0)
+ {
+ tape_toss_input (in_file_des, file_hdr.c_filesize);
+ tape_skip_padding (in_file_des, file_hdr.c_filesize);
+ }
+ }
+ else
+ {
+ tape_toss_input (in_file_des, file_hdr.c_filesize);
+ tape_skip_padding (in_file_des, file_hdr.c_filesize);
+ }
+ }
+ else if (table_flag)
+ {
+ list_file(&file_hdr, in_file_des);
+ }
+ else if (append_flag)
+ {
+ tape_toss_input (in_file_des, file_hdr.c_filesize);
+ tape_skip_padding (in_file_des, file_hdr.c_filesize);
+ }
+ else if (only_verify_crc_flag)
+ {
+#ifdef CP_IFLNK
+ if ((file_hdr.c_mode & CP_IFMT) == CP_IFLNK)
+ {
+ if (archive_format != arf_tar && archive_format != arf_ustar)
+ {
+ tape_toss_input (in_file_des, file_hdr.c_filesize);
+ tape_skip_padding (in_file_des, file_hdr.c_filesize);
+ continue;
+ }
+ }
+#endif
+ crc = 0;
+ tape_toss_input (in_file_des, file_hdr.c_filesize);
+ tape_skip_padding (in_file_des, file_hdr.c_filesize);
+ if (crc != file_hdr.c_chksum)
+ {
+ error (0, 0, _("%s: checksum error (0x%x, should be 0x%x)"),
+ file_hdr.c_name, crc, file_hdr.c_chksum);
+ }
+ /* Debian hack: -v and -V now work with --only-verify-crc.
+ (99/11/10) -BEM */
+ if (verbose_flag)
+ {
+ fprintf (stderr, "%s\n", file_hdr.c_name);
+ }
+ if (dot_flag)
+ {
+ fputc ('.', stderr);
+ }
+ }
+ else
+ {
+ /* Copy the input file into the directory structure. */
+
+ /* Do we need to rename the file? */
+ if (rename_flag || rename_batch_file)
+ {
+ if (query_rename(&file_hdr, tty_in, tty_out, rename_in) < 0)
+ {
+ tape_toss_input (in_file_des, file_hdr.c_filesize);
+ tape_skip_padding (in_file_des, file_hdr.c_filesize);
+ continue;
+ }
+ }
+
+ copyin_file(&file_hdr, in_file_des);
+
+ if (verbose_flag)
+ fprintf (stderr, "%s\n", file_hdr.c_name);
+ if (dot_flag)
+ fputc ('.', stderr);
+ }
+ }
+
+ if (dot_flag)
+ fputc ('\n', stderr);
+
+ if (append_flag)
+ return;
+
+ if (archive_format == arf_newascii || archive_format == arf_crcascii)
+ {
+ create_final_defers ();
+ }
+ if (!quiet_flag)
+ {
+ int blocks;
+ blocks = (input_bytes + io_block_size - 1) / io_block_size;
+ fprintf (stderr, ngettext ("%d block\n", "%d blocks\n", blocks), blocks);
+ }
+}
+
+static int
+query_rename(file_hdr, tty_in, tty_out, rename_in)
+ struct new_cpio_header* file_hdr;
+ FILE *tty_in;
+ FILE *tty_out;
+ FILE *rename_in;
+{
+ char *str_res; /* Result for string function. */
+ static dynamic_string new_name; /* New file name for rename option. */
+ static int initialized_new_name = FALSE;
+ if (!initialized_new_name)
+ {
+ ds_init (&new_name, 128);
+ initialized_new_name = TRUE;
+ }
+
+ if (rename_flag)
+ {
+ fprintf (tty_out, _("rename %s -> "), file_hdr->c_name);
+ fflush (tty_out);
+ str_res = ds_fgets (tty_in, &new_name);
+ }
+ else
+ {
+ str_res = ds_fgetstr (rename_in, &new_name, '\n');
+ }
+ if (str_res == NULL || str_res[0] == 0)
+ {
+ return -1;
+ }
+ else
+ /* Debian hack: file_hrd.c_name is sometimes set to
+ point to static memory by code in tar.c. This
+ causes a segfault. This has been fixed and an
+ additional check to ensure that the file name
+ is not too long has been added. (Reported by
+ Horst Knobloch.) This bug has been reported to
+ "bug-gnu-utils@prep.ai.mit.edu". (99/1/6) -BEM */
+ {
+ if (archive_format != arf_tar && archive_format != arf_ustar)
+ {
+ free (file_hdr->c_name);
+ file_hdr->c_name = xstrdup (new_name.ds_string);
+ }
+ else
+ {
+ if (is_tar_filename_too_long (new_name.ds_string))
+ error (0, 0, _("%s: file name too long"),
+ new_name.ds_string);
+ else
+ strcpy (file_hdr->c_name, new_name.ds_string);
+ }
+ }
+ return 0;
+}
+
+static void
+list_file(file_hdr, in_file_des)
+ struct new_cpio_header* file_hdr;
+ int in_file_des;
+{
+ if (verbose_flag)
+ {
+#ifdef CP_IFLNK
+ if ((file_hdr->c_mode & CP_IFMT) == CP_IFLNK)
+ {
+ if (archive_format != arf_tar && archive_format != arf_ustar)
+ {
+ char *link_name = NULL; /* Name of hard and symbolic links. */
+
+ link_name = (char *) xmalloc ((unsigned int) file_hdr->c_filesize + 1);
+ link_name[file_hdr->c_filesize] = '\0';
+ tape_buffered_read (link_name, in_file_des, file_hdr->c_filesize);
+ long_format (file_hdr, link_name);
+ free (link_name);
+ tape_skip_padding (in_file_des, file_hdr->c_filesize);
+ return;
+ }
+ else
+ {
+ long_format (file_hdr, file_hdr->c_tar_linkname);
+ return;
+ }
+ }
+ else
+#endif
+ long_format (file_hdr, (char *) 0);
+ }
+ else
+ {
+ /* Debian hack: Modified to print a list of filenames
+ terminiated by a null character when the -t and -0
+ flags are used. This has been submitted as a
+ suggestion to "bug-gnu-utils@prep.ai.mit.edu". -BEM */
+ printf ("%s%c", file_hdr->c_name, name_end);
+ }
+
+ crc = 0;
+ tape_toss_input (in_file_des, file_hdr->c_filesize);
+ tape_skip_padding (in_file_des, file_hdr->c_filesize);
+ if (only_verify_crc_flag)
+ {
+#ifdef CP_IFLNK
+ if ((file_hdr->c_mode & CP_IFMT) == CP_IFLNK)
+ {
+ return; /* links don't have a checksum */
+ }
+#endif
+ if (crc != file_hdr->c_chksum)
+ {
+ error (0, 0, _("%s: checksum error (0x%x, should be 0x%x)"),
+ file_hdr->c_name, crc, file_hdr->c_chksum);
+ }
+ }
+}
+
+static void
+copyin_file(file_hdr, in_file_des)
+ struct new_cpio_header* file_hdr;
+ int in_file_des;
+{
+ int existing_dir;
+
+ if (try_existing_file(file_hdr, in_file_des, &existing_dir) < 0)
+ {
+ return;
+ }
+
+ /* Do the real copy or link. */
+ switch (file_hdr->c_mode & CP_IFMT)
+ {
+ case CP_IFREG:
+ copyin_regular_file(file_hdr, in_file_des);
+ break;
+
+ case CP_IFDIR:
+ copyin_directory(file_hdr, existing_dir);
+ break;
+
+#ifndef __MSDOS__
+ case CP_IFCHR:
+ case CP_IFBLK:
+#ifdef CP_IFSOCK
+ case CP_IFSOCK:
+#endif
+#ifdef CP_IFIFO
+ case CP_IFIFO:
+#endif
+ copyin_device(file_hdr);
+ break;
+#endif
+
+#ifdef CP_IFLNK
+ case CP_IFLNK:
+ copyin_link(file_hdr, in_file_des);
+ break;
+#endif
+
+ default:
+ error (0, 0, _("%s: unknown file type"), file_hdr->c_name);
+ tape_toss_input (in_file_des, file_hdr->c_filesize);
+ tape_skip_padding (in_file_des, file_hdr->c_filesize);
+ }
+}
+
+static int
+try_existing_file(file_hdr, in_file_des, existing_dir)
+ struct new_cpio_header* file_hdr;
+ int in_file_des;
+ int* existing_dir;
+{
+ struct stat file_stat;
+
+ *existing_dir = FALSE;
+ if (lstat (file_hdr->c_name, &file_stat) == 0)
+ {
+ if (S_ISDIR (file_stat.st_mode)
+ && ((file_hdr->c_mode & CP_IFMT) == CP_IFDIR))
+ {
+ /* If there is already a directory there that
+ we are trying to create, don't complain about
+ it. */
+ *existing_dir = TRUE;
+ return 0;
+ }
+ else if (!unconditional_flag
+ && file_hdr->c_mtime <= file_stat.st_mtime)
+ {
+ error (0, 0, _("%s not created: newer or same age version exists"),
+ file_hdr->c_name);
+ tape_toss_input (in_file_des, file_hdr->c_filesize);
+ tape_skip_padding (in_file_des, file_hdr->c_filesize);
+ return -1; /* Go to the next file. */
+ }
+ else if (S_ISDIR (file_stat.st_mode)
+ ? rmdir (file_hdr->c_name)
+ : unlink (file_hdr->c_name))
+ {
+ error (0, errno, _("cannot remove current %s"),
+ file_hdr->c_name);
+ tape_toss_input (in_file_des, file_hdr->c_filesize);
+ tape_skip_padding (in_file_des, file_hdr->c_filesize);
+ return -1; /* Go to the next file. */
+ }
+ }
+ return 0;
+}
+
+static void
+copyin_regular_file(file_hdr, in_file_des)
+ struct new_cpio_header* file_hdr;
+ int in_file_des;
+{
+ int out_file_des; /* Output file descriptor. */
+
+#ifndef __MSDOS__
+ /* Can the current file be linked to a previously copied file? */
+ if (file_hdr->c_nlink > 1 && (archive_format == arf_newascii
+ || archive_format == arf_crcascii) )
+ {
+ int link_res;
+ if (file_hdr->c_filesize == 0)
+ {
+ /* The newc and crc formats store multiply linked copies
+ of the same file in the archive only once. The
+ actual data is attached to the last link in the
+ archive, and the other links all have a filesize
+ of 0. Since this file has multiple links and a
+ filesize of 0, its data is probably attatched to
+ another file in the archive. Save the link, and
+ process it later when we get the actual data. We
+ can't just create it with length 0 and add the
+ data later, in case the file is readonly. We still
+ lose if its parent directory is readonly (and we aren't
+ running as root), but there's nothing we can do about
+ that. */
+ defer_copyin (file_hdr);
+ tape_toss_input (in_file_des, file_hdr->c_filesize);
+ tape_skip_padding (in_file_des, file_hdr->c_filesize);
+ return;
+ }
+ /* If the file has data (filesize != 0), then presumably
+ any other links have already been defer_copyin'ed(),
+ but GNU cpio version 2.0-2.2 didn't do that, so we
+ still have to check for links here (and also in case
+ the archive was created and later appeneded to). */
+ /* Debian hack: (97/1/2) This was reported by Ronald
+ F. Guilmette to the upstream maintainers. -BEM */
+ link_res = link_to_maj_min_ino (file_hdr->c_name,
+ file_hdr->c_dev_maj, file_hdr->c_dev_min,
+ file_hdr->c_ino);
+ if (link_res == 0)
+ {
+ tape_toss_input (in_file_des, file_hdr->c_filesize);
+ tape_skip_padding (in_file_des, file_hdr->c_filesize);
+ return;
+ }
+ }
+ else if (file_hdr->c_nlink > 1 && archive_format != arf_tar
+ && archive_format != arf_ustar)
+ {
+ int link_res;
+ /* Debian hack: (97/1/2) This was reported by Ronald
+ F. Guilmette to the upstream maintainers. -BEM */
+ link_res = link_to_maj_min_ino (file_hdr->c_name,
+ file_hdr->c_dev_maj, file_hdr->c_dev_min,
+ file_hdr->c_ino);
+ if (link_res == 0)
+ {
+ tape_toss_input (in_file_des, file_hdr->c_filesize);
+ tape_skip_padding (in_file_des, file_hdr->c_filesize);
+ return;
+ }
+ }
+ else if ((archive_format == arf_tar || archive_format == arf_ustar)
+ && file_hdr->c_tar_linkname &&
+ file_hdr->c_tar_linkname[0] != '\0')
+ {
+ int link_res;
+ link_res = link_to_name (file_hdr->c_name,
+ file_hdr->c_tar_linkname);
+ if (link_res < 0)
+ {
+ error (0, errno, _("cannot link %s to %s"),
+ file_hdr->c_tar_linkname, file_hdr->c_name);
+ }
+ return;
+ }
+#endif
+
+ /* If not linked, copy the contents of the file. */
+ out_file_des = open (file_hdr->c_name,
+ O_CREAT | O_WRONLY | O_BINARY, 0600);
+ if (out_file_des < 0 && create_dir_flag)
+ {
+ create_all_directories (file_hdr->c_name);
+ out_file_des = open (file_hdr->c_name,
+ O_CREAT | O_WRONLY | O_BINARY,
+ 0600);
+ }
+ if (out_file_des < 0)
+ {
+ error (0, errno, "%s", file_hdr->c_name);
+ tape_toss_input (in_file_des, file_hdr->c_filesize);
+ tape_skip_padding (in_file_des, file_hdr->c_filesize);
+ return;
+ }
+
+ crc = 0;
+ if (swap_halfwords_flag)
+ {
+ if ((file_hdr->c_filesize % 4) == 0)
+ swapping_halfwords = TRUE;
+ else
+ error (0, 0, _("cannot swap halfwords of %s: odd number of halfwords"),
+ file_hdr->c_name);
+ }
+ if (swap_bytes_flag)
+ {
+ if ((file_hdr->c_filesize % 2) == 0)
+ swapping_bytes = TRUE;
+ else
+ error (0, 0, _("cannot swap bytes of %s: odd number of bytes"),
+ file_hdr->c_name);
+ }
+ copy_files_tape_to_disk (in_file_des, out_file_des, file_hdr->c_filesize);
+ disk_empty_output_buffer (out_file_des);
+ /* Debian hack to fix a bug in the --sparse option.
+ This bug has been reported to
+ "bug-gnu-utils@prep.ai.mit.edu". (96/7/10) -BEM */
+ if (delayed_seek_count > 0)
+ {
+ lseek (out_file_des, delayed_seek_count-1, SEEK_CUR);
+ write (out_file_des, "", 1);
+ delayed_seek_count = 0;
+ }
+ if (close (out_file_des) < 0)
+ error (0, errno, "%s", file_hdr->c_name);
+
+ if (archive_format == arf_crcascii)
+ {
+ if (crc != file_hdr->c_chksum)
+ error (0, 0, _("%s: checksum error (0x%x, should be 0x%x)"),
+ file_hdr->c_name, crc, file_hdr->c_chksum);
+ }
+ /* File is now copied; set attributes. */
+ if (!no_chown_flag)
+ if ((chown (file_hdr->c_name,
+ set_owner_flag ? set_owner : file_hdr->c_uid,
+ set_group_flag ? set_group : file_hdr->c_gid) < 0)
+ && errno != EPERM)
+ error (0, errno, "%s", file_hdr->c_name);
+ /* chown may have turned off some permissions we wanted. */
+ if (chmod (file_hdr->c_name, (int) file_hdr->c_mode) < 0)
+ error (0, errno, "%s", file_hdr->c_name);
+ if (retain_time_flag)
+ {
+ struct utimbuf times; /* For setting file times. */
+ /* Initialize this in case it has members we don't know to set. */
+ bzero (&times, sizeof (struct utimbuf));
+
+ times.actime = times.modtime = file_hdr->c_mtime;
+ if (utime (file_hdr->c_name, &times) < 0)
+ error (0, errno, "%s", file_hdr->c_name);
+ }
+ tape_skip_padding (in_file_des, file_hdr->c_filesize);
+ if (file_hdr->c_nlink > 1 && (archive_format == arf_newascii
+ || archive_format == arf_crcascii) )
+ {
+ /* (see comment above for how the newc and crc formats
+ store multiple links). Now that we have the data
+ for this file, create any other links to it which
+ we defered. */
+ create_defered_links (file_hdr);
+ }
+}
+
+static void
+copyin_directory(file_hdr, existing_dir)
+ struct new_cpio_header* file_hdr;
+ int existing_dir;
+{
+ int res; /* Result of various function calls. */
+#ifdef HPUX_CDF
+ int cdf_flag; /* True if file is a CDF. */
+ int cdf_char; /* Index of `+' char indicating a CDF. */
+#endif
+
+ /* Strip any trailing `/'s off the filename; tar puts
+ them on. We might as well do it here in case anybody
+ else does too, since they cause strange things to happen. */
+ strip_trailing_slashes (file_hdr->c_name);
+
+ /* Ignore the current directory. It must already exist,
+ and we don't want to change its permission, ownership
+ or time. */
+ if (file_hdr->c_name[0] == '.' && file_hdr->c_name[1] == '\0')
+ {
+ return;
+ }
+
+#ifdef HPUX_CDF
+ cdf_flag = 0;
+#endif
+ if (!existing_dir)
+
+ {
+#ifdef HPUX_CDF
+ /* If the directory name ends in a + and is SUID,
+ then it is a CDF. Strip the trailing + from
+ the name before creating it. */
+ cdf_char = strlen (file_hdr->c_name) - 1;
+ if ( (cdf_char > 0) &&
+ (file_hdr->c_mode & 04000) &&
+ (file_hdr->c_name [cdf_char] == '+') )
+ {
+ file_hdr->c_name [cdf_char] = '\0';
+ cdf_flag = 1;
+ }
+#endif
+ res = mkdir (file_hdr->c_name, file_hdr->c_mode);
+ }
+ else
+ res = 0;
+ if (res < 0 && create_dir_flag)
+ {
+ create_all_directories (file_hdr->c_name);
+ res = mkdir (file_hdr->c_name, file_hdr->c_mode);
+ }
+ if (res < 0)
+ {
+ /* In some odd cases where the file_hdr->c_name includes `.',
+ the directory may have actually been created by
+ create_all_directories(), so the mkdir will fail
+ because the directory exists. If that's the case,
+ don't complain about it. */
+ struct stat file_stat;
+ if ( (errno != EEXIST) ||
+ (lstat (file_hdr->c_name, &file_stat) != 0) ||
+ !(S_ISDIR (file_stat.st_mode) ) )
+ {
+ error (0, errno, "%s", file_hdr->c_name);
+ return;
+ }
+ }
+ if (!no_chown_flag)
+ if ((chown (file_hdr->c_name,
+ set_owner_flag ? set_owner : file_hdr->c_uid,
+ set_group_flag ? set_group : file_hdr->c_gid) < 0)
+ && errno != EPERM)
+ error (0, errno, "%s", file_hdr->c_name);
+ /* chown may have turned off some permissions we wanted. */
+ if (chmod (file_hdr->c_name, (int) file_hdr->c_mode) < 0)
+ error (0, errno, "%s", file_hdr->c_name);
+#ifdef HPUX_CDF
+ if (cdf_flag)
+ /* Once we "hide" the directory with the chmod(),
+ we have to refer to it using name+ instead of name. */
+ file_hdr->c_name [cdf_char] = '+';
+#endif
+ if (retain_time_flag)
+ {
+ struct utimbuf times; /* For setting file times. */
+ /* Initialize this in case it has members we don't know to set. */
+ bzero (&times, sizeof (struct utimbuf));
+
+ times.actime = times.modtime = file_hdr->c_mtime;
+ if (utime (file_hdr->c_name, &times) < 0)
+ error (0, errno, "%s", file_hdr->c_name);
+ }
+}
+
+static void
+copyin_device(file_hdr)
+ struct new_cpio_header* file_hdr;
+{
+ int res; /* Result of various function calls. */
+
+ if (file_hdr->c_nlink > 1 && archive_format != arf_tar
+ && archive_format != arf_ustar)
+ {
+ int link_res;
+ /* Debian hack: This was reported by Horst
+ Knobloch. This bug has been reported to
+ "bug-gnu-utils@prep.ai.mit.edu". (99/1/6) -BEM */
+ link_res = link_to_maj_min_ino (file_hdr->c_name,
+ file_hdr->c_dev_maj, file_hdr->c_dev_min,
+ file_hdr->c_ino);
+ if (link_res == 0)
+ {
+ return;
+ }
+ }
+ else if (archive_format == arf_ustar &&
+ file_hdr->c_tar_linkname &&
+ file_hdr->c_tar_linkname [0] != '\0')
+ {
+ int link_res;
+ link_res = link_to_name (file_hdr->c_name,
+ file_hdr->c_tar_linkname);
+ if (link_res < 0)
+ {
+ error (0, errno, _("cannot link %s to %s"),
+ file_hdr->c_tar_linkname, file_hdr->c_name);
+ /* Something must be wrong, because we couldn't
+ find the file to link to. But can we assume
+ that the device maj/min numbers are correct
+ and fall through to the mknod? It's probably
+ safer to just return, rather than possibly
+ creating a bogus device file. */
+ }
+ return;
+ }
+
+ res = mknod (file_hdr->c_name, file_hdr->c_mode,
+ makedev (file_hdr->c_rdev_maj, file_hdr->c_rdev_min));
+ if (res < 0 && create_dir_flag)
+ {
+ create_all_directories (file_hdr->c_name);
+ res = mknod (file_hdr->c_name, file_hdr->c_mode,
+ makedev (file_hdr->c_rdev_maj, file_hdr->c_rdev_min));
+ }
+ if (res < 0)
+ {
+ error (0, errno, "%s", file_hdr->c_name);
+ return;
+ }
+ if (!no_chown_flag)
+ if ((chown (file_hdr->c_name,
+ set_owner_flag ? set_owner : file_hdr->c_uid,
+ set_group_flag ? set_group : file_hdr->c_gid) < 0)
+ && errno != EPERM)
+ error (0, errno, "%s", file_hdr->c_name);
+ /* chown may have turned off some permissions we wanted. */
+ if (chmod (file_hdr->c_name, file_hdr->c_mode) < 0)
+ error (0, errno, "%s", file_hdr->c_name);
+ if (retain_time_flag)
+ {
+ struct utimbuf times; /* For setting file times. */
+ /* Initialize this in case it has members we don't know to set. */
+ bzero (&times, sizeof (struct utimbuf));
+
+ times.actime = times.modtime = file_hdr->c_mtime;
+ if (utime (file_hdr->c_name, &times) < 0)
+ error (0, errno, "%s", file_hdr->c_name);
+ }
+}
+
+static void
+copyin_link(file_hdr, in_file_des)
+ struct new_cpio_header* file_hdr;
+ int in_file_des;
+{
+ char *link_name = NULL; /* Name of hard and symbolic links. */
+ int res; /* Result of various function calls. */
+
+ if (archive_format != arf_tar && archive_format != arf_ustar)
+ {
+ link_name = (char *) xmalloc ((unsigned int) file_hdr->c_filesize + 1);
+ link_name[file_hdr->c_filesize] = '\0';
+ tape_buffered_read (link_name, in_file_des, file_hdr->c_filesize);
+ tape_skip_padding (in_file_des, file_hdr->c_filesize);
+ }
+ else
+ {
+ link_name = xstrdup (file_hdr->c_tar_linkname);
+ }
+
+ res = UMASKED_SYMLINK (link_name, file_hdr->c_name,
+ file_hdr->c_mode);
+ if (res < 0 && create_dir_flag)
+ {
+ create_all_directories (file_hdr->c_name);
+ res = UMASKED_SYMLINK (link_name, file_hdr->c_name,
+ file_hdr->c_mode);
+ }
+ if (res < 0)
+ {
+ error (0, errno, "%s", file_hdr->c_name);
+ free (link_name);
+ return;
+ }
+ if (!no_chown_flag)
+ if ((lchown (file_hdr->c_name,
+ set_owner_flag ? set_owner : file_hdr->c_uid,
+ set_group_flag ? set_group : file_hdr->c_gid) < 0)
+ && errno != EPERM)
+ {
+ error (0, errno, "%s", file_hdr->c_name);
+ }
+ free (link_name);
+}
+
+/* Print the file described by FILE_HDR in long format.
+ If LINK_NAME is nonzero, it is the name of the file that
+ this file is a symbolic link to. */
+
+void
+long_format (file_hdr, link_name)
+ struct new_cpio_header *file_hdr;
+ char *link_name;
+{
+ char mbuf[11];
+ char tbuf[40];
+ time_t when;
+
+ mode_string (file_hdr->c_mode, mbuf);
+ mbuf[10] = '\0';
+
+ /* Get time values ready to print. */
+ when = file_hdr->c_mtime;
+ strcpy (tbuf, ctime (&when));
+ if (current_time - when > 6L * 30L * 24L * 60L * 60L
+ || current_time - when < 0L)
+ {
+ /* The file is older than 6 months, or in the future.
+ Show the year instead of the time of day. */
+ strcpy (tbuf + 11, tbuf + 19);
+ }
+ tbuf[16] = '\0';
+
+ printf ("%s %3u ", mbuf, file_hdr->c_nlink);
+
+#ifndef __MSDOS__
+ if (numeric_uid)
+#endif
+ printf ("%-8u %-8u ", (unsigned int) file_hdr->c_uid,
+ (unsigned int) file_hdr->c_gid);
+#ifndef __MSDOS__
+ else
+ printf ("%-8.8s %-8.8s ", getuser (file_hdr->c_uid),
+ getgroup (file_hdr->c_gid));
+
+ if ((file_hdr->c_mode & CP_IFMT) == CP_IFCHR
+ || (file_hdr->c_mode & CP_IFMT) == CP_IFBLK)
+ printf ("%3u, %3u ", file_hdr->c_rdev_maj,
+ file_hdr->c_rdev_min);
+ else
+#endif
+ printf ("%8lu ", file_hdr->c_filesize);
+
+ printf ("%s ", tbuf + 4);
+
+ print_name_with_quoting (file_hdr->c_name);
+ if (link_name)
+ {
+ printf (" -> ");
+ print_name_with_quoting (link_name);
+ }
+ putc ('\n', stdout);
+}
+
+void
+print_name_with_quoting (p)
+ register char *p;
+{
+ register unsigned char c;
+
+ while ( (c = *p++) )
+ {
+ switch (c)
+ {
+#ifndef __MSDOS__
+ case '\\':
+ printf ("\\\\");
+ break;
+#endif
+
+ case '\n':
+ printf ("\\n");
+ break;
+
+ case '\b':
+ printf ("\\b");
+ break;
+
+ case '\r':
+ printf ("\\r");
+ break;
+
+ case '\t':
+ printf ("\\t");
+ break;
+
+ case '\f':
+ printf ("\\f");
+ break;
+
+ case ' ':
+ printf ("\\ ");
+ break;
+
+ case '"':
+ printf ("\\\"");
+ break;
+
+ default:
+ if (c > 040 &&
+#ifdef __MSDOS__
+ c < 0377 && c != 0177
+#else
+ c < 0177
+#endif
+ )
+ putchar (c);
+ else
+ printf ("\\%03o", (unsigned int) c);
+ }
+ }
+}
+
+/* Read a pattern file (for the -E option). Put a list of
+ `num_patterns' elements in `save_patterns'. Any patterns that were
+ already in `save_patterns' (from the command line) are preserved. */
+
+static void
+read_pattern_file ()
+{
+ int max_new_patterns;
+ char **new_save_patterns;
+ int new_num_patterns;
+ int i;
+ dynamic_string pattern_name;
+ FILE *pattern_fp;
+
+ if (num_patterns < 0)
+ num_patterns = 0;
+ max_new_patterns = 1 + num_patterns;
+ new_save_patterns = (char **) xmalloc (max_new_patterns * sizeof (char *));
+ new_num_patterns = num_patterns;
+ ds_init (&pattern_name, 128);
+
+ pattern_fp = fopen (pattern_file_name, "r");
+ if (pattern_fp == NULL)
+ error (1, errno, "%s", pattern_file_name);
+ while (ds_fgetstr (pattern_fp, &pattern_name, '\n') != NULL)
+ {
+ if (new_num_patterns >= max_new_patterns)
+ {
+ max_new_patterns += 1;
+ new_save_patterns = (char **)
+ xrealloc ((char *) new_save_patterns,
+ max_new_patterns * sizeof (char *));
+ }
+ new_save_patterns[new_num_patterns] = xstrdup (pattern_name.ds_string);
+ ++new_num_patterns;
+ }
+ if (ferror (pattern_fp) || fclose (pattern_fp) == EOF)
+ error (1, errno, "%s", pattern_file_name);
+
+ for (i = 0; i < num_patterns; ++i)
+ new_save_patterns[i] = save_patterns[i];
+
+ save_patterns = new_save_patterns;
+ num_patterns = new_num_patterns;
+}
+
+/* Skip the padding on IN_FILE_DES after a header or file,
+ up to the next header.
+ The number of bytes skipped is based on OFFSET -- the current offset
+ from the last start of a header (or file) -- and the current
+ header type. */
+
+static void
+tape_skip_padding (in_file_des, offset)
+ int in_file_des;
+ int offset;
+{
+ int pad;
+
+ if (archive_format == arf_crcascii || archive_format == arf_newascii)
+ pad = (4 - (offset % 4)) % 4;
+ else if (archive_format == arf_binary || archive_format == arf_hpbinary)
+ pad = (2 - (offset % 2)) % 2;
+ else if (archive_format == arf_tar || archive_format == arf_ustar)
+ pad = (512 - (offset % 512)) % 512;
+ else
+ pad = 0;
+
+ if (pad != 0)
+ tape_toss_input (in_file_des, pad);
+}
+
+
+/* The newc and crc formats store multiply linked copies of the same file
+ in the archive only once. The actual data is attached to the last link
+ in the archive, and the other links all have a filesize of 0. When a
+ file in the archive has multiple links and a filesize of 0, its data is
+ probably "attatched" to another file in the archive, so we can't create
+ it right away. We have to "defer" creating it until we have created
+ the file that has the data "attatched" to it. We keep a list of the
+ "defered" links on deferments. */
+
+struct deferment *deferments = NULL;
+
+/* Add a file header to the deferments list. For now they all just
+ go on one list, although we could optimize this if necessary. */
+
+static void
+defer_copyin (file_hdr)
+ struct new_cpio_header *file_hdr;
+{
+ struct deferment *d;
+ d = create_deferment (file_hdr);
+ d->next = deferments;
+ deferments = d;
+ return;
+}
+
+/* We just created a file that (probably) has some other links to it
+ which have been defered. Go through all of the links on the deferments
+ list and create any which are links to this file. */
+
+static void
+create_defered_links (file_hdr)
+ struct new_cpio_header *file_hdr;
+{
+ struct deferment *d;
+ struct deferment *d_prev;
+ int ino;
+ int maj;
+ int min;
+ int link_res;
+ ino = file_hdr->c_ino;
+ maj = file_hdr->c_dev_maj;
+ min = file_hdr->c_dev_min;
+ d = deferments;
+ d_prev = NULL;
+ while (d != NULL)
+ {
+ if ( (d->header.c_ino == ino) && (d->header.c_dev_maj == maj)
+ && (d->header.c_dev_min == min) )
+ {
+ struct deferment *d_free;
+ link_res = link_to_name (d->header.c_name, file_hdr->c_name);
+ if (link_res < 0)
+ {
+ error (0, errno, _("cannot link %s to %s"),
+ d->header.c_name, file_hdr->c_name);
+ }
+ if (d_prev != NULL)
+ d_prev->next = d->next;
+ else
+ deferments = d->next;
+ d_free = d;
+ d = d->next;
+ free_deferment (d_free);
+ }
+ else
+ {
+ d_prev = d;
+ d = d->next;
+ }
+ }
+}
+
+/* We are skipping a file but there might be other links to it that we
+ did not skip, so we have to copy its data for the other links. Find
+ the first link that we didn't skip and try to create that. That will
+ then create the other deferred links. */
+
+static int
+create_defered_links_to_skipped (file_hdr, in_file_des)
+ struct new_cpio_header *file_hdr;
+ int in_file_des;
+{
+ struct deferment *d;
+ struct deferment *d_prev;
+ int ino;
+ int maj;
+ int min;
+ int link_res;
+ if (file_hdr->c_filesize == 0)
+ {
+ /* The file doesn't have any data attached to it so we don't have
+ to bother. */
+ return -1;
+ }
+ ino = file_hdr->c_ino;
+ maj = file_hdr->c_dev_maj;
+ min = file_hdr->c_dev_min;
+ d = deferments;
+ d_prev = NULL;
+ while (d != NULL)
+ {
+ if ( (d->header.c_ino == ino) && (d->header.c_dev_maj == maj)
+ && (d->header.c_dev_min == min) )
+ {
+ if (d_prev != NULL)
+ d_prev->next = d->next;
+ else
+ deferments = d->next;
+ free (file_hdr->c_name);
+ file_hdr->c_name = xstrdup(d->header.c_name);
+ free_deferment (d);
+ copyin_regular_file(file_hdr, in_file_des);
+ return 0;
+ }
+ else
+ {
+ d_prev = d;
+ d = d->next;
+ }
+ }
+ return -1;
+}
+
+/* If we had a multiply linked file that really was empty then we would
+ have defered all of its links, since we never found any with data
+ "attached", and they will still be on the deferment list even when
+ we are done reading the whole archive. Write out all of these
+ empty links that are still on the deferments list. */
+
+static void
+create_final_defers ()
+{
+ struct deferment *d;
+ int link_res;
+ int out_file_des;
+ struct utimbuf times; /* For setting file times. */
+ /* Initialize this in case it has members we don't know to set. */
+ bzero (&times, sizeof (struct utimbuf));
+
+ for (d = deferments; d != NULL; d = d->next)
+ {
+ /* Debian hack: A line, which could cause an endless loop, was
+ removed (97/1/2). It was reported by Ronald F. Guilmette to
+ the upstream maintainers. -BEM */
+ /* Debian hack: This was reported by Horst Knobloch. This bug has
+ been reported to "bug-gnu-utils@prep.ai.mit.edu". (99/1/6) -BEM
+ */
+ link_res = link_to_maj_min_ino (d->header.c_name,
+ d->header.c_dev_maj, d->header.c_dev_min,
+ d->header.c_ino);
+ if (link_res == 0)
+ {
+ continue;
+ }
+ out_file_des = open (d->header.c_name,
+ O_CREAT | O_WRONLY | O_BINARY, 0600);
+ if (out_file_des < 0 && create_dir_flag)
+ {
+ create_all_directories (d->header.c_name);
+ out_file_des = open (d->header.c_name,
+ O_CREAT | O_WRONLY | O_BINARY,
+ 0600);
+ }
+ if (out_file_des < 0)
+ {
+ error (0, errno, "%s", d->header.c_name);
+ continue;
+ }
+
+ if (close (out_file_des) < 0)
+ error (0, errno, "%s", d->header.c_name);
+
+ /* File is now copied; set attributes. */
+ if (!no_chown_flag)
+ if ((chown (d->header.c_name,
+ set_owner_flag ? set_owner : d->header.c_uid,
+ set_group_flag ? set_group : d->header.c_gid) < 0)
+ && errno != EPERM)
+ error (0, errno, "%s", d->header.c_name);
+ /* chown may have turned off some permissions we wanted. */
+ if (chmod (d->header.c_name, (int) d->header.c_mode) < 0)
+ error (0, errno, "%s", d->header.c_name);
+ if (retain_time_flag)
+ {
+ times.actime = times.modtime = d->header.c_mtime;
+ if (utime (d->header.c_name, &times) < 0)
+ error (0, errno, "%s", d->header.c_name);
+ }
+ }
+}
diff --git a/src/copyout.c b/src/copyout.c
new file mode 100644
index 0000000..6be7983
--- a/dev/null
+++ b/src/copyout.c
@@ -0,0 +1,827 @@
+/* copyout.c - create a cpio archive
+ Copyright (C) 1990, 1991, 1992, 2001, 2003 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#if defined(HAVE_CONFIG_H)
+# include <config.h>
+#endif
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "filetypes.h"
+#include "system.h"
+#include "cpiohdr.h"
+#include "dstring.h"
+#include "extern.h"
+#include "defer.h"
+#include "rmt.h"
+
+static unsigned long read_for_checksum ();
+static void tape_clear_rest_of_block ();
+static void tape_pad_output ();
+static int last_link ();
+static int count_defered_links_to_dev_ino ();
+static void add_link_defer ();
+static void writeout_other_defers ();
+static void writeout_final_defers();
+static void writeout_defered_file ();
+
+/* Write out header FILE_HDR, including the file name, to file
+ descriptor OUT_DES. */
+
+void
+write_out_header (file_hdr, out_des)
+ struct new_cpio_header *file_hdr;
+ int out_des;
+{
+ if (archive_format == arf_newascii || archive_format == arf_crcascii)
+ {
+ char ascii_header[112];
+ char *magic_string;
+
+ if (archive_format == arf_crcascii)
+ magic_string = "070702";
+ else
+ magic_string = "070701";
+ sprintf (ascii_header,
+ "%6s%08lx%08lx%08lx%08lx%08lx%08lx%08lx%08lx%08lx%08lx%08lx%08lx%08lx",
+ magic_string,
+ file_hdr->c_ino, file_hdr->c_mode, file_hdr->c_uid,
+ file_hdr->c_gid, file_hdr->c_nlink, file_hdr->c_mtime,
+ file_hdr->c_filesize, file_hdr->c_dev_maj, file_hdr->c_dev_min,
+ file_hdr->c_rdev_maj, file_hdr->c_rdev_min, file_hdr->c_namesize,
+ file_hdr->c_chksum);
+ tape_buffered_write (ascii_header, out_des, 110L);
+
+ /* Write file name to output. */
+ tape_buffered_write (file_hdr->c_name, out_des, (long) file_hdr->c_namesize);
+ tape_pad_output (out_des, file_hdr->c_namesize + 110);
+ }
+ else if (archive_format == arf_oldascii || archive_format == arf_hpoldascii)
+ {
+ char ascii_header[78];
+#ifndef __MSDOS__
+ dev_t dev;
+ dev_t rdev;
+
+ if (archive_format == arf_oldascii)
+ {
+ dev = makedev (file_hdr->c_dev_maj, file_hdr->c_dev_min);
+ rdev = makedev (file_hdr->c_rdev_maj, file_hdr->c_rdev_min);
+ }
+ else
+ {
+ /* HP/UX cpio creates archives that look just like ordinary archives,
+ but for devices it sets major = 0, minor = 1, and puts the
+ actual major/minor number in the filesize field. */
+ switch (file_hdr->c_mode & CP_IFMT)
+ {
+ case CP_IFCHR:
+ case CP_IFBLK:
+#ifdef CP_IFSOCK
+ case CP_IFSOCK:
+#endif
+#ifdef CP_IFIFO
+ case CP_IFIFO:
+#endif
+ file_hdr->c_filesize = makedev (file_hdr->c_rdev_maj,
+ file_hdr->c_rdev_min);
+ rdev = 1;
+ break;
+ default:
+ dev = makedev (file_hdr->c_dev_maj, file_hdr->c_dev_min);
+ rdev = makedev (file_hdr->c_rdev_maj, file_hdr->c_rdev_min);
+ break;
+ }
+ }
+#else
+ int dev = 0, rdev = 0;
+#endif
+
+ if ((file_hdr->c_ino >> 16) != 0)
+ error (0, 0, _("%s: truncating inode number"), file_hdr->c_name);
+
+ /* Debian hack: The type of dev_t has changed in glibc. Fixed output
+ to ensure that a long int is passed to sprintf. This has been
+ reported to "bug-gnu-utils@prep.ai.mit.edu". (1998/5/26) -BEM */
+ sprintf (ascii_header,
+ "%06ho%06lo%06lo%06lo%06lo%06lo%06lo%06lo%011lo%06lo%011lo",
+ file_hdr->c_magic & 0xFFFF, (long) dev & 0xFFFF,
+ file_hdr->c_ino & 0xFFFF, file_hdr->c_mode & 0xFFFF,
+ file_hdr->c_uid & 0xFFFF, file_hdr->c_gid & 0xFFFF,
+ file_hdr->c_nlink & 0xFFFF, (long) rdev & 0xFFFF,
+ file_hdr->c_mtime, file_hdr->c_namesize & 0xFFFF,
+ file_hdr->c_filesize);
+ tape_buffered_write (ascii_header, out_des, 76L);
+
+ /* Write file name to output. */
+ tape_buffered_write (file_hdr->c_name, out_des, (long) file_hdr->c_namesize);
+ }
+ else if (archive_format == arf_tar || archive_format == arf_ustar)
+ {
+ write_out_tar_header (file_hdr, out_des);
+ }
+ else
+ {
+ struct old_cpio_header short_hdr;
+
+ short_hdr.c_magic = 070707;
+ short_hdr.c_dev = makedev (file_hdr->c_dev_maj, file_hdr->c_dev_min);
+
+ if ((file_hdr->c_ino >> 16) != 0)
+ error (0, 0, _("%s: truncating inode number"), file_hdr->c_name);
+
+ short_hdr.c_ino = file_hdr->c_ino & 0xFFFF;
+ short_hdr.c_mode = file_hdr->c_mode & 0xFFFF;
+ short_hdr.c_uid = file_hdr->c_uid & 0xFFFF;
+ short_hdr.c_gid = file_hdr->c_gid & 0xFFFF;
+ short_hdr.c_nlink = file_hdr->c_nlink & 0xFFFF;
+ if (archive_format != arf_hpbinary)
+ short_hdr.c_rdev = makedev (file_hdr->c_rdev_maj, file_hdr->c_rdev_min);
+ else
+ {
+ switch (file_hdr->c_mode & CP_IFMT)
+ {
+ /* HP/UX cpio creates archives that look just like ordinary
+ archives, but for devices it sets major = 0, minor = 1, and
+ puts the actual major/minor number in the filesize field. */
+ case CP_IFCHR:
+ case CP_IFBLK:
+#ifdef CP_IFSOCK
+ case CP_IFSOCK:
+#endif
+#ifdef CP_IFIFO
+ case CP_IFIFO:
+#endif
+ file_hdr->c_filesize = makedev (file_hdr->c_rdev_maj,
+ file_hdr->c_rdev_min);
+ short_hdr.c_rdev = makedev (0, 1);
+ break;
+ default:
+ short_hdr.c_rdev = makedev (file_hdr->c_rdev_maj,
+ file_hdr->c_rdev_min);
+ break;
+ }
+ }
+ short_hdr.c_mtimes[0] = file_hdr->c_mtime >> 16;
+ short_hdr.c_mtimes[1] = file_hdr->c_mtime & 0xFFFF;
+
+ short_hdr.c_namesize = file_hdr->c_namesize & 0xFFFF;
+
+ short_hdr.c_filesizes[0] = file_hdr->c_filesize >> 16;
+ short_hdr.c_filesizes[1] = file_hdr->c_filesize & 0xFFFF;
+
+ /* Output the file header. */
+ tape_buffered_write ((char *) &short_hdr, out_des, 26L);
+
+ /* Write file name to output. */
+ tape_buffered_write (file_hdr->c_name, out_des, (long) file_hdr->c_namesize);
+
+ tape_pad_output (out_des, file_hdr->c_namesize + 26);
+ }
+}
+
+/* Read a list of file names from the standard input
+ and write a cpio collection on the standard output.
+ The format of the header depends on the compatibility (-c) flag. */
+
+void
+process_copy_out ()
+{
+ int res; /* Result of functions. */
+ dynamic_string input_name; /* Name of file read from stdin. */
+ struct utimbuf times; /* For resetting file times after copy. */
+ struct stat file_stat; /* Stat record for file. */
+ struct new_cpio_header file_hdr; /* Output header information. */
+ int in_file_des; /* Source file descriptor. */
+ int out_file_des; /* Output file descriptor. */
+ char *p;
+
+ /* Initialize the copy out. */
+ ds_init (&input_name, 128);
+ /* Initialize this in case it has members we don't know to set. */
+ bzero (&times, sizeof (struct utimbuf));
+ file_hdr.c_magic = 070707;
+
+#ifdef __MSDOS__
+ setmode (archive_des, O_BINARY);
+#endif
+ /* Check whether the output file might be a tape. */
+ out_file_des = archive_des;
+ if (_isrmt (out_file_des))
+ {
+ output_is_special = 1;
+ output_is_seekable = 0;
+ }
+ else
+ {
+ if (fstat (out_file_des, &file_stat))
+ error (1, errno, _("standard output is closed"));
+ output_is_special =
+#ifdef S_ISBLK
+ S_ISBLK (file_stat.st_mode) ||
+#endif
+ S_ISCHR (file_stat.st_mode);
+ output_is_seekable = S_ISREG (file_stat.st_mode);
+ }
+
+ if (append_flag)
+ {
+ process_copy_in ();
+ prepare_append (out_file_des);
+ }
+
+ /* Copy files with names read from stdin. */
+ while (ds_fgetstr (stdin, &input_name, name_end) != NULL)
+ {
+ /* Check for blank line. */
+ if (input_name.ds_string[0] == 0)
+ {
+ error (0, 0, _("blank line ignored"));
+ continue;
+ }
+
+ /* Process next file. */
+ if ((*xstat) (input_name.ds_string, &file_stat) < 0)
+ error (0, errno, "%s", input_name.ds_string);
+ else
+ {
+ /* Set values in output header. */
+ file_hdr.c_dev_maj = major (file_stat.st_dev);
+ file_hdr.c_dev_min = minor (file_stat.st_dev);
+ file_hdr.c_ino = file_stat.st_ino;
+ /* For POSIX systems that don't define the S_IF macros,
+ we can't assume that S_ISfoo means the standard Unix
+ S_IFfoo bit(s) are set. So do it manually, with a
+ different name. Bleah. */
+ file_hdr.c_mode = (file_stat.st_mode & 07777);
+ if (S_ISREG (file_stat.st_mode))
+ file_hdr.c_mode |= CP_IFREG;
+ else if (S_ISDIR (file_stat.st_mode))
+ file_hdr.c_mode |= CP_IFDIR;
+#ifdef S_ISBLK
+ else if (S_ISBLK (file_stat.st_mode))
+ file_hdr.c_mode |= CP_IFBLK;
+#endif
+#ifdef S_ISCHR
+ else if (S_ISCHR (file_stat.st_mode))
+ file_hdr.c_mode |= CP_IFCHR;
+#endif
+#ifdef S_ISFIFO
+ else if (S_ISFIFO (file_stat.st_mode))
+ file_hdr.c_mode |= CP_IFIFO;
+#endif
+#ifdef S_ISLNK
+ else if (S_ISLNK (file_stat.st_mode))
+ file_hdr.c_mode |= CP_IFLNK;
+#endif
+#ifdef S_ISSOCK
+ else if (S_ISSOCK (file_stat.st_mode))
+ file_hdr.c_mode |= CP_IFSOCK;
+#endif
+#ifdef S_ISNWK
+ else if (S_ISNWK (file_stat.st_mode))
+ file_hdr.c_mode |= CP_IFNWK;
+#endif
+ file_hdr.c_uid = file_stat.st_uid;
+ file_hdr.c_gid = file_stat.st_gid;
+ file_hdr.c_nlink = file_stat.st_nlink;
+ file_hdr.c_rdev_maj = major (file_stat.st_rdev);
+ file_hdr.c_rdev_min = minor (file_stat.st_rdev);
+ file_hdr.c_mtime = file_stat.st_mtime;
+ file_hdr.c_filesize = file_stat.st_size;
+ file_hdr.c_chksum = 0;
+ file_hdr.c_tar_linkname = NULL;
+
+ /* Strip leading `./' from the filename. */
+ p = input_name.ds_string;
+ while (*p == '.' && *(p + 1) == '/')
+ {
+ ++p;
+ while (*p == '/')
+ ++p;
+ }
+#ifndef HPUX_CDF
+ file_hdr.c_name = p;
+ file_hdr.c_namesize = strlen (p) + 1;
+#else
+ if ( (archive_format != arf_tar) && (archive_format != arf_ustar) )
+ {
+ /* We mark CDF's in cpio files by adding a 2nd `/' after the
+ "hidden" directory name. We need to do this so we can
+ properly recreate the directory as hidden (in case the
+ files of a directory go into the archive before the
+ directory itself (e.g from "find ... -depth ... | cpio")). */
+ file_hdr.c_name = add_cdf_double_slashes (p);
+ file_hdr.c_namesize = strlen (file_hdr.c_name) + 1;
+ }
+ else
+ {
+ /* We don't mark CDF's in tar files. We assume the "hidden"
+ directory will always go into the archive before any of
+ its files. */
+ file_hdr.c_name = p;
+ file_hdr.c_namesize = strlen (p) + 1;
+ }
+#endif
+ if ((archive_format == arf_tar || archive_format == arf_ustar)
+ && is_tar_filename_too_long (file_hdr.c_name))
+ {
+ error (0, 0, _("%s: file name too long"), file_hdr.c_name);
+ continue;
+ }
+
+ /* Copy the named file to the output. */
+ switch (file_hdr.c_mode & CP_IFMT)
+ {
+ case CP_IFREG:
+#ifndef __MSDOS__
+ if (archive_format == arf_tar || archive_format == arf_ustar)
+ {
+ char *otherfile;
+ if ((otherfile = find_inode_file (file_hdr.c_ino,
+ file_hdr.c_dev_maj,
+ file_hdr.c_dev_min)))
+ {
+ file_hdr.c_tar_linkname = otherfile;
+ write_out_header (&file_hdr, out_file_des);
+ break;
+ }
+ }
+ if ( (archive_format == arf_newascii || archive_format == arf_crcascii)
+ && (file_hdr.c_nlink > 1) )
+ {
+ if (last_link (&file_hdr) )
+ {
+ writeout_other_defers (&file_hdr, out_file_des);
+ }
+ else
+ {
+ add_link_defer (&file_hdr);
+ break;
+ }
+ }
+#endif
+ in_file_des = open (input_name.ds_string,
+ O_RDONLY | O_BINARY, 0);
+ if (in_file_des < 0)
+ {
+ error (0, errno, "%s", input_name.ds_string);
+ continue;
+ }
+
+ if (archive_format == arf_crcascii)
+ file_hdr.c_chksum = read_for_checksum (in_file_des,
+ file_hdr.c_filesize,
+ input_name.ds_string);
+
+ write_out_header (&file_hdr, out_file_des);
+ copy_files_disk_to_tape (in_file_des, out_file_des, file_hdr.c_filesize, input_name.ds_string);
+ warn_if_file_changed(input_name.ds_string, file_hdr.c_filesize,
+ file_hdr.c_mtime);
+
+#ifndef __MSDOS__
+ if (archive_format == arf_tar || archive_format == arf_ustar)
+ add_inode (file_hdr.c_ino, file_hdr.c_name, file_hdr.c_dev_maj,
+ file_hdr.c_dev_min);
+#endif
+
+ tape_pad_output (out_file_des, file_hdr.c_filesize);
+
+ if (close (in_file_des) < 0)
+ error (0, errno, "%s", input_name.ds_string);
+ if (reset_time_flag)
+ {
+ times.actime = file_stat.st_atime;
+ times.modtime = file_stat.st_mtime;
+ /* Debian hack: Silently ignore EROFS because
+ reading the file won't have upset its timestamp
+ if it's on a read-only filesystem. This has been
+ submitted as a suggestion to
+ "bug-gnu-utils@prep.ai.mit.edu". -BEM */
+ if (utime (file_hdr.c_name, &times) < 0
+ && errno != EROFS)
+ error (0, errno, "%s", file_hdr.c_name);
+ }
+ break;
+
+ case CP_IFDIR:
+ file_hdr.c_filesize = 0;
+ write_out_header (&file_hdr, out_file_des);
+ break;
+
+#ifndef __MSDOS__
+ case CP_IFCHR:
+ case CP_IFBLK:
+#ifdef CP_IFSOCK
+ case CP_IFSOCK:
+#endif
+#ifdef CP_IFIFO
+ case CP_IFIFO:
+#endif
+ if (archive_format == arf_tar)
+ {
+ error (0, 0, _("%s not dumped: not a regular file"),
+ file_hdr.c_name);
+ continue;
+ }
+ else if (archive_format == arf_ustar)
+ {
+ char *otherfile;
+ if ((otherfile = find_inode_file (file_hdr.c_ino,
+ file_hdr.c_dev_maj,
+ file_hdr.c_dev_min)))
+ {
+ /* This file is linked to another file already in the
+ archive, so write it out as a hard link. */
+ file_hdr.c_mode = (file_stat.st_mode & 07777);
+ file_hdr.c_mode |= CP_IFREG;
+ file_hdr.c_tar_linkname = otherfile;
+ write_out_header (&file_hdr, out_file_des);
+ break;
+ }
+ add_inode (file_hdr.c_ino, file_hdr.c_name,
+ file_hdr.c_dev_maj, file_hdr.c_dev_min);
+ }
+ file_hdr.c_filesize = 0;
+ write_out_header (&file_hdr, out_file_des);
+ break;
+#endif
+
+#ifdef CP_IFLNK
+ case CP_IFLNK:
+ {
+ char *link_name = (char *) xmalloc (file_stat.st_size + 1);
+ int link_size;
+
+ link_size = readlink (input_name.ds_string, link_name,
+ file_stat.st_size);
+ if (link_size < 0)
+ {
+ error (0, errno, "%s", input_name.ds_string);
+ free (link_name);
+ continue;
+ }
+ file_hdr.c_filesize = link_size;
+ if (archive_format == arf_tar || archive_format == arf_ustar)
+ {
+ if (link_size + 1 > 100)
+ {
+ error (0, 0, _("%s: symbolic link too long"),
+ file_hdr.c_name);
+ }
+ else
+ {
+ link_name[link_size] = '\0';
+ file_hdr.c_tar_linkname = link_name;
+ write_out_header (&file_hdr, out_file_des);
+ }
+ }
+ else
+ {
+ write_out_header (&file_hdr, out_file_des);
+ tape_buffered_write (link_name, out_file_des, link_size);
+ tape_pad_output (out_file_des, link_size);
+ }
+ free (link_name);
+ }
+ break;
+#endif
+
+ default:
+ error (0, 0, _("%s: unknown file type"), input_name.ds_string);
+ }
+
+ if (verbose_flag)
+ fprintf (stderr, "%s\n", input_name.ds_string);
+ if (dot_flag)
+ fputc ('.', stderr);
+ }
+ }
+
+ writeout_final_defers(out_file_des);
+ /* The collection is complete; append the trailer. */
+ file_hdr.c_ino = 0;
+ file_hdr.c_mode = 0;
+ file_hdr.c_uid = 0;
+ file_hdr.c_gid = 0;
+ file_hdr.c_nlink = 1; /* Must be 1 for crc format. */
+ file_hdr.c_dev_maj = 0;
+ file_hdr.c_dev_min = 0;
+ file_hdr.c_rdev_maj = 0;
+ file_hdr.c_rdev_min = 0;
+ file_hdr.c_mtime = 0;
+ file_hdr.c_chksum = 0;
+
+ file_hdr.c_filesize = 0;
+ file_hdr.c_namesize = 11;
+ file_hdr.c_name = "TRAILER!!!";
+ if (archive_format != arf_tar && archive_format != arf_ustar)
+ write_out_header (&file_hdr, out_file_des);
+ else
+ {
+ tape_buffered_write (zeros_512, out_file_des, 512);
+ tape_buffered_write (zeros_512, out_file_des, 512);
+ }
+
+ /* Fill up the output block. */
+ tape_clear_rest_of_block (out_file_des);
+ tape_empty_output_buffer (out_file_des);
+ if (dot_flag)
+ fputc ('\n', stderr);
+ if (!quiet_flag)
+ {
+ res = (output_bytes + io_block_size - 1) / io_block_size;
+ fprintf (stderr, ngettext ("%d block\n", "%d blocks\n", res), res);
+ }
+}
+
+/* Read FILE_SIZE bytes of FILE_NAME from IN_FILE_DES and
+ compute and return a checksum for them. */
+
+static unsigned long
+read_for_checksum (in_file_des, file_size, file_name)
+ int in_file_des;
+ int file_size;
+ char *file_name;
+{
+ unsigned long crc;
+ char buf[BUFSIZ];
+ int bytes_left;
+ int bytes_read;
+ int i;
+
+ crc = 0;
+
+ for (bytes_left = file_size; bytes_left > 0; bytes_left -= bytes_read)
+ {
+ bytes_read = read (in_file_des, buf, BUFSIZ);
+ if (bytes_read < 0)
+ error (1, errno, _("cannot read checksum for %s"), file_name);
+ if (bytes_read == 0)
+ break;
+ if (bytes_left < bytes_read)
+ bytes_read = bytes_left;
+ for (i = 0; i < bytes_read; ++i)
+ crc += buf[i] & 0xff;
+ }
+ if (lseek (in_file_des, 0L, SEEK_SET))
+ error (1, errno, _("cannot read checksum for %s"), file_name);
+
+ return crc;
+}
+
+/* Write out NULs to fill out the rest of the current block on
+ OUT_FILE_DES. */
+
+static void
+tape_clear_rest_of_block (out_file_des)
+ int out_file_des;
+{
+ while (output_size < io_block_size)
+ {
+ if ((io_block_size - output_size) > 512)
+ tape_buffered_write (zeros_512, out_file_des, 512);
+ else
+ tape_buffered_write (zeros_512, out_file_des, io_block_size - output_size);
+ }
+}
+
+/* Write NULs on OUT_FILE_DES to move from OFFSET (the current location)
+ to the end of the header. */
+
+static void
+tape_pad_output (out_file_des, offset)
+ int out_file_des;
+ int offset;
+{
+ int pad;
+
+ if (archive_format == arf_newascii || archive_format == arf_crcascii)
+ pad = (4 - (offset % 4)) % 4;
+ else if (archive_format == arf_tar || archive_format == arf_ustar)
+ pad = (512 - (offset % 512)) % 512;
+ else if (archive_format != arf_oldascii && archive_format != arf_hpoldascii)
+ pad = (2 - (offset % 2)) % 2;
+ else
+ pad = 0;
+
+ if (pad != 0)
+ tape_buffered_write (zeros_512, out_file_des, pad);
+}
+
+
+/* When creating newc and crc archives if a file has multiple (hard)
+ links, we don't put any of them into the archive until we have seen
+ all of them (or until we get to the end of the list of files that
+ are going into the archive and know that we have seen all of the links
+ to the file that we will see). We keep these "defered" files on
+ this list. */
+
+struct deferment *deferouts = NULL;
+
+
+/* Is this file_hdr the last (hard) link to a file? I.e., have
+ we already seen and defered all of the other links? */
+
+static int
+last_link (file_hdr)
+ struct new_cpio_header *file_hdr;
+{
+ int other_files_sofar;
+
+ other_files_sofar = count_defered_links_to_dev_ino (file_hdr);
+ if (file_hdr->c_nlink == (other_files_sofar + 1) )
+ {
+ return 1;
+ }
+ return 0;
+}
+
+/* Count the number of other (hard) links to this file that have
+ already been defered. */
+
+static int
+count_defered_links_to_dev_ino (file_hdr)
+ struct new_cpio_header *file_hdr;
+{
+ struct deferment *d;
+ int ino;
+ int maj;
+ int min;
+ int count;
+ ino = file_hdr->c_ino;
+ maj = file_hdr->c_dev_maj;
+ min = file_hdr->c_dev_min;
+ count = 0;
+ for (d = deferouts; d != NULL; d = d->next)
+ {
+ if ( (d->header.c_ino == ino) && (d->header.c_dev_maj == maj)
+ && (d->header.c_dev_min == min) )
+ ++count;
+ }
+ return count;
+}
+
+/* Add the file header for a link that is being defered to the deferouts
+ list. */
+
+static void
+add_link_defer (file_hdr)
+ struct new_cpio_header *file_hdr;
+{
+ struct deferment *d;
+ d = create_deferment (file_hdr);
+ d->next = deferouts;
+ deferouts = d;
+}
+
+/* We are about to put a file into a newc or crc archive that is
+ multiply linked. We have already seen and defered all of the
+ other links to the file but haven't written them into the archive.
+ Write the other links into the archive, and remove them from the
+ deferouts list. */
+
+static void
+writeout_other_defers (file_hdr, out_des)
+ struct new_cpio_header *file_hdr;
+ int out_des;
+{
+ struct deferment *d;
+ struct deferment *d_prev;
+ int ino;
+ int maj;
+ int min;
+ ino = file_hdr->c_ino;
+ maj = file_hdr->c_dev_maj;
+ min = file_hdr->c_dev_min;
+ d_prev = NULL;
+ d = deferouts;
+ while (d != NULL)
+ {
+ if ( (d->header.c_ino == ino) && (d->header.c_dev_maj == maj)
+ && (d->header.c_dev_min == min) )
+ {
+ struct deferment *d_free;
+ d->header.c_filesize = 0;
+ write_out_header (&d->header, out_des);
+ if (d_prev != NULL)
+ d_prev->next = d->next;
+ else
+ deferouts = d->next;
+ d_free = d;
+ d = d->next;
+ free_deferment (d_free);
+ }
+ else
+ {
+ d_prev = d;
+ d = d->next;
+ }
+ }
+ return;
+}
+/* When writing newc and crc format archives we defer multiply linked
+ files until we have seen all of the links to the file. If a file
+ has links to it that aren't going into the archive, then we will
+ never see the "last" link to the file, so at the end we just write
+ all of the leftover defered files into the archive. */
+
+static void
+writeout_final_defers(out_des)
+ int out_des;
+{
+ struct deferment *d;
+ int other_count;
+ while (deferouts != NULL)
+ {
+ d = deferouts;
+ other_count = count_defered_links_to_dev_ino (&d->header);
+ if (other_count == 1)
+ {
+ writeout_defered_file (&d->header, out_des);
+ }
+ else
+ {
+ struct new_cpio_header file_hdr;
+ file_hdr = d->header;
+ file_hdr.c_filesize = 0;
+ write_out_header (&file_hdr, out_des);
+ }
+ deferouts = deferouts->next;
+ }
+}
+
+/* Write a file into the archive. This code is the same as
+ the code in process_copy_out(), but we need it here too
+ for writeout_final_defers() to call. */
+
+static void
+writeout_defered_file (header, out_file_des)
+ struct new_cpio_header *header;
+ int out_file_des;
+{
+ int in_file_des;
+ struct new_cpio_header file_hdr;
+ struct utimbuf times; /* For setting file times. */
+ /* Initialize this in case it has members we don't know to set. */
+ bzero (&times, sizeof (struct utimbuf));
+
+ file_hdr = *header;
+
+
+ in_file_des = open (header->c_name,
+ O_RDONLY | O_BINARY, 0);
+ if (in_file_des < 0)
+ {
+ error (0, errno, "%s", header->c_name);
+ return;
+ }
+
+ if (archive_format == arf_crcascii)
+ file_hdr.c_chksum = read_for_checksum (in_file_des,
+ file_hdr.c_filesize,
+ header->c_name);
+
+ write_out_header (&file_hdr, out_file_des);
+ copy_files_disk_to_tape (in_file_des, out_file_des, file_hdr.c_filesize, header->c_name);
+ warn_if_file_changed(header->c_name, file_hdr.c_filesize, file_hdr.c_mtime);
+
+#ifndef __MSDOS__
+ if (archive_format == arf_tar || archive_format == arf_ustar)
+ add_inode (file_hdr.c_ino, file_hdr.c_name, file_hdr.c_dev_maj,
+ file_hdr.c_dev_min);
+#endif
+
+ tape_pad_output (out_file_des, file_hdr.c_filesize);
+
+ if (close (in_file_des) < 0)
+ error (0, errno, "%s", header->c_name);
+ if (reset_time_flag)
+ {
+ times.actime = file_hdr.c_mtime;
+ times.modtime = file_hdr.c_mtime;
+ /* Debian hack: Silently ignore EROFS because reading the file
+ won't have upset its timestamp if it's on a read-only
+ filesystem. This has been submitted as a suggestion to
+ "bug-gnu-utils@prep.ai.mit.edu". -BEM */
+ if (utime (file_hdr.c_name, &times) < 0
+ && errno != EROFS)
+ error (0, errno, "%s", file_hdr.c_name);
+ }
+ return;
+}
+
diff --git a/src/copypass.c b/src/copypass.c
new file mode 100644
index 0000000..a852a3d
--- a/dev/null
+++ b/src/copypass.c
@@ -0,0 +1,482 @@
+/* copypass.c - cpio copy pass sub-function.
+ Copyright (C) 1990, 1991, 1992, 2001, 2003 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#if defined(HAVE_CONFIG_H)
+# include <config.h>
+#endif
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "filetypes.h"
+#include "system.h"
+#include "cpiohdr.h"
+#include "dstring.h"
+#include "extern.h"
+
+#ifndef HAVE_LCHOWN
+#define lchown chown
+#endif
+
+/* Copy files listed on the standard input into directory `directory_name'.
+ If `link_flag', link instead of copying. */
+
+void
+process_copy_pass ()
+{
+ dynamic_string input_name; /* Name of file from stdin. */
+ dynamic_string output_name; /* Name of new file. */
+ int dirname_len; /* Length of `directory_name'. */
+ int res; /* Result of functions. */
+ char *slash; /* For moving past slashes in input name. */
+ struct utimbuf times; /* For resetting file times after copy. */
+ struct stat in_file_stat; /* Stat record for input file. */
+ struct stat out_file_stat; /* Stat record for output file. */
+ int in_file_des; /* Input file descriptor. */
+ int out_file_des; /* Output file descriptor. */
+ int existing_dir; /* True if file is a dir & already exists. */
+#ifdef HPUX_CDF
+ int cdf_flag;
+ int cdf_char;
+#endif
+
+ /* Initialize the copy pass. */
+ dirname_len = strlen (directory_name);
+ ds_init (&input_name, 128);
+ ds_init (&output_name, dirname_len + 2);
+ strcpy (output_name.ds_string, directory_name);
+ output_name.ds_string[dirname_len] = '/';
+ output_is_seekable = TRUE;
+ /* Initialize this in case it has members we don't know to set. */
+ bzero (&times, sizeof (struct utimbuf));
+
+ /* Copy files with names read from stdin. */
+ while (ds_fgetstr (stdin, &input_name, name_end) != NULL)
+ {
+ int link_res = -1;
+
+ /* Check for blank line and ignore it if found. */
+ if (input_name.ds_string[0] == '\0')
+ {
+ error (0, 0, _("blank line ignored"));
+ continue;
+ }
+
+ /* Check for current directory and ignore it if found. */
+ if (input_name.ds_string[0] == '.'
+ && (input_name.ds_string[1] == '\0'
+ || (input_name.ds_string[1] == '/'
+ && input_name.ds_string[2] == '\0')))
+ continue;
+
+ if ((*xstat) (input_name.ds_string, &in_file_stat) < 0)
+ {
+ error (0, errno, "%s", input_name.ds_string);
+ continue;
+ }
+
+ /* Make the name of the new file. */
+ for (slash = input_name.ds_string; *slash == '/'; ++slash)
+ ;
+#ifdef HPUX_CDF
+ /* For CDF's we add a 2nd `/' after all "hidden" directories.
+ This kind of a kludge, but it's what we do when creating
+ archives, and it's easier to do this than to separately
+ keep track of which directories in a path are "hidden". */
+ slash = add_cdf_double_slashes (slash);
+#endif
+ ds_resize (&output_name, dirname_len + strlen (slash) + 2);
+ strcpy (output_name.ds_string + dirname_len + 1, slash);
+
+ existing_dir = FALSE;
+ if (lstat (output_name.ds_string, &out_file_stat) == 0)
+ {
+ if (S_ISDIR (out_file_stat.st_mode)
+ && S_ISDIR (in_file_stat.st_mode))
+ {
+ /* If there is already a directory there that
+ we are trying to create, don't complain about it. */
+ existing_dir = TRUE;
+ }
+ else if (!unconditional_flag
+ && in_file_stat.st_mtime <= out_file_stat.st_mtime)
+ {
+ error (0, 0, _("%s not created: newer or same age version exists"),
+ output_name.ds_string);
+ continue; /* Go to the next file. */
+ }
+ else if (S_ISDIR (out_file_stat.st_mode)
+ ? rmdir (output_name.ds_string)
+ : unlink (output_name.ds_string))
+ {
+ error (0, errno, _("cannot remove current %s"),
+ output_name.ds_string);
+ continue; /* Go to the next file. */
+ }
+ }
+
+ /* Do the real copy or link. */
+ if (S_ISREG (in_file_stat.st_mode))
+ {
+#ifndef __MSDOS__
+ /* Can the current file be linked to a another file?
+ Set link_name to the original file name. */
+ if (link_flag)
+ /* User said to link it if possible. Try and link to
+ the original copy. If that fails we'll still try
+ and link to a copy we've already made. */
+ link_res = link_to_name (output_name.ds_string,
+ input_name.ds_string);
+ if ( (link_res < 0) && (in_file_stat.st_nlink > 1) )
+ link_res = link_to_maj_min_ino (output_name.ds_string,
+ major (in_file_stat.st_dev),
+ minor (in_file_stat.st_dev),
+ in_file_stat.st_ino);
+#endif
+
+ /* If the file was not linked, copy contents of file. */
+ if (link_res < 0)
+ {
+ in_file_des = open (input_name.ds_string,
+ O_RDONLY | O_BINARY, 0);
+ if (in_file_des < 0)
+ {
+ error (0, errno, "%s", input_name.ds_string);
+ continue;
+ }
+ out_file_des = open (output_name.ds_string,
+ O_CREAT | O_WRONLY | O_BINARY, 0600);
+ if (out_file_des < 0 && create_dir_flag)
+ {
+ create_all_directories (output_name.ds_string);
+ out_file_des = open (output_name.ds_string,
+ O_CREAT | O_WRONLY | O_BINARY, 0600);
+ }
+ if (out_file_des < 0)
+ {
+ error (0, errno, "%s", output_name.ds_string);
+ close (in_file_des);
+ continue;
+ }
+
+ copy_files_disk_to_disk (in_file_des, out_file_des, in_file_stat.st_size, input_name.ds_string);
+ disk_empty_output_buffer (out_file_des);
+ /* Debian hack to fix a bug in the --sparse option.
+ This bug has been reported to
+ "bug-gnu-utils@prep.ai.mit.edu". (96/7/10) -BEM */
+ if (delayed_seek_count > 0)
+ {
+ lseek (out_file_des, delayed_seek_count-1, SEEK_CUR);
+ write (out_file_des, "", 1);
+ delayed_seek_count = 0;
+ }
+ if (close (in_file_des) < 0)
+ error (0, errno, "%s", input_name.ds_string);
+ if (close (out_file_des) < 0)
+ error (0, errno, "%s", output_name.ds_string);
+
+ /* Set the attributes of the new file. */
+ if (!no_chown_flag)
+ if ((chown (output_name.ds_string,
+ set_owner_flag ? set_owner : in_file_stat.st_uid,
+ set_group_flag ? set_group : in_file_stat.st_gid) < 0)
+ && errno != EPERM)
+ error (0, errno, "%s", output_name.ds_string);
+ /* chown may have turned off some permissions we wanted. */
+ if (chmod (output_name.ds_string, in_file_stat.st_mode) < 0)
+ error (0, errno, "%s", output_name.ds_string);
+ if (reset_time_flag)
+ {
+ times.actime = in_file_stat.st_atime;
+ times.modtime = in_file_stat.st_mtime;
+ /* Debian hack: Silently ignore EROFS because
+ reading the file won't have upset its timestamp
+ if it's on a read-only filesystem. This has been
+ submitted as a suggestion to
+ "bug-gnu-utils@prep.ai.mit.edu". -BEM */
+ if (utime (input_name.ds_string, &times) < 0
+ && errno != EROFS)
+ error (0, errno, "%s", input_name.ds_string);
+ if (utime (output_name.ds_string, &times) < 0
+ && errno != EROFS)
+ error (0, errno, "%s", output_name.ds_string);
+ }
+ if (retain_time_flag)
+ {
+ times.actime = times.modtime = in_file_stat.st_mtime;
+ if (utime (output_name.ds_string, &times) < 0)
+ error (0, errno, "%s", output_name.ds_string);
+ }
+ warn_if_file_changed(input_name.ds_string, in_file_stat.st_size,
+ in_file_stat.st_mtime);
+ }
+ }
+ else if (S_ISDIR (in_file_stat.st_mode))
+ {
+#ifdef HPUX_CDF
+ cdf_flag = 0;
+#endif
+ if (!existing_dir)
+ {
+#ifdef HPUX_CDF
+ /* If the directory name ends in a + and is SUID,
+ then it is a CDF. Strip the trailing + from the name
+ before creating it. */
+ cdf_char = strlen (output_name.ds_string) - 1;
+ if ( (cdf_char > 0) &&
+ (in_file_stat.st_mode & 04000) &&
+ (output_name.ds_string [cdf_char] == '+') )
+ {
+ output_name.ds_string [cdf_char] = '\0';
+ cdf_flag = 1;
+ }
+#endif
+ res = mkdir (output_name.ds_string, in_file_stat.st_mode);
+
+ }
+ else
+ res = 0;
+ if (res < 0 && create_dir_flag)
+ {
+ create_all_directories (output_name.ds_string);
+ res = mkdir (output_name.ds_string, in_file_stat.st_mode);
+ }
+ if (res < 0)
+ {
+ /* In some odd cases where the output_name includes `.',
+ the directory may have actually been created by
+ create_all_directories(), so the mkdir will fail
+ because the directory exists. If that's the case,
+ don't complain about it. */
+ if ( (errno != EEXIST) ||
+ (lstat (output_name.ds_string, &out_file_stat) != 0) ||
+ !(S_ISDIR (out_file_stat.st_mode) ) )
+ {
+ error (0, errno, "%s", output_name.ds_string);
+ continue;
+ }
+ }
+ if (!no_chown_flag)
+ if ((chown (output_name.ds_string,
+ set_owner_flag ? set_owner : in_file_stat.st_uid,
+ set_group_flag ? set_group : in_file_stat.st_gid) < 0)
+ && errno != EPERM)
+ error (0, errno, "%s", output_name.ds_string);
+ /* chown may have turned off some permissions we wanted. */
+ if (chmod (output_name.ds_string, in_file_stat.st_mode) < 0)
+ error (0, errno, "%s", output_name.ds_string);
+#ifdef HPUX_CDF
+ if (cdf_flag)
+ /* Once we "hide" the directory with the chmod(),
+ we have to refer to it using name+ isntead of name. */
+ output_name.ds_string [cdf_char] = '+';
+#endif
+ if (retain_time_flag)
+ {
+ times.actime = times.modtime = in_file_stat.st_mtime;
+ if (utime (output_name.ds_string, &times) < 0)
+ error (0, errno, "%s", output_name.ds_string);
+ }
+ }
+#ifndef __MSDOS__
+ else if (S_ISCHR (in_file_stat.st_mode) ||
+ S_ISBLK (in_file_stat.st_mode) ||
+#ifdef S_ISFIFO
+ S_ISFIFO (in_file_stat.st_mode) ||
+#endif
+#ifdef S_ISSOCK
+ S_ISSOCK (in_file_stat.st_mode) ||
+#endif
+ 0)
+ {
+ /* Can the current file be linked to a another file?
+ Set link_name to the original file name. */
+ if (link_flag)
+ /* User said to link it if possible. */
+ link_res = link_to_name (output_name.ds_string,
+ input_name.ds_string);
+ if ( (link_res < 0) && (in_file_stat.st_nlink > 1) )
+ link_res = link_to_maj_min_ino (output_name.ds_string,
+ major (in_file_stat.st_dev),
+ minor (in_file_stat.st_dev),
+ in_file_stat.st_ino);
+
+ if (link_res < 0)
+ {
+ res = mknod (output_name.ds_string, in_file_stat.st_mode,
+ in_file_stat.st_rdev);
+ if (res < 0 && create_dir_flag)
+ {
+ create_all_directories (output_name.ds_string);
+ res = mknod (output_name.ds_string, in_file_stat.st_mode,
+ in_file_stat.st_rdev);
+ }
+ if (res < 0)
+ {
+ error (0, errno, "%s", output_name.ds_string);
+ continue;
+ }
+ if (!no_chown_flag)
+ if ((chown (output_name.ds_string,
+ set_owner_flag ? set_owner : in_file_stat.st_uid,
+ set_group_flag ? set_group : in_file_stat.st_gid) < 0)
+ && errno != EPERM)
+ error (0, errno, "%s", output_name.ds_string);
+ /* chown may have turned off some permissions we wanted. */
+ if (chmod (output_name.ds_string, in_file_stat.st_mode) < 0)
+ error (0, errno, "%s", output_name.ds_string);
+ if (retain_time_flag)
+ {
+ times.actime = times.modtime = in_file_stat.st_mtime;
+ if (utime (output_name.ds_string, &times) < 0)
+ error (0, errno, "%s", output_name.ds_string);
+ }
+ }
+ }
+#endif
+
+#ifdef S_ISLNK
+ else if (S_ISLNK (in_file_stat.st_mode))
+ {
+ char *link_name;
+ int link_size;
+ link_name = (char *) xmalloc ((unsigned int) in_file_stat.st_size + 1);
+
+ link_size = readlink (input_name.ds_string, link_name,
+ in_file_stat.st_size);
+ if (link_size < 0)
+ {
+ error (0, errno, "%s", input_name.ds_string);
+ free (link_name);
+ continue;
+ }
+ link_name[link_size] = '\0';
+
+ res = UMASKED_SYMLINK (link_name, output_name.ds_string,
+ in_file_stat.st_mode);
+ if (res < 0 && create_dir_flag)
+ {
+ create_all_directories (output_name.ds_string);
+ res = UMASKED_SYMLINK (link_name, output_name.ds_string,
+ in_file_stat.st_mode);
+ }
+ if (res < 0)
+ {
+ error (0, errno, "%s", output_name.ds_string);
+ free (link_name);
+ continue;
+ }
+
+ /* Set the attributes of the new link. */
+ if (!no_chown_flag)
+ if ((lchown (output_name.ds_string,
+ set_owner_flag ? set_owner : in_file_stat.st_uid,
+ set_group_flag ? set_group : in_file_stat.st_gid) < 0)
+ && errno != EPERM)
+ error (0, errno, "%s", output_name.ds_string);
+ free (link_name);
+ }
+#endif
+ else
+ {
+ error (0, 0, _("%s: unknown file type"), input_name.ds_string);
+ }
+
+ if (verbose_flag)
+ fprintf (stderr, "%s\n", output_name.ds_string);
+ if (dot_flag)
+ fputc ('.', stderr);
+ }
+
+ if (dot_flag)
+ fputc ('\n', stderr);
+ if (!quiet_flag)
+ {
+ res = (output_bytes + io_block_size - 1) / io_block_size;
+ fprintf (stderr, ngettext ("%d block\n", "%d blocks\n", res), res);
+ }
+}
+
+/* Try and create a hard link from FILE_NAME to another file
+ with the given major/minor device number and inode. If no other
+ file with the same major/minor/inode numbers is known, add this file
+ to the list of known files and associated major/minor/inode numbers
+ and return -1. If another file with the same major/minor/inode
+ numbers is found, try and create another link to it using
+ link_to_name, and return 0 for success and -1 for failure. */
+
+int
+link_to_maj_min_ino (file_name, st_dev_maj, st_dev_min, st_ino)
+ char *file_name;
+ int st_dev_maj;
+ int st_dev_min;
+ int st_ino;
+{
+ int link_res;
+ char *link_name;
+ link_res = -1;
+#ifndef __MSDOS__
+ /* Is the file a link to a previously copied file? */
+ link_name = find_inode_file (st_ino,
+ st_dev_maj,
+ st_dev_min);
+ if (link_name == NULL)
+ add_inode (st_ino, file_name,
+ st_dev_maj,
+ st_dev_min);
+ else
+ link_res = link_to_name (file_name, link_name);
+#endif
+ return link_res;
+}
+
+/* Try and create a hard link from LINK_NAME to LINK_TARGET. If
+ `create_dir_flag' is set, any non-existent (parent) directories
+ needed by LINK_NAME will be created. If the link is successfully
+ created and `verbose_flag' is set, print "LINK_TARGET linked to LINK_NAME\n".
+ If the link can not be created and `link_flag' is set, print
+ "cannot link LINK_TARGET to LINK_NAME\n". Return 0 if the link
+ is created, -1 otherwise. */
+
+int
+link_to_name (link_name, link_target)
+ char *link_name;
+ char *link_target;
+{
+ int res;
+#ifdef __MSDOS__
+ res = -1;
+#else /* not __MSDOS__ */
+ res = link (link_target, link_name);
+ if (res < 0 && create_dir_flag)
+ {
+ create_all_directories (link_name);
+ res = link (link_target, link_name);
+ }
+ if (res == 0)
+ {
+ if (verbose_flag)
+ error (0, 0, _("%s linked to %s"),
+ link_target, link_name);
+ }
+ else if (link_flag)
+ {
+ error (0, errno, _("cannot link %s to %s"),
+ link_target, link_name);
+ }
+#endif /* not __MSDOS__ */
+ return res;
+}
diff --git a/src/cpio.h b/src/cpio.h
new file mode 100644
index 0000000..3861ab0
--- a/dev/null
+++ b/src/cpio.h
@@ -0,0 +1,69 @@
+/* Extended cpio format from POSIX.1.
+ Copyright (C) 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef _CPIO_H
+
+#define _CPIO_H 1
+
+/* A cpio archive consists of a sequence of files.
+ Each file has a 76 byte header,
+ a variable length, NUL terminated filename,
+ and variable length file data.
+ A header for a filename "TRAILER!!!" indicates the end of the archive. */
+
+/* All the fields in the header are ISO 646 (approximately ASCII) strings
+ of octal numbers, left padded, not NUL terminated.
+
+ Field Name Length in Bytes Notes
+ c_magic 6 must be "070707"
+ c_dev 6
+ c_ino 6
+ c_mode 6 see below for value
+ c_uid 6
+ c_gid 6
+ c_nlink 6
+ c_rdev 6 only valid for chr and blk special files
+ c_mtime 11
+ c_namesize 6 count includes terminating NUL in pathname
+ c_filesize 11 must be 0 for FIFOs and directories */
+
+/* Values for c_mode, OR'd together: */
+
+#define C_IRUSR 000400
+#define C_IWUSR 000200
+#define C_IXUSR 000100
+#define C_IRGRP 000040
+#define C_IWGRP 000020
+#define C_IXGRP 000010
+#define C_IROTH 000004
+#define C_IWOTH 000002
+#define C_IXOTH 000001
+
+#define C_ISUID 004000
+#define C_ISGID 002000
+#define C_ISVTX 001000
+
+#define C_ISBLK 060000
+#define C_ISCHR 020000
+#define C_ISDIR 040000
+#define C_ISFIFO 010000
+#define C_ISSOCK 0140000
+#define C_ISLNK 0120000
+#define C_ISCTG 0110000
+#define C_ISREG 0100000
+
+#endif /* cpio.h */
diff --git a/src/cpiohdr.h b/src/cpiohdr.h
new file mode 100644
index 0000000..c3943b4
--- a/dev/null
+++ b/src/cpiohdr.h
@@ -0,0 +1,90 @@
+/* Extended cpio header from POSIX.1.
+ Copyright (C) 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef _CPIOHDR_H
+
+#define _CPIOHDR_H 1
+
+#include <cpio.h>
+
+struct old_cpio_header
+{
+ unsigned short c_magic;
+ short c_dev;
+ unsigned short c_ino;
+ unsigned short c_mode;
+ unsigned short c_uid;
+ unsigned short c_gid;
+ unsigned short c_nlink;
+ short c_rdev;
+ unsigned short c_mtimes[2];
+ unsigned short c_namesize;
+ unsigned short c_filesizes[2];
+ unsigned long c_mtime; /* Long-aligned copy of `c_mtimes'. */
+ unsigned long c_filesize; /* Long-aligned copy of `c_filesizes'. */
+ char *c_name;
+};
+
+/* "New" portable format and CRC format:
+
+ Each file has a 110 byte header,
+ a variable length, NUL terminated filename,
+ and variable length file data.
+ A header for a filename "TRAILER!!!" indicates the end of the archive. */
+
+/* All the fields in the header are ISO 646 (approximately ASCII) strings
+ of hexadecimal numbers, left padded, not NUL terminated.
+
+ Field Name Length in Bytes Notes
+ c_magic 6 "070701" for "new" portable format
+ "070702" for CRC format
+ c_ino 8
+ c_mode 8
+ c_uid 8
+ c_gid 8
+ c_nlink 8
+ c_mtime 8
+ c_filesize 8 must be 0 for FIFOs and directories
+ c_maj 8
+ c_min 8
+ c_rmaj 8 only valid for chr and blk special files
+ c_rmin 8 only valid for chr and blk special files
+ c_namesize 8 count includes terminating NUL in pathname
+ c_chksum 8 0 for "new" portable format; for CRC format
+ the sum of all the bytes in the file */
+
+struct new_cpio_header
+{
+ unsigned short c_magic;
+ unsigned long c_ino;
+ unsigned long c_mode;
+ unsigned long c_uid;
+ unsigned long c_gid;
+ unsigned long c_nlink;
+ unsigned long c_mtime;
+ unsigned long c_filesize;
+ long c_dev_maj;
+ long c_dev_min;
+ long c_rdev_maj;
+ long c_rdev_min;
+ unsigned long c_namesize;
+ unsigned long c_chksum;
+ char *c_name;
+ char *c_tar_linkname;
+};
+
+#endif /* cpiohdr.h */
diff --git a/src/defer.c b/src/defer.c
new file mode 100644
index 0000000..458cad8
--- a/dev/null
+++ b/src/defer.c
@@ -0,0 +1,46 @@
+/* defer.c - handle "defered" links in newc and crc archives
+ Copyright (C) 1993,2003 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#if defined(HAVE_CONFIG_H)
+# include <config.h>
+#endif
+#include <stdio.h>
+#include <sys/types.h>
+#include "system.h"
+#include "cpiohdr.h"
+#include "extern.h"
+#include "defer.h"
+
+struct deferment *
+create_deferment (file_hdr)
+ struct new_cpio_header *file_hdr;
+{
+ struct deferment *d;
+ d = (struct deferment *) xmalloc (sizeof (struct deferment) );
+ d->header = *file_hdr;
+ d->header.c_name = (char *) xmalloc (strlen (file_hdr->c_name) + 1);
+ strcpy (d->header.c_name, file_hdr->c_name);
+ return d;
+}
+
+void
+free_deferment (d)
+ struct deferment *d;
+{
+ free (d->header.c_name);
+ free (d);
+}
diff --git a/src/defer.h b/src/defer.h
new file mode 100644
index 0000000..2907fa4
--- a/dev/null
+++ b/src/defer.h
@@ -0,0 +1,25 @@
+/* defer.h
+ Copyright (C) 1993, 2001 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+struct deferment
+ {
+ struct deferment *next;
+ struct new_cpio_header header;
+ };
+
+struct deferment *create_deferment P_((struct new_cpio_header *file_hdr));
+void free_deferment P_((struct deferment *d));
diff --git a/src/dirname.c b/src/dirname.c
new file mode 100644
index 0000000..7c11c76
--- a/dev/null
+++ b/src/dirname.c
@@ -0,0 +1,70 @@
+/* dirname.c -- return all but the last element in a path
+ Copyright (C) 1990 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+#else
+char *malloc ();
+#endif
+#if defined(STDC_HEADERS) || defined(HAVE_STRING_H)
+#include <string.h>
+#else
+#include <strings.h>
+#ifndef strrchr
+#define strrchr rindex
+#endif
+#endif
+
+/* Return the leading directories part of PATH,
+ allocated with malloc. If out of memory, return 0.
+ Assumes that trailing slashes have already been
+ removed. */
+
+char *
+dirname (path)
+ char *path;
+{
+ char *newpath;
+ char *slash;
+ int length; /* Length of result, not including NUL. */
+
+ slash = strrchr (path, '/');
+ if (slash == 0)
+ {
+ /* File is in the current directory. */
+ path = ".";
+ length = 1;
+ }
+ else
+ {
+ /* Remove any trailing slashes from the result. */
+ while (slash > path && *slash == '/')
+ --slash;
+
+ length = slash - path + 1;
+ }
+ newpath = (char *) malloc (length + 1);
+ if (newpath == 0)
+ return 0;
+ strncpy (newpath, path, length);
+ newpath[length] = 0;
+ return newpath;
+}
diff --git a/src/dstring.c b/src/dstring.c
new file mode 100644
index 0000000..981a452
--- a/dev/null
+++ b/src/dstring.c
@@ -0,0 +1,118 @@
+/* dstring.c - The dynamic string handling routines used by cpio.
+ Copyright (C) 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#if defined(HAVE_CONFIG_H)
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#if defined(HAVE_STRING_H) || defined(STDC_HEADERS)
+#include <string.h>
+#else
+#include <strings.h>
+#endif
+#include "dstring.h"
+
+#if __STDC__
+# define P_(s) s
+#else
+# define P_(s) ()
+#endif
+char *xmalloc P_((unsigned n));
+char *xrealloc P_((char *p, unsigned n));
+
+/* Initialiaze dynamic string STRING with space for SIZE characters. */
+
+void
+ds_init (string, size)
+ dynamic_string *string;
+ int size;
+{
+ string->ds_length = size;
+ string->ds_string = (char *) xmalloc (size);
+}
+
+/* Expand dynamic string STRING, if necessary, to hold SIZE characters. */
+
+void
+ds_resize (string, size)
+ dynamic_string *string;
+ int size;
+{
+ if (size > string->ds_length)
+ {
+ string->ds_length = size;
+ string->ds_string = (char *) xrealloc ((char *) string->ds_string, size);
+ }
+}
+
+/* Dynamic string S gets a string terminated by the EOS character
+ (which is removed) from file F. S will increase
+ in size during the function if the string from F is longer than
+ the current size of S.
+ Return NULL if end of file is detected. Otherwise,
+ Return a pointer to the null-terminated string in S. */
+
+char *
+ds_fgetstr (f, s, eos)
+ FILE *f;
+ dynamic_string *s;
+ char eos;
+{
+ int insize; /* Amount needed for line. */
+ int strsize; /* Amount allocated for S. */
+ int next_ch;
+
+ /* Initialize. */
+ insize = 0;
+ strsize = s->ds_length;
+
+ /* Read the input string. */
+ next_ch = getc (f);
+ while (next_ch != eos && next_ch != EOF)
+ {
+ if (insize >= strsize - 1)
+ {
+ ds_resize (s, strsize * 2 + 2);
+ strsize = s->ds_length;
+ }
+ s->ds_string[insize++] = next_ch;
+ next_ch = getc (f);
+ }
+ s->ds_string[insize++] = '\0';
+
+ if (insize == 1 && next_ch == EOF)
+ return NULL;
+ else
+ return s->ds_string;
+}
+
+char *
+ds_fgets (f, s)
+ FILE *f;
+ dynamic_string *s;
+{
+ return ds_fgetstr (f, s, '\n');
+}
+
+char *
+ds_fgetname (f, s)
+ FILE *f;
+ dynamic_string *s;
+{
+ return ds_fgetstr (f, s, '\0');
+}
diff --git a/src/dstring.h b/src/dstring.h
new file mode 100644
index 0000000..515ee29
--- a/dev/null
+++ b/src/dstring.h
@@ -0,0 +1,49 @@
+/* dstring.h - Dynamic string handling include file. Requires strings.h.
+ Copyright (C) 1990, 1991, 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+/* A dynamic string consists of record that records the size of an
+ allocated string and the pointer to that string. The actual string
+ is a normal zero byte terminated string that can be used with the
+ usual string functions. The major difference is that the
+ dynamic_string routines know how to get more space if it is needed
+ by allocating new space and copying the current string. */
+
+typedef struct
+{
+ int ds_length; /* Actual amount of storage allocated. */
+ char *ds_string; /* String. */
+} dynamic_string;
+
+
+/* Macros that look similar to the original string functions.
+ WARNING: These macros work only on pointers to dynamic string records.
+ If used with a real record, an "&" must be used to get the pointer. */
+#define ds_strlen(s) strlen ((s)->ds_string)
+#define ds_strcmp(s1, s2) strcmp ((s1)->ds_string, (s2)->ds_string)
+#define ds_strncmp(s1, s2, n) strncmp ((s1)->ds_string, (s2)->ds_string, n)
+#define ds_index(s, c) index ((s)->ds_string, c)
+#define ds_rindex(s, c) rindex ((s)->ds_string, c)
+
+void ds_init ();
+void ds_resize ();
+char *ds_fgetname ();
+char *ds_fgets ();
+char *ds_fgetstr ();
diff --git a/src/error.c b/src/error.c
new file mode 100644
index 0000000..5daafd8
--- a/dev/null
+++ b/src/error.c
@@ -0,0 +1,109 @@
+/* error.c -- error handler for noninteractive utilities
+ Copyright (C) 1990, 91, 92, 93, 94, 95 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+/* Written by David MacKenzie <djm@gnu.ai.mit.edu>. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include "system.h"
+
+#if !HAVE_DECL_STRERROR
+extern const char *strerror (int en);
+#endif
+
+#if HAVE_VPRINTF || HAVE_DOPRNT || _LIBC
+# if __STDC__
+# include <stdarg.h>
+# define VA_START(args, lastarg) va_start(args, lastarg)
+# else
+# include <varargs.h>
+# define VA_START(args, lastarg) va_start(args)
+# endif
+#else
+# define va_alist a1, a2, a3, a4, a5, a6, a7, a8
+# define va_dcl char *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8;
+#endif
+
+/* This variable is incremented each time `error' is called. */
+unsigned int error_message_count;
+
+/* If NULL, error will flush stdout, then print on stderr the program
+ name, a colon and a space. Otherwise, error will call this
+ function without parameters instead. */
+void (*error_print_progname) () = NULL;
+
+#ifdef _LIBC
+# define program_name program_invocation_name
+#endif
+
+/* The calling program should define program_name and set it to the
+ name of the executing program. */
+extern char *program_name;
+
+/* Print the program name and error message MESSAGE, which is a printf-style
+ format string with optional args.
+ If ERRNUM is nonzero, print its corresponding system error message.
+ Exit with status STATUS if it is nonzero. */
+/* VARARGS */
+
+void
+#if defined(VA_START) && __STDC__
+error (int status, int errnum, const char *message, ...)
+#else
+error (status, errnum, message, va_alist)
+ int status;
+ int errnum;
+ char *message;
+ va_dcl
+#endif
+{
+#ifdef VA_START
+ va_list args;
+#endif
+
+ if (error_print_progname)
+ (*error_print_progname) ();
+ else
+ {
+ fflush (stdout);
+ fprintf (stderr, "%s: ", program_name);
+ }
+
+#ifdef VA_START
+ VA_START (args, message);
+# if HAVE_VPRINTF || _LIBC
+ vfprintf (stderr, message, args);
+# else
+ _doprnt (message, args, stderr);
+# endif
+ va_end (args);
+#else
+ fprintf (stderr, message, a1, a2, a3, a4, a5, a6, a7, a8);
+#endif
+
+ ++error_message_count;
+
+ if (errnum)
+ fprintf (stderr, ": %s", strerror (errnum));
+ putc ('\n', stderr);
+ fflush (stderr);
+ if (status)
+ exit (status);
+}
diff --git a/src/extern.h b/src/extern.h
new file mode 100644
index 0000000..0dfcc5e
--- a/dev/null
+++ b/src/extern.h
@@ -0,0 +1,198 @@
+/* extern.h - External declarations for cpio. Requires system.h.
+ Copyright (C) 1990, 1991, 1992, 2001 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+enum archive_format
+{
+ arf_unknown, arf_binary, arf_oldascii, arf_newascii, arf_crcascii,
+ arf_tar, arf_ustar, arf_hpoldascii, arf_hpbinary
+};
+extern enum archive_format archive_format;
+extern int reset_time_flag;
+extern int io_block_size;
+extern int create_dir_flag;
+extern int rename_flag;
+extern char *rename_batch_file;
+extern int table_flag;
+extern int unconditional_flag;
+extern int verbose_flag;
+extern int dot_flag;
+extern int link_flag;
+extern int retain_time_flag;
+extern int crc_i_flag;
+extern int append_flag;
+extern int swap_bytes_flag;
+extern int swap_halfwords_flag;
+extern int swapping_bytes;
+extern int swapping_halfwords;
+extern int set_owner_flag;
+extern uid_t set_owner;
+extern int set_group_flag;
+extern gid_t set_group;
+extern int no_chown_flag;
+extern int sparse_flag;
+extern int quiet_flag;
+extern int only_verify_crc_flag;
+extern int no_abs_paths_flag;
+
+extern int last_header_start;
+extern int copy_matching_files;
+extern int numeric_uid;
+extern char *pattern_file_name;
+extern char *new_media_message;
+extern char *new_media_message_with_number;
+extern char *new_media_message_after_number;
+extern int archive_des;
+extern char *archive_name;
+extern char *rsh_command_option;
+extern unsigned long crc;
+extern int delayed_seek_count;
+#ifdef DEBUG_CPIO
+extern int debug_flag;
+#endif
+
+extern char *input_buffer, *output_buffer;
+extern char *in_buff, *out_buff;
+extern long input_buffer_size;
+extern long input_size, output_size;
+#ifdef __GNUC__
+extern long long input_bytes, output_bytes;
+#else
+extern long input_bytes, output_bytes;
+#endif
+extern char zeros_512[];
+extern char *directory_name;
+extern char **save_patterns;
+extern int num_patterns;
+extern char name_end;
+extern char input_is_special;
+extern char output_is_special;
+extern char input_is_seekable;
+extern char output_is_seekable;
+extern int f_force_local;
+extern char *program_name;
+extern int (*xstat) ();
+extern void (*copy_function) ();
+
+#if __STDC__ || defined(__MSDOS__)
+# define P_(s) s
+#else
+# define P_(s) ()
+#endif
+
+/* copyin.c */
+void read_in_header P_((struct new_cpio_header *file_hdr, int in_des));
+void read_in_old_ascii P_((struct new_cpio_header *file_hdr, int in_des));
+void read_in_new_ascii P_((struct new_cpio_header *file_hdr, int in_des));
+void read_in_binary P_((struct new_cpio_header *file_hdr, int in_des));
+void swab_array P_((char *arg, int count));
+void process_copy_in P_((void));
+void long_format P_((struct new_cpio_header *file_hdr, char *link_name));
+void print_name_with_quoting P_((char *p));
+
+/* copyout.c */
+void write_out_header P_((struct new_cpio_header *file_hdr, int out_des));
+void process_copy_out P_((void));
+
+/* copypass.c */
+void process_copy_pass P_((void));
+int link_to_maj_min_ino P_((char *file_name, int st_dev_maj,
+ int st_dev_min, int st_ino));
+int link_to_name P_((char *link_name, char *link_target));
+
+/* dirname.c */
+char *dirname P_((char *path));
+
+/* error.c */
+void error P_((int status, int errnum, char *message, ...));
+
+/* filemode.c */
+void mode_string P_((unsigned int mode, char *str));
+
+/* idcache.c */
+#ifndef __MSDOS__
+char *getgroup ();
+char *getuser ();
+uid_t *getuidbyname ();
+gid_t *getgidbyname ();
+#endif
+
+/* main.c */
+void process_args P_((int argc, char *argv[]));
+void initialize_buffers P_((void));
+
+/* makepath.c */
+int make_path P_((char *argpath, int mode, int parent_mode,
+ uid_t owner, gid_t group, char *verbose_fmt_string));
+
+/* stripslash.c */
+void strip_trailing_slashes P_((char *path));
+
+/* tar.c */
+void write_out_tar_header P_((struct new_cpio_header *file_hdr, int out_des));
+int null_block P_((long *block, int size));
+void read_in_tar_header P_((struct new_cpio_header *file_hdr, int in_des));
+int otoa P_((char *s, unsigned long *n));
+int is_tar_header P_((char *buf));
+int is_tar_filename_too_long P_((char *name));
+
+/* userspec.c */
+#ifndef __MSDOS__
+char *parse_user_spec P_((char *name, uid_t *uid, gid_t *gid,
+ char **username, char **groupname));
+#endif
+
+/* util.c */
+void tape_empty_output_buffer P_((int out_des));
+void disk_empty_output_buffer P_((int out_des));
+void swahw_array P_((char *ptr, int count));
+void tape_buffered_write P_((char *in_buf, int out_des, long num_bytes));
+void tape_buffered_read P_((char *in_buf, int in_des, long num_bytes));
+int tape_buffered_peek P_((char *peek_buf, int in_des, int num_bytes));
+void tape_toss_input P_((int in_des, long num_bytes));
+void copy_files_tape_to_disk P_((int in_des, int out_des, long num_bytes));
+void copy_files_disk_to_tape P_((int in_des, int out_des, long num_bytes, char *filename));
+void copy_files_disk_to_disk P_((int in_des, int out_des, long num_bytes, char *filename));
+void warn_if_file_changed P_((char *file_name, unsigned long old_file_size,
+ unsigned long old_file_mtime));
+void create_all_directories P_((char *name));
+void prepare_append P_((int out_file_des));
+char *find_inode_file P_((unsigned long node_num,
+ unsigned long major_num, unsigned long minor_num));
+void add_inode P_((unsigned long node_num, char *file_name,
+ unsigned long major_num, unsigned long minor_num));
+int open_archive P_((char *file));
+void tape_offline P_((int tape_des));
+void get_next_reel P_((int tape_des));
+void set_new_media_message P_((char *message));
+#if defined(__MSDOS__) && !defined(__GNUC__)
+int chown P_((char *path, int owner, int group));
+#endif
+#ifdef __TURBOC__
+int utime P_((char *filename, struct utimbuf *utb));
+#endif
+#ifdef HPUX_CDF
+char *add_cdf_double_slashes P_((char *filename));
+#endif
+
+/* xmalloc.c */
+char *xmalloc P_((unsigned n));
+char *xrealloc P_((char *p, unsigned n));
+
+/* xstrdup.c */
+char *xstrdup P_((char *string));
+
+#define DISK_IO_BLOCK_SIZE (512)
diff --git a/src/filemode.c b/src/filemode.c
new file mode 100644
index 0000000..49f3d5f
--- a/dev/null
+++ b/src/filemode.c
@@ -0,0 +1,255 @@
+/* filemode.c -- make a string describing file modes
+ Copyright (C) 1985, 1990, 1993 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#if !S_IRUSR
+# if S_IREAD
+# define S_IRUSR S_IREAD
+# else
+# define S_IRUSR 00400
+# endif
+#endif
+
+#if !S_IWUSR
+# if S_IWRITE
+# define S_IWUSR S_IWRITE
+# else
+# define S_IWUSR 00200
+# endif
+#endif
+
+#if !S_IXUSR
+# if S_IEXEC
+# define S_IXUSR S_IEXEC
+# else
+# define S_IXUSR 00100
+# endif
+#endif
+
+#ifdef STAT_MACROS_BROKEN
+#undef S_ISBLK
+#undef S_ISCHR
+#undef S_ISDIR
+#undef S_ISFIFO
+#undef S_ISLNK
+#undef S_ISMPB
+#undef S_ISMPC
+#undef S_ISNWK
+#undef S_ISREG
+#undef S_ISSOCK
+#endif /* STAT_MACROS_BROKEN. */
+
+#if !defined(S_ISBLK) && defined(S_IFBLK)
+#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
+#endif
+#if !defined(S_ISCHR) && defined(S_IFCHR)
+#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
+#endif
+#if !defined(S_ISDIR) && defined(S_IFDIR)
+#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+#endif
+#if !defined(S_ISREG) && defined(S_IFREG)
+#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
+#endif
+#if !defined(S_ISFIFO) && defined(S_IFIFO)
+#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
+#endif
+#if !defined(S_ISLNK) && defined(S_IFLNK)
+#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
+#endif
+#if !defined(S_ISSOCK) && defined(S_IFSOCK)
+#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
+#endif
+#if !defined(S_ISMPB) && defined(S_IFMPB) /* V7 */
+#define S_ISMPB(m) (((m) & S_IFMT) == S_IFMPB)
+#define S_ISMPC(m) (((m) & S_IFMT) == S_IFMPC)
+#endif
+#if !defined(S_ISNWK) && defined(S_IFNWK) /* HP/UX */
+#define S_ISNWK(m) (((m) & S_IFMT) == S_IFNWK)
+#endif
+
+void mode_string ();
+static char ftypelet ();
+static void rwx ();
+static void setst ();
+
+/* filemodestring - fill in string STR with an ls-style ASCII
+ representation of the st_mode field of file stats block STATP.
+ 10 characters are stored in STR; no terminating null is added.
+ The characters stored in STR are:
+
+ 0 File type. 'd' for directory, 'c' for character
+ special, 'b' for block special, 'm' for multiplex,
+ 'l' for symbolic link, 's' for socket, 'p' for fifo,
+ '-' for regular, '?' for any other file type
+
+ 1 'r' if the owner may read, '-' otherwise.
+
+ 2 'w' if the owner may write, '-' otherwise.
+
+ 3 'x' if the owner may execute, 's' if the file is
+ set-user-id, '-' otherwise.
+ 'S' if the file is set-user-id, but the execute
+ bit isn't set.
+
+ 4 'r' if group members may read, '-' otherwise.
+
+ 5 'w' if group members may write, '-' otherwise.
+
+ 6 'x' if group members may execute, 's' if the file is
+ set-group-id, '-' otherwise.
+ 'S' if it is set-group-id but not executable.
+
+ 7 'r' if any user may read, '-' otherwise.
+
+ 8 'w' if any user may write, '-' otherwise.
+
+ 9 'x' if any user may execute, 't' if the file is "sticky"
+ (will be retained in swap space after execution), '-'
+ otherwise.
+ 'T' if the file is sticky but not executable. */
+
+void
+filemodestring (statp, str)
+ struct stat *statp;
+ char *str;
+{
+ mode_string (statp->st_mode, str);
+}
+
+/* Like filemodestring, but only the relevant part of the `struct stat'
+ is given as an argument. */
+
+void
+mode_string (mode, str)
+ unsigned short mode;
+ char *str;
+{
+ str[0] = ftypelet ((long) mode);
+ rwx ((mode & 0700) << 0, &str[1]);
+ rwx ((mode & 0070) << 3, &str[4]);
+ rwx ((mode & 0007) << 6, &str[7]);
+ setst (mode, str);
+}
+
+/* Return a character indicating the type of file described by
+ file mode BITS:
+ 'd' for directories
+ 'b' for block special files
+ 'c' for character special files
+ 'm' for multiplexor files
+ 'l' for symbolic links
+ 's' for sockets
+ 'p' for fifos
+ '-' for regular files
+ '?' for any other file type. */
+
+static char
+ftypelet (bits)
+ long bits;
+{
+#ifdef S_ISBLK
+ if (S_ISBLK (bits))
+ return 'b';
+#endif
+ if (S_ISCHR (bits))
+ return 'c';
+ if (S_ISDIR (bits))
+ return 'd';
+ if (S_ISREG (bits))
+ return '-';
+#ifdef S_ISFIFO
+ if (S_ISFIFO (bits))
+ return 'p';
+#endif
+#ifdef S_ISLNK
+ if (S_ISLNK (bits))
+ return 'l';
+#endif
+#ifdef S_ISSOCK
+ if (S_ISSOCK (bits))
+ return 's';
+#endif
+#ifdef S_ISMPC
+ if (S_ISMPC (bits))
+ return 'm';
+#endif
+#ifdef S_ISNWK
+ if (S_ISNWK (bits))
+ return 'n';
+#endif
+ return '?';
+}
+
+/* Look at read, write, and execute bits in BITS and set
+ flags in CHARS accordingly. */
+
+static void
+rwx (bits, chars)
+ unsigned short bits;
+ char *chars;
+{
+ chars[0] = (bits & S_IRUSR) ? 'r' : '-';
+ chars[1] = (bits & S_IWUSR) ? 'w' : '-';
+ chars[2] = (bits & S_IXUSR) ? 'x' : '-';
+}
+
+/* Set the 's' and 't' flags in file attributes string CHARS,
+ according to the file mode BITS. */
+
+static void
+setst (bits, chars)
+ unsigned short bits;
+ char *chars;
+{
+#ifdef S_ISUID
+ if (bits & S_ISUID)
+ {
+ if (chars[3] != 'x')
+ /* Set-uid, but not executable by owner. */
+ chars[3] = 'S';
+ else
+ chars[3] = 's';
+ }
+#endif
+#ifdef S_ISGID
+ if (bits & S_ISGID)
+ {
+ if (chars[6] != 'x')
+ /* Set-gid, but not executable by group. */
+ chars[6] = 'S';
+ else
+ chars[6] = 's';
+ }
+#endif
+#ifdef S_ISVTX
+ if (bits & S_ISVTX)
+ {
+ if (chars[9] != 'x')
+ /* Sticky, but not executable by others. */
+ chars[9] = 'T';
+ else
+ chars[9] = 't';
+ }
+#endif
+}
diff --git a/src/filetypes.h b/src/filetypes.h
new file mode 100644
index 0000000..9f785a1
--- a/dev/null
+++ b/src/filetypes.h
@@ -0,0 +1,84 @@
+/* filetypes.h - deal with POSIX annoyances
+ Copyright (C) 1991 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+/* Include sys/types.h and sys/stat.h before this file. */
+
+#ifndef S_ISREG /* Doesn't have POSIX.1 stat stuff. */
+#define mode_t unsigned short
+#endif
+
+/* Define the POSIX macros for systems that lack them. */
+#if !defined(S_ISBLK) && defined(S_IFBLK)
+#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
+#endif
+#if !defined(S_ISCHR) && defined(S_IFCHR)
+#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
+#endif
+#if !defined(S_ISDIR) && defined(S_IFDIR)
+#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+#endif
+#if !defined(S_ISREG) && defined(S_IFREG)
+#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
+#endif
+#if !defined(S_ISFIFO) && defined(S_IFIFO)
+#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
+#endif
+#if !defined(S_ISLNK) && defined(S_IFLNK)
+#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
+#endif
+#if !defined(S_ISSOCK) && defined(S_IFSOCK)
+#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
+#endif
+#if !defined(S_ISNWK) && defined(S_IFNWK) /* HP/UX network special */
+#define S_ISNWK(m) (((m) & S_IFMT) == S_IFNWK)
+#endif
+
+/* Define the file type bits used in cpio archives.
+ They have the same values as the S_IF bits in traditional Unix. */
+
+#define CP_IFMT 0170000 /* Mask for all file type bits. */
+
+#if defined(S_ISBLK)
+#define CP_IFBLK 0060000
+#endif
+#if defined(S_ISCHR)
+#define CP_IFCHR 0020000
+#endif
+#if defined(S_ISDIR)
+#define CP_IFDIR 0040000
+#endif
+#if defined(S_ISREG)
+#define CP_IFREG 0100000
+#endif
+#if defined(S_ISFIFO)
+#define CP_IFIFO 0010000
+#endif
+#if defined(S_ISLNK)
+#define CP_IFLNK 0120000
+#endif
+#if defined(S_ISSOCK)
+#define CP_IFSOCK 0140000
+#endif
+#if defined(S_ISNWK)
+#define CP_IFNWK 0110000
+#endif
+
+#ifndef S_ISLNK
+#define lstat stat
+#endif
+int lstat ();
+int stat ();
diff --git a/src/fnmatch.c b/src/fnmatch.c
new file mode 100644
index 0000000..c205e2d
--- a/dev/null
+++ b/src/fnmatch.c
@@ -0,0 +1,200 @@
+/* Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc.,
+59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <fnmatch.h>
+#include <ctype.h>
+
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+ actually compiling the library itself. This code is part of the GNU C
+ Library, but also included in many other GNU distributions. Compiling
+ and linking in this code is a waste when using the GNU C library
+ (especially if it is a shared library). Rather than having every GNU
+ program understand `configure --with-gnu-libc' and omit the object files,
+ it is simpler to just do this in the source for each such file. */
+
+#if defined (_LIBC) || !defined (__GNU_LIBRARY__)
+
+
+#ifndef errno
+extern int errno;
+#endif
+
+/* Match STRING against the filename pattern PATTERN, returning zero if
+ it matches, nonzero if not. */
+int
+fnmatch (pattern, string, flags)
+ const char *pattern;
+ const char *string;
+ int flags;
+{
+ register const char *p = pattern, *n = string;
+ register char c;
+
+/* Note that this evalutes C many times. */
+#define FOLD(c) ((flags & FNM_CASEFOLD) && isupper (c) ? tolower (c) : (c))
+
+ while ((c = *p++) != '\0')
+ {
+ c = FOLD (c);
+
+ switch (c)
+ {
+ case '?':
+ if (*n == '\0')
+ return FNM_NOMATCH;
+ else if ((flags & FNM_FILE_NAME) && *n == '/')
+ return FNM_NOMATCH;
+ else if ((flags & FNM_PERIOD) && *n == '.' &&
+ (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/')))
+ return FNM_NOMATCH;
+ break;
+
+ case '\\':
+ if (!(flags & FNM_NOESCAPE))
+ {
+ c = *p++;
+ c = FOLD (c);
+ }
+ if (FOLD (*n) != c)
+ return FNM_NOMATCH;
+ break;
+
+ case '*':
+ if ((flags & FNM_PERIOD) && *n == '.' &&
+ (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/')))
+ return FNM_NOMATCH;
+
+ for (c = *p++; c == '?' || c == '*'; c = *p++, ++n)
+ if (((flags & FNM_FILE_NAME) && *n == '/') ||
+ (c == '?' && *n == '\0'))
+ return FNM_NOMATCH;
+
+ if (c == '\0')
+ return 0;
+
+ {
+ char c1 = (!(flags & FNM_NOESCAPE) && c == '\\') ? *p : c;
+ c1 = FOLD (c1);
+ for (--p; *n != '\0'; ++n)
+ if ((c == '[' || FOLD (*n) == c1) &&
+ fnmatch (p, n, flags & ~FNM_PERIOD) == 0)
+ return 0;
+ return FNM_NOMATCH;
+ }
+
+ case '[':
+ {
+ /* Nonzero if the sense of the character class is inverted. */
+ register int not;
+
+ if (*n == '\0')
+ return FNM_NOMATCH;
+
+ if ((flags & FNM_PERIOD) && *n == '.' &&
+ (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/')))
+ return FNM_NOMATCH;
+
+ not = (*p == '!' || *p == '^');
+ if (not)
+ ++p;
+
+ c = *p++;
+ for (;;)
+ {
+ register char cstart = c, cend = c;
+
+ if (!(flags & FNM_NOESCAPE) && c == '\\')
+ cstart = cend = *p++;
+
+ cstart = cend = FOLD (cstart);
+
+ if (c == '\0')
+ /* [ (unterminated) loses. */
+ return FNM_NOMATCH;
+
+ c = *p++;
+ c = FOLD (c);
+
+ if ((flags & FNM_FILE_NAME) && c == '/')
+ /* [/] can never match. */
+ return FNM_NOMATCH;
+
+ if (c == '-' && *p != ']')
+ {
+ cend = *p++;
+ if (!(flags & FNM_NOESCAPE) && cend == '\\')
+ cend = *p++;
+ if (cend == '\0')
+ return FNM_NOMATCH;
+ cend = FOLD (cend);
+
+ c = *p++;
+ }
+
+ if (FOLD (*n) >= cstart && FOLD (*n) <= cend)
+ goto matched;
+
+ if (c == ']')
+ break;
+ }
+ if (!not)
+ return FNM_NOMATCH;
+ break;
+
+ matched:;
+ /* Skip the rest of the [...] that already matched. */
+ while (c != ']')
+ {
+ if (c == '\0')
+ /* [... (unterminated) loses. */
+ return FNM_NOMATCH;
+
+ c = *p++;
+ if (!(flags & FNM_NOESCAPE) && c == '\\')
+ /* XXX 1003.2d11 is unclear if this is right. */
+ ++p;
+ }
+ if (not)
+ return FNM_NOMATCH;
+ }
+ break;
+
+ default:
+ if (c != FOLD (*n))
+ return FNM_NOMATCH;
+ }
+
+ ++n;
+ }
+
+ if (*n == '\0')
+ return 0;
+
+ if ((flags & FNM_LEADING_DIR) && *n == '/')
+ /* The FNM_LEADING_DIR flag says that "foo*" matches "foobar/frobozz". */
+ return 0;
+
+ return FNM_NOMATCH;
+}
+
+#endif /* _LIBC or not __GNU_LIBRARY__. */
diff --git a/src/fnmatch.h b/src/fnmatch.h
new file mode 100644
index 0000000..578aca1
--- a/dev/null
+++ b/src/fnmatch.h
@@ -0,0 +1,67 @@
+/* Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc.,
+59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef _FNMATCH_H
+
+#define _FNMATCH_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined (__cplusplus) || (defined (__STDC__) && __STDC__)
+#undef __P
+#define __P(protos) protos
+#else /* Not C++ or ANSI C. */
+#undef __P
+#define __P(protos) ()
+/* We can get away without defining `const' here only because in this file
+ it is used only inside the prototype for `fnmatch', which is elided in
+ non-ANSI C where `const' is problematical. */
+#endif /* C++ or ANSI C. */
+
+
+/* We #undef these before defining them because some losing systems
+ (HP-UX A.08.07 for example) define these in <unistd.h>. */
+#undef FNM_PATHNAME
+#undef FNM_NOESCAPE
+#undef FNM_PERIOD
+
+/* Bits set in the FLAGS argument to `fnmatch'. */
+#define FNM_PATHNAME (1 << 0) /* No wildcard can ever match `/'. */
+#define FNM_NOESCAPE (1 << 1) /* Backslashes don't quote special chars. */
+#define FNM_PERIOD (1 << 2) /* Leading `.' is matched only explicitly. */
+
+#if !defined (_POSIX_C_SOURCE) || _POSIX_C_SOURCE < 2 || defined (_GNU_SOURCE)
+#define FNM_FILE_NAME FNM_PATHNAME /* Preferred GNU name. */
+#define FNM_LEADING_DIR (1 << 3) /* Ignore `/...' after a match. */
+#define FNM_CASEFOLD (1 << 4) /* Compare without regard to case. */
+#endif
+
+/* Value returned by `fnmatch' if STRING does not match PATTERN. */
+#define FNM_NOMATCH 1
+
+/* Match STRING against the filename pattern PATTERN,
+ returning zero if it matches, FNM_NOMATCH if not. */
+extern int fnmatch __P ((const char *__pattern, const char *__string,
+ int __flags));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* fnmatch.h */
diff --git a/src/getopt.c b/src/getopt.c
new file mode 100644
index 0000000..c4701b0
--- a/dev/null
+++ b/src/getopt.c
@@ -0,0 +1,765 @@
+/* Getopt for GNU.
+ NOTE: getopt is now part of the C library, so if you don't know what
+ "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu
+ before changing it!
+
+ Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95
+ Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>.
+ Ditto for AIX 3.2 and <stdlib.h>. */
+#ifndef _NO_PROTO
+#define _NO_PROTO
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if !defined (__STDC__) || !__STDC__
+/* This is a separate conditional since some stdc systems
+ reject `defined (const)'. */
+#ifndef const
+#define const
+#endif
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+ actually compiling the library itself. This code is part of the GNU C
+ Library, but also included in many other GNU distributions. Compiling
+ and linking in this code is a waste when using the GNU C library
+ (especially if it is a shared library). Rather than having every GNU
+ program understand `configure --with-gnu-libc' and omit the object files,
+ it is simpler to just do this in the source for each such file. */
+
+#if defined (_LIBC) || !defined (__GNU_LIBRARY__)
+
+
+/* This needs to come after some library #include
+ to get __GNU_LIBRARY__ defined. */
+#ifdef __GNU_LIBRARY__
+/* Don't include stdlib.h for non-GNU C libraries because some of them
+ contain conflicting prototypes for getopt. */
+#include <stdlib.h>
+#endif /* GNU C library. */
+
+#ifndef _
+/* This is for other GNU distributions with internationalized messages.
+ When compiling libc, the _ macro is predefined. */
+#ifdef HAVE_LIBINTL_H
+# include <libintl.h>
+# define _(msgid) gettext (msgid)
+#else
+# define _(msgid) (msgid)
+#endif
+#endif
+
+/* This version of `getopt' appears to the caller like standard Unix `getopt'
+ but it behaves differently for the user, since it allows the user
+ to intersperse the options with the other arguments.
+
+ As `getopt' works, it permutes the elements of ARGV so that,
+ when it is done, all the options precede everything else. Thus
+ all application programs are extended to handle flexible argument order.
+
+ Setting the environment variable POSIXLY_CORRECT disables permutation.
+ Then the behavior is completely standard.
+
+ GNU application programs can use a third alternative mode in which
+ they can distinguish the relative order of options and other arguments. */
+
+#include "getopt.h"
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+char *optarg = NULL;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns EOF, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+/* XXX 1003.2 says this must be 1 before any call. */
+int optind = 0;
+
+/* The next char to be scanned in the option-element
+ in which the last option character we returned was found.
+ This allows us to pick up the scan where we left off.
+
+ If this is zero, or a null string, it means resume the scan
+ by advancing to the next ARGV-element. */
+
+static char *nextchar;
+
+/* Callers store zero here to inhibit the error message
+ for unrecognized options. */
+
+int opterr = 1;
+
+/* Set to an option character which was unrecognized.
+ This must be initialized on some systems to avoid linking in the
+ system's own getopt implementation. */
+
+int optopt = '?';
+
+/* Describe how to deal with options that follow non-option ARGV-elements.
+
+ If the caller did not specify anything,
+ the default is REQUIRE_ORDER if the environment variable
+ POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+ REQUIRE_ORDER means don't recognize them as options;
+ stop option processing when the first non-option is seen.
+ This is what Unix does.
+ This mode of operation is selected by either setting the environment
+ variable POSIXLY_CORRECT, or using `+' as the first character
+ of the list of option characters.
+
+ PERMUTE is the default. We permute the contents of ARGV as we scan,
+ so that eventually all the non-options are at the end. This allows options
+ to be given in any order, even with programs that were not written to
+ expect this.
+
+ RETURN_IN_ORDER is an option available to programs that were written
+ to expect options and other ARGV-elements in any order and that care about
+ the ordering of the two. We describe each non-option ARGV-element
+ as if it were the argument of an option with character code 1.
+ Using `-' as the first character of the list of option characters
+ selects this mode of operation.
+
+ The special argument `--' forces an end of option-scanning regardless
+ of the value of `ordering'. In the case of RETURN_IN_ORDER, only
+ `--' can cause `getopt' to return EOF with `optind' != ARGC. */
+
+static enum
+{
+ REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
+} ordering;
+
+/* Value of POSIXLY_CORRECT environment variable. */
+static char *posixly_correct;
+
+#ifdef __GNU_LIBRARY__
+/* We want to avoid inclusion of string.h with non-GNU libraries
+ because there are many ways it can cause trouble.
+ On some systems, it contains special magic macros that don't work
+ in GCC. */
+#include <string.h>
+#define my_index strchr
+#else
+
+/* Avoid depending on library functions or files
+ whose names are inconsistent. */
+
+char *getenv ();
+
+static char *
+my_index (str, chr)
+ const char *str;
+ int chr;
+{
+ while (*str)
+ {
+ if (*str == chr)
+ return (char *) str;
+ str++;
+ }
+ return 0;
+}
+
+/* If using GCC, we can safely declare strlen this way.
+ If not using GCC, it is ok not to declare it. */
+#ifdef __GNUC__
+/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h.
+ That was relevant to code that was here before. */
+#if !defined (__STDC__) || !__STDC__
+/* gcc with -traditional declares the built-in strlen to return int,
+ and has done so at least since version 2.4.5. -- rms. */
+extern int strlen (const char *);
+#endif /* not __STDC__ */
+#endif /* __GNUC__ */
+
+#endif /* not __GNU_LIBRARY__ */
+
+/* Handle permutation of arguments. */
+
+/* Describe the part of ARGV that contains non-options that have
+ been skipped. `first_nonopt' is the index in ARGV of the first of them;
+ `last_nonopt' is the index after the last of them. */
+
+static int first_nonopt;
+static int last_nonopt;
+
+/* Exchange two adjacent subsequences of ARGV.
+ One subsequence is elements [first_nonopt,last_nonopt)
+ which contains all the non-options that have been skipped so far.
+ The other is elements [last_nonopt,optind), which contains all
+ the options processed since those non-options were skipped.
+
+ `first_nonopt' and `last_nonopt' are relocated so that they describe
+ the new indices of the non-options in ARGV after they are moved. */
+
+static void
+exchange (argv)
+ char **argv;
+{
+ int bottom = first_nonopt;
+ int middle = last_nonopt;
+ int top = optind;
+ char *tem;
+
+ /* Exchange the shorter segment with the far end of the longer segment.
+ That puts the shorter segment into the right place.
+ It leaves the longer segment in the right place overall,
+ but it consists of two parts that need to be swapped next. */
+
+ while (top > middle && middle > bottom)
+ {
+ if (top - middle > middle - bottom)
+ {
+ /* Bottom segment is the short one. */
+ int len = middle - bottom;
+ register int i;
+
+ /* Swap it with the top part of the top segment. */
+ for (i = 0; i < len; i++)
+ {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[top - (middle - bottom) + i];
+ argv[top - (middle - bottom) + i] = tem;
+ }
+ /* Exclude the moved bottom segment from further swapping. */
+ top -= len;
+ }
+ else
+ {
+ /* Top segment is the short one. */
+ int len = top - middle;
+ register int i;
+
+ /* Swap it with the bottom part of the bottom segment. */
+ for (i = 0; i < len; i++)
+ {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[middle + i];
+ argv[middle + i] = tem;
+ }
+ /* Exclude the moved top segment from further swapping. */
+ bottom += len;
+ }
+ }
+
+ /* Update records for the slots the non-options now occupy. */
+
+ first_nonopt += (optind - last_nonopt);
+ last_nonopt = optind;
+}
+
+/* Initialize the internal data when the first call is made. */
+
+static const char *
+_getopt_initialize (optstring)
+ const char *optstring;
+{
+ /* Start processing options with ARGV-element 1 (since ARGV-element 0
+ is the program name); the sequence of previously skipped
+ non-option ARGV-elements is empty. */
+
+ first_nonopt = last_nonopt = optind = 1;
+
+ nextchar = NULL;
+
+ posixly_correct = getenv ("POSIXLY_CORRECT");
+
+ /* Determine how to handle the ordering of options and nonoptions. */
+
+ if (optstring[0] == '-')
+ {
+ ordering = RETURN_IN_ORDER;
+ ++optstring;
+ }
+ else if (optstring[0] == '+')
+ {
+ ordering = REQUIRE_ORDER;
+ ++optstring;
+ }
+ else if (posixly_correct != NULL)
+ ordering = REQUIRE_ORDER;
+ else
+ ordering = PERMUTE;
+
+ return optstring;
+}
+
+/* Scan elements of ARGV (whose length is ARGC) for option characters
+ given in OPTSTRING.
+
+ If an element of ARGV starts with '-', and is not exactly "-" or "--",
+ then it is an option element. The characters of this element
+ (aside from the initial '-') are option characters. If `getopt'
+ is called repeatedly, it returns successively each of the option characters
+ from each of the option elements.
+
+ If `getopt' finds another option character, it returns that character,
+ updating `optind' and `nextchar' so that the next call to `getopt' can
+ resume the scan with the following option character or ARGV-element.
+
+ If there are no more option characters, `getopt' returns `EOF'.
+ Then `optind' is the index in ARGV of the first ARGV-element
+ that is not an option. (The ARGV-elements have been permuted
+ so that those that are not options now come last.)
+
+ OPTSTRING is a string containing the legitimate option characters.
+ If an option character is seen that is not listed in OPTSTRING,
+ return '?' after printing an error message. If you set `opterr' to
+ zero, the error message is suppressed but we still return '?'.
+
+ If a char in OPTSTRING is followed by a colon, that means it wants an arg,
+ so the following text in the same ARGV-element, or the text of the following
+ ARGV-element, is returned in `optarg'. Two colons mean an option that
+ wants an optional arg; if there is text in the current ARGV-element,
+ it is returned in `optarg', otherwise `optarg' is set to zero.
+
+ If OPTSTRING starts with `-' or `+', it requests different methods of
+ handling the non-option ARGV-elements.
+ See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
+
+ Long-named options begin with `--' instead of `-'.
+ Their names may be abbreviated as long as the abbreviation is unique
+ or is an exact match for some defined option. If they have an
+ argument, it follows the option name in the same ARGV-element, separated
+ from the option name by a `=', or else the in next ARGV-element.
+ When `getopt' finds a long-named option, it returns 0 if that option's
+ `flag' field is nonzero, the value of the option's `val' field
+ if the `flag' field is zero.
+
+ The elements of ARGV aren't really const, because we permute them.
+ But we pretend they're const in the prototype to be compatible
+ with other systems.
+
+ LONGOPTS is a vector of `struct option' terminated by an
+ element containing a name which is zero.
+
+ LONGIND returns the index in LONGOPT of the long-named option found.
+ It is only valid when a long-named option has been found by the most
+ recent call.
+
+ If LONG_ONLY is nonzero, '-' as well as '--' can introduce
+ long-named options. */
+
+int
+_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
+ int argc;
+ char *const *argv;
+ const char *optstring;
+ const struct option *longopts;
+ int *longind;
+ int long_only;
+{
+ optarg = NULL;
+
+ if (optind == 0)
+ {
+ optstring = _getopt_initialize (optstring);
+ optind = 1; /* Don't scan ARGV[0], the program name. */
+ }
+
+ if (nextchar == NULL || *nextchar == '\0')
+ {
+ /* Advance to the next ARGV-element. */
+
+ if (ordering == PERMUTE)
+ {
+ /* If we have just processed some options following some non-options,
+ exchange them so that the options come first. */
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange ((char **) argv);
+ else if (last_nonopt != optind)
+ first_nonopt = optind;
+
+ /* Skip any additional non-options
+ and extend the range of non-options previously skipped. */
+
+ while (optind < argc
+ && (argv[optind][0] != '-' || argv[optind][1] == '\0'))
+ optind++;
+ last_nonopt = optind;
+ }
+
+ /* The special ARGV-element `--' means premature end of options.
+ Skip it like a null option,
+ then exchange with previous non-options as if it were an option,
+ then skip everything else like a non-option. */
+
+ if (optind != argc && !strcmp (argv[optind], "--"))
+ {
+ optind++;
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange ((char **) argv);
+ else if (first_nonopt == last_nonopt)
+ first_nonopt = optind;
+ last_nonopt = argc;
+
+ optind = argc;
+ }
+
+ /* If we have done all the ARGV-elements, stop the scan
+ and back over any non-options that we skipped and permuted. */
+
+ if (optind == argc)
+ {
+ /* Set the next-arg-index to point at the non-options
+ that we previously skipped, so the caller will digest them. */
+ if (first_nonopt != last_nonopt)
+ optind = first_nonopt;
+ return EOF;
+ }
+
+ /* If we have come to a non-option and did not permute it,
+ either stop the scan or describe it to the caller and pass it by. */
+
+ if ((argv[optind][0] != '-' || argv[optind][1] == '\0'))
+ {
+ if (ordering == REQUIRE_ORDER)
+ return EOF;
+ optarg = argv[optind++];
+ return 1;
+ }
+
+ /* We have found another option-ARGV-element.
+ Skip the initial punctuation. */
+
+ nextchar = (argv[optind] + 1
+ + (longopts != NULL && argv[optind][1] == '-'));
+ }
+
+ /* Decode the current option-ARGV-element. */
+
+ /* Check whether the ARGV-element is a long option.
+
+ If long_only and the ARGV-element has the form "-f", where f is
+ a valid short option, don't consider it an abbreviated form of
+ a long option that starts with f. Otherwise there would be no
+ way to give the -f short option.
+
+ On the other hand, if there's a long option "fubar" and
+ the ARGV-element is "-fu", do consider that an abbreviation of
+ the long option, just like "--fu", and not "-f" with arg "u".
+
+ This distinction seems to be the most useful approach. */
+
+ if (longopts != NULL
+ && (argv[optind][1] == '-'
+ || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1])))))
+ {
+ char *nameend;
+ const struct option *p;
+ const struct option *pfound = NULL;
+ int exact = 0;
+ int ambig = 0;
+ int indfound;
+ int option_index;
+
+ for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
+ /* Do nothing. */ ;
+
+ /* Test all long options for either exact match
+ or abbreviated matches. */
+ for (p = longopts, option_index = 0; p->name; p++, option_index++)
+ if (!strncmp (p->name, nextchar, nameend - nextchar))
+ {
+ if (nameend - nextchar == strlen (p->name))
+ {
+ /* Exact match found. */
+ pfound = p;
+ indfound = option_index;
+ exact = 1;
+ break;
+ }
+ else if (pfound == NULL)
+ {
+ /* First nonexact match found. */
+ pfound = p;
+ indfound = option_index;
+ }
+ else
+ /* Second or later nonexact match found. */
+ ambig = 1;
+ }
+
+ if (ambig && !exact)
+ {
+ if (opterr)
+ fprintf (stderr, _("%s: option `%s' is ambiguous\n"),
+ argv[0], argv[optind]);
+ nextchar += strlen (nextchar);
+ optind++;
+ return '?';
+ }
+
+ if (pfound != NULL)
+ {
+ option_index = indfound;
+ optind++;
+ if (*nameend)
+ {
+ /* Don't test has_arg with >, because some C compilers don't
+ allow it to be used on enums. */
+ if (pfound->has_arg)
+ optarg = nameend + 1;
+ else
+ {
+ if (opterr)
+ if (argv[optind - 1][1] == '-')
+ /* --option */
+ fprintf (stderr,
+ _("%s: option `--%s' doesn't allow an argument\n"),
+ argv[0], pfound->name);
+ else
+ /* +option or -option */
+ fprintf (stderr,
+ _("%s: option `%c%s' doesn't allow an argument\n"),
+ argv[0], argv[optind - 1][0], pfound->name);
+
+ nextchar += strlen (nextchar);
+ return '?';
+ }
+ }
+ else if (pfound->has_arg == 1)
+ {
+ if (optind < argc)
+ optarg = argv[optind++];
+ else
+ {
+ if (opterr)
+ fprintf (stderr,
+ _("%s: option `%s' requires an argument\n"),
+ argv[0], argv[optind - 1]);
+ nextchar += strlen (nextchar);
+ return optstring[0] == ':' ? ':' : '?';
+ }
+ }
+ nextchar += strlen (nextchar);
+ if (longind != NULL)
+ *longind = option_index;
+ if (pfound->flag)
+ {
+ *(pfound->flag) = pfound->val;
+ return 0;
+ }
+ return pfound->val;
+ }
+
+ /* Can't find it as a long option. If this is not getopt_long_only,
+ or the option starts with '--' or is not a valid short
+ option, then it's an error.
+ Otherwise interpret it as a short option. */
+ if (!long_only || argv[optind][1] == '-'
+ || my_index (optstring, *nextchar) == NULL)
+ {
+ if (opterr)
+ {
+ if (argv[optind][1] == '-')
+ /* --option */
+ fprintf (stderr, _("%s: unrecognized option `--%s'\n"),
+ argv[0], nextchar);
+ else
+ /* +option or -option */
+ fprintf (stderr, _("%s: unrecognized option `%c%s'\n"),
+ argv[0], argv[optind][0], nextchar);
+ }
+ nextchar = (char *) "";
+ optind++;
+ return '?';
+ }
+ }
+
+ /* Look at and handle the next short option-character. */
+
+ {
+ char c = *nextchar++;
+ char *temp = my_index (optstring, c);
+
+ /* Increment `optind' when we start to process its last character. */
+ if (*nextchar == '\0')
+ ++optind;
+
+ if (temp == NULL || c == ':')
+ {
+ if (opterr)
+ {
+ if (posixly_correct)
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr, _("%s: illegal option -- %c\n"),
+ argv[0], c);
+ else
+ fprintf (stderr, _("%s: invalid option -- %c\n"),
+ argv[0], c);
+ }
+ optopt = c;
+ return '?';
+ }
+ if (temp[1] == ':')
+ {
+ if (temp[2] == ':')
+ {
+ /* This is an option that accepts an argument optionally. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ optind++;
+ }
+ else
+ optarg = NULL;
+ nextchar = NULL;
+ }
+ else
+ {
+ /* This is an option that requires an argument. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ /* If we end this ARGV-element by taking the rest as an arg,
+ we must advance to the next element now. */
+ optind++;
+ }
+ else if (optind == argc)
+ {
+ if (opterr)
+ {
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr,
+ _("%s: option requires an argument -- %c\n"),
+ argv[0], c);
+ }
+ optopt = c;
+ if (optstring[0] == ':')
+ c = ':';
+ else
+ c = '?';
+ }
+ else
+ /* We already incremented `optind' once;
+ increment it again when taking next ARGV-elt as argument. */
+ optarg = argv[optind++];
+ nextchar = NULL;
+ }
+ }
+ return c;
+ }
+}
+
+int
+getopt (argc, argv, optstring)
+ int argc;
+ char *const *argv;
+ const char *optstring;
+{
+ return _getopt_internal (argc, argv, optstring,
+ (const struct option *) 0,
+ (int *) 0,
+ 0);
+}
+
+#endif /* _LIBC or not __GNU_LIBRARY__. */
+
+#ifdef TEST
+
+/* Compile with -DTEST to make an executable for use in testing
+ the above definition of `getopt'. */
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+
+ c = getopt (argc, argv, "abc:d:0123456789");
+ if (c == EOF)
+ break;
+
+ switch (c)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf ("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf ("option %c\n", c);
+ break;
+
+ case 'a':
+ printf ("option a\n");
+ break;
+
+ case 'b':
+ printf ("option b\n");
+ break;
+
+ case 'c':
+ printf ("option c with value `%s'\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ printf ("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf ("%s ", argv[optind++]);
+ printf ("\n");
+ }
+
+ exit (0);
+}
+
+#endif /* TEST */
diff --git a/src/getopt.h b/src/getopt.h
new file mode 100644
index 0000000..2cd4d1e
--- a/dev/null
+++ b/src/getopt.h
@@ -0,0 +1,129 @@
+/* Declarations for getopt.
+ Copyright (C) 1989, 90, 91, 92, 93, 94 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef _GETOPT_H
+#define _GETOPT_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+extern char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns EOF, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+extern int optind;
+
+/* Callers store zero here to inhibit the error message `getopt' prints
+ for unrecognized options. */
+
+extern int opterr;
+
+/* Set to an option character which was unrecognized. */
+
+extern int optopt;
+
+/* Describe the long-named options requested by the application.
+ The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
+ of `struct option' terminated by an element containing a name which is
+ zero.
+
+ The field `has_arg' is:
+ no_argument (or 0) if the option does not take an argument,
+ required_argument (or 1) if the option requires an argument,
+ optional_argument (or 2) if the option takes an optional argument.
+
+ If the field `flag' is not NULL, it points to a variable that is set
+ to the value given in the field `val' when the option is found, but
+ left unchanged if the option is not found.
+
+ To have a long-named option do something other than set an `int' to
+ a compiled-in constant, such as set a value from `optarg', set the
+ option's `flag' field to zero and its `val' field to a nonzero
+ value (the equivalent single-letter option character, if there is
+ one). For long options that have a zero `flag' field, `getopt'
+ returns the contents of the `val' field. */
+
+struct option
+{
+#if defined (__STDC__) && __STDC__
+ const char *name;
+#else
+ char *name;
+#endif
+ /* has_arg can't be an enum because some compilers complain about
+ type mismatches in all the code that assumes it is an int. */
+ int has_arg;
+ int *flag;
+ int val;
+};
+
+/* Names for the values of the `has_arg' field of `struct option'. */
+
+#define no_argument 0
+#define required_argument 1
+#define optional_argument 2
+
+#if defined (__STDC__) && __STDC__
+#ifdef __GNU_LIBRARY__
+/* Many other libraries have conflicting prototypes for getopt, with
+ differences in the consts, in stdlib.h. To avoid compilation
+ errors, only prototype getopt for the GNU C library. */
+extern int getopt (int argc, char *const *argv, const char *shortopts);
+#else /* not __GNU_LIBRARY__ */
+extern int getopt ();
+#endif /* __GNU_LIBRARY__ */
+extern int getopt_long (int argc, char *const *argv, const char *shortopts,
+ const struct option *longopts, int *longind);
+extern int getopt_long_only (int argc, char *const *argv,
+ const char *shortopts,
+ const struct option *longopts, int *longind);
+
+/* Internal only. Users should not call this directly. */
+extern int _getopt_internal (int argc, char *const *argv,
+ const char *shortopts,
+ const struct option *longopts, int *longind,
+ int long_only);
+#else /* not __STDC__ */
+extern int getopt ();
+extern int getopt_long ();
+extern int getopt_long_only ();
+
+extern int _getopt_internal ();
+#endif /* __STDC__ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _GETOPT_H */
diff --git a/src/getopt1.c b/src/getopt1.c
new file mode 100644
index 0000000..52c5321
--- a/dev/null
+++ b/src/getopt1.c
@@ -0,0 +1,180 @@
+/* getopt_long and getopt_long_only entry points for GNU getopt.
+ Copyright (C) 1987, 88, 89, 90, 91, 92, 1993, 1994
+ Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "getopt.h"
+
+#if !defined (__STDC__) || !__STDC__
+/* This is a separate conditional since some stdc systems
+ reject `defined (const)'. */
+#ifndef const
+#define const
+#endif
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+ actually compiling the library itself. This code is part of the GNU C
+ Library, but also included in many other GNU distributions. Compiling
+ and linking in this code is a waste when using the GNU C library
+ (especially if it is a shared library). Rather than having every GNU
+ program understand `configure --with-gnu-libc' and omit the object files,
+ it is simpler to just do this in the source for each such file. */
+
+#if defined (_LIBC) || !defined (__GNU_LIBRARY__)
+
+
+/* This needs to come after some library #include
+ to get __GNU_LIBRARY__ defined. */
+#ifdef __GNU_LIBRARY__
+#include <stdlib.h>
+#else
+char *getenv ();
+#endif
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+int
+getopt_long (argc, argv, options, long_options, opt_index)
+ int argc;
+ char *const *argv;
+ const char *options;
+ const struct option *long_options;
+ int *opt_index;
+{
+ return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
+}
+
+/* Like getopt_long, but '-' as well as '--' can indicate a long option.
+ If an option that starts with '-' (not '--') doesn't match a long option,
+ but does match a short option, it is parsed as a short option
+ instead. */
+
+int
+getopt_long_only (argc, argv, options, long_options, opt_index)
+ int argc;
+ char *const *argv;
+ const char *options;
+ const struct option *long_options;
+ int *opt_index;
+{
+ return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
+}
+
+
+#endif /* _LIBC or not __GNU_LIBRARY__. */
+
+#ifdef TEST
+
+#include <stdio.h>
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+ int option_index = 0;
+ static struct option long_options[] =
+ {
+ {"add", 1, 0, 0},
+ {"append", 0, 0, 0},
+ {"delete", 1, 0, 0},
+ {"verbose", 0, 0, 0},
+ {"create", 0, 0, 0},
+ {"file", 1, 0, 0},
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long (argc, argv, "abc:d:0123456789",
+ long_options, &option_index);
+ if (c == EOF)
+ break;
+
+ switch (c)
+ {
+ case 0:
+ printf ("option %s", long_options[option_index].name);
+ if (optarg)
+ printf (" with arg %s", optarg);
+ printf ("\n");
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf ("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf ("option %c\n", c);
+ break;
+
+ case 'a':
+ printf ("option a\n");
+ break;
+
+ case 'b':
+ printf ("option b\n");
+ break;
+
+ case 'c':
+ printf ("option c with value `%s'\n", optarg);
+ break;
+
+ case 'd':
+ printf ("option d with value `%s'\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ printf ("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf ("%s ", argv[optind++]);
+ printf ("\n");
+ }
+
+ exit (0);
+}
+
+#endif /* TEST */
diff --git a/src/gettext.h b/src/gettext.h
new file mode 100644
index 0000000..835732e
--- a/dev/null
+++ b/src/gettext.h
@@ -0,0 +1,68 @@
+/* Convenience header for conditional use of GNU <libintl.h>.
+ Copyright (C) 1995-1998, 2000-2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef _LIBGETTEXT_H
+#define _LIBGETTEXT_H 1
+
+/* NLS can be disabled through the configure --disable-nls option. */
+#if ENABLE_NLS
+
+/* Get declarations of GNU message catalog functions. */
+# include <libintl.h>
+
+#else
+
+/* Solaris /usr/include/locale.h includes /usr/include/libintl.h, which
+ chokes if dcgettext is defined as a macro. So include it now, to make
+ later inclusions of <locale.h> a NOP. We don't include <libintl.h>
+ as well because people using "gettext.h" will not include <libintl.h>,
+ and also including <libintl.h> would fail on SunOS 4, whereas <locale.h>
+ is OK. */
+#if defined(__sun)
+# include <locale.h>
+#endif
+
+/* Disabled NLS.
+ The casts to 'const char *' serve the purpose of producing warnings
+ for invalid uses of the value returned from these functions.
+ On pre-ANSI systems without 'const', the config.h file is supposed to
+ contain "#define const". */
+# define gettext(Msgid) ((const char *) (Msgid))
+# define dgettext(Domainname, Msgid) ((const char *) (Msgid))
+# define dcgettext(Domainname, Msgid, Category) ((const char *) (Msgid))
+# define ngettext(Msgid1, Msgid2, N) \
+ ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
+# define dngettext(Domainname, Msgid1, Msgid2, N) \
+ ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
+# define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \
+ ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
+# define textdomain(Domainname) ((const char *) (Domainname))
+# define bindtextdomain(Domainname, Dirname) ((const char *) (Dirname))
+# define bind_textdomain_codeset(Domainname, Codeset) ((const char *) (Codeset))
+
+#endif
+
+/* A pseudo function call that serves as a marker for the automated
+ extraction of messages, but does not call gettext(). The run-time
+ translation is done at a different place in the code.
+ The argument, String, should be a literal string. Concatenated strings
+ and other string expressions won't work.
+ The macro's expansion is not parenthesized, so that it is suitable as
+ initializer for static 'char[]' or 'const char[]' variables. */
+#define gettext_noop(String) String
+
+#endif /* _LIBGETTEXT_H */
diff --git a/src/global.c b/src/global.c
new file mode 100644
index 0000000..0f2bd47
--- a/dev/null
+++ b/src/global.c
@@ -0,0 +1,204 @@
+/* global.c - global variables and initial values for cpio.
+ Copyright (C) 1990, 1991, 1992, 2001 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#if defined(HAVE_CONFIG_H)
+# include <config.h>
+#endif
+
+#include <sys/types.h>
+#include "cpiohdr.h"
+#include "dstring.h"
+#include "system.h"
+#include "extern.h"
+
+/* If TRUE, reset access times after reading files (-a). */
+int reset_time_flag = FALSE;
+
+/* Block size value, initially 512. -B sets to 5120. */
+int io_block_size = 512;
+
+/* The header format to recognize and produce. */
+enum archive_format archive_format = arf_unknown;
+
+/* If TRUE, create directories as needed. (-d with -i or -p) */
+int create_dir_flag = FALSE;
+
+/* If TRUE, interactively rename files. (-r) */
+int rename_flag = FALSE;
+
+/* If non-NULL, the name of a file that will be read to
+ rename all of the files in the archive. --rename-batch-file. */
+char *rename_batch_file = NULL;
+
+/* If TRUE, print a table of contents of input. (-t) */
+int table_flag = FALSE;
+
+/* If TRUE, copy unconditionally (older replaces newer). (-u) */
+int unconditional_flag = FALSE;
+
+/* If TRUE, list the files processed, or ls -l style output with -t. (-v) */
+int verbose_flag = FALSE;
+
+/* If TRUE, print a . for each file processed. (-V) */
+int dot_flag = FALSE;
+
+/* If TRUE, link files whenever possible. Used with -p option. (-l) */
+int link_flag = FALSE;
+
+/* If TRUE, retain previous file modification time. (-m) */
+int retain_time_flag = FALSE;
+
+/* Set TRUE if crc_flag is TRUE and we are doing a cpio -i. Used
+ by copy_files so it knows whether to compute the crc. */
+int crc_i_flag = FALSE;
+
+/* If TRUE, append to end of archive. (-A) */
+int append_flag = FALSE;
+
+/* If TRUE, swap bytes of each file during cpio -i. */
+int swap_bytes_flag = FALSE;
+
+/* If TRUE, swap halfwords of each file during cpio -i. */
+int swap_halfwords_flag = FALSE;
+
+/* If TRUE, we are swapping halfwords on the current file. */
+int swapping_halfwords = FALSE;
+
+/* If TRUE, we are swapping bytes on the current file. */
+int swapping_bytes = FALSE;
+
+/* If TRUE, set ownership of all files to UID `set_owner'. */
+int set_owner_flag = FALSE;
+uid_t set_owner;
+
+/* If TRUE, set group ownership of all files to GID `set_group'. */
+int set_group_flag = FALSE;
+gid_t set_group;
+
+/* If TRUE, do not chown the files. */
+int no_chown_flag = FALSE;
+
+/* If TRUE, try to write sparse ("holey") files. */
+int sparse_flag = FALSE;
+
+/* If TRUE, don't report number of blocks copied. */
+int quiet_flag = FALSE;
+
+/* If TRUE, only read the archive and verify the files' CRC's, don't
+ actually extract the files. */
+int only_verify_crc_flag = FALSE;
+
+/* If TRUE, don't use any absolute paths, prefix them by `./'. */
+int no_abs_paths_flag = FALSE;
+
+#ifdef DEBUG_CPIO
+/* If TRUE, print debugging information. */
+int debug_flag = FALSE;
+#endif
+
+/* File position of last header read. Only used during -A to determine
+ where the old TRAILER!!! record started. */
+int last_header_start = 0;
+
+/* With -i; if TRUE, copy only files that match any of the given patterns;
+ if FALSE, copy only files that do not match any of the patterns. (-f) */
+int copy_matching_files = TRUE;
+
+/* With -itv; if TRUE, list numeric uid and gid instead of translating them
+ into names. */
+int numeric_uid = FALSE;
+
+/* Name of file containing additional patterns (-E). */
+char *pattern_file_name = NULL;
+
+/* Message to print when end of medium is reached (-M). */
+char *new_media_message = NULL;
+
+/* With -M with %d, message to print when end of medium is reached. */
+char *new_media_message_with_number = NULL;
+char *new_media_message_after_number = NULL;
+
+/* File descriptor containing the archive. */
+int archive_des;
+
+/* Name of file containing the archive, if known; NULL if stdin/out. */
+char *archive_name = NULL;
+
+/* Name of the remote shell command, if known; NULL otherwise. */
+char *rsh_command_option = NULL;
+
+/* CRC checksum. */
+unsigned long crc;
+
+/* Input and output buffers. */
+char *input_buffer, *output_buffer;
+
+/* The size of the input buffer. */
+long input_buffer_size;
+
+/* Current locations in `input_buffer' and `output_buffer'. */
+char *in_buff, *out_buff;
+
+/* Current number of bytes stored at `input_buff' and `output_buff'. */
+long input_size, output_size;
+
+/* Total number of bytes read and written for all files.
+ Now that many tape drives hold more than 4Gb we need more than 32
+ bits to hold input_bytes and output_bytes. But it's not worth
+ the trouble of adding special multi-precision arithmetic if the
+ compiler doesn't support 64 bit ints since input_bytes and
+ output_bytes are only used to print the number of blocks copied. */
+#ifdef __GNUC__
+long long input_bytes, output_bytes;
+#else
+long input_bytes, output_bytes;
+#endif
+
+/* 512 bytes of 0; used for various padding operations. */
+char zeros_512[512];
+
+/* Saving of argument values for later reference. */
+char *directory_name = NULL;
+char **save_patterns;
+int num_patterns;
+
+/* Character that terminates file names read from stdin. */
+char name_end = '\n';
+
+/* TRUE if input (cpio -i) or output (cpio -o) is a device node. */
+char input_is_special = FALSE;
+char output_is_special = FALSE;
+
+/* TRUE if lseek works on the input. */
+char input_is_seekable = FALSE;
+
+/* TRUE if lseek works on the output. */
+char output_is_seekable = FALSE;
+
+/* If nonzero, don't consider file names that contain a `:' to be
+ on remote hosts; all files are local. */
+int f_force_local = 0;
+
+/* The name this program was run with. */
+char *program_name;
+
+/* A pointer to either lstat or stat, depending on whether
+ dereferencing of symlinks is done for input files. */
+int (*xstat) ();
+
+/* Which copy operation to perform. (-i, -o, -p) */
+void (*copy_function) () = 0;
diff --git a/src/idcache.c b/src/idcache.c
new file mode 100644
index 0000000..03a5e4d
--- a/dev/null
+++ b/src/idcache.c
@@ -0,0 +1,210 @@
+/* idcache.c -- map user and group IDs, cached for speed
+ Copyright (C) 1985, 1988, 1989, 1990 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+
+#if defined(STDC_HEADERS) || defined(HAVE_STRING_H)
+#include <string.h>
+#else
+#include <strings.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifndef _POSIX_VERSION
+struct passwd *getpwuid ();
+struct passwd *getpwnam ();
+struct group *getgrgid ();
+struct group *getgrnam ();
+#endif
+
+char *xmalloc ();
+char *xstrdup ();
+
+struct userid
+{
+ union
+ {
+ uid_t u;
+ gid_t g;
+ } id;
+ char *name;
+ struct userid *next;
+};
+
+static struct userid *user_alist;
+
+/* The members of this list have names not in the local passwd file. */
+static struct userid *nouser_alist;
+
+/* Translate UID to a login name or a stringified number,
+ with cache. */
+
+char *
+getuser (uid)
+ uid_t uid;
+{
+ register struct userid *tail;
+ struct passwd *pwent;
+ char usernum_string[20];
+
+ for (tail = user_alist; tail; tail = tail->next)
+ if (tail->id.u == uid)
+ return tail->name;
+
+ pwent = getpwuid (uid);
+ tail = (struct userid *) xmalloc (sizeof (struct userid));
+ tail->id.u = uid;
+ if (pwent == 0)
+ {
+ sprintf (usernum_string, "%u", (unsigned) uid);
+ tail->name = xstrdup (usernum_string);
+ }
+ else
+ tail->name = xstrdup (pwent->pw_name);
+
+ /* Add to the head of the list, so most recently used is first. */
+ tail->next = user_alist;
+ user_alist = tail;
+ return tail->name;
+}
+
+/* Translate USER to a UID, with cache.
+ Return NULL if there is no such user.
+ (We also cache which user names have no passwd entry,
+ so we don't keep looking them up.) */
+
+uid_t *
+getuidbyname (user)
+ char *user;
+{
+ register struct userid *tail;
+ struct passwd *pwent;
+
+ for (tail = user_alist; tail; tail = tail->next)
+ /* Avoid a function call for the most common case. */
+ if (*tail->name == *user && !strcmp (tail->name, user))
+ return &tail->id.u;
+
+ for (tail = nouser_alist; tail; tail = tail->next)
+ /* Avoid a function call for the most common case. */
+ if (*tail->name == *user && !strcmp (tail->name, user))
+ return 0;
+
+ pwent = getpwnam (user);
+
+ tail = (struct userid *) xmalloc (sizeof (struct userid));
+ tail->name = xstrdup (user);
+
+ /* Add to the head of the list, so most recently used is first. */
+ if (pwent)
+ {
+ tail->id.u = pwent->pw_uid;
+ tail->next = user_alist;
+ user_alist = tail;
+ return &tail->id.u;
+ }
+
+ tail->next = nouser_alist;
+ nouser_alist = tail;
+ return 0;
+}
+
+/* Use the same struct as for userids. */
+static struct userid *group_alist;
+static struct userid *nogroup_alist;
+
+/* Translate GID to a group name or a stringified number,
+ with cache. */
+
+char *
+getgroup (gid)
+ gid_t gid;
+{
+ register struct userid *tail;
+ struct group *grent;
+ char groupnum_string[20];
+
+ for (tail = group_alist; tail; tail = tail->next)
+ if (tail->id.g == gid)
+ return tail->name;
+
+ grent = getgrgid (gid);
+ tail = (struct userid *) xmalloc (sizeof (struct userid));
+ tail->id.g = gid;
+ if (grent == 0)
+ {
+ sprintf (groupnum_string, "%u", (unsigned int) gid);
+ tail->name = xstrdup (groupnum_string);
+ }
+ else
+ tail->name = xstrdup (grent->gr_name);
+
+ /* Add to the head of the list, so most recently used is first. */
+ tail->next = group_alist;
+ group_alist = tail;
+ return tail->name;
+}
+
+/* Translate GROUP to a UID, with cache.
+ Return NULL if there is no such group.
+ (We also cache which group names have no group entry,
+ so we don't keep looking them up.) */
+
+gid_t *
+getgidbyname (group)
+ char *group;
+{
+ register struct userid *tail;
+ struct group *grent;
+
+ for (tail = group_alist; tail; tail = tail->next)
+ /* Avoid a function call for the most common case. */
+ if (*tail->name == *group && !strcmp (tail->name, group))
+ return &tail->id.g;
+
+ for (tail = nogroup_alist; tail; tail = tail->next)
+ /* Avoid a function call for the most common case. */
+ if (*tail->name == *group && !strcmp (tail->name, group))
+ return 0;
+
+ grent = getgrnam (group);
+
+ tail = (struct userid *) xmalloc (sizeof (struct userid));
+ tail->name = xstrdup (group);
+
+ /* Add to the head of the list, so most recently used is first. */
+ if (grent)
+ {
+ tail->id.g = grent->gr_gid;
+ tail->next = group_alist;
+ group_alist = tail;
+ return &tail->id.g;
+ }
+
+ tail->next = nogroup_alist;
+ nogroup_alist = tail;
+ return 0;
+}
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..1a2a165
--- a/dev/null
+++ b/src/main.c
@@ -0,0 +1,550 @@
+/* main.c - main program and argument processing for cpio.
+ Copyright (C) 1990, 1991, 1992, 2001, 2003 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+/* Written by Phil Nelson <phil@cs.wwu.edu>,
+ David MacKenzie <djm@gnu.ai.mit.edu>,
+ and John Oleynick <juo@klinzhai.rutgers.edu>. */
+
+#if defined(HAVE_CONFIG_H)
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_LOCALE_H
+# include <locale.h>
+#endif
+
+#include "filetypes.h"
+#include "system.h"
+#include "cpiohdr.h"
+#include "dstring.h"
+#include "extern.h"
+#include "rmt.h"
+#include "localedir.h"
+
+struct option long_opts[] =
+{
+ {"null", 0, 0, '0'},
+ {"append", 0, 0, 'A'},
+ {"block-size", 1, 0, 130},
+ {"create", 0, 0, 'o'},
+ {"dereference", 0, 0, 'L'},
+ {"dot", 0, 0, 'V'},
+ {"extract", 0, 0, 'i'},
+ {"file", 1, 0, 'F'},
+ {"force-local", 0, &f_force_local, 1},
+ {"format", 1, 0, 'H'},
+ {"help", 0, 0, 132},
+ {"io-size", 1, 0, 'C'},
+ {"link", 0, &link_flag, TRUE},
+ {"list", 0, &table_flag, TRUE},
+ {"make-directories", 0, &create_dir_flag, TRUE},
+ {"message", 1, 0, 'M'},
+ {"no-absolute-filenames", 0, 0, 136},
+ {"no-preserve-owner", 0, 0, 134},
+ {"nonmatching", 0, &copy_matching_files, FALSE},
+ {"numeric-uid-gid", 0, &numeric_uid, TRUE},
+ {"only-verify-crc", 0, 0, 139},
+ {"owner", 1, 0, 'R'},
+ {"pass-through", 0, 0, 'p'},
+ {"pattern-file", 1, 0, 'E'},
+ {"preserve-modification-time", 0, &retain_time_flag, TRUE},
+ {"rename", 0, &rename_flag, TRUE},
+ {"rename-batch-file", 1, 0, 137},
+ {"rsh-command", 1, 0, 140},
+ {"quiet", 0, 0, 138},
+ {"sparse", 0, 0, 135},
+ {"swap", 0, 0, 'b'},
+ {"swap-bytes", 0, 0, 's'},
+ {"swap-halfwords", 0, 0, 'S'},
+ {"reset-access-time", 0, &reset_time_flag, TRUE},
+ {"unconditional", 0, &unconditional_flag, TRUE},
+ {"verbose", 0, &verbose_flag, TRUE},
+ {"version", 0, 0, 131},
+#ifdef DEBUG_CPIO
+ {"debug", 0, &debug_flag, TRUE},
+#endif
+ {0, 0, 0, 0}
+};
+
+/* Print usage message and exit with error. */
+
+void
+usage (fp, status)
+ FILE *fp;
+ int status;
+{
+ fprintf (fp, _("\
+Usage: %s {-o|--create} [-0acvABLV] [-C bytes] [-H format] [-M message]\n\
+ [-O [[user@]host:]archive] [-F [[user@]host:]archive]\n\
+ [--file=[[user@]host:]archive] [--format=format] [--message=message]\n\
+ [--null] [--reset-access-time] [--verbose] [--dot] [--append]\n\
+ [--block-size=blocks] [--dereference] [--io-size=bytes] [--quiet]\n\
+ [--force-local] [--rsh-command=command] [--help] [--version] < name-list\n\
+ [> archive]\n"), program_name);
+ fprintf (fp, _("\
+ %s {-i|--extract} [-bcdfmnrtsuvBSV] [-C bytes] [-E file] [-H format]\n\
+ [-M message] [-R [user][:.][group]] [-I [[user@]host:]archive]\n\
+ [-F [[user@]host:]archive] [--file=[[user@]host:]archive]\n\
+ [--make-directories] [--nonmatching] [--preserve-modification-time]\n\
+ [--numeric-uid-gid] [--rename] [--list] [--swap-bytes] [--swap] [--dot]\n\
+ [--unconditional] [--verbose] [--block-size=blocks] [--swap-halfwords]\n\
+ [--io-size=bytes] [--pattern-file=file] [--format=format]\n\
+ [--owner=[user][:.][group]] [--no-preserve-owner] [--message=message]\n\
+ [--force-local] [--no-absolute-filenames] [--sparse] [--only-verify-crc]\n\
+ [--quiet] [--rsh-command=command] [--help] [--version] [pattern...]\n\
+ [< archive]\n"),
+ program_name);
+ fprintf (fp, _("\
+ %s {-p|--pass-through} [-0adlmuvLV] [-R [user][:.][group]]\n\
+ [--null] [--reset-access-time] [--make-directories] [--link] [--quiet]\n\
+ [--preserve-modification-time] [--unconditional] [--verbose] [--dot]\n\
+ [--dereference] [--owner=[user][:.][group]] [--no-preserve-owner]\n\
+ [--sparse] [--help] [--version] destination-directory < name-list\n"), program_name);
+ exit (status);
+}
+
+/* Process the arguments. Set all options and set up the copy pass
+ directory or the copy in patterns. */
+
+void
+process_args (argc, argv)
+ int argc;
+ char *argv[];
+{
+ void (*copy_in) (); /* Work around for pcc bug. */
+ void (*copy_out) ();
+ int c;
+ char *input_archive_name = 0;
+ char *output_archive_name = 0;
+
+ if (argc < 2)
+ usage (stderr, 2);
+
+ xstat = lstat;
+
+ while ((c = getopt_long (argc, argv,
+ "0aAbBcC:dfE:F:H:iI:lLmM:noO:prR:sStuvVz",
+ long_opts, (int *) 0)) != -1)
+ {
+ switch (c)
+ {
+ case 0: /* A long option that just sets a flag. */
+ break;
+
+ case '0': /* Read null-terminated filenames. */
+ name_end = '\0';
+ break;
+
+ case 'a': /* Reset access times. */
+ reset_time_flag = TRUE;
+ break;
+
+ case 'A': /* Append to the archive. */
+ append_flag = TRUE;
+ break;
+
+ case 'b': /* Swap bytes and halfwords. */
+ swap_bytes_flag = TRUE;
+ swap_halfwords_flag = TRUE;
+ break;
+
+ case 'B': /* Set block size to 5120. */
+ io_block_size = 5120;
+ break;
+
+ case 130: /* --block-size */
+ io_block_size = atoi (optarg);
+ if (io_block_size < 1)
+ error (2, 0, _("invalid block size"));
+ io_block_size *= 512;
+ break;
+
+ case 'c': /* Use the old portable ASCII format. */
+ if (archive_format != arf_unknown)
+ usage (stderr, 2);
+#ifdef SVR4_COMPAT
+ archive_format = arf_newascii; /* -H newc. */
+#else
+ archive_format = arf_oldascii; /* -H odc. */
+#endif
+ break;
+
+ case 'C': /* Block size. */
+ io_block_size = atoi (optarg);
+ if (io_block_size < 1)
+ error (2, 0, _("invalid block size"));
+ break;
+
+ case 'd': /* Create directories where needed. */
+ create_dir_flag = TRUE;
+ break;
+
+ case 'f': /* Only copy files not matching patterns. */
+ copy_matching_files = FALSE;
+ break;
+
+ case 'E': /* Pattern file name. */
+ pattern_file_name = optarg;
+ break;
+
+ case 'F': /* Archive file name. */
+ archive_name = optarg;
+ break;
+
+ case 'H': /* Header format name. */
+ if (archive_format != arf_unknown)
+ usage (stderr, 2);
+ if (!strcmp (optarg, "crc") || !strcmp (optarg, "CRC"))
+ archive_format = arf_crcascii;
+ else if (!strcmp (optarg, "newc") || !strcmp (optarg, "NEWC"))
+ archive_format = arf_newascii;
+ else if (!strcmp (optarg, "odc") || !strcmp (optarg, "ODC"))
+ archive_format = arf_oldascii;
+ else if (!strcmp (optarg, "bin") || !strcmp (optarg, "BIN"))
+ archive_format = arf_binary;
+ else if (!strcmp (optarg, "ustar") || !strcmp (optarg, "USTAR"))
+ archive_format = arf_ustar;
+ else if (!strcmp (optarg, "tar") || !strcmp (optarg, "TAR"))
+ archive_format = arf_tar;
+ else if (!strcmp (optarg, "hpodc") || !strcmp (optarg, "HPODC"))
+ archive_format = arf_hpoldascii;
+ else if (!strcmp (optarg, "hpbin") || !strcmp (optarg, "HPBIN"))
+ archive_format = arf_hpbinary;
+ else
+ error (2, 0, _("\
+invalid archive format `%s'; valid formats are:\n\
+crc newc odc bin ustar tar (all-caps also recognized)"), optarg);
+ break;
+
+ case 'i': /* Copy-in mode. */
+ if (copy_function != 0)
+ usage (stderr, 2);
+ copy_function = process_copy_in;
+ break;
+
+ case 'I': /* Input archive file name. */
+ input_archive_name = optarg;
+ break;
+
+ case 'k': /* Handle corrupted archives. We always handle
+ corrupted archives, but recognize this
+ option for compatability. */
+ break;
+
+ case 'l': /* Link files when possible. */
+ link_flag = TRUE;
+ break;
+
+ case 'L': /* Dereference symbolic links. */
+ xstat = stat;
+ break;
+
+ case 'm': /* Retain previous file modify times. */
+ retain_time_flag = TRUE;
+ break;
+
+ case 'M': /* New media message. */
+ set_new_media_message (optarg);
+ break;
+
+ case 'n': /* Long list owner and group as numbers. */
+ numeric_uid = TRUE;
+ break;
+
+ case 136: /* --no-absolute-filenames */
+ no_abs_paths_flag = TRUE;
+ break;
+
+ case 134: /* --no-preserve-owner */
+ if (set_owner_flag || set_group_flag)
+ usage (stderr, 2);
+ no_chown_flag = TRUE;
+ break;
+
+ case 'o': /* Copy-out mode. */
+ if (copy_function != 0)
+ usage (stderr, 2);
+ copy_function = process_copy_out;
+ break;
+
+ case 'O': /* Output archive file name. */
+ output_archive_name = optarg;
+ break;
+
+ case 139:
+ only_verify_crc_flag = TRUE;
+ break;
+
+ case 'p': /* Copy-pass mode. */
+ if (copy_function != 0)
+ usage (stderr, 2);
+ copy_function = process_copy_pass;
+ break;
+
+ case 140:
+ rsh_command_option = optarg;
+ break;
+
+ case 'r': /* Interactively rename. */
+ rename_flag = TRUE;
+ break;
+
+ case 137:
+ rename_batch_file = optarg;
+ break;
+
+ case 138:
+ quiet_flag = TRUE;
+ break;
+
+ case 'R': /* Set the owner. */
+ if (no_chown_flag)
+ usage (stderr, 2);
+#ifndef __MSDOS__
+ {
+ char *e, *u, *g;
+
+ e = parse_user_spec (optarg, &set_owner, &set_group, &u, &g);
+ if (e)
+ error (2, 0, "%s: %s", optarg, e);
+ if (u)
+ {
+ free (u);
+ set_owner_flag = TRUE;
+ }
+ if (g)
+ {
+ free (g);
+ set_group_flag = TRUE;
+ }
+ }
+#endif
+ break;
+
+ case 's': /* Swap bytes. */
+ swap_bytes_flag = TRUE;
+ break;
+
+ case 'S': /* Swap halfwords. */
+ swap_halfwords_flag = TRUE;
+ break;
+
+ case 't': /* Only print a list. */
+ table_flag = TRUE;
+ break;
+
+ case 'u': /* Replace all! Unconditionally! */
+ unconditional_flag = TRUE;
+ break;
+
+ case 'v': /* Verbose! */
+ verbose_flag = TRUE;
+ break;
+
+ case 'V': /* Print `.' for each file. */
+ dot_flag = TRUE;
+ break;
+
+ case 131:
+ printf ("%s\n", PACKAGE_STRING);
+ exit (0);
+ break;
+
+ case 135:
+ sparse_flag = TRUE;
+ break;
+
+ case 132: /* --help */
+ usage (stdout, 0);
+ break;
+
+ default:
+ usage (stderr, 2);
+ }
+ }
+
+ /* Do error checking and look at other args. */
+
+ if (copy_function == 0)
+ {
+ if (table_flag)
+ copy_function = process_copy_in;
+ else
+ usage (stderr, 2);
+ }
+
+ /* Debian hack: This version of cpio uses the -n flag also to extract
+ tar archives using the numeric UID/GID instead of the user/group
+ names in /etc/passwd and /etc/groups. (98/10/15) -BEM */
+
+ /* Work around for pcc bug. */
+ copy_in = process_copy_in;
+ copy_out = process_copy_out;
+
+ if (copy_function == copy_in)
+ {
+ archive_des = 0;
+ if (link_flag || reset_time_flag || xstat != lstat || append_flag
+ /* Debian hack: The sparse option is used with copy-in not
+ copy-out. This bug has been reported to
+ "bug-gnu-utils@prep.ai.mit.edu". (96/7/10) -BEM */
+ || output_archive_name
+ || (archive_name && input_archive_name))
+ usage (stderr, 2);
+ if (archive_format == arf_crcascii)
+ crc_i_flag = TRUE;
+ num_patterns = argc - optind;
+ save_patterns = &argv[optind];
+ if (input_archive_name)
+ archive_name = input_archive_name;
+ }
+ else if (copy_function == copy_out)
+ {
+ archive_des = 1;
+ if (argc != optind || create_dir_flag || rename_flag
+ || table_flag || unconditional_flag || link_flag
+ || retain_time_flag || no_chown_flag || set_owner_flag
+ || set_group_flag || swap_bytes_flag || swap_halfwords_flag
+ || (append_flag && !(archive_name || output_archive_name))
+ || rename_batch_file || no_abs_paths_flag
+ /* Debian hack: The sparse option is used with copy-in not
+ copy-out. This bug has been reported to
+ "bug-gnu-utils@prep.ai.mit.edu". (96/7/10) -BEM */
+ || sparse_flag
+ || input_archive_name || (archive_name && output_archive_name))
+ usage (stderr, 2);
+ if (archive_format == arf_unknown)
+ archive_format = arf_binary;
+ if (output_archive_name)
+ archive_name = output_archive_name;
+ }
+ else
+ {
+ /* Copy pass. */
+ archive_des = -1;
+ if (argc - 1 != optind || archive_format != arf_unknown
+ || swap_bytes_flag || swap_halfwords_flag
+ || table_flag || rename_flag || append_flag
+ || rename_batch_file || no_abs_paths_flag)
+ usage (stderr, 2);
+ directory_name = argv[optind];
+ }
+
+ if (archive_name)
+ {
+ if (copy_function != copy_in && copy_function != copy_out)
+ usage (stderr, 2);
+ archive_des = open_archive (archive_name);
+ if (archive_des < 0)
+ error (1, errno, "%s", archive_name);
+ }
+
+#ifndef __MSDOS__
+ /* Prevent SysV non-root users from giving away files inadvertantly.
+ This happens automatically on BSD, where only root can give
+ away files. */
+ if (set_owner_flag == FALSE && set_group_flag == FALSE && geteuid ())
+ no_chown_flag = TRUE;
+#endif
+}
+
+/* Initialize the input and output buffers to their proper size and
+ initialize all variables associated with the input and output
+ buffers. */
+
+void
+initialize_buffers ()
+{
+ int in_buf_size, out_buf_size;
+
+ if (copy_function == process_copy_in)
+ {
+ /* Make sure the input buffer can always hold 2 blocks and that it
+ is big enough to hold 1 tar record (512 bytes) even if it
+ is not aligned on a block boundary. The extra buffer space
+ is needed by process_copyin and peek_in_buf to automatically
+ figure out what kind of archive it is reading. */
+ if (io_block_size >= 512)
+ in_buf_size = 2 * io_block_size;
+ else
+ in_buf_size = 1024;
+ out_buf_size = DISK_IO_BLOCK_SIZE;
+ }
+ else if (copy_function == process_copy_out)
+ {
+ in_buf_size = DISK_IO_BLOCK_SIZE;
+ out_buf_size = io_block_size;
+ }
+ else
+ {
+ in_buf_size = DISK_IO_BLOCK_SIZE;
+ out_buf_size = DISK_IO_BLOCK_SIZE;
+ }
+
+ input_buffer = (char *) xmalloc (in_buf_size);
+ in_buff = input_buffer;
+ input_buffer_size = in_buf_size;
+ input_size = 0;
+ input_bytes = 0;
+
+ output_buffer = (char *) xmalloc (out_buf_size);
+ out_buff = output_buffer;
+ output_size = 0;
+ output_bytes = 0;
+
+ /* Clear the block of zeros. */
+ bzero (zeros_512, 512);
+}
+
+int
+main (argc, argv)
+ int argc;
+ char *argv[];
+{
+#ifdef HAVE_LOCALE_H
+ setlocale (LC_ALL, "");
+#endif
+ bindtextdomain (PACKAGE, LOCALEDIR);
+ textdomain (PACKAGE);
+
+ program_name = argv[0];
+ umask (0);
+
+#ifdef __TURBOC__
+ _fmode = O_BINARY; /* Put stdin and stdout in binary mode. */
+#endif
+#ifdef __EMX__ /* gcc on OS/2. */
+ _response (&argc, &argv);
+ _wildcard (&argc, &argv);
+#endif
+
+ process_args (argc, argv);
+
+ initialize_buffers ();
+
+ (*copy_function) ();
+
+ if (archive_des >= 0 && rmtclose (archive_des) == -1)
+ error (1, errno, _("error closing archive"));
+
+ exit (0);
+}
diff --git a/src/makepath.c b/src/makepath.c
new file mode 100644
index 0000000..0c62860
--- a/dev/null
+++ b/src/makepath.c
@@ -0,0 +1,311 @@
+/* makepath.c -- Ensure that a directory path exists.
+ Copyright (C) 1990 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+/* Written by David MacKenzie <djm@gnu.ai.mit.edu> and
+ Jim Meyering <meyering@cs.utexas.edu>. */
+
+/* This copy of makepath is almost like the fileutils one, but has
+ changes for HPUX CDF's. Maybe the 2 versions of makepath can
+ come together again in the future. */
+
+#if defined(HAVE_CONFIG_H)
+# include <config.h>
+#endif
+
+#ifdef __GNUC__
+#define alloca __builtin_alloca
+#else
+#ifdef HAVE_ALLOCA_H
+#include <alloca.h>
+#else
+#ifdef _AIX
+ #pragma alloca
+#else
+char *alloca ();
+#endif
+#endif
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if !defined(S_ISDIR) && defined(S_IFDIR)
+#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+#endif
+
+#include <errno.h>
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+#else
+extern int errno;
+#endif
+
+#if defined(STDC_HEADERS) || defined(HAVE_STRING_H)
+#include <string.h>
+#ifndef index
+#define index strchr
+#endif
+#else
+#include <strings.h>
+#endif
+
+#if defined(__MSDOS__) && !defined(__GNUC__)
+typedef int uid_t;
+typedef int gid_t;
+#endif
+
+void error ();
+
+/* Ensure that the directory ARGPATH exists.
+ Remove any trailing slashes from ARGPATH before calling this function.
+
+ Make any leading directories that don't already exist, with
+ permissions PARENT_MODE.
+ If the last element of ARGPATH does not exist, create it as
+ a new directory with permissions MODE.
+ If OWNER and GROUP are non-negative, make them the UID and GID of
+ created directories.
+ If VERBOSE_FMT_STRING is nonzero, use it as a printf format
+ string for printing a message after successfully making a directory,
+ with the name of the directory that was just made as an argument.
+
+ Return 0 if ARGPATH exists as a directory with the proper
+ ownership and permissions when done, otherwise 1. */
+
+int
+make_path (argpath, mode, parent_mode, owner, group, verbose_fmt_string)
+ char *argpath;
+ int mode;
+ int parent_mode;
+ uid_t owner;
+ gid_t group;
+ char *verbose_fmt_string;
+{
+ char *dirpath; /* A copy we can scribble NULs on. */
+ struct stat stats;
+ int retval = 0;
+ int oldmask = umask (0);
+ dirpath = alloca (strlen (argpath) + 1);
+ strcpy (dirpath, argpath);
+
+ if (stat (dirpath, &stats))
+ {
+ char *slash;
+ int tmp_mode; /* Initial perms for leading dirs. */
+ int re_protect; /* Should leading dirs be unwritable? */
+ struct ptr_list
+ {
+ char *dirname_end;
+ struct ptr_list *next;
+ };
+ struct ptr_list *p, *leading_dirs = NULL;
+
+ /* If leading directories shouldn't be writable or executable,
+ or should have set[ug]id or sticky bits set and we are setting
+ their owners, we need to fix their permissions after making them. */
+ if (((parent_mode & 0300) != 0300)
+ || (owner != (uid_t) -1 && group != (gid_t) -1
+ && (parent_mode & 07000) != 0))
+ {
+ tmp_mode = 0700;
+ re_protect = 1;
+ }
+ else
+ {
+ tmp_mode = parent_mode;
+ re_protect = 0;
+ }
+
+ slash = dirpath;
+ while (*slash == '/')
+ slash++;
+ while ((slash = index (slash, '/')))
+ {
+#ifdef HPUX_CDF
+ int iscdf;
+ iscdf = 0;
+#endif
+ *slash = '\0';
+ if (stat (dirpath, &stats))
+ {
+#ifdef HPUX_CDF
+ /* If this component of the pathname ends in `+' and is
+ followed by 2 `/'s, then this is a CDF. We remove the
+ `+' from the name and create the directory. Later
+ we will "hide" the directory. */
+ if ( (*(slash +1) == '/') && (*(slash -1) == '+') )
+ {
+ iscdf = 1;
+ *(slash -1) = '\0';
+ }
+#endif
+ if (mkdir (dirpath, tmp_mode))
+ {
+ error (0, errno, _("cannot make directory `%s'"), dirpath);
+ umask (oldmask);
+ return 1;
+ }
+ else
+ {
+ if (verbose_fmt_string != NULL)
+ error (0, 0, verbose_fmt_string, dirpath);
+
+ if (owner != (uid_t) -1 && group != (gid_t) -1
+ && chown (dirpath, owner, group)
+#ifdef AFS
+ && errno != EPERM
+#endif
+ )
+ {
+ error (0, errno, "%s", dirpath);
+ retval = 1;
+ }
+ if (re_protect)
+ {
+ struct ptr_list *new = (struct ptr_list *)
+ alloca (sizeof (struct ptr_list));
+ new->dirname_end = slash;
+ new->next = leading_dirs;
+ leading_dirs = new;
+ }
+#ifdef HPUX_CDF
+ if (iscdf)
+ {
+ /* If this is a CDF, "hide" the directory by setting
+ its hidden/setuid bit. Also add the `+' back to
+ its name (since once it's "hidden" we must refer
+ to as `name+' instead of `name'). */
+ chmod (dirpath, 04700);
+ *(slash - 1) = '+';
+ }
+#endif
+ }
+ }
+ else if (!S_ISDIR (stats.st_mode))
+ {
+ error (0, 0, _("`%s' exists but is not a directory"), dirpath);
+ umask (oldmask);
+ return 1;
+ }
+
+ *slash++ = '/';
+
+ /* Avoid unnecessary calls to `stat' when given
+ pathnames containing multiple adjacent slashes. */
+ while (*slash == '/')
+ slash++;
+ }
+
+ /* We're done making leading directories.
+ Make the final component of the path. */
+
+ if (mkdir (dirpath, mode))
+ {
+ /* In some cases, if the final component in dirpath was `.' then we
+ just got an EEXIST error from that last mkdir(). If that's
+ the case, ignore it. */
+ if ( (errno != EEXIST) ||
+ (stat (dirpath, &stats) != 0) ||
+ (!S_ISDIR (stats.st_mode) ) )
+ {
+ error (0, errno, _("cannot make directory `%s'"), dirpath);
+ umask (oldmask);
+ return 1;
+ }
+ }
+ if (verbose_fmt_string != NULL)
+ error (0, 0, verbose_fmt_string, dirpath);
+
+ if (owner != (uid_t) -1 && group != (gid_t) -1)
+ {
+ if (chown (dirpath, owner, group)
+#ifdef AFS
+ && errno != EPERM
+#endif
+ )
+ {
+ error (0, errno, "%s", dirpath);
+ retval = 1;
+ }
+ }
+ /* chown may have turned off some permission bits we wanted. */
+ if ((mode & 07000) != 0 && chmod (dirpath, mode))
+ {
+ error (0, errno, "%s", dirpath);
+ retval = 1;
+ }
+
+ /* If the mode for leading directories didn't include owner "wx"
+ privileges, we have to reset their protections to the correct
+ value. */
+ for (p = leading_dirs; p != NULL; p = p->next)
+ {
+ *(p->dirname_end) = '\0';
+#if 0
+ /* cpio always calls make_path with parent mode 0700, so
+ we don't have to do this. If we ever do have to do this,
+ we have to stat the directory first to get the setuid
+ bit so we don't break HP CDF's. */
+ if (chmod (dirpath, parent_mode))
+ {
+ error (0, errno, "%s", dirpath);
+ retval = 1;
+ }
+#endif
+
+ }
+ }
+ else
+ {
+ /* We get here if the entire path already exists. */
+
+ if (!S_ISDIR (stats.st_mode))
+ {
+ error (0, 0, _("`%s' exists but is not a directory"), dirpath);
+ umask (oldmask);
+ return 1;
+ }
+
+ /* chown must precede chmod because on some systems,
+ chown clears the set[ug]id bits for non-superusers,
+ resulting in incorrect permissions.
+ On System V, users can give away files with chown and then not
+ be able to chmod them. So don't give files away. */
+
+ if (owner != (uid_t) -1 && group != (gid_t) -1
+ && chown (dirpath, owner, group)
+#ifdef AFS
+ && errno != EPERM
+#endif
+ )
+ {
+ error (0, errno, "%s", dirpath);
+ retval = 1;
+ }
+ if (chmod (dirpath, mode))
+ {
+ error (0, errno, "%s", dirpath);
+ retval = 1;
+ }
+ }
+
+ umask (oldmask);
+ return retval;
+}
diff --git a/src/mkdir.c b/src/mkdir.c
new file mode 100644
index 0000000..aa1c4ff
--- a/dev/null
+++ b/src/mkdir.c
@@ -0,0 +1,100 @@
+/* mkdir.c -- BSD compatible make directory function for System V
+ Copyright (C) 1988, 1990 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+#if !HAVE_DECL_ERRNO
+extern int errno;
+#endif
+
+#ifdef STAT_MACROS_BROKEN
+#undef S_ISDIR
+#endif
+
+#if !defined(S_ISDIR) && defined(S_IFDIR)
+#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+#endif
+
+#include "safe-stat.h"
+
+/* mkdir adapted from GNU tar. */
+
+/* Make directory DPATH, with permission mode DMODE.
+
+ Written by Robert Rother, Mariah Corporation, August 1985
+ (sdcsvax!rmr or rmr@uscd). If you want it, it's yours.
+
+ Severely hacked over by John Gilmore to make a 4.2BSD compatible
+ subroutine. 11Mar86; hoptoad!gnu
+
+ Modified by rmtodd@uokmax 6-28-87 -- when making an already existing dir,
+ subroutine didn't return EEXIST. It does now. */
+
+int
+mkdir (dpath, dmode)
+ char *dpath;
+ int dmode;
+{
+ int cpid, status;
+ struct stat statbuf;
+
+ if (SAFE_STAT (dpath, &statbuf) == 0)
+ {
+ errno = EEXIST; /* stat worked, so it already exists. */
+ return -1;
+ }
+
+ /* If stat fails for a reason other than non-existence, return error. */
+ if (errno != ENOENT)
+ return -1;
+
+ cpid = fork ();
+ switch (cpid)
+ {
+ case -1: /* Cannot fork. */
+ return -1; /* errno is already set. */
+
+ case 0: /* Child process. */
+ /* Cheap hack to set mode of new directory. Since this child
+ process is going away anyway, we zap its umask.
+ This won't suffice to set SUID, SGID, etc. on this
+ directory, so the parent process calls chmod afterward. */
+ status = umask (0); /* Get current umask. */
+ umask (status | (0777 & ~dmode)); /* Set for mkdir. */
+ execl ("/bin/mkdir", "mkdir", dpath, (char *) 0);
+ _exit (1);
+
+ default: /* Parent process. */
+ /* Wait for kid to finish. */
+ while (wait (&status) != cpid)
+ /* Do nothing. */ ;
+
+ if (status & 0xFFFF)
+ {
+ /* /bin/mkdir failed. */
+ errno = EIO;
+ return -1;
+ }
+ return chmod (dpath, dmode);
+ }
+}
diff --git a/src/mt.c b/src/mt.c
new file mode 100644
index 0000000..9cfcb61
--- a/dev/null
+++ b/src/mt.c
@@ -0,0 +1,366 @@
+/* mt -- control magnetic tape drive operation
+ Copyright (C) 1991, 1992, 1995, 2001 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+
+/* If -f is not given, the environment variable TAPE is used;
+ if that is not set, a default device defined in sys/mtio.h is used.
+ The device must be either a character special file or a remote
+ tape drive with the form "[user@]system:path".
+ The default count is 1. Some operations ignore it.
+
+ Exit status:
+ 0 success
+ 1 invalid operation or device name
+ 2 operation failed
+
+ Operations (unique abbreviations are accepted):
+ eof, weof Write COUNT EOF marks at current position on tape.
+ fsf Forward space COUNT files.
+ Tape is positioned on the first block of the file.
+ bsf Backward space COUNT files.
+ Tape is positioned on the first block of the file.
+ fsr Forward space COUNT records.
+ bsr Backward space COUNT records.
+ bsfm Backward space COUNT file marks.
+ Tape is positioned on the beginning-of-the-tape side of
+ the file mark.
+ asf Absolute space to file number COUNT.
+ Equivalent to rewind followed by fsf COUNT.
+ eom Space to the end of the recorded media on the tape
+ (for appending files onto tapes).
+ rewind Rewind the tape.
+ offline, rewoffl
+ Rewind the tape and, if applicable, unload the tape.
+ status Print status information about the tape unit.
+ retension Rewind the tape, then wind it to the end of the reel,
+ then rewind it again.
+ erase Erase the tape.
+
+ David MacKenzie <djm@gnu.ai.mit.edu> */
+
+#if defined(HAVE_CONFIG_H)
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#ifdef HAVE_SYS_MTIO_H
+# ifdef HAVE_SYS_IO_TRIOCTL_H
+# include <sys/io/trioctl.h>
+# endif
+# include <sys/mtio.h>
+#endif
+#include <sys/file.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <getopt.h>
+
+#ifdef HAVE_LOCALE_H
+# include <locale.h>
+#endif
+#include "localedir.h"
+
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#include "rmt.h"
+
+#if defined(HAVE_STRING_H) || defined(STDC_HEADERS)
+#include <string.h>
+#else
+#include <strings.h>
+#endif
+
+#if defined(HAVE_STDLIB_H)
+# include <stdlib.h>
+#endif
+
+#if !HAVE_DECL_ERRNO
+extern int errno;
+#endif
+#if !HAVE_DECL_GETENV
+char *getenv ();
+#endif
+#if !HAVE_DECL_ATOI
+int atoi ();
+#endif
+#if !HAVE_DECL_EXIT
+void exit ();
+#endif
+
+int fstat ();
+
+int argmatch ();
+void check_type ();
+void error ();
+void invalid_arg ();
+void perform_operation ();
+void print_status ();
+void usage ();
+
+char *opnames[] =
+{
+ "eof", "weof", "fsf", "bsf", "fsr", "bsr",
+ "rewind", "offline", "rewoffl", "eject", "status",
+#ifdef MTBSFM
+ "bsfm",
+#endif
+#ifdef MTEOM
+ "eom",
+#endif
+#ifdef MTRETEN
+ "retension",
+#endif
+#ifdef MTERASE
+ "erase",
+#endif
+ "asf",
+#ifdef MTFSFM
+ "fsfm",
+#endif
+#ifdef MTSEEK
+ "seek",
+#endif
+ NULL
+};
+
+#define MTASF 600 /* Random unused number. */
+short operations[] =
+{
+ MTWEOF, MTWEOF, MTFSF, MTBSF, MTFSR, MTBSR,
+ MTREW, MTOFFL, MTOFFL, MTOFFL, MTNOP,
+#ifdef MTBSFM
+ MTBSFM,
+#endif
+#ifdef MTEOM
+ MTEOM,
+#endif
+#ifdef MTRETEN
+ MTRETEN,
+#endif
+#ifdef MTERASE
+ MTERASE,
+#endif
+ MTASF,
+#ifdef MTFSFM
+ MTFSFM,
+#endif
+#ifdef MTSEEK
+ MTSEEK,
+#endif
+ 0
+};
+
+/* If nonzero, don't consider file names that contain a `:' to be
+ on remote hosts; all files are local. Always zero for mt;
+ since when do local device names contain colons? */
+int f_force_local = 0;
+
+struct option longopts[] =
+{
+ {"file", 1, NULL, 'f'},
+ {"rsh-command", 1, NULL, 1},
+ {"version", 0, NULL, 'V'},
+ {"help", 0, NULL, 'H'},
+ {NULL, 0, NULL, 0}
+};
+
+/* The name this program was run with. */
+char *program_name;
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ short operation;
+ int count;
+ char *tapedev;
+ int tapedesc;
+ int i;
+ char *rsh_command_option = NULL;
+
+#ifdef HAVE_LOCALE_H
+ setlocale (LC_ALL, "");
+#endif
+ bindtextdomain (PACKAGE, LOCALEDIR);
+ textdomain (PACKAGE);
+
+ program_name = argv[0];
+ tapedev = NULL;
+ count = 1;
+
+ /* Debian hack: Fixed a bug in the -V flag. This bug has been
+ reported to "bug-gnu-utils@prep.ai.mit.edu". -BEM */
+ while ((i = getopt_long (argc, argv, "f:t:VH", longopts, (int *) 0)) != -1)
+ {
+ switch (i)
+ {
+ case 'f':
+ case 't':
+ tapedev = optarg;
+ break;
+
+ case 1:
+ rsh_command_option = optarg;
+ break;
+
+ case 'V':
+ printf ("mt (%s)\n", PACKAGE_STRING);
+ exit (0);
+ break;
+
+ case 'H':
+ default:
+ usage (stdout, 0);
+ }
+ }
+
+ if (optind == argc)
+ usage (stderr, 1);
+
+ i = argmatch (argv[optind], opnames);
+ if (i < 0)
+ {
+ invalid_arg ("tape operation", argv[optind], i);
+ exit (1);
+ }
+ operation = operations[i];
+
+ if (++optind < argc)
+ /* Debian hack: Replaced the atoi function call with strtol so
+ that hexidecimal values can be used for the count parameter.
+ This bug has been reported to "bug-gnu-utils@prep.ai.mit.edu".
+ (97/12/5) -BEM */
+#if defined(STDC_HEADERS)
+ count = (int) strtol (argv[optind], NULL, 0);
+#else
+ count = atoi (argv[optind]);
+#endif
+ if (++optind < argc)
+ usage (stderr, 1);
+
+ if (tapedev == NULL)
+ {
+ tapedev = getenv ("TAPE");
+ if (tapedev == NULL)
+#ifdef DEFTAPE /* From sys/mtio.h. */
+ tapedev = DEFTAPE;
+#else
+ error (1, 0, _("no tape device specified"));
+#endif
+ }
+
+ if ( (operation == MTWEOF)
+#ifdef MTERASE
+ || (operation == MTERASE)
+#endif
+ )
+ tapedesc = rmtopen (tapedev, O_WRONLY, 0, rsh_command_option);
+ else
+ tapedesc = rmtopen (tapedev, O_RDONLY, 0, rsh_command_option);
+ if (tapedesc == -1)
+ error (1, errno, "%s", tapedev);
+ check_type (tapedev, tapedesc);
+
+ if (operation == MTASF)
+ {
+ perform_operation (tapedev, tapedesc, MTREW, 1);
+ operation = MTFSF;
+ }
+ perform_operation (tapedev, tapedesc, operation, count);
+ if (operation == MTNOP)
+ print_status (tapedev, tapedesc);
+
+ if (rmtclose (tapedesc) == -1)
+ error (2, errno, "%s", tapedev);
+
+ exit (0);
+}
+
+void
+check_type (dev, desc)
+ char *dev;
+ int desc;
+{
+ struct stat stats;
+
+ if (_isrmt (desc))
+ return;
+ if (fstat (desc, &stats) == -1)
+ error (1, errno, "%s", dev);
+ if ((stats.st_mode & S_IFMT) != S_IFCHR)
+ error (1, 0, "%s is not a character special file", dev);
+}
+
+void
+perform_operation (dev, desc, op, count)
+ char *dev;
+ int desc;
+ short op;
+ int count;
+{
+ struct mtop control;
+
+ control.mt_op = op;
+ control.mt_count = count;
+ /* Debian hack: The rmtioctl function returns -1 in case of an
+ error, not 0. This bug has been reported to
+ "bug-gnu-utils@prep.ai.mit.edu". (96/7/10) -BEM */
+ if (rmtioctl (desc, MTIOCTOP, &control) == -1)
+ error (2, errno, "%s", dev);
+}
+
+void
+print_status (dev, desc)
+ char *dev;
+ int desc;
+{
+ struct mtget status;
+
+ if (rmtioctl (desc, MTIOCGET, &status) == -1)
+ error (2, errno, "%s", dev);
+
+ printf ("drive type = %d\n", (int) status.mt_type);
+#if defined(hpux) || defined(__hpux)
+ printf ("drive status (high) = %d\n", (int) status.mt_dsreg1);
+ printf ("drive status (low) = %d\n", (int) status.mt_dsreg2);
+#else
+ printf ("drive status = %d\n", (int) status.mt_dsreg);
+#endif
+ printf ("sense key error = %d\n", (int) status.mt_erreg);
+ printf ("residue count = %d\n", (int) status.mt_resid);
+#if !defined(ultrix) && !defined(__ultrix__) && !defined(hpux) && !defined(__hpux) && !defined(__osf__)
+ printf ("file number = %d\n", (int) status.mt_fileno);
+ printf ("block number = %d\n", (int) status.mt_blkno);
+#endif
+}
+
+void
+usage (fp, status)
+ FILE *fp;
+ int status;
+{
+ fprintf (fp, _("\
+Usage: %s [-V] [-f device] [--file=device] [--rsh-command=command]\n\
+\t[--help] [--version] operation [count]\n"),
+ program_name);
+ exit (status);
+}
diff --git a/src/rmt.c b/src/rmt.c
new file mode 100644
index 0000000..d616b9e
--- a/dev/null
+++ b/src/rmt.c
@@ -0,0 +1,473 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1983 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+/*
+ * rmt
+ */
+#if defined(HAVE_CONFIG_H)
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#ifndef __linux
+/* Kludge alert: We should really fix this by having configure figure
+ out if we need sgtty.h or not, so it can define HAVE_SGGTY_H and
+ also add rmt to the build targets. For now we'll just add this
+ ifndef so make rmt will work on Linux. */
+#include <sgtty.h>
+#endif
+#include <sys/types.h>
+#include <sys/socket.h>
+#ifdef HAVE_SYS_GENTAPE_H /* e.g., ISC UNIX */
+#include <sys/gentape.h>
+#else
+#ifdef M_UNIX
+#include <sys/tape.h>
+#else
+#include <sys/mtio.h>
+#endif
+#endif
+#include <errno.h>
+
+/* Debian hack: gcc has exhibited problems loading fcntl.h. Therefore,
+ I removed the preprocessor conditionals here - BEM */
+#include <fcntl.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#else
+long lseek ();
+#endif
+
+#ifdef STDC_HEADERS
+#include <string.h>
+#include <stdlib.h>
+#else
+extern char *malloc ();
+#endif
+
+#include <errno.h>
+#if !HAVE_DECL_STRERROR
+extern const char *strerror (int en);
+#endif
+
+int tape = -1;
+
+char *record;
+int maxrecsize = -1;
+char *checkbuf ();
+void getstring ();
+void error ();
+
+#define SSIZE 64
+char device[SSIZE];
+char count[SSIZE], mode[SSIZE], pos[SSIZE], op[SSIZE];
+
+char resp[BUFSIZ];
+
+FILE *debug;
+#define DEBUG(f) if (debug) fprintf(debug, f)
+#define DEBUG1(f,a) if (debug) fprintf(debug, f, a)
+#define DEBUG2(f,a1,a2) if (debug) fprintf(debug, f, a1, a2)
+
+#ifdef EXTENDED_RMT_PROTOCOL
+/*
+ * Support for Sun's extended RMT protocol
+ */
+#define RMTI_VERSION -1
+#define RMT_VERSION 1
+
+/* Extended 'i' commands */
+#define RMTI_CACHE 0
+#define RMTI_NOCACHE 1
+#define RMTI_RETEN 2
+#define RMTI_ERASE 3
+#define RMTI_EOM 4
+#define RMTI_NBSF 5
+
+/* Extended 's' comands */
+#define MTS_TYPE 'T'
+#define MTS_DSREG 'D'
+#define MTS_ERREG 'E'
+#define MTS_RESID 'R'
+#define MTS_FILENO 'F'
+#define MTS_BLKNO 'B'
+#define MTS_FLAGS 'f'
+#define MTS_BF 'b'
+#endif /* EXTENDED_RMT_PROTOCOL */
+
+static struct option gnu_options[] =
+ {
+ {"version", no_argument, 0, 'v'},
+ {"help", no_argument, 0, 'h'},
+ {0, 0, 0, 0}
+ };
+
+void
+rmt_help ()
+{
+ printf ("GNU rmt: remote magtape protocol module\n");
+ printf ("usage: rmt [debug_file]\n");
+}
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int rval;
+ char c;
+ int n, i, cc;
+
+ while ((c = getopt_long (argc, argv, "vh", gnu_options, &n)) != EOF)
+ switch (c) {
+ case 'v':
+ printf ("rmt (%s)\n", PACKAGE_STRING);
+ return 0;
+
+ case 'h':
+ rmt_help ();
+ return 0;
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc > 0)
+ {
+ debug = fopen (*argv, "w");
+ if (debug == 0)
+ exit (1);
+ (void) setbuf (debug, (char *) 0);
+ }
+top:
+ errno = 0;
+ rval = 0;
+ if (read (0, &c, 1) != 1)
+ exit (0);
+ switch (c)
+ {
+
+ case 'O':
+ if (tape >= 0)
+ (void) close (tape);
+ getstring (device);
+ getstring (mode);
+ DEBUG2 ("rmtd: O %s %s\n", device, mode);
+#if defined (i386) && defined (AIX)
+ /* This is alleged to fix a byte ordering problem. */
+ /* I'm quite suspicious if it's right. -- mib */
+ {
+ int oflag = atoi (mode);
+ int nflag = 0;
+ if ((oflag & 3) == 0)
+ nflag |= O_RDONLY;
+ if (oflag & 1)
+ nflag |= O_WRONLY;
+ if (oflag & 2)
+ nflag |= O_RDWR;
+ if (oflag & 0x0008)
+ nflag |= O_APPEND;
+ if (oflag & 0x0200)
+ nflag |= O_CREAT;
+ if (oflag & 0x0400)
+ nflag |= O_TRUNC;
+ if (oflag & 0x0800)
+ nflag |= O_EXCL;
+ tape = open (device, nflag, 0666);
+ }
+#else
+ tape = open (device, atoi (mode), 0666);
+#endif
+ if (tape < 0)
+ goto ioerror;
+ goto respond;
+
+ case 'C':
+ DEBUG ("rmtd: C\n");
+ getstring (device); /* discard */
+ if (close (tape) < 0)
+ goto ioerror;
+ tape = -1;
+ goto respond;
+
+ case 'L':
+ getstring (count);
+ getstring (pos);
+ DEBUG2 ("rmtd: L %s %s\n", count, pos);
+ rval = lseek (tape, (long) atoi (count), atoi (pos));
+ if (rval < 0)
+ goto ioerror;
+ goto respond;
+
+ case 'W':
+ getstring (count);
+ n = atoi (count);
+ DEBUG1 ("rmtd: W %s\n", count);
+ record = checkbuf (record, n);
+ for (i = 0; i < n; i += cc)
+ {
+ cc = read (0, &record[i], n - i);
+ if (cc <= 0)
+ {
+ DEBUG ("rmtd: premature eof\n");
+ exit (2);
+ }
+ }
+ rval = write (tape, record, n);
+ if (rval < 0)
+ goto ioerror;
+ goto respond;
+
+ case 'R':
+ getstring (count);
+ DEBUG1 ("rmtd: R %s\n", count);
+ n = atoi (count);
+ record = checkbuf (record, n);
+ rval = read (tape, record, n);
+ if (rval < 0)
+ goto ioerror;
+ (void) sprintf (resp, "A%d\n", rval);
+ (void) write (1, resp, strlen (resp));
+ (void) write (1, record, rval);
+ goto top;
+
+ case 'I':
+ getstring (op);
+ getstring (count);
+ DEBUG2 ("rmtd: I %s %s\n", op, count);
+#ifdef EXTENDED_RMT_PROTOCOL
+ if (atoi(op) == RMTI_VERSION)
+ {
+ rval = RMT_VERSION;
+ }
+#endif /* EXTENDED_RMT_PROTOCOL */
+#ifdef MTIOCTOP
+#ifdef EXTENDED_RMT_PROTOCOL
+ else
+#endif /* EXTENDED_RMT_PROTOCOL */
+ {
+ struct mtop mtop;
+ mtop.mt_op = atoi (op);
+ mtop.mt_count = atoi (count);
+ if (ioctl (tape, MTIOCTOP, (char *) &mtop) < 0)
+ goto ioerror;
+ rval = mtop.mt_count;
+ }
+#endif
+ goto respond;
+
+#ifdef EXTENDED_RMT_PROTOCOL
+ case 'i':
+ {
+ struct mtop mtop;
+
+ getstring (op);
+ getstring (count);
+ DEBUG2 ("rmtd: i %s %s\n", op, count);
+ switch (atoi(op))
+ {
+#ifdef MTCACHE
+ case RMTI_CACHE:
+ mtop.mt_op = MTCACHE;
+ break;
+#endif
+#ifdef MTNOCACHE
+ case RMTI_NOCACHE:
+ mtop.mt_op = MTNOCACHE;
+ break;
+#endif
+#ifdef MTRETEN
+ case RMTI_RETEN:
+ mtop.mt_op = MTRETEN;
+ break;
+#endif
+#ifdef MTERASE
+ case RMTI_ERASE:
+ mtop.mt_op = MTERASE;
+ break;
+#endif
+#ifdef MTEOM
+ case RMTI_EOM:
+ mtop.mt_op = MTEOM;
+ break;
+#endif
+#ifdef MTNBSF
+ case RMTI_NBSF:
+ mtop.mt_op = MTNBSF;
+ break;
+#endif
+ default:
+ errno = EINVAL;
+ goto ioerror;
+ }
+#ifdef MTIOCTOP
+ mtop.mt_count = atoi (count);
+ if (ioctl (tape, MTIOCTOP, (char *) &mtop) < 0)
+ goto ioerror;
+ rval = mtop.mt_count;
+ }
+#endif
+ goto respond;
+#endif /* EXTENDED_RMT_PROTOCOL */
+
+ case 'S': /* status */
+ DEBUG ("rmtd: S\n");
+ {
+#ifdef MTIOCGET
+ struct mtget mtget;
+ if (ioctl (tape, MTIOCGET, (char *) &mtget) < 0)
+ goto ioerror;
+ rval = sizeof (mtget);
+ (void) sprintf (resp, "A%d\n", rval);
+ (void) write (1, resp, strlen (resp));
+ (void) write (1, (char *) &mtget, sizeof (mtget));
+#endif
+ goto top;
+ }
+
+#ifdef EXTENDED_RMT_PROTOCOL
+ case 's':
+ {
+ char s;
+ struct mtget mtget;
+
+ if (read (0, &s, 1) != 1)
+ goto top;
+
+#ifdef MTIOCGET
+ if (ioctl (tape, MTIOCGET, (char *) &mtget) < 0)
+ goto ioerror;
+#endif
+ switch (s)
+ {
+ case MTS_TYPE:
+ rval = mtget.mt_type;
+ break;
+
+ case MTS_DSREG:
+ rval = mtget.mt_dsreg;
+ break;
+
+ case MTS_ERREG:
+ rval = mtget.mt_erreg;
+ break;
+
+ case MTS_RESID:
+ rval = mtget.mt_resid;
+ break;
+
+ case MTS_FILENO:
+ rval = mtget.mt_fileno;
+ break;
+
+ case MTS_BLKNO:
+ rval = mtget.mt_blkno;
+ break;
+
+ case MTS_FLAGS:
+ rval = mtget.mt_gstat;
+ break;
+
+ case MTS_BF:
+ rval = 0;
+ break;
+
+ default:
+ errno = EINVAL;
+ goto ioerror;
+ }
+ goto respond;
+ }
+#endif /* EXTENDED_RMT_PROTOCOL */
+
+ default:
+ DEBUG1 ("rmtd: garbage command %c\n", c);
+ exit (3);
+ }
+respond:
+ DEBUG1 ("rmtd: A %d\n", rval);
+ (void) sprintf (resp, "A%d\n", rval);
+ (void) write (1, resp, strlen (resp));
+ goto top;
+ioerror:
+ error (errno);
+ goto top;
+}
+
+void
+getstring (bp)
+ char *bp;
+{
+ int i;
+ char *cp = bp;
+
+ for (i = 0; i < SSIZE; i++)
+ {
+ if (read (0, cp + i, 1) != 1)
+ exit (0);
+ if (cp[i] == '\n')
+ break;
+ }
+ cp[i] = '\0';
+}
+
+char *
+checkbuf (record, size)
+ char *record;
+ int size;
+{
+ if (size <= maxrecsize)
+ return (record);
+ if (record != 0)
+ free (record);
+ record = malloc (size);
+ if (record == 0)
+ {
+ DEBUG ("rmtd: cannot allocate buffer space\n");
+ exit (4);
+ }
+ maxrecsize = size;
+#ifdef SO_RCVBUF
+ while (size > 1024 &&
+ setsockopt (0, SOL_SOCKET, SO_RCVBUF, (char *) &size, sizeof (size)) < 0)
+ size -= 1024;
+#else
+ size = 1 + ((size - 1) % 1024);
+#endif
+ return (record);
+}
+
+void
+error (num)
+ int num;
+{
+
+/* Debian hack: rmt has problems on systems (such as the Hurd) where
+ sys_errlist is not available therefore I borrowed some code from
+ error.c to fix this problem. This has been reported to the upstream
+ maintainers. (7/22/99) - BEM */
+ DEBUG2 ("rmtd: E %d (%s)\n", num, strerror (num));
+ (void) sprintf (resp, "E%d\n%s\n", num, strerror (num));
+ (void) write (1, resp, strlen (resp));
+}
diff --git a/src/rmt.h b/src/rmt.h
new file mode 100644
index 0000000..5edea96
--- a/dev/null
+++ b/src/rmt.h
@@ -0,0 +1,98 @@
+/* Definitions for communicating with a remote tape drive.
+Copyright (C) 1988, 1992, 2001 Free Software Foundation, Inc.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#if !defined(_POSIX_VERSION)
+#ifdef __MSDOS__
+#include <io.h>
+#else /* !__MSDOS__ */
+extern off_t lseek ();
+#endif /* __MSDOS__ */
+#endif /* _POSIX_VERSION */
+
+#ifdef NO_REMOTE
+#define _isrmt(f) 0
+#define rmtopen open
+#define rmtaccess access
+#define rmtstat stat
+#define rmtcreat creat
+#define rmtlstat lstat
+#define rmtread read
+#define rmtwrite write
+#define rmtlseek lseek
+#define rmtclose close
+#define rmtioctl ioctl
+#define rmtdup dup
+#define rmtfstat fstat
+#define rmtfcntl fcntl
+#define rmtisatty isatty
+
+#else /* !NO_REMOTE */
+
+#define __REM_BIAS 128
+#define RMTIOCTL
+
+#ifndef O_CREAT
+#define O_CREAT 01000
+#endif
+
+extern char *__rmt_path;
+
+#if defined(STDC_HEADERS) || defined(HAVE_STRING_H)
+#include <string.h>
+#ifndef index
+#define index strchr
+#endif
+#else
+extern char *index ();
+#endif
+
+#define _remdev(path) (!f_force_local && (__rmt_path=index(path, ':')))
+#define _isrmt(fd) ((fd) >= __REM_BIAS)
+
+#define rmtopen(path,oflag,mode,rsh) (_remdev(path) ? __rmt_open(path, oflag, mode, __REM_BIAS,rsh) : open(path, oflag, mode))
+#define rmtaccess(path, amode) (_remdev(path) ? 0 : access(path, amode))
+#define rmtstat(path, buf) (_remdev(path) ? (errno = EOPNOTSUPP), -1 : stat(path, buf))
+#define rmtcreat(path, mode, rsh) (_remdev(path) ? __rmt_open (path, 1 | O_CREAT, mode, __REM_BIAS, rsh) : creat(path, mode))
+#define rmtlstat(path,buf) (_remdev(path) ? (errno = EOPNOTSUPP), -1 : lstat(path,buf))
+
+#define rmtread(fd, buf, n) (_isrmt(fd) ? __rmt_read(fd - __REM_BIAS, buf, n) : read(fd, buf, n))
+#define rmtwrite(fd, buf, n) (_isrmt(fd) ? __rmt_write(fd - __REM_BIAS, buf, n) : write(fd, buf, n))
+#define rmtlseek(fd, off, wh) (_isrmt(fd) ? __rmt_lseek(fd - __REM_BIAS, off, wh) : lseek(fd, off, wh))
+#define rmtclose(fd) (_isrmt(fd) ? __rmt_close(fd - __REM_BIAS) : close(fd))
+#ifdef RMTIOCTL
+#define rmtioctl(fd,req,arg) (_isrmt(fd) ? __rmt_ioctl(fd - __REM_BIAS, req, arg) : ioctl(fd, req, arg))
+#else
+#define rmtioctl(fd,req,arg) (_isrmt(fd) ? (errno = EOPNOTSUPP), -1 : ioctl(fd, req, arg))
+#endif
+#define rmtdup(fd) (_isrmt(fd) ? (errno = EOPNOTSUPP), -1 : dup(fd))
+#define rmtfstat(fd, buf) (_isrmt(fd) ? (errno = EOPNOTSUPP), -1 : fstat(fd, buf))
+#define rmtfcntl(fd,cmd,arg) (_isrmt(fd) ? (errno = EOPNOTSUPP), -1 : fcntl (fd, cmd, arg))
+#define rmtisatty(fd) (_isrmt(fd) ? 0 : isatty(fd))
+
+#undef RMTIOCTL
+
+int __rmt_open ();
+int __rmt_close ();
+int __rmt_read ();
+int __rmt_write ();
+long __rmt_lseek ();
+int __rmt_ioctl ();
+#endif /* !NO_REMOTE */
diff --git a/src/rtapelib.c b/src/rtapelib.c
new file mode 100644
index 0000000..96dc82c
--- a/dev/null
+++ b/src/rtapelib.c
@@ -0,0 +1,601 @@
+/* Functions for communicating with a remote tape drive.
+ Copyright (C) 1988, 1992, 2001 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+/* The man page rmt(8) for /etc/rmt documents the remote mag tape
+ protocol which rdump and rrestore use. Unfortunately, the man
+ page is *WRONG*. The author of the routines I'm including originally
+ wrote his code just based on the man page, and it didn't work, so he
+ went to the rdump source to figure out why. The only thing he had to
+ change was to check for the 'F' return code in addition to the 'E',
+ and to separate the various arguments with \n instead of a space. I
+ personally don't think that this is much of a problem, but I wanted to
+ point it out. -- Arnold Robbins
+
+ Originally written by Jeff Lee, modified some by Arnold Robbins.
+ Redone as a library that can replace open, read, write, etc., by
+ Fred Fish, with some additional work by Arnold Robbins.
+ Modified to make all rmtXXX calls into macros for speed by Jay Fenlason.
+ Use -DHAVE_NETDB_H for rexec code, courtesy of Dan Kegel, srs!dan. */
+
+#if defined(HAVE_CONFIG_H)
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <signal.h>
+
+#ifdef HAVE_SYS_MTIO_H
+#include <sys/ioctl.h>
+#include <sys/mtio.h>
+#endif
+
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+#include <errno.h>
+#include <setjmp.h>
+#include <sys/stat.h>
+
+#if! HAVE_DECL_ERRNO
+extern int errno;
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef STDC_HEADERS
+#include <string.h>
+#include <stdlib.h>
+#endif
+
+/* Maximum size of a fully qualified host name. */
+#define MAXHOSTLEN 257
+
+/* Size of buffers for reading and writing commands to rmt.
+ (An arbitrary limit.) */
+#define CMDBUFSIZE 64
+
+#ifndef RETSIGTYPE
+#define RETSIGTYPE void
+#endif
+
+/* Maximum number of simultaneous remote tape connections.
+ (Another arbitrary limit.) */
+#define MAXUNIT 4
+
+/* Return the parent's read side of remote tape connection FILDES. */
+#define READ(fildes) (from_rmt[fildes][0])
+
+/* Return the parent's write side of remote tape connection FILDES. */
+#define WRITE(fildes) (to_rmt[fildes][1])
+
+/* The pipes for receiving data from remote tape drives. */
+static int from_rmt[MAXUNIT][2] =
+{-1, -1, -1, -1, -1, -1, -1, -1};
+
+/* The pipes for sending data to remote tape drives. */
+static int to_rmt[MAXUNIT][2] =
+{-1, -1, -1, -1, -1, -1, -1, -1};
+
+/* Temporary variable used by macros in rmt.h. */
+char *__rmt_path;
+
+/* Close remote tape connection FILDES. */
+
+static void
+_rmt_shutdown (fildes)
+ int fildes;
+{
+ close (READ (fildes));
+ close (WRITE (fildes));
+ READ (fildes) = -1;
+ WRITE (fildes) = -1;
+}
+
+/* Attempt to perform the remote tape command specified in BUF
+ on remote tape connection FILDES.
+ Return 0 if successful, -1 on error. */
+
+static int
+command (fildes, buf)
+ int fildes;
+ char *buf;
+{
+ register int buflen;
+ RETSIGTYPE (*pipe_handler) ();
+
+ /* Save the current pipe handler and try to make the request. */
+
+ pipe_handler = signal (SIGPIPE, SIG_IGN);
+ buflen = strlen (buf);
+ if (write (WRITE (fildes), buf, buflen) == buflen)
+ {
+ signal (SIGPIPE, pipe_handler);
+ return 0;
+ }
+
+ /* Something went wrong. Close down and go home. */
+
+ signal (SIGPIPE, pipe_handler);
+ _rmt_shutdown (fildes);
+ errno = EIO;
+ return -1;
+}
+
+/* Read and return the status from remote tape connection FILDES.
+ If an error occurred, return -1 and set errno. */
+
+static int
+status (fildes)
+ int fildes;
+{
+ int i;
+ char c, *cp;
+ char buffer[CMDBUFSIZE];
+
+ /* Read the reply command line. */
+
+ for (i = 0, cp = buffer; i < CMDBUFSIZE; i++, cp++)
+ {
+ if (read (READ (fildes), cp, 1) != 1)
+ {
+ _rmt_shutdown (fildes);
+ errno = EIO;
+ return -1;
+ }
+ if (*cp == '\n')
+ {
+ *cp = '\0';
+ break;
+ }
+ }
+
+ if (i == CMDBUFSIZE)
+ {
+ _rmt_shutdown (fildes);
+ errno = EIO;
+ return -1;
+ }
+
+ /* Check the return status. */
+
+ for (cp = buffer; *cp; cp++)
+ if (*cp != ' ')
+ break;
+
+ if (*cp == 'E' || *cp == 'F')
+ {
+ errno = atoi (cp + 1);
+ /* Skip the error message line. */
+ while (read (READ (fildes), &c, 1) == 1)
+ if (c == '\n')
+ break;
+
+ if (*cp == 'F')
+ _rmt_shutdown (fildes);
+
+ return -1;
+ }
+
+ /* Check for mis-synced pipes. */
+
+ if (*cp != 'A')
+ {
+ _rmt_shutdown (fildes);
+ errno = EIO;
+ return -1;
+ }
+
+ /* Got an `A' (success) response. */
+ return atoi (cp + 1);
+}
+
+#ifdef HAVE_NETDB_H
+/* Execute /etc/rmt as user USER on remote system HOST using rexec.
+ Return a file descriptor of a bidirectional socket for stdin and stdout.
+ If USER is NULL, or an empty string, use the current username.
+
+ By default, this code is not used, since it requires that
+ the user have a .netrc file in his/her home directory, or that the
+ application designer be willing to have rexec prompt for login and
+ password info. This may be unacceptable, and .rhosts files for use
+ with rsh are much more common on BSD systems. */
+
+static int
+_rmt_rexec (host, user)
+ char *host;
+ char *user;
+{
+ struct servent *rexecserv;
+ int save_stdin = dup (fileno (stdin));
+ int save_stdout = dup (fileno (stdout));
+ int tape_fd; /* Return value. */
+
+ /* When using cpio -o < filename, stdin is no longer the tty.
+ But the rexec subroutine reads the login and the passwd on stdin,
+ to allow remote execution of the command.
+ So, reopen stdin and stdout on /dev/tty before the rexec and
+ give them back their original value after. */
+ if (freopen ("/dev/tty", "r", stdin) == NULL)
+ freopen ("/dev/null", "r", stdin);
+ if (freopen ("/dev/tty", "w", stdout) == NULL)
+ freopen ("/dev/null", "w", stdout);
+
+ rexecserv = getservbyname ("exec", "tcp");
+ if (NULL == rexecserv)
+ {
+ fprintf (stderr, _("exec/tcp: service not available"));
+ exit (1);
+ }
+ if (user != NULL && *user == '\0')
+ user = NULL;
+ tape_fd = rexec (&host, rexecserv->s_port, user, NULL,
+ "/etc/rmt", (int *) NULL);
+ fclose (stdin);
+ fdopen (save_stdin, "r");
+ fclose (stdout);
+ fdopen (save_stdout, "w");
+
+ return tape_fd;
+}
+
+#endif /* HAVE_NETDB_H */
+
+/* Open a magtape device on the system specified in PATH, as the given user.
+ PATH has the form `[user@]system:/dev/????'.
+ If COMPAT is defined, it can also have the form `system[.user]:/dev/????'.
+
+ OFLAG is O_RDONLY, O_WRONLY, etc.
+ MODE is ignored; 0666 is always used.
+
+ If successful, return the remote tape pipe number plus BIAS.
+ On error, return -1. */
+
+int
+__rmt_open (path, oflag, mode, bias, remote_shell)
+ char *path;
+ int oflag;
+ int mode;
+ int bias;
+ const char *remote_shell;
+{
+ int i, rc;
+ char buffer[CMDBUFSIZE]; /* Command buffer. */
+ char system[MAXHOSTLEN]; /* The remote host name. */
+ char device[CMDBUFSIZE]; /* The remote device name. */
+ char login[CMDBUFSIZE]; /* The remote user name. */
+ char *sys, *dev, *user; /* For copying into the above buffers. */
+
+ sys = system;
+ dev = device;
+ user = login;
+
+ /* Find an unused pair of file descriptors. */
+
+ for (i = 0; i < MAXUNIT; i++)
+ if (READ (i) == -1 && WRITE (i) == -1)
+ break;
+
+ if (i == MAXUNIT)
+ {
+ errno = EMFILE;
+ return -1;
+ }
+
+ /* Pull apart the system and device, and optional user.
+ Don't munge the original string. */
+
+ while (*path != '@'
+#ifdef COMPAT
+ && *path != '.'
+#endif
+ && *path != ':')
+ {
+ *sys++ = *path++;
+ }
+ *sys = '\0';
+ path++;
+
+ if (*(path - 1) == '@')
+ {
+ /* Saw user part of user@host. Start over. */
+ strcpy (user, system);
+ sys = system;
+ while (*path != ':')
+ {
+ *sys++ = *path++;
+ }
+ *sys = '\0';
+ path++;
+ }
+#ifdef COMPAT
+ else if (*(path - 1) == '.')
+ {
+ while (*path != ':')
+ {
+ *user++ = *path++;
+ }
+ *user = '\0';
+ path++;
+ }
+#endif
+ else
+ *user = '\0';
+
+ while (*path)
+ {
+ *dev++ = *path++;
+ }
+ *dev = '\0';
+
+#ifdef HAVE_NETDB_H
+ /* Execute the remote command using rexec. */
+ READ (i) = WRITE (i) = _rmt_rexec (system, login);
+ if (READ (i) < 0)
+ return -1;
+#else /* !HAVE_NETDB_H */
+ /* Set up the pipes for the `rsh' command, and fork. */
+
+ if (pipe (to_rmt[i]) == -1 || pipe (from_rmt[i]) == -1)
+ return -1;
+
+ rc = fork ();
+ if (rc == -1)
+ return -1;
+
+ if (rc == 0)
+ {
+ /* Child. */
+ close (0);
+ dup (to_rmt[i][0]);
+ close (to_rmt[i][0]);
+ close (to_rmt[i][1]);
+
+ close (1);
+ dup (from_rmt[i][1]);
+ close (from_rmt[i][0]);
+ close (from_rmt[i][1]);
+
+ setuid (getuid ());
+ setgid (getgid ());
+
+ if (*login)
+ {
+ /* Debian hack: added remote shell command line option.
+ (98/5/20) -BEM */
+ if (remote_shell) {
+ const char *remote_shell_basename;
+ remote_shell_basename = strrchr (remote_shell, '/');
+ if (remote_shell_basename)
+ remote_shell_basename++;
+ else
+ remote_shell_basename = remote_shell;
+ execl (remote_shell, remote_shell_basename, system, "-l", login,
+ "/etc/rmt", (char *) 0);
+ } else {
+ execl ("/usr/bin/ssh", "ssh", system, "-l", login,
+ "/etc/rmt", (char *) 0);
+ execl ("/usr/bin/rsh", "rsh", system, "-l", login,
+ "/etc/rmt", (char *) 0);
+ }
+ }
+ else
+ {
+ /* Debian hack: added remote shell command line option.
+ (98/5/20) -BEM */
+ if (remote_shell) {
+ const char *remote_shell_basename;
+ remote_shell_basename = strrchr (remote_shell, '/');
+ if (remote_shell_basename)
+ remote_shell_basename++;
+ else
+ remote_shell_basename = remote_shell;
+ execl (remote_shell, remote_shell_basename, system,
+ "/etc/rmt", (char *) 0);
+ } else {
+ execl ("/usr/bin/ssh", "ssh", system,
+ "/etc/rmt", (char *) 0);
+ execl ("/usr/bin/rsh", "rsh", system,
+ "/etc/rmt", (char *) 0);
+ }
+ }
+
+ /* Bad problems if we get here. */
+
+ perror (_("cannot execute remote shell"));
+ _exit (1);
+ }
+
+ /* Parent. */
+ close (to_rmt[i][0]);
+ close (from_rmt[i][1]);
+#endif /* !HAVE_NETDB_H */
+
+ /* Attempt to open the tape device. */
+
+ sprintf (buffer, "O%s\n%d\n", device, oflag);
+ if (command (i, buffer) == -1 || status (i) == -1)
+ return -1;
+
+ return i + bias;
+}
+
+/* Close remote tape connection FILDES and shut down.
+ Return 0 if successful, -1 on error. */
+
+int
+__rmt_close (fildes)
+ int fildes;
+{
+ int rc;
+
+ if (command (fildes, "C\n") == -1)
+ return -1;
+
+ rc = status (fildes);
+ _rmt_shutdown (fildes);
+ return rc;
+}
+
+/* Read up to NBYTE bytes into BUF from remote tape connection FILDES.
+ Return the number of bytes read on success, -1 on error. */
+
+int
+__rmt_read (fildes, buf, nbyte)
+ int fildes;
+ char *buf;
+ unsigned int nbyte;
+{
+ int rc, i;
+ char buffer[CMDBUFSIZE];
+
+ sprintf (buffer, "R%d\n", nbyte);
+ if (command (fildes, buffer) == -1 || (rc = status (fildes)) == -1)
+ return -1;
+
+ for (i = 0; i < rc; i += nbyte, buf += nbyte)
+ {
+ nbyte = read (READ (fildes), buf, rc - i);
+ if (nbyte <= 0)
+ {
+ _rmt_shutdown (fildes);
+ errno = EIO;
+ return -1;
+ }
+ }
+
+ return rc;
+}
+
+/* Write NBYTE bytes from BUF to remote tape connection FILDES.
+ Return the number of bytes written on success, -1 on error. */
+
+int
+__rmt_write (fildes, buf, nbyte)
+ int fildes;
+ char *buf;
+ unsigned int nbyte;
+{
+ char buffer[CMDBUFSIZE];
+ RETSIGTYPE (*pipe_handler) ();
+
+ sprintf (buffer, "W%d\n", nbyte);
+ if (command (fildes, buffer) == -1)
+ return -1;
+
+ pipe_handler = signal (SIGPIPE, SIG_IGN);
+ if (write (WRITE (fildes), buf, nbyte) == nbyte)
+ {
+ signal (SIGPIPE, pipe_handler);
+ return status (fildes);
+ }
+
+ /* Write error. */
+ signal (SIGPIPE, pipe_handler);
+ _rmt_shutdown (fildes);
+ errno = EIO;
+ return -1;
+}
+
+/* Perform an imitation lseek operation on remote tape connection FILDES.
+ Return the new file offset if successful, -1 if on error. */
+
+long
+__rmt_lseek (fildes, offset, whence)
+ int fildes;
+ long offset;
+ int whence;
+{
+ char buffer[CMDBUFSIZE];
+
+ sprintf (buffer, "L%ld\n%d\n", offset, whence);
+ if (command (fildes, buffer) == -1)
+ return -1;
+
+ return status (fildes);
+}
+
+/* Perform a raw tape operation on remote tape connection FILDES.
+ Return the results of the ioctl, or -1 on error. */
+
+#ifdef MTIOCTOP
+int
+__rmt_ioctl (fildes, op, arg)
+ int fildes, op;
+ char *arg;
+{
+ char c;
+ int rc, cnt;
+ char buffer[CMDBUFSIZE];
+
+ switch (op)
+ {
+ default:
+ errno = EINVAL;
+ return -1;
+
+ case MTIOCTOP:
+ /* MTIOCTOP is the easy one. Nothing is transfered in binary. */
+ sprintf (buffer, "I%d\n%d\n", ((struct mtop *) arg)->mt_op,
+ ((struct mtop *) arg)->mt_count);
+ if (command (fildes, buffer) == -1)
+ return -1;
+ return status (fildes); /* Return the count. */
+
+ case MTIOCGET:
+ /* Grab the status and read it directly into the structure.
+ This assumes that the status buffer is not padded
+ and that 2 shorts fit in a long without any word
+ alignment problems; i.e., the whole struct is contiguous.
+ NOTE - this is probably NOT a good assumption. */
+
+ if (command (fildes, "S") == -1 || (rc = status (fildes)) == -1)
+ return -1;
+
+ for (; rc > 0; rc -= cnt, arg += cnt)
+ {
+ cnt = read (READ (fildes), arg, rc);
+ if (cnt <= 0)
+ {
+ _rmt_shutdown (fildes);
+ errno = EIO;
+ return -1;
+ }
+ }
+
+ /* Check for byte position. mt_type is a small integer field
+ (normally) so we will check its magnitude. If it is larger than
+ 256, we will assume that the bytes are swapped and go through
+ and reverse all the bytes. */
+
+ if (((struct mtget *) arg)->mt_type < 256)
+ return 0;
+
+ for (cnt = 0; cnt < rc; cnt += 2)
+ {
+ c = arg[cnt];
+ arg[cnt] = arg[cnt + 1];
+ arg[cnt + 1] = c;
+ }
+
+ return 0;
+ }
+}
+
+#endif
diff --git a/src/safe-stat.h b/src/safe-stat.h
new file mode 100644
index 0000000..3a37970
--- a/dev/null
+++ b/src/safe-stat.h
@@ -0,0 +1 @@
+#define SAFE_STAT(path,pbuf) stat(path,pbuf)
diff --git a/src/strdup.c b/src/strdup.c
new file mode 100644
index 0000000..6b486ca
--- a/dev/null
+++ b/src/strdup.c
@@ -0,0 +1,43 @@
+/* strdup.c -- return a newly allocated copy of a string
+ Copyright (C) 1990 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef STDC_HEADERS
+#include <string.h>
+#include <stdlib.h>
+#else
+char *malloc ();
+char *strcpy ();
+#endif
+
+/* Return a newly allocated copy of STR,
+ or 0 if out of memory. */
+
+char *
+strdup (str)
+ const char *str;
+{
+ char *newstr;
+
+ newstr = (char *) malloc (strlen (str) + 1);
+ if (newstr)
+ strcpy (newstr, str);
+ return newstr;
+}
diff --git a/src/strerror.c b/src/strerror.c
new file mode 100644
index 0000000..a838ed5
--- a/dev/null
+++ b/src/strerror.c
@@ -0,0 +1,63 @@
+/* This file is part of GNU Radius.
+ Copyright (C) 2000,2001,2002,2003 Free Software Foundation, Inc.
+
+ Written by Sergey Poznyakoff
+
+ GNU Radius is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ GNU Radius is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Radius; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#if defined(HAVE_SYS_ERRLIST)
+
+#if !HAVE_DECL_SYS_ERRLIST
+extern char *sys_errlist[];
+#endif
+#if !HAVE_DECL_SYS_NERR
+extern int sys_nerr;
+#endif
+
+char *
+strerror (int err)
+{
+ static char buf[80];
+
+ if (err > sys_nerr)
+ {
+ sprintf(buf, _("error %d"), err);
+ return buf;
+ }
+ return sys_errlist[err];
+}
+
+#else
+
+char *
+strerror (int err)
+{
+ static char buf[80];
+
+ sprintf(buf, _("error %d"), err);
+ return buf;
+}
+
+#endif /* HAVE_SYS_ERRLIST */
+
+
diff --git a/src/stripslash.c b/src/stripslash.c
new file mode 100644
index 0000000..4512262
--- a/dev/null
+++ b/src/stripslash.c
@@ -0,0 +1,43 @@
+/* stripslash.c -- remove trailing slashes from a string
+ Copyright (C) 1990 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(STDC_HEADERS) || defined(HAVE_STRING_H)
+#include <string.h>
+#else
+#include <strings.h>
+#endif
+
+/* Remove trailing slashes from PATH.
+ This is useful when using filename completion from a shell that
+ adds a "/" after directory names (such as tcsh and bash), because
+ the Unix rename and rmdir system calls return an "Invalid argument" error
+ when given a path that ends in "/" (except for the root directory). */
+
+void
+strip_trailing_slashes (path)
+ char *path;
+{
+ int last;
+
+ last = strlen (path) - 1;
+ while (last > 0 && path[last] == '/')
+ path[last--] = '\0';
+}
diff --git a/src/system.h b/src/system.h
new file mode 100644
index 0000000..275697e
--- a/dev/null
+++ b/src/system.h
@@ -0,0 +1,146 @@
+/* System dependent declarations. Requires sys/types.h.
+ Copyright (C) 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#if defined(HAVE_STRING_H)
+# include <string.h>
+#else
+# include <strings.h>
+#endif
+
+#ifndef index
+# define index strchr
+#endif
+#ifndef rindex
+# define rindex strrchr
+#endif
+#ifndef bcmp
+# define bcmp(s1, s2, n) memcmp ((s1), (s2), (n))
+#endif
+#ifndef bzero
+# define bzero(s, n) memset ((s), 0, (n))
+#endif
+
+#if !HAVE_DECL_STRDUP
+# ifdef __STDC__
+char *strdup (const char *s);
+# else /* !__STDC__ */
+char *strdup ();
+# endif /* __STDC__ */
+#endif
+
+#include <time.h>
+
+#ifndef SEEK_SET
+# define SEEK_SET 0
+# define SEEK_CUR 1
+# define SEEK_END 2
+#endif
+
+#ifndef _POSIX_VERSION
+# if defined(__MSDOS__) && !defined(__GNUC__)
+typedef long off_t;
+# endif
+off_t lseek ();
+#endif
+
+/* Since major is a function on SVR4, we can't use `ifndef major'. */
+#ifdef MAJOR_IN_MKDEV
+# include <sys/mkdev.h>
+# define HAVE_MAJOR
+#endif
+
+#ifdef MAJOR_IN_SYSMACROS
+# include <sys/sysmacros.h>
+# define HAVE_MAJOR
+#endif
+
+#ifdef major /* Might be defined in sys/types.h. */
+# define HAVE_MAJOR
+#endif
+
+#ifndef HAVE_MAJOR
+# define major(dev) (((dev) >> 8) & 0xff)
+# define minor(dev) ((dev) & 0xff)
+# defin makedev(ma, mi) (((ma) << 8) | (mi))
+#endif
+#undef HAVE_MAJOR
+
+#if defined(__MSDOS__) || defined(_POSIX_VERSION) || defined(HAVE_FCNTL_H)
+# include <fcntl.h>
+#else
+# include <sys/file.h>
+#endif
+#ifndef O_BINARY
+# define O_BINARY 0
+#endif
+
+#include <errno.h>
+#ifndef errno
+extern int errno;
+#endif
+#ifdef __EMX__ /* gcc on OS/2. */
+# define EPERM EACCES
+# define ENXIO EIO
+#endif
+
+#ifdef HAVE_UTIME_H
+# include <utime.h>
+#else
+struct utimbuf
+{
+ time_t actime;
+ time_t modtime;
+};
+#endif
+
+#ifdef TRUE
+# undef TRUE
+#endif
+#define TRUE 1
+#ifdef FALSE
+# undef FALSE
+#endif
+#define FALSE 0
+
+#ifndef __MSDOS__
+#define CONSOLE "/dev/tty"
+#else
+#define CONSOLE "con"
+#endif
+
+/* On most systems symlink() always creates links with rwxrwxrwx
+ protection modes, but on some (HP/UX 8.07; I think maybe DEC's OSF
+ on MIPS too) symlink() uses the value of umask, so links' protection modes
+ aren't always rwxrwxrwx. There doesn't seem to be any way to change
+ the modes of a link (no system call like, say, lchmod() ), it seems
+ the only way to set the modes right is to set umask before calling
+ symlink(). */
+
+#ifndef SYMLINK_USES_UMASK
+# define UMASKED_SYMLINK(name1,name2,mode) symlink(name1,name2)
+#else
+# define UMASKED_SYMLINK(name1,name2,mode) umasked_symlink(name1,name2,mode)
+#endif /* SYMLINK_USES_UMASK */
+
diff --git a/src/tar.c b/src/tar.c
new file mode 100644
index 0000000..05569c5
--- a/dev/null
+++ b/src/tar.c
@@ -0,0 +1,528 @@
+/* tar.c - read in write tar headers for cpio
+ Copyright (C) 1992, 2001 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#if defined(HAVE_CONFIG_H)
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "filetypes.h"
+#include "system.h"
+#include "cpiohdr.h"
+#include "dstring.h"
+#include "extern.h"
+#include "rmt.h"
+#include "tarhdr.h"
+
+static void to_oct ();
+static char *stash_tar_linkname ();
+static char *stash_tar_filename ();
+
+/* Compute and return a checksum for TAR_HDR,
+ counting the checksum bytes as if they were spaces. */
+
+unsigned long
+tar_checksum (tar_hdr)
+ struct tar_header *tar_hdr;
+{
+ unsigned long sum = 0;
+ char *p = (char *) tar_hdr;
+ char *q = p + TARRECORDSIZE;
+ int i;
+
+ while (p < tar_hdr->chksum)
+ sum += *p++ & 0xff;
+ for (i = 0; i < 8; ++i)
+ {
+ sum += ' ';
+ ++p;
+ }
+ while (p < q)
+ sum += *p++ & 0xff;
+ return sum;
+}
+
+/* Write out header FILE_HDR, including the file name, to file
+ descriptor OUT_DES. */
+
+void
+write_out_tar_header (file_hdr, out_des)
+ struct new_cpio_header *file_hdr;
+ int out_des;
+{
+ int name_len;
+ union tar_record tar_rec;
+ struct tar_header *tar_hdr = (struct tar_header *) &tar_rec;
+
+ bzero ((char *) &tar_rec, TARRECORDSIZE);
+
+ /* process_copy_out must ensure that file_hdr->c_name is short enough,
+ or we will lose here. */
+
+ name_len = strlen (file_hdr->c_name);
+ if (name_len <= TARNAMESIZE)
+ {
+ strncpy (tar_hdr->name, file_hdr->c_name, name_len);
+ }
+ else
+ {
+ /* Fit as much as we can into `name', the rest into `prefix'. */
+ char *suffix = file_hdr->c_name + name_len - TARNAMESIZE;
+
+ /* We have to put the boundary at a slash. */
+ name_len = TARNAMESIZE;
+ while (*suffix != '/')
+ {
+ --name_len;
+ ++suffix;
+ }
+ strncpy (tar_hdr->name, suffix + 1, name_len);
+ strncpy (tar_hdr->prefix, file_hdr->c_name, suffix - file_hdr->c_name);
+ }
+
+ /* SVR4 seems to want the whole mode, not just protection modes.
+ Nobody else seems to care, so we might as well put it all in. */
+ to_oct (file_hdr->c_mode, 8, tar_hdr->mode);
+ to_oct (file_hdr->c_uid, 8, tar_hdr->uid);
+ to_oct (file_hdr->c_gid, 8, tar_hdr->gid);
+ to_oct (file_hdr->c_filesize, 12, tar_hdr->size);
+ to_oct (file_hdr->c_mtime, 12, tar_hdr->mtime);
+
+ switch (file_hdr->c_mode & CP_IFMT)
+ {
+ case CP_IFREG:
+ if (file_hdr->c_tar_linkname)
+ {
+ /* process_copy_out makes sure that c_tar_linkname is shorter
+ than TARLINKNAMESIZE. */
+ strncpy (tar_hdr->linkname, file_hdr->c_tar_linkname,
+ TARLINKNAMESIZE);
+ tar_hdr->typeflag = LNKTYPE;
+ to_oct (0, 12, tar_hdr->size);
+ }
+ else
+ tar_hdr->typeflag = REGTYPE;
+ break;
+ case CP_IFDIR:
+ tar_hdr->typeflag = DIRTYPE;
+ break;
+#ifndef __MSDOS__
+ case CP_IFCHR:
+ tar_hdr->typeflag = CHRTYPE;
+ break;
+ case CP_IFBLK:
+ tar_hdr->typeflag = BLKTYPE;
+ break;
+#ifdef CP_IFIFO
+ case CP_IFIFO:
+ tar_hdr->typeflag = FIFOTYPE;
+ break;
+#endif /* CP_IFIFO */
+#ifdef CP_IFLNK
+ case CP_IFLNK:
+ tar_hdr->typeflag = SYMTYPE;
+ /* process_copy_out makes sure that c_tar_linkname is shorter
+ than TARLINKNAMESIZE. */
+ strncpy (tar_hdr->linkname, file_hdr->c_tar_linkname,
+ TARLINKNAMESIZE);
+ to_oct (0, 12, tar_hdr->size);
+ break;
+#endif /* CP_IFLNK */
+#endif /* !__MSDOS__ */
+ }
+
+ if (archive_format == arf_ustar)
+ {
+ char *name;
+
+ strncpy (tar_hdr->magic, TMAGIC, TMAGLEN);
+ strncpy (tar_hdr->magic + TMAGLEN, TVERSION, TVERSLEN);
+
+#ifndef __MSDOS__
+ name = getuser (file_hdr->c_uid);
+ if (name)
+ strcpy (tar_hdr->uname, name);
+ name = getgroup (file_hdr->c_gid);
+ if (name)
+ strcpy (tar_hdr->gname, name);
+#endif
+
+ to_oct (file_hdr->c_rdev_maj, 8, tar_hdr->devmajor);
+ to_oct (file_hdr->c_rdev_min, 8, tar_hdr->devminor);
+ }
+
+ to_oct (tar_checksum (tar_hdr), 8, tar_hdr->chksum);
+
+ tape_buffered_write ((char *) &tar_rec, out_des, TARRECORDSIZE);
+}
+
+/* Return nonzero iff all the bytes in BLOCK are NUL.
+ SIZE is the number of bytes to check in BLOCK; it must be a
+ multiple of sizeof (long). */
+
+int
+null_block (block, size)
+ long *block;
+ int size;
+{
+ register long *p = block;
+ register int i = size / sizeof (long);
+
+ while (i--)
+ if (*p++)
+ return 0;
+ return 1;
+}
+
+/* Read a tar header, including the file name, from file descriptor IN_DES
+ into FILE_HDR. */
+
+void
+read_in_tar_header (file_hdr, in_des)
+ struct new_cpio_header *file_hdr;
+ int in_des;
+{
+ long bytes_skipped = 0;
+ int warned = FALSE;
+ union tar_record tar_rec;
+ struct tar_header *tar_hdr = (struct tar_header *) &tar_rec;
+#ifndef __MSDOS__
+ uid_t *uidp;
+ gid_t *gidp;
+#endif
+
+ tape_buffered_read ((char *) &tar_rec, in_des, TARRECORDSIZE);
+
+ /* Check for a block of 0's. */
+ if (null_block ((long *) &tar_rec, TARRECORDSIZE))
+ {
+#if 0
+ /* Found one block of 512 0's. If the next block is also all 0's
+ then this is the end of the archive. If not, assume the
+ previous block was all corruption and continue reading
+ the archive. */
+ /* Commented out because GNU tar sometimes creates archives with
+ only one block of 0's at the end. This happened for the
+ cpio 2.0 distribution! */
+ tape_buffered_read ((char *) &tar_rec, in_des, TARRECORDSIZE);
+ if (null_block ((long *) &tar_rec, TARRECORDSIZE))
+#endif
+ {
+ file_hdr->c_name = "TRAILER!!!";
+ return;
+ }
+#if 0
+ bytes_skipped = TARRECORDSIZE;
+#endif
+ }
+
+ while (1)
+ {
+ otoa (tar_hdr->chksum, &file_hdr->c_chksum);
+
+ if (file_hdr->c_chksum != tar_checksum (tar_hdr))
+ {
+ /* If the checksum is bad, skip 1 byte and try again. When
+ we try again we do not look for an EOF record (all zeros),
+ because when we start skipping bytes in a corrupted archive
+ the chances are pretty good that we might stumble across
+ 2 blocks of 512 zeros (that probably is not really the last
+ record) and it is better to miss the EOF and give the user
+ a "premature EOF" error than to give up too soon on a corrupted
+ archive. */
+ if (!warned)
+ {
+ error (0, 0, _("invalid header: checksum error"));
+ warned = TRUE;
+ }
+ bcopy (((char *) &tar_rec) + 1, (char *) &tar_rec,
+ TARRECORDSIZE - 1);
+ tape_buffered_read (((char *) &tar_rec) + (TARRECORDSIZE - 1), in_des, 1);
+ ++bytes_skipped;
+ continue;
+ }
+
+ if (archive_format != arf_ustar)
+ file_hdr->c_name = stash_tar_filename (NULL, tar_hdr->name);
+ else
+ file_hdr->c_name = stash_tar_filename (tar_hdr->prefix, tar_hdr->name);
+ file_hdr->c_nlink = 1;
+ otoa (tar_hdr->mode, &file_hdr->c_mode);
+ file_hdr->c_mode = file_hdr->c_mode & 07777;
+ /* Debian hack: This version of cpio uses the -n flag also to extract
+ tar archives using the numeric UID/GID instead of the user/group
+ names in /etc/passwd and /etc/groups. (98/10/15) -BEM */
+#ifndef __MSDOS__
+ if (archive_format == arf_ustar && !numeric_uid
+ && (uidp = getuidbyname (tar_hdr->uname)))
+ file_hdr->c_uid = *uidp;
+ else
+#endif
+ otoa (tar_hdr->uid, &file_hdr->c_uid);
+#ifndef __MSDOS__
+ if (archive_format == arf_ustar && !numeric_uid
+ && (gidp = getgidbyname (tar_hdr->gname)))
+ file_hdr->c_gid = *gidp;
+ else
+#endif
+ otoa (tar_hdr->gid, &file_hdr->c_gid);
+ otoa (tar_hdr->size, &file_hdr->c_filesize);
+ otoa (tar_hdr->mtime, &file_hdr->c_mtime);
+ otoa (tar_hdr->devmajor, (unsigned long *) &file_hdr->c_rdev_maj);
+ otoa (tar_hdr->devminor, (unsigned long *) &file_hdr->c_rdev_min);
+ file_hdr->c_tar_linkname = NULL;
+
+ switch (tar_hdr->typeflag)
+ {
+ case REGTYPE:
+ case CONTTYPE: /* For now, punt. */
+ default:
+ file_hdr->c_mode |= CP_IFREG;
+ break;
+ case DIRTYPE:
+ file_hdr->c_mode |= CP_IFDIR;
+ break;
+#ifndef __MSDOS__
+ case CHRTYPE:
+ file_hdr->c_mode |= CP_IFCHR;
+ /* If a POSIX tar header has a valid linkname it's always supposed
+ to set typeflag to be LNKTYPE. System V.4 tar seems to
+ be broken, and for device files with multiple links it
+ puts the name of the link into linkname, but leaves typeflag
+ as CHRTYPE, BLKTYPE, FIFOTYPE, etc. */
+ file_hdr->c_tar_linkname = stash_tar_linkname (tar_hdr->linkname);
+
+ /* Does POSIX say that the filesize must be 0 for devices? We
+ assume so, but HPUX's POSIX tar sets it to be 1 which causes
+ us problems (when reading an archive we assume we can always
+ skip to the next file by skipping filesize bytes). For
+ now at least, it's easier to clear filesize for devices,
+ rather than check everywhere we skip in copyin.c. */
+ file_hdr->c_filesize = 0;
+ break;
+ case BLKTYPE:
+ file_hdr->c_mode |= CP_IFBLK;
+ file_hdr->c_tar_linkname = stash_tar_linkname (tar_hdr->linkname);
+ file_hdr->c_filesize = 0;
+ break;
+#ifdef CP_IFIFO
+ case FIFOTYPE:
+ file_hdr->c_mode |= CP_IFIFO;
+ file_hdr->c_tar_linkname = stash_tar_linkname (tar_hdr->linkname);
+ file_hdr->c_filesize = 0;
+ break;
+#endif
+ case SYMTYPE:
+#ifdef CP_IFLNK
+ file_hdr->c_mode |= CP_IFLNK;
+ file_hdr->c_tar_linkname = stash_tar_linkname (tar_hdr->linkname);
+ file_hdr->c_filesize = 0;
+ break;
+ /* Else fall through. */
+#endif
+ case LNKTYPE:
+ file_hdr->c_mode |= CP_IFREG;
+ file_hdr->c_tar_linkname = stash_tar_linkname (tar_hdr->linkname);
+ file_hdr->c_filesize = 0;
+ break;
+#endif /* !__MSDOS__ */
+ case AREGTYPE:
+ /* Old tar format; if the last char in filename is '/' then it is
+ a directory, otherwise it's a regular file. */
+ if (file_hdr->c_name[strlen (file_hdr->c_name) - 1] == '/')
+ file_hdr->c_mode |= CP_IFDIR;
+ else
+ file_hdr->c_mode |= CP_IFREG;
+ break;
+ }
+ break;
+ }
+ if (bytes_skipped > 0)
+ error (0, 0, _("warning: skipped %ld bytes of junk"), bytes_skipped);
+}
+
+/* Stash the tar linkname in static storage. */
+
+static char *
+stash_tar_linkname (linkname)
+ char *linkname;
+{
+ static char hold_tar_linkname[TARLINKNAMESIZE + 1];
+
+ strncpy (hold_tar_linkname, linkname, TARLINKNAMESIZE);
+ hold_tar_linkname[TARLINKNAMESIZE] = '\0';
+ return hold_tar_linkname;
+}
+
+/* Stash the tar filename and optional prefix in static storage. */
+
+static char *
+stash_tar_filename (prefix, filename)
+ char *prefix;
+ char *filename;
+{
+ static char hold_tar_filename[TARNAMESIZE + TARPREFIXSIZE + 2];
+ if (prefix == NULL || *prefix == '\0')
+ {
+ strncpy (hold_tar_filename, filename, TARNAMESIZE);
+ hold_tar_filename[TARNAMESIZE] = '\0';
+ }
+ else
+ {
+ strncpy (hold_tar_filename, prefix, TARPREFIXSIZE);
+ hold_tar_filename[TARPREFIXSIZE] = '\0';
+ strcat (hold_tar_filename, "/");
+ strncat (hold_tar_filename, filename, TARNAMESIZE);
+ hold_tar_filename[TARPREFIXSIZE + TARNAMESIZE] = '\0';
+ }
+ return hold_tar_filename;
+}
+
+/* Convert the string of octal digits S into a number and store
+ it in *N. Return nonzero if the whole string was converted,
+ zero if there was something after the number.
+ Skip leading and trailing spaces. */
+
+int
+otoa (s, n)
+ char *s;
+ unsigned long *n;
+{
+ unsigned long val = 0;
+
+ while (*s == ' ')
+ ++s;
+ while (*s >= '0' && *s <= '7')
+ val = 8 * val + *s++ - '0';
+ while (*s == ' ')
+ ++s;
+ *n = val;
+ return *s == '\0';
+}
+
+/* Convert a number into a string of octal digits.
+ Convert long VALUE into a DIGITS-digit field at WHERE,
+ including a trailing space and room for a NUL. DIGITS==3 means
+ 1 digit, a space, and room for a NUL.
+
+ We assume the trailing NUL is already there and don't fill it in.
+ This fact is used by start_header and finish_header, so don't change it!
+
+ This is be equivalent to:
+ sprintf (where, "%*lo ", digits - 2, value);
+ except that sprintf fills in the trailing NUL and we don't. */
+
+static void
+to_oct (value, digits, where)
+ register long value;
+ register int digits;
+ register char *where;
+{
+ --digits; /* Leave the trailing NUL slot alone. */
+
+ /* Produce the digits -- at least one. */
+ do
+ {
+ where[--digits] = '0' + (char) (value & 7); /* One octal digit. */
+ value >>= 3;
+ }
+ while (digits > 0 && value != 0);
+
+ /* Add leading spaces, if necessary. */
+ while (digits > 0)
+ where[--digits] = ' ';
+}
+
+/* Return
+ 2 if BUF is a valid POSIX tar header (the checksum is correct
+ and it has the "ustar" magic string),
+ 1 if BUF is a valid old tar header (the checksum is correct),
+ 0 otherwise. */
+
+int
+is_tar_header (buf)
+ char *buf;
+{
+ struct tar_header *tar_hdr = (struct tar_header *) buf;
+ unsigned long chksum;
+
+ otoa (tar_hdr->chksum, &chksum);
+
+ if (chksum != tar_checksum (tar_hdr))
+ return 0;
+
+ /* GNU tar 1.10 and previous set the magic field to be "ustar " instead
+ of "ustar\0". Only look at the first 5 characters of the magic
+ field so we can recognize old GNU tar ustar archives. */
+ if (!strncmp (tar_hdr->magic, TMAGIC, TMAGLEN - 1))
+ return 2;
+ return 1;
+}
+
+/* Return TRUE if the filename is too long to fit in a tar header.
+ For old tar headers, if the filename's length is less than or equal
+ to 100 then it will fit, otherwise it will not. For POSIX tar headers,
+ if the filename's length is less than or equal to 100 then it
+ will definitely fit, and if it is greater than 256 then it
+ will definitely not fit. If the length is between 100 and 256,
+ then the filename will fit only if it is possible to break it
+ into a 155 character "prefix" and 100 character "name". There
+ must be a slash between the "prefix" and the "name", although
+ the slash is not stored or counted in either the "prefix" or
+ the "name", and there must be at least one character in both
+ the "prefix" and the "name". If it is not possible to break down
+ the filename like this then it will not fit. */
+
+int
+is_tar_filename_too_long (name)
+ char *name;
+{
+ int whole_name_len;
+ int prefix_name_len;
+ char *p;
+
+ whole_name_len = strlen (name);
+ if (whole_name_len <= TARNAMESIZE)
+ return FALSE;
+
+ if (archive_format != arf_ustar)
+ return TRUE;
+
+ if (whole_name_len > TARNAMESIZE + TARPREFIXSIZE + 1)
+ return TRUE;
+
+ /* See whether we can split up the name into acceptably-sized
+ `prefix' and `name' (`p') pieces. Start out by making `name'
+ as big as possible, then shrink it by looking for the first '/'. */
+ p = name + whole_name_len - TARNAMESIZE;
+ while (*p != '/' && *p != '\0')
+ ++p;
+ if (*p == '\0')
+ /* The last component of the path is longer than TARNAMESIZE. */
+ return TRUE;
+
+ prefix_name_len = p - name - 1;
+ /* Interestingly, a name consisting of a slash followed by
+ TARNAMESIZE characters can't be stored, because the prefix
+ would be empty, and thus ignored. */
+ if (prefix_name_len > TARPREFIXSIZE || prefix_name_len == 0)
+ return TRUE;
+
+ return FALSE;
+}
diff --git a/src/tar.h b/src/tar.h
new file mode 100644
index 0000000..79f289e
--- a/dev/null
+++ b/src/tar.h
@@ -0,0 +1,112 @@
+/* Extended tar format from POSIX.1.
+ Copyright (C) 1992 Free Software Foundation, Inc.
+ Written by David J. MacKenzie.
+
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this library; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc.,
+59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef _TAR_H
+
+#define _TAR_H 1
+
+
+/* A tar archive consists of 512-byte blocks.
+ Each file in the archive has a header block followed by 0+ data blocks.
+ Two blocks of NUL bytes indicate the end of the archive. */
+
+/* The fields of header blocks:
+ All strings are stored as ISO 646 (approximately ASCII) strings.
+
+ Fields are numeric unless otherwise noted below; numbers are ISO 646
+ representations of octal numbers, with leading zeros as needed.
+
+ linkname is only valid when typeflag==LNKTYPE. It doesn't use prefix;
+ files that are links to pathnames >100 chars long can not be stored
+ in a tar archive.
+
+ If typeflag=={LNKTYPE,SYMTYPE,DIRTYPE} then size must be 0.
+
+ devmajor and devminor are only valid for typeflag=={BLKTYPE,CHRTYPE}.
+
+ chksum contains the sum of all 512 bytes in the header block,
+ treating each byte as an 8-bit unsigned value and treating the
+ 8 bytes of chksum as blank characters.
+
+ uname and gname are used in preference to uid and gid, if those
+ names exist locally.
+
+ Field Name Byte Offset Length in Bytes Field Type
+ name 0 100 NUL-terminated if NUL fits
+ mode 100 8
+ uid 108 8
+ gid 116 8
+ size 124 12
+ mtime 136 12
+ chksum 148 8
+ typeflag 156 1 see below
+ linkname 157 100 NUL-terminated if NUL fits
+ magic 257 6 must be TMAGIC (NUL term.)
+ version 263 2 must be TVERSION
+ uname 265 32 NUL-terminated
+ gname 297 32 NUL-terminated
+ devmajor 329 8
+ devminor 337 8
+ prefix 345 155 NUL-terminated if NUL fits
+
+ If the first character of prefix is '\0', the file name is name;
+ otherwise, it is prefix/name. Files whose pathnames don't fit in that
+ length can not be stored in a tar archive. */
+
+/* The bits in mode: */
+#define TSUID 04000
+#define TSGID 02000
+#define TSVTX 01000
+#define TUREAD 00400
+#define TUWRITE 00200
+#define TUEXEC 00100
+#define TGREAD 00040
+#define TGWRITE 00020
+#define TGEXEC 00010
+#define TOREAD 00004
+#define TOWRITE 00002
+#define TOEXEC 00001
+
+/* The values for typeflag:
+ Values 'A'-'Z' are reserved for custom implementations.
+ All other values are reserved for future POSIX.1 revisions. */
+
+#define REGTYPE '0' /* Regular file (preferred code). */
+#define AREGTYPE '\0' /* Regular file (alternate code). */
+#define LNKTYPE '1' /* Hard link. */
+#define SYMTYPE '2' /* Symbolic link (hard if not supported). */
+#define CHRTYPE '3' /* Character special. */
+#define BLKTYPE '4' /* Block special. */
+#define DIRTYPE '5' /* Directory. */
+#define FIFOTYPE '6' /* Named pipe. */
+#define CONTTYPE '7' /* Contiguous file */
+ /* (regular file if not supported). */
+
+/* Contents of magic field and its length. */
+#define TMAGIC "ustar"
+#define TMAGLEN 6
+
+/* Contents of the version field and its length. */
+#define TVERSION "00"
+#define TVERSLEN 2
+
+
+#endif /* tar.h */
diff --git a/src/tarhdr.h b/src/tarhdr.h
new file mode 100644
index 0000000..8b1211d
--- a/dev/null
+++ b/src/tarhdr.h
@@ -0,0 +1,62 @@
+/* Extended tar header from POSIX.1.
+ Copyright (C) 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef _TARHDR_H
+
+#define _TARHDR_H 1
+
+#include <tar.h>
+
+/* Size of `name' field. */
+#define TARNAMESIZE 100
+
+/* Size of `linkname' field. */
+#define TARLINKNAMESIZE 100
+
+/* Size of `prefix' field. */
+#define TARPREFIXSIZE 155
+
+/* Size of entire tar header. */
+#define TARRECORDSIZE 512
+
+struct tar_header
+{
+ char name[TARNAMESIZE];
+ char mode[8];
+ char uid[8];
+ char gid[8];
+ char size[12];
+ char mtime[12];
+ char chksum[8];
+ char typeflag;
+ char linkname[TARLINKNAMESIZE];
+ char magic[6];
+ char version[2];
+ char uname[32];
+ char gname[32];
+ char devmajor[8];
+ char devminor[8];
+ char prefix[TARPREFIXSIZE];
+};
+
+union tar_record
+{
+ struct tar_header header;
+ char buffer[TARRECORDSIZE];
+};
+
+#endif /* tarhdr.h */
diff --git a/src/userspec.c b/src/userspec.c
new file mode 100644
index 0000000..6def61f
--- a/dev/null
+++ b/src/userspec.c
@@ -0,0 +1,265 @@
+/* userspec.c -- Parse a user and group string.
+ Copyright (C) 1989, 1990, 1991, 1992, 2001 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+/* Written by David MacKenzie <djm@gnu.ai.mit.edu>. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "system.h"
+
+#ifdef __GNUC__
+#define alloca __builtin_alloca
+#else
+#ifdef HAVE_ALLOCA_H
+#include <alloca.h>
+#else
+#ifdef _AIX
+ #pragma alloca
+#else
+char *alloca ();
+#endif
+#endif
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+
+#if !HAVE_DECL_GETPWNAM
+extern struct passwd *getpwnam (const char *name);
+#endif
+#if !HAVE_DECL_GETGRNAM
+extern struct group *getgrnam (const char *name);
+#endif
+#if !HAVE_DECL_GETGRGID
+extern struct group *getgrgid (gid_t gid);
+#endif
+
+#ifndef HAVE_ENDPWENT
+# define endpwent()
+#endif
+#ifndef HAVE_ENDGRENT
+# define endgrent()
+#endif
+
+/* Perform the equivalent of the statement `dest = strdup (src);',
+ but obtaining storage via alloca instead of from the heap. */
+
+#define V_STRDUP(dest, src) \
+ do \
+ { \
+ int _len = strlen ((src)); \
+ (dest) = (char *) alloca (_len + 1); \
+ strcpy (dest, src); \
+ } \
+ while (0)
+
+/* Return nonzero if STR represents an unsigned decimal integer,
+ otherwise return 0. */
+
+static int
+isnumber (str)
+ const char *str;
+{
+ for (; *str; str++)
+ if (!isdigit (*str))
+ return 0;
+ return 1;
+}
+
+/* Extract from NAME, which has the form "[user][:.][group]",
+ a USERNAME, UID U, GROUPNAME, and GID G.
+ Either user or group, or both, must be present.
+ If the group is omitted but the ":" or "." separator is given,
+ use the given user's login group.
+
+ USERNAME and GROUPNAME will be in newly malloc'd memory.
+ Either one might be NULL instead, indicating that it was not
+ given and the corresponding numeric ID was left unchanged.
+
+ Return NULL if successful, a static error message string if not. */
+
+const char *
+parse_user_spec (spec_arg, uid, gid, username_arg, groupname_arg)
+ const char *spec_arg;
+ uid_t *uid;
+ gid_t *gid;
+ char **username_arg, **groupname_arg;
+{
+ static const char *tired = "virtual memory exhausted";
+ const char *error_msg;
+ char *spec; /* A copy we can write on. */
+ struct passwd *pwd;
+ struct group *grp;
+ char *g, *u, *separator;
+ char *groupname;
+
+ error_msg = NULL;
+ *username_arg = *groupname_arg = NULL;
+ groupname = NULL;
+
+ V_STRDUP (spec, spec_arg);
+
+ /* Find the separator if there is one. */
+ separator = index (spec, ':');
+ if (separator == NULL)
+ separator = index (spec, '.');
+
+ /* Replace separator with a NUL. */
+ if (separator != NULL)
+ *separator = '\0';
+
+ /* Set U and G to non-zero length strings corresponding to user and
+ group specifiers or to NULL. */
+ u = (*spec == '\0' ? NULL : spec);
+
+ g = (separator == NULL || *(separator + 1) == '\0'
+ ? NULL
+ : separator + 1);
+
+ if (u == NULL && g == NULL)
+ return "can not omit both user and group";
+
+ if (u != NULL)
+ {
+ pwd = getpwnam (u);
+ if (pwd == NULL)
+ {
+
+ if (!isnumber (u))
+ error_msg = _("invalid user");
+ else
+ {
+ int use_login_group;
+ use_login_group = (separator != NULL && g == NULL);
+ if (use_login_group)
+ error_msg = _("cannot get the login group of a numeric UID");
+ else
+ *uid = atoi (u);
+ }
+ }
+ else
+ {
+ *uid = pwd->pw_uid;
+ if (g == NULL && separator != NULL)
+ {
+ /* A separator was given, but a group was not specified,
+ so get the login group. */
+ *gid = pwd->pw_gid;
+ grp = getgrgid (pwd->pw_gid);
+ if (grp == NULL)
+ {
+ /* This is enough room to hold the unsigned decimal
+ representation of any 32-bit quantity and the trailing
+ zero byte. */
+ char uint_buf[21];
+ sprintf (uint_buf, "%u", (unsigned) (pwd->pw_gid));
+ V_STRDUP (groupname, uint_buf);
+ }
+ else
+ {
+ V_STRDUP (groupname, grp->gr_name);
+ }
+ endgrent ();
+ }
+ }
+ endpwent ();
+ }
+
+ if (g != NULL && error_msg == NULL)
+ {
+ /* Explicit group. */
+ grp = getgrnam (g);
+ if (grp == NULL)
+ {
+ if (!isnumber (g))
+ error_msg = _("invalid group");
+ else
+ *gid = atoi (g);
+ }
+ else
+ *gid = grp->gr_gid;
+ endgrent (); /* Save a file descriptor. */
+
+ if (error_msg == NULL)
+ V_STRDUP (groupname, g);
+ }
+
+ if (error_msg == NULL)
+ {
+ if (u != NULL)
+ {
+ *username_arg = strdup (u);
+ if (*username_arg == NULL)
+ error_msg = tired;
+ }
+
+ if (groupname != NULL && error_msg == NULL)
+ {
+ *groupname_arg = strdup (groupname);
+ if (*groupname_arg == NULL)
+ {
+ if (*username_arg != NULL)
+ {
+ free (*username_arg);
+ *username_arg = NULL;
+ }
+ error_msg = tired;
+ }
+ }
+ }
+
+ return error_msg;
+}
+
+#ifdef TEST
+
+#define NULL_CHECK(s) ((s) == NULL ? "(null)" : (s))
+
+int
+main (int argc, char **argv)
+{
+ int i;
+
+ for (i = 1; i < argc; i++)
+ {
+ const char *e;
+ char *username, *groupname;
+ uid_t uid;
+ gid_t gid;
+ char *tmp;
+
+ tmp = strdup (argv[i]);
+ e = parse_user_spec (tmp, &uid, &gid, &username, &groupname);
+ free (tmp);
+ printf ("%s: %u %u %s %s %s\n",
+ argv[i],
+ (unsigned int) uid,
+ (unsigned int) gid,
+ NULL_CHECK (username),
+ NULL_CHECK (groupname),
+ NULL_CHECK (e));
+ }
+
+ exit (0);
+}
+
+#endif
diff --git a/src/util.c b/src/util.c
new file mode 100644
index 0000000..e98768c
--- a/dev/null
+++ b/src/util.c
@@ -0,0 +1,1376 @@
+/* util.c - Several utility routines for cpio.
+ Copyright (C) 1990, 1991, 1992, 2001 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#if defined(HAVE_CONFIG_H)
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "system.h"
+#include "cpiohdr.h"
+#include "dstring.h"
+#include "extern.h"
+#include "rmt.h"
+
+#ifndef __MSDOS__
+#include <sys/ioctl.h>
+#else
+#include <io.h>
+#endif
+
+#ifdef HAVE_SYS_MTIO_H
+#ifdef HAVE_SYS_IO_TRIOCTL_H
+#include <sys/io/trioctl.h>
+#endif
+#include <sys/mtio.h>
+#endif
+
+#if !HAVE_DECL_ERRNO
+extern int errno;
+#endif
+
+static void tape_fill_input_buffer P_((int in_des, int num_bytes));
+static int disk_fill_input_buffer P_((int in_des, int num_bytes));
+static void hash_insert ();
+static void write_nuls_to_file P_((long num_bytes, int out_des));
+
+/* Write `output_size' bytes of `output_buffer' to file
+ descriptor OUT_DES and reset `output_size' and `out_buff'. */
+
+void
+tape_empty_output_buffer (out_des)
+ int out_des;
+{
+ int bytes_written;
+
+#ifdef BROKEN_LONG_TAPE_DRIVER
+ static long output_bytes_before_lseek = 0;
+
+ /* Some tape drivers seem to have a signed internal seek pointer and
+ they lose if it overflows and becomes negative (e.g. when writing
+ tapes > 2Gb). Doing an lseek (des, 0, SEEK_SET) seems to reset the
+ seek pointer and prevent it from overflowing. */
+ if (output_is_special
+ && ( (output_bytes_before_lseek += output_size) >= 1073741824L) )
+ {
+ lseek(out_des, 0L, SEEK_SET);
+ output_bytes_before_lseek = 0;
+ }
+#endif
+
+ bytes_written = rmtwrite (out_des, output_buffer, output_size);
+ if (bytes_written != output_size)
+ {
+ int rest_bytes_written;
+ int rest_output_size;
+
+ if (output_is_special
+ && (bytes_written >= 0
+ || (bytes_written < 0
+ && (errno == ENOSPC || errno == EIO || errno == ENXIO))))
+ {
+ get_next_reel (out_des);
+ if (bytes_written > 0)
+ rest_output_size = output_size - bytes_written;
+ else
+ rest_output_size = output_size;
+ rest_bytes_written = rmtwrite (out_des, output_buffer,
+ rest_output_size);
+ if (rest_bytes_written != rest_output_size)
+ error (1, errno, _("write error"));
+ }
+ else
+ error (1, errno, _("write error"));
+ }
+ output_bytes += output_size;
+ out_buff = output_buffer;
+ output_size = 0;
+}
+
+/* Write `output_size' bytes of `output_buffer' to file
+ descriptor OUT_DES and reset `output_size' and `out_buff'.
+ If `swapping_halfwords' or `swapping_bytes' is set,
+ do the appropriate swapping first. Our callers have
+ to make sure to only set these flags if `output_size'
+ is appropriate (a multiple of 4 for `swapping_halfwords',
+ 2 for `swapping_bytes'). The fact that DISK_IO_BLOCK_SIZE
+ must always be a multiple of 4 helps us (and our callers)
+ insure this. */
+
+void
+disk_empty_output_buffer (out_des)
+ int out_des;
+{
+ int bytes_written;
+
+ if (swapping_halfwords || swapping_bytes)
+ {
+ if (swapping_halfwords)
+ {
+ int complete_words;
+ complete_words = output_size / 4;
+ swahw_array (output_buffer, complete_words);
+ if (swapping_bytes)
+ swab_array (output_buffer, 2 * complete_words);
+ }
+ else
+ {
+ int complete_halfwords;
+ complete_halfwords = output_size /2;
+ swab_array (output_buffer, complete_halfwords);
+ }
+ }
+
+ if (sparse_flag)
+ bytes_written = sparse_write (out_des, output_buffer, output_size);
+ else
+ bytes_written = write (out_des, output_buffer, output_size);
+
+ if (bytes_written != output_size)
+ {
+ error (1, errno, _("write error"));
+ }
+ output_bytes += output_size;
+ out_buff = output_buffer;
+ output_size = 0;
+}
+
+/* Exchange the halfwords of each element of the array of COUNT longs
+ starting at PTR. PTR does not have to be aligned at a word
+ boundary. */
+
+void
+swahw_array (ptr, count)
+ char *ptr;
+ int count;
+{
+ char tmp;
+
+ for (; count > 0; --count)
+ {
+ tmp = *ptr;
+ *ptr = *(ptr + 2);
+ *(ptr + 2) = tmp;
+ ++ptr;
+ tmp = *ptr;
+ *ptr = *(ptr + 2);
+ *(ptr + 2) = tmp;
+ ptr += 3;
+ }
+}
+
+/* Read at most NUM_BYTES or `io_block_size' bytes, whichever is smaller,
+ into the start of `input_buffer' from file descriptor IN_DES.
+ Set `input_size' to the number of bytes read and reset `in_buff'.
+ Exit with an error if end of file is reached. */
+
+#ifdef BROKEN_LONG_TAPE_DRIVER
+static long input_bytes_before_lseek = 0;
+#endif
+
+static void
+tape_fill_input_buffer (in_des, num_bytes)
+ int in_des;
+ int num_bytes;
+{
+#ifdef BROKEN_LONG_TAPE_DRIVER
+ /* Some tape drivers seem to have a signed internal seek pointer and
+ they lose if it overflows and becomes negative (e.g. when writing
+ tapes > 4Gb). Doing an lseek (des, 0, SEEK_SET) seems to reset the
+ seek pointer and prevent it from overflowing. */
+ if (input_is_special
+ && ( (input_bytes_before_lseek += num_bytes) >= 1073741824L) )
+ {
+ lseek(in_des, 0L, SEEK_SET);
+ input_bytes_before_lseek = 0;
+ }
+#endif
+ in_buff = input_buffer;
+ num_bytes = (num_bytes < io_block_size) ? num_bytes : io_block_size;
+ input_size = rmtread (in_des, input_buffer, num_bytes);
+ if (input_size == 0 && input_is_special)
+ {
+ get_next_reel (in_des);
+ input_size = rmtread (in_des, input_buffer, num_bytes);
+ }
+ if (input_size < 0)
+ error (1, errno, _("read error"));
+ if (input_size == 0)
+ {
+ error (0, 0, _("premature end of file"));
+ exit (1);
+ }
+ input_bytes += input_size;
+}
+
+/* Read at most NUM_BYTES or `DISK_IO_BLOCK_SIZE' bytes, whichever is smaller,
+ into the start of `input_buffer' from file descriptor IN_DES.
+ Set `input_size' to the number of bytes read and reset `in_buff'.
+ Exit with an error if end of file is reached. */
+
+static int
+disk_fill_input_buffer (in_des, num_bytes)
+ int in_des;
+ int num_bytes;
+{
+ in_buff = input_buffer;
+ num_bytes = (num_bytes < DISK_IO_BLOCK_SIZE) ? num_bytes : DISK_IO_BLOCK_SIZE;
+ input_size = read (in_des, input_buffer, num_bytes);
+ if (input_size < 0)
+ {
+ input_size = 0;
+ return (-1);
+ }
+ else if (input_size == 0)
+ return (1);
+ input_bytes += input_size;
+ return (0);
+}
+
+/* Copy NUM_BYTES of buffer IN_BUF to `out_buff', which may be partly full.
+ When `out_buff' fills up, flush it to file descriptor OUT_DES. */
+
+void
+tape_buffered_write (in_buf, out_des, num_bytes)
+ char *in_buf;
+ int out_des;
+ long num_bytes;
+{
+ register long bytes_left = num_bytes; /* Bytes needing to be copied. */
+ register long space_left; /* Room left in output buffer. */
+
+ while (bytes_left > 0)
+ {
+ space_left = io_block_size - output_size;
+ if (space_left == 0)
+ tape_empty_output_buffer (out_des);
+ else
+ {
+ if (bytes_left < space_left)
+ space_left = bytes_left;
+ bcopy (in_buf, out_buff, (unsigned) space_left);
+ out_buff += space_left;
+ output_size += space_left;
+ in_buf += space_left;
+ bytes_left -= space_left;
+ }
+ }
+}
+
+/* Copy NUM_BYTES of buffer IN_BUF to `out_buff', which may be partly full.
+ When `out_buff' fills up, flush it to file descriptor OUT_DES. */
+
+void
+disk_buffered_write (in_buf, out_des, num_bytes)
+ char *in_buf;
+ int out_des;
+ long num_bytes;
+{
+ register long bytes_left = num_bytes; /* Bytes needing to be copied. */
+ register long space_left; /* Room left in output buffer. */
+
+ while (bytes_left > 0)
+ {
+ space_left = DISK_IO_BLOCK_SIZE - output_size;
+ if (space_left == 0)
+ disk_empty_output_buffer (out_des);
+ else
+ {
+ if (bytes_left < space_left)
+ space_left = bytes_left;
+ bcopy (in_buf, out_buff, (unsigned) space_left);
+ out_buff += space_left;
+ output_size += space_left;
+ in_buf += space_left;
+ bytes_left -= space_left;
+ }
+ }
+}
+
+/* Copy NUM_BYTES of buffer `in_buff' into IN_BUF.
+ `in_buff' may be partly full.
+ When `in_buff' is exhausted, refill it from file descriptor IN_DES. */
+
+void
+tape_buffered_read (in_buf, in_des, num_bytes)
+ char *in_buf;
+ int in_des;
+ long num_bytes;
+{
+ register long bytes_left = num_bytes; /* Bytes needing to be copied. */
+ register long space_left; /* Bytes to copy from input buffer. */
+
+ while (bytes_left > 0)
+ {
+ if (input_size == 0)
+ tape_fill_input_buffer (in_des, io_block_size);
+ if (bytes_left < input_size)
+ space_left = bytes_left;
+ else
+ space_left = input_size;
+ bcopy (in_buff, in_buf, (unsigned) space_left);
+ in_buff += space_left;
+ in_buf += space_left;
+ input_size -= space_left;
+ bytes_left -= space_left;
+ }
+}
+
+/* Copy the the next NUM_BYTES bytes of `input_buffer' into PEEK_BUF.
+ If NUM_BYTES bytes are not available, read the next `io_block_size' bytes
+ into the end of `input_buffer' and update `input_size'.
+
+ Return the number of bytes copied into PEEK_BUF.
+ If the number of bytes returned is less than NUM_BYTES,
+ then EOF has been reached. */
+
+int
+tape_buffered_peek (peek_buf, in_des, num_bytes)
+ char *peek_buf;
+ int in_des;
+ int num_bytes;
+{
+ long tmp_input_size;
+ long got_bytes;
+ char *append_buf;
+
+#ifdef BROKEN_LONG_TAPE_DRIVER
+ /* Some tape drivers seem to have a signed internal seek pointer and
+ they lose if it overflows and becomes negative (e.g. when writing
+ tapes > 4Gb). Doing an lseek (des, 0, SEEK_SET) seems to reset the
+ seek pointer and prevent it from overflowing. */
+ if (input_is_special
+ && ( (input_bytes_before_lseek += num_bytes) >= 1073741824L) )
+ {
+ lseek(in_des, 0L, SEEK_SET);
+ input_bytes_before_lseek = 0;
+ }
+#endif
+
+ while (input_size < num_bytes)
+ {
+ append_buf = in_buff + input_size;
+ if ( (append_buf - input_buffer) >= input_buffer_size)
+ {
+ /* We can keep up to 2 "blocks" (either the physical block size
+ or 512 bytes(the size of a tar record), which ever is
+ larger) in the input buffer when we are peeking. We
+ assume that our caller will never be interested in peeking
+ ahead at more than 512 bytes, so we know that by the time
+ we need a 3rd "block" in the buffer we can throw away the
+ first block to make room. */
+ int half;
+ half = input_buffer_size / 2;
+ bcopy (input_buffer + half, input_buffer, half);
+ in_buff = in_buff - half;
+ append_buf = append_buf - half;
+ }
+ tmp_input_size = rmtread (in_des, append_buf, io_block_size);
+ if (tmp_input_size == 0)
+ {
+ if (input_is_special)
+ {
+ get_next_reel (in_des);
+ tmp_input_size = rmtread (in_des, append_buf, io_block_size);
+ }
+ else
+ break;
+ }
+ if (tmp_input_size < 0)
+ error (1, errno, _("read error"));
+ input_bytes += tmp_input_size;
+ input_size += tmp_input_size;
+ }
+ if (num_bytes <= input_size)
+ got_bytes = num_bytes;
+ else
+ got_bytes = input_size;
+ bcopy (in_buff, peek_buf, (unsigned) got_bytes);
+ return got_bytes;
+}
+
+/* Skip the next NUM_BYTES bytes of file descriptor IN_DES. */
+
+void
+tape_toss_input (in_des, num_bytes)
+ int in_des;
+ long num_bytes;
+{
+ register long bytes_left = num_bytes; /* Bytes needing to be copied. */
+ register long space_left; /* Bytes to copy from input buffer. */
+
+ while (bytes_left > 0)
+ {
+ if (input_size == 0)
+ tape_fill_input_buffer (in_des, io_block_size);
+ if (bytes_left < input_size)
+ space_left = bytes_left;
+ else
+ space_left = input_size;
+
+ if (crc_i_flag && only_verify_crc_flag)
+ {
+ int k;
+ for (k = 0; k < space_left; ++k)
+ crc += in_buff[k] & 0xff;
+ }
+
+ in_buff += space_left;
+ input_size -= space_left;
+ bytes_left -= space_left;
+ }
+}
+
+/* Copy a file using the input and output buffers, which may start out
+ partly full. After the copy, the files are not closed nor the last
+ block flushed to output, and the input buffer may still be partly
+ full. If `crc_i_flag' is set, add each byte to `crc'.
+ IN_DES is the file descriptor for input;
+ OUT_DES is the file descriptor for output;
+ NUM_BYTES is the number of bytes to copy. */
+
+void
+copy_files_tape_to_disk (in_des, out_des, num_bytes)
+ int in_des;
+ int out_des;
+ long num_bytes;
+{
+ long size;
+ long k;
+
+ while (num_bytes > 0)
+ {
+ if (input_size == 0)
+ tape_fill_input_buffer (in_des, io_block_size);
+ size = (input_size < num_bytes) ? input_size : num_bytes;
+ if (crc_i_flag)
+ {
+ for (k = 0; k < size; ++k)
+ crc += in_buff[k] & 0xff;
+ }
+ disk_buffered_write (in_buff, out_des, size);
+ num_bytes -= size;
+ input_size -= size;
+ in_buff += size;
+ }
+}
+/* Copy a file using the input and output buffers, which may start out
+ partly full. After the copy, the files are not closed nor the last
+ block flushed to output, and the input buffer may still be partly
+ full. If `crc_i_flag' is set, add each byte to `crc'.
+ IN_DES is the file descriptor for input;
+ OUT_DES is the file descriptor for output;
+ NUM_BYTES is the number of bytes to copy. */
+
+void
+copy_files_disk_to_tape (in_des, out_des, num_bytes, filename)
+ int in_des;
+ int out_des;
+ long num_bytes;
+ char *filename;
+{
+ long size;
+ long k;
+ int rc;
+ long original_num_bytes;
+
+ original_num_bytes = num_bytes;
+
+ while (num_bytes > 0)
+ {
+ if (input_size == 0)
+ if (rc = disk_fill_input_buffer (in_des,
+ num_bytes < DISK_IO_BLOCK_SIZE ?
+ num_bytes : DISK_IO_BLOCK_SIZE))
+ {
+ if (rc > 0)
+ error (0, 0, _("File %s shrunk by %ld bytes, padding with zeros"),
+ filename, num_bytes);
+ else
+ error (0, 0, _("Read error at byte %ld in file %s, padding with zeros"),
+ original_num_bytes - num_bytes, filename);
+ write_nuls_to_file (num_bytes, out_des);
+ break;
+ }
+ size = (input_size < num_bytes) ? input_size : num_bytes;
+ if (crc_i_flag)
+ {
+ for (k = 0; k < size; ++k)
+ crc += in_buff[k] & 0xff;
+ }
+ tape_buffered_write (in_buff, out_des, size);
+ num_bytes -= size;
+ input_size -= size;
+ in_buff += size;
+ }
+}
+/* Copy a file using the input and output buffers, which may start out
+ partly full. After the copy, the files are not closed nor the last
+ block flushed to output, and the input buffer may still be partly
+ full. If `crc_i_flag' is set, add each byte to `crc'.
+ IN_DES is the file descriptor for input;
+ OUT_DES is the file descriptor for output;
+ NUM_BYTES is the number of bytes to copy. */
+
+void
+copy_files_disk_to_disk (in_des, out_des, num_bytes, filename)
+ int in_des;
+ int out_des;
+ long num_bytes;
+ char *filename;
+{
+ long size;
+ long k;
+ long original_num_bytes;
+ int rc;
+
+ original_num_bytes = num_bytes;
+ while (num_bytes > 0)
+ {
+ if (input_size == 0)
+ if (rc = disk_fill_input_buffer (in_des, DISK_IO_BLOCK_SIZE))
+ {
+ if (rc > 0)
+ error (0, 0, _("File %s shrunk by %ld bytes, padding with zeros"),
+ filename, num_bytes);
+ else
+ error (0, 0, _("Read error at byte %ld in file %s, padding with zeros"),
+ original_num_bytes - num_bytes, filename);
+ write_nuls_to_file (num_bytes, out_des);
+ break;
+ }
+ size = (input_size < num_bytes) ? input_size : num_bytes;
+ if (crc_i_flag)
+ {
+ for (k = 0; k < size; ++k)
+ crc += in_buff[k] & 0xff;
+ }
+ disk_buffered_write (in_buff, out_des, size);
+ num_bytes -= size;
+ input_size -= size;
+ in_buff += size;
+ }
+}
+
+/* Warn if file changed while it was being copied. */
+
+void
+warn_if_file_changed(file_name, old_file_size, old_file_mtime)
+ char *file_name;
+ unsigned long old_file_size;
+ unsigned long old_file_mtime;
+{
+ struct stat new_file_stat;
+ if ((*xstat) (file_name, &new_file_stat) < 0)
+ {
+ error (0, errno, "%s", file_name);
+ return;
+ }
+
+ /* Only check growth, shrinkage detected in copy_files_disk_to_{disk,tape}()
+ */
+ if (new_file_stat.st_size > old_file_size)
+ error (0, 0, _("File %s grew, %ld new bytes not copied"),
+ file_name, (long)(new_file_stat.st_size - old_file_size));
+
+ else if (new_file_stat.st_mtime != old_file_mtime)
+ error (0, 0, _("File %s was modified while being copied"), file_name);
+}
+
+/* Create all directories up to but not including the last part of NAME.
+ Do not destroy any nondirectories while creating directories. */
+
+void
+create_all_directories (name)
+ char *name;
+{
+ char *dir;
+ int mode;
+#ifdef HPUX_CDF
+ int cdf;
+#endif
+
+ dir = dirname (name);
+ mode = 0700;
+#ifdef HPUX_CDF
+ cdf = islastparentcdf (name);
+ if (cdf)
+ {
+ dir [strlen (dir) - 1] = '\0'; /* remove final + */
+ mode = 04700;
+ }
+
+#endif
+
+ if (dir == NULL)
+ error (2, 0, _("virtual memory exhausted"));
+
+ if (dir[0] != '.' || dir[1] != '\0')
+ make_path (dir, mode, 0700, -1, -1, (char *) NULL);
+
+ free (dir);
+}
+
+/* Prepare to append to an archive. We have been in
+ process_copy_in, keeping track of the position where
+ the last header started in `last_header_start'. Now we
+ have the starting position of the last header (the TRAILER!!!
+ header, or blank record for tar archives) and we want to start
+ writing (appending) over the last header. The last header may
+ be in the middle of a block, so to keep the buffering in sync
+ we lseek back to the start of the block, read everything up
+ to but not including the last header, lseek back to the start
+ of the block, and then do a copy_buf_out of what we read.
+ Actually, we probably don't have to worry so much about keeping the
+ buffering perfect since you can only append to archives that
+ are disk files. */
+
+void
+prepare_append (out_file_des)
+ int out_file_des;
+{
+ int start_of_header;
+ int start_of_block;
+ int useful_bytes_in_block;
+ char *tmp_buf;
+
+ start_of_header = last_header_start;
+ /* Figure out how many bytes we will rewrite, and where they start. */
+ useful_bytes_in_block = start_of_header % io_block_size;
+ start_of_block = start_of_header - useful_bytes_in_block;
+
+ if (lseek (out_file_des, start_of_block, SEEK_SET) < 0)
+ error (1, errno, _("cannot seek on output"));
+ if (useful_bytes_in_block > 0)
+ {
+ tmp_buf = (char *) xmalloc (useful_bytes_in_block);
+ read (out_file_des, tmp_buf, useful_bytes_in_block);
+ if (lseek (out_file_des, start_of_block, SEEK_SET) < 0)
+ error (1, errno, _("cannot seek on output"));
+ /* fix juo -- is this copy_tape_buf_out? or copy_disk? */
+ tape_buffered_write (tmp_buf, out_file_des, useful_bytes_in_block);
+ free (tmp_buf);
+ }
+
+ /* We are done reading the archive, so clear these since they
+ will now be used for reading in files that we are appending
+ to the archive. */
+ input_size = 0;
+ input_bytes = 0;
+ in_buff = input_buffer;
+}
+
+/* Support for remembering inodes with multiple links. Used in the
+ "copy in" and "copy pass" modes for making links instead of copying
+ the file. */
+
+struct inode_val
+{
+ unsigned long inode;
+ unsigned long major_num;
+ unsigned long minor_num;
+ char *file_name;
+};
+
+/* Inode hash table. Allocated by first call to add_inode. */
+static struct inode_val **hash_table = NULL;
+
+/* Size of current hash table. Initial size is 47. (47 = 2*22 + 3) */
+static int hash_size = 22;
+
+/* Number of elements in current hash table. */
+static int hash_num;
+
+/* Find the file name associated with NODE_NUM. If there is no file
+ associated with NODE_NUM, return NULL. */
+
+char *
+find_inode_file (node_num, major_num, minor_num)
+ unsigned long node_num;
+ unsigned long major_num;
+ unsigned long minor_num;
+{
+#ifndef __MSDOS__
+ int start; /* Initial hash location. */
+ int temp; /* Rehash search variable. */
+
+ if (hash_table != NULL)
+ {
+ /* Hash function is node number modulo the table size. */
+ start = node_num % hash_size;
+
+ /* Initial look into the table. */
+ if (hash_table[start] == NULL)
+ return NULL;
+ if (hash_table[start]->inode == node_num
+ && hash_table[start]->major_num == major_num
+ && hash_table[start]->minor_num == minor_num)
+ return hash_table[start]->file_name;
+
+ /* The home position is full with a different inode record.
+ Do a linear search terminated by a NULL pointer. */
+ for (temp = (start + 1) % hash_size;
+ hash_table[temp] != NULL && temp != start;
+ temp = (temp + 1) % hash_size)
+ {
+ if (hash_table[temp]->inode == node_num
+ && hash_table[start]->major_num == major_num
+ && hash_table[start]->minor_num == minor_num)
+ return hash_table[temp]->file_name;
+ }
+ }
+#endif
+ return NULL;
+}
+
+/* Associate FILE_NAME with the inode NODE_NUM. (Insert into hash table.) */
+
+void
+add_inode (node_num, file_name, major_num, minor_num)
+ unsigned long node_num;
+ char *file_name;
+ unsigned long major_num;
+ unsigned long minor_num;
+{
+#ifndef __MSDOS__
+ struct inode_val *temp;
+
+ /* Create new inode record. */
+ temp = (struct inode_val *) xmalloc (sizeof (struct inode_val));
+ temp->inode = node_num;
+ temp->major_num = major_num;
+ temp->minor_num = minor_num;
+ temp->file_name = xstrdup (file_name);
+
+ /* Do we have to increase the size of (or initially allocate)
+ the hash table? */
+ if (hash_num == hash_size || hash_table == NULL)
+ {
+ struct inode_val **old_table; /* Pointer to old table. */
+ int i; /* Index for re-insert loop. */
+
+ /* Save old table. */
+ old_table = hash_table;
+ if (old_table == NULL)
+ hash_num = 0;
+
+ /* Calculate new size of table and allocate it.
+ Sequence of table sizes is 47, 97, 197, 397, 797, 1597, 3197, 6397 ...
+ where 3197 and most of the sizes after 6397 are not prime. The other
+ numbers listed are prime. */
+ hash_size = 2 * hash_size + 3;
+ hash_table = (struct inode_val **)
+ xmalloc (hash_size * sizeof (struct inode_val *));
+ bzero (hash_table, hash_size * sizeof (struct inode_val *));
+
+ /* Insert the values from the old table into the new table. */
+ for (i = 0; i < hash_num; i++)
+ hash_insert (old_table[i]);
+
+ if (old_table != NULL)
+ free (old_table);
+ }
+
+ /* Insert the new record and increment the count of elements in the
+ hash table. */
+ hash_insert (temp);
+ hash_num++;
+#endif /* __MSDOS__ */
+}
+
+/* Do the hash insert. Used in normal inserts and resizing the hash
+ table. It is guaranteed that there is room to insert the item.
+ NEW_VALUE is the pointer to the previously allocated inode, file
+ name association record. */
+
+static void
+hash_insert (new_value)
+ struct inode_val *new_value;
+{
+ int start; /* Home position for the value. */
+ int temp; /* Used for rehashing. */
+
+ /* Hash function is node number modulo the table size. */
+ start = new_value->inode % hash_size;
+
+ /* Do the initial look into the table. */
+ if (hash_table[start] == NULL)
+ {
+ hash_table[start] = new_value;
+ return;
+ }
+
+ /* If we get to here, the home position is full with a different inode
+ record. Do a linear search for the first NULL pointer and insert
+ the new item there. */
+ temp = (start + 1) % hash_size;
+ while (hash_table[temp] != NULL)
+ temp = (temp + 1) % hash_size;
+
+ /* Insert at the NULL. */
+ hash_table[temp] = new_value;
+}
+
+/* Open FILE in the mode specified by the command line options
+ and return an open file descriptor for it,
+ or -1 if it can't be opened. */
+
+int
+open_archive (file)
+ char *file;
+{
+ int fd;
+ void (*copy_in) (); /* Workaround for pcc bug. */
+
+ copy_in = process_copy_in;
+
+ if (copy_function == copy_in)
+ fd = rmtopen (file, O_RDONLY | O_BINARY, 0666, rsh_command_option);
+ else
+ {
+ if (!append_flag)
+ fd = rmtopen (file, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666,
+ rsh_command_option);
+ else
+ fd = rmtopen (file, O_RDWR | O_BINARY, 0666, rsh_command_option);
+ }
+
+ return fd;
+}
+
+/* Attempt to rewind the tape drive on file descriptor TAPE_DES
+ and take it offline. */
+
+void
+tape_offline (tape_des)
+ int tape_des;
+{
+#if defined(MTIOCTOP) && defined(MTOFFL)
+ struct mtop control;
+
+ control.mt_op = MTOFFL;
+ control.mt_count = 1;
+ rmtioctl (tape_des, MTIOCTOP, &control); /* Don't care if it fails. */
+#endif
+}
+
+/* The file on file descriptor TAPE_DES is assumed to be magnetic tape
+ (or floppy disk or other device) and the end of the medium
+ has been reached. Ask the user for to mount a new "tape" to continue
+ the processing. If the user specified the device name on the
+ command line (with the -I, -O, -F or --file options), then we can
+ automatically re-open the same device to use the next medium. If the
+ user did not specify the device name, then we have to ask them which
+ device to use. */
+
+void
+get_next_reel (tape_des)
+ int tape_des;
+{
+ static int reel_number = 1;
+ FILE *tty_in; /* File for interacting with user. */
+ FILE *tty_out; /* File for interacting with user. */
+ int old_tape_des;
+ char *next_archive_name;
+ dynamic_string new_name;
+ char *str_res;
+
+ ds_init (&new_name, 128);
+
+ /* Open files for interactive communication. */
+ tty_in = fopen (CONSOLE, "r");
+ if (tty_in == NULL)
+ error (2, errno, CONSOLE);
+ tty_out = fopen (CONSOLE, "w");
+ if (tty_out == NULL)
+ error (2, errno, CONSOLE);
+
+ old_tape_des = tape_des;
+ tape_offline (tape_des);
+ rmtclose (tape_des);
+
+ /* Give message and wait for carrage return. User should hit carrage return
+ only after loading the next tape. */
+ ++reel_number;
+ if (new_media_message)
+ fprintf (tty_out, "%s", new_media_message);
+ else if (new_media_message_with_number)
+ fprintf (tty_out, "%s%d%s", new_media_message_with_number, reel_number,
+ new_media_message_after_number);
+ else if (archive_name)
+ fprintf (tty_out, _("Found end of tape. Load next tape and press RETURN. "));
+ else
+ fprintf (tty_out, _("Found end of tape. To continue, type device/file name when ready.\n"));
+
+ fflush (tty_out);
+
+ if (archive_name)
+ {
+ int c;
+
+ do
+ c = getc (tty_in);
+ while (c != EOF && c != '\n');
+
+ tape_des = open_archive (archive_name);
+ if (tape_des == -1)
+ error (1, errno, "%s", archive_name);
+ }
+ else
+ {
+ do
+ {
+ if (tape_des < 0)
+ {
+ fprintf (tty_out,
+ _("To continue, type device/file name when ready.\n"));
+ fflush (tty_out);
+ }
+
+ str_res = ds_fgets (tty_in, &new_name);
+ if (str_res == NULL || str_res[0] == '\0')
+ exit (1);
+ next_archive_name = str_res;
+
+ tape_des = open_archive (next_archive_name);
+ if (tape_des == -1)
+ error (0, errno, "%s", next_archive_name);
+ }
+ while (tape_des < 0);
+ }
+
+ /* We have to make sure that `tape_des' has not changed its value even
+ though we closed it and reopened it, since there are local
+ copies of it in other routines. This works fine on Unix (even with
+ rmtread and rmtwrite) since open will always return the lowest
+ available file descriptor and we haven't closed any files (e.g.,
+ stdin, stdout or stderr) that were opened before we originally opened
+ the archive. */
+
+ if (tape_des != old_tape_des)
+ error (1, 0, _("internal error: tape descriptor changed from %d to %d"),
+ old_tape_des, tape_des);
+
+ free (new_name.ds_string);
+ fclose (tty_in);
+ fclose (tty_out);
+}
+
+/* If MESSAGE does not contain the string "%d", make `new_media_message'
+ a copy of MESSAGE. If MESSAGES does contain the string "%d", make
+ `new_media_message_with_number' a copy of MESSAGE up to, but
+ not including, the string "%d", and make `new_media_message_after_number'
+ a copy of MESSAGE after the string "%d". */
+
+void
+set_new_media_message (message)
+ char *message;
+{
+ char *p;
+ int prev_was_percent;
+
+ p = message;
+ prev_was_percent = 0;
+ while (*p != '\0')
+ {
+ if (*p == 'd' && prev_was_percent)
+ break;
+ prev_was_percent = (*p == '%');
+ ++p;
+ }
+ if (*p == '\0')
+ {
+ new_media_message = xstrdup (message);
+ }
+ else
+ {
+ int length = p - message - 1;
+
+ new_media_message_with_number = xmalloc (length + 1);
+ strncpy (new_media_message_with_number, message, length);
+ new_media_message_with_number[length] = '\0';
+ length = strlen (p + 1);
+ new_media_message_after_number = xmalloc (length + 1);
+ strcpy (new_media_message_after_number, p + 1);
+ }
+}
+
+#ifdef SYMLINK_USES_UMASK
+/* Most machines always create symlinks with rwxrwxrwx protection,
+ but some (HP/UX 8.07; maybe DEC's OSF on MIPS, too?) use the
+ umask when creating symlinks, so if your umask is 022 you end
+ up with rwxr-xr-x symlinks (although HP/UX seems to completely
+ ignore the protection). There doesn't seem to be any way to
+ manipulate the modes once the symlinks are created (e.g.
+ a hypothetical "lchmod"), so to create them with the right
+ modes we have to set the umask first. */
+
+int
+umasked_symlink (name1, name2, mode)
+ char *name1;
+ char *name2;
+ int mode;
+{
+ int old_umask;
+ int rc;
+ mode = ~(mode & 0777) & 0777;
+ old_umask = umask (mode);
+ rc = symlink (name1, name2);
+ umask (old_umask);
+ return rc;
+}
+#endif /* SYMLINK_USES_UMASK */
+
+#if defined(__MSDOS__) && !defined(__GNUC__)
+int
+chown (path, owner, group)
+ char *path;
+ int owner, group;
+{
+ return 0;
+}
+#endif
+
+#ifdef __TURBOC__
+#include <time.h>
+#include <fcntl.h>
+#include <io.h>
+
+int
+utime (char *filename, struct utimbuf *utb)
+{
+ struct tm *tm;
+ struct ftime filetime;
+ time_t when;
+ int fd;
+ int status;
+
+ if (utb == 0)
+ when = time (0);
+ else
+ when = utb->modtime;
+
+ fd = _open (filename, O_RDWR);
+ if (fd == -1)
+ return -1;
+
+ tm = localtime (&when);
+ if (tm->tm_year < 80)
+ filetime.ft_year = 0;
+ else
+ filetime.ft_year = tm->tm_year - 80;
+ filetime.ft_month = tm->tm_mon + 1;
+ filetime.ft_day = tm->tm_mday;
+ if (tm->tm_hour < 0)
+ filetime.ft_hour = 0;
+ else
+ filetime.ft_hour = tm->tm_hour;
+ filetime.ft_min = tm->tm_min;
+ filetime.ft_tsec = tm->tm_sec / 2;
+
+ status = setftime (fd, &filetime);
+ _close (fd);
+ return status;
+}
+#endif
+#ifdef HPUX_CDF
+/* When we create a cpio archive we mark CDF's by putting an extra `/'
+ after their component name so we can distinguish the CDF's when we
+ extract the archive (in case the "hidden" directory's files appear
+ in the archive before the directory itself). E.g., in the path
+ "a/b+/c", if b+ is a CDF, we will write this path as "a/b+//c" in
+ the archive so when we extract the archive we will know that b+
+ is actually a CDF, and not an ordinary directory whose name happens
+ to end in `+'. We also do the same thing internally in copypass.c. */
+
+
+/* Take an input pathname and check it for CDF's. Insert an extra
+ `/' in the pathname after each "hidden" directory. If we add
+ any `/'s, return a malloced string (which it will reuse for
+ later calls so our caller doesn't have to worry about freeing
+ the string) instead of the original input string. */
+
+char *
+add_cdf_double_slashes (input_name)
+ char *input_name;
+{
+ static char *ret_name = NULL; /* re-usuable return buffer (malloc'ed) */
+ static int ret_size = -1; /* size of return buffer. */
+ char *p;
+ char *q;
+ int n;
+ struct stat dir_stat;
+
+ /* Search for a `/' preceeded by a `+'. */
+
+ for (p = input_name; *p != '\0'; ++p)
+ {
+ if ( (*p == '+') && (*(p + 1) == '/') )
+ break;
+ }
+
+ /* If we didn't find a `/' preceeded by a `+' then there are
+ no CDF's in this pathname. Return the original pathname. */
+
+ if (*p == '\0')
+ return input_name;
+
+ /* There was a `/' preceeded by a `+' in the pathname. If it is a CDF
+ then we will need to copy the input pathname to our return
+ buffer so we can insert the extra `/'s. Since we can't tell
+ yet whether or not it is a CDF we will just always copy the
+ string to the return buffer. First we have to make sure the
+ buffer is large enough to hold the string and any number of
+ extra `/'s we might add. */
+
+ n = 2 * (strlen (input_name) + 1);
+ if (n >= ret_size)
+ {
+ if (ret_size < 0)
+ ret_name = (char *) malloc (n);
+ else
+ ret_name = (char *)realloc (ret_name, n);
+ ret_size = n;
+ }
+
+ /* Clear the `/' after this component, so we can stat the pathname
+ up to and including this component. */
+ ++p;
+ *p = '\0';
+ if ((*xstat) (input_name, &dir_stat) < 0)
+ {
+ error (0, errno, "%s", input_name);
+ return input_name;
+ }
+
+ /* Now put back the `/' after this component and copy the pathname up to
+ and including this component and its trailing `/' to the return
+ buffer. */
+ *p++ = '/';
+ strncpy (ret_name, input_name, p - input_name);
+ q = ret_name + (p - input_name);
+
+ /* If it was a CDF, add another `/'. */
+ if (S_ISDIR (dir_stat.st_mode) && (dir_stat.st_mode & 04000) )
+ *q++ = '/';
+
+ /* Go through the rest of the input pathname, copying it to the
+ return buffer, and adding an extra `/' after each CDF. */
+ while (*p != '\0')
+ {
+ if ( (*p == '+') && (*(p + 1) == '/') )
+ {
+ *q++ = *p++;
+
+ *p = '\0';
+ if ((*xstat) (input_name, &dir_stat) < 0)
+ {
+ error (0, errno, "%s", input_name);
+ return input_name;
+ }
+ *p = '/';
+
+ if (S_ISDIR (dir_stat.st_mode) && (dir_stat.st_mode & 04000) )
+ *q++ = '/';
+ }
+ *q++ = *p++;
+ }
+ *q = '\0';
+
+ return ret_name;
+}
+
+/* Is the last parent directory (e.g., c in a/b/c/d) a CDF? If the
+ directory name ends in `+' and is followed by 2 `/'s instead of 1
+ then it is. This is only the case for cpio archives, but we don't
+ have to worry about tar because tar always has the directory before
+ its files (or else we lose). */
+
+islastparentcdf(path)
+ char *path;
+{
+ char *newpath;
+ char *slash;
+ int slash_count;
+ int length; /* Length of result, not including NUL. */
+
+ slash = rindex (path, '/');
+ if (slash == 0)
+ return 0;
+ else
+ {
+ slash_count = 0;
+ while (slash > path && *slash == '/')
+ {
+ ++slash_count;
+ --slash;
+ }
+
+
+ if ( (*slash == '+') && (slash_count >= 2) )
+ return 1;
+ }
+ return 0;
+}
+#endif
+
+#define DISKBLOCKSIZE (512)
+
+enum sparse_write_states { begin, in_zeros, not_in_zeros };
+
+
+static int
+buf_all_zeros (buf, bufsize)
+ char *buf;
+ int bufsize;
+{
+ int i;
+ for (i = 0; i < bufsize; ++i)
+ {
+ if (*buf++ != '\0')
+ return 0;
+ }
+ return 1;
+}
+
+int delayed_seek_count = 0;
+
+/* Write NBYTE bytes from BUF to remote tape connection FILDES.
+ Return the number of bytes written on success, -1 on error. */
+
+int
+sparse_write (fildes, buf, nbyte)
+ int fildes;
+ char *buf;
+ unsigned int nbyte;
+{
+ int complete_block_count;
+ int leftover_bytes_count;
+ int seek_count;
+ int write_count;
+ char *cur_write_start;
+ int lseek_rc;
+ int write_rc;
+ int i;
+ enum sparse_write_states state;
+
+ complete_block_count = nbyte / DISKBLOCKSIZE;
+ leftover_bytes_count = nbyte % DISKBLOCKSIZE;
+
+ if (delayed_seek_count != 0)
+ state = in_zeros;
+ else
+ state = begin;
+
+ seek_count = delayed_seek_count;
+
+ for (i = 0; i < complete_block_count; ++i)
+ {
+ switch (state)
+ {
+ case begin :
+ if (buf_all_zeros (buf, DISKBLOCKSIZE))
+ {
+ seek_count = DISKBLOCKSIZE;
+ state = in_zeros;
+ }
+ else
+ {
+ cur_write_start = buf;
+ write_count = DISKBLOCKSIZE;
+ state = not_in_zeros;
+ }
+ buf += DISKBLOCKSIZE;
+ break;
+ case in_zeros :
+ if (buf_all_zeros (buf, DISKBLOCKSIZE))
+ {
+ seek_count += DISKBLOCKSIZE;
+ }
+ else
+ {
+ lseek (fildes, seek_count, SEEK_CUR);
+ cur_write_start = buf;
+ write_count = DISKBLOCKSIZE;
+ state = not_in_zeros;
+ }
+ buf += DISKBLOCKSIZE;
+ break;
+ case not_in_zeros :
+ if (buf_all_zeros (buf, DISKBLOCKSIZE))
+ {
+ write_rc = write (fildes, cur_write_start, write_count);
+ seek_count = DISKBLOCKSIZE;
+ state = in_zeros;
+ }
+ else
+ {
+ write_count += DISKBLOCKSIZE;
+ }
+ buf += DISKBLOCKSIZE;
+ break;
+ }
+ }
+
+ switch (state)
+ {
+ case begin :
+ case in_zeros :
+ delayed_seek_count = seek_count;
+ break;
+ case not_in_zeros :
+ write_rc = write (fildes, cur_write_start, write_count);
+ delayed_seek_count = 0;
+ break;
+ }
+
+ if (leftover_bytes_count != 0)
+ {
+ if (delayed_seek_count != 0)
+ {
+ lseek_rc = lseek (fildes, delayed_seek_count, SEEK_CUR);
+ delayed_seek_count = 0;
+ }
+ write_rc = write (fildes, buf, leftover_bytes_count);
+ }
+ return nbyte;
+}
+
+static void
+write_nuls_to_file (num_bytes, out_des)
+ long num_bytes;
+ int out_des;
+{
+ long blocks;
+ long extra_bytes;
+ long i;
+
+ blocks = num_bytes / 512;
+ extra_bytes = num_bytes % 512;
+ for (i = 0; i < extra_bytes; ++i)
+ {
+ if (write (out_des, zeros_512, 512) != 512)
+ error (1, errno, _("error writing NUL's"));
+ }
+ if (extra_bytes != 0)
+ {
+ if (write (out_des, zeros_512, extra_bytes) != extra_bytes)
+ error (1, errno, _("error writing NUL's"));
+ }
+}
diff --git a/src/xmalloc.c b/src/xmalloc.c
new file mode 100644
index 0000000..e2486d8
--- a/dev/null
+++ b/src/xmalloc.c
@@ -0,0 +1,103 @@
+/* xmalloc.c -- malloc with out of memory checking
+ Copyright (C) 1990, 91, 92, 93, 94 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if __STDC__
+#define VOID void
+#else
+#define VOID char
+#endif
+
+#include <sys/types.h>
+
+#if STDC_HEADERS
+#include <stdlib.h>
+#else
+VOID *malloc ();
+VOID *realloc ();
+void free ();
+#endif
+
+/* This is for other GNU distributions with internationalized messages.
+ The GNU C Library itself does not yet support such messages. */
+#if HAVE_LIBINTL_H
+# include <libintl.h>
+#else
+# define gettext(msgid) (msgid)
+#endif
+
+#ifndef EXIT_FAILURE
+#define EXIT_FAILURE 1
+#endif
+
+/* Exit value when the requested amount of memory is not available.
+ The caller may set it to some other value. */
+int xmalloc_exit_failure = EXIT_FAILURE;
+
+#if __STDC__ && (HAVE_VPRINTF || HAVE_DOPRNT)
+void error (int, int, const char *, ...);
+#else
+void error ();
+#endif
+
+static VOID *
+fixup_null_alloc (n)
+ size_t n;
+{
+ VOID *p;
+
+ p = 0;
+ if (n == 0)
+ p = malloc ((size_t) 1);
+ if (p == 0)
+ error (xmalloc_exit_failure, 0, gettext ("Memory exhausted"));
+ return p;
+}
+
+/* Allocate N bytes of memory dynamically, with error checking. */
+
+VOID *
+xmalloc (n)
+ size_t n;
+{
+ VOID *p;
+
+ p = malloc (n);
+ if (p == 0)
+ p = fixup_null_alloc (n);
+ return p;
+}
+
+/* Change the size of an allocated block of memory P to N bytes,
+ with error checking.
+ If P is NULL, run xmalloc. */
+
+VOID *
+xrealloc (p, n)
+ VOID *p;
+ size_t n;
+{
+ if (p == 0)
+ return xmalloc (n);
+ p = realloc (p, n);
+ if (p == 0)
+ p = fixup_null_alloc (n);
+ return p;
+}
diff --git a/src/xstrdup.c b/src/xstrdup.c
new file mode 100644
index 0000000..beb6f11
--- a/dev/null
+++ b/src/xstrdup.c
@@ -0,0 +1,36 @@
+/* xstrdup.c -- copy a string with out of memory checking
+ Copyright (C) 1990 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if defined(STDC_HEADERS) || defined(HAVE_STRING_H)
+#include <string.h>
+#else
+#include <strings.h>
+#endif
+char *xmalloc ();
+
+/* Return a newly allocated copy of STRING. */
+
+char *
+xstrdup (string)
+ char *string;
+{
+ return strcpy (xmalloc (strlen (string) + 1), string);
+}

Return to:

Send suggestions and report system problems to the System administrator.