author | Sergey Poznyakoff <gray@gnu.org.ua> | 2004-02-27 13:28:40 (GMT) |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2004-02-27 13:28:40 (GMT) |
commit | a783e2c18c6b566b2f4c2b06e6c388a9645c826c (patch) (side-by-side diff) | |
tree | ae12abc157dee99bd4c67bab25316be06d3519a2 | |
parent | cb721df9888a296af824a4acab4916694d6dc35d (diff) | |
download | cpio-a783e2c18c6b566b2f4c2b06e6c388a9645c826c.tar.gz cpio-a783e2c18c6b566b2f4c2b06e6c388a9645c826c.tar.bz2 |
Added to the repository
-rw-r--r-- | doc/.cvsignore | 2 | ||||
-rw-r--r-- | doc/Makefile.am | 20 | ||||
-rw-r--r-- | doc/cpio.1 | 340 | ||||
-rw-r--r-- | doc/cpio.info | 493 | ||||
-rw-r--r-- | doc/cpio.texi | 563 | ||||
-rw-r--r-- | doc/mt.1 | 126 | ||||
-rw-r--r-- | src/.cvsignore | 11 | ||||
-rw-r--r-- | src/Makefile.am | 81 | ||||
-rw-r--r-- | src/alloca.c | 495 | ||||
-rw-r--r-- | src/argmatch.c | 87 | ||||
-rw-r--r-- | src/bcopy.c | 19 | ||||
-rw-r--r-- | src/copyin.c | 1605 | ||||
-rw-r--r-- | src/copyout.c | 827 | ||||
-rw-r--r-- | src/copypass.c | 482 | ||||
-rw-r--r-- | src/cpio.h | 69 | ||||
-rw-r--r-- | src/cpiohdr.h | 90 | ||||
-rw-r--r-- | src/defer.c | 46 | ||||
-rw-r--r-- | src/defer.h | 25 | ||||
-rw-r--r-- | src/dirname.c | 70 | ||||
-rw-r--r-- | src/dstring.c | 118 | ||||
-rw-r--r-- | src/dstring.h | 49 | ||||
-rw-r--r-- | src/error.c | 109 | ||||
-rw-r--r-- | src/extern.h | 198 | ||||
-rw-r--r-- | src/filemode.c | 255 | ||||
-rw-r--r-- | src/filetypes.h | 84 | ||||
-rw-r--r-- | src/fnmatch.c | 200 | ||||
-rw-r--r-- | src/fnmatch.h | 67 | ||||
-rw-r--r-- | src/getopt.c | 765 | ||||
-rw-r--r-- | src/getopt.h | 129 | ||||
-rw-r--r-- | src/getopt1.c | 180 | ||||
-rw-r--r-- | src/gettext.h | 68 | ||||
-rw-r--r-- | src/global.c | 204 | ||||
-rw-r--r-- | src/idcache.c | 210 | ||||
-rw-r--r-- | src/main.c | 550 | ||||
-rw-r--r-- | src/makepath.c | 311 | ||||
-rw-r--r-- | src/mkdir.c | 100 | ||||
-rw-r--r-- | src/mt.c | 366 | ||||
-rw-r--r-- | src/rmt.c | 473 | ||||
-rw-r--r-- | src/rmt.h | 98 | ||||
-rw-r--r-- | src/rtapelib.c | 601 | ||||
-rw-r--r-- | src/safe-stat.h | 1 | ||||
-rw-r--r-- | src/strdup.c | 43 | ||||
-rw-r--r-- | src/strerror.c | 63 | ||||
-rw-r--r-- | src/stripslash.c | 43 | ||||
-rw-r--r-- | src/system.h | 146 | ||||
-rw-r--r-- | src/tar.c | 528 | ||||
-rw-r--r-- | src/tar.h | 112 | ||||
-rw-r--r-- | src/tarhdr.h | 62 | ||||
-rw-r--r-- | src/userspec.c | 265 | ||||
-rw-r--r-- | src/util.c | 1376 | ||||
-rw-r--r-- | src/xmalloc.c | 103 | ||||
-rw-r--r-- | src/xstrdup.c | 36 |
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 (¤t_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 (×, sizeof (struct utimbuf)); + + times.actime = times.modtime = file_hdr->c_mtime; + if (utime (file_hdr->c_name, ×) < 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 (×, sizeof (struct utimbuf)); + + times.actime = times.modtime = file_hdr->c_mtime; + if (utime (file_hdr->c_name, ×) < 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 (×, sizeof (struct utimbuf)); + + times.actime = times.modtime = file_hdr->c_mtime; + if (utime (file_hdr->c_name, ×) < 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 (×, 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, ×) < 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 (×, 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, ×) < 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 (×, 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, ×) < 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 (×, 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, ×) < 0 + && errno != EROFS) + error (0, errno, "%s", input_name.ds_string); + if (utime (output_name.ds_string, ×) < 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, ×) < 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, ×) < 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, ×) < 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, ©_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); +} |