home *** CD-ROM | disk | FTP | other *** search
Text File | 1988-09-11 | 84.3 KB | 3,307 lines |
- Subject: v07i088: Public-domain TAR program
- Newsgroups: mod.sources
- Approved: mirror!rs
-
- Submitted by: ihnp4!hoptoad!gnu (John Gilmore)
- Mod.sources: Volume 7, Issue 88
- Archive-name: pdtar
-
- ----------CUT HERE----------
- : To unbundle, sh this file
- echo README 1>&2
- cat >README <<'@@@ Fin de README'
- This is a public domain tar(1) replacement. It implements the 'c',
- 'x', and 't' commands of Unix tar, and many of the options. It creates
- P1003 "Unix Standard" [draft 6] tapes by default, and can read and
- write both old and new formats. It can decompress tar archives when
- reading them from disk files (using the 'z' option), but cannot do so
- when writing, or when reading from a tape drive. Its verbose output
- looks more like "ls -l" than the Unix tar, and even lines up the
- columns. It is a little better at reading damaged tapes than Unix tar.
-
- It is designed to be a lot more efficient than the standard Unix tar;
- it does as little bcopy-ing as possible, and does file I/O in large
- blocks. On the other hand, it has not been timed or performance-tuned;
- it's just *designed* to be faster.
-
- On the Sun, the tar archives it creates under the 'old' option are
- byte-for-byte the same as those created by /bin/tar, except the trash
- at the end of each file and at the end of the archive.
-
- It was written and initially debugged on a Sun Workstation running
- 4.2BSD. It has been run on Xenix, Unisoft, Vax 4.2BSD, V7, and USG
- systems. I'm interested in finding people who will port it to other
- types of (Unix and non-Unix) systems, use it, and send back the
- changes; and people who will add the obscure tar options that they
- happen to use and I don't. In particular, VMS, MSDOS, Mac, Atari and
- Amiga versions would be handy.
-
- It still has a number of loose ends, marked by "FIXME" comments in the
- source. For example, it does not chown() extracted files. Fixes to
- these things are also welcome.
-
- I am the author of all the code in this program. I hereby place it in
- the public domain. If you modify it, or port it to another system,
- please send me back a copy, so I can keep a master source.
-
- John Gilmore
- Nebula Consultants
- 1504 Golden Gate Ave.
- San Francisco, California, USA 94115
- +1 415 931 4667 voice
- hoptoad!gnu data
- jgilmore@lll-crg.arpa data
- Hoptoad talks to sun, ptsfa, well, lll-crg, ihnp4, cbosgd, ucsfcgl, pyramid.
-
- @(#)README 1.5 86/10/29
- @@@ Fin de README
- echo PORTING 1>&2
- cat >PORTING <<'@@@ Fin de PORTING'
- Porting hints for public domain tar
- John Gilmore, ihnp4!hoptoad!gnu
- @(#)PORTING 1.5 86/10/29
-
- The Makefile should be edited to comment out all the undesired
- versions, and create the following configuration lines for the system
- you are compiling it on:
-
- DEFS = the proper #define's to conditionally compile for
- your system.
- LIBS = the system libraries and/or object modules to link
- with the program.
- LINTFLAGS = a good strong way to invoke 'lint' on your system.
- DEF_AR_FILE = the name of the default archive file on your system.
- It should be enclosed in quoted quotes, e.g. \"/dev/foo\" .
- DEFBLOCKING = the default blocking factor on your system.
-
- A copy of "getopt", the standard argument parser, is required. It's in
- libc on Missed'em V systems and 4.3BSD; on most other systems, you'll
- need a copy of a public domain getopt, available through mod.sources
- or the AT&T Toolchest if you can't find it elsewhere.
-
- A copy of the Berkeley directory access routines is also required.
- These are in libc and <sys/dir.h> on Berkeley systems. A public domain
- version is available through mod.sources. There is an #include you
- have to change in create.c for this, to set the name of the include
- file you have. Some systems have the include file in <sys/ndir.h>.
- You'll have to find it on your system, or get the public domain one
- and place it somewhere.
-
- Grep for FIXME to find places that aren't finished or which have
- portability problems. Also see the file TODO.
- @@@ Fin de PORTING
- echo Makefile 1>&2
- cat >Makefile <<'@@@ Fin de Makefile'
- # Makefile for public domain tar program.
- # @(#)Makefile 1.13 86/10/29
-
- # Berserkeley version
- DEFS = -DBSD42
- LIBS =
- LINTFLAGS = -abchx
- DEF_AR_FILE = \"/dev/rmt8\"
- DEFBLOCKING = 20
-
- # USG version
- #DEFS = -DUSG
- #LIBS = -lndir
- #LINTFLAGS = -bx
- #DEF_AR_FILE = \"/dev/rmt8\"
- #DEFBLOCKING = 20
-
- # UniSoft's Uniplus SVR2 with NFS
- #DEFS = -DUSG -DUNIPLUS -DNFS -DSVR2
- #LIBS = -lndir
- #LINTFLAGS = -bx
- #DEF_AR_FILE = \"/dev/rmt8\"
- #DEFBLOCKING = 20
-
- # V7 version
- #DEFS = -DV7 -Dvoid=int
- #LIBS = -lndir
- #LINTFLAGS = -abchx
- #DEF_AR_FILE = \"/dev/rmt8\"
- #DEFBLOCKING = 20
-
- CFLAGS = $(COPTS) $(DEFS) \
- -DDEF_AR_FILE=$(DEF_AR_FILE) \
- -DDEFBLOCKING=$(DEFBLOCKING)
- # next line for Debugging
- COPTS = -g
- # next line for Production
- #COPTS = -O
-
- # Add things here like getopt, readdir, etc that aren't in your
- # standard libraries.
- SUBSRC=
- SUBOBJ=
-
- SRCS = tar.c create.c extract.c buffer.c getoldopt.c list.c names.c \
- port.c $(SUBSRC)
- OBJS = tar.o create.o extract.o buffer.o getoldopt.o list.o names.o \
- port.o $(SUBOBJ)
- AUX = README PORTING Makefile TODO tar.1 tar.5 tar.h port.h
-
- tar: $(OBJS)
- cc -o tar $(COPTS) $(OBJS) $(LIBS)
-
- lint: $(SRCS)
- lint $(LINTFLAGS) $(CFLAGS) $(SRCS)
-
- clean:
- rm -f errs *.o tar
-
- tar.shar: $(SRCS) $(AUX)
- shar >tar.shar $(AUX) $(SRCS)
-
- tar.tar.Z: $(SRCS) $(AUX)
- /bin/tar cf - $(AUX) $(SRCS) | compress -v >tar.tar.Z
-
- $(OBJS): tar.h port.h
- @@@ Fin de Makefile
- echo TODO 1>&2
- cat >TODO <<'@@@ Fin de TODO'
- @(#) TODO 1.6 86/10/29
-
- Install new mkdir from the net for non-Berkeley systems.
-
- Look at SUID, SGID; look at -p and -m options. (test them).
-
- Handle owner/group on extraction.
-
- creation of links and symlinks doesn't follow the -k (f_keep) guidelines;
- if the file already exists, it is not replaced, even though no -k.
-
- Check stderr and stdout for errors after writing, and quit if so.
-
- Compression option to automatically pipe thru compress (both input&output).
- (Need a 3rd process to reblock compress's output for output case, and when
- reading from tape drives.)
-
- Preliminary design of Multifile option to handle EOFs on input and output.
- Multifile can just write EOF when it hits end of archive, and ask for
- archive to be changed. Start off 2nd archive medium with odd header
- block, duplicating original, but with offset to start of data spec'd.
- Reading such a header causes tar non-'M' to complain while extracting
- (but to seek there and do it anyway!) Big win -- this works on
- cartridge tapes, should work on floppies, might work on magtape.
- It would encourage the *&%#$ systems programmers to fix their drivers, too!
-
- Profile it and see where the time, call counts, etc are going.
-
- Test reading compressed tapes with odd blocksizes.
- (real tape drives, that is...)
- (may need buffer proc no matter what.)
-
- Fix directory timestamps after inserting files into them. Wait til next
- file that's not in the directory. Need a stack of them.
-
- Add option to delete N matching(?) chars from the front of a file to
- be extracted/listed. Great for reading tapes written with names starting
- from "/"...
-
- Option to seek the input file (in skip_file) rather than reading
- and tossing it? (Could just jump in buffer if stuff is in core.)
- Could misalign archive reads versus filesys and slow it down, who knows?
-
- Add -C option for creating from odd directories a la 4.2BSD?
-
- Break out odd bits of code into separate support modules.
-
- Add the r, u, X, l, F, C, and digit options of Unix tar.
- @@@ Fin de TODO
- echo tar.1 1>&2
- cat >tar.1 <<'@@@ Fin de tar.1'
- .TH TAR 1 "31 October 1986"
- .SH NAME
- tar \- tape (or other media) file archiver
- .SH SYNOPSIS
- \fBtar\fP \-[\fBBcDhikmopstvxzZ\fP]
- [\fB\-b\fP \fIN\fP]
- [\fB\-f\fP \fIF\fP]
- [\fB\-T\fP \fIF\fP]
- [ \fIfilename\fP\| .\|.\|. ]
- .SH DESCRIPTION
- \fItar\fP provides a way to store many files into a single archive,
- which can be kept in another Unix file, stored on an I/O device
- such as tape, floppy, cartridge, or disk, or piped to another program.
- It is useful for making backup copies, or for packaging up a set of
- files to move them to another system.
- .LP
- \fItar\fP has existed since Version 7 Unix with very little change.
- It has been proposed as the standard format for interchange of files
- among systems that conform to the P1003 ``Portable Operating System''
- standard.
- .LP
- This version of \fItar\fP supports the extensions which
- were proposed in the P1003 draft standards, including owner and group
- names, and support for named pipes, fifos, and block and character devices.
- .LP
- When reading an archive, this version of \fItar\fP continues after
- finding an error. Previous versions required the `i' option to ignore
- checksum errors.
- .SH OPTIONS
- \fItar\fP options can be specified in either of two ways. The usual
- Unix conventions can be used: each option is preceded by `\-'; arguments
- directly follow each option; multiple options can be combined behind one `\-'
- as long as they take no arguments. For compatability with the Unix
- \fItar\fP program, the options may also be specified as ``keyletters,''
- wherein all the option letters occur in the first argument to \fItar\fP,
- with no `\-', and their arguments, if any, occur in the second, third, ...
- arguments. Examples:
- .LP
- Normal: tar -f arcname -cv file1 file2
- .LP
- Old: tar fcv arcname file1 file2
- .LP
- At least one of the \fB\-c\fP, \fB\-t\fP, or \fB\-x\fP options
- must be included. The rest are optional.
- .LP
- Files to be operated upon are specified by a list of file names, which
- follows the option specifications (or can be read from a file by the
- \fB\-T\fP option). Specifying a directory name causes that directory
- and all the files it contains to be (recursively) processed. In general,
- specifying full path names when creating an archive is a bad idea,
- since when the files are
- extracted, they will have to be extracted into exactly where they were
- dumped from. Instead, \fIcd\fP to the
- root directory and use relative file names.
- .IP "\fB\-b\fP \fIN\fP"
- Specify a blocking factor for the archive. The block size will be
- \fIN\fP x 512 bytes. Larger blocks typically run faster and let you
- fit more data on a tape. The default blocking factor is set when
- \fItar\fP is compiled, and is typically 20. There is no limit to the
- maximum block size, as long as enough memory can be allocated for it,
- and as long as the device containing the archive can read or write
- that block size.
- .IP \fB\-B\fP
- When reading an archive, reblock it as we read it.
- Normally, \fItar\fP reads each
- block with a single \fIread(2)\fP system call. This does not work
- when reading from a pipe or network socket under Berkeley Unix.
- With this option, it
- will do multiple \fIread(2)\fPs until it gets enough data to fill
- the specified block size. \fB\-B\fP can also be used to speed up
- the reading of tapes that were written with small blocking factors,
- by specifying a large blocking factor with \fB\-b\fP and having \fItar\fP
- read many small blocks into memory before it tries to process them.
- .IP \fB\-c\fP
- Create an archive from a list of files.
- .IP \fB\-D\fP
- With each message that \fItar\fP produces, print the record number
- within the archive where the message occurred. This option is especially
- useful when reading damaged archives, since it helps to pinpoint the damaged
- section.
- .IP "\fB\-f\fP \fIF\fP"
- Specify the filename of the archive. If the specified filename is ``\-'',
- the archive is read from the standard input or written to the standard output.
- If this option is not used,
- a default archive name (which was picked when tar was compiled) is used.
- The default is normally set to the ``first'' tape drive or other transportable
- I/O medium on the system.
- .IP \fB\-h\fP
- When creating an archive, if a symbolic link is encountered, dump
- the file or directory to which it points, rather than
- dumping it as a symbolic link.
- .IP \fB\-i\fP
- When reading an archive, ignore blocks of zeros in the archive. Normally
- a block of zeros indicates the end of the archive,
- but in a damaged archive, or one which was
- created by appending several archives, this option allows \fItar\fP to
- continue. It is not on by default because there is garbage written after the
- zeroed blocks by the Unix \fItar\fP program.
- .IP \fB\-k\fP
- When extracting files from an archive, keep existing files, rather than
- overwriting them with the version from the archive.
- .IP \fB\-m\fP
- When extracting files from an archive, set each file's modified timestamp
- to the current time, rather than extracting each file's modified
- timestamp from the archive.
- .IP \fB\-o\fP
- When creating an archive, write an old format archive, which does not
- include information about directories, pipes, or device files, and
- specifies file ownership by uid's and gid's rather than by
- user names and group names. In most cases, a ``new'' format archive
- can be read by an ``old'' tar program without serious trouble, so this
- option should seldom be needed.
- .IP \fB\-p\fP
- When extracting files from an archive, restore them to the same permissions
- that they had in the archive. If \fB\-p\fP is not specified, the current
- umask limits the permissions of the extracted files. See \fIumask(2)\fP.
- .IP \fB\-t\fP
- List a table of contents of an existing archive. If file names are
- specified, just list files matching the specified names.
- .IP \fB\-s\fP
- When specifying a list of filenames to be listed
- or extracted from an archive,
- the \fB\-s\fP flag specifies that the list
- is sorted into the same order as the tape. This allows a large list
- to be used, even on small machines, because
- the entire list need not be read into memory at once. Such a sorted
- list can easily be created by running ``tar \-t'' on the archive and
- editing its output.
- .IP "\fB\-T\fP \fIF\fP"
- Rather than specifying the file names to operate on as arguments to
- the \fItar\fP command, this option specifies that the file names should
- be read from the file \fIF\fP, one per line.
- If the file name specified is ``\-'',
- the list is read from the standard input.
- This option, in conjunction with the \fB\-s\fP option,
- allows an arbitrarily large list of files to be processed,
- and allows the list to be piped to \fItar\fP.
- .IP \fB\-v\fP
- Be verbose about the files that are being processed or listed. Normally,
- archive creation or file extraction are silent, and archive listing just
- gives file names. The \fB\-v\fP option causes an ``ls \-l''\-like listing
- to be produced.
- .IP \fB\-x\fP
- Extract files from an existing archive. If file names are
- specified, just extract files matching the specified names, otherwise extract
- all the files in the archive.
- .IP "\fB\-z\fP or \fB\-Z\fP"
- When extracting or listing an archive,
- these options specify
- that the archive should be decompressed while it is read, using the \-d
- option of the \fIcompress(1)\fP program. The archive itself is not
- modified.
- .SH "SEE ALSO"
- shar(1), tar(5), compress(1), ar(1), arc(1), cpio(1), dump(8), restore(8),
- restor(8)
- .SH BUGS
- The \fBr, u, w, X, l, F, C\fP, and \fIdigit\fP options of Unix \fItar\fP
- are not supported.
- .LP
- It should be possible to create a compressed archive with the \fB\-z\fP option.
- .LP
- @@@ Fin de tar.1
- echo tar.5 1>&2
- cat >tar.5 <<'@@@ Fin de tar.5'
- .TH TAR 5 "31 October 1986"
- .SH NAME
- tar \- tape (or other media) archive file format
- .SH DESCRIPTION
- A ``tar tape'' or file contains a series of records. Each record contains
- TRECORDSIZE bytes (see below). Although this format may be thought of as
- being on magnetic tape, other media are often used.
- Each file archived is represented by a header record
- which describes the file, followed by zero or more records which give the
- contents of the file. At the end of the archive file there may be a record
- filled with binary zeros as an end-of-file indicator. A conforming
- system must write a record of zeros at the end, but must not assume that
- an end-of-file record exists when reading an archive.
-
- The records may be blocked for physical I/O operations. Each block of
- \fIN\fP records (where \fIN\fP is set by the \fB\-b\fP option to \fItar\fP)
- is written with a single write() operation. On
- magnetic tapes, the result of such a write is a single tape record.
- When writing an archive, the last block of records shall be written
- at the full size, with records after the zero record containing
- undefined data. When reading an archive, a confirming system shall
- properly handle an archive whose last block is shorter than the rest.
-
- The header record is defined in the header file <tar.h> as follows:
- .nf
- .sp .5v
- .DT
- /*
- * Standard Archive Format - Standard TAR - USTAR
- */
- #define RECORDSIZE 512
- #define NAMSIZ 100
- #define TUNMLEN 32
- #define TGNMLEN 32
-
- union record {
- char charptr[RECORDSIZE];
- struct header {
- char name[NAMSIZ];
- char mode[8];
- char uid[8];
- char gid[8];
- char size[12];
- char mtime[12];
- char chksum[8];
- char linkflag;
- char linkname[NAMSIZ];
- char magic[8];
- char uname[TUNMLEN];
- char gname[TGNMLEN];
- char devmajor[8];
- char devminor[8];
- } header;
- };
-
- /* The checksum field is filled with this while the checksum is computed. */
- #define CHKBLANKS " " /* 8 blanks, no null */
-
- /* The magic field is filled with this if uname and gname are valid. */
- #define TMAGIC "ustar " /* 7 chars and a null */
-
- /* The linkflag defines the type of file */
- #define LF_OLDNORMAL '\\0' /* Normal disk file, Unix compatible */
- #define LF_NORMAL '0' /* Normal disk file */
- #define LF_LINK '1' /* Link to previously dumped file */
- #define LF_SYMLINK '2' /* Symbolic link */
- #define LF_CHR '3' /* Character special file */
- #define LF_BLK '4' /* Block special file */
- #define LF_DIR '5' /* Directory */
- #define LF_FIFO '6' /* FIFO special file */
- #define LF_CONTIG '7' /* Contiguous file */
- /* Further link types may be defined later. */
-
- /* Bits used in the mode field - values in octal */
- #define TSUID 04000 /* Set UID on execution */
- #define TSGID 02000 /* Set GID on execution */
- #define TSVTX 01000 /* Save text (sticky bit) */
-
- /* File permissions */
- #define TUREAD 00400 /* read by owner */
- #define TUWRITE 00200 /* write by owner */
- #define TUEXEC 00100 /* execute/search by owner */
- #define TGREAD 00040 /* read by group */
- #define TGWRITE 00020 /* write by group */
- #define TGEXEC 00010 /* execute/search by group */
- #define TOREAD 00004 /* read by other */
- #define TOWRITE 00002 /* write by other */
- #define TOEXEC 00001 /* execute/search by other */
- .fi
- .LP
- All characters in header records
- are represented using 8-bit characters in the local
- variant of ASCII.
- Each field within the structure is contiguous; that is, there is
- no padding used within the structure. Each character on the archive medium
- is stored contiguously.
-
- Bytes representing the contents of files (after the header record
- of each file) are not translated in any way and
- are not constrained to represent characters or to be in any character set.
- The \fItar\fP(5) format does not distinguish text files from binary
- files, and no translation of file contents should be performed.
-
- The fields \fIname, linkname, magic, uname\fP, and \fIgname\fP are
- null-terminated
- character strings. All other fields are zero-filled octal numbers in
- ASCII. Each numeric field (of width \fIw\fP) contains \fIw\fP-2 digits, a space, and
- a null, except \fIsize\fP and \fImtime\fP,
- which do not contain the trailing null.
-
- The \fIname\fP field is the pathname of the file, with directory names
- (if any) preceding the file name, separated by slashes.
-
- The \fImode\fP field provides nine bits specifying file permissions and three
- bits to specify the Set UID, Set GID and Save Text (TSVTX) modes. Values
- for these bits are defined above. When special permissions are required
- to create a file with a given mode, and the user restoring files from the
- archive does not hold such permissions, the mode bit(s) specifying those
- special permissions are ignored. Modes which are not supported by the
- operating system restoring files from the archive will be ignored.
- Unsupported modes should be faked up when creating an archive; e.g.
- the group permission could be copied from the `other' permission.
-
- The \fIuid\fP and \fIgid\fP fields are the user and group ID of the file owners,
- respectively.
-
- The \fIsize\fP field is the size of the file in bytes; linked files are archived
- with this field specified as zero.
-
- The \fImtime\fP field is the modification time of the file at the time it was
- archived. It is the ASCII representation of the octal value of the
- last time the file was modified, represented as in integer number of
- seconds since January 1, 1970, 00:00 Coordinated Universal Time.
-
- The \fIchksum\fP field is the ASCII representaion of the octal value of the
- simple sum of all bytes in the header record. Each 8-bit byte in the
- header is treated as an unsigned value. These values are added to an
- unsigned integer, initialized to zero, the precision of which shall be no
- less than seventeen bits. When calculating the checksum, the \fIchksum\fP
- field is treated as if it were all blanks.
-
- The \fItypeflag\fP field specifies the type of file archived. If a particular
- implementation does not recognize or permit the specified type, the file
- will be extracted as if it were a regular file. As this action occurs,
- \fItar\fP issues a warning to the standard error.
- .IP "LF_NORMAL or LF_OLDNORMAL"
- represents a regular file.
- For backward compatibility, a \fItypeflag\fP value of LF_OLDNORMAL
- should be silently recognized as a regular file. New archives should
- be created using LF_NORMAL.
- Also, for backward
- compatability, \fItar\fP treats a regular file whose name ends
- with a slash as a directory.
- .IP LF_LINK
- represents a file linked to another file, of any type,
- previously archived. Such files are identified (in Unix) by each file
- having the same device and inode number. The linked-to
- name is specified in the \fIlinkname\fP field with a trailing null.
- .IP LF_SYMLINK
- represents a symbolic link to another file. The linked-to
- name is specified in the \fIlinkname\fP field with a trailing null.
- .IP "LF_CHR or LF_BLK"
- represent character special files and block
- special files respectively.
- In this case the \fIdevmajor\fP and \fIdevminor\fP
- fields will contain the
- major and minor device numbers respectively. Operating
- systems may map the device specifications to their own local
- specification, or may ignore the entry.
- .IP LF_DIR
- specifies a directory or sub-directory. The directory name
- in the \fIname\fP field should end with a slash.
- On systems where
- disk allocation is performed on a directory basis the \fIsize\fP
- field will contain the maximum number of bytes (which may be
- rounded to the nearest disk block allocation unit) which the
- directory may hold. A \fIsize\fP field of zero indicates no such
- limiting. Systems which do not support limiting in this
- manner should ignore the \fIsize\fP field.
- .IP LF_FIFO
- specifies a FIFO special file. Note that the archiving of
- a FIFO file archives the existence of this file and not its
- contents.
- .IP LF_CONTIG
- specifies a contiguous file, which is the same as a normal
- file except that, in operating systems which support it,
- all its space is allocated contiguously on the disk. Operating
- systems which do not allow contiguous allocation should silently treat
- this type as a normal file.
- .IP "`A' \- `Z'"
- are reserved for custom implementations. None are used by this
- version of the \fItar\fP program.
- .IP \fIother\fP
- values are reserved for specification in future revisions of the
- P1003 standard, and should not be used by any \fItar\fP program.
- .LP
- The \fImagic\fP field indicates that this archive was output in the P1003
- archive format. If this field contains TMAGIC, then the
- \fIuname\fP and \fIgname\fP
- fields will contain the ASCII representation of the owner and group of the
- file respectively. If found, the user and group ID represented by these
- names
- will be used rather than the values contained
- within the \fIuid\fP and \fIgid\fP fields.
- User names longer than TUNMLEN-1 or group
- names longer than TGNMLEN-1 characters will be truncated.
- .SH "SEE ALSO"
- tar(1), ar(5), cpio(5)
- .SH BUGS
- Names or link names longer than NAMSIZ-1 characters cannot be archived.
-
- This format does not address multi-volume archives.
- .SH NOTES
- This manual page was adapted by John Gilmore
- from Draft 6 of the P1003 specification
- @@@ Fin de tar.5
- echo tar.h 1>&2
- cat >tar.h <<'@@@ Fin de tar.h'
- /*
- * Header file for public domain tar (tape archive) program.
- *
- * @(#)tar.h 1.20 86/10/29 Public Domain.
- *
- * Created 25 August 1985 by John Gilmore, ihnp4!hoptoad!gnu.
- */
-
- /*
- * Kludge for handling systems that can't cope with multiple
- * external definitions of a variable. In ONE routine (tar.c),
- * we #define TAR_EXTERN to null; here, we set it to "extern" if
- * it is not already set.
- */
- #ifndef TAR_EXTERN
- #define TAR_EXTERN extern
- #endif
-
- /*
- * Header block on tape.
- *
- * I'm going to use traditional DP naming conventions here.
- * A "block" is a big chunk of stuff that we do I/O on.
- * A "record" is a piece of info that we care about.
- * Typically many "record"s fit into a "block".
- */
- #define RECORDSIZE 512
- #define NAMSIZ 100
- #define TUNMLEN 32
- #define TGNMLEN 32
-
- union record {
- char charptr[RECORDSIZE];
- struct header {
- char name[NAMSIZ];
- char mode[8];
- char uid[8];
- char gid[8];
- char size[12];
- char mtime[12];
- char chksum[8];
- char linkflag;
- char linkname[NAMSIZ];
- char magic[8];
- char uname[TUNMLEN];
- char gname[TGNMLEN];
- char devmajor[8];
- char devminor[8];
- } header;
- };
-
- /* The checksum field is filled with this while the checksum is computed. */
- #define CHKBLANKS " " /* 8 blanks, no null */
-
- /* The magic field is filled with this if uname and gname are valid. */
- #define TMAGIC "ustar " /* 7 chars and a null */
-
- /* The linkflag defines the type of file */
- #define LF_OLDNORMAL '\0' /* Normal disk file, Unix compat */
- #define LF_NORMAL '0' /* Normal disk file */
- #define LF_LINK '1' /* Link to previously dumped file */
- #define LF_SYMLINK '2' /* Symbolic link */
- #define LF_CHR '3' /* Character special file */
- #define LF_BLK '4' /* Block special file */
- #define LF_DIR '5' /* Directory */
- #define LF_FIFO '6' /* FIFO special file */
- #define LF_CONTIG '7' /* Contiguous file */
- /* Further link types may be defined later. */
-
- /*
- * Exit codes from the "tar" program
- */
- #define EX_SUCCESS 0 /* success! */
- #define EX_ARGSBAD 1 /* invalid args */
- #define EX_BADFILE 2 /* invalid filename */
- #define EX_BADARCH 3 /* bad archive */
- #define EX_SYSTEM 4 /* system gave unexpected error */
-
-
- /*
- * Global variables
- */
- TAR_EXTERN union record *ar_block; /* Start of block of archive */
- TAR_EXTERN union record *ar_record; /* Current record of archive */
- TAR_EXTERN union record *ar_last; /* Last+1 record of archive block */
- TAR_EXTERN char ar_reading; /* 0 writing, !0 reading archive */
- TAR_EXTERN int blocking; /* Size of each block, in records */
- TAR_EXTERN int blocksize; /* Size of each block, in bytes */
- TAR_EXTERN char *ar_file; /* File containing archive */
- TAR_EXTERN char *name_file; /* File containing names to work on */
- TAR_EXTERN char *tar; /* Name of this program */
-
- /*
- * Flags from the command line
- */
- TAR_EXTERN char f_reblock; /* -B */
- TAR_EXTERN char f_create; /* -c */
- TAR_EXTERN char f_debug; /* -d */
- TAR_EXTERN char f_sayblock; /* -D */
- TAR_EXTERN char f_follow_links; /* -h */
- TAR_EXTERN char f_ignorez; /* -i */
- TAR_EXTERN char f_keep; /* -k */
- TAR_EXTERN char f_modified; /* -m */
- TAR_EXTERN char f_oldarch; /* -o */
- TAR_EXTERN char f_use_protection; /* -p */
- TAR_EXTERN char f_sorted_names; /* -s */
- TAR_EXTERN char f_list; /* -t */
- TAR_EXTERN char f_namefile; /* -T */
- TAR_EXTERN char f_verbose; /* -v */
- TAR_EXTERN char f_extract; /* -x */
- TAR_EXTERN char f_compress; /* -z */
-
- /*
- * We now default to Unix Standard format rather than 4.2BSD tar format.
- * The code can actually produce all three:
- * f_standard ANSI standard
- * f_oldarch V7
- * neither 4.2BSD
- * but we don't bother, since 4.2BSD can read ANSI standard format anyway.
- * The only advantage to the "neither" option is that we can cmp(1) our
- * output to the output of 4.2BSD tar, for debugging.
- */
- #define f_standard (!f_oldarch)
-
- /*
- * Structure for keeping track of filenames and lists thereof.
- */
- struct name {
- struct name *next;
- short length;
- char found;
- char name[NAMSIZ+1];
- };
-
- TAR_EXTERN struct name *namelist; /* Points to first name in list */
- TAR_EXTERN struct name *namelast; /* Points to last name in list */
-
- TAR_EXTERN int archive; /* File descriptor for archive file */
- TAR_EXTERN int errors; /* # of files in error */
-
- /*
- *
- * Due to the next struct declaration, each routine that includes
- * "tar.h" must also include <sys/types.h>. I tried to make it automatic,
- * but System V has no defines in <sys/types.h>, so there is no way of
- * knowing when it has been included. In addition, it cannot be included
- * twice, but must be included exactly once. Argghh!
- *
- * Thanks, typedef. Thanks, USG.
- */
- struct link {
- struct link *next;
- dev_t dev;
- ino_t ino;
- short linkcount;
- char name[NAMSIZ+1];
- };
-
- TAR_EXTERN struct link *linklist; /* Points to first link in list */
-
-
- /*
- * Error recovery stuff
- */
- TAR_EXTERN char read_error_flag;
-
-
- /*
- * Declarations of functions available to the world.
- */
- union record *findrec();
- void userec();
- union record *endofrecs();
- void anno();
- #define annorec(stream, msg) anno(stream, msg, 0) /* Cur rec */
- #define annofile(stream, msg) anno(stream, msg, 1) /* Saved rec */
- @@@ Fin de tar.h
- echo port.h 1>&2
- cat >port.h <<'@@@ Fin de port.h'
- /*
- * Portability declarations for public domain tar.
- *
- * @(#)port.h 1.1 86/03/11 Public Domain by John Gilmore, 1986
- */
-
- /*
- * Everybody does wait() differently. There seem to be no definitions
- * for this in V7 (e.g. you are supposed to shift and mask things out
- * using constant shifts and masks.) So fuck 'em all -- my own non
- * standard but portable macros. Don't change to a "union wait"
- * based approach -- the ordering of the elements of the struct
- * depends on the byte-sex of the machine. Foo!
- */
- #define TERM_SIGNAL(status) ((status) & 0x7F)
- #define TERM_COREDUMP(status) (((status) & 0x80) != 0)
- #define TERM_VALUE(status) ((status) >> 8)
-
-
- @@@ Fin de port.h
- echo tar.c 1>&2
- cat >tar.c <<'@@@ Fin de tar.c'
- /*
- * A public domain tar(1) program.
- *
- * Written by John Gilmore, ihnp4!hoptoad!gnu, starting 25 Aug 85.
- *
- * @(#)tar.c 1.21 10/29/86 Public Domain - gnu
- */
-
- #include <stdio.h>
- #include <sys/types.h> /* Needed for typedefs in tar.h */
-
- extern char *malloc();
- extern char *strncpy();
- extern char *optarg; /* Pointer to argument */
- extern int optind; /* Global argv index from getopt */
-
- /*
- * The following causes "tar.h" to produce definitions of all the
- * global variables, rather than just "extern" declarations of them.
- */
- #define TAR_EXTERN /**/
- #include "tar.h"
-
- /*
- * We should use a conversion routine that does reasonable error
- * checking -- atoi doesn't. For now, punt. FIXME.
- */
- #define intconv atoi
- extern int getoldopt();
- extern void read_and();
- extern void list_archive();
- extern void extract_archive();
- extern void create_archive();
-
- static FILE *namef; /* File to read names from */
- static char **n_argv; /* Argv used by name routines */
- static int n_argc; /* Argc used by name routines */
- /* They also use "optind" from getopt(). */
-
- void describe();
-
-
- /*
- * Main routine for tar.
- */
- main(argc, argv)
- int argc;
- char **argv;
- {
-
- /* Uncomment this message in particularly buggy versions...
- fprintf(stderr,
- "tar: You are running an experimental PD tar, maybe use /bin/tar.\n");
- */
-
- tar = "tar"; /* Set program name */
-
- options(argc, argv);
-
- name_init(argc, argv);
-
- if (f_create) {
- if (f_extract || f_list) goto dupflags;
- create_archive();
- } else if (f_extract) {
- if (f_list) goto dupflags;
- read_and(extract_archive);
- } else if (f_list) {
- read_and(list_archive);
- } else {
- dupflags:
- fprintf (stderr,
- "tar: you must specify exactly one of the c, t, or x options\n");
- describe();
- exit(EX_ARGSBAD);
- }
- exit(0);
- }
-
-
- /*
- * Parse the options for tar.
- */
- int
- options(argc, argv)
- int argc;
- char **argv;
- {
- register int c; /* Option letter */
-
- /* Set default option values */
- blocking = DEFBLOCKING; /* From Makefile */
- ar_file = DEF_AR_FILE; /* From Makefile */
-
- /* Parse options */
- while ((c = getoldopt(argc, argv, "b:BcdDf:hikmopstT:vxzZ")
- ) != EOF) {
- switch (c) {
-
- case 'b':
- blocking = intconv(optarg);
- break;
-
- case 'B':
- f_reblock++; /* For reading 4.2BSD pipes */
- break;
-
- case 'c':
- f_create++;
- break;
-
- case 'd':
- f_debug++; /* Debugging code */
- break; /* Yes, even with dbx */
-
- case 'D':
- f_sayblock++; /* Print block #s for debug */
- break; /* of bad tar archives */
-
- case 'f':
- ar_file = optarg;
- break;
-
- case 'h':
- f_follow_links++; /* follow symbolic links */
- break;
-
- case 'i':
- f_ignorez++; /* Ignore zero records (eofs) */
- /*
- * This can't be the default, because Unix tar
- * writes two records of zeros, then pads out the
- * block with garbage.
- */
- break;
-
- case 'k': /* Don't overwrite files */
- f_keep++;
- break;
-
- case 'm':
- f_modified++;
- break;
-
- case 'o': /* Generate old archive */
- f_oldarch++;
- break;
-
- case 'p':
- f_use_protection++;
- (void)umask(0); /* Turn off kernel "help" */
- break;
-
- case 's':
- f_sorted_names++; /* Names to extr are sorted */
- break;
-
- case 't':
- f_list++;
- break;
-
- case 'T':
- name_file = optarg;
- f_namefile++;
- break;
-
- case 'v':
- f_verbose++;
- break;
-
- case 'x':
- f_extract++;
- break;
-
- case 'z': /* Easy to type */
- case 'Z': /* Like the filename extension .Z */
- f_compress++;
- break;
-
- case '?':
- describe();
- exit(EX_ARGSBAD);
-
- }
- }
-
- blocksize = blocking * RECORDSIZE;
- }
-
-
- /* FIXME, describe tar options here */
- void
- describe()
- {
-
- fputs("tar: valid options:\n\
- -b N blocking factor N (block size = Nx512 bytes)\n\
- -B reblock as we read (for reading 4.2BSD pipes)\n\
- -c create an archive\n\
- -D dump record number within archive with each message\n\
- -f F read/write archive from file or device F\n\
- -h don't dump symbolic links; dump the files they point to\n\
- -i ignore blocks of zeros in the archive, which normally mean EOF\n\
- -k keep existing files, don't overwrite them from the archive\n\
- -m don't extract file modified time\n\
- -o write an old V7 format archive, rather than ANSI [draft 6] format\n\
- -p do extract all protection information\n\
- -s list of names to extract is sorted to match the archive\n\
- -t list a table of contents of an archive\n\
- -T F get names to extract or create from file F\n\
- -v verbosely list what files we process\n\
- -x extract files from an archive\n\
- -z or Z run the archive through compress(1)\n\
- ", stderr);
- }
-
-
- /*
- * Set up to gather file names for tar.
- *
- * They can either come from stdin or from argv.
- */
- name_init(argc, argv)
- int argc;
- char **argv;
- {
-
- if (f_namefile) {
- if (optind < argc) {
- fprintf(stderr, "tar: too many args with -T option\n");
- exit(EX_ARGSBAD);
- }
- if (!strcmp(name_file, "-")) {
- namef = stdin;
- } else {
- namef = fopen(name_file, "r");
- if (namef == NULL) {
- fprintf(stderr, "tar: ");
- perror(name_file);
- exit(EX_BADFILE);
- }
- }
- } else {
- /* Get file names from argv, after options. */
- n_argc = argc;
- n_argv = argv;
- }
- }
-
- /*
- * Get the next name from argv or the name file.
- *
- * Result is in static storage and can't be relied upon across two calls.
- */
- char *
- name_next()
- {
- static char buffer[NAMSIZ+2]; /* Holding pattern */
- register char *p;
- register char *q;
-
- if (namef == NULL) {
- /* Names come from argv, after options */
- if (optind < n_argc)
- return n_argv[optind++];
- return (char *)NULL;
- }
- p = fgets(buffer, NAMSIZ+1 /*nl*/, namef);
- if (p == NULL) return p; /* End of file */
- q = p+strlen(p)-1; /* Find the newline */
- *q-- = '\0'; /* Zap the newline */
- while (*q == '/') *q-- = '\0'; /* Zap trailing slashes too */
- return p;
- }
-
-
- /*
- * Close the name file, if any.
- */
- name_close()
- {
-
- if (namef != NULL && namef != stdin) fclose(namef);
- }
-
-
- /*
- * Gather names in a list for scanning.
- * Could hash them later if we really care.
- *
- * If the names are already sorted to match the archive, we just
- * read them one by one. name_gather reads the first one, and it
- * is called by name_match as appropriate to read the next ones.
- * At EOF, the last name read is just left in the buffer.
- * This option lets users of small machines extract an arbitrary
- * number of files by doing "tar t" and editing down the list of files.
- */
- name_gather()
- {
- register char *p;
- static struct name namebuf[1]; /* One-name buffer */
-
- if (f_sorted_names) {
- p = name_next();
- if (p) {
- namebuf->length = strlen(p);
- if (namebuf->length >= sizeof namebuf->name) {
- fprintf(stderr, "Argument name too long: %s\n",
- p);
- namebuf->length = (sizeof namebuf->name) - 1;
- }
- strncpy(namebuf->name, p, namebuf->length);
- namebuf->next = (struct name *)NULL;
- namebuf->found = 0;
- namelist = namebuf;
- namelast = namelist;
- }
- return;
- }
-
- /* Non sorted names -- read them all in */
- while (NULL != (p = name_next())) {
- addname(p);
- }
- }
-
- /*
- * A name from the namelist has been found.
- * If it's just a list,
-
- /*
- * Add a name to the namelist.
- */
- addname(name)
- char *name; /* pointer to name */
- {
- register int i; /* Length of string */
- register struct name *p; /* Current struct pointer */
-
- i = strlen(name);
- /*NOSTRICT*/
- p = (struct name *)
- malloc((unsigned)(i + sizeof(struct name) - NAMSIZ));
- p->next = (struct name *)NULL;
- p->length = i;
- p->found = 0;
- strncpy(p->name, name, i);
- p->name[i] = '\0'; /* Null term */
- if (namelast) namelast->next = p;
- namelast = p;
- if (!namelist) namelist = p;
- }
-
-
- /*
- * Match a name from an archive, p, with a name from the namelist.
- *
- * FIXME: Allow regular expressions in the name list.
- */
- name_match(p)
- register char *p;
- {
- register struct name *nlp;
- register int len;
-
- again:
- if (0 == (nlp = namelist)) /* Empty namelist is easy */
- return 1;
- len = strlen(p);
- for (; nlp != 0; nlp = nlp->next) {
- if ( nlp->name[0] == p[0] /* First chars match */
- && nlp->length <= len /* Archive len >= specified */
- && (p[nlp->length] == '\0' || p[nlp->length] == '/')
- /* Full match on file/dirname */
- && strncmp(p, nlp->name, nlp->length) == 0) /* Name compare */
- {
- nlp->found = 1; /* Remember it matched */
- return 1; /* We got a match */
- }
- }
- /*
- * Filename from archive not found in namelist.
- * If we have the whole namelist here, just return 0.
- * Otherwise, read the next name in and compare it.
- * If this was the last name, namelist->found will remain on.
- * If not, we loop to compare the newly read name.
- */
- if (f_sorted_names && namelist->found) {
- name_gather(); /* Read one more */
- if (!namelist->found) goto again;
- }
- return 0;
- }
-
-
- /*
- * Print the names of things in the namelist that were not matched.
- */
- names_notfound()
- {
- register struct name *nlp;
- register char *p;
-
- for (nlp = namelist; nlp != 0; nlp = nlp->next) {
- if (!nlp->found) {
- fprintf(stderr, "tar: %s not found in archive\n",
- nlp->name);
- }
- /*
- * We could free() the list, but the process is about
- * to die anyway, so save some CPU time. Amigas and
- * other similarly broken software will need to waste
- * the time, though.
- */
- #ifndef unix
- if (!f_sorted_names)
- free(nlp);
- #endif unix
- }
- namelist = (struct name *)NULL;
- namelast = (struct name *)NULL;
-
- if (f_sorted_names) {
- while (0 != (p = name_next()))
- fprintf(stderr, "tar: %s not found in archive\n", p);
- }
- }
- @@@ Fin de tar.c
- echo create.c 1>&2
- cat >create.c <<'@@@ Fin de create.c'
- /*
- * Create a tar archive.
- *
- * Written 25 Aug 1985 by John Gilmore, ihnp4!hoptoad!gnu.
- *
- * @(#)create.c 1.19 9/9/86 Public Domain - gnu
- */
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <stdio.h>
- #include <pwd.h>
- #include <grp.h>
-
- #ifdef BSD42
- #include <sys/dir.h>
- #else
- /*
- * FIXME: On other systems there is no standard place for the header file
- * for the portable directory access routines. Change the #include line
- * below to bring it in from wherever it is.
- */
- #include "ndir.h"
- #endif
-
- #ifdef USG
- #include <sys/sysmacros.h> /* major() and minor() defined here */
- #endif
-
- /*
- * V7 doesn't have a #define for this.
- */
- #ifndef O_RDONLY
- #define O_RDONLY 0
- #endif
-
- #include "tar.h"
-
- /*
- * If there are no symbolic links, there is no lstat(). Use stat().
- */
- #ifndef S_IFLNK
- #define lstat stat
- #endif
-
- extern char *malloc();
- extern char *strcpy();
- extern char *strncpy();
- extern int errno;
-
- union record *start_header();
- void finish_header();
- void finduname();
- void findgname();
- char *name_next();
- void to_oct();
-
-
- void
- create_archive()
- {
- register char *p;
-
- open_archive(0); /* Open for writing */
-
- while (p = name_next()) {
- dump_file(p);
- }
-
- write_eot();
- close_archive();
- name_close();
- }
-
- /*
- * Dump a single file. If it's a directory, recurse.
- * Result is 1 for success, 0 for failure.
- */
- int
- dump_file(p)
- char *p; /* File name to dump */
- {
- struct stat statbuf[1];
- union record *header;
- char type;
-
- /*
- * Use stat if following (rather than dumping) 4.2BSD's
- * symbolic links. Otherwise, use lstat (which, on non-4.2
- * systems, is #define'd to stat anyway.
- */
- if (0 != f_follow_links? stat(p, statbuf): lstat(p, statbuf))
- {
- badperror:
- perror(p);
- badfile:
- errors++;
- return 0;
- }
-
- switch (statbuf->st_mode & S_IFMT) {
-
- case S_IFREG: /* Regular file */
- {
- int f; /* File descriptor */
- int bufsize, count;
- register long sizeleft;
- register union record *start;
-
- /*
- * Handle a regular file with multiple links.
- *
- * We maintain a list of all such files that we've written so
- * far. Any time we see another, we check the list and
- * avoid dumping the data again if we've done it once already.
- */
- if (statbuf->st_nlink > 1) {
- register struct link *lp;
-
- /* First quick and dirty. Hashing, etc later FIXME */
- for (lp = linklist; lp; lp = lp->next) {
- if (lp->ino == statbuf->st_ino &&
- lp->dev == statbuf->st_dev) {
- /* We found a link. */
- statbuf->st_size = 0;
- header = start_header(p, statbuf);
- if (header == NULL) goto badfile;
- strcpy(header->header.linkname,
- lp->name);
- header->header.linkflag = LF_LINK;
- finish_header(header);
- if (f_verbose)
- annorec(stdout, (char *)NULL);
- printf("%s link to %s\n",
- p, lp->name);
- /* Maybe remove from list after all links found? */
- /* If so, have to compare names in case he dumps twice. */
- /* Later: I don't understand the above. If she
- * dumps the file twice, it would be BAD to dump
- * it the second time as a link... gnu 25Jul86
- */
- /* FIXME */
- goto donefile;
- }
- }
-
- /* Not found. Add it to the list. */
- lp = (struct link *) malloc( (unsigned)
- (strlen(p) + sizeof(struct link) - NAMSIZ));
- lp->ino = statbuf->st_ino;
- lp->dev = statbuf->st_dev;
- strcpy(lp->name, p);
- lp->next = linklist;
- linklist = lp;
- }
-
- sizeleft = statbuf->st_size;
- /* Don't bother opening empty, world readable files. */
- if (sizeleft > 0 || 0444 != (0444 & statbuf->st_mode)) {
- f = open(p, O_RDONLY);
- if (f < 0) goto badperror;
- } else {
- f = -1;
- }
- header = start_header(p, statbuf);
- if (header == NULL) goto badfile;
- finish_header(header);
- while (sizeleft > 0) {
- start = findrec();
- bufsize = endofrecs()->charptr - start->charptr;
- if (sizeleft < bufsize)
- bufsize = sizeleft;
- count = read(f, start->charptr, bufsize);
- if (count < 0) {
- annorec(stderr, tar);
- fprintf(stderr,
- "read error at byte %ld, reading %d bytes, in file ",
- statbuf->st_size - sizeleft,
- bufsize);
- perror(p); /* FIXME */
- goto padit;
- }
- sizeleft -= count;
- userec(start+(count-1)/RECORDSIZE);
- if (count == bufsize) continue;
- annorec(stderr, tar);
- fprintf(stderr,
- "%s: file shrunk by %d bytes, padding with zeros.\n",
- p, sizeleft);
- goto padit; /* Short read */
- }
- if (f >= 0)
- (void)close(f);
-
- /* Clear last block garbage to zeros, FIXME */
-
- if (f_verbose) {
- annorec(stdout, (char *)NULL);
- printf("%s\n", p);
- }
- donefile:
- break;
-
- /*
- * File shrunk or gave error, pad out tape to match
- * the size we specified in the header.
- */
- padit:
- abort();
- }
-
- #ifdef S_IFLNK
- case S_IFLNK: /* Symbolic link */
- {
- int size;
-
- statbuf->st_size = 0; /* Force 0 size on symlink */
- header = start_header(p, statbuf);
- if (header == NULL) goto badfile;
- size = readlink(p, header->header.linkname, NAMSIZ);
- if (size < 0) goto badperror;
- if (size == NAMSIZ) {
- annorec(stderr, tar);
- fprintf(stderr,
- "%s: symbolic link too long\n", p);
- break;
- }
- header->header.linkname[size] = '\0';
- header->header.linkflag = LF_SYMLINK;
- finish_header(header); /* Nothing more to do to it */
- if (f_verbose) {
- annorec(stdout, (char *)NULL);
- printf("%s\n", p);
- }
- }
- break;
- #endif
-
- case S_IFDIR: /* Directory */
- {
- register DIR *dirp;
- register struct direct *d;
- char namebuf[NAMSIZ+2];
- register int len;
-
- /* Build new prototype name */
- strncpy(namebuf, p, sizeof (namebuf));
- len = strlen(namebuf);
- while (len >= 1 && '/' == namebuf[len-1])
- len--; /* Delete trailing slashes */
- namebuf[len++] = '/'; /* Now add exactly one back */
-
- /*
- * Output directory header record with permissions
- * FIXME, do this AFTER files, to avoid R/O dir problems?
- * If Unix Std format, don't put / on end of dir name
- * If old archive format, don't write record at all.
- */
- if (!f_oldarch) {
- statbuf->st_size = 0; /* Force 0 size on dir */
- /*
- * If people could really read standard archives,
- * this should be: (FIXME)
- header = start_header(f_standard? p: namebuf, statbuf);
- * but since they'd interpret LF_DIR records as
- * regular files, we'd better put the / on the name.
- */
- header = start_header(namebuf, statbuf);
- if (header == NULL)
- goto badfile; /* eg name too long */
- if (f_standard) {
- header->header.linkflag = LF_DIR;
- }
- finish_header(header); /* Done with directory header */
- }
- if (f_verbose) {
- annorec(stdout, (char *)NULL);
- printf("%s\n", p);
- }
-
- /* Hack to remove "./" from the front of all the file names */
- if (len == 2 && namebuf[0] == '.') {
- len = 0;
- }
-
- /* Now output all the files in the directory */
- errno = 0;
- dirp = opendir(p);
- if (!dirp) {
- if (errno) {
- perror (p);
- } else {
- annorec(stderr, tar);
- fprintf(stderr, "%s: error opening directory",
- p);
- }
- break;
- }
-
- /* Should speed this up by cd-ing into the dir, FIXME */
- while (NULL != (d=readdir(dirp))) {
- /* Skip . and .. */
- if (d->d_name[0] == '.') {
- if (d->d_name[1] == '\0') continue;
- if (d->d_name[1] == '.') {
- if (d->d_name[2] == '\0') continue;
- }
- }
- if (d->d_namlen + len >= NAMSIZ) {
- annorec(stderr, tar);
- fprintf(stderr, "%s%s: name too long\n",
- namebuf, d->d_name);
- continue;
- }
- strcpy(namebuf+len, d->d_name);
- dump_file(namebuf);
- }
-
- closedir(dirp);
- }
- break;
-
- case S_IFCHR: /* Character special file */
- type = LF_CHR;
- goto easy;
-
- case S_IFBLK: /* Block special file */
- type = LF_BLK;
- goto easy;
-
- #ifdef S_IFIFO
- case S_IFIFO: /* Fifo special file */
- type = LF_FIFO;
- #endif S_IFIFO
-
- easy:
- if (!f_standard) goto unknown;
-
- statbuf->st_size = 0; /* Force 0 size */
- header = start_header(p, statbuf);
- if (header == NULL) goto badfile; /* eg name too long */
-
- header->header.linkflag = type;
- if (type != LF_FIFO) {
- to_oct((long) major(statbuf->st_rdev), 8,
- header->header.devmajor);
- to_oct((long) minor(statbuf->st_rdev), 8,
- header->header.devminor);
- }
-
- finish_header(header);
- if (f_verbose) {
- annorec(stdout, (char *)NULL);
- printf("%s\n", p);
- }
- break;
-
- default:
- unknown:
- annorec(stderr, tar);
- fprintf(stderr,
- "%s: Unknown file type; file ignored.\n", p);
- break;
- }
-
- return 1; /* Success */
- }
-
-
- /*
- * Make a header block for the file name whose stat info is st .
- * Return header pointer for success, NULL if the name is too long.
- */
- union record *
- start_header(name, st)
- char *name;
- register struct stat *st;
- {
- register union record *header;
-
- header = (union record *) findrec();
- bzero(header->charptr, sizeof(*header)); /* XXX speed up */
- strcpy(header->header.name, name);
- if (header->header.name[NAMSIZ-1]) {
- annorec(stderr, tar);
- fprintf(stderr, "%s: name too long\n", name);
- return NULL;
- }
- to_oct((long) (st->st_mode & ~S_IFMT),
- 8, header->header.mode);
- to_oct((long) st->st_uid, 8, header->header.uid);
- to_oct((long) st->st_gid, 8, header->header.gid);
- to_oct((long) st->st_size, 1+12, header->header.size);
- to_oct((long) st->st_mtime, 1+12, header->header.mtime);
- /* header->header.linkflag is left as null */
-
- #ifndef NONAMES
- /* Fill in new Unix Standard fields if desired. */
- if (f_standard) {
- header->header.linkflag = LF_NORMAL; /* New default */
- strcpy(header->header.magic, TMAGIC); /* Mark as Unix Std */
- finduname(header->header.uname, st->st_uid);
- findgname(header->header.gname, st->st_gid);
- }
- #endif
- return header;
- }
-
- /*
- * Finish off a filled-in header block and write it out.
- */
- void
- finish_header(header)
- register union record *header;
- {
- register int i, sum;
- register char *p;
-
- bcopy(CHKBLANKS, header->header.chksum, sizeof(header->header.chksum));
-
- sum = 0;
- p = header->charptr;
- for (i = sizeof(*header); --i >= 0; ) {
- /*
- * We can't use unsigned char here because of old compilers,
- * e.g. V7.
- */
- sum += 0xFF & *p++;
- }
-
- /*
- * Fill in the checksum field. It's formatted differently
- * from the other fields: it has [6] digits, a null, then a
- * space -- rather than digits, a space, then a null.
- * We use to_oct then write the null in over to_oct's space.
- * The final space is already there, from checksumming, and
- * to_oct doesn't modify it.
- *
- * This is a fast way to do:
- * (void) sprintf(header->header.chksum, "%6o", sum);
- */
- to_oct((long) sum, 8, header->header.chksum);
- header->header.chksum[6] = '\0'; /* Zap the space */
-
- userec(header);
- return;
- }
-
-
- /*
- * Quick and dirty octal conversion.
- * Converts long "value" into a "digs"-digit field at "where",
- * including a trailing space and room for a null. "digs"==3 means
- * 1 digit, a space, and room for a null.
- *
- * We assume the trailing null 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 should be equivalent to:
- * (void) sprintf(where, "%*lo ", digs-2, value);
- * except that sprintf fills in the trailing null and we don't.
- */
- void
- to_oct(value, digs, where)
- register long value;
- register int digs;
- register char *where;
- {
-
- --digs; /* Trailing null slot is left alone */
- where[--digs] = ' '; /* Put in the space, though */
-
- /* Produce the digits -- at least one */
- do {
- where[--digs] = '0' + (value & 7); /* one octal digit */
- value >>= 3;
- } while (digs > 0 && value != 0);
-
- /* Leading spaces, if necessary */
- while (digs > 0)
- where[--digs] = ' ';
-
- }
-
-
- /*
- * Write the EOT block(s).
- */
- write_eot()
- {
- union record *p;
-
- p = findrec();
- bzero(p->charptr, RECORDSIZE);
- userec(p);
- /* FIXME, only one EOT block should be needed. */
- p = findrec();
- bzero(p->charptr, RECORDSIZE);
- userec(p);
- }
- @@@ Fin de create.c
- echo extract.c 1>&2
- cat >extract.c <<'@@@ Fin de extract.c'
- /*
- * Extract files from a tar archive.
- *
- * Written 19 Nov 1985 by John Gilmore, ihnp4!hoptoad!gnu.
- *
- * @(#) extract.c 1.17 86/10/29 Public Domain - gnu
- */
-
- #include <stdio.h>
- #include <errno.h>
- #include <sys/types.h>
- #include <sys/stat.h>
-
- #ifdef BSD42
- #include <sys/file.h>
- #endif
-
- #ifdef USG
- #include <fcntl.h>
- #endif
-
- extern int errno; /* From libc.a */
- extern char *index(); /* From libc.a or port.c */
-
- #include "tar.h"
-
- extern union record *head; /* Points to current tape header */
- extern struct stat hstat[1]; /* Stat struct corresponding */
-
- extern void print_header();
- extern void skip_file();
- extern void pr_mkdir();
-
- int make_dirs(); /* Makes required directories */
-
- time_t now = 0; /* Current time */
-
- /*
- * Extract a file from the archive.
- */
- void
- extract_archive()
- {
- register char *data;
- int fd, check, namelen, written;
- long size;
- time_t acc_upd_times[2];
- int standard; /* Is header standard? */
-
- saverec(&head); /* Make sure it sticks around */
- userec(head); /* And go past it in the archive */
- decode_header(head, hstat, &standard, 1); /* Snarf fields */
-
- /* Print the record from 'head' and 'hstat' */
- if (f_verbose)
- print_header();
-
- switch (head->header.linkflag) {
-
- default:
- annofile(stderr, tar);
- fprintf(stderr, "Unknown file type %d for %s\n",
- head->header.linkflag, head->header.name);
- /* FALL THRU */
-
- case LF_OLDNORMAL:
- case LF_NORMAL:
- /*
- * Appears to be a file.
- * See if it's really a directory.
- */
- namelen = strlen(head->header.name)-1;
- if (head->header.name[namelen] == '/')
- goto really_dir;
-
- /* FIXME, deal with protection issues */
- /* FIXME, f_keep doesn't work on V7, st_mode loses too */
- again_file:
- fd = open(head->header.name,
- f_keep?
- O_NDELAY|O_WRONLY|O_APPEND|O_CREAT|O_EXCL:
- O_NDELAY|O_WRONLY|O_APPEND|O_CREAT|O_TRUNC,
- hstat->st_mode);
- if (fd < 0) {
- if (make_dirs(head->header.name))
- goto again_file;
- annofile(stderr, tar);
- fprintf(stderr, "Could not make file ");
- perror(head->header.name);
- skip_file((long)hstat->st_size);
- goto quit;
- }
-
- for (size = hstat->st_size;
- size > 0;
- size -= written) {
- /*
- * Locate data, determine max length
- * writeable, write it, record that
- * we have used the data, then check
- * if the write worked.
- */
- data = findrec()->charptr;
- written = endofrecs()->charptr - data;
- if (written > size) written = size;
- errno = 0;
- check = write (fd, data, written);
- /*
- * The following is in violation of strict
- * typing, since the arg to userec
- * should be a struct rec *. FIXME.
- */
- userec(data + written - 1);
- if (check == written) continue;
- /*
- * Error in writing to file.
- * Print it, skip to next file in archive.
- */
- annofile(stderr, tar);
- fprintf(stderr,
- "Tried to write %d bytes to file, could only write %d:\n",
- written, check);
- perror(head->header.name);
- (void) close(fd);
- skip_file((long)(size - written));
- goto quit;
- }
-
- check = close(fd);
- if (check < 0) {
- annofile(stderr, tar);
- fprintf(stderr, "Error while closing ");
- perror(head->header.name);
- }
-
- /* FIXME, deal with uid/gid/mtimes/suid */
-
- /*
- * Set the modified time of the file.
- *
- * Note that we set the accessed time to "now", which
- * is really "the time we started extracting files".
- */
- if (!f_modified) {
- if (!now)
- now = time((time_t *)0); /* Just do it once */
- acc_upd_times[0] = now; /* Accessed now */
- acc_upd_times[1] = hstat->st_mtime; /* Mod'd */
- if (utime(head->header.name, acc_upd_times) < 0) {
- annofile(stderr, tar);
- perror(head->header.name);
- }
- }
-
- /*
- * If '-p' is not set, OR if the file has pretty normal
- * mode bits, we can skip the chmod and save a sys call.
- * This works because we did umask(0) if -p is set, so
- * the open() that created the file will have set the modes
- * properly.
- * FIXME: I don't know what open() does w/UID/GID/SVTX bits.
- * However, if we've done a chown(), they got reset.
- */
- if (f_use_protection
- && (hstat->st_mode & (S_ISUID|S_ISGID|S_ISVTX))) {
- if (chmod(head->header.name, (int)hstat->st_mode) < 0) {
- annofile(stderr, tar);
- perror(head->header.name);
- }
- }
-
- quit:
- break;
-
- case LF_LINK:
- again_link:
- check = link (head->header.linkname,
- head->header.name);
- /* FIXME, don't worry uid, gid, etc... */
- if (check == 0)
- break;
- if (make_dirs(head->header.linkname))
- goto again_link;
- annofile(stderr, tar);
- fprintf(stderr, "Could not link %s to ",
- head->header.name);
- perror(head->header.linkname);
- break;
-
- #ifdef S_IFLNK
- case LF_SYMLINK:
- again_symlink:
- check = symlink(head->header.linkname,
- head->header.name);
- /* FIXME, don't worry uid, gid, etc... */
- if (check == 0)
- break;
- if (make_dirs(head->header.linkname))
- goto again_symlink;
- annofile(stderr, tar);
- fprintf(stderr, "Could not create symlink ");
- perror(head->header.linkname);
- break;
- #endif
-
- case LF_CHR:
- hstat->st_mode |= S_IFCHR;
- goto make_node;
-
- case LF_BLK:
- hstat->st_mode |= S_IFBLK;
- make_node:
- check = mknod(head->header.name, (int) hstat->st_mode,
- (int) hstat->st_dev);
- if (check != 0) {
- if (make_dirs(head->header.name))
- goto make_node;
- annofile(stderr, tar);
- fprintf(stderr, "Could not make special file ");
- perror(head->header.name);
- break;
- };
- break;
-
- case LF_DIR:
- /* Check for trailing / */
- namelen = strlen(head->header.name)-1;
- really_dir:
- while (namelen && head->header.name[namelen] == '/')
- head->header.name[namelen--] = '\0'; /* Zap / */
-
- /* FIXME, deal with umask */
- again_dir:
- check = mkdir(head->header.name, (int)hstat->st_mode);
- if (check != 0) {
- if (make_dirs(head->header.name))
- goto again_dir;
- annofile(stderr, tar);
- fprintf(stderr, "Could not make directory ");
- perror(head->header.name);
- break;
- }
-
- /* FIXME, deal with uid/gid */
- /* FIXME, Remember timestamps for after files created? */
- break;
-
- case LF_FIFO:
- abort(); /* FIXME */
- break;
-
- }
-
- /* We don't need to save it any longer. */
- saverec((union record **) 0); /* Unsave it */
- }
-
- /*
- * After a file/link/symlink/dir creation has failed, see if
- * it's because some required directory was not present, and if
- * so, create all required dirs.
- */
- int
- make_dirs(pathname)
- char *pathname;
- {
- char *p; /* Points into path */
- int madeone = 0; /* Did we do anything yet? */
- int save_errno = errno; /* Remember caller's errno */
- int check;
-
- if (errno != ENOENT)
- return 0; /* Not our problem */
-
- for (p = index(pathname, '/'); p != NULL; p = index(p+1, '/')) {
- /* Avoid mkdir of empty string, if leading or double '/' */
- if (p == pathname || p[-1] == '/')
- continue;
- /* Avoid mkdir where last part of path is '.' */
- if (p[-1] == '.' && (p == pathname+1 || p[-2] == '/'))
- continue;
- *p = 0; /* Truncate the path there */
- check = mkdir (pathname, 0777); /* Try to create it as a dir */
- *p = '/';
- if (check == 0) {
- /* FIXME chown, chgrp it same as file being created */
- /* FIXME, show mode as modified by current umask */
- pr_mkdir(pathname, p-pathname, 0777);
- madeone++; /* Remember if we made one */
- continue;
- }
- if (errno == EEXIST) /* Directory already exists */
- continue;
- /*
- * Some other error in the mkdir. We return to the caller.
- */
- break;
- }
-
- errno = save_errno; /* Restore caller's errno */
- return madeone; /* Tell them to retry if we made one */
- }
- @@@ Fin de extract.c
- echo buffer.c 1>&2
- cat >buffer.c <<'@@@ Fin de buffer.c'
- /*
- * Buffer management for public domain tar.
- *
- * Written by John Gilmore, ihnp4!hoptoad!gnu, on 25 August 1985.
- *
- * @(#) buffer.c 1.14 10/28/86 Public Domain - gnu
- */
-
- #include <stdio.h>
- #include <errno.h>
- #include <sys/types.h> /* For non-Berkeley systems */
- #include <sys/file.h>
- #include <signal.h>
-
- #include "tar.h"
- #include "port.h"
-
- #define STDIN 0 /* Standard input file descriptor */
- #define STDOUT 1 /* Standard output file descriptor */
-
- #define PREAD 0 /* Read file descriptor from pipe() */
- #define PWRITE 1 /* Write file descriptor from pipe() */
-
- extern char *valloc();
-
- /*
- * V7 doesn't have a #define for this.
- */
- #ifndef O_RDONLY
- #define O_RDONLY 0
- #endif
-
- #define MAGIC_STAT 105 /* Magic status returned by child, if
- it can't exec compress. We hope compress
- never returns this status! */
- /*
- * The record pointed to by save_rec should not be overlaid
- * when reading in a new tape block. Copy it to record_save_area first, and
- * change the pointer in *save_rec to point to record_save_area.
- * Saved_recno records the record number at the time of the save.
- * This is used by annofile() to print the record number of a file's
- * header record.
- */
- static union record **save_rec;
- static union record record_save_area;
- static int saved_recno;
-
- /*
- * PID of child compress program, if f_compress.
- */
- static int compress_pid;
-
- /*
- * Record number of the start of this block of records
- */
- static int baserec;
-
- /*
- * Error recovery stuff
- */
- static int r_error_count;
-
-
- /*
- * Return the location of the next available input or output record.
- */
- union record *
- findrec()
- {
- if (ar_record == ar_last) {
- flush_archive();
- if (ar_record == ar_last)
- return (union record *)NULL; /* EOF */
- }
- return ar_record;
- }
-
-
- /*
- * Indicate that we have used all records up thru the argument.
- * (should the arg have an off-by-1? XXX FIXME)
- */
- void
- userec(rec)
- union record *rec;
- {
- while(rec >= ar_record)
- ar_record++;
- /*
- * Do NOT flush the archive here. If we do, the same
- * argument to userec() could mean the next record (if the
- * input block is exactly one record long), which is not what
- * is intended.
- */
- if (ar_record > ar_last)
- abort();
- }
-
-
- /*
- * Return a pointer to the end of the current records buffer.
- * All the space between findrec() and endofrecs() is available
- * for filling with data, or taking data from.
- */
- union record *
- endofrecs()
- {
- return ar_last;
- }
-
-
- /*
- * Open an archive file. The argument specifies whether we are
- * reading or writing.
- */
- open_archive(read)
- int read;
- {
-
- if (ar_file[0] == '-' && ar_file[1] == '\0') {
- if (read) archive = STDIN;
- else archive = STDOUT;
- } else if (read) {
- archive = open(ar_file, O_RDONLY);
- } else {
- archive = creat(ar_file, 0666);
- }
-
- if (archive < 0) {
- perror(ar_file);
- exit(EX_BADARCH);
- }
-
- /*NOSTRICT*/
- ar_block = (union record *) valloc((unsigned)blocksize);
- if (!ar_block) {
- fprintf(stderr,
- "tar: could not allocate memory for blocking factor %d\n",
- blocking);
- exit(EX_ARGSBAD);
- }
-
- ar_record = ar_block;
- ar_last = ar_block + blocking;
-
- /*
- * Handle compressed archives.
- *
- * FIXME, currently supported for reading only.
- * FIXME, writing involves forking again for a small process
- * that will reblock the output of compress to the user's specs.
- */
- if (f_compress) {
- int pipes[2];
- int err;
-
- if (!read) {
- fprintf(stderr,
- "tar: cannot write compressed archives yet.\n");
- exit(EX_ARGSBAD);
- }
-
- /* Create a pipe to get compress's output to us */
- err = pipe(pipes);
- if (err < 0) {
- perror ("tar: cannot create pipe to compress");
- exit(EX_SYSTEM);
- }
-
- /* Fork compress process */
- compress_pid = fork();
- if (compress_pid < 0) {
- perror("tar: cannot fork compress");
- exit(EX_SYSTEM);
- }
-
- /*
- * Child process.
- *
- * Move input to stdin, write side of pipe to stdout,
- * then exec compress.
- */
- if (compress_pid == 0) {
- (void) close (pipes[PREAD]); /* We won't use it */
- if (archive != STDIN) {
- (void) close(STDIN);
- err = dup(archive);
- if (err != 0) {
- perror(
- "tar: cannot dup input to stdin");
- exit(EX_SYSTEM);
- }
- (void) close(archive);
- }
- if (pipes[PWRITE] != STDOUT) {
- (void) close (STDOUT);
- err = dup (pipes[PWRITE]);
- if (err != STDOUT) {
- perror(
- "tar: cannot dup pipe output");
- exit(MAGIC_STAT);
- }
- (void) close (pipes[PWRITE]);
- }
- execlp("compress", "compress", "-d", (char *)0);
- perror("tar: cannot exec compress");
- exit(MAGIC_STAT);
- }
-
- /*
- * Parent process. Clean up.
- * FIXME, note that this may leave standard input closed,
- * if the compressed archive was on standard input.
- */
- (void) close (archive); /* Close compressed archive */
- (void) close (pipes[PWRITE]); /* Close write side of pipe */
- archive = pipes[PREAD]; /* Read side is our archive */
-
- #ifdef BSD42
- f_reblock++; /* Pipe will give random # of bytes */
- #endif BSD42
- }
-
- ar_reading = read;
- if (read) {
- ar_last = ar_block; /* Set up for 1st block = # 0 */
- flush_archive();
- }
- }
-
-
- /*
- * Remember a union record * as pointing to something that we
- * need to keep when reading onward in the file. Only one such
- * thing can be remembered at once, and it only works when reading
- * an archive.
- */
- saverec(pointer)
- union record **pointer;
- {
-
- save_rec = pointer;
- saved_recno = baserec + ar_record - ar_block;
- }
-
- /*
- * Perform a write to flush the buffer.
- */
- fl_write()
- {
- int err;
-
- err = write(archive, ar_block->charptr, blocksize);
- if (err == blocksize) return;
- /* FIXME, multi volume support on write goes here */
- if (err < 0)
- perror(ar_file);
- else
- fprintf(stderr, "tar: %s: write failed, short %d bytes\n",
- ar_file, blocksize - err);
- exit(EX_BADARCH);
- }
-
-
- /*
- * Handle read errors on the archive.
- *
- * If the read should be retried, readerror() returns to the caller.
- */
- void
- readerror()
- {
- # define READ_ERROR_MAX 10
-
- read_error_flag++; /* Tell callers */
-
- annorec(stderr, tar);
- fprintf(stderr, "Read error on ");
- perror(ar_file);
-
- if (baserec == 0) {
- /* First block of tape. Probably stupidity error */
- exit(EX_BADARCH);
- }
-
- /*
- * Read error in mid archive. We retry up to READ_ERROR_MAX times
- * and then give up on reading the archive. We set read_error_flag
- * for our callers, so they can cope if they want.
- */
- if (r_error_count++ > READ_ERROR_MAX) {
- annorec(stderr, tar);
- fprintf(stderr, "Too many errors, quitting.\n");
- exit(EX_BADARCH);
- }
- return;
- }
-
-
- /*
- * Perform a read to flush the buffer.
- */
- fl_read()
- {
- int err; /* Result from system call */
- int left; /* Bytes left */
- char *more; /* Pointer to next byte to read */
-
- /*
- * Clear the count of errors. This only applies to a single
- * call to fl_read. We leave read_error_flag alone; it is
- * only turned off by higher level software.
- */
- r_error_count = 0; /* Clear error count */
-
- /*
- * If we are about to wipe out a record that
- * somebody needs to keep, copy it out to a holding
- * area and adjust somebody's pointer to it.
- */
- if (save_rec &&
- *save_rec >= ar_record &&
- *save_rec < ar_last) {
- record_save_area = **save_rec;
- *save_rec = &record_save_area;
- }
- error_loop:
- err = read(archive, ar_block->charptr, blocksize);
- if (err == blocksize) return;
- if (err < 0) {
- readerror();
- goto error_loop; /* Try again */
- }
-
- more = ar_block->charptr + err;
- left = blocksize - err;
-
- again:
- if (0 == (((unsigned)left) % RECORDSIZE)) {
- /* FIXME, for size=0, multi vol support */
- /* On the first block, warn about the problem */
- if (!f_reblock && baserec == 0 && f_verbose) {
- annorec(stderr, tar);
- fprintf(stderr, "Blocksize = %d records\n",
- err / RECORDSIZE);
- }
- ar_last = ar_block + ((unsigned)(blocksize - left))/RECORDSIZE;
- return;
- }
- if (f_reblock) {
- /*
- * User warned us about this. Fix up.
- */
- if (left > 0) {
- error_loop_2:
- err = read(archive, more, left);
- if (err < 0) {
- readerror();
- goto error_loop_2; /* Try again */
- }
- if (err == 0) {
- annorec(stderr, tar);
- fprintf(stderr,
- "%s: eof not on block boundary, strange...\n",
- ar_file);
- exit(EX_BADARCH);
- }
- left -= err;
- more += err;
- goto again;
- }
- } else {
- annorec(stderr, tar);
- fprintf(stderr, "%s: read %d bytes, strange...\n",
- ar_file, err);
- exit(EX_BADARCH);
- }
- }
-
-
- /*
- * Flush the current buffer to/from the archive.
- */
- flush_archive()
- {
- baserec += ar_last - ar_block;/* Keep track of block #s */
- ar_record = ar_block; /* Restore pointer to start */
- ar_last = ar_block + blocking; /* Restore pointer to end */
-
- if (!ar_reading)
- fl_write();
- else
- fl_read();
- }
-
- /*
- * Close the archive file.
- */
- close_archive()
- {
- int child;
- int status;
-
- if (!ar_reading) flush_archive();
- (void) close(archive);
-
- if (f_compress) {
- /*
- * Loop waiting for the right child to die, or for
- * no more kids.
- */
- while (((child = wait(&status)) != compress_pid) && child != -1)
- ;
-
- if (child != -1) {
- switch (TERM_SIGNAL(status)) {
- case 0: /* Terminated by itself */
- if (TERM_VALUE(status) == MAGIC_STAT) {
- exit(EX_SYSTEM);/* Child had trouble */
- }
- if (TERM_VALUE(status))
- fprintf(stderr,
- "tar: compress child returned status %d\n",
- TERM_VALUE(status));
- case SIGPIPE:
- break; /* This is OK. */
-
- default:
- fprintf(stderr,
- "tar: compress child died with signal %d%s\n",
- TERM_SIGNAL(status),
- TERM_COREDUMP(status)? " (core dumped)": "");
- }
- }
- }
- }
-
-
- /*
- * Message management.
- *
- * anno writes a message prefix on stream (eg stdout, stderr).
- *
- * The specified prefix is normally output followed by a colon and a space.
- * However, if other command line options are set, more output can come
- * out, such as the record # within the archive.
- *
- * If the specified prefix is NULL, no output is produced unless the
- * command line option(s) are set.
- *
- * If the third argument is 1, the "saved" record # is used; if 0, the
- * "current" record # is used.
- */
- void
- anno(stream, prefix, savedp)
- FILE *stream;
- char *prefix;
- int savedp;
- {
- # define MAXANNO 50
- char buffer[MAXANNO]; /* Holds annorecment */
- # define ANNOWIDTH 13
- int space;
-
- if (f_sayblock) {
- if (prefix) {
- fputs(prefix, stream);
- putc(' ', stream);
- }
- sprintf(buffer, "rec %d: ",
- savedp? saved_recno:
- baserec + ar_record - ar_block);
- fputs(buffer, stream);
- space = ANNOWIDTH - strlen(buffer);
- if (space > 0) {
- fprintf(stream, "%*s", space, "");
- }
- } else if (prefix) {
- fputs(prefix, stream);
- fputs(": ", stream);
- }
- }
- @@@ Fin de buffer.c
- echo getoldopt.c 1>&2
- cat >getoldopt.c <<'@@@ Fin de getoldopt.c'
- /*
- * Plug-compatible replacement for getopt() for parsing tar-like
- * arguments. If the first argument begins with "-", it uses getopt;
- * otherwise, it uses the old rules used by tar, dump, and ps.
- *
- * Written 25 August 1985 by John Gilmore (ihnp4!hoptoad!gnu) and placed
- * in the Pubic Domain for your edification and enjoyment.
- *
- * @(#)getoldopt.c 1.4 2/4/86 Public Domain - gnu
- */
-
- #include <stdio.h>
-
-
- int
- getoldopt(argc, argv, optstring)
- int argc;
- char **argv;
- char *optstring;
- {
- extern char *optarg; /* Points to next arg */
- extern int optind; /* Global argv index */
- static char *key; /* Points to next keyletter */
- static char use_getopt; /* !=0 if argv[1][0] was '-' */
- extern char *index();
- char c;
- char *place;
-
- optarg = NULL;
-
- if (key == NULL) { /* First time */
- if (argc < 2) return EOF;
- key = argv[1];
- if (*key == '-')
- use_getopt++;
- else
- optind = 2;
- }
-
- if (use_getopt)
- return getopt(argc, argv, optstring);
-
- c = *key++;
- if (c == '\0') {
- key--;
- return EOF;
- }
- place = index(optstring, c);
-
- if (place == NULL || c == ':') {
- fprintf(stderr, "%s: unknown option %c\n", argv[0], c);
- return('?');
- }
-
- place++;
- if (*place == ':') {
- if (optind < argc) {
- optarg = argv[optind];
- optind++;
- } else {
- fprintf(stderr, "%s: %c argument missing\n",
- argv[0], c);
- return('?');
- }
- }
-
- return(c);
- }
- @@@ Fin de getoldopt.c
- echo list.c 1>&2
- cat >list.c <<'@@@ Fin de list.c'
- /*
- * List a tar archive.
- *
- * Also includes support routines for reading a tar archive.
- *
- * Pubic Domain version written 26 Aug 1985 by John Gilmore (ihnp4!hoptoad!gnu).
- *
- * @(#)list.c 1.18 9/23/86 Public Domain - gnu
- */
- #include <stdio.h>
- #include <ctype.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <sys/file.h>
-
- char *ctime(); /* From libc.a */
-
- #define isodigit(c) ( ((c) >= '0') && ((c) <= '7') )
-
- #include "tar.h"
-
- long from_oct(); /* Decode octal number */
- void demode(); /* Print file mode */
-
- union record *head; /* Points to current archive header */
- struct stat hstat[1]; /* Stat struct corresponding */
-
- void print_header();
- void skip_file();
-
-
- /*
- * Main loop for reading an archive.
- */
- void
- read_and(do_something)
- void (*do_something)();
- {
- int status = 1;
- int prev_status;
-
- name_gather(); /* Gather all the names */
- open_archive(1); /* Open for reading */
-
- for(;;) {
- prev_status = status;
- status = read_header();
- switch (status) {
-
- case 1: /* Valid header */
- /* We should decode next field (mode) first... */
- /* Ensure incoming names are null terminated. */
- head->header.name[NAMSIZ-1] = '\0';
-
- if (!name_match(head->header.name)) {
- /* Skip past it in the archive */
- userec(head);
- /* Skip to the next header on the archive */
- skip_file((long)hstat->st_size);
- continue;
- }
-
- (*do_something)();
- continue;
-
- /*
- * If the previous header was good, tell them
- * that we are skipping bad ones.
- */
- case 0: /* Invalid header */
- case0:
- userec(head);
- if (prev_status == 1) {
- annorec(stderr, tar);
- fprintf(stderr,
- "Skipping to next file header...\n");
- }
- continue;
-
- case 2: /* Block of zeroes */
- if (f_ignorez)
- goto case0; /* Just skip if asked */
- /* FALL THRU */
- case EOF: /* End of archive */
- break;
- }
- break;
- };
-
- close_archive();
- names_notfound(); /* Print names not found */
- }
-
-
- /*
- * Print a header record, based on tar options.
- */
- void
- list_archive()
- {
-
- /* Save the record */
- saverec(&head);
-
- /* Print the header record */
- print_header();
-
- /* Skip past it in the archive */
- saverec((union record **) 0); /* Unsave it */
- userec(head);
-
- /* Skip to the next header on the archive */
- skip_file((long)hstat->st_size);
- }
-
-
- /*
- * Read a record that's supposed to be a header record.
- * Return its address in "head", and if it is good, the file's
- * size in hstat->st_size.
- *
- * Return 1 for success, 0 if the checksum is bad, EOF on eof,
- * 2 for a block full of zeros (EOF marker).
- *
- * You must always userec(head) to skip past the header which this
- * routine reads.
- */
- int
- read_header()
- {
- register int i;
- register long sum, recsum;
- register char *p;
- register union record *header;
-
- header = findrec();
- head = header; /* This is our current header */
- if (NULL == header) return EOF;
-
- recsum = from_oct(8, header->header.chksum);
-
- sum = 0;
- p = header->charptr;
- for (i = sizeof(*header); --i >= 0;) {
- /*
- * We can't use unsigned char here because of old compilers,
- * e.g. V7.
- */
- sum += 0xFF & *p++;
- }
-
- /* Adjust checksum to count the "chksum" field as blanks. */
- for (i = sizeof(header->header.chksum); --i >= 0;)
- sum -= 0xFF & header->header.chksum[i];
- sum += ' '* sizeof header->header.chksum;
-
- if (sum == recsum) {
- /*
- * Good record. Decode file size and return.
- */
- if (header->header.linkflag == LF_LINK)
- hstat->st_size = 0; /* Links 0 size on tape */
- else
- hstat->st_size = from_oct(1+12, header->header.size);
- return 1;
- }
-
- if (sum == 8*' ') {
- /*
- * This is a zeroed block...whole block is 0's except
- * for the 8 blanks we faked for the checksum field.
- */
- return 2;
- }
-
- return 0;
- }
-
-
- /*
- * Decode things from a file header record into a "struct stat".
- * Also set "*stdp" to !=0 or ==0 depending whether header record is "Unix
- * Standard" tar format or regular old tar format.
- *
- * read_header() has already decoded the checksum and length, so we don't.
- *
- * If wantug != 0, we want the uid/group info decoded from Unix Standard
- * tapes (for extraction). If == 0, we are just printing anyway, so save time.
- */
- decode_header(header, st, stdp, wantug)
- register union record *header;
- register struct stat *st;
- int *stdp;
- int wantug;
- {
-
- st->st_mode = from_oct(8, header->header.mode);
- st->st_mtime = from_oct(1+12, header->header.mtime);
-
- if (0==strcmp(header->header.magic, TMAGIC)) {
- /* Unix Standard tar archive */
- *stdp = 1;
- if (wantug) {
- st->st_uid = finduid(header->header.uname);
- st->st_gid = findgid(header->header.gname);
- }
- switch (header->header.linkflag)
- case LF_BLK: case LF_CHR:
- st->st_dev = makedev(from_oct(8, header->header.devmajor),
- from_oct(8, header->header.devminor));
- } else {
- /* Old fashioned tar archive */
- *stdp = 0;
- st->st_uid = from_oct(8, header->header.uid);
- st->st_gid = from_oct(8, header->header.gid);
- st->st_dev = 0;
- }
- }
-
-
- /*
- * Quick and dirty octal conversion.
- *
- * Result is -1 if the field is invalid (all blank, or nonoctal).
- */
- long
- from_oct(digs, where)
- register int digs;
- register char *where;
- {
- register long value;
-
- while (isspace(*where)) { /* Skip spaces */
- where++;
- if (--digs <= 0)
- return -1; /* All blank field */
- }
- value = 0;
- while (digs > 0 && isodigit(*where)) { /* Scan til nonoctal */
- value = (value << 3) | (*where++ - '0');
- --digs;
- }
-
- if (digs > 0 && *where && !isspace(*where))
- return -1; /* Ended on non-space/nul */
-
- return value;
- }
-
-
- /*
- * Actually print it.
- */
- #define UGSWIDTH 11 /* min width of User, group, size */
- #define DATEWIDTH 19 /* Last mod date */
- static int ugswidth = UGSWIDTH; /* Max width encountered so far */
-
- void
- print_header()
- {
- char modes[11];
- char *timestamp;
- char uform[11], gform[11]; /* These hold formatted ints */
- char *user, *group;
- char size[24]; /* Holds a formatted long or maj, min */
- long longie; /* To make ctime() call portable */
- int pad;
- int header_std; /* Is header standard or not? */
-
- annofile(stdout, (char *)NULL);
-
- if (f_verbose) {
- decode_header(head, hstat, &header_std, 0);
-
- /* File type and modes */
- modes[0] = '?';
- switch (head->header.linkflag) {
- case LF_NORMAL:
- case LF_OLDNORMAL:
- case LF_LINK:
- modes[0] = '-';
- if ('/' == head->header.name[strlen(head->header.name)-1])
- modes[0] = 'd';
- break;
- case LF_DIR: modes[0] = 'd'; break;
- case LF_SYMLINK:modes[0] = 'l'; break;
- case LF_BLK: modes[0] = 'b'; break;
- case LF_CHR: modes[0] = 'c'; break;
- case LF_FIFO: modes[0] = 'f'; break;
- case LF_CONTIG: modes[0] = '='; break;
- }
-
- demode((unsigned)hstat->st_mode, modes+1);
-
- /* Timestamp */
- longie = hstat->st_mtime;
- timestamp = ctime(&longie);
- timestamp[16] = '\0';
- timestamp[24] = '\0';
-
- /* User and group names */
- if (*head->header.uname && header_std) {
- user = head->header.uname;
- } else {
- user = uform;
- (void)sprintf(uform, "%d", (int)hstat->st_uid);
- }
- if (*head->header.gname && header_std) {
- group = head->header.gname;
- } else {
- group = gform;
- (void)sprintf(gform, "%d", (int)hstat->st_gid);
- }
-
- /* Format the file size or major/minor device numbers */
- switch (head->header.linkflag) {
- case LF_CHR:
- case LF_BLK:
- (void)sprintf(size, "%d, %d",
- major(hstat->st_dev),
- minor(hstat->st_dev));
- break;
-
- default:
- (void)sprintf(size, "%ld", (long)hstat->st_size);
- }
-
- /* Figure out padding and print the whole line. */
- pad = strlen(user) + strlen(group) + strlen(size) + 1;
- if (pad > ugswidth) ugswidth = pad;
-
- printf("%s %s/%s %*s%s %s %s %.*s",
- modes,
- user,
- group,
- ugswidth - pad,
- "",
- size,
- timestamp+4, timestamp+20,
- sizeof(head->header.name),
- head->header.name);
- } else {
- printf("%s", head->header.name);
- }
-
- if (f_verbose) switch (head->header.linkflag) {
- case LF_SYMLINK:
- printf(" -> %s\n", head->header.linkname);
- break;
-
- case LF_LINK:
- printf(" link to %s\n", head->header.linkname);
- break;
-
- default:
- printf(" unknown file type '%c'\n", head->header.linkflag);
- break;
-
- case LF_OLDNORMAL:
- case LF_NORMAL:
- case LF_CHR:
- case LF_BLK:
- case LF_DIR:
- case LF_FIFO:
- case LF_CONTIG:
- putc('\n', stdout);
- break;
- } else {
- putc('\n', stdout);
- }
-
- /* FIXME: we don't print major/minor device numbers */
- }
-
- /*
- * Print a similar line when we make a directory automatically.
- */
- void
- pr_mkdir(pathname, length, mode)
- char *pathname;
- int length;
- int mode;
- {
- char modes[11];
-
- if (f_verbose) {
- /* File type and modes */
- modes[0] = 'd';
- demode((unsigned)mode, modes+1);
-
- annofile(stdout, (char *)NULL);
- printf("%s %*s %.*s\n",
- modes,
- ugswidth+DATEWIDTH,
- "Creating directory:",
- length,
- pathname);
- }
- }
-
-
- /*
- * Skip over <size> bytes of data in records in the archive.
- */
- void
- skip_file(size)
- register long size;
- {
- union record *x;
-
- while (size > 0) {
- x = findrec();
- if (x == NULL) { /* Check it... */
- annorec(stderr, tar);
- fprintf(stderr, "Unexpected EOF on archive file\n");
- exit(EX_BADARCH);
- }
- userec(x);
- size -= RECORDSIZE;
- }
- }
-
-
- /*
- * Decode the mode string from a stat entry into a 9-char string and a null.
- */
- void
- demode(mode, string)
- register unsigned mode;
- register char *string;
- {
- register unsigned mask;
- register char *rwx = "rwxrwxrwx";
-
- for (mask = 0400; mask != 0; mask >>= 1) {
- if (mode & mask)
- *string++ = *rwx++;
- else {
- *string++ = '-';
- rwx++;
- }
- }
-
- if (mode & S_ISUID)
- if (string[-7] == 'x')
- string[-7] = 's';
- else
- string[-7] = 'S';
- if (mode & S_ISGID)
- if (string[-4] == 'x')
- string[-4] = 's';
- else
- string[-4] = 'S';
- if (mode & S_ISVTX)
- if (string[-1] == 'x')
- string[-1] = 't';
- else
- string[-1] = 'T';
- *string = '\0';
- }
- @@@ Fin de list.c
- echo names.c 1>&2
- cat >names.c <<'@@@ Fin de names.c'
- /*
- * Look up user and/or group names.
- *
- * This file should be modified for non-unix systems to do something
- * reasonable.
- *
- * @(#)names.c 1.1 9/9/86 Public Domain - gnu
- */
- #include <sys/types.h>
- #include <pwd.h>
- #include <grp.h>
- #include "tar.h"
-
- static int saveuid = -993;
- static char saveuname[TUNMLEN];
- static int my_uid = -993;
-
- static int savegid = -993;
- static char savegname[TGNMLEN];
- static int my_gid = -993;
-
- #define myuid ( my_uid < 0? my_uid = getuid(): my_uid )
- #define mygid ( my_gid < 0? my_gid = getgid(): my_gid )
-
-
- #ifndef NONAMES
- /*
- * Look up a user or group name from a uid/gid, maintaining a cache.
- * FIXME, for now it's a one-entry cache.
- * FIXME2, the "-993" is to reduce the chance of a hit on the first lookup.
- *
- * This is ifdef'd because on Suns, it drags in about 38K of "yellow
- * pages" code, roughly doubling the program size. Thanks guys.
- */
- void
- finduname(uname, uid)
- char uname[TUNMLEN];
- int uid;
- {
- struct passwd *pw;
- extern struct passwd *getpwuid ();
-
- if (uid != saveuid) {
- saveuid = uid;
- saveuname[0] = '\0';
- pw = getpwuid(uid);
- if (pw)
- strncpy(saveuname, pw->pw_name, TUNMLEN);
- }
- strncpy(uname, saveuname, TUNMLEN);
- }
-
- int
- finduid(uname)
- char uname[TUNMLEN];
- {
- struct passwd *pw;
- extern struct passwd *getpwnam();
-
- if (uname[0] != saveuname[0] /* Quick test w/o proc call */
- || 0!=strncmp(uname, saveuname, TUNMLEN)) {
- strncpy(saveuname, uname, TUNMLEN);
- pw = getpwnam(uname);
- if (pw) {
- saveuid = pw->pw_uid;
- } else {
- saveuid = myuid;
- }
- }
- return saveuid;
- }
-
-
- void
- findgname(gname, gid)
- char gname[TGNMLEN];
- int gid;
- {
- struct group *gr;
- extern struct group *getgrgid ();
-
- if (gid != savegid) {
- savegid = gid;
- savegname[0] = '\0';
- (void)setgrent();
- gr = getgrgid(gid);
- if (gr)
- strncpy(savegname, gr->gr_name, TGNMLEN);
- }
- (void) strncpy(gname, savegname, TGNMLEN);
- }
-
-
- int
- findgid(gname)
- char gname[TUNMLEN];
- {
- struct group *gr;
- extern struct group *getgrnam();
-
- if (gname[0] != savegname[0] /* Quick test w/o proc call */
- || 0!=strncmp(gname, savegname, TUNMLEN)) {
- strncpy(savegname, gname, TUNMLEN);
- gr = getgrnam(gname);
- if (gr) {
- savegid = gr->gr_gid;
- } else {
- savegid = mygid;
- }
- }
- return savegid;
- }
- #endif
- @@@ Fin de names.c
- echo port.c 1>&2
- cat >port.c <<'@@@ Fin de port.c'
- /*
- * @(#)port.c 1.6 86/08/11 Public Domain, by John Gilmore, 1986
- *
- * These are routines not available in all environments.
- *
- * I know this introduces an extra level of subroutine calls and is
- * slightly slower. Frankly, my dear, I don't give a damn. Let the
- * Missed-Em Vee losers suffer a little. This software is proud to
- * have been written on a BSD system.
- */
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <signal.h>
- #include <errno.h>
-
- #include "port.h"
-
- #ifndef BSD42
- /*
- * lstat() is a stat() which does not follow symbolic links.
- * If there are no symbolic links, just use stat().
- */
- int
- lstat (path, buf)
- char *path;
- struct stat *buf;
- {
- extern int stat ();
- return (stat (path, buf));
- }
-
- /*
- * valloc() does a malloc() on a page boundary. On some systems,
- * this can make large block I/O more efficient.
- */
- char *
- valloc (size)
- unsigned size;
- {
- extern char *malloc ();
- return (malloc (size));
- }
-
- /*
- ** NMKDIR.C
- **
- ** Written by Robert Rother, Mariah Corporation, August 1985.
- **
- ** I wrote this out of shear disgust with myself because I couldn't
- ** figure out how to do this in /bin/sh.
- **
- ** If you want it, it's yours. All I ask in return is that if you
- ** figure out how to do this in a Bourne Shell script you send me
- ** a copy.
- ** sdcsvax!rmr or rmr@uscd
- *
- * Severely hacked over by John Gilmore to make a 4.2BSD compatible
- * subroutine. 11Mar86; hoptoad!gnu
- */
-
- /*
- * Make a directory. Compatible with the mkdir() system call on 4.2BSD.
- */
- int
- mkdir(dpath, dmode)
- char *dpath;
- int dmode;
- {
- int cpid, status;
- extern int errno;
-
- switch (cpid = fork()) {
-
- case -1: /* Error in fork() */
- return(-1); /* Errno is set already */
-
- case 0: /* Child process */
- /*
- * Cheap hack to set mode of new directory. Since this
- * child process is going away anyway, we zap its umask.
- * FIXME, this won't suffice to set SUID, SGID, etc. on this
- * directory. Does anybody care?
- */
- status = umask(0); /* Get current umask */
- status = umask(status | (0777 & ~dmode)); /* Set for mkdir */
- execl("/bin/mkdir", "mkdir", dpath, (char *)0);
- _exit(-1); /* Can't exec /bin/mkdir */
-
- default: /* Parent process */
- while (cpid != wait(&status)) ; /* Wait for kid to finish */
- }
-
- if (TERM_SIGNAL(status) != 0 || TERM_VALUE(status) != 0) {
- errno = EIO; /* We don't know why, but */
- return -1; /* /bin/mkdir failed */
- }
-
- return 0;
- }
- #endif
-
- #ifdef USG
- /*
- * Translate V7 style into Sys V style.
- */
- #include <string.h>
- #include <memory.h>
-
- char *
- index (s, c)
- char *s;
- int c;
- {
- return (strchr (s, c));
- }
-
- char *
- bcopy (s1, s2, n)
- char *s1, *s2;
- int n;
- {
- (void) memcpy (s2, s1, n);
- return (s1);
- }
-
- void
- bzero (s1, n)
- char *s1;
- int n;
- {
- (void) memset(s1, 0, n);
- }
- #endif
- @@@ Fin de port.c
- exit 0
-
-