Category : C Source Code
Archive   : GTAR108S.ZIP
Filename : GNUTAR.TAR
but with added features. See tar.texinfo for details.
Please send bug reports, etc to [email protected] [email protected]
or [email protected]
hack
Gnutar-1.08/Makefile 600 10 5 14064 4704275043 7341 # Makefile for GNU tar program.
# @(#)Makefile 1.30 87/11/11
# In addition to setting DEFS appropriately for your system, you might
# have to hand edit the #defines and #undefs in port.c and rtape_lib.c.
# For Ultrix 3.1, you will have to compile rtape_lib.c with -DUSG
#
## GNU version
#DEFS = -DBSD42
#LOCAL_SRC =
#LOCAL_OBJ =
#LDFLAGS =
#LIBS = -lutils
#LINT = lint
#LINTFLAGS = -abchx
#DEF_AR_FILE = \"/dev/rmt8\"
#DEFBLOCKING = 20
#O = o
# Berserkeley version
#DEFS = -DBSD42
#LOCAL_SRC = getdate.y rtape_lib.c
#LOCAL_OBJ = getdate.$O rtape_lib.$O
#LDFLAGS =
#LIBS =
#LINT = lint
#LINTFLAGS = -abchx
#DEF_AR_FILE = \"/dev/rmt8\"
#DEFBLOCKING = 20
#O = o
# USG version
# Add -DNDIR to DEFS if your system used ndir.h instead of dirent.h
# Add -DDIRECT to DEFS if your system uses 'struct direct' instead of
# 'struct dirent' (this is the case at least with one add-on ndir
# library)
# Add -lndir to LIBS if your ndir routines aren't in libc.a
# Add -lPW to LIBS if you don't compile with gcc (to get alloca)
#DEFS = -DUSG #-DNDIR -DDIRECT
#LOCAL_SRC = getdate.y rtape_lib.c
#LOCAL_OBJ = getdate.$O rtape_lib.$O
#LDFLAGS =
#LIBS = #-lndir -lPW
#LINT = lint
#LINTFLAGS = -p
#DEF_AR_FILE = \"/dev/rmt8\"
#DEFBLOCKING = 20
#O = o
# COHERENT version
# Get the libndir routines if you don't already have libndir.a
# (Get them wherever you got this, or from simtel20, or from piggy.ucsb.edu)
DEFS = -DEMUL_OPEN3
LOCAL_SRC = getdate.y rtape_lib.c alloca.c
LOCAL_OBJ = getdate.$O rtape_lib.$O alloca.$O
LDFLAGS =
LIBS = -lndir
LINT = lint
LINTFLAGS = -p
DEF_AR_FILE = \"/dev/mt0\"
DEFBLOCKING = 20
O = o
# UniSoft's Uniplus SVR2 with NFS
#DEFS = -DUSG -DUNIPLUS -DNFS -DSVR2
#LOCAL_SRC = getdate.y rtape_lib.c
#LOCAL_OBJ = getdate.$O rtape_lib.$O
#LDFLAGS =
#LIBS = -lndir
#LINT = lint
#LINTFLAGS = -bx
#DEF_AR_FILE = \"/dev/rmt8\"
#DEFBLOCKING = 20
#O = o
# MASSCOMP version
#CC = ucb cc
#DEFS = -DBSD42
#LOCAL_SRC = getdate.y rtape_lib.c
#LOCAL_OBJ = getdate.$O rtape_lib.$O
#LDFLAGS =
#LIBS =
#LINT = lint
#LINTFLAGS = -bx
#DEF_AR_FILE = \"/dev/rmt0\"
#DEFBLOCKING = 20
#O = o
# (yuk) MS-DOS (Microsoft C 4.0) version
#MODEL = S
#DEFS = -DNONAMES -A$(MODEL) -DNO_REMOTE
#LOCAL_SRC = getdate.y rtape_lib.c
#LOCAL_OBJ = getdate.$O rtape_lib.$O
#LDFLAGS =
#LIBS = $(MODEL)dir.lib
#LINT = $(CC)
#LINTFLAGS = -W3
#DEF_AR_FILE = \"tar.out\"
#DEFBLOCKING = 20
#O = obj
# V7 version
# Pick open3 emulation or nonexistence. See open3.h, port.c.
##DEFS = -DV7 -DEMUL_OPEN3 -Dvoid=int
##DEFS = -DV7 -DNO_OPEN3 -Dvoid=int
#LOCAL_SRC = getdate.y rtape_lib.c
#LOCAL_OBJ = getdate.$O rtape_lib.$O
#LDFLAGS =
#LIBS = -lndir
#LINT = lint
#LINTFLAGS = -abchx
#DEF_AR_FILE = \"/dev/rmt8\"
#DEFBLOCKING = 20
#O = o
# Minix version
# No lint, so no lintflags. Default file is stdin/out. (Minix "tar"
# doesn't even take an "f" flag, it assumes argv[2] is the archive name!)
# Minix "make" doesn't expand macros right, so Minix users will have
# to expand CFLAGS, SRCS, O, etc by hand, or fix your make. Not my problem!
# You'll also need to come up with ctime(), the directory
# library, and a fixed doprintf() that handles %*s. Put this stuff in
# the "SUBSRC/SUBOBJ" macro below if you didn't put it in your C library.
# Note that Minix "cc" produces ".s" files, not .o's, so O = s has been set.
#
# Pick open3 emulation or nonexistence. See open3.h, port.c.
##DEFS = -DV7 -DMINIX -DEMUL_OPEN3
##DEFS = -DV7 -DMINIX -DNO_OPEN3
#LOCAL_SRC = getdate.y rtape_lib.c
#LOCAL_OBJ = getdate.$O rtape_lib.$O
#LDFLAGS =
#LIBS =
#DEF_AR_FILE = \"-\"
#DEFBLOCKING = 8 /* No good reason for this, change at will */
#O = s
# Xenix version
#DEFS = -DUSG -DXENIX
#LOCAL_SRC = getdate.y rtape_lib.c
#LOCAL_OBJ = getdate.$O rtape_lib.$O
#LDFLAGS =
#LIBS = -lx
#LINT = lint
#LINTFLAGS = -p
#DEF_AR_FILE = \"/dev/rmt8\"
#DEFBLOCKING = 20
#O = o
# SGI 4D version
#DEFS = -DUSG -I/usr/include/bsd
#LOCAL_SRC = getdate.y rtape_lib.c
#LOCAL_OBJ = getdate.$O rtape_lib.$O
#LDFLAGS =
#LIBS =
#LINT = lint
#LINTFLAGS = -p
#DEF_AR_FILE = \"/dev/tape\"
#DEFBLOCKING = 20
#O = o
#CC = gcc
#TARGET_ARCH =
CFLAGS = $(COPTS) $(ALLDEFS)
ALLDEFS = $(DEFS) \
-DDEF_AR_FILE=$(DEF_AR_FILE) \
-DDEFBLOCKING=$(DEFBLOCKING)
# next line for Debugging
#COPTS = -g
# next line for Production
COPTS = -O -VSUVAR
# Add things here like readdir that aren't in your standard libraries.
# (E.g. MSDOS needs msd_dir.c, msd_dir.obj)
SUBSRC=
SUBOBJ=
# Destination directory and installation program for make install
DESTDIR = /usr/local
INSTALL = cp
RM = rm -f
SRC1 = tar.c create.c extract.c buffer.c getoldopt.c update.c
SRC2 = version.c list.c names.c diffarch.c port.c wildmat.c getopt.c getopt1.c
SRC3 = $(LOCAL_SRC) $(SUBSRC)
SRCS = $(SRC1) $(SRC2) $(SRC3)
OBJ1 = tar.$O create.$O extract.$O buffer.$O getoldopt.$O list.$O update.$O
OBJ2 = version.$O names.$O diffarch.$O port.$O wildmat.$O getopt.$O getopt1.$O
OBJ3 = $(LOCAL_OBJ) $(SUBOBJ)
OBJS = $(OBJ1) $(OBJ2) $(OBJ3)
# AUX = README PORTING Makefile TODO tar.h port.h open3.h \
# msd_dir.h msd_dir.c
AUX = README COPYING ChangeLog Makefile tar.texinfo tar.h port.h open3.h \
rmt.h msd_dir.h msd_dir.c rtape_server.c rtape_lib.c getdate.y getopt.h
all: tar # rmt
echo Made all.
tar: $(OBJS)
$(CC) $(LDFLAGS) -o tar $(COPTS) $(OBJS) $(LIBS)
rmt: rtape_server.c
$(CC) $(CFLAGS) -o rmt rtape_server.c
# command is too long for Messy-Dos (128 char line length limit) so
# this kludge is used...
# @echo $(OBJ1) + > command
# @echo $(OBJ2) >> command
# link @command, $@,,$(LIBS) /NOI;
# @$(RM) command
install: all
$(RM) $(DESTDIR)/bin/tar
$(INSTALL) tar $(DESTDIR)/bin/tar
$(INSTALL) tar.texinfo $(DESTDIR)/man/tar.texinfo
$(INSTALL) rmt /etc/rmt
lint: $(SRCS)
$(LINT) $(LINTFLAGS) $(ALLDEFS) $(SRCS)
clean:
$(RM) errs $(OBJS) tar rmt
tar.shar.Z: $(SRCS) $(AUX)
shar $(SRCS) $(AUX) | compress > tar.shar.Z
dist: tar.tar tar.tar.Z
tar.tar: $(SRCS) $(AUX)
-rm -rf tar-dist
mkdir tar-dist
-ln $(AUX) $(SRCS) tar-dist
/bin/tar cf tar.tar tar-dist
tar.tar.Z: tar.tar
compress < tar.tar > tar.tar.Z
# /bin/tar cf - $(AUX) $(SRCS) | compress -v >tar.tar.Z
tar.zoo: $(SRCS) $(AUX)
zoo a tar $(AUX) $(SRCS)
$(OBJS): tar.h port.h
Gnutar-1.08/tar.texinfo 600 10 5 153361 4553200170 10100 \input texinfo @c -*-texinfo-*-
@setfilename tar-info
@settitle The @code{tar} Manual: DRAFT
@ifinfo
This file documents the tape archive of the GNU system.
Copyright (C) 1988 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
(this paragraph not being relevant to the printed manual).
@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.
@end ifinfo
@iftex
@finalout
@end iftex
@titlepage
@sp 11
@center @titlefont{tar}
@sp 1
@center The GNU tape archive
@sp 2
@center by Jay Fenlason
@sp 2
@center DRAFT!
@sp 1
@center @today
@page
@vskip 0pt plus 1filll
Copyright @copyright{} 1988 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
(this paragraph not being relevant to the printed manual).
@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 same conditions as for modified versions.
@end titlepage
@node Top, Preface, (dir), (dir)
@node Preface, Why, Top, Top
@unnumbered Preface
This manual describes the GNU tape archiver, @code{tar}, and how you
can use it to store copies of a file or a group of files in an
@dfn{archive}. This archive may be written directly to a magnetic
tape or other storage medium, stored as a file, or sent through a
pipe to another program. @code{tar} can also be used to add files
to an already existing archive, list the files in an archive, or
extract the files in the archive.
@sp 2
GNU @code{tar} was written by John Gilmore, and modified by many
people. The GNU enhancements were written by Jay Fenlason.
@ifinfo
@ichapter Using the Tape Archiver
You can use the GNU tape archiver, @code{tar}, to store copies of a file
or a group of files in an @dfn{archive}. This archive may be written
directly to a magnetic tape or other storage medium, stored as a file,
or sent through a pipe to another program. @code{tar} can also be used
to add files to an already existing archive, list the files in an
archive, or extract the files in the archive.
@menu
* Why:: What @code{tar} archives are good for.
* Commands:: How to tell @code{tar} what to do.
* Options:: Options that change the way @code{tar} behaves.
* FullDumps:: Using @code{tar} to perform full dumps.
* IncDumps:: Using @code{tar} to perform incremental dumps.
* Problems:: Common problems using @code{tar}.
* Rem Tape:: The remote tape server.
* Format:: The format of a @code{tar} archive.
@end menu
@end ifinfo
@node Why, Commands, Preface, Top
@chapter The Uses of Tape Archives
The tape archiver @code{tar} allows you to store many files in an
@dfn{archive file} or @dfn{tar file} which describes the names and contents
of the constituent files. Later you can extract some or all of these files
from the archive.
Tar files are not restricted to magnetic tapes. The @code{tar} program
can equally well use an ordinary file, or a pipe, or any device, as the
archive. But they were originally designed for use with magnetic tapes,
and that is how the name ``tar'' came about.
Archive files can be used for transporting a group of files from one system
to another: put all relevant files into an archive on one computer system,
transfer the archive to another, and extract the contents there. The basic
transfer medium might be magnetic tape, Internet FTP, or even electronic
mail (though you must encode the archive with @code{uuencode} in order to
transport it properly by mail). Both machines do not have to use the same
operating system, as long as they both support the @code{tar} program.
A magnetic tape can store several files in sequence, but has no names for
them, just relative position on the tape. A tar file or something like it
is the only way to store several files on one tape and retain their names.
Even when the basic transfer mechanism can keep track of names, as FTP can,
the nuisance of handling multiple files, directories, and multiple links,
may make a tar file a much easier method.
Archive files are also used for long-term storage, which you can think
of as transportation from one time to another.
Piping one @code{tar} to another is an easy way to copy a directory's
contents from one disk to another, while preserving the dates, modes, owners
and link-structure of all the files therein.
The GNU version of @code{tar} has special features that allow it to be
used to make incremental and full dumps of all the files in a
filesystem.
@node Commands, Options, Why, Top
@chapter The Different Operations @code{tar} Can Perform
One program, @code{tar}, is used to create an archive, to extract files
from an archive, to modify an archive, or to list the contents. Each
time you run @code{tar}, you must give a @dfn{command} to specify which
one of these things you want to do.
The command must always be in the first argument to @code{tar}. This
argument can also contain options (@pxref{Options}). For compatibility
with Unix @code{tar}, the first argument is always treated as containing
command and option letters even if it doesn't start with @samp{-}. Thus,
@samp{tar c} is equivalent to @samp{tar -c}: both of them specify the
@samp{-c} command to create an archive.
In addition, a set of long-named options are provided which can be used
instead of or inter-mixed with the single-letter flags. The long-named
options are meant to be easy to remember and logical, while the single
letter flags may not always be. Long-named options are preceded by a
@samp{+}. In the list of single-letter commands below, each
corresponding long-named option is listed next to it, in parentheses.
The remaining arguments to @code{tar} are either options, if they start
with @samp{-} or @samp{+}, or files to operate on.
The file names that you give as arguments are the files that @code{tar} will
act on -- for example, they are the files to put in the archive, or the files
to extract from it. If you don't give any file name arguments, the default
depends on which command you used. Some commands use all relevant files;
some commands have no default and will report an error if you don't specify
files.
If a file name argument actually names a directory, then that directory
and all files and subdirectories in it are used.
Here is a list of all the @code{tar} commands:
@table @samp
@item -c (+create)
The @samp{-c} command tells @code{tar} to create a new archive that
contains the file(s) specified on the command line. If you don't
specify files, all the files in the current directory are used.
If the archive file already exists, it is overwritten; the old contents
are lost.
@item -d (+compare or +diff)
The @samp{-d} command causes @code{tar} to compare the archive with
the files in the file system. It will report differences in file
size, mode, owner, and contents. If a file exists in the archive, but
not in the file system, @code{tar} will report this.
If you specify file names, those files are compared with the tape and
they must all exist in the archive. If you don't specify files, all
the files in the archive are compared.
@item -r (+append)
The @samp{-r} command causes @code{tar} to add the specified file(s)
to the end of the archive. This assumes that the archive file already
exists and is in the proper format (which probably means it was
created previously with the @code{tar} program). If the archive is
not in a format that @code{tar} understands, the results will be
unpredictable.
You must specify the files to be used; there is no default.
@item -t (+list)
The @samp{-t} command causes @code{tar} to display a list of the files
in the archive. If you specify file names, only the files
that you specify will be mentioned (but each of them is mentioned only
if it appears in the archive).
@item -u (+update)
The @samp{-u} command causes @code{tar} to add the specified files to
the end of the archive, like @w{@samp{-r}}, but only when a file doesn't
already exist in the archive or is newer than the version in the
archive (last-modification time is compared). This command can be
very slow.
You must specify the files to be used; there is no default.
@item -x (+extract or +get)
The @samp{-x} command causes @code{tar} to extract the specified files
from the archive. If no file names are given, all the files in the
archive will be extracted.
@item -A (+catenate or +concatenate)
The @samp{-A} command is used for concatenating several archive files
into one big archive file. The files to operate on should all be
archive files. They are all appended to the end of @emph{the} archive
file which @code{tar} works on. (The other files are not changed).
You might be tempted to use @code{cat} for this, but it won't
ordinarily work. A @code{tar} archive contains data which indicates
the end of the archive, so more material added to the end with
@code{cat} would be ignored. The @samp{tar -A} command works because
it removes the end-of-archive markers from the middle of the result.
@item -D (+delete)
The @samp{-D} command causes @code{tar} to delete the specified files
from the archive. This command is extremely slow. Warning: Use of
this command on archives stored on magnetic tape may result in a
scrambled archive. There is no safe way (except for completely
re-writing the archive) to delete files from a magnetic tape.
@end table
@node Options, FullDumps, Commands, Top
@chapter Options That Change How @code{tar} Works
Options may be specified as individual arguments starting with @samp{-}.
In this case, if the option wants an argument (as does, for example,
@samp{-f}) then the argument should come after the option, separated
from it by a space.
All options are optional. Some options make sense with any command, while
others are meaningful only with particular commands.@refill
@menu
* General Options:: Options that are always meaningful.
* Creation Options:: Options for creating or updating an archive.
* Extraction Options:: Options for listing or extracting files.
* Option Syntax:: Old syntax for options
@end menu
@node General Options, Creation Options, , Options
@section Options That Are Always Meaningful
@table @code
@item -b (+block-size) @var{number}
This option is used to specify a @dfn{blocking factor} for the
archive. When reading or writing the archive, @code{tar}, will do
reads and writes of the archive in blocks of @var{number}*512 bytes.
The default blocking factor is set when @code{tar} is compiled, and is
typically 20.
Blocking factors larger than 20 cannot be read by very old versions of
@code{tar}, or by some newer versions of @code{tar} running on old machines
with small address spaces.
With a magnetic tape, larger blocks give faster throughput and fit
more data on a tape (because there are fewer inter-record gaps). If
the archive is in a disk file or a pipe, you may want to specify a
smaller blocking factor, since a large one will result in a large
number of null bytes at the end of the archive.
When writing cartridge or other streaming tapes, a much larger
blocking factor (say 126 or more) will greatly increase performance.
However, you must specify the same blocking factor when reading or
updating the archive.
With GNU @code{tar} the blocking factor is limited only by the maximum
block size of the device containing the archive, or by the amount of
available virtual memory.
@item -f (+file) @var{filename}
This option is used to specify the file name of the archive @code{tar}
works on.
If no @samp{-f} option is given, but the environment variable
@code{TAPE} exists, its value is used; otherwise, a default archive
name (which was picked when @code{tar} was compiled) is used. The
default is normally set up to be the ``first'' tape drive or other
transportable I/O medium on the system.
If the filename is @samp{-}, @code{tar} reads the archive from
standard input (when listing or extracting), or writes it to standard
output (when creating). If the @samp{-} filename is given when
updating an archive, @code{tar} will read the original archive from
its standard input, and will write the entire new archive to its
standard output.
If the filename contains @samp{:/dev/}, it is interpreted as
@samp{hostname:filename}. If the @var{hostname} contains an ``at'' sign
(@samp{@@}), it is treated as @samp{user@@hostname:filename}.
In either case, @code{tar} will invoke the command @code{rsh}
(or @code{remsh}) to start
up an @code{/etc/rmt} on the remote machine. If you give an alternate login
name, it will be given to the @code{rsh}. Naturally, the remote machine must
have a copy of @file{/etc/rmt}. @code{/etc/rmt} is free software
from the University of California, and a copy of the source code can be found
with the sources for @code{tar}. @code{/etc/rmt} will have to be modified to
run on non-BSD4.3 systems.@refill
@item -C (+directory) @var{dir}
The @samp{-C} option causes @code{tar} to change into the
directory @var{dir} before continuing. This option is usually
interspersed with the files @code{tar} is to work on. For example,
@example
tar -c iggy ziggy -C baz melvin
@end example
@noindent
will place the files @file{iggy} and @file{ziggy} from the current
directory on the tape, followed by the file @file{melvin} from the
directory @file{baz}. This option is especially useful when you have
several widely separated files that you want to store in the same
directory in the archive.
Note that the file @file{melvin} is recorded in the archive under the
precise name @file{melvin}, @emph{not} @file{baz/melvin}. Thus, the
archive will contain three files that all appear to have come from the
same directory; if the archive is extracted with plain @samp{tar -x},
all three files will be created in the current directory.
Contrast this with the command
@example
tar -c iggy ziggy bar/melvin
@end example
@noindent
which records the third file in the archive under the name @file{bar/melvin}
so that, if plain @samp{tar -x} is used, the third file will be created
in a subdirectory named @file{bar}.
@item -M (+multi-volume)
The @samp{-M} option causes @code{tar} to write a @dfn{multi-volume}
archive--one that may be larger than will fit on the medium used to
hold it.
When this option is used, @code{tar} will not abort when it cannot
read or write any more data. Instead, it will ask you to prepare a
new volume. If the archive is on a magnetic tape, you should change
tapes now; if the archive is on a floppy disk, you should change
disks, etc.
Each volume of a multi-volume archive is an independent tar archive,
complete in itself. For example, you can list or extract any volume
alone (just don't specify @samp{-M}). However, if one file in the
archive is split across volumes, the only way to extract it
successfully is with a multi-volume extract command (@samp{-xM})
starting on or before the volume where the file begins.
@item -N (+after-date) @var{date}
This option causes @code{tar} to only work on files whose modification
or inode-changed times are newer than the @var{date} given. The main
use is for creating an archive; then only new files are written. If
extracting, only newer files are extracted.
Remember that the entire date argument should be quoted if it contains
any spaces.
The date is parsed using @code{getdate}.
@item -R (+record-number)
If @samp{-R} is used, @code{tar} prints, along with every message it
would normally produce, the record number within the archive where
the message occurred. This option is especially useful when reading
damaged archives, since it helps pinpoint the damaged sections.
This can also be useful when making a log of a file-system backup tape,
since the results allow you to find the file you want to retrieve
on several backup tapes and choose the tape where the file appears
earliest (closest to the front of the tape).
@item -T (+files-from) @var{filename}
Instead of taking the list of files to work on from the command
line, the list of files to work on is read from the file
@var{filename}. If @var{filename} is given as @samp{-}, the list is
read from standard input. Note that using both @samp{-T -} and
@samp{-f -} will not work unless you are using the @samp{-c} command.
@item -v (+verbose)
This option causes @code{tar} to be verbose about the actions it is
taking.
Normally, the @samp{-t} command to list an archive prints
just the file names (one per line) and the other commands are silent.
@samp{-tv} prints a full line of information about each file, like the
output of @samp{ls -l}. @samp{-v} with any other command (aside from
@samp{-t}) prints just the name of each file operated on.
The output from @samp{-v} appears on the standard output except when
creating or updating an archive to the standard output,
in which case the output from @samp{-v} is sent to the standard
error.
@item +version
This option causes @code{tar} to print out its version number to the
standard error. It has no equivalent short option name.
@item -w (+interactive)
This option causes @code{tar} to print a message for each action it
intends to take, and ask for confirmation on the terminal. To
confirm, you must type a line of input. If your input line begins
with @samp{y}, the action is performed, otherwise it is skipped.
The actions which require confirmation include adding a file to the
archive, extracting a file from the archive, deleting a file from the
archive, and deleting a file from disk.
If @code{tar} is reading the archive from the standard input,
@code{tar} will open the file @file{/dev/tty} to ask for
confirmation on.
@item -X (+exclude) @var{file} This option causes @code{tar} to
read a list of filename regular expressions, one per line, from
the file @var{file}; @code{tar} will ignore files with those
names. Thus if @code{tar} is called as @samp{tar -c -X foo .}
and the file @file{foo} contains @samp{*.o} none of the files
whose names end in @file{.o} in the current directory (or any
subdirectory of the current directory) will be added to the
archive. Multiple @code{-X} options may be given.
@item -z
@itemx -Z (+compress or +uncompress)
The archive should be compressed as it is written, or decompressed
as it is read, using the @code{compress} program. This option works
on physical devices (tape drives, etc.) and remote files as well as
on normal files; data to or from such devices or remote files is
reblocked by another copy of the @code{tar} program to enforce the
specified (or default) block size. The default compression
parameters are used; if you need to override them, avoid the
@samp{-z} option and run @code{compress} explicitly.
If the @samp{-z} option is given twice, or the @samp{+compress-block} option
is used, @code{tar} will pad the archive out to the next block boundry
(@pxref{General Options}). This may be useful with some devices that
require that all write operations be a multiple of a certain size.
Note that the @samp{-z} option will not work with the @samp{-M} option,
or with the @samp{-u}, @samp{-r}, @samp{-A}, or @samp{-D} commands.
@end table
@node Creation Options, Extraction Options, General Options, Options
@section Options for Creating Or Updating an Archive
These options are used to control which files @code{tar} puts in an
archive, or to control the format the archive is written in (@pxref{Format}).
Except as noted below, these options are useful with the @samp{-c},
@samp{-r}, @samp{-u}, @samp{-A}, and @samp{-D} commands.
Also note that the @samp{-B} option, (@pxref{Extraction Options}),
is also useful with the @samp{-r}, @samp{-u}, @samp{-A}, and @samp{-D} commands.
@table @code
@c this command no longer exists -D is now the old -J command
@c @item -D
@c The @samp{-D} option tells @code{tar} to only store entries for the
@c directories it encounters, and to not to store the files inside the
@c directories. In conjunction with @code{find} this is useful for
@c creating incremental dumps for archival backups, similar to those
@c produced by @code{dump}.
@item -G (+incremental)
This option should only be used when creating an incremental backup of
a filesystem. When the @samp{-G} option is used, @code{tar} writes, at
the beginning of the archive, an entry for each of the directories that
will be operated on. The entry for a directory includes a list of all
the files in the directory at the time the dump was done, and a flag
for each file indicating whether the file is going to be put in the
archive. This information is used when doing a complete incremental
restore.
Note that this option causes @code{tar} to create a non-standard
archive that may not be readable by non-GNU versions of the @code{tar}
program.
@item -h (+dereference)
If @samp{-h} is used, when @code{tar} encounters a symbolic link, it
will archive the linked-to file, instead of simply recording the
presence of a symbolic link. If the linked-to file is archived
again, an entire second copy of it will be archived, instead of a
link. This could be considered a bug.
@item -l (+one-file-system)
This option causes @code{tar} to not cross filesystem boundaries
when archiving parts of a directory tree. This option only
affects files that are archived because they are in a directory that
is archived; files named on the command line are archived
regardless, and they can be from various file systems.
This option is useful for making full or incremental archival backups of
a file system, as with the Unix @code{dump} command.
Files which are skipped due to this option are mentioned on the
standard error.
@item -o (+old-archive or +old or +portability)
This option causes @code{tar} to write an old format archive, which
does not include information about directories, pipes, fifos,
contiguous files, or device files, and specifies file ownership by
numeric user- and group-ids rather than by user and group names. In
most cases, a @emph{new} format archive can be read by an @emph{old}
@code{tar} program without serious trouble, so this option should
seldom be needed. When updating an archive, do not use @samp{-o}
unless the archive was created with the @samp{-o} option.
@item -S (+sparse-file)
This option causes all files to be put in the archive to be tested for
sparseness, and handled specially if they are. The @item{-S} option
is useful when many dbm files, for example, are being backed up, and
running out of space on the tape is an issue. Using this option
dramatically decreases the amount of space needed to store such a file.
In later versions, this option may be removed, and the testing and
treatment of sparse files may be done automatically with any special
GNU options. For now, it is an option needing to be specified on the
command line with the creation or updating of an archive.
@item -V (+volume) @var{name}
This option causes @code{tar} to write out a @dfn{volume header} at
the beginning of the archive. If @samp{-M} is used, each volume of
the archive will have a volume header of @samp{@var{name} Volume @var{N}},
where @var{N} is 1 for the first volume, 2 for the next, and so on.
@item -W (+verify)
This option causes @code{tar} to verify the archive after writing it.
Each volume is checked after it is written, and any discrepancies are
recorded on the standard error output.
Verification requires that the archive be on a back-space-able medium.
This means pipes, some cartridge tape drives, and some other devices
cannot be verified.
@end table
@node Extraction Options, Option Syntax, Creation Options, Options
@section Options for Listing Or Extracting Files
The options in this section are meaningful with the @samp{-x} command.
Unless otherwise stated, they are also meaningful with the @samp{-t}
command.
@table @code
@item -B (+read-full-blocks)
If @samp{-B} is used, @code{tar} will not panic if an attempt to
read a block from the archive does not return a full block. Instead,
@code{tar} will keep reading until it has obtained a full block.
This option is turned on by default when @code{tar} is reading an
archive from standard input, or from a remote machine. This is
because on BSD Unix systems, a read of a pipe will return however much
happens to be in the pipe, even if it is less than @code{tar}
requested. If this option was not used, @code{tar} would fail
as soon as it read an incomplete block from the pipe.
This option is also useful with the commands for updating an archive.
@item -G (+incremental)
The @samp{-G} option means the archive is an incremental backup.
Its meaning depends on the command that it modifies.
If the @samp{-G} option is used with @samp{-t}, @code{tar} will
list, for each directory in the archive, the list of files in that
directory at the time the archive was created. This information is
put out in a format that is not easy for humans to read, but which
is unambiguous for a program: each filename is preceded by either a
@samp{Y} if the file is present in the archive, an @samp{N} if the
file is not included in the archive, or a @samp{D} if the file is a
directory (and is included in the archive). Each filename is
terminated by a null character. The last file is followed by an
additional null and a newline to indicate the end of the data.
If the @samp{-G} option is used with @samp{-x}, then when the entry
for a directory is found, all files that currently exist in that directory
but are not listed in the archive @emph{are deleted from the directory}.
This behavior is convenient when you are restoring a damaged file system
from a succession of incremental backups: it restores the entire state
of the file system to that which obtained when the backup was made.
If you don't use @samp{-G}, the file system will probably fill up
with files that shouldn't exist any more.
@item -i (+ignore-zeros)
The @samp{-i} option causes @code{tar} to ignore blocks of zeros in the
archive. Normally a block of zeros indicates the end of the
archive, but when reading a damaged archive, or one which was created by
@code{cat}-ing several archives together, this option allows
@code{tar} to read the entire archive. This option is not on by
default because many versions of @code{tar} write garbage after the
zeroed blocks.
Note that this option causes @code{tar} to read to the end of the
archive file, which may sometimes avoid problems when multiple files
are stored on a single physical tape.
@item -k (+keep-old-files)
The @samp{-k} option prevents @code{tar} from over-writing existing
files with files with the same name from the archive.
The @samp{-k} option is meaningless with @samp{-t}.
@item -K (+starting-file) @var{filename}
The @samp{-K} option causes @code{tar} to begin extracting or listing
the archive with the file @var{filename}, and to consider only the
files starting at that point in the archive. This is useful if a
previous attempt to extract files failed when it reached
@var{filename} due to lack of free space. (This assumes, of course,
that there is now free space, or that you are now extracting into a
different file system.)
@item -m (+modification-time)
When this option is used, @code{tar} leaves the modification times of
the files it extracts as the time when the files were extracted,
instead of setting it to the time recorded in the archive.
The @samp{-m} option is meaningless with @samp{-t}.
@item -O (+to-stdout)
When this option is used, instead of creating the files
specified, @code{tar} writes the contents of the files
extracted to its standard output. This may be useful if you
are only extracting the files in order to send them through a
pipe.
The @samp{-O} option is meaningless with @samp{-t}.
@item -p (+same-permissions or +preserve-permissions)
This option causes @code{tar} to set the modes (access permissions) of
extracted files exactly as recorded in the archive. If this option is
not used, the current @code{umask} setting limits the permissions on
extracted files.
The @samp{-p} option is meaningless with @samp{-t}.
@item -P (+absolute-paths) This option should be used when the absolute
pathname of a file should be preserved in the archive. @code{tar}
normally strips the leading `/' from the name of the file, thus making
/usr/foo/bar/baz into usr/foo/bar/baz. Using the @samp{-P} option keeps
the pathname intact, and is useful in that it is not necessary to change
to the root directory when extracting files.
@item -s (+same-order or +preserve-order)
The @samp{-s} option tells @code{tar} that the list of filenames to be
listed or extracted is sorted in the same order as the files in the
archive. This allows a large list of names to be used, even on a
small machine that would not otherwise be able to hold all the names
in memory at the same time. Such a sorted list can easily be created
by running @samp{tar -t} on the archive and editing its output.
@samp{-s} is probably never needed on modern computer systems.
@item +preserve
The @samp{+preserve} option has no equivalent short option name.
It is equivalent to @samp{-p} plus @samp{-s}.
@end table
@node Option Syntax, , Extraction Options, Options
@section Old Syntax for Options
For compatibility with Unix @code{tar}, the first argument can contain
option letters in addition to the command letter; for example, @samp{tar
cv} specifies the option @samp{-v} in addition to the command @samp{-c}.
The first argument to GNU @code{tar} is always treated as command and
option letters even if it doesn't start with @samp{-}.
Some options need their own arguments; for example, @samp{-f} is followed
by the name of the archive file. When the option is given separately, its
argument follows it, as is usual for Unix programs. For example:
@example
tar -c -v -b 20 -f /dev/rmt0
@end example
When options that need arguments are given together with the command, all
the associated arguments follow, in the same order as the options. Thus,
the example above could also be written in the old style as follows:
@example
tar cvbf 20 /dev/rmt0
@end example
@noindent
Here @samp{20} is the argument of @samp{-b} and @file{/dev/rmt0} is the
argument of @samp{-f}.
The long-named options can be used instead of the single-letter flags.
They are meant to be obvious and easy to remember, possibly more so than
their corresponding single-letter options. The above example using
long-named options would look like this:
@example
tar +create +verbose +block-size +file 20 /dev/rmt0
@end example
@node FullDumps, IncDumps, Options, Top
@chapter Using @code{tar} to Perform Full Dumps
Full dumps should only be made when no other people or programs are
modifying files in the filesystem. If files are modified while
@code{tar} is making the backup, they may not be stored properly in
the archive, in which case you won't be able to restore them if you
have to.
You will want to use the @samp{-V} option to give the archive a
volume label, so you can tell what this archive is even if the label
falls off the tape, or anything like that.
Unless the filesystem you are dumping is guaranteed to fit on one
volume, you will need to use the @samp{-M} option. Make sure you
have enough tapes on hand to complete the backup.
If you want to dump each filesystem separately you will need to use
the @samp{-l} option to prevent @code{tar} from crossing filesystem
boundaries when storing (sub)directories.
The @samp{-G} option is not needed, since this is a complete copy of
everything in the filesystem, and a full restore from this backup
would only be done onto a completely empty disk.
Unless you are in a hurry, and trust the @code{tar} program (and
your tapes), it is a good idea to use the @code{-W} (verify) option,
to make sure your files really made it onto the dump properly. This
will also detect cases where the file was modified while (or just
after) it was being archived.
@node IncDumps, Problems, FullDumps, Top
@chapter Using @code{tar} to Perform Incremental Dumps
Performing incremental dumps is similar to performing full dumps,
although a few more options will usually be needed.
You will need to use the @samp{-N @var{date}} option to tell @code{tar} to
only store files that have been modified since @var{date}.
@var{date} should be the date and time of the last full/incremental
dump.
A standard scheme is to do a @samp{monthly} (full) dump once a month,
a @samp{weekly} dump once a week of everything since the last monthly and
a @samp{daily} every day of everything since the last (weekly or monthly)
dump.
Here is a copy of the script used to dump the filesystems of the
machines here at the Free Software Foundation. This script is run
(semi-)automatically late at night when people are least likely to
be using the machines. This script dumps several filesystems from
several machines at once (by using a network-filesystem). The
operator is responsible for ensuring that all the machines will be
up at the time the dump happens. If a machine is not running, its
files will not be dumped, and the next day's incremental dump will
@emph{not} store files that would have gone onto that dump.
@example
#!/bin/csh
# Dump thingie
set now = `date`
set then = `cat date.nfs.dump`
/u/hack/bin/tar -c -G -v\
-f /dev/rtu20\
-b 126\
-N "$then"\
-V "Dump from $then to $now"\
/alpha-bits/gp\
/gnu/hack\
/hobbes/u\
/spiff/u\
/sugar-bombs/u
echo $now > date.nfs.dump
mt -f /dev/rtu20 rew
@end example
Output from this script is stored in a file, for the operator to
read later.
This script uses the file @file{date.nfs.dump} to store the date/time of
the last dump.
Since this is a streaming tape drive, no attempt to verify the
archive is done. This is also why the high blocking factor (126) is
used. The tape drive must also be rewound by the @code{mt} command
after the dump is made.
@node Problems, Rem Tape, IncDumps, Top
@chapter Common Problems Using @code{tar}
Unless you use the @code{-P} option, GNU @code{tar} will not allow
you to create an archive that contains absolute pathnames. (An
absolute pathname is one that begins with a @samp{/}.) If you try,
@code{tar} will automatically remove the leading @samp{/} from the
file names it stores in the archive. It will also type a warning
message telling you what it is doing.
When reading an archive that was created with a different @code{tar}
program, GNU @code{tar} automatically extracts entries in the
archive which have absolute pathnames as if the pathnames were not
absolute. If the archive contained a file @samp{/usr/bin/computoy},
GNU @code{tar} would extract the file to @samp{usr/bin/computoy} in
the current directory. If you want to extract the files in an
archive to the same absolute names that they had when the archive
was created, you should do a @samp{cd /} before extracting the files
from the archive, or you should either use the @samp{-P} option, or
use the command @samp{tar -C / @dots{}}.
Some versions of UNIX, (Ultrix 3.1 is know to have this problem) can
claim that a short write near the end of a tape succeeded, when it
actually failed. This will result in the -M option not working
correctly. The best workaround at the moment is to use a
significantly larger blocksize than the default 20.
In order to update an archive, @code{tar} must be able to backspace
the archive in order to re-read or re-write a block that was just read
(or written). This is currently possible only on two kinds of
files: normal disk files (or any other file that can be
backspaced with @code{lseek()}), and industry-standard 9-track magnetic
tape (or any other kind of tape that can be backspaced with
@code{ioctl(@dots{},MTIOCTOP,@dots{})}).
This means that the @samp{-r}, @samp{-u}, @samp{-A}, and @samp{-D}
commands will not work on any other kind of file. Some media simply
cannot be backspaced, which means these commands and options will
never be able to work on them. These non-backspacing media include
pipes and cartridge tape drives.
Some other media can be backspaced, and @code{tar} will work on them
once @code{tar} is modified to do so.
Archives created with the @samp{-M}, @samp{-V}, and @samp{-G}
options may not be readable by other version of @code{tar}. In particular,
restoring a file that was split over a volume boundary will require
some careful work with @code{dd}, if it can be done at all. Other versions
of @code{tar} may also create an empty file whose name is that of
the volume header. Some versions of @code{tar} may create normal
files instead of directories archived with the @samp{-G} option.
@node Rem Tape, Format, Problems, Top
@chapter The Remote Tape Server
In order to access the tape drive on a remote machine, @code{tar}
uses the remote tape server written at the University of California
at Berkeley. The remote tape server must be installed as
@file{/etc/rmt} on any machine whose tape drive you want to use.
@code{tar} calls @file{/etc/rmt} by running an @code{rsh} or
@code{remsh} to the remote machine, optionally using a different
login name if one is supplied.
A copy of the source for the remote tape server is provided. It is
Copyright @copyright{} 1983 by the Regents of the University of California, but
can be freely distributed. Instructions for compiling and
installing it are included in the @file{Makefile}.
The remote tape server may need to be modified in order to run on a
non-4.3BSD system.
@node Format, , Rem Tape, Top
@chapter The Format of a @code{tar} Archive
This chapter is based heavily on John Gilmore's @i{tar}(5) manual page
for the public domain @code{tar} that GNU @code{tar} is based on.
@section The Standard Format
A @dfn{tar tape} or file contains a series of records. Each record
contains @code{RECORDSIZE} bytes. 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 marker. A reasonable
system should write a record of zeros at the end, but must not
assume that such a record exists when reading an archive.
The records may be @dfn{blocked} for physical I/O operations. Each
block of @var{N} records (where @var{N} is set by the @samp{-b}
option to @code{tar}) is written with a single @code{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 should be written at the full size, with records after the
zero record containing all zeroes. When reading an archive, a
reasonable system should properly handle an archive whose last block
is shorter than the rest, or which contains garbage records after a
zero record.
The header record is defined in C as follows:
@example
/*
* Standard Archive Format - Standard TAR - USTAR
*/
#define RECORDSIZE 512
#define NAMSIZ 100
#define TUNMLEN 32
#define TGNMLEN 32
#define SPARSE_EXT_HDR 21
#define SPARSE_IN_HDR 4
struct sparse {
char offset[12];
char numbytes[12];
};
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];
/* these following fields were added by JF for gnu */
/* and are NOT standard */
char atime[12];
char ctime[12];
char offset[12];
char longnames[4];
/* the next three fields were added by JK to deal with
shrinking down sparse files */
struct sparse sp[SPARSE_IN_HDR];
char isextended;
char ending_blanks[12]; /* number of nulls at the
end of the file, if any */
@} header;
struct extended_header @{
struct sparse sp[21];
char isextended;
@} ext_hdr;
@};
/* 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 magic field is filled with this if this is a GNU format dump entry */
#define GNUMAGIC "GNUtar " /* 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 which were defined later. */
#define LF_DUMPDIR 'D' /* This is a dir entry that contains
the names of files that were in
the dir at the time the dump
was made */
#define LF_MULTIVOL 'M' /* This is the continuation
of a file that began on another
volume */
#define LF_SPARSE 'S' /* This is for sparse files */
#define LF_VOLHDR 'V' /* This file is a tape/volume header */
/* Ignore it on extraction */
/* 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 */
@end example
All characters in header records are represented by 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 in any character set. The @code{tar} format
does not distinguish text files from binary files, and no
translation of file contents is performed.
The @code{name}, @code{linkname}, @code{magic}, @code{uname}, and
@code{gname} are null-terminated character strings. All other
fileds are zero-filled octal numbers in ASCII. Each numeric field
of width @var{w} contains @var{w} minus 2 digits, a space, and a null,
except @code{size}, and @code{mtime}, which do not contain the
trailing null.
The @code{name} field is the pathname of the file, with directory
names (if any) preceding the file name, separated by slashes.
The @code{mode} field provides nine bits specifying file permissions
and three bits to specify the Set UID, Set GID, and Save Text
(``stick'') 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 or updating an archive; e.g. the
group permission could be copied from the @code{other} permission.
The @code{uid} and @code{gid} fields are the numeric user and group
ID of the file owners, respectively. If the operating system does
not support numeric user or group IDs, these fields should be
ignored.
The @code{size} field is the size of the file in bytes; linked files
are archived with this field specified as zero.
@xref{Extraction Options}; in particular the @samp{-G} option.@refill
The @code{mtime} 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 an
integer number of seconds since January 1, 1970, 00:00 Coordinated
Universal Time.
The @code{chksum} field is the ASCII representation of the octal
value of the simple sum of all bytes in the header record. Each
8-bit byte in the header is added to an unsigned integer,
initialized to zero, the precision of which shall be no less than
seventeen bits. When calculating the checksum, the @code{chksum}
field is treated as if it were all blanks.
The @code{typeflag} 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, @code{tar} issues a warning to the standard
error.
The @code{atime} and @code{ctime} fields are used in making incremental
backups; they store, respectively, the particular file's access time and
last inode-change time.
The @code{offset} is used by the -M option, when making a multi-volume
archive. The offset is number of bytes into the file that we need to
restart at to continue the file on the next tape, i.e., where we store
the location that a continued file is continued at.
The @code{longnames} field of the header belongs with something that is
not yet implemented in tar, and is therefore empty.
The following fields were added to deal with sparse files. A file is
@dfn{sparse} if it takes in unallocated blocks which end up being
represented as zeros, i.e., no useful data. A test to see if a file is
sparse is to look at the number blocks allocated for it versus the number of
characters in the file; if there are fewer blocks allocated for the file
than would normally be allocated for a file of that size, then the file is
sparse. This is the method tar uses to detect a sparse file, and once such
a file is detected, it is treated differently from non-sparse files.
Sparse files are often dbm files, or other database-type files which have
data at some points and emptiness in the greater part of the file. Such
files can appear to be very large when an @code{ls -l} is done on them,
when in truth, there may be a very small amount of important data
contained in the file. It is thus undesirable to have tar think that it
must back up this entire file, as great quantities of room are wasted on
empty blocks, which can lead to running out of room on a tape far
earlier than is necessary. Thus, sparse files are dealt with so that
these empty blocks are not written to the tape. Instead, what is
written to the tape is a description, of sorts, of the sparse file: where
the holes are, how big the holes are, and how much data is found at the
end of the hole. This way, the file takes up potentially far less room
on the tape, and when the file is extracted later on, it will look
exactly the way it looked beforehand. The following is a description of
the fields used to handle a sparse file:
The @code{sp} is an array of @code{struct sparse}. Each @code{struct
sparse} contains two 12-character strings which represent an offset into
the file and a number of bytes to be written at that offset. The offset
is absolute, and not relative to the offset in preceding array element.
The header can hold four of these @code{struct sparse} at the moment; if
more are needed, they are not stored in the header.
The @code{isextended} flag is set when an @code{extended_header} is
needed to deal with a file. Note that this means that this flag can only
be set when dealing with a sparse file, and it is only set in the event
that the description of the file will not fit in the alloted room for
sparse structures in the header. In other words, an extended_header is
needed.
The @code{extended_header} structure is used for sparse files which need
more sparse structures than can fit in the header. The header can fit
4 such structures; if more are needed, the flag @code{isextended} gets set
and the next record is an @code{extended_header}.
Each @code{extended_header} structure contains an array of 21 sparse
structures, along with a similar @code{isextended} flag that the header
had. There can be an indeterminate number of such @code{extended_header}s
to describe a sparse file.
@table @code
@item LF_NORMAL
@itemx LF_OLDNORMAL
These flags represent a regular file. In order to be compatible with
older versions of @code{tar}, a @code{typeflag} value of
@code{LF_OLDNORMAL} should be silently recognized as a regular
file. New archives should be created using @code{LF_NORMAL}. Also,
for backward compatibility, @code{tar} treats a regular file whose
name ends with a slash as a directory.
@item LF_LINK
This flag 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 @code{linkname} field with a trailing null.
@item LF_SYMLINK
This represents a symbolic link to another file. The linked-to
name is specified in the @code{linkname} field with a trailing null.
@item LF_CHR
@itemx LF_BLK
These represent character special files and block special files
respectively. In this case the @code{devmajor} and @code{devminor}
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.
@item LF_DIR
This flag specifies a directory or sub-directory. The directory name in
the @code{name} field should end with a slash. On systems where
disk allocation is performed on a directory basis, the @code{size}
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 @code{size} field of zero indicates no such limiting.
Systems which do not support limiting in this manner should ignore
the @code{size} field.
@item LF_FIFO
This specifies a FIFO special file. Note that the archiving of a
FIFO file archives the existence of this file and not its contents.
@item LF_CONTIG
This 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.
@item 'A' @dots{}
@itemx 'Z'
These are reserved for custom implementations. Some of these are
used in the GNU modified format, as described below.
@end table
Other values are reserved for specification in future revisions of
the P1003 standard, and should not be used by any @code{tar} program.
The @code{magic} field indicates that this archive was output in the
P1003 archive format. If this field contains @code{TMAGIC}, the
@code{uname} and @code{gname} 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 within the @code{uid} and @code{gid} fields.
@section GNU Extensions to the Archive Format
The GNU format uses additional file types to describe new types of
files in an archive. These are listed below.
@table @code
@item LF_DUMPDIR
@itemx 'D'
This represents a directory and a list of files created by the
@samp{-G} option. The @code{size} field gives the total size of the
associated list of files. Each filename is preceded by either a @code{'Y'}
(the file should be in this archive) or an @code{'N'} (The file is a
directory, or is not stored in the archive). Each filename is
terminated by a null. There is an additional null after the last
filename.
@item LF_MULTIVOL
@itemx 'M'
This represents a file continued from another volume of a
multi-volume archive created with the @samp{-M} option. The original
type of the file is not given here. The @code{size} field gives the
maximum size of this piece of the file (assuming the volume does not
end before the file is written out). The @code{offset} field gives
the offset from the beginning of the file where this part of the
file begins. Thus @code{size} plus @code{offset} should equal the
original size of the file.
@item LF_SPARSE
@itemx 'S'
This flag indicates that we are dealing with a sparse file. Note that
archiving a sparse file requires special operations to find holes in the
file, which mark the positions of these holes, along with the number of
bytes of data to be found after the hole.
@item LF_VOLHDR
@itemx 'V'
This file type is used to mark the volume header that was given with
the @samp{-V} option when the archive was created. The @code{name}
field contains the @code{name} given after the @samp{-V} option.
The @code{size} field is zero. Only the first file in each volume
of an archive should have this type.
@end table
You may have trouble reading a GNU format archive on a non-GNU system if
the options @samp{-G}, @samp{-M}, @samp{-S}, or @samp{-V} were used when
writing the archive. In general, if tar does not use the GNU-added
fields of the header, other versions of tar should be able to read the
archive. Otherwise, the tar program will give an error, the most likely
one being a checksum error.
@unnumbered Concept Index
@printindex cp
@setchapternewpage odd
@contents
@bye
Gnutar-1.08/tar.h 600 10 5 21053 4676440270 6640 /*
Copyright (C) 1988 Free Software Foundation
GNU tar is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY. No author or distributor accepts responsibility to anyone
for the consequences of using it or for whether it serves any
particular purpose or works at all, unless he says so in writing.
Refer to the GNU tar General Public License for full details.
Everyone is granted permission to copy, modify and redistribute GNU tar,
but only under the conditions described in the GNU tar General Public
License. A copy of this license is supposed to have been given to you
along with GNU tar so you can know your rights and responsibilities. It
should be in a file named COPYING. Among other things, the copyright
notice and this notice must be preserved on all copies.
In other words, go ahead and share GNU tar, but don't try to stop
anyone else from sharing it farther. Help stamp out software hoarding!
*/
/*
* Header file for tar (tape archive) program.
*
* @(#)tar.h 1.24 87/11/06
*
* 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
#ifdef USG
typedef int size_t;
#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
#define SPARSE_EXT_HDR 21
#define SPARSE_IN_HDR 4
struct sparse {
char offset[12];
char numbytes[12];
};
struct sp_array {
int offset;
int numbytes;
};
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];
/* these following fields were added by JF for gnu */
/* and are NOT standard */
char atime[12];
char ctime[12];
char offset[12];
char longnames[4];
struct sparse sp[SPARSE_IN_HDR];
char isextended;
char realsize[12]; /* true size of the sparse file */
/* char ending_blanks[12];*/ /* number of nulls at the
end of the file, if any */
} header;
struct extended_header {
struct sparse sp[21];
char isextended;
} ext_hdr;
};
/* 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. */
#define LF_DUMPDIR 'D' /* This is a dir entry that contains
the names of files that were in
the dir at the time the dump
was made */
#define LF_MULTIVOL 'M' /* This is the continuation
of a file that began on another
volume */
#define LF_SPARSE 'S' /* This is for sparse files */
#define LF_VOLHDR 'V' /* This file is a tape/volume header */
/* Ignore it on extraction */
/*
* 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 *info_script; /* Script to run at end of each tape change */
TAR_EXTERN char *name_file; /* File containing names to work on */
TAR_EXTERN char *tar; /* Name of this program */
TAR_EXTERN struct sp_array *sparsearray;/* Pointer to the start of the scratch space */
TAR_EXTERN int sp_array_size; /* Initial size of the sparsearray */
/*
* Flags from the command line
*/
TAR_EXTERN int cmd_mode;
#define CMD_NONE 0
#define CMD_CAT 1 /* -A */
#define CMD_CREATE 2 /* -c */
#define CMD_DIFF 3 /* -d */
#define CMD_APPEND 4 /* -r */
#define CMD_LIST 5 /* -t */
#define CMD_UPDATE 6 /* -u */
#define CMD_EXTRACT 7 /* -x */
#define CMD_DELETE 8 /* -D */
/* -[0-9][lmh] */
/* CMD_CAT -A */
/* -b */
TAR_EXTERN int f_reblock; /* -B */
/* CMD_CREATE -c */
/* -C */
/* CMD_DIFF -d */
/* TAR_EXTERN char f_dironly; /* -D */
/* -f */
TAR_EXTERN int f_run_script_at_end; /* -F */
TAR_EXTERN int f_gnudump; /* -G */
TAR_EXTERN int f_follow_links; /* -h */
TAR_EXTERN int f_ignorez; /* -i */
/* CMD_DELETE -J */
TAR_EXTERN int f_keep; /* -k */
TAR_EXTERN int f_startfile; /* -K */
TAR_EXTERN int f_local_filesys; /* -l */
TAR_EXTERN int f_modified; /* -m */
TAR_EXTERN int f_multivol; /* -M */
TAR_EXTERN int f_new_files; /* -N */
TAR_EXTERN int f_oldarch; /* -o */
TAR_EXTERN int f_exstdout; /* -O */
TAR_EXTERN int f_use_protection; /* -p */
TAR_EXTERN int f_absolute_paths; /* -P */
TAR_EXTERN int f_sayblock; /* -R */
TAR_EXTERN int f_sorted_names; /* -s */
TAR_EXTERN int f_sparse_files; /* -S ... JK */
TAR_EXTERN int f_namefile; /* -T */
/* CMD_UPDATE -u */
TAR_EXTERN int f_verbose; /* -v */
TAR_EXTERN char *f_volhdr; /* -V */
TAR_EXTERN int f_confirm; /* -w */
TAR_EXTERN int f_verify; /* -W */
/* CMD_EXTRACT -x */
TAR_EXTERN int f_exclude; /* -X */
TAR_EXTERN int f_compress; /* -z */
/* -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 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; /* cached strlen(name) */
char found; /* A matching file has been found */
char firstch; /* First char is literally matched */
char regexp; /* This name is a regexp, not literal */
char *change_dir; /* JF set with the -C option */
char *dir_contents; /* JF for f_gnudump */
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
* but System V has no defines in
* 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();
/* Do not prototype these for BSD--see port.c [DOPRNT_MSG]. */
#if __STDC__
void msg(char *, ...);
void msg_perror(char *, ...);
#else
void msg();
void msg_perror();
#endif
/* #define annorec(stream, msg) anno(stream, msg, 0) /* Cur rec */
/* #define annofile(stream, msg) anno(stream, msg, 1) /* Saved rec */
Gnutar-1.08/port.h 600 10 5 3144 4403301073 7000 /* Portability declarations.
Copyright (C) 1988 Free Software Foundation
This file is part of GNU Tar.
GNU Tar 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 1, or (at your option)
any later version.
GNU Tar 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 Tar; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
/*
* Portability declarations for tar.
*
* @(#)port.h 1.3 87/11/11 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)
#ifdef MSDOS
/* missing things from sys/stat.h */
#define S_ISUID 0
#define S_ISGID 0
#define S_ISVTX 0
/* device stuff */
#define makedev(ma, mi) ((ma << 8) | mi)
#define major(dev) (dev)
#define minor(dev) (dev)
#endif /* MSDOS */
Gnutar-1.08/open3.h 600 10 5 5157 4403300753 7052 /* Defines for Sys V style 3-argument open call.
Copyright (C) 1988 Free Software Foundation
This file is part of GNU Tar.
GNU Tar 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 1, or (at your option)
any later version.
GNU Tar 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 Tar; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
/*
* @(#)open3.h 1.4 87/11/11
*
* open3.h -- #defines for the various flags for the Sys V style 3-argument
* open() call. On BSD or System 5, the system already has this in an
* include file. This file is needed for V7 and MINIX systems for the
* benefit of open3() in port.c, a routine that emulates the 3-argument
* call using system calls available on V7/MINIX.
*
* This file is needed by PD tar even if we aren't using the
* emulator, since the #defines for O_WRONLY, etc. are used in
* a couple of places besides the open() calls, (e.g. in the assignment
* to openflag in extract.c). We just #include this rather than
* #ifdef them out.
*
* Written 6/10/87 by rmtodd@uokmax (Richard Todd).
*
* The names have been changed by John Gilmore, 31 July 1987, since
* Richard called it "bsdopen", and really this change was introduced in
* AT&T Unix systems before BSD picked it up.
*/
/* Only one of the next three should be specified */
#define O_RDONLY 0 /* only allow read */
#define O_WRONLY 1 /* only allow write */
#define O_RDWR 2 /* both are allowed */
/* The rest of these can be OR-ed in to the above. */
/*
* O_NDELAY isn't implemented by the emulator. It's only useful (to tar) on
* systems that have named pipes anyway; it prevents tar's hanging by
* opening a named pipe. We #ifndef it because some systems already have
* it defined.
*/
#ifndef O_NDELAY
#define O_NDELAY 4 /* don't block on opening devices that would
* block on open -- ignored by emulator. */
#endif
#define O_CREAT 8 /* create file if needed */
#define O_EXCL 16 /* file cannot already exist */
#define O_TRUNC 32 /* truncate file on open */
#define O_APPEND 64 /* always write at end of file -- ignored by emul */
#ifdef EMUL_OPEN3
/*
* make emulation transparent to rest of file -- redirect all open() calls
* to our routine
*/
#define open open3
#endif
Gnutar-1.08/rmt.h 600 10 5 5516 4560141624 6633 /* Remote tape drive defines for tar.
Copyright (C) 1988 Free Software Foundation
This file is part of GNU Tar.
GNU Tar 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 1, or (at your option)
any later version.
GNU Tar 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 Tar; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#ifdef NO_REMOTE
#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
extern long lseek();
#else
#ifndef USG
#define strchr index
#endif
#define __REM_BIAS 128
#define RMTIOCTL
#ifndef O_CREAT
#define O_CREAT 01000
#endif
extern char *__rmt_path;
extern char *strchr();
#define _remdev(path) ((__rmt_path=strchr(path, ':')) && strncmp(__rmt_path, ":/dev/", 6)==0)
#define _isrmt(fd) ((fd) >= __REM_BIAS)
#define rmtopen(path,oflag,mode) (_remdev(path) ? __rmt_open(path, oflag, mode, __REM_BIAS) : 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) (_remdev(path) ? __rmt_open (path, 1 | O_CREAT, mode, __REM_BIAS) : 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
extern long lseek(),__rmt_lseek();
#endif
Gnutar-1.08/msd_dir.h 600 10 5 1672 4451270725 7455 /*
* @(#)msd_dir.h 1.4 87/11/06 Public Domain.
*
* A public domain implementation of BSD directory routines for
* MS-DOS. Written by Michael Rendell ({uunet,utai}michael@garfield),
* August 1897
*/
#define rewinddir(dirp) seekdir(dirp, 0L)
#define MAXNAMLEN 12
struct direct {
ino_t d_ino; /* a bit of a farce */
int d_reclen; /* more farce */
int d_namlen; /* length of d_name */
char d_name[MAXNAMLEN + 1]; /* garentee null termination */
};
struct _dircontents {
char *_d_entry;
struct _dircontents *_d_next;
};
typedef struct _dirdesc {
int dd_id; /* uniquely identify each open directory */
long dd_loc; /* where we are in directory entry is this */
struct _dircontents *dd_contents; /* pointer to contents of dir */
struct _dircontents *dd_cp; /* pointer to current position */
} DIR;
extern DIR *opendir();
extern struct direct *readdir();
extern void seekdir();
extern long telldir();
extern void closedir();
Gnutar-1.08/msd_dir.c 600 10 5 10146 4220301620 7443 /*
* @(#)msd_dir.c 1.4 87/11/06 Public Domain.
*
* A public domain implementation of BSD directory routines for
* MS-DOS. Written by Michael Rendell ({uunet,utai}michael@garfield),
* August 1897
*/
#include
#include
#include
#include
#include
#include
#ifndef NULL
# define NULL 0
#endif /* NULL */
#ifndef MAXPATHLEN
# define MAXPATHLEN 255
#endif /* MAXPATHLEN */
/* attribute stuff */
#define A_RONLY 0x01
#define A_HIDDEN 0x02
#define A_SYSTEM 0x04
#define A_LABEL 0x08
#define A_DIR 0x10
#define A_ARCHIVE 0x20
/* dos call values */
#define DOSI_FINDF 0x4e
#define DOSI_FINDN 0x4f
#define DOSI_SDTA 0x1a
#define Newisnull(a, t) ((a = (t *) malloc(sizeof(t))) == (t *) NULL)
/* #define ATTRIBUTES (A_DIR | A_HIDDEN | A_SYSTEM) */
#define ATTRIBUTES (A_RONLY | A_SYSTEM | A_DIR)
/* what find first/next calls look use */
typedef struct {
char d_buf[21];
char d_attribute;
unsigned short d_time;
unsigned short d_date;
long d_size;
char d_name[13];
} Dta_buf;
static char *getdirent();
static void setdta();
static void free_dircontents();
static Dta_buf dtabuf;
static Dta_buf *dtapnt = &dtabuf;
static union REGS reg, nreg;
#if defined(M_I86LM)
static struct SREGS sreg;
#endif
DIR *
opendir(name)
char *name;
{
struct stat statb;
DIR *dirp;
char c;
char *s;
struct _dircontents *dp;
char nbuf[MAXPATHLEN + 1];
if (stat(name, &statb) < 0 || (statb.st_mode & S_IFMT) != S_IFDIR)
return (DIR *) NULL;
if (Newisnull(dirp, DIR))
return (DIR *) NULL;
if (*name && (c = name[strlen(name) - 1]) != '\\' && c != '/')
(void) strcat(strcpy(nbuf, name), "\\*.*");
else
(void) strcat(strcpy(nbuf, name), "*.*");
dirp->dd_loc = 0;
setdta();
dirp->dd_contents = dirp->dd_cp = (struct _dircontents *) NULL;
if ((s = getdirent(nbuf)) == (char *) NULL)
return dirp;
do {
if (Newisnull(dp, struct _dircontents) || (dp->_d_entry =
malloc((unsigned) (strlen(s) + 1))) == (char *) NULL)
{
if (dp)
free((char *) dp);
free_dircontents(dirp->dd_contents);
return (DIR *) NULL;
}
if (dirp->dd_contents)
dirp->dd_cp = dirp->dd_cp->_d_next = dp;
else
dirp->dd_contents = dirp->dd_cp = dp;
(void) strcpy(dp->_d_entry, s);
dp->_d_next = (struct _dircontents *) NULL;
} while ((s = getdirent((char *) NULL)) != (char *) NULL);
dirp->dd_cp = dirp->dd_contents;
return dirp;
}
void
closedir(dirp)
DIR *dirp;
{
free_dircontents(dirp->dd_contents);
free((char *) dirp);
}
struct direct *
readdir(dirp)
DIR *dirp;
{
static struct direct dp;
if (dirp->dd_cp == (struct _dircontents *) NULL)
return (struct direct *) NULL;
dp.d_namlen = dp.d_reclen =
strlen(strcpy(dp.d_name, dirp->dd_cp->_d_entry));
strlwr(dp.d_name); /* JF */
dp.d_ino = 0;
dirp->dd_cp = dirp->dd_cp->_d_next;
dirp->dd_loc++;
return &dp;
}
void
seekdir(dirp, off)
DIR *dirp;
long off;
{
long i = off;
struct _dircontents *dp;
if (off < 0)
return;
for (dp = dirp->dd_contents ; --i >= 0 && dp ; dp = dp->_d_next)
;
dirp->dd_loc = off - (i + 1);
dirp->dd_cp = dp;
}
long
telldir(dirp)
DIR *dirp;
{
return dirp->dd_loc;
}
static void
free_dircontents(dp)
struct _dircontents *dp;
{
struct _dircontents *odp;
while (dp) {
if (dp->_d_entry)
free(dp->_d_entry);
dp = (odp = dp)->_d_next;
free((char *) odp);
}
}
static char *
getdirent(dir)
char *dir;
{
if (dir != (char *) NULL) { /* get first entry */
reg.h.ah = DOSI_FINDF;
reg.h.cl = ATTRIBUTES;
#if defined(M_I86LM)
reg.x.dx = FP_OFF(dir);
sreg.ds = FP_SEG(dir);
#else
reg.x.dx = (unsigned) dir;
#endif
} else { /* get next entry */
reg.h.ah = DOSI_FINDN;
#if defined(M_I86LM)
reg.x.dx = FP_OFF(dtapnt);
sreg.ds = FP_SEG(dtapnt);
#else
reg.x.dx = (unsigned) dtapnt;
#endif
}
#if defined(M_I86LM)
intdosx(®, &nreg, &sreg);
#else
intdos(®, &nreg);
#endif
if (nreg.x.cflag)
return (char *) NULL;
return dtabuf.d_name;
}
static void
setdta()
{
reg.h.ah = DOSI_SDTA;
#if defined(M_I86LM)
reg.x.dx = FP_OFF(dtapnt);
sreg.ds = FP_SEG(dtapnt);
intdosx(®, &nreg, &sreg);
#else
reg.x.dx = (int) dtapnt;
intdos(®, &nreg);
#endif
}
Gnutar-1.08/rtape_server.c 600 10 5 11407 4345026736 10550 /*
* 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 */
#ifndef lint
static char sccsid[] = "@(#)rmt.c 5.4 (Berkeley) 6/29/88";
#endif /* not lint */
/* JF added #ifdef about SO_RCVBUF in attempt to make this run on more
machines. Maybe it'll work */
/*
* rmt
*/
#include
#include
#include
#include
#include
#include
int tape = -1;
char *record;
int maxrecsize = -1;
char *checkbuf();
#define SSIZE 64
char device[SSIZE];
char count[SSIZE], mode[SSIZE], pos[SSIZE], op[SSIZE];
extern errno;
char *sys_errlist[];
char resp[BUFSIZ];
long lseek();
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)
main(argc, argv)
int argc;
char **argv;
{
int rval;
char c;
int n, i, cc;
argc--, argv++;
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);
tape = open(device, atoi(mode),0666);
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);
{ 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;
}
goto respond;
case 'S': /* status */
DEBUG("rmtd: S\n");
{ 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));
goto top;
}
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;
}
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;
{
extern char *malloc();
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, &size, sizeof (size)) < 0)
size -= 1024;
#else
size= 1+((size-1)%1024);
#endif
return (record);
}
error(num)
int num;
{
DEBUG2("rmtd: E %d (%s)\n", num, sys_errlist[num]);
(void) sprintf(resp, "E%d\n%s\n", num, sys_errlist[num]);
(void) write(1, resp, strlen (resp));
}
Gnutar-1.08/rtape_lib.c 600 10 5 27640 4676044644 10023 /* Remote tape emulator subroutines.
Copyright (C) 1988 Free Software Foundation
This file is part of GNU Tar.
GNU Tar 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 1, or (at your option)
any later version.
GNU Tar 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 Tar; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
/* JF: modified to make all rmtXXX calls into macros for speed */
#ifndef lint
static char *RCSid = "$Header: /usr/src/local/usr.lib/librmt/RCS/rmtlib.c,v 1.7 89/03/23 14:09:51 root Exp Locker: root $";
#endif
/*
* $Log: rmtlib.c,v $
* Revision 1.7 89/03/23 14:09:51 root
* Fix from [email protected] for use w/compat. ADR.
*
* Revision 1.6 88/10/25 17:04:29 root
* rexec code and a bug fix from srs!dan, miscellanious cleanup. ADR.
*
* Revision 1.5 88/10/25 16:30:17 root
* Fix from [email protected] for getting user@host:dev right. ADR.
*
* Revision 1.4 87/10/30 10:36:12 root
* Made 4.2 syntax a compile time option. ADR.
*
* Revision 1.3 87/04/22 11:16:48 root
* Two fixes from [email protected] to correctly
* do fd biasing and rmt protocol on 'S' command. ADR.
*
* Revision 1.2 86/10/09 16:38:53 root
* Changed to reflect 4.3BSD rcp syntax. ADR.
*
* Revision 1.1 86/10/09 16:17:35 root
* Initial revision
*
*/
/*
* rmt --- remote tape emulator subroutines
*
* Originally written by Jeff Lee, modified some by Arnold Robbins
*
* WARNING: 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
*
* Redone as a library that can replace open, read, write, etc, by
* Fred Fish, with some additional work by Arnold Robbins.
*/
/*
* MAXUNIT --- Maximum number of remote tape file units
*
* READ --- Return the number of the read side file descriptor
* WRITE --- Return the number of the write side file descriptor
*/
/*#define RMTIOCTL 1 */
/* #define USE_REXEC 1 /* rexec code courtesy of Dan Kegel, srs!dan */
#include
#include
#include
#ifdef RMTIOCTL
#ifdef COHERENT
#include
#include
#else
#include
#include
#endif
#endif
#ifdef USE_REXEC
#include
#endif
#include
#include
#include
#define BUFMAGIC 64 /* a magic number for buffer sizes */
#define MAXUNIT 4
#define READ(fd) (Ctp[fd][0])
#define WRITE(fd) (Ptc[fd][1])
static int Ctp[MAXUNIT][2] = { -1, -1, -1, -1, -1, -1, -1, -1 };
static int Ptc[MAXUNIT][2] = { -1, -1, -1, -1, -1, -1, -1, -1 };
extern int errno;
char *__rmt_path;
/*
* _rmt_panic --- close off a remote tape connection
*/
static void _rmt_panic(fildes)
int fildes;
{
close(READ(fildes));
close(WRITE(fildes));
READ(fildes) = -1;
WRITE(fildes) = -1;
}
/*
* command --- attempt to perform a remote tape command
*/
static int command(fildes, buf)
int fildes;
char *buf;
{
register int blen;
#ifdef USG
void (*pstat)();
#else
int (*pstat)();
#endif
/*
* save current pipe status and try to make the request
*/
blen = strlen(buf);
pstat = signal(SIGPIPE, SIG_IGN);
if (write(WRITE(fildes), buf, blen) == blen)
{
signal(SIGPIPE, pstat);
return(0);
}
/*
* something went wrong. close down and go home
*/
signal(SIGPIPE, pstat);
_rmt_panic(fildes);
errno = EIO;
return(-1);
}
/*
* status --- retrieve the status from the pipe
*/
static int status(fildes)
int fildes;
{
int i;
char c, *cp;
char buffer[BUFMAGIC];
/*
* read the reply command line
*/
for (i = 0, cp = buffer; i < BUFMAGIC; i++, cp++)
{
if (read(READ(fildes), cp, 1) != 1)
{
_rmt_panic(fildes);
errno = EIO;
return(-1);
}
if (*cp == '\n')
{
*cp = 0;
break;
}
}
if (i == BUFMAGIC)
{
_rmt_panic(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);
while (read(READ(fildes), &c, 1) == 1)
if (c == '\n')
break;
if (*cp == 'F')
_rmt_panic(fildes);
return(-1);
}
/*
* check for mis-synced pipes
*/
if (*cp != 'A')
{
_rmt_panic(fildes);
errno = EIO;
return(-1);
}
return(atoi(cp + 1));
}
#ifdef USE_REXEC
/*
* _rmt_rexec
*
* execute /etc/rmt on a remote system using rexec().
* Return file descriptor of bidirectional socket for stdin and stdout
* If username is NULL, or an empty string, uses current username.
*
* ADR: 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; /* may be NULL */
{
struct servent *rexecserv;
rexecserv = getservbyname("exec", "tcp");
if (NULL == rexecserv) {
fprintf (stderr, "? exec/tcp: service not available.");
exit (-1);
}
if ((user != NULL) && *user == '\0')
user = (char *) NULL;
return rexec (&host, rexecserv->s_port, user, NULL,
"/etc/rmt", (int *)NULL);
}
#endif /* USE_REXEC */
/*
* _rmt_open --- open a magtape device on system specified, as given user
*
* file name has the form [user@]system:/dev/????
#ifdef COMPAT
* file name has the form system[.user]:/dev/????
#endif
*/
#define MAXHOSTLEN 257 /* BSD allows very long host names... */
int __rmt_open (path, oflag, mode, bias)
char *path;
int oflag;
int mode;
int bias;
{
int i, rc;
char buffer[BUFMAGIC];
char system[MAXHOSTLEN];
char device[BUFMAGIC];
char login[BUFMAGIC];
char *sys, *dev, *user;
sys = system;
dev = device;
user = login;
/*
* first, find an open 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 system and device, and optional user
* don't munge original string
* if COMPAT is defined, also handle old (4.2) style person.site notation.
*/
while (*path != '@'
#ifdef COMPAT
&& *path != '.'
#endif
&& *path != ':') {
*sys++ = *path++;
}
*sys = '\0';
path++;
if (*(path - 1) == '@')
{
(void) strcpy (user, system); /* saw user part of user@host */
sys = system; /* start over */
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 USE_REXEC
/*
* Execute the remote command using rexec
*/
READ(i) = WRITE(i) = _rmt_rexec(system, login);
if (READ(i) < 0)
return -1;
#else
/*
* setup the pipes for the 'rsh' command and fork
*/
if (pipe(Ptc[i]) == -1 || pipe(Ctp[i]) == -1)
return(-1);
if ((rc = fork()) == -1)
return(-1);
if (rc == 0)
{
close(0);
dup(Ptc[i][0]);
close(Ptc[i][0]); close(Ptc[i][1]);
close(1);
dup(Ctp[i][1]);
close(Ctp[i][0]); close(Ctp[i][1]);
(void) setuid (getuid ());
(void) setgid (getgid ());
if (*login)
{
execl("/usr/ucb/rsh", "rsh", system, "-l", login,
"/etc/rmt", (char *) 0);
execl("/usr/bin/remsh", "remsh", system, "-l", login,
"/etc/rmt", (char *) 0);
execl("/usr/bin/rsh", "rsh", system, "-l", login,
"/etc/rmt", (char *) 0);
execl("/usr/bsd/rsh", "rsh", system, "-l", login,
"/etc/rmt", (char *)0);
}
else
{
execl("/usr/ucb/rsh", "rsh", system,
"/etc/rmt", (char *) 0);
execl("/usr/bin/remsh", "remsh", system,
"/etc/rmt", (char *) 0);
execl("/usr/bin/rsh", "rsh", system,
"/etc/rmt", (char *) 0);
execl("/usr/bsd/rsh", "rsh", system,
"/etc/rmt", (char *) 0);
}
/*
* bad problems if we get here
*/
perror("remote shell exec");
exit(1);
}
close(Ptc[i][0]); close(Ctp[i][1]);
#endif
/*
* now 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);
}
/*
* _rmt_close --- close a remote magtape unit and shut down
*/
int __rmt_close(fildes)
int fildes;
{
int rc;
if (command(fildes, "C\n") != -1)
{
rc = status(fildes);
_rmt_panic(fildes);
return(rc);
}
return(-1);
}
/*
* _rmt_read --- read a buffer from a remote tape
*/
int __rmt_read(fildes, buf, nbyte)
int fildes;
char *buf;
unsigned int nbyte;
{
int rc, i;
char buffer[BUFMAGIC];
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);
if (nbyte <= 0)
{
_rmt_panic(fildes);
errno = EIO;
return(-1);
}
}
return(rc);
}
/*
* _rmt_write --- write a buffer to the remote tape
*/
int __rmt_write(fildes, buf, nbyte)
int fildes;
char *buf;
unsigned int nbyte;
{
char buffer[BUFMAGIC];
#ifdef USG
void (*pstat)();
#else
int (*pstat)();
#endif
sprintf(buffer, "W%d\n", nbyte);
if (command(fildes, buffer) == -1)
return(-1);
pstat = signal(SIGPIPE, SIG_IGN);
if (write(WRITE(fildes), buf, nbyte) == nbyte)
{
signal (SIGPIPE, pstat);
return(status(fildes));
}
signal (SIGPIPE, pstat);
_rmt_panic(fildes);
errno = EIO;
return(-1);
}
/*
* _rmt_lseek --- perform an imitation lseek operation remotely
*/
long __rmt_lseek(fildes, offset, whence)
int fildes;
long offset;
int whence;
{
char buffer[BUFMAGIC];
sprintf(buffer, "L%d\n%d\n", offset, whence);
if (command(fildes, buffer) == -1)
return(-1);
return(status(fildes));
}
/*
* _rmt_ioctl --- perform raw tape operations remotely
*/
#ifdef RMTIOCTL
__rmt_ioctl(fildes, op, arg)
int fildes, op;
char *arg;
{
char c;
int rc, cnt;
char buffer[BUFMAGIC];
/*
* MTIOCOP is the easy one. nothing is transfered in binary
*/
if (op == MTIOCTOP)
{
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));
}
/*
* we can only handle 2 ops, if not the other one, punt
*/
if (op != MTIOCGET)
{
errno = EINVAL;
return(-1);
}
/*
* grab the status and read it directly into the structure
* this assumes that the status buffer is (hopefully) not
* padded and that 2 shorts fit in a long without any word
* alignment problems, ie - 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_panic(fildes);
errno = EIO;
return(-1);
}
}
/*
* now we 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 /* RMTIOCTL */
Gnutar-1.08/getdate.y 600 10 5 32100 4553203656 7502 %token ID MONTH DAY MERIDIAN NUMBER UNIT MUNIT SUNIT ZONE DAYZONE AGO
%{
/* Originally from: Steven M. Bellovin (unc!smb) */
/* Dept. of Computer Science */
/* University of North Carolina at Chapel Hill */
/* @(#)getdate.y 2.17 11/30/87 */
/* #include "defs.h" JF not used any more */
#include
#ifdef USG
struct timeb
{
time_t time;
unsigned short millitm;
short timezone;
short dstflag;
};
#else
#include
#endif
#include
#if defined(BSD4_2) || defined (BSD4_1C)
#include
#else /* sane */
#include
#endif /* sane */
#ifdef __GNUC__
#define alloca __builtin_alloca
#else
#ifdef sparc
#include
#endif
#endif
#define NULL 0
#define daysec (24L*60L*60L)
static int timeflag, zoneflag, dateflag, dayflag, relflag;
static time_t relsec, relmonth;
static int hh, mm, ss, merid, day_light;
static int dayord, dayreq;
static int month, day, year;
static int ourzone;
#define AM 1
#define PM 2
#define DAYLIGHT 1
#define STANDARD 2
#define MAYBE 3
static time_t timeconv();
static time_t daylcorr();
static lookup();
%}
%%
timedate: /* empty */
| timedate item
;
item: tspec
{timeflag++;}
| zone
{zoneflag++;}
| dtspec
{dateflag++;}
| dyspec
{dayflag++;}
| rspec
{relflag++;}
| nspec;
nspec: NUMBER
{if (timeflag && dateflag && !relflag) year = $1;
else if ($1 > 10000) {
dateflag++;
day= $1%100;
month= ($1/100)%100;
year = month/10000;
} else {timeflag++;hh = $1/100;mm = $1%100;ss = 0;merid = 24;}};
tspec: NUMBER MERIDIAN
{hh = $1; mm = 0; ss = 0; merid = $2;}
| NUMBER ':' NUMBER
{hh = $1; mm = $3; merid = 24;}
| NUMBER ':' NUMBER MERIDIAN
{hh = $1; mm = $3; merid = $4;}
| NUMBER ':' NUMBER NUMBER
{hh = $1; mm = $3; merid = 24;
day_light = STANDARD; ourzone = -($4%100 + 60*($4/100));}
| NUMBER ':' NUMBER ':' NUMBER
{hh = $1; mm = $3; ss = $5; merid = 24;}
| NUMBER ':' NUMBER ':' NUMBER MERIDIAN
{hh = $1; mm = $3; ss = $5; merid = $6;}
| NUMBER ':' NUMBER ':' NUMBER NUMBER
{hh = $1; mm = $3; ss = $5; merid = 24;
day_light = STANDARD; ourzone = -($6%100 + 60*($6/100));};
zone: ZONE
{ourzone = $1; day_light = STANDARD;}
| DAYZONE
{ourzone = $1; day_light = DAYLIGHT;};
dyspec: DAY
{dayord = 1; dayreq = $1;}
| DAY ','
{dayord = 1; dayreq = $1;}
| NUMBER DAY
{dayord = $1; dayreq = $2;};
dtspec: NUMBER '/' NUMBER
{month = $1; day = $3;}
| NUMBER '/' NUMBER '/' NUMBER
{month = $1; day = $3; year = $5;}
| MONTH NUMBER
{month = $1; day = $2;}
| MONTH NUMBER ',' NUMBER
{month = $1; day = $2; year = $4;}
| NUMBER MONTH
{month = $2; day = $1;}
| NUMBER MONTH NUMBER
{month = $2; day = $1; year = $3;};
rspec: NUMBER UNIT
{relsec += 60L * $1 * $2;}
| NUMBER MUNIT
{relmonth += $1 * $2;}
| NUMBER SUNIT
{relsec += $1;}
| UNIT
{relsec += 60L * $1;}
| MUNIT
{relmonth += $1;}
| SUNIT
{relsec++;}
| rspec AGO
{relsec = -relsec; relmonth = -relmonth;};
%%
static int mdays[12] =
{31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
#define epoch 1970
extern struct tm *localtime();
static time_t
dateconv(mm, dd, yy, h, m, s, mer, zone, dayflag)
int mm, dd, yy, h, m, s, mer, zone, dayflag;
{
time_t tod, jdate;
register int i;
if (yy < 0) yy = -yy;
if (yy < 100) yy += 1900;
mdays[1] = 28 + (yy%4 == 0 && (yy%100 != 0 || yy%400 == 0));
if (yy < epoch || yy > 1999 || mm < 1 || mm > 12 ||
dd < 1 || dd > mdays[--mm]) return (-1);
jdate = dd-1;
for (i=0; i
jdate *= daysec;
jdate += zone * 60L;
if ((tod = timeconv(h, m, s, mer)) < 0) return (-1);
jdate += tod;
if (dayflag==DAYLIGHT || (dayflag==MAYBE&&localtime(&jdate)->tm_isdst))
jdate += -1*60*60;
return (jdate);
}
static time_t
dayconv(ord, day, now)
int ord, day; time_t now;
{
register struct tm *loctime;
time_t tod;
tod = now;
loctime = localtime(&tod);
tod += daysec * ((day - loctime->tm_wday + 7) % 7);
tod += 7*daysec*(ord<=0?ord:ord-1);
return daylcorr(tod, now);
}
static time_t
timeconv(hh, mm, ss, mer)
register int hh, mm, ss, mer;
{
if (mm < 0 || mm > 59 || ss < 0 || ss > 59) return (-1);
switch (mer) {
case AM: if (hh < 1 || hh > 12) return(-1);
return (60L * ((hh%12)*60L + mm)+ss);
case PM: if (hh < 1 || hh > 12) return(-1);
return (60L * ((hh%12 +12)*60L + mm)+ss);
case 24: if (hh < 0 || hh > 23) return (-1);
return (60L * (hh*60L + mm)+ss);
default: return (-1);
}
}
static time_t
monthadd(sdate, relmonth)
time_t sdate, relmonth;
{
struct tm *ltime;
time_t dateconv();
int mm, yy;
if (relmonth == 0) return 0;
ltime = localtime(&sdate);
mm = 12*ltime->tm_year + ltime->tm_mon + relmonth;
yy = mm/12;
mm = mm%12 + 1;
return daylcorr(dateconv(mm, ltime->tm_mday, yy, ltime->tm_hour,
ltime->tm_min, ltime->tm_sec, 24, ourzone, MAYBE), sdate);
}
static time_t
daylcorr(future, now)
time_t future, now;
{
int fdayl, nowdayl;
nowdayl = (localtime(&now)->tm_hour+1) % 24;
fdayl = (localtime(&future)->tm_hour+1) % 24;
return (future-now) + 60L*60L*(nowdayl-fdayl);
}
static char *lptr;
static yylex()
{
extern int yylval;
int sign;
register char c;
register char *p;
char idbuf[20];
int pcnt;
for (;;) {
while (isspace(*lptr))
lptr++;
if (isdigit(c = *lptr) || c == '-' || c == '+') {
if (c== '-' || c == '+') {
if (c=='-') sign = -1;
else sign = 1;
if (!isdigit(*++lptr)) {
/* yylval = sign; return (NUMBER); */
return yylex(); /* skip the '-' sign */
}
} else sign = 1;
yylval = 0;
while (isdigit(c = *lptr++))
yylval = 10*yylval + c - '0';
yylval *= sign;
lptr--;
return (NUMBER);
} else if (isalpha(c)) {
p = idbuf;
while (isalpha(c = *lptr++) || c=='.')
if (p < &idbuf[sizeof(idbuf)-1]) *p++ = c;
*p = '\0';
lptr--;
return (lookup(idbuf));
}
else if (c == '(') {
pcnt = 0;
do {
c = *lptr++;
if (c == '\0') return(c);
else if (c == '(') pcnt++;
else if (c == ')') pcnt--;
} while (pcnt > 0);
}
else return (*lptr++);
}
}
struct table {
char *name;
int type, value;
};
static struct table mdtab[] = {
{"january", MONTH, 1},
{"february", MONTH, 2},
{"march", MONTH, 3},
{"april", MONTH, 4},
{"may", MONTH, 5},
{"june", MONTH, 6},
{"july", MONTH, 7},
{"august", MONTH, 8},
{"september", MONTH, 9},
{"sept", MONTH, 9},
{"october", MONTH, 10},
{"november", MONTH, 11},
{"december", MONTH, 12},
{"sunday", DAY, 0},
{"monday", DAY, 1},
{"tuesday", DAY, 2},
{"tues", DAY, 2},
{"wednesday", DAY, 3},
{"wednes", DAY, 3},
{"thursday", DAY, 4},
{"thur", DAY, 4},
{"thurs", DAY, 4},
{"friday", DAY, 5},
{"saturday", DAY, 6},
{0, 0, 0}};
#define HRS *60
#define HALFHR 30
static struct table mztab[] = {
{"a.m.", MERIDIAN, AM},
{"am", MERIDIAN, AM},
{"p.m.", MERIDIAN, PM},
{"pm", MERIDIAN, PM},
{"nst", ZONE, 3 HRS + HALFHR}, /* Newfoundland */
{"n.s.t.", ZONE, 3 HRS + HALFHR},
{"ast", ZONE, 4 HRS}, /* Atlantic */
{"a.s.t.", ZONE, 4 HRS},
{"adt", DAYZONE, 4 HRS},
{"a.d.t.", DAYZONE, 4 HRS},
{"est", ZONE, 5 HRS}, /* Eastern */
{"e.s.t.", ZONE, 5 HRS},
{"edt", DAYZONE, 5 HRS},
{"e.d.t.", DAYZONE, 5 HRS},
{"cst", ZONE, 6 HRS}, /* Central */
{"c.s.t.", ZONE, 6 HRS},
{"cdt", DAYZONE, 6 HRS},
{"c.d.t.", DAYZONE, 6 HRS},
{"mst", ZONE, 7 HRS}, /* Mountain */
{"m.s.t.", ZONE, 7 HRS},
{"mdt", DAYZONE, 7 HRS},
{"m.d.t.", DAYZONE, 7 HRS},
{"pst", ZONE, 8 HRS}, /* Pacific */
{"p.s.t.", ZONE, 8 HRS},
{"pdt", DAYZONE, 8 HRS},
{"p.d.t.", DAYZONE, 8 HRS},
{"yst", ZONE, 9 HRS}, /* Yukon */
{"y.s.t.", ZONE, 9 HRS},
{"ydt", DAYZONE, 9 HRS},
{"y.d.t.", DAYZONE, 9 HRS},
{"hst", ZONE, 10 HRS}, /* Hawaii */
{"h.s.t.", ZONE, 10 HRS},
{"hdt", DAYZONE, 10 HRS},
{"h.d.t.", DAYZONE, 10 HRS},
{"gmt", ZONE, 0 HRS},
{"g.m.t.", ZONE, 0 HRS},
{"bst", DAYZONE, 0 HRS}, /* British Summer Time */
{"b.s.t.", DAYZONE, 0 HRS},
{"eet", ZONE, 0 HRS}, /* European Eastern Time */
{"e.e.t.", ZONE, 0 HRS},
{"eest", DAYZONE, 0 HRS}, /* European Eastern Summer Time */
{"e.e.s.t.", DAYZONE, 0 HRS},
{"met", ZONE, -1 HRS}, /* Middle European Time */
{"m.e.t.", ZONE, -1 HRS},
{"mest", DAYZONE, -1 HRS}, /* Middle European Summer Time */
{"m.e.s.t.", DAYZONE, -1 HRS},
{"wet", ZONE, -2 HRS }, /* Western European Time */
{"w.e.t.", ZONE, -2 HRS },
{"west", DAYZONE, -2 HRS}, /* Western European Summer Time */
{"w.e.s.t.", DAYZONE, -2 HRS},
{"jst", ZONE, -9 HRS}, /* Japan Standard Time */
{"j.s.t.", ZONE, -9 HRS}, /* Japan Standard Time */
/* No daylight savings time */
{"aest", ZONE, -10 HRS}, /* Australian Eastern Time */
{"a.e.s.t.", ZONE, -10 HRS},
{"aesst", DAYZONE, -10 HRS}, /* Australian Eastern Summer Time */
{"a.e.s.s.t.", DAYZONE, -10 HRS},
{"acst", ZONE, -(9 HRS + HALFHR)}, /* Australian Central Time */
{"a.c.s.t.", ZONE, -(9 HRS + HALFHR)},
{"acsst", DAYZONE, -(9 HRS + HALFHR)}, /* Australian Central Summer */
{"a.c.s.s.t.", DAYZONE, -(9 HRS + HALFHR)},
{"awst", ZONE, -8 HRS}, /* Australian Western Time */
{"a.w.s.t.", ZONE, -8 HRS}, /* (no daylight time there, I'm told */
{0, 0, 0}};
static struct table unittb[] = {
{"year", MUNIT, 12},
{"month", MUNIT, 1},
{"fortnight", UNIT, 14*24*60},
{"week", UNIT, 7*24*60},
{"day", UNIT, 1*24*60},
{"hour", UNIT, 60},
{"minute", UNIT, 1},
{"min", UNIT, 1},
{"second", SUNIT, 1},
{"sec", SUNIT, 1},
{0, 0, 0}};
static struct table othertb[] = {
{"tomorrow", UNIT, 1*24*60},
{"yesterday", UNIT, -1*24*60},
{"today", UNIT, 0},
{"now", UNIT, 0},
{"last", NUMBER, -1},
{"this", UNIT, 0},
{"next", NUMBER, 2},
{"first", NUMBER, 1},
/* {"second", NUMBER, 2}, */
{"third", NUMBER, 3},
{"fourth", NUMBER, 4},
{"fifth", NUMBER, 5},
{"sixth", NUMBER, 6},
{"seventh", NUMBER, 7},
{"eigth", NUMBER, 8},
{"ninth", NUMBER, 9},
{"tenth", NUMBER, 10},
{"eleventh", NUMBER, 11},
{"twelfth", NUMBER, 12},
{"ago", AGO, 1},
{0, 0, 0}};
static struct table milzone[] = {
{"a", ZONE, 1 HRS},
{"b", ZONE, 2 HRS},
{"c", ZONE, 3 HRS},
{"d", ZONE, 4 HRS},
{"e", ZONE, 5 HRS},
{"f", ZONE, 6 HRS},
{"g", ZONE, 7 HRS},
{"h", ZONE, 8 HRS},
{"i", ZONE, 9 HRS},
{"k", ZONE, 10 HRS},
{"l", ZONE, 11 HRS},
{"m", ZONE, 12 HRS},
{"n", ZONE, -1 HRS},
{"o", ZONE, -2 HRS},
{"p", ZONE, -3 HRS},
{"q", ZONE, -4 HRS},
{"r", ZONE, -5 HRS},
{"s", ZONE, -6 HRS},
{"t", ZONE, -7 HRS},
{"u", ZONE, -8 HRS},
{"v", ZONE, -9 HRS},
{"w", ZONE, -10 HRS},
{"x", ZONE, -11 HRS},
{"y", ZONE, -12 HRS},
{"z", ZONE, 0 HRS},
{0, 0, 0}};
static
lookup(id)
char *id;
{
#define gotit (yylval=i->value, i->type)
char idvar[128];
register char *j, *k;
register struct table *i;
int abbrev;
(void) strcpy(idvar, id);
j = idvar;
k = id - 1;
while (*++k)
*j++ = isupper(*k) ? tolower(*k) : *k;
*j = '\0';
if (strlen(idvar) == 3)
abbrev = 1;
else
if (strlen(idvar) == 4 && idvar[3] == '.') {
abbrev = 1;
idvar[3] = '\0';
}
else
abbrev = 0;
for (i = mdtab; i->name; i++) {
k = idvar;
for (j = i->name; *j++ == *k++;) {
if (abbrev && j == i->name+3)
return gotit;
if (j[-1] == 0)
return gotit;
}
}
for (i = mztab; i->name; i++)
if (strcmp(i->name, idvar) == 0)
return gotit;
for (i=mztab; i->name; i++)
if (strcmp(i->name, idvar) == 0)
return gotit;
for (i=unittb; i->name; i++)
if (strcmp(i->name, idvar) == 0)
return gotit;
if (idvar[strlen(idvar)-1] == 's')
idvar[strlen(idvar)-1] = '\0';
for (i=unittb; i->name; i++)
if (strcmp(i->name, idvar) == 0)
return gotit;
for (i = othertb; i->name; i++)
if (strcmp(i->name, idvar) == 0)
return gotit;
if (strlen(idvar) == 1 && isalpha(*idvar)) {
for (i = milzone; i->name; i++)
if (strcmp(i->name, idvar) == 0)
return gotit;
}
return ID;
}
time_t
getdate(p, now)
char *p;
struct timeb *now;
{
#define mcheck(f) if (f>1) err++
time_t monthadd();
int err;
struct tm *lt;
struct timeb ftz;
time_t sdate, tod;
lptr = p;
if (now == ((struct timeb *) NULL)) {
now = &ftz;
ftime(&ftz);
}
lt = localtime(&now->time);
year = lt->tm_year;
month = lt->tm_mon+1;
day = lt->tm_mday;
relsec = 0; relmonth = 0;
timeflag=zoneflag=dateflag=dayflag=relflag=0;
ourzone = now->timezone;
day_light = MAYBE;
hh = mm = ss = 0;
merid = 24;
if (err = yyparse()) return (-1);
mcheck(timeflag);
mcheck(zoneflag);
mcheck(dateflag);
mcheck(dayflag);
if (err) return (-1);
if (dateflag || timeflag || dayflag) {
sdate = dateconv(month,day,year,hh,mm,ss,merid,ourzone,day_light);
if (sdate < 0) return -1;
}
else {
sdate = now->time;
if (relflag == 0)
sdate -= (lt->tm_sec + lt->tm_min*60 +
lt->tm_hour*(60L*60L));
}
sdate += relsec;
sdate += monthadd(sdate, relmonth);
if (dayflag && !dateflag) {
tod = dayconv(dayord, dayreq, sdate);
sdate += tod;
}
/*
** Have to do *something* with a legitimate -1 so it's distinguishable
** from the error return value. (Alternately could set errno on error.)
*/
return (sdate == -1) ? 0 : sdate;
}
static
yyerror(s)
char *s;
{
}
#ifdef USG
ftime(timeb)
struct timeb *timeb;
{
extern long timezone;
extern int daylight;
long t = 0;
localtime(&t); /* Dummy to init timzeone */
time(&(timeb->time));
timeb->millitm=0;
timeb->timezone=timezone/60; /* Timezone is in seconds! */
timeb->dstflag=daylight;
}
#endif
Gnutar-1.08/getopt.h 600 10 5 5367 4676440240 7343 /* declarations for getopt
Copyright (C) 1989 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */
/* 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;
/* Describe the long-named options requested by the application.
_GETOPT_LONG_OPTIONS is a vector of `struct option' terminated by an
element containing a name which is zero.
The field `has_arg' is:
0 if the option does not take an argument,
1 if the option requires an argument,
2 if the option takes an optional argument.
If the field `flag' is nonzero, 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. */
struct option
{
char *name;
int has_arg;
int *flag;
int val;
};
extern struct option *_getopt_long_options;
/* Name of long-named option actually found.
Only changed when a long-named option is found. */
extern char *_getopt_option_name;
/* The index in GETOPT_LONG_OPTIONS of the long-named option found.
Only valid when a long-named option has been found by the most
recent call to `getopt'. */
extern int option_index;
#if __STDC__
int getopt (int, char **, char *);
int getopt_long (int, char **, char *, struct option *, int *);
#else
int getopt ();
int getopt_long ();
#endif
Gnutar-1.08/tar.c 600 10 5 104654 4704274132 6656 /* Tar -- a tape archiver.
Copyright (C) 1988 Free Software Foundation
GNU tar is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY. No author or distributor accepts responsibility to anyone
for the consequences of using it or for whether it serves any
particular purpose or works at all, unless he says so in writing.
Refer to the GNU tar General Public License for full details.
Everyone is granted permission to copy, modify and redistribute GNU tar,
but only under the conditions described in the GNU tar General Public
License. A copy of this license is supposed to have been given to you
along with GNU tar so you can know your rights and responsibilities. It
should be in a file named COPYING. Among other things, the copyright
notice and this notice must be preserved on all copies.
In other words, go ahead and share GNU tar, but don't try to stop
anyone else from sharing it farther. Help stamp out software hoarding!
*/
/*
* A tar (tape archiver) program.
*
* Written by John Gilmore, ihnp4!hoptoad!gnu, starting 25 Aug 85.
*
* @(#)tar.c 1.34 11/6/87 - gnu
*/
#include
#include
#include
#include "getopt.h"
#ifdef USG
#define rindex strrchr
#endif
#ifdef BSD42
#include
#else
#ifdef MSDOS
#include
#else
#ifdef USG
#ifdef NDIR
#include
#else
#include
#endif
#ifndef DIRECT
#define direct dirent
#endif
#define DP_NAMELEN(x) strlen((x)->d_name)
#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
#define direct dirent
#endif
#endif
#endif
#ifdef COHERENT
#define DP_NAMELEN(x) strlen((x)->d_name)
static char *ggg_buf; /* for getwd() */
#endif
#ifndef DP_NAMELEN
#define DP_NAMELEN(x) (x)->d_namlen
#endif
extern char *malloc();
extern char *getenv();
extern char *strncpy();
extern char *index();
extern char *strcpy(); /* JF */
extern char *strcat(); /* JF */
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 diff_archive();
extern void create_archive();
extern void update_archive();
extern void junk_archive();
/* JF */
extern time_t getdate();
#ifdef __GNU__
extern void *init_buffer();
#else
extern char *init_buffer();
#endif
extern char *get_buffer();
extern void add_buffer();
extern void flush_buffer();
time_t new_time;
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();
void options();
#ifndef S_IFLNK
#define lstat stat
#endif
#ifndef DEFBLOCKING
#define DEFBLOCKING 20
#endif
#ifndef DEF_AR_FILE
#define DEF_AR_FILE "tar.out"
#endif
/* For long options that unconditionally set a single flag, we have getopt
do it. For the others, we share the code for the equivalent short
named option, the name of which is stored in the otherwise-unused `val'
field of the `struct option'; for long options that have no equivalent
short option, we use nongraphic characters as pseudo short option
characters, starting (for no particular reason) with character 10. */
struct option long_options[] =
{
{"create", 0, 0, 'c'},
{"append", 1, 0, 'r'},
{"extract", 1, 0, 'x'},
{"get", 1, 0, 'x'},
{"list", 1, 0, 't'},
{"update", 1, 0, 'u'},
{"catenate", 1, 0, 'A'},
{"concatenate", 1, 0, 'A'},
{"read-full-blocks", 0, &f_reblock, 1},
/* {"directory", 1, 0, 'C'}, */
{"newer", 0, 0, 13}, /* JF */
{"newer-mtime", 1, 0, 'N'}, /* JF */
{"delete", 1, 0, 'D'},
{"incremental", 0, 0, 'G'},
{"starting-file", 1, 0, 'K'},
{"multi-volume", 0, &f_multivol, 1},
{"after-date", 1, 0, 'N'},
{"to-stdout", 0, &f_exstdout, 1},
{"record-number", 0, &f_sayblock, 1},
{"files-from", 1, 0, 'T'},
{"volume", 1, 0, 'V'},
{"verify", 0, &f_verify, 1},
{"exclude", 1, 0, 'X'},
{"block-size", 1, 0, 'b'},
{"compare", 0, 0, 'd'},
{"diff", 0, 0, 'd'},
{"file", 1, 0, 'f'},
{"dereference", 0, &f_follow_links, 1},
{"ignore-zeros", 0, &f_ignorez, 1},
{"keep-old-files", 0, 0, 'k'},
{"one-file-system", 0, &f_local_filesys, 1},
{"modification-time", 0, &f_modified, 1},
{"old-archive", 0, 0, 'o'},
{"old", 0, 0, 'o'},
{"portability", 0, 0, 'o'},
{"same-permissions", 0, &f_use_protection, 1},
{"preserve-permissions",0, &f_use_protection, 1},
{"same-order", 0, &f_sorted_names, 1},
{"preserve-order", 0, &f_sorted_names, 1},
{"preserve", 0, 0, 10},
{"verbose", 0, &f_verbose, 1},
{"interactive", 0, &f_confirm, 1},
{"uncompress", 0, &f_compress, 1},
{"compress", 0, &f_compress, 1},
{"compress-block", 0, &f_compress, 2},
{"info-script", 1, &f_run_script_at_end, 1},
{"absolute-paths", 0, &f_absolute_paths, 1},
{"version", 0, 0, 11},
{"help", 0, 0, 12},
{0, 0, 0, 0}
};
/*
* Main routine for tar.
*/
main(argc, argv)
int argc;
char **argv;
{
tar = argv[0]; /* JF: was "tar" Set program name */
options(argc, argv);
name_init(argc, argv);
switch(cmd_mode) {
case CMD_CAT:
case CMD_UPDATE:
case CMD_APPEND:
update_archive();
break;
case CMD_DELETE:
junk_archive();
break;
case CMD_CREATE:
create_archive();
break;
case CMD_EXTRACT:
extr_init();
read_and(extract_archive);
break;
case CMD_LIST:
read_and(list_archive);
break;
case CMD_DIFF:
diff_init();
read_and(diff_archive);
break;
case CMD_NONE:
fprintf (stderr,"%s: you must specify exactly one of the r, c, t, x, or d options\n", tar);
fprintf (stderr,"For more information, type ``%s +help''.\n",tar);
exit(EX_ARGSBAD);
}
exit(0);
/* NOTREACHED */
}
/*
* Parse the options for tar.
*/
void
options(argc, argv)
int argc;
char **argv;
{
register int c; /* Option letter */
int ind = -1;
extern char version_string[];
/* Set default option values */
blocking = DEFBLOCKING; /* From Makefile */
ar_file = getenv("TAPE"); /* From environment, or */
if (ar_file == 0)
ar_file = DEF_AR_FILE; /* From Makefile */
/* Parse options */
while ((c = getoldopt(argc, argv,
"01234567Ab:BcC:dDf:F:GhikK:lmMN:oOpPrRsStT:uvV:wWxX:zZ",
long_options, &ind)) != EOF) {
if (c == 0 && long_options[ind].flag == 0)
c = long_options[ind].val;
switch (c) {
case 0: /* long options that set a single flag */
break;
case 10: /* preserve */
f_use_protection = f_sorted_names = 1;
break;
case 11: /* version */
fprintf(stderr,"%s\n",version_string);
break;
case 12: /* help */
fprintf(stderr,"This is GNU tar, the tape archiving program.\n");
describe();
exit(1);
case 13:
f_new_files++;
goto get_newer;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
{
/* JF this'll have to be modified for other
systems, of course! */
#ifndef LOW_NUM
#define LOW_NUM 0
#define MID_NUM 8
#define HGH_NUM 16
#endif
int d,add;
static char buf[50];
d=getoldopt(argc,argv,"lmh");
if(d=='l') add=LOW_NUM;
else if(d=='m') add=MID_NUM;
else if(d=='h') add=HGH_NUM;
else goto badopt;
sprintf(buf,"/dev/rmt%d",add+c-'0');
ar_file=buf;
}
break;
case 'A': /* Arguments are tar files,
just cat them onto the end
of the archive. */
if(cmd_mode!=CMD_NONE)
goto badopt;
cmd_mode=CMD_CAT;
break;
case 'b': /* Set blocking factor */
blocking = intconv(optarg);
break;
case 'B': /* Try to reblock input */
f_reblock++; /* For reading 4.2BSD pipes */
break;
case 'c': /* Create an archive */
if(cmd_mode!=CMD_NONE)
goto badopt;
cmd_mode=CMD_CREATE;
break;
case 'C':
if(chdir(optarg)<0)
msg_perror("Can't change directory to %d",optarg);
break;
case 'd': /* Find difference tape/disk */
if(cmd_mode!=CMD_NONE)
goto badopt;
cmd_mode=CMD_DIFF;
break;
case 'D': /* Delete in the archive */
if(cmd_mode!=CMD_NONE)
goto badopt;
cmd_mode=CMD_DELETE;
break;
case 'f': /* Use ar_file for the archive */
ar_file = optarg;
break;
case 'F':
/* Since -F is only useful with -M , make it implied */
f_run_script_at_end++; /* run this script at the end */
info_script = optarg; /* of each tape */
f_multivol++;
break;
case 'G': /* We are making a GNU dump; save
directories at the beginning of
the archive, and include in each
directory its contents */
if(f_oldarch)
goto badopt;
f_gnudump++;
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 */
#ifdef NO_OPEN3
fprintf(stderr,
"%s: can't do -k option on this system\n",tar);
exit(EX_ARGSBAD);
#else
f_keep++;
#endif
break;
case 'K':
f_startfile++;
addname(optarg);
break;
case 'l': /* When dumping directories, don't
dump files/subdirectories that are
on other filesystems. */
f_local_filesys++;
break;
case 'm':
f_modified++;
break;
case 'M': /* Make Multivolume archive:
When we can't write any more
into the archive, re-open it,
and continue writing */
f_multivol++;
break;
case 'N': /* Only write files newer than X */
get_newer:
f_new_files++;
new_time=getdate(optarg,(struct timeb *)0);
break;
case 'o': /* Generate old archive */
if(f_gnudump /* || f_dironly */)
goto badopt;
f_oldarch++;
break;
case 'O':
f_exstdout++;
break;
case 'p':
f_use_protection++;
break;
case 'P':
f_absolute_paths++;
break;
case 'r': /* Append files to the archive */
if(cmd_mode!=CMD_NONE)
goto badopt;
cmd_mode=CMD_APPEND;
break;
case 'R':
f_sayblock++; /* Print block #s for debug */
break; /* of bad tar archives */
case 's':
f_sorted_names++; /* Names to extr are sorted */
break;
case 'S': /* deal with sparse files */
f_sparse_files++;
break;
case 't':
if(cmd_mode!=CMD_NONE)
goto badopt;
cmd_mode=CMD_LIST;
f_verbose++; /* "t" output == "cv" or "xv" */
break;
case 'T':
name_file = optarg;
f_namefile++;
break;
case 'u': /* Append files to the archive that
aren't there, or are newer than the
copy in the archive */
if(cmd_mode!=CMD_NONE)
goto badopt;
cmd_mode=CMD_UPDATE;
break;
case 'v':
f_verbose++;
break;
case 'V':
f_volhdr=optarg;
break;
case 'w':
f_confirm++;
break;
case 'W':
f_verify++;
break;
case 'x': /* Extract files from the archive */
if(cmd_mode!=CMD_NONE)
goto badopt;
cmd_mode=CMD_EXTRACT;
break;
case 'X':
f_exclude++;
add_exclude(optarg);
break;
case 'z': /* Easy to type */
case 'Z': /* Like the filename extension .Z */
f_compress++;
break;
case '?':
badopt:
fprintf(stderr, "%s: bad option. Use '%s +help' for a complete list of options.\n", tar, tar);
exit(EX_ARGSBAD);
}
}
blocksize = blocking * RECORDSIZE;
}
/*
* Print as much help as the user's gonna get.
*
* We have to sprinkle in the KLUDGE lines because too many compilers
* cannot handle character strings longer than about 512 bytes. Yuk!
* In particular, MSDOS and Xenix MSC and PDP-11 V7 Unix have this
* problem.
*/
void
describe()
{
fprintf(stderr,"%s: choose one of the following:\n",tar);
fputs("\
-A, +catenate append tar files to an archive\n\
-c, +create create a new archive\n\
-d, +compare find differences between archive and file system\n\
-D, +delete delete from the archive (not for use on mag tapes!)\n\
-r, +append append files to the end of an archive\n\
-t, +list list the contents of an archive\n\
-u, +update only append files that are newer than copy in archive\n\
-x, +extract extract files from an archive\n",stderr);
fputs("\
Other options:\n\
-b, +block-size N block size of Nx512 bytes\n\
-B, +read-full-blocks reblock as we read (for reading 4.2BSD pipes)\n\
-C, +directory dir change to directory DIR\n\
", stderr); /* KLUDGE */ fputs("\
-f, +file F use archive file or device F (or hostname:/dev/file)\n\
-G, +incremental create/list/extract GNU-format incremental backup\n\
-h, +dereference don't dump symlinks; dump the files they point to\n\
-i, +ignore-zeros ignore blocks of zeros in archive (normally mean EOF)\n\
-k, +keep-old-files keep existing files; don't overwrite them from archive\n\
-K, +starting-file file begin at FILE in the archive\n\
-l, +one-file-system stay in local file system when creating an archive\n\
", stderr); /* KLUDGE */ fputs("\
-m, +modification-time don't extract file modified time\n\
-M, +multi-volume create/list/extract multi-volume archive\n\
-N, +after-date date only store files newer than DATE\n\
-o, +old-archive write a V7 format archive, rather than ANSI format\n\
-O, +to-stdout extract files to standard output\n\
-p, +same-permissions extract all protection information\n\
-P, +absolute-paths don't strip leading \"/\"es from file names\n\
+preserve like -p -s\n\
-R, +record-number show record number within archive with each message\n\
-s, +same-order list of names to extract is sorted to match archive\n\
-S, +sparse-file handle sparse files specially\n\
", stderr); /* KLUDGE */ fputs("\
-T, +files-from F get names to extract or create from file F\n\
-v, +verbose verbosely list files processed\n\
-V, +volume vnam create archive with volume name VNAM\n\
+version print tar program version number\n\
-w, +interactive ask for confirmation for every action\n\
-W, +verify attempt to verify the archive after writing it\n\
-X, +exclude file exclude files listed in FILE\n\
-z,-Z,+compress filter the archive through compress\n\
-[0-7][lmh] specify drive and density\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) {
msg_perror("can't open file %s",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.
*/
/* C is non-zero if we should deal with -C */
char *
name_next(c)
{
static char buffer[NAMSIZ+2]; /* Holding pattern */
register char *p;
register char *q;
extern char *un_quote_string();
tryagain:
if (namef == NULL) {
/* Names come from argv, after options */
if (optind < n_argc) {
/* JF trivial support for -C option. I don't know if
chdir'ing at this point is dangerous or not.
It seems to work, which is all I ask. */
if(c && n_argv[optind][0]=='-' && n_argv[optind][1]=='C' && n_argv[optind][2]=='\0' && n_argv[optind+1]!=0) {
optind++;
if(chdir(n_argv[optind])<0) {
msg_perror("Can't chdir to %s",n_argv[optind]);
}
optind++;
if(optind>=n_argc)
return (char *)NULL;
}
/* End of JF quick -C hack */
if(f_exclude && check_exclude(n_argv[optind])) {
optind++;
goto tryagain;
}
return un_quote_string(n_argv[optind++]);
}
return (char *)NULL;
}
while(p = fgets(buffer, NAMSIZ+1 /*nl*/, namef)) {
q = p+strlen(p)-1; /* Find the newline */
if (q <= p) /* Ignore empty lines */
continue;
*q-- = '\0'; /* Zap the newline */
while (q > p && *q == '/') /* Zap trailing /s */
*q-- = '\0';
if(f_exclude && check_exclude(p))
goto tryagain;
return un_quote_string(p);
}
return NULL;
}
/*
* 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 */
static char *chdir_name;
if (f_sorted_names) {
p = name_next(0);
if (p) {
if(*p=='-' && p[1]=='C' && p[2]=='\0') {
chdir_name=name_next(0);
p=name_next(0);
if(!p) {
fprintf(stderr,"Missing file name after -C\n");
exit(EX_ARGSBAD);
}
namebuf[0].change_dir=chdir_name;
}
namebuf[0].length = strlen(p);
if (namebuf[0].length >= sizeof namebuf[0].name) {
fprintf(stderr, "Argument name too long: %s\n",
p);
namebuf[0].length = (sizeof namebuf[0].name) - 1;
}
strncpy(namebuf[0].name, p, namebuf[0].length);
namebuf[0].name[ namebuf[0].length ] = 0;
namebuf[0].next = (struct name *)NULL;
namebuf[0].found = 0;
namelist = namebuf;
namelast = namelist;
}
return;
}
/* Non sorted names -- read them all in */
while (p = name_next(0))
addname(p);
}
/*
* 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 */
static char *chdir_name;
char *new_name();
#define MAXPATHLEN 1024
if(name[0]=='-' && name[1]=='C' && name[2]=='\0') {
chdir_name=name_next(0);
name=name_next(0);
if(!name) {
fprintf(stderr,"Missing file name after -C\n");
exit(EX_ARGSBAD);
}
if(chdir_name[0]!='/') {
char path[MAXPATHLEN];
#if defined(MSDOS) || defined(USG)
int getcwd();
if(!getcwd(path,MAXPATHLEN))
fprintf(stderr,"Couldn't get current dir\n");
exit(EX_SYSTEM);
#elif defined(COHERENT)
char *getwd();
if (!(ggg_buf = getwd())) {
fprintf(stderr,"Couldn't get current directory.\n");
exit(EX_SYSTEM);
}
strncpy(path,ggg_buf,MAXPATHLEN);
#else /* bsd, etc. */
char *getwd();
if(!getwd(path)) {
fprintf(stderr,"Couldn't get current dir: %s\n",path);
exit(EX_SYSTEM);
}
#endif
chdir_name=new_name(path,chdir_name);
}
}
i = strlen(name);
/*NOSTRICT*/
p = (struct name *)
malloc((unsigned)(i + sizeof(struct name) - NAMSIZ));
if (!p) {
fprintf(stderr,"tar: cannot allocate mem for name %s\n",name);
exit(EX_SYSTEM);
}
p->next = (struct name *)NULL;
p->length = i;
strncpy(p->name, name, i);
p->name[i] = '\0'; /* Null term */
p->found = 0;
p->regexp = 0; /* Assume not a regular expression */
p->firstch = 1; /* Assume first char is literal */
p->change_dir=chdir_name;
p->dir_contents = 0; /* JF */
if (index(name, '*') || index(name, '[') || index(name, '?')) {
p->regexp = 1; /* No, it's a regexp */
if (name[0] == '*' || name[0] == '[' || name[0] == '?')
p->firstch = 0; /* Not even 1st char literal */
}
if (namelast) namelast->next = p;
namelast = p;
if (!namelist) namelist = p;
}
add_dir_to_name(name,dirc)
char *name;
char *dirc;
{
struct name *n;
for(n=namelist;n;n=n->next) {
if(!strcmp(n->name,name)) {
n->dir_contents = dirc;
return;
}
}
}
/*
* Match a name from an archive, p, with a name from the namelist.
*/
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 first chars don't match, quick skip */
if (nlp->firstch && nlp->name[0] != p[0])
continue;
/* Regular expressions */
if (nlp->regexp) {
if (wildmat(p, nlp->name)) {
nlp->found = 1; /* Remember it matched */
if(f_startfile) {
free(namelist);
namelist=0;
}
if(nlp->change_dir && chdir(nlp->change_dir))
msg_perror("Can't change to directory %s\n",nlp->change_dir);
return 1; /* We got a match */
}
continue;
}
/* Plain Old Strings */
if (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 */
if(f_startfile) {
free(namelist);
namelist = 0;
}
if(nlp->change_dir && chdir(nlp->change_dir))
msg_perror("Can't change to directory %s\n",nlp->change_dir);
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
}
namelist = (struct name *)NULL;
namelast = (struct name *)NULL;
if (f_sorted_names) {
while (0 != (p = name_next(1)))
fprintf(stderr, "tar: %s not found in archive\n", p);
}
}
/* These next routines were created by JF */
name_expand()
{
;
}
/* p is a directory. Add all the files in P to the namelist. If any of the
files is a directory, recurse on the subdirectory. . . */
static
add_dir_name(p,device)
char *p;
int device;
{
char *new_buf;
char *p_buf;
char *get_dir_contents();
new_buf=get_dir_contents(p,device);
add_dir_to_name(p,new_buf);
{
char namebuf[NAMSIZ+2];
register int len;
(void)strcpy(namebuf,p);
len=strlen(namebuf);
if(namebuf[len-1]!='/') {
namebuf[len++]='/';
namebuf[len]='\0';
}
for(p_buf=new_buf;*p_buf;p_buf+=strlen(p_buf)+1) {
if(*p_buf=='D') {
(void)strcpy(namebuf+len,p_buf+1);
addname(namebuf);
add_dir_name(namebuf,device);
}
}
}
}
char *
get_dir_contents(p,device)
char *p;
int device;
{
register DIR *dirp;
register struct direct *d;
char namebuf[NAMSIZ+2];
register int len;
extern int errno;
#ifdef __GNU__
void *the_buffer;
#else
char *the_buffer;
#endif
char *buf,*p_buf;
char **vec,**p_vec;
int n_strs,n_size;
char *new_buf;
int dirent_cmp();
if(f_local_filesys && device<0) {
struct stat hs;
if (0 != f_follow_links ? stat(p, &hs) : lstat(p, &hs))
msg_perror("can't stat %s",namebuf);
else
device=hs.st_dev;
}
errno=0;
dirp=opendir(p);
if(!dirp) {
if(errno)
msg_perror("can't open directory %s",p);
else
msg("error opening directory %s",p);
return "\0\0\0\0";
}
(void) strcpy(namebuf,p);
if(p[strlen(p)-1]!='/')
(void) strcat(namebuf,"/");
len=strlen(namebuf);
the_buffer=init_buffer();
while(d=readdir(dirp)) {
struct stat hs;
/* Skip . and .. */
if(is_dot_or_dotdot(d->d_name))
continue;
if(DP_NAMELEN(d) + len >=NAMSIZ) {
msg("%s%s name too long: skipped",namebuf,d->d_name);
continue;
}
(void) strcpy(namebuf+len,d->d_name);
if (0 != f_follow_links? stat(namebuf, &hs): lstat(namebuf, &hs)) {
msg_perror("can't stat %s",namebuf);
continue;
}
if((f_new_files && new_time>hs.st_mtime && (hs.st_mode&S_IFMT)!=S_IFDIR
&& (f_new_files>1 || new_time>hs.st_ctime) )
|| (f_local_filesys && device>=0 && device!=hs.st_dev)
|| (f_exclude && check_exclude(namebuf)))
add_buffer(the_buffer,"N",1);
else if((hs.st_mode&S_IFMT)==S_IFDIR)
add_buffer(the_buffer,"D",1);
else
add_buffer(the_buffer,"Y",1);
add_buffer(the_buffer,d->d_name,(int)(DP_NAMELEN(d)+1));
}
add_buffer(the_buffer,"\000\000",2);
closedir(dirp);
/* Well, we've read in the contents of the dir, now sort them */
buf=get_buffer(the_buffer);
n_strs=0;
n_size=0;
for(p_buf=buf;*p_buf;) {
int tmp;
tmp=strlen(p_buf)+1;
n_strs++;
n_size+=tmp;
p_buf+=tmp;
}
if(n_strs==0) {
flush_buffer(the_buffer);
return "\0\0\0\0";
}
vec=(char **)malloc(sizeof(char *)*(n_strs+1));
for(p_vec=vec,p_buf=buf;*p_buf;p_buf+=strlen(p_buf)+1)
*p_vec++= p_buf;
*p_vec= 0;
qsort(vec,n_strs,sizeof(char *),dirent_cmp);
new_buf=(char *)malloc(n_size+2);
for(p_vec=vec,p_buf=new_buf;*p_vec;p_vec++) {
char *p_tmp;
for(p_tmp= *p_vec;*p_buf++= *p_tmp++;)
;
}
*p_buf++='\0';
free(vec);
flush_buffer(the_buffer);
return new_buf;
}
int dirent_cmp(p1,p2)
char **p1,**p2;
{
char *frst,*scnd;
frst= (*p1)+1;
scnd= (*p2)+1;
return strcmp(frst,scnd);
}
/* This is like name_match(), except that it returns a pointer to the name
it matched, and doesn't set ->found The caller will have to do that
if it wants to. Oh, and if the namelist is empty, it returns 0, unlike
name_match(), which returns TRUE */
struct name *
name_scan(p)
register char *p;
{
register struct name *nlp;
register int len;
again:
if (0 == (nlp = namelist)) /* Empty namelist is easy */
return 0;
len = strlen(p);
for (; nlp != 0; nlp = nlp->next) {
/* If first chars don't match, quick skip */
if (nlp->firstch && nlp->name[0] != p[0])
continue;
/* Regular expressions */
if (nlp->regexp) {
if (wildmat(p, nlp->name))
return nlp; /* We got a match */
continue;
}
/* Plain Old Strings */
if (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 */
return nlp; /* 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 (struct name *) 0;
}
/* This returns a name from the namelist which doesn't have ->found set.
It sets ->found before returning, so successive calls will find and return
all the non-found names in the namelist */
struct name *gnu_list_name;
char *
name_from_list()
{
if(!gnu_list_name)
gnu_list_name = namelist;
while(gnu_list_name && gnu_list_name->found)
gnu_list_name=gnu_list_name->next;
if(gnu_list_name) {
gnu_list_name->found++;
if(gnu_list_name->change_dir)
if(chdir(gnu_list_name->change_dir)<0)
msg_perror("can't chdir to %s",gnu_list_name->change_dir);
return gnu_list_name->name;
}
return (char *)0;
}
blank_name_list()
{
struct name *n;
gnu_list_name = 0;
for(n=namelist;n;n=n->next)
n->found = 0;
}
/* Collect all the names from argv[] (or whatever), then expand them into
a directory tree, and put all the directories at the beginning. */
collect_and_sort_names()
{
struct name *n,*n_next;
int num_names;
int name_cmp();
char *merge_sort();
name_gather();
if(!namelist) addname(".");
for(n=namelist;n;n=n_next) {
n_next=n->next;
if(n->found || n->dir_contents)
continue;
if(n->regexp) /* FIXME just skip regexps for now */
continue;
if(n->change_dir)
if(chdir(n->change_dir)<0) {
msg_perror("can't chdir to %s",n->change_dir);
continue;
}
if(is_a_directory(n->name)) {
n->found++;
add_dir_name(n->name,-1);
}
}
num_names=0;
for(n=namelist;n;n=n->next)
num_names++;
namelist=(struct name *)merge_sort(namelist,num_names,(char *)(&(namelist->next))-(char *)namelist,name_cmp);
for(n=namelist;n;n=n->next) {
n->found=0;
}
}
int name_cmp(n1,n2)
struct name *n1,*n2;
{
if(n1->found) {
if(n2->found)
return strcmp(n1->name,n2->name);
else
return -1;
} else if(n2->found)
return 1;
else
return strcmp(n1->name,n2->name);
}
char *new_name();
gnu_restore(name)
char *name;
{
char *current_dir;
/* int current_dir_length; */
char *archive_dir;
/* int archive_dir_length; */
#ifdef __GNU__
void *the_buffer;
#else
char *the_buffer;
#endif
char *p;
DIR *dirp;
struct direct *d;
char *cur,*arc;
extern struct stat hstat; /* Stat struct corresponding */
long size,copied;
char *from,*to;
dirp=opendir(name);
if(!dirp) {
/* The directory doesn't exist now. It'll be created.
In any case, we don't have to delete any files out
of it */
return;
}
the_buffer=init_buffer();
while(d=readdir(dirp)) {
if(is_dot_or_dotdot(d->d_name))
continue;
add_buffer(the_buffer,d->d_name,(int)(DP_NAMELEN(d)+1));
}
closedir(dirp);
add_buffer(the_buffer,"",1);
current_dir=get_buffer(the_buffer);
archive_dir=(char *)malloc(hstat.st_size);
if(archive_dir==0) {
fprintf(stderr,"Can't allocate %d bytes for restore\n",hstat.st_size);
return;
}
to=archive_dir;
for(size=hstat.st_size;size>0;size-=copied) {
from=findrec()->charptr;
if(!from) {
msg("Unexpected EOF in archive\n");
break;
}
copied=endofrecs()->charptr - from;
if(copied>size)
copied=size;
bcopy(from,to,(int)copied);
to+=copied;
userec((union record *)(from+copied-1));
}
for(cur=current_dir;*cur;cur+=strlen(cur)+1) {
for(arc=archive_dir;*arc;arc+=strlen(arc)+1) {
arc++;
if(!strcmp(arc,cur))
break;
}
if(*arc=='\0') {
p=new_name(name,cur);
if(f_confirm && !confirm("delete",p)) {
free(p);
continue;
}
if(f_verbose)
printf("%s: deleting %s\n",p);
if(recursively_delete(p)) {
msg("%s: Error while deleting\n",p);
}
free(p);
}
}
flush_buffer(the_buffer);
free(archive_dir);
}
recursively_delete(path)
char *path;
{
struct stat sbuf;
DIR *dirp;
struct direct *dp;
char *path_buf;
/* int path_len; */
if(lstat(path,&sbuf)<0)
return 1;
if((sbuf.st_mode &S_IFMT)==S_IFDIR) {
/* path_len=strlen(path); */
dirp=opendir(path);
if(dirp==0)
return 1;
while(dp=readdir(dirp)) {
if(is_dot_or_dotdot(dp->d_name))
continue;
path_buf=new_name(path,dp->d_name);
if(recursively_delete(path_buf)) {
free(path_buf);
closedir(dirp);
return 1;
}
free(path_buf);
}
closedir(dirp);
if(rmdir(path)<0)
return 1;
return 0;
}
if(unlink(path)<0)
return 1;
return 0;
}
char *
new_name(path,name)
char *path,*name;
{
char *path_buf;
path_buf=(char *)malloc(strlen(path)+strlen(name)+2);
if(path_buf==0) {
fprintf(stderr,"Can't allocate memory for name '%s/%s\n",path,name);
exit(EX_SYSTEM);
}
(void) sprintf(path_buf,"%s/%s",path,name);
return path_buf;
}
/* return non-zero if p is the name of a directory */
is_a_directory(p)
char *p;
{
struct stat stbuf;
if(lstat(p,&stbuf)<0) {
msg_perror("can't stat %s",p);
return 0;
}
if((stbuf.st_mode&S_IFMT)==S_IFDIR)
return 1;
return 0;
}
/* Returns non-zero if p is . or .. This could be a macro for speed. */
is_dot_or_dotdot(p)
char *p;
{
return (p[0]=='.' && (p[1]=='\0' || (p[1]=='.' && p[2]=='\0')));
}
/* returns non-zero if the luser typed 'y' or 'Y', zero otherwise. */
int
confirm(action,file)
char *action, *file;
{
int c,nl;
static FILE *confirm_file = 0;
extern FILE *msg_file;
extern char TTY_NAME[];
fprintf(msg_file,"%s %s?", action, file);
fflush(msg_file);
if(!confirm_file) {
confirm_file = (archive == 0) ? fopen(TTY_NAME, "r") : stdin;
if(!confirm_file) {
fprintf(stderr,"Can't read confirmation from user\n");
exit(EX_SYSTEM);
}
}
c=getc(confirm_file);
for(nl = c; nl != '\n' && nl != EOF; nl = getc(confirm_file))
;
return (c=='y' || c=='Y');
}
char *x_buffer = 0;
int size_x_buffer;
int free_x_buffer;
char **exclude = 0;
int size_exclude = 0;
int free_exclude = 0;
char **re_exclude = 0;
int size_re_exclude = 0;
int free_re_exclude = 0;
add_exclude(file)
char *file;
{
FILE *fp;
char buf[1024];
extern char *rindex();
if(strcmp(file, "-"))
fp=fopen(file,"r");
else
/* Let's hope the person knows what they're doing. */
/* Using -X - -T - -f - will get you *REALLY* strange
results. . . */
fp=stdin;
if(!fp) {
msg_perror("can't open %s",file);
exit(2);
}
while(fgets(buf,1024,fp)) {
int size_buf;
char *end_str;
end_str=rindex(buf,'\n');
*end_str='\0';
un_quote_string(buf);
size_buf = strlen(buf);
if(x_buffer==0) {
x_buffer = (char *)ck_malloc(size_buf+1024);
free_x_buffer=1024;
} else if(free_x_buffer<=size_buf) {
char *old_x_buffer;
char **tmp_ptr;
old_x_buffer = x_buffer;
x_buffer = (char *)ck_realloc(x_buffer,size_x_buffer+1024);
free_x_buffer = 1024;
for(tmp_ptr=exclude;tmp_ptr
for(tmp_ptr=re_exclude;tmp_ptr
}
if(is_regex(buf)) {
if(free_re_exclude==0) {
re_exclude= (char **)(re_exclude ? ck_realloc(re_exclude,(size_re_exclude+32)*sizeof(char *)) : ck_malloc(sizeof(char *)*32));
free_re_exclude+=32;
}
re_exclude[size_re_exclude]=x_buffer+size_x_buffer;
size_re_exclude++;
free_re_exclude--;
} else {
if(free_exclude==0) {
exclude=(char **)(exclude ? ck_realloc(exclude,(size_exclude+32)*sizeof(char *)) : ck_malloc(sizeof(char *)*32));
free_exclude+=32;
}
exclude[size_exclude]=x_buffer+size_x_buffer;
size_exclude++;
free_exclude--;
}
strcpy(x_buffer+size_x_buffer,buf);
size_x_buffer+=size_buf+1;
free_x_buffer-=size_buf+1;
}
fclose(fp);
}
int
is_regex(str)
char *str;
{
return index(str,'*') || index(str,'[') || index(str,'?');
}
/* Returns non-zero if the file 'name' should not be added/extracted */
int
check_exclude(name)
char *name;
{
int n;
for(n=0;n
return 1;
}
for(n=0;n
return 1;
}
return 0;
}
Gnutar-1.08/create.c 600 10 5 67273 4704274102 7315 /* Create a tar archive.
Copyright (C) 1988 Free Software Foundation
This file is part of GNU Tar.
GNU Tar 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 1, or (at your option)
any later version.
GNU Tar 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 Tar; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
/*
* Create a tar archive.
*
* Written 25 Aug 1985 by John Gilmore, ihnp4!hoptoad!gnu.
*
* @(#)create.c 1.36 11/6/87 - gnu
*/
#include
#include
/*#include
#include
/* JF: this one is my fault */
/* #include "utils.h" */
#ifdef V7
#elif defined(COHERENT)
#include
#else
#include
#endif
#ifndef MSDOS
#include
#include
#endif
#ifdef BSD42
#include
#else
#ifdef MSDOS
#include
#else
#ifdef USG
#include "dirent.h"
#define direct dirent
#define DP_NAMELEN(x) strlen((x)->d_name)
#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
#define direct dirent
#endif
#endif
#endif
#ifdef COHERENT
#define DP_NAMELEN(x) strlen((x)->d_name)
#endif
#ifndef DP_NAMELEN
#define DP_NAMELEN(x) (x)->d_namlen
#endif
#ifdef USG
#include
#endif
/*
* V7 doesn't have a #define for this.
*/
#ifndef O_RDONLY
#define O_RDONLY 0
#endif
/*
* Most people don't have a #define for this.
*/
#ifndef O_BINARY
#define O_BINARY 0
#endif
#include "tar.h"
#include "port.h"
extern union record *head; /* Points to current tape header */
extern struct stat hstat; /* Stat struct corresponding */
extern int head_standard; /* Tape header is in ANSI format */
/* JF */
extern struct name *gnu_list_name;
/*
* 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 void bzero();
extern void bcopy();
extern int errno;
extern void print_header();
union record *start_header();
void finish_header();
void finduname();
void findgname();
char *name_next();
void to_oct();
void dump_file();
static nolinks; /* Gets set if we run out of RAM */
/*
* "Scratch" space to store the information about a sparse file before
* writing the info into the header or extended header
*/
/* struct sp_array *sparsearray;*/
/* number of elts storable in the sparsearray */
/*int sparse_array_size = 10;*/
void
create_archive()
{
register char *p;
char *name_from_list();
open_archive(0); /* Open for writing */
if(f_gnudump) {
char buf[MAXNAMLEN],*q,*bufp;
collect_and_sort_names();
while(p=name_from_list())
dump_file(p,-1);
/* if(!f_dironly) { */
blank_name_list();
while(p=name_from_list()) {
strcpy(buf,p);
if(p[strlen(p)-1]!='/')
strcat(buf,"/");
bufp=buf+strlen(buf);
for(q=gnu_list_name->dir_contents;*q;q+=strlen(q)+1) {
if(*q=='Y') {
strcpy(bufp,q+1);
dump_file(buf,-1);
}
}
}
/* } */
} else {
while (p = name_next(1)) {
dump_file(p, -1);
}
}
write_eot();
close_archive();
name_close();
}
/*
* Dump a single file. If it's a directory, recurse.
* Result is 1 for success, 0 for failure.
* Sets global "hstat" to stat() output for this file.
*/
void
dump_file (p, curdev)
char *p; /* File name to dump */
int curdev; /* Device our parent dir was on */
{
union record *header;
char type;
extern char *save_name; /* JF for multi-volume support */
extern long save_totsize;
extern long save_sizeleft;
union record *exhdr;
char save_linkflag;
extern time_t new_time;
int sparse_ind = 0;
if(f_confirm && !confirm("add",p))
return;
/*
* 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, &hstat): lstat(p, &hstat))
{
badperror:
msg_perror("can't add file %s",p);
badfile:
errors++;
return;
}
/* See if we only want new files, and check if this one is too old to
put in the archive. */
if( f_new_files
&& new_time>hstat.st_mtime
&& (hstat.st_mode&S_IFMT)!=S_IFDIR
&& (f_new_files>1 || new_time>hstat.st_ctime)) {
if(curdev<0) {
msg("%s: is unchanged; not dumped",p);
}
return;
}
/*
* See if we are crossing from one file system to another,
* and avoid doing so if the user only wants to dump one file system.
*/
if (f_local_filesys && curdev >= 0 && curdev != hstat.st_dev) {
msg("%s: is on a different filesystem; not dumped",p);
return;
}
/*
* Check for 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 (hstat.st_nlink > 1) switch (hstat.st_mode & S_IFMT) {
register struct link *lp;
case S_IFREG: /* Regular file */
#ifdef S_IFCTG
case S_IFCTG: /* Contigous file */
#endif
#ifdef S_IFCHR
case S_IFCHR: /* Character special file */
#endif
#ifdef S_IFBLK
case S_IFBLK: /* Block special file */
#endif
#ifdef S_IFIFO
case S_IFIFO: /* Fifo special file */
#endif
/* First quick and dirty. Hashing, etc later FIXME */
for (lp = linklist; lp; lp = lp->next) {
if (lp->ino == hstat.st_ino &&
lp->dev == hstat.st_dev) {
char *link_name = lp->name;
/* We found a link. */
hstat.st_size = 0;
header = start_header(p, &hstat);
if (header == NULL) goto badfile;
while(!f_absolute_paths && *link_name == '/') {
static int link_warn = 0;
if (!link_warn) {
msg("Removing leading / from absolute links");
link_warn++;
}
link_name++;
}
strcpy(header->header.linkname,
link_name);
header->header.linkflag = LF_LINK;
finish_header(header);
/* FIXME: Maybe remove from list after all links found? */
return; /* We dumped it */
}
}
/* Not found. Add it to the list of possible links. */
lp = (struct link *) malloc( (unsigned)
(strlen(p) + sizeof(struct link) - NAMSIZ));
if (!lp) {
if (!nolinks) {
fprintf(stderr,
"tar: no memory for links, they will be dumped as separate files\n");
nolinks++;
}
}
lp->ino = hstat.st_ino;
lp->dev = hstat.st_dev;
strcpy(lp->name, p);
lp->next = linklist;
linklist = lp;
}
/*
* This is not a link to a previously dumped file, so dump it.
*/
switch (hstat.st_mode & S_IFMT) {
case S_IFREG: /* Regular file */
#ifdef S_IFCTG
case S_IFCTG: /* Contiguous file */
#endif
{
int f; /* File descriptor */
long bufsize, count;
long sizeleft;
register union record *start;
int header_moved;
char isextended = 0;
int upperbound;
int end_nulls = 0;
header_moved = 0;
#ifdef BSD42
if (f_sparse_files) {
/*
* JK - This is the test for sparseness: whether the
* "size" of the file matches the number of blocks
* allocated for it. If there is a smaller number
* of blocks that would be necessary to accommodate
* a file of this size, we have a sparse file, i.e.,
* at least one of those records in the file is just
* a useless hole.
*/
if (hstat.st_size - (hstat.st_blocks * RECORDSIZE) > RECORDSIZE) {
int filesize = hstat.st_size;
register int i;
printf("File is sparse: %s\n", p);
header = start_header(p, &hstat);
if (header == NULL)
goto badfile;
header->header.linkflag = LF_SPARSE;
header_moved++;
/*
* Call the routine that figures out the
* layout of the sparse file in question.
* UPPERBOUND is the index of the last
* element of the "sparsearray," i.e.,
* the number of elements it needed to
* describe the file.
*/
upperbound = deal_with_sparse(p, header);
/*
* See if we'll need an extended header
* later
*/
if (upperbound > SPARSE_IN_HDR-1)
header->header.isextended++;
/*
* We store the "real" file size so
* we can show that in case someone wants
* to list the archive, i.e., tar tvf
* It might be kind of disconcerting if the
* shrunken file size was the one that showed
* up.
*/
to_oct((long) hstat.st_size, 1+12,
header->header.realsize);
/*
* This will be the new "size" of the
* file, i.e., the size of the file
* minus the records of holes that we're
* skipping over.
*/
find_new_file_size(&filesize, upperbound);
printf("File %s is now size %d\n",
p, filesize);
hstat.st_size = filesize;
to_oct((long) filesize, 1+12,
header->header.size);
/* to_oct((long) end_nulls, 1+12,
header->header.ending_blanks);*/
for (i = 0; i < SPARSE_IN_HDR; i++) {
if (!sparsearray[i].numbytes)
break;
to_oct(sparsearray[i].offset, 1+12,
header->header.sp[i].offset);
to_oct(sparsearray[i].numbytes, 1+12,
header->header.sp[i].numbytes);
}
}
}
#endif
sizeleft = hstat.st_size;
/* Don't bother opening empty, world readable files. */
if (sizeleft > 0 || 0444 != (0444 & hstat.st_mode)) {
f = open(p, O_RDONLY|O_BINARY);
if (f < 0) goto badperror;
} else {
f = -1;
}
/* If the file is sparse, we've already taken care of this */
if (!header_moved) {
header = start_header(p, &hstat);
if (header == NULL) {
if(f>=0)
(void)close(f);
goto badfile;
}
}
#ifdef S_IFCTG
/* Mark contiguous files, if we support them */
if (f_standard && (hstat.st_mode & S_IFMT) == S_IFCTG) {
header->header.linkflag = LF_CONTIG;
}
#endif
isextended = header->header.isextended;
save_linkflag = header->header.linkflag;
finish_header(header);
if (isextended) {
int sum = 0;
register int i;
/* register union record *exhdr;*/
int arraybound = SPARSE_EXT_HDR;
static int index_offset = SPARSE_IN_HDR;
extend: exhdr = findrec();
if (exhdr == NULL) goto badfile;
bzero(exhdr->charptr, RECORDSIZE);
for (i = 0; i < SPARSE_EXT_HDR; i++) {
if (i+index_offset > upperbound)
break;
to_oct((long) sparsearray[i+index_offset].numbytes,
1+12,
exhdr->ext_hdr.sp[i].numbytes);
to_oct((long) sparsearray[i+index_offset].offset,
1+12,
exhdr->ext_hdr.sp[i].offset);
}
userec(exhdr);
/* sum += i;
if (sum < upperbound)
goto extend;*/
if (index_offset+i < upperbound) {
index_offset += i;
exhdr->ext_hdr.isextended++;
goto extend;
}
}
if (save_linkflag == LF_SPARSE) {
if (finish_sparse_file(f, &sizeleft, hstat.st_size, p))
goto padit;
}
else
while (sizeleft > 0) {
if(f_multivol) {
save_name = p;
save_sizeleft = sizeleft;
save_totsize = hstat.st_size;
}
start = findrec();
bufsize = endofrecs()->charptr - start->charptr;
if (sizeleft < bufsize) {
/* Last read -- zero out area beyond */
bufsize = (int)sizeleft;
count = bufsize % RECORDSIZE;
if (count)
bzero(start->charptr + sizeleft,
(int)(RECORDSIZE - count));
}
count = read(f, start->charptr, bufsize);
if (count < 0) {
msg_perror("read error at byte %ld, reading\
%d bytes, in file %s", hstat.st_size - sizeleft, bufsize,p);
goto padit;
}
sizeleft -= count;
/* This is nonportable (the type of userec's arg). */
userec(start+(count-1)/RECORDSIZE);
if (count == bufsize) continue;
msg( "file %s shrunk by %d bytes, padding with zeros.\n", p, sizeleft);
goto padit; /* Short read */
}
if(f_multivol)
save_name = 0;
if (f >= 0)
(void)close(f);
break;
/*
* File shrunk or gave error, pad out tape to match
* the size we specified in the header.
*/
padit:
while(sizeleft>0) {
save_sizeleft=sizeleft;
start=findrec();
bzero(start->charptr,RECORDSIZE);
userec(start);
sizeleft-=RECORDSIZE;
}
if(f_multivol)
save_name=0;
if(f>=0)
(void)close(f);
break;
/* abort(); */
}
#ifdef S_IFLNK
case S_IFLNK: /* Symbolic link */
{
int size;
hstat.st_size = 0; /* Force 0 size on symlink */
header = start_header(p, &hstat);
if (header == NULL) goto badfile;
size = readlink(p, header->header.linkname, NAMSIZ);
if (size < 0) goto badperror;
if (size == NAMSIZ) {
msg("symbolic link %s too long\n",p);
break;
}
header->header.linkname[size] = '\0';
header->header.linkflag = LF_SYMLINK;
finish_header(header); /* Nothing more to do to it */
}
break;
#endif
case S_IFDIR: /* Directory */
{
register DIR *dirp;
register struct direct *d;
char namebuf[NAMSIZ+2];
register int len;
int our_device = hstat.st_dev;
/* 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 */
namebuf[len] = '\0'; /* Make sure null-terminated */
/*
* Output directory header record with permissions
* FIXME, do this AFTER files, to avoid R/O dir problems?
* If old archive format, don't write record at all.
*/
if (!f_oldarch) {
hstat.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, &hstat);
* but since they'd interpret LF_DIR records as
* regular files, we'd better put the / on the name.
*/
header = start_header(namebuf, &hstat);
if (header == NULL)
goto badfile; /* eg name too long */
if (f_gnudump)
header->header.linkflag = LF_DUMPDIR;
else if (f_standard)
header->header.linkflag = LF_DIR;
/* If we're gnudumping, we aren't done yet so don't close it. */
if(!f_gnudump)
finish_header(header); /* Done with directory header */
}
/* Hack to remove "./" from the front of all the file names */
if (len == 2 && namebuf[0] == '.' && namebuf[1]=='/') {
len = 0;
}
if(f_gnudump) {
int sizeleft;
int totsize;
int bufsize;
union record *start;
int count;
char *buf,*p_buf;
buf=gnu_list_name->dir_contents; /* FOO */
totsize=0;
for(p_buf=buf;*p_buf;) {
int tmp;
tmp=strlen(p_buf)+1;
totsize+=tmp;
p_buf+=tmp;
}
totsize++;
to_oct((long)totsize,1+12,header->header.size);
finish_header(header);
p_buf=buf;
sizeleft=totsize;
while(sizeleft>0) {
if(f_multivol) {
save_name=p;
save_sizeleft=sizeleft;
save_totsize=totsize;
}
start=findrec();
bufsize=endofrecs()->charptr - start->charptr;
if(sizeleft
count=bufsize%RECORDSIZE;
if(count)
bzero(start->charptr+sizeleft,RECORDSIZE-count);
}
bcopy(p_buf,start->charptr,bufsize);
sizeleft-=bufsize;
p_buf+=bufsize;
userec(start+(bufsize-1)/RECORDSIZE);
}
if(f_multivol)
save_name = 0;
break;
}
/* Now output all the files in the directory */
/* if (f_dironly)
break; /* Unless the cmdline said not to */
errno = 0;
dirp = opendir(p);
if (!dirp) {
if (errno) {
msg_perror ("can't open directory %s",p);
} else {
msg("error opening directory %s",
p);
}
break;
}
/* Should speed this up by cd-ing into the dir, FIXME */
while (NULL != (d=readdir(dirp))) {
/* Skip . and .. */
if(is_dot_or_dotdot(d->d_name))
continue;
if (DP_NAMELEN(d) + len >= NAMSIZ) {
msg("file name %s%s too long\n",
namebuf, d->d_name);
continue;
}
strcpy(namebuf+len, d->d_name);
if(f_exclude && check_exclude(namebuf))
continue;
dump_file(namebuf, our_device);
}
closedir(dirp);
}
break;
#ifdef S_IFCHR
case S_IFCHR: /* Character special file */
type = LF_CHR;
goto easy;
#endif
#ifdef S_IFBLK
case S_IFBLK: /* Block special file */
type = LF_BLK;
goto easy;
#endif
#ifdef S_IFIFO
case S_IFIFO: /* Fifo special file */
type = LF_FIFO;
#endif
easy:
if (!f_standard) goto unknown;
hstat.st_size = 0; /* Force 0 size */
header = start_header(p, &hstat);
if (header == NULL) goto badfile; /* eg name too long */
header->header.linkflag = type;
if (type != LF_FIFO) {
to_oct((long) major(hstat.st_rdev), 8,
header->header.devmajor);
to_oct((long) minor(hstat.st_rdev), 8,
header->header.devminor);
}
finish_header(header);
break;
default:
unknown:
msg("%s: Unknown file type; file ignored.\n", p);
break;
}
}
int
finish_sparse_file(fd, sizeleft, fullsize, name)
int fd;
long *sizeleft,
fullsize;
char *name;
{
union record *start;
char tempbuf[RECORDSIZE];
int bufsize,
sparse_ind = 0,
count;
long pos;
while (*sizeleft > 0) {
start = findrec();
bzero(start->charptr, RECORDSIZE);
bufsize = sparsearray[sparse_ind].numbytes;
if (!bufsize) { /* we blew it, maybe */
fprintf(stderr, "Wrote %ld of %ld bytes to file %s\n",
fullsize - *sizeleft, fullsize, name);
break;
}
pos = lseek(fd, sparsearray[sparse_ind++].offset, 0);
/*
* If the number of bytes to be written here exceeds
* the size of the temporary buffer, do it in steps.
*/
while (bufsize > RECORDSIZE) {
/* if (amt_read) {
count = read(fd, start->charptr+amt_read, RECORDSIZE-amt_read);
bufsize -= RECORDSIZE - amt_read;
amt_read = 0;
userec(start);
start = findrec();
bzero(start->charptr, RECORDSIZE);
}*/
/* store the data */
count = read(fd, start->charptr, RECORDSIZE);
if (count < 0) {
msg_perror("read error at byte %ld, reading %d bytes, in file %s",
fullsize - *sizeleft, bufsize, name);
return 1;
}
bufsize -= count;
*sizeleft -= count;
userec(start);
start = findrec();
bzero(start->charptr, RECORDSIZE);
}
clear_buffer(tempbuf);
count = read(fd, tempbuf, bufsize);
bcopy(tempbuf, start->charptr, RECORDSIZE);
if (count < 0) {
msg_perror("read error at byte %ld, reading %d bytes, in file %s",
fullsize - *sizeleft, bufsize, name);
return 1;
}
/* if (amt_read >= RECORDSIZE) {
amt_read = 0;
userec(start+(count-1)/RECORDSIZE);
if (count != bufsize) {
msg("file %s shrunk by %d bytes, padding with zeros.\n", name, sizeleft);
return 1;
}
start = findrec();
} else
amt_read += bufsize;*/
*sizeleft -= count;
userec(start);
}
/* userec(start+(count-1)/RECORDSIZE);*/
return 0;
}
init_sparsearray()
{
register int i;
for (i = 0; i < sp_array_size; i++) {
sparsearray[i].offset = 0;
sparsearray[i].numbytes = 0;
}
}
/*
* Okay, we've got a sparse file on our hands -- now, what we need to do is
* make a pass through the file and carefully note where any data is, i.e.,
* we want to find how far into the file each instance of data is, and how
* many bytes are there. We store this information in the sparsearray,
* which will later be translated into header information. For now, we use
* the sparsearray as convenient storage.
*
* As a side note, this routine is a mess. If I could have found a cleaner
* way to do it, I would have. If anyone wants to find a nicer way to do
* this, feel free.
*/
int
deal_with_sparse(name, header, nulls_at_end)
char *name;
union record *header;
{
long numbytes = 0;
long offset = 0;
long save_offset;
int fd;
int start,
end;
int end_nulls = 0;
int current_size = hstat.st_size;
int sparse_ind = 0,
cc;
char buf[RECORDSIZE];
int read_last_data = 0; /* did we just read the last record? */
int amidst_data = 0;
header->header.isextended = 0;
/*
* Can't open the file -- this problem will be caught later on,
* so just return.
*/
if ((fd = open(name, O_RDONLY)) < 0)
return;
sp_array_size = 10;
/*
* Make room for our scratch space -- initially is 10 elts long
*/
sparsearray = (struct sp_array *) malloc(sp_array_size * sizeof(struct sp_array));
init_sparsearray();
clear_buffer(buf);
while ((cc = read(fd, buf, sizeof buf)) != 0) {
if (sparse_ind > sp_array_size-1) {
/*
* realloc the scratch area, since we've run out of room --
*/
sparsearray = (struct sp_array *)
realloc(sparsearray,
2 * sp_array_size * (sizeof(struct sp_array)));
sp_array_size *= 2;
}
if (cc == sizeof buf) {
if (zero_record(buf)) {
if (amidst_data) {
sparsearray[sparse_ind++].numbytes
= numbytes;
amidst_data = 0;
numbytes = 0;
}
offset += cc;
} else { /* !zero_record(buf) */
if (!amidst_data) {
amidst_data = 1;
where_is_data(&start, &end, buf);
numbytes += end - start;
offset += start;
sparsearray[sparse_ind].offset = offset;
} else
numbytes += cc;
offset += cc;
}
} else if (cc < sizeof buf) {
if (!zero_record(buf)) {
if (!amidst_data) {
amidst_data = 1;
where_is_data(&start, &end, buf);
/* In case there are nulls at the
end that we need to remember */
if (end < cc)
end = cc;
numbytes += start - end;
offset += start;
/* end_nulls = RECORDSIZE - end;*/
} else {
numbytes += cc;
/* end_nulls = RECORDSIZE - end;*/
}
sparsearray[sparse_ind].numbytes = numbytes;
} /* else
end_nulls = cc;*/
}
clear_buffer(buf);
}
if (numbytes)
sparsearray[sparse_ind].numbytes = numbytes;
close(fd);
/* printf("%d\n", end_nulls);
*nulls_at_end = end_nulls;*/
return sparse_ind;
}
/*
* Just zeroes out the buffer so we don't confuse ourselves with leftover
* data.
*/
clear_buffer(buf)
char *buf;
{
register int i;
for (i = 0; i < RECORDSIZE; i++)
buf[i] = '\0';
}
/*
* JK -
* This routine takes a character array, and tells where within that array
* the data can be found. It skips over any zeros, and sets the first
* non-zero point in the array to be the "start", and continues until it
* finds non-data again, which is marked as the "end." This routine is
* mainly for 1) seeing how far into a file we must lseek to data, given
* that we have a sparse file, and 2) determining the "real size" of the
* file, i.e., the number of bytes in the sparse file that are data, as
* opposed to the zeros we are trying to skip.
*/
where_is_data(from, to, buffer)
int *from,
*to;
char *buffer;
{
register int i = 0;
register int save_to = *to;
int amidst_data = 0;
while (!buffer[i])
i++;
*from = i;
if (*from < 16) /* don't bother */
*from = 0;
/* keep going to make sure there isn't more real
data in this record */
while (i < RECORDSIZE) {
if (!buffer[i]) {
if (amidst_data) {
save_to = i;
amidst_data = 0;
}
i++;
}
else if (buffer[i]) {
if (!amidst_data)
amidst_data = 1;
i++;
}
}
if (i == RECORDSIZE)
*to = i;
else
*to = save_to;
}
/*
* Takes a recordful of data and basically cruises through it to see if
* it's made *entirely* of zeros, returning a 0 the instant it finds
* something that is a non-zero, i.e., useful data.
*/
zero_record(buffer)
char *buffer;
{
register int i;
for (i = 0; i < RECORDSIZE; i++)
if (buffer[i] != '\000')
return 0;
return 1;
}
find_new_file_size(filesize, highest_index)
int *filesize;
int highest_index;
{
register int i;
*filesize = 0;
for (i = 0; sparsearray[i].numbytes && i <= highest_index; i++)
*filesize += sparsearray[i].numbytes;
}
/*
* 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 */
/*
* Check the file name and put it in the record.
*/
#ifdef MSDOS
if(name[1]==':') {
static int warned_once = 0;
name+=2;
if(!warned_once++) {
msg("Removing drive spec from names in the archive");
}
}
#endif
while (!f_absolute_paths && '/' == *name) {
static int warned_once = 0;
name++; /* Force relative path */
if (!warned_once++) {
msg("Removing leading / from absolute path names in the archive.");
}
}
strcpy(header->header.name, name);
if (header->header.name[NAMSIZ-1]) {
msg("%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 */
if(f_gnudump) {
to_oct((long) st->st_atime, 1+12, header->header.atime);
to_oct((long) st->st_ctime, 1+12, header->header.ctime);
}
#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.
* We also print the file name and/or full info if verbose is on.
*/
void
finish_header(header)
register union record *header;
{
register int i, sum;
register char *p;
void bcopy();
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);
if (f_verbose) {
/* These globals are parameters to print_header, sigh */
head = header;
/* hstat is already set up */
head_standard = f_standard;
print_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' + (char)(value & 7); /* one octal digit */
value >>= 3;
} while (digs > 0 && value != 0);
/* Leading spaces, if necessary */
while (digs > 0)
where[--digs] = ' ';
}
/*
* Write the EOT record(s).
* We actually zero at least one record, through the end of the block.
* Old tar writes garbage after two zeroed records -- and PDtar used to.
*/
write_eot()
{
union record *p;
int bufsize;
void bzero();
p = findrec();
bufsize = endofrecs()->charptr - p->charptr;
bzero(p->charptr, bufsize);
userec(p);
}
Gnutar-1.08/extract.c 600 10 5 46041 4676046215 7524 /* Extract files from a tar archive.
Copyright (C) 1988 Free Software Foundation
This file is part of GNU Tar.
GNU Tar 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 1, or (at your option)
any later version.
GNU Tar 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 Tar; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
/*
* Extract files from a tar archive.
*
* Written 19 Nov 1985 by John Gilmore, ihnp4!hoptoad!gnu.
*
* @(#) extract.c 1.32 87/11/11 - gnu
*/
#include
#include
#include
#include
#ifdef BSD42
#include
#endif
#ifdef USG
#include
#endif
#ifdef MSDOS
#include
#endif /* MSDOS */
/*
* Some people don't have a #define for these.
*/
#ifndef O_BINARY
#define O_BINARY 0
#endif
#ifndef O_NDELAY
#define O_NDELAY 0
#endif
#ifdef NO_OPEN3
/* We need the #define's even though we don't use them. */
#include "open3.h"
#endif
#ifdef EMUL_OPEN3
/* Simulated 3-argument open for systems that don't have it */
#include "open3.h"
#endif
extern int errno; /* From libc.a */
extern time_t time(); /* From libc.a */
extern char *index(); /* From libc.a or port.c */
#include "tar.h"
#include "port.h"
extern FILE *msg_file;
extern union record *head; /* Points to current tape header */
extern struct stat hstat; /* Stat struct corresponding */
extern int head_standard; /* Tape header is in ANSI format */
extern char *save_name;
extern long save_totsize;
extern long save_sizeleft;
extern void print_header();
extern void skip_file();
extern void skip_extended_headers();
extern void pr_mkdir();
int make_dirs(); /* Makes required directories */
static time_t now = 0; /* Current time */
static we_are_root = 0; /* True if our effective uid == 0 */
static int notumask = ~0; /* Masks out bits user doesn't want */
/*
* "Scratch" space to store the information about a sparse file before
* writing the info into the header or extended header
*/
/*struct sp_array *sparsearray;*/
/* number of elts storable in the sparsearray */
/*int sp_array_size = 10;*/
/*
* Set up to extract files.
*/
extr_init()
{
int ourmask;
now = time((time_t *)0);
if (geteuid() == 0)
we_are_root = 1;
/*
* We need to know our umask. But if f_use_protection is set,
* leave our kernel umask at 0, and our "notumask" at ~0.
*/
ourmask = umask(0); /* Read it */
if (!f_use_protection) {
(void) umask (ourmask); /* Set it back how it was */
notumask = ~ourmask; /* Make umask override permissions */
}
}
/*
* Extract a file from the archive.
*/
void
extract_archive()
{
register char *data;
int fd, check, namelen, written, openflag;
long size;
time_t acc_upd_times[2];
register int skipcrud;
register int i;
int sparse_ind = 0;
union record *exhdr;
int end_nulls;
sp_array_size = 10;
saverec(&head); /* Make sure it sticks around */
userec(head); /* And go past it in the archive */
decode_header(head, &hstat, &head_standard, 1); /* Snarf fields */
if(f_confirm && !confirm("extract",head->header.name)) {
if (head->header.isextended)
skip_extended_headers();
skip_file((long)hstat.st_size);
saverec((union record **)0);
return;
}
/* Print the record from 'head' and 'hstat' */
if (f_verbose)
print_header();
/*
* Check for fully specified pathnames and other atrocities.
*
* Note, we can't just make a pointer to the new file name,
* since saverec() might move the header and adjust "head".
* We have to start from "head" every time we want to touch
* the header record.
*/
skipcrud = 0;
while (!f_absolute_paths && '/' == head->header.name[skipcrud]) {
static int warned_once = 0;
skipcrud++; /* Force relative path */
if (!warned_once++) {
msg("Removing leading / from absolute path names in the archive.");
}
}
sparsearray = (struct sp_array *) malloc(sp_array_size * sizeof(struct sp_array));
switch (head->header.linkflag) {
default:
msg("Unknown file type '%c' for %s, extracted as normal file",
head->header.linkflag, skipcrud+head->header.name);
/* FALL THRU */
/*
* JK - What we want to do if the file is sparse is loop through
* the array of sparse structures in the header and read in
* and translate the character strings representing 1) the offset
* at which to write and 2) how many bytes to write into numbers,
* which we store into the scratch array, "sparsearray". This
* array makes our life easier the same way it did in creating
* the tar file that had to deal with a sparse file.
*
* After we read in the first five (at most) sparse structures,
* we check to see if the file has an extended header, i.e.,
* if more sparse structures are needed to describe the contents
* of the new file. If so, we read in the extended headers
* and continue to store their contents into the sparsearray.
*/
case LF_SPARSE:
for (i = 0; i < SPARSE_IN_HDR; i++) {
if (!head->header.sp[i].numbytes)
break;
sparsearray[i].offset =
from_oct(1+12, head->header.sp[i].offset);
sparsearray[i].numbytes =
from_oct(1+12, head->header.sp[i].numbytes);
}
/* end_nulls = from_oct(1+12, head->header.ending_blanks);*/
if (head->header.isextended) {
/* read in the list of extended headers
and translate them into the sparsearray
as before */
static int ind = SPARSE_IN_HDR;
for (;;) {
exhdr = findrec();
for (i = 0; i < SPARSE_EXT_HDR; i++) {
if (i+ind > sp_array_size-1) {
/*
* realloc the scratch area
* since we've run out of room --
*/
sparsearray = (struct sp_array *)
realloc(sparsearray,
2 * sp_array_size * (sizeof(struct sp_array)));
sp_array_size *= 2;
}
if (!exhdr->ext_hdr.sp[i].numbytes)
break;
sparsearray[i+ind].offset =
from_oct(1+12, exhdr->ext_hdr.sp[i].offset);
sparsearray[i+ind].numbytes =
from_oct(1+12, exhdr->ext_hdr.sp[i].numbytes);
}
if (!exhdr->ext_hdr.isextended)
break;
else {
ind += SPARSE_EXT_HDR;
userec(exhdr);
}
}
userec(exhdr);
}
/* FALL THRU */
case LF_OLDNORMAL:
case LF_NORMAL:
case LF_CONTIG:
/*
* Appears to be a file.
* See if it's really a directory.
*/
namelen = strlen(skipcrud+head->header.name)-1;
if (head->header.name[skipcrud+namelen] == '/')
goto really_dir;
/* FIXME, deal with protection issues */
again_file:
openflag = (f_keep?
O_BINARY|O_NDELAY|O_WRONLY|O_CREAT|O_EXCL:
O_BINARY|O_NDELAY|O_WRONLY|O_CREAT|O_TRUNC)
| ((head->header.linkflag == LF_SPARSE) ? 0 : O_APPEND);
/*
* JK - The last | is a kludge to solve the problem
* the O_APPEND flag causes with files we are
* trying to make sparse: when a file is opened
* with O_APPEND, it writes to the last place
* that something was written, thereby ignoring
* any lseeks that we have done. We add this
* extra condition to make it able to lseek when
* a file is sparse, i.e., we don't open the new
* file with this flag. (Grump -- this bug caused
* me to waste a good deal of time, I might add)
*/
if(f_exstdout) {
fd = 1;
goto extract_file;
}
#ifdef O_CTG
/*
* Contiguous files (on the Masscomp) have to specify
* the size in the open call that creates them.
*/
if (head->header.linkflag == LF_CONTIG)
fd = open(skipcrud+head->header.name, openflag | O_CTG,
hstat.st_mode, hstat.st_size);
else
#endif
{
#ifdef NO_OPEN3
/*
* On raw V7 we won't let them specify -k (f_keep), but
* we just bull ahead and create the files.
*/
fd = creat(skipcrud+head->header.name,
hstat.st_mode);
#else
/*
* With 3-arg open(), we can do this up right.
*/
fd = open(skipcrud+head->header.name, openflag,
hstat.st_mode);
#endif
}
if (fd < 0) {
if (make_dirs(skipcrud+head->header.name))
goto again_file;
msg_perror("Could not create file %s",skipcrud+head->header.name);
if (head->header.isextended)
skip_extended_headers();
skip_file((long)hstat.st_size);
goto quit;
}
extract_file:
if (head->header.linkflag == LF_SPARSE) {
char *name;
int namelen;
/*
* Kludge alert. NAME is assigned to header.name
* because during the extraction, the space that
* contains the header will get scribbled on, and
* the name will get munged, so any error messages
* that happen to contain the filename will look
* REAL interesting unless we do this.
*/
namelen = strlen(skipcrud+head->header.name);
name = (char *) malloc((sizeof(char)) * namelen);
bcopy(skipcrud+head->header.name, name, namelen);
size = hstat.st_size;
extract_sparse_file(fd, &size, hstat.st_size,
name);
}
else
for (size = hstat.st_size;
size > 0;
size -= written) {
long offset,
numbytes;
if(f_multivol) {
save_name=head->header.name;
save_totsize=hstat.st_size;
save_sizeleft=size;
}
/*
* Locate data, determine max length
* writeable, write it, record that
* we have used the data, then check
* if the write worked.
*/
data = findrec()->charptr;
if (data == NULL) { /* Check it... */
msg("Unexpected EOF on archive file");
break;
}
/*
* JK - If the file is sparse, use the sparsearray
* that we created before to lseek into the new
* file the proper amount, and to see how many
* bytes we want to write at that position.
*/
/* if (head->header.linkflag == LF_SPARSE) {
off_t pos;
pos = lseek(fd, (off_t) sparsearray[sparse_ind].offset, 0);
printf("%d at %d\n", (int) pos, sparse_ind);
written = sparsearray[sparse_ind++].numbytes;
} else*/
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((union record *)(data + written - 1));
if (check == written) continue;
/*
* Error in writing to file.
* Print it, skip to next file in archive.
*/
if(check<0)
msg_perror("couldn't write to file %s",skipcrud+head->header.name);
else
msg("could only write %d of %d bytes to file %s",written,check,skipcrud+head->header.name);
skip_file((long)(size - written));
break; /* Still do the close, mod time, chmod, etc */
}
if(f_multivol)
save_name = 0;
/* If writing to stdout, don't try to do anything
to the filename; it doesn't exist, or we don't
want to touch it anyway */
if(f_exstdout)
break;
/* if (head->header.isextended) {
register union record *exhdr;
register int i;
for (i = 0; i < 21; i++) {
long offset;
if (!exhdr->ext_hdr.sp[i].numbytes)
break;
offset = from_oct(1+12,
exhdr->ext_hdr.sp[i].offset);
written = from_oct(1+12,
exhdr->ext_hdr.sp[i].numbytes);
lseek(fd, offset, 0);
check = write(fd, data, written);
if (check == written) continue;
}
}*/
check = close(fd);
if (check < 0) {
msg_perror("Error while closing %s",skipcrud+head->header.name);
}
set_filestat:
/*
* If we are root, set the owner and group of the extracted
* file. This does what is wanted both on real Unix and on
* System V. If we are running as a user, we extract as that
* user; if running as root, we extract as the original owner.
*/
if (we_are_root) {
if (chown(skipcrud+head->header.name, hstat.st_uid,
hstat.st_gid) < 0) {
msg_perror("cannot chown file %s to uid %d gid %d",skipcrud+head->header.name,hstat.st_uid,hstat.st_gid);
}
}
/*
* If '-k' is not set, open() or creat() could have saved
* the permission bits from a previously created file,
* ignoring the ones we specified.
* Even if -k is set, if the file has abnormal
* mode bits, we must chmod since writing or chown() has
* probably reset them.
*
* If -k is set, we know *we* created this file, so the mode
* bits were set by our open(). If the file is "normal", we
* skip the chmod. This works because we did umask(0) if -p
* is set, so umask will have left the specified mode alone.
*/
if ((!f_keep)
|| (hstat.st_mode & (S_ISUID|S_ISGID|S_ISVTX))) {
if (chmod(skipcrud+head->header.name,
notumask & (int)hstat.st_mode) < 0) {
msg_perror("cannot change mode of file %s to %ld",skipcrud+head->header.name,notumask & (int)hstat.st_mode);
}
}
/*
* 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".
* unless f_gnudump is used, in which case .st_atime is used
*/
if (!f_modified) {
/* fixme if f_gnudump should set ctime too, but how? */
if(f_gnudump) acc_upd_times[0]=hstat.st_atime;
else acc_upd_times[0] = now; /* Accessed now */
acc_upd_times[1] = hstat.st_mtime; /* Mod'd */
if (utime(skipcrud+head->header.name,
acc_upd_times) < 0) {
msg_perror("couldn't change access and modification times of %s",skipcrud+head->header.name);
}
}
quit:
break;
case LF_LINK:
again_link:
check = link (head->header.linkname,
skipcrud+head->header.name);
if (check == 0)
break;
if (make_dirs(skipcrud+head->header.name))
goto again_link;
if(f_gnudump && errno==EEXIST)
break;
msg_perror("Could not link %s to %s",
skipcrud+head->header.name,head->header.linkname);
break;
#ifdef S_IFLNK
case LF_SYMLINK:
again_symlink:
check = symlink(head->header.linkname,
skipcrud+head->header.name);
/* FIXME, don't worry uid, gid, etc... */
if (check == 0)
break;
if (make_dirs(skipcrud+head->header.name))
goto again_symlink;
msg_perror("Could not create symlink to %s",head->header.linkname);
break;
#endif
#ifdef S_IFCHR
case LF_CHR:
hstat.st_mode |= S_IFCHR;
goto make_node;
#endif
#ifdef S_IFBLK
case LF_BLK:
hstat.st_mode |= S_IFBLK;
goto make_node;
#endif
#ifdef S_IFIFO
/* If local system doesn't support FIFOs, use default case */
case LF_FIFO:
hstat.st_mode |= S_IFIFO;
hstat.st_rdev = 0; /* FIXME, do we need this? */
goto make_node;
#endif
make_node:
check = mknod(skipcrud+head->header.name,
(int) hstat.st_mode, (int) hstat.st_rdev);
if (check != 0) {
if (make_dirs(skipcrud+head->header.name))
goto make_node;
msg_perror("Could not make %s",skipcrud+head->header.name);
break;
};
goto set_filestat;
case LF_DIR:
case LF_DUMPDIR:
namelen = strlen(skipcrud+head->header.name)-1;
really_dir:
/* Check for trailing /, and zap as many as we find. */
while (namelen && head->header.name[skipcrud+namelen] == '/')
head->header.name[skipcrud+namelen--] = '\0';
if(f_gnudump) { /* Read the entry and delete files
that aren't listed in the archive */
gnu_restore(skipcrud+head->header.name);
} else if(head->header.linkflag==LF_DUMPDIR)
skip_file((long)(hstat.st_size));
again_dir:
check = mkdir(skipcrud+head->header.name,
0300 | (int)hstat.st_mode);
if (check != 0) {
if (make_dirs(skipcrud+head->header.name))
goto again_dir;
/* If we're trying to create '.', let it be. */
if (head->header.name[skipcrud+namelen] == '.' &&
(namelen==0 ||
head->header.name[skipcrud+namelen-1]=='/'))
goto check_perms;
if(f_gnudump && errno==EEXIST)
break;
msg_perror("Could not make directory %s",skipcrud+head->header.name);
break;
}
check_perms:
if (0300 != (0300 & (int) hstat.st_mode)) {
hstat.st_mode |= 0300;
msg("Added write and execute permission to directory %s",
skipcrud+head->header.name);
}
goto set_filestat;
/* FIXME, Remember timestamps for after files created? */
/* FIXME, change mode after files created (if was R/O dir) */
case LF_VOLHDR:
if(f_verbose) {
printf("Reading %s\n",head->header.name);
}
break;
case LF_MULTIVOL:
msg("Can't extract '%s'--file is continued from another volume\n",head->header.name);
skip_file((long)hstat.st_size);
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 */
if (check == 0) {
/* Fix ownership */
if (we_are_root) {
if (chown(pathname, hstat.st_uid,
hstat.st_gid) < 0) {
msg_perror("cannot change owner of %s to uid %d gid %d",pathname,hstat.st_uid,hstat.st_gid);
}
}
pr_mkdir(pathname, p-pathname, notumask&0777);
madeone++; /* Remember if we made one */
*p = '/';
continue;
}
*p = '/';
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 */
}
extract_sparse_file(fd, sizeleft, totalsize, name)
int fd;
long *sizeleft,
totalsize;
char *name;
{
register char *data;
union record *datarec;
int sparse_ind = 0;
int written,
count;
/* assuming sizeleft is initially totalsize */
while (*sizeleft > 0) {
datarec = findrec();
if (datarec == NULL) {
msg("Unexpected EOF on archive file");
return;
}
lseek(fd, sparsearray[sparse_ind].offset, 0);
written = sparsearray[sparse_ind++].numbytes;
while (written > RECORDSIZE) {
count = write(fd, datarec->charptr, RECORDSIZE);
if (count < 0)
msg_perror("couldn't write to file %s", name);
written -= count;
*sizeleft -= count;
userec(datarec);
datarec = findrec();
}
count = write(fd, datarec->charptr, written);
if (count < 0) {
msg_perror("couldn't write to file %s", name);
} else if (count != written) {
msg("could only write %d of %d bytes to file %s", totalsize - *sizeleft, totalsize, name);
skip_file((long) (*sizeleft));
}
written -= count;
*sizeleft -= count;
userec(datarec);
}
/* if (end_nulls) {
register int i;
printf("%d\n", (int) end_nulls);
for (i = 0; i < end_nulls; i++)
write(fd, "\000", 1);
}*/
userec(datarec);
}
Gnutar-1.08/buffer.c 600 10 5 70463 4676034463 7332 /* Buffer management for tar.
Copyright (C) 1988 Free Software Foundation
This file is part of GNU Tar.
GNU Tar 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 1, or (at your option)
any later version.
GNU Tar 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 Tar; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
/*
* Buffer management for tar.
*
* Written by John Gilmore, ihnp4!hoptoad!gnu, on 25 August 1985.
*
* @(#) buffer.c 1.28 11/6/87 - gnu
*/
#include
#include
#include
#include
#include
#ifdef COHERENT
# include
# include
#else
# ifndef MSDOS
# include
# ifndef USG
# include
# endif
# endif
#endif /*COHERENT*/
#ifdef MSDOS
# include
# include
#else
# ifdef XENIX
# include
# endif
# ifndef COHERENT
# include
# endif
#endif
extern int errno;
#include "tar.h"
#include "port.h"
#include "rmt.h"
/* Either stdout or stderr: The thing we write messages (standard msgs, not
errors) to. Stdout unless we're writing a pipe, in which case stderr */
FILE *msg_file = stdout;
#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() */
#ifdef __GNU__
extern void *malloc();
extern void *valloc();
#else
extern char *malloc();
extern char *valloc();
#endif
extern char *index(), *strcat();
extern char *strcpy();
/*
* V7 doesn't have a #define for this.
*/
#ifndef O_RDONLY
#define O_RDONLY 0
#endif
#ifndef O_RDWR
#define O_RDWR 2
#endif
#ifndef O_CREAT
#define O_CREAT 0
#endif
#ifndef O_BINARY
#define O_BINARY 0
#endif
#define MAGIC_STAT 105 /* Magic status returned by child, if
it can't exec. We hope compress/sh
never return this status! */
void writeerror();
void readerror();
void ck_pipe();
void ck_close();
extern void finish_header();
/*
* 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;
union record record_save_area;
static long saved_recno;
/*
* PID of child program, if f_compress or remote archive access.
*/
static int childpid = 0;
/*
* Record number of the start of this block of records
*/
long baserec;
/*
* Error recovery stuff
*/
static int r_error_count;
/*
* Have we hit EOF yet?
*/
static int eof;
/* JF we're reading, but we just read the last record and its time to update */
extern time_to_start_writing;
int file_to_switch_to= -1; /* If remote update, close archive, and use
this descriptor to write to */
static int volno = 1; /* JF which volume of a multi-volume tape
we're on */
char *save_name = 0; /* Name of the file we are currently writing */
long save_totsize; /* total size of file we are writing. Only
valid if save_name is non_zero */
long save_sizeleft; /* Where we are in the file we are writing.
Only valid if save_name is non-zero */
int write_archive_to_stdout;
/* Used by fl_read and fl_write to store the real info about saved names */
static char real_s_name[NAMSIZ];
static long real_s_totsize;
static long real_s_sizeleft;
/*
* Return the location of the next available input or output record.
* Return NULL for EOF. Once we have returned NULL, we just keep returning
* it, to avoid accidentally going on to the next file on the "tape".
*/
union record *
findrec()
{
if (ar_record == ar_last) {
if (eof)
return (union record *)NULL; /* EOF */
flush_archive();
if (ar_record == ar_last) {
eof++;
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;
}
/*
* Duplicate a file descriptor into a certain slot.
* Equivalent to BSD "dup2" with error reporting.
*/
void
dupto(from, to, msg)
int from, to;
char *msg;
{
int err;
if (from != to) {
err=close(to);
if(err<0 && errno!=EBADF) {
msg_perror("Cannot close descriptor %d",to);
exit(EX_SYSTEM);
}
err = dup(from);
if (err != to) {
msg_perror("cannot dup %s",msg);
exit(EX_SYSTEM);
}
ck_close(from);
}
}
#ifdef MSDOS
void
child_open()
{
fprintf(stderr,"MSDOS %s can't use compressed or remote archives\n",tar);
exit(EX_ARGSBAD);
}
#else
void
child_open()
{
int pipe[2];
int err = 0;
int kidpipe[2];
int kidchildpid;
#define READ 0
#define WRITE 1
ck_pipe(pipe);
childpid=fork();
if(childpid<0) {
msg_perror("cannot fork");
exit(EX_SYSTEM);
}
if(childpid>0) {
/* We're the parent. Clean up and be happy */
/* This, at least, is easy */
if(ar_reading) {
f_reblock++;
archive=pipe[READ];
ck_close(pipe[WRITE]);
} else {
archive = pipe[WRITE];
ck_close(pipe[READ]);
}
return;
}
/* We're the kid */
if(ar_reading) {
dupto(pipe[WRITE],STDOUT,"(child) pipe to stdout");
ck_close(pipe[READ]);
} else {
dupto(pipe[READ],STDIN,"(child) pipe to stdin");
ck_close(pipe[WRITE]);
}
/* We need a child tar only if
1: we're reading/writing stdin/out (to force reblocking)
2: the file is to be accessed by rmt (compress doesn't know how)
3: the file is not a plain file */
#ifdef NO_REMOTE
if(!(ar_file[0]=='-' && ar_file[1]=='\0') && isfile(ar_file))
#else
if(!(ar_file[0]=='-' && ar_file[1]=='\0') && !_remdev(ar_file) && isfile(ar_file))
#endif
{
/* We don't need a child tar. Open the archive */
if(ar_reading) {
archive=open(ar_file, O_RDONLY|O_BINARY, 0666);
if(archive<0) {
msg_perror("can't open archive %s",ar_file);
exit(EX_BADARCH);
}
dupto(archive,STDIN,"archive to stdin");
/* close(archive); */
} else {
archive=creat(ar_file,0666);
if(archive<0) {
msg_perror("can't open archive %s",ar_file);
exit(EX_BADARCH);
}
dupto(archive,STDOUT,"archive to stdout");
/* close(archive); */
}
} else {
/* We need a child tar */
ck_pipe(kidpipe);
kidchildpid=fork();
if(kidchildpid<0) {
msg_perror("child can't fork");
exit(EX_SYSTEM);
}
if(kidchildpid>0) {
/* About to exec compress: set up the files */
if(ar_reading) {
dupto(kidpipe[READ],STDIN,"((child)) pipe to stdin");
ck_close(kidpipe[WRITE]);
/* dup2(pipe[WRITE],STDOUT); */
} else {
/* dup2(pipe[READ],STDIN); */
dupto(kidpipe[WRITE],STDOUT,"((child)) pipe to stdout");
ck_close(kidpipe[READ]);
}
/* ck_close(pipe[READ]); */
/* ck_close(pipe[WRITE]); */
/* ck_close(kidpipe[READ]);
ck_close(kidpipe[WRITE]); */
} else {
/* Grandchild. Do the right thing, namely sit here and
read/write the archive, and feed stuff back to compress */
tar="tar (child)";
if(ar_reading) {
dupto(kidpipe[WRITE],STDOUT,"[child] pipe to stdout");
ck_close(kidpipe[READ]);
} else {
dupto(kidpipe[READ],STDIN,"[child] pipe to stdin");
ck_close(kidpipe[WRITE]);
}
if (ar_file[0] == '-' && ar_file[1] == '\0') {
if (ar_reading)
archive = STDIN;
else
archive = STDOUT;
} else /* This can't happen if (ar_reading==2)
archive = rmtopen(ar_file, O_RDWR|O_CREAT|O_BINARY, 0666);
else */if(ar_reading)
archive = rmtopen(ar_file, O_RDONLY|O_BINARY, 0666);
else
archive = rmtcreat(ar_file, 0666);
if (archive < 0) {
msg_perror("can't open archive %s",ar_file);
exit(EX_BADARCH);
}
if(ar_reading) {
for(;;) {
char *ptr;
int max,count;
r_error_count = 0;
error_loop:
err=rmtread(archive, ar_block->charptr,(int)(blocksize));
if(err<0) {
readerror();
goto error_loop;
}
if(err==0)
break;
ptr = ar_block->charptr;
max = err;
while(max) {
count = (max
if(err!=count) {
if(err<0) {
msg_perror("can't write to compress");
exit(EX_SYSTEM);
} else
msg("write to compress short %d bytes",count-err);
count = (err<0) ? 0 : err;
}
ptr+=count;
max-=count;
}
}
} else {
for(;;) {
int n;
char *ptr;
n=blocksize;
ptr = ar_block->charptr;
while(n) {
err=read(STDIN,ptr,(n
break;
n-=err;
ptr+=err;
}
/* EOF */
if(err==0) {
if(f_compress<2)
blocksize-=n;
else
bzero(ar_block->charptr+n,blocksize-n);
err=rmtwrite(archive,ar_block->charptr,blocksize);
if(err!=(blocksize))
writeerror(err);
if(f_compress<2)
blocksize+=n;
break;
}
if(n) {
msg_perror("can't read from compress");
exit(EX_SYSTEM);
}
err=rmtwrite(archive, ar_block->charptr, (int)blocksize);
if(err!=blocksize)
writeerror(err);
}
}
/* close_archive(); */
exit(0);
}
}
/* So we should exec compress (-d) */
if(ar_reading)
execlp("compress", "compress", "-d", (char *)0);
else
execlp("compress", "compress", (char *)0);
msg_perror("can't exec compress");
_exit(EX_SYSTEM);
}
/* return non-zero if p is the name of a directory */
isfile(p)
char *p;
{
struct stat stbuf;
if(stat(p,&stbuf)<0)
return 1;
if((stbuf.st_mode&S_IFMT)==S_IFREG)
return 1;
return 0;
}
#endif
/*
* Open an archive file. The argument specifies whether we are
* reading or writing.
*/
/* JF if the arg is 2, open for reading and writing. */
open_archive(reading)
int reading;
{
msg_file = stdout;
if (blocksize == 0) {
msg("invalid value for blocksize");
exit(EX_ARGSBAD);
}
if(ar_file==0) {
msg("No archive name given, what should I do?");
exit(EX_BADARCH);
}
/*NOSTRICT*/
if(f_multivol) {
ar_block = (union record *) valloc((unsigned)(blocksize+(2*RECORDSIZE)));
if(ar_block)
ar_block += 2;
} else
ar_block = (union record *) valloc((unsigned)blocksize);
if (!ar_block) {
msg("could not allocate memory for blocking factor %d",
blocking);
exit(EX_ARGSBAD);
}
ar_record = ar_block;
ar_last = ar_block + blocking;
ar_reading = reading;
if (f_compress) {
if(reading==2 || f_verify) {
msg("cannot update or verify compressed archives");
exit(EX_ARGSBAD);
}
child_open();
if(!reading && ar_file[0]=='-' && ar_file[1]=='\0')
msg_file = stderr;
/* child_open(rem_host, rem_file); */
} else if (ar_file[0] == '-' && ar_file[1] == '\0') {
f_reblock++; /* Could be a pipe, be safe */
if(f_verify) {
msg("can't verify stdin/stdout archive");
exit(EX_ARGSBAD);
}
if(reading==2) {
archive=STDIN;
msg_file=stderr;
write_archive_to_stdout++;
} else if (reading)
archive = STDIN;
else {
archive = STDOUT;
msg_file = stderr;
}
} else if (reading==2 || f_verify) {
archive = rmtopen(ar_file, O_RDWR|O_CREAT|O_BINARY, 0666);
} else if(reading) {
archive = rmtopen(ar_file, O_RDONLY|O_BINARY, 0666);
} else {
archive = rmtcreat(ar_file, 0666);
}
if (archive < 0) {
msg_perror("can't open %s",ar_file);
exit(EX_BADARCH);
}
#ifdef MSDOS
setmode(archive, O_BINARY);
#endif
if (reading) {
ar_last = ar_block; /* Set up for 1st block = # 0 */
(void) findrec(); /* Read it in, check for EOF */
if(f_volhdr) {
union record *head;
char *ptr;
if(f_multivol) {
ptr=malloc(strlen(f_volhdr)+20);
sprintf(ptr,"%s Volume %d",f_volhdr,1);
} else
ptr=f_volhdr;
head=findrec();
if(!head)
exit(EX_BADARCH);
if(strcmp(ptr,head->header.name)) {
msg("Volume mismatch! %s!=%s\n",ptr,head->header.name);
exit(EX_BADARCH);
}
if(ptr!=f_volhdr)
free(ptr);
}
} else if(f_volhdr) {
bzero(ar_block,RECORDSIZE);
if(f_multivol)
sprintf(ar_block->header.name,"%s Volume 1",f_volhdr);
else
strcpy(ar_block->header.name,f_volhdr);
ar_block->header.linkflag = LF_VOLHDR;
finish_header(ar_block);
/* ar_record++; */
}
}
/*
* 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.
*
* We calculate "offset" then add it because some compilers end up
* adding (baserec+ar_record), doing a 9-bit shift of baserec, then
* subtracting ar_block from that, shifting it back, losing the top 9 bits.
*/
saverec(pointer)
union record **pointer;
{
long offset;
save_rec = pointer;
offset = ar_record - ar_block;
saved_recno = baserec + offset;
}
/*
* Perform a write to flush the buffer.
*/
/*send_buffer_to_file();
if(new_volume) {
deal_with_new_volume_stuff();
send_buffer_to_file();
}
*/
fl_write()
{
int err;
int copy_back;
#ifdef TEST
static long test_written = 0;
#endif
#ifdef TEST
if(test_written>=30720) {
errno = ENOSPC;
err = 0;
} else
#endif
err = rmtwrite(archive, ar_block->charptr,(int) blocksize);
if(err!=blocksize && !f_multivol)
writeerror(err);
#ifdef TEST
if(err>0)
test_written+=err;
#endif
if (err == blocksize) {
if(f_multivol) {
if(!save_name) {
real_s_name[0]='\0';
real_s_totsize=0;
real_s_sizeleft = 0;
return;
}
#ifdef MSDOS
if(save_name[1]==':')
save_name+=2;
#endif
while(*save_name=='/')
save_name++;
strcpy(real_s_name,save_name);
real_s_totsize = save_totsize;
real_s_sizeleft = save_sizeleft;
}
return;
}
/* We're multivol Panic if we didn't get the right kind of response */
/* ENXIO is for the UNIX PC */
if(err>0 || (err<0 && errno!=ENOSPC && errno!=EIO && errno!=ENXIO))
writeerror(err);
if(new_volume(0)<0)
return;
#ifdef TEST
test_written=0;
#endif
if(f_volhdr && real_s_name[0]) {
copy_back=2;
ar_block-=2;
} else if(f_volhdr || real_s_name[0]) {
copy_back = 1;
ar_block--;
} else
copy_back = 0;
if(f_volhdr) {
bzero(ar_block,RECORDSIZE);
sprintf(ar_block->header.name,"%s Volume %d",f_volhdr,volno);
ar_block->header.linkflag = LF_VOLHDR;
finish_header(ar_block);
}
if(real_s_name[0]) {
extern void to_oct();
int tmp;
if(f_volhdr)
ar_block++;
bzero(ar_block,RECORDSIZE);
strcpy(ar_block->header.name,real_s_name);
ar_block->header.linkflag = LF_MULTIVOL;
to_oct((long)real_s_sizeleft,1+12,
ar_block->header.size);
to_oct((long)real_s_totsize-real_s_sizeleft,
1+12,ar_block->header.offset);
tmp=f_verbose;
f_verbose=0;
finish_header(ar_block);
f_verbose=tmp;
if(f_volhdr)
ar_block--;
}
err = rmtwrite(archive, ar_block->charptr,(int) blocksize);
if(err!=blocksize)
writeerror(err);
#ifdef TEST
test_written = blocksize;
#endif
if(copy_back) {
ar_block+=copy_back;
bcopy((ar_block+blocking-copy_back),
ar_record,
copy_back*RECORDSIZE);
ar_record+=copy_back;
if(real_s_sizeleft>=copy_back*RECORDSIZE)
real_s_sizeleft-=copy_back*RECORDSIZE;
else if((real_s_sizeleft+RECORDSIZE-1)/RECORDSIZE<=copy_back)
real_s_name[0] = '\0';
else {
#ifdef MSDOS
if(save_name[1]==':')
save_name+=2;
#endif
while(*save_name=='/')
save_name++;
strcpy(real_s_name,save_name);
real_s_sizeleft = save_sizeleft;
real_s_totsize=save_totsize;
}
copy_back = 0;
}
}
/* Handle write errors on the archive. Write errors are always fatal */
/* Hitting the end of a volume does not cause a write error unless the write
* was the first block of the volume */
void
writeerror(err)
int err;
{
if (err < 0) {
msg_perror("can't write to %s",ar_file);
exit(EX_BADARCH);
} else {
msg("only wrote %u of %u bytes to %s",err,blocksize,ar_file);
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 */
msg_perror("read error on %s",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) {
msg("Too many errors, quitting.");
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;
}
if(write_archive_to_stdout && baserec!=0) {
err=rmtwrite(1, ar_block->charptr, blocksize);
if(err!=blocksize)
writeerror(err);
}
if(f_multivol) {
if(save_name) {
if(save_name!=real_s_name) {
#ifdef MSDOS
if(save_name[1]==':')
save_name+=2;
#endif
while(*save_name=='/')
save_name++;
strcpy(real_s_name,save_name);
save_name=real_s_name;
}
real_s_totsize = save_totsize;
real_s_sizeleft = save_sizeleft;
} else {
real_s_name[0]='\0';
real_s_totsize=0;
real_s_sizeleft = 0;
}
}
error_loop:
err = rmtread(archive, ar_block->charptr, (int)blocksize);
if (err == blocksize)
return;
if((err == 0 || (err<0 && errno==ENOSPC)) && f_multivol) {
union record *head;
try_volume:
if(new_volume((cmd_mode==CMD_APPEND || cmd_mode==CMD_CAT || cmd_mode==CMD_UPDATE) ? 2 : 1)<0)
return;
vol_error:
err = rmtread(archive, ar_block->charptr,(int) blocksize);
if(err < 0) {
readerror();
goto vol_error;
}
if(err!=blocksize)
goto short_read;
head=ar_block;
if(head->header.linkflag==LF_VOLHDR) {
if(f_volhdr) {
char *ptr;
ptr=(char *)malloc(strlen(f_volhdr)+20);
sprintf(ptr,"%s Volume %d",f_volhdr,volno);
if(strcmp(ptr,head->header.name)) {
msg("Volume mismatch! %s!=%s\n",ptr,head->header.name);
--volno;
free(ptr);
goto try_volume;
}
free(ptr);
}
if(f_verbose)
fprintf(msg_file,"Reading %s\n",head->header.name);
head++;
} else if(f_volhdr) {
msg("Warning: No volume header!");
}
if(real_s_name[0]) {
long from_oct();
if(head->header.linkflag!=LF_MULTIVOL || strcmp(head->header.name,real_s_name)) {
msg("%s is not continued on this volume!",real_s_name);
--volno;
goto try_volume;
}
if(real_s_totsize!=from_oct(1+12,head->header.size)+from_oct(1+12,head->header.offset)) {
msg("%s is the wrong size (%ld!=%ld+%ld)",
head->header.name,save_totsize,
from_oct(1+12,head->header.size),
from_oct(1+12,head->header.offset));
--volno;
goto try_volume;
}
if(real_s_totsize-real_s_sizeleft!=from_oct(1+12,head->header.offset)) {
msg("This volume is out of sequence");
--volno;
goto try_volume;
}
head++;
}
ar_record=head;
return;
} else if (err < 0) {
readerror();
goto error_loop; /* Try again */
}
short_read:
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 && err > 0) {
/* msg("Blocksize = %d record%s",
err / RECORDSIZE, (err > RECORDSIZE)? "s": "");*/
msg("Blocksize = %d records", err / RECORDSIZE);
}
ar_last = ar_block + ((unsigned)(blocksize - left))/RECORDSIZE;
return;
}
if (f_reblock) {
/*
* User warned us about this. Fix up.
*/
if (left > 0) {
error2loop:
err = rmtread(archive, more, (int)left);
if (err < 0) {
readerror();
goto error2loop; /* Try again */
}
if (err == 0) {
msg("archive %s EOF not on block boundary",ar_file);
exit(EX_BADARCH);
}
left -= err;
more += err;
goto again;
}
} else {
msg("only read %d bytes from archive %s",err,ar_file);
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) {
if(time_to_start_writing) {
time_to_start_writing=0;
ar_reading=0;
if(file_to_switch_to>=0) {
rmtclose(archive);
archive=file_to_switch_to;
} else
(void)backspace_output();
fl_write();
} else
fl_read();
} else {
fl_write();
}
}
/* Backspace the archive descriptor by one blocks worth.
If its a tape, MTIOCTOP will work. If its something else,
we try to seek on it. If we can't seek, we lose! */
backspace_output()
{
long cur;
/* int er; */
extern char *output_start;
#ifdef MTIOCTOP
struct mtop t;
t.mt_op = MTBSR;
t.mt_count = 1;
if((rmtioctl(archive,MTIOCTOP,&t))>=0)
return 1;
if(errno==EIO && (rmtioctl(archive,MTIOCTOP,&t))>=0)
return 1;
#endif
cur=rmtlseek(archive,0L,1);
cur-=blocksize;
/* Seek back to the beginning of this block and
start writing there. */
if(rmtlseek(archive,cur,0)!=cur) {
/* Lseek failed. Try a different method */
msg("Couldn't backspace archive file. It may be unreadable without -i.");
/* Replace the first part of the block with nulls */
if(ar_block->charptr!=output_start)
bzero(ar_block->charptr,output_start-ar_block->charptr);
return 2;
}
return 3;
}
/*
* Close the archive file.
*/
close_archive()
{
int child;
int status;
if (time_to_start_writing || !ar_reading)
flush_archive();
if(cmd_mode==CMD_DELETE) {
long pos;
pos = rmtlseek(archive,0L,1);
#ifndef MSDOS
/* FIXME does ftruncate really take an INT?! */
(void) ftruncate(archive,(int)pos);
#else
(void)rmtwrite(archive,"",0);
#endif
}
if(f_verify)
verify_volume();
(void) rmtclose(archive);
#ifndef MSDOS
if (childpid) {
/*
* Loop waiting for the right child to die, or for
* no more kids.
*/
while (((child = wait(&status)) != childpid) && child != -1)
;
if (child != -1) {
switch (TERM_SIGNAL(status)) {
case 0:
/* Child voluntarily terminated -- but why? */
if (TERM_VALUE(status) == MAGIC_STAT) {
exit(EX_SYSTEM);/* Child had trouble */
}
if (TERM_VALUE(status) == (SIGPIPE + 128)) {
/*
* /bin/sh returns this if its child
* dies with SIGPIPE. 'Sok.
*/
break;
} else if (TERM_VALUE(status))
msg("child returned status %d",
TERM_VALUE(status));
case SIGPIPE:
break; /* This is OK. */
default:
msg("child died with signal %d%s",
TERM_SIGNAL(status),
TERM_COREDUMP(status)? " (core dumped)": "");
}
}
}
#endif /* MSDOS */
}
#ifdef DONTDEF
/*
* 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;
long offset;
int save_e;
save_e=errno;
/* Make sure previous output gets out in sequence */
if (stream == stderr)
fflush(stdout);
if (f_sayblock) {
if (prefix) {
fputs(prefix, stream);
putc(' ', stream);
}
offset = ar_record - ar_block;
(void) sprintf(buffer, "rec %d: ",
savedp? saved_recno:
baserec + offset);
fputs(buffer, stream);
space = ANNOWIDTH - strlen(buffer);
if (space > 0) {
fprintf(stream, "%*s", space, "");
}
} else if (prefix) {
fputs(prefix, stream);
fputs(": ", stream);
}
errno=save_e;
}
#endif
/* We've hit the end of the old volume. Close it and open the next one */
/* Values for type: 0: writing 1: reading 2: updating */
new_volume(type)
int type;
{
/* int c; */
char inbuf[80];
char *p;
static FILE *read_file = 0;
extern int now_verifying;
extern char TTY_NAME[];
char *getenv();
if(!read_file && !f_run_script_at_end)
read_file = (archive==0) ? fopen(TTY_NAME, "r") : stdin;
if(now_verifying)
return -1;
if(f_verify)
verify_volume();
if(rmtclose(archive)<0) {
msg_perror("can't close %s",ar_file);
exit(EX_BADARCH);
}
volno++;
if (f_run_script_at_end)
system(info_script);
else for(;;) {
fprintf(msg_file,"Prepare volume #%d and hit return: ",volno);
if(fgets(inbuf,sizeof(inbuf),read_file)==0 || inbuf[0]=='\n')
break;
switch(inbuf[0]) {
case '?':
{
fprintf(msg_file,"\
n [name] Give a new filename for the next (and subsequent) volume(s)\n\
q Abort tar\n\
! Spawn a subshell\n\
? Print this list\n");
}
break;
case 'q': /* Quit */
fprintf(msg_file,"No new volume; exiting.\n");
if(cmd_mode!=CMD_EXTRACT && cmd_mode!=CMD_LIST && cmd_mode!=CMD_DIFF)
msg("Warning: Archive is INCOMPLETE!");
exit(EX_BADARCH);
case 'n': /* Get new file name */
{
char *q,*r;
static char *old_name;
for(q= &inbuf[1];*q==' ' || *q=='\t';q++)
;
for(r=q;*r;r++)
if(*r=='\n')
*r='\0';
if(old_name)
free(old_name);
old_name=p=(char *)malloc((unsigned)(strlen(q)+2));
if(p==0) {
msg("Can't allocate memory for name");
exit(EX_SYSTEM);
}
(void) strcpy(p,q);
ar_file=p;
}
break;
case '!':
#ifdef MSDOS
spawnl(P_WAIT,getenv("COMSPEC"),"-",0);
#else
/* JF this needs work! */
switch(fork()) {
case -1:
msg_perror("can't fork!");
break;
case 0:
p=getenv("SHELL");
if(p==0) p="/bin/sh";
execlp(p,"-sh","-i",0);
msg_perror("can't exec a shell %s",p);
_exit(55);
default:
wait(0);
break;
}
#endif
break;
}
}
if(type==2 || f_verify)
archive=rmtopen(ar_file,O_RDWR|O_CREAT,0666);
else if(type==1)
archive=rmtopen(ar_file,O_RDONLY,0666);
else if(type==0)
archive=rmtcreat(ar_file,0666);
else
archive= -1;
if(archive<0) {
msg_perror("can't open %s",ar_file);
exit(EX_BADARCH);
}
#ifdef MSDOS
setmode(archive,O_BINARY);
#endif
return 0;
}
/* this is a useless function that takes a buffer returned by wantbytes
and does nothing with it. If the function called by wantbytes returns
an error indicator (non-zero), this function is called for the rest of
the file.
*/
int
no_op(size,data)
int size;
char *data;
{
return 0;
}
/* Some other routine wants SIZE bytes in the archive. For each chunk of
the archive, call FUNC with the size of the chunk, and the address of
the chunk it can work with.
*/
int
wantbytes(size,func)
long size;
int (*func)();
{
char *data;
long data_size;
while(size) {
data = findrec()->charptr;
if (data == NULL) { /* Check it... */
msg("Unexpected EOF on archive file");
return -1;
}
data_size = endofrecs()->charptr - data;
if(data_size>size)
data_size=size;
if((*func)(data_size,data))
func=no_op;
userec((union record *)(data + data_size - 1));
size-=data_size;
}
return 0;
}
Gnutar-1.08/getoldopt.c 600 10 5 4165 4533457545 10040 /* Replacement for getopt() that can be used by tar.
Copyright (C) 1988 Free Software Foundation
This file is part of GNU Tar.
GNU Tar 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 1, or (at your option)
any later version.
GNU Tar 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 Tar; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
/*
* 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)
*
* @(#)getoldopt.c 1.4 2/4/86 - gnu
*/
#include
#include "getopt.h"
int
getoldopt(argc, argv, optstring, long_options, opt_index)
int argc;
char **argv;
char *optstring;
struct option *long_options;
int *opt_index;
{
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 == '-') || (*key == '+'))
use_getopt++;
else
optind = 2;
}
if (use_getopt)
return getopt_long(argc, argv, optstring,
long_options, opt_index);
c = *key++;
if (c == '\0') {
key--;
return EOF;
}
place = index(optstring, c);
if (place == NULL || c == ':') {
msg("unknown option %c", c);
return('?');
}
place++;
if (*place == ':') {
if (optind < argc) {
optarg = argv[optind];
optind++;
} else {
msg("%c argument missing", c);
return('?');
}
}
return(c);
}
Gnutar-1.08/update.c 600 10 5 30771 4676035425 7340 /* Update a tar archive.
Copyright (C) 1988 Free Software Foundation
This file is part of GNU Tar.
GNU Tar 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 1, or (at your option)
any later version.
GNU Tar 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 Tar; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
/* JF implement the 'r' 'u' and 'A' options for tar. */
/* The 'A' option is my own invention: It means that the file-names are
tar files, and they should simply be appended to the end of the archive.
No attempt is made to block the reads from the args; if they're on raw
tape or something like that, it'll probably lose. . . */
#include
#include
#include
#include
/* JF these includes are copied from create.c I'm not sure if they're right
or not. */
#ifdef V7
#elif defined(COHERENT)
#include
#else
#include
#endif
#ifndef MSDOS
#include
#include
#endif
#ifdef USG
#include
#endif
/*
* V7 doesn't have a #define for this.
*/
#ifndef O_RDONLY
#define O_RDONLY 0
#endif
/*
* Most people don't have a #define for this.
*/
#ifndef O_BINARY
#define O_BINARY 0
#endif
#define STDIN 0
#define STDOUT 1
#include "tar.h"
#include "port.h"
#include "rmt.h"
int time_to_start_writing = 0; /* We've hit the end of the old stuff,
and its time to start writing new stuff
to the tape. This involves seeking
back one block and re-writing the current
block (which has been changed). */
char *output_start; /* Pointer to where we started to write in
the first block we write out. This is used
if we can't backspace the output and have
to null out the first part of the block */
extern void skip_file();
extern void skip_extended_headers();
extern union record *head;
extern struct stat hstat;
struct name *name_scan();
char *name_from_list();
/* Implement the 'r' (add files to end of archive), and 'u' (add files to
end of archive if they arent there, or are more up to date than the
version in the archive.) commands.*/
void
update_archive()
{
int found_end = 0;
int status = 3;
int prev_status;
char *p;
struct name *name;
extern void dump_file();
name_gather();
if(cmd_mode==CMD_UPDATE)
name_expand();
open_archive(2); /* Open for updating */
do {
prev_status=status;
status=read_header();
switch(status) {
case EOF:
found_end=1;
break;
case 0: /* A bad record */
userec(head);
switch(prev_status) {
case 3:
msg("This doesn't look like a tar archive.\n");
/* FALL THROUGH */
case 2:
case 1:
msg("Skipping to next header\n");
case 0:
break;
}
break;
/* A good record */
case 1:
/* printf("File %s\n",head->header.name); */
head->header.name[NAMSIZ-1]='\0';
if(cmd_mode==CMD_UPDATE && (name=name_scan(head->header.name))) {
/* struct stat hstat; */
struct stat nstat;
int head_standard;
decode_header(head,&hstat,&head_standard,0);
if(stat(head->header.name,&nstat)<0) {
msg_perror("can't stat %s:",head->header.name);
} else {
if(hstat.st_mtime>=nstat.st_mtime)
name->found++;
}
}
userec(head);
if (head->header.isextended)
skip_extended_headers();
skip_file((long)hstat.st_size);
break;
case 2:
ar_record=head;
found_end = 1;
break;
}
} while(!found_end);
time_to_start_writing = 1;
output_start=ar_record->charptr;
while(p=name_from_list()) {
if(f_confirm && !confirm("add", p))
continue;
if(cmd_mode==CMD_CAT)
append_file(p);
else
dump_file(p,-1);
}
write_eot();
close_archive();
names_notfound();
}
/* Catenate file p to the archive without creating a header for it. It had
better be a tar file or the archive is screwed */
append_file(p)
char *p;
{
int fd;
struct stat statbuf;
long bytes_left;
union record *start;
long bufsiz,count;
if(0 != stat(p,&statbuf) || (fd=open(p,O_RDONLY|O_BINARY))<0) {
msg_perror("can't open file %s",p);
errors++;
return;
}
bytes_left = statbuf.st_size;
while(bytes_left>0) {
start=findrec();
bufsiz=endofrecs()->charptr - start->charptr;
if(bytes_left < bufsiz) {
bufsiz = bytes_left;
count = bufsiz % RECORDSIZE;
if(count)
bzero(start->charptr + bytes_left,(int)(RECORDSIZE-count));
}
count=read(fd,start->charptr,bufsiz);
if(count<0) {
msg_perror("read error at byte %ld reading %d bytes in file %s",statbuf.st_size-bytes_left,bufsiz,p);
exit(EX_ARGSBAD); /* FOO */
}
bytes_left-=count;
userec(start+(count-1)/RECORDSIZE);
if(count!=bufsiz) {
msg("%s: file shrunk by %d bytes, yark!\n",p,bytes_left);
abort();
}
}
(void)close(fd);
}
#ifdef DONTDEF
bprint(fp,buf,num)
FILE *fp;
char *buf;
{
int c;
if(num==0 || num==-1)
return;
fputs(" '",fp);
while(num--) {
c= *buf++;
if(c=='\\') fputs("\\\\",fp);
else if(c>=' ' && c<='~')
putc(c,fp);
else switch(c) {
case '\n':
fputs("\\n",fp);
break;
case '\r':
fputs("\\r",fp);
break;
case '\b':
fputs("\\b",fp);
break;
case '\0':
/* fputs("\\-",fp); */
break;
default:
fprintf(fp,"\\%03o",c);
break;
}
}
fputs("'\n",fp);
}
#endif
int number_of_blocks_read = 0;
int number_of_new_records = 0;
int number_of_records_needed = 0;
union record *new_block = 0;
union record *save_block = 0;
void
junk_archive()
{
int found_stuff = 0;
int status = 3;
int prev_status;
struct name *name;
/* int dummy_head; */
int number_of_records_to_skip = 0;
int number_of_records_to_keep = 0;
int number_of_kept_records_in_block;
int sub_status;
extern int write_archive_to_stdout;
/* fprintf(stderr,"Junk files\n"); */
name_gather();
open_archive(2);
while(!found_stuff) {
prev_status=status;
status=read_header();
switch(status) {
case EOF:
found_stuff = 1;
break;
case 0:
userec(head);
switch(prev_status) {
case 3:
msg("This doesn't look like a tar archive.\n");
/* FALL THROUGH */
case 2:
case 1:
msg("Skipping to next header\n");
/* FALL THROUGH */
case 0:
break;
}
break;
case 1:
head->header.name[NAMSIZ-1] = '\0';
/* fprintf(stderr,"file %s\n",head->header.name); */
if((name=name_scan(head->header.name))==(struct name *)0) {
userec(head);
/* fprintf(stderr,"Skip %ld\n",(long)(hstat.st_size)); */
if (head->header.isextended)
skip_extended_headers();
skip_file((long)(hstat.st_size));
break;
}
name->found = 1;
found_stuff = 2;
break;
case 2:
found_stuff = 1;
break;
}
}
/* fprintf(stderr,"Out of first loop\n"); */
if(found_stuff!=2) {
write_eot();
close_archive();
names_notfound();
return;
}
if(write_archive_to_stdout)
write_archive_to_stdout = 0;
new_block = (union record *)malloc(blocksize);
if(new_block==0) {
fprintf(stderr,"Can't allocate secondary block of %d bytes\n",blocksize);
exit(EX_SYSTEM);
}
/* Save away records before this one in this block */
number_of_new_records=ar_record-ar_block;
number_of_records_needed = blocking - number_of_new_records;
if(number_of_new_records)
bcopy(ar_block,new_block,(number_of_new_records)*RECORDSIZE);
/* fprintf(stderr,"Saved %d recs, need %d more\n",number_of_new_records,number_of_records_needed); */
userec(head);
if (head->header.isextended)
skip_extended_headers();
skip_file((long)(hstat.st_size));
found_stuff=0;
/* goto flush_file; */
for(;;) {
/* Fill in a block */
/* another_file: */
if(ar_record==ar_last) {
/* fprintf(stderr,"New block\n"); */
flush_archive();
number_of_blocks_read++;
}
sub_status = read_header();
/* fprintf(stderr,"Header type %d\n",sub_status); */
if(sub_status==2 && f_ignorez) {
userec(head);
continue;
}
if(sub_status==EOF || sub_status==2) {
found_stuff = 1;
bzero(new_block[number_of_new_records].charptr,RECORDSIZE*number_of_records_needed);
number_of_new_records+=number_of_records_needed;
number_of_records_needed = 0;
write_block(0);
break;
}
if(sub_status==0) {
fprintf(stderr,"Deleting non-header from archive.\n");
userec(head);
continue;
}
/* Found another header. Yipee! */
head->header.name[NAMSIZ-1] = '\0';
/* fprintf(stderr,"File %s ",head->header.name); */
if(name=name_scan(head->header.name)) {
name->found = 1;
/* fprintf(stderr,"Flush it\n"); */
/* flush_file: */
/* decode_header(head,&hstat,&dummy_head,0); */
userec(head);
number_of_records_to_skip=(hstat.st_size+RECORDSIZE-1)/RECORDSIZE;
/* fprintf(stderr,"Flushing %d recs from %s\n",number_of_records_to_skip,head->header.name); */
while(ar_last-ar_record<=number_of_records_to_skip) {
/* fprintf(stderr,"Block: %d <= %d ",ar_last-ar_record,number_of_records_to_skip); */
number_of_records_to_skip -= (ar_last - ar_record);
flush_archive();
number_of_blocks_read++;
/* fprintf(stderr,"Block %d left\n",number_of_records_to_skip); */
}
ar_record+=number_of_records_to_skip;
/* fprintf(stderr,"Final %d\n",number_of_records_to_skip); */
number_of_records_to_skip = 0;
continue;
}
/* copy_header: */
new_block[number_of_new_records]= *head;
number_of_new_records++;
number_of_records_needed--;
number_of_records_to_keep=(hstat.st_size+RECORDSIZE-1)/RECORDSIZE;
userec(head);
if(number_of_records_needed==0)
write_block(1);
/* copy_data: */
number_of_kept_records_in_block = ar_last - ar_record;
if(number_of_kept_records_in_block > number_of_records_to_keep)
number_of_kept_records_in_block = number_of_records_to_keep;
/* fprintf(stderr,"Need %d kept_in %d keep %d\n",blocking,number_of_kept_records_in_block,number_of_records_to_keep); */
while(number_of_records_to_keep) {
int n;
if(ar_record==ar_last) {
/* fprintf(stderr,"Flush. . .\n"); */
fl_read();
number_of_blocks_read++;
ar_record=ar_block;
number_of_kept_records_in_block = blocking;
if(number_of_kept_records_in_block > number_of_records_to_keep)
number_of_kept_records_in_block = number_of_records_to_keep;
}
n = number_of_kept_records_in_block;
if(n>number_of_records_needed)
n = number_of_records_needed;
/* fprintf(stderr,"Copying %d\n",n); */
bcopy(ar_record, (new_block+number_of_new_records), n*RECORDSIZE);
number_of_new_records += n;
number_of_records_needed -= n;
ar_record += n;
number_of_records_to_keep -= n;
number_of_kept_records_in_block -= n;
/* fprintf(stderr,"Now new %d need %d keep %d keep_in %d rec %d/%d\n",
number_of_new_records,number_of_records_needed,number_of_records_to_keep,
number_of_kept_records_in_block,ar_record-ar_block,ar_last-ar_block); */
if(number_of_records_needed == 0) {
write_block(1);
}
}
}
write_eot();
close_archive();
names_notfound();
}
write_block(f)
{
/* fprintf(stderr,"Write block\n"); */
/* We've filled out a block. Write it out. */
/* Backspace back to where we started. . . */
if(archive!=STDIN)
(void)move_arch(-(number_of_blocks_read+1));
save_block = ar_block;
ar_block = new_block;
if(archive==STDIN)
archive=STDOUT;
fl_write();
if(archive==STDOUT)
archive=STDIN;
ar_block = save_block;
if(f) {
/* Move the tape head back to where we were */
if(archive!=STDIN)
(void)move_arch(number_of_blocks_read);
number_of_blocks_read--;
}
number_of_records_needed = blocking;
number_of_new_records = 0;
}
/* Move archive descriptor by n blocks worth. If n is positive we move
forward, else we move negative. If its a tape, MTIOCTOP had better
work. If its something else, we try to seek on it. If we can't
seek, we lose! */
move_arch(n)
{
long cur;
extern int errno;
#ifdef MTIOCTOP
struct mtop t;
int er;
if(n>0) {
t.mt_op = MTFSR;
t.mt_count = n;
} else {
t.mt_op = MTBSR;
t.mt_count = -n;
}
if((er=rmtioctl(archive,MTIOCTOP,&t))>=0)
return 1;
if(errno==EIO && (er=rmtioctl(archive,MTIOCTOP,&t))>=0)
return 1;
#endif
cur=rmtlseek(archive,0L,1);
cur+=blocksize*n;
/* fprintf(stderr,"Fore to %x\n",cur); */
if(rmtlseek(archive,cur,0)!=cur) {
/* Lseek failed. Try a different method */
fprintf(stderr,"tar: Couldn't re-position archive file.\n");
exit(EX_BADARCH);
}
return 3;
}
Gnutar-1.08/version.c 600 10 5 7131 4476540506 7514 /* Version info for tar.
Copyright (C) 1989, Free Software Foundation.
This file is part of GNU Tar.
GNU Tar 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 1, or (at your option)
any later version.
GNU Tar 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 Tar; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
char version_string[] = "GNU tar version 1.08";
/* Version 1.00: This file added. -version option added */
/* Installed new version of the remote-tape library */
/* Added -help option */
/* 1.01 Fixed typoes in tar.texinfo */
/* Fixed a bug in the #define for rmtcreat() */
/* Fixed the -X option to not call realloc() of 0. */
/* 1.02 Fixed tar.c so 'tar -h' and 'tar -v' don't cause core dump */
/* Also fixed the 'usage' message to be more up-to-date. */
/* Fixed diffarch.c so verify should compile without MTIOCTOP
defined */
/* 1.03 Fixed buffer.c so 'tar tzf NON_EXISTENT_FILE' returns an error
message instead of hanging forever */
/* More fixes to tar.texinfo */
/* 1.04 Added functions msg() and msg_perror() Modified all the
files to call them. Also checked that all (I hope)
calls to msg_perror() have a valid errno value
(modified anno() to leave errno alone), etc
Re-fixed the -X option. This
time for sure. . . */
/* re-modified the msg stuff. flushed anno() completely */
/* Modified the directory stuff so it should work on sysV boxes */
/* added ftime() to getdate.y */
/* Fixed un_quote_string() so it won't wedge on \" Also fixed
\ddd (like \123, etc) */
/* More fixes to tar.texinfo */
/* 1.05 A fix to make confirm() work when the archive is on stdin
include 'extern FILE *msg_file;' in pr_mkdir(), and fix
tar.h to work with __STDC__ */
/* Added to port.c: mkdir() ftruncate() Removed: lstat() */
/* Fixed -G to work with -X */
/* Another fix to tar.texinfo */
/* Changed tar.c to say argv[0]":you must specify exactly ... */
/* buffer.c: modified child_open() to keep tar from hanging when
it is done reading/writing a compressed archive */
/* added fflush(msg_file) before printing error messages */
/* create.c: fixed to make link_names non-absolute */
/* 1.06 Use STDC_MSG if __STDC__ defined
ENXIO meand end-of-volume in archive (for the UNIX PC)
Added break after volume-header case (line 440) extract.c
Added patch from [email protected] to rtape_lib.c
Added f_absolute_paths option.
Deleted refereces to UN*X manual sections (dump(8), etc)
Fixed to not core-dump on illegal options
Modified msg_perror to call perror("") instead of perror(0)
patch so -X - works
Fixed tar.c so 'tar cf - -C dir' doesn't core-dump
tar.c (name_match): Fixed to chdir() to the appropriate
directory if the matching name's change_dir is set. This
makes tar xv -C foo {files} work. */
/* 1.07 New version to go on beta tape with GCC 1.35
Better USG support. Also support for __builtin_alloca
if we're compiling with GCC.
diffarch.c: Include the correct header files so MTIOCTOP
is defined.
tar.c: Don't print the verbose list of options unless
given -help. The list of options is *way* too long.
1.08 Sparse file support added. Also various other features.
See ChangeLog for details.
*/
Gnutar-1.08/list.c 600 10 5 40023 4676035212 7012 /* List a tar archive.
Copyright (C) 1988 Free Software Foundation
This file is part of GNU Tar.
GNU Tar 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 1, or (at your option)
any later version.
GNU Tar 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 Tar; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
/*
* List a tar archive.
*
* Also includes support routines for reading a tar archive.
*
* this version written 26 Aug 1985 by John Gilmore (ihnp4!hoptoad!gnu).
*
* @(#)list.c 1.31 11/5/87 - gnu
*/
#include
#include
#include
#include
#if !defined(MSDOS) && !defined(COHERENT)
#include
#endif /* MSDOS */
#ifdef COHERENT
#include
#include
#endif
#ifdef USG
#include
#endif
char *ctime(); /* From libc.a */
#define isodigit(c) ( ((c) >= '0') && ((c) <= '7') )
#include "tar.h"
#include "port.h"
extern FILE *msg_file;
long from_oct(); /* Decode octal number */
void demode(); /* Print file mode */
union record *head; /* Points to current archive header */
struct stat hstat; /* Stat struct corresponding */
int head_standard; /* Tape header is in ANSI format */
void print_header();
void skip_file();
void skip_extended_headers();
extern char *quote_copy_string();
/*
* Main loop for reading an archive.
*/
void
read_and(do_something)
void (*do_something)();
{
int status = 3; /* Initial status at start of archive */
int prev_status;
extern time_t new_time;
char save_linkflag;
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)
|| (f_new_files && hstat.st_mtime
int isextended = 0;
/* Skip past it in the archive */
if (head->header.isextended)
isextended = 1;
save_linkflag = head->header.linkflag;
userec(head);
if (isextended) {
/* register union record *exhdr;
for (;;) {
exhdr = findrec();
if (!exhdr->ext_hdr.isextended) {
userec(exhdr);
break;
}
}
userec(exhdr);*/
skip_extended_headers();
}
/* Skip to the next header on the archive */
if(save_linkflag != LF_DIR)
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 */
userec(head);
switch (prev_status) {
case 3: /* Error on first record */
msg("Hmm, this doesn't look like a tar archive.");
/* FALL THRU */
case 2: /* Error after record of zeroes */
case 1: /* Error after header rec */
msg("Skipping to next file header...\n");
case 0: /* Error after error */
break;
}
continue;
case 2: /* Record of zeroes */
userec(head);
status = prev_status; /* If error after 0's */
if (f_ignorez)
continue;
/* 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()
{
extern char *save_name;
int isextended = 0; /* Flag to remember if head is extended */
/* Save the record */
saverec(&head);
/* Print the header record */
if (f_verbose) {
if (f_verbose > 1)
decode_header(head, &hstat, &head_standard, 0);
print_header();
}
if(f_gnudump && head->header.linkflag==LF_DUMPDIR) {
size_t size, written, check;
char *data;
extern int errno;
extern long save_totsize;
extern long save_sizeleft;
userec(head);
if(f_multivol) {
save_name = head->header.name;
save_totsize=hstat.st_size;
}
for(size = hstat.st_size;size>0;size-=written) {
if(f_multivol)
save_sizeleft=size;
data = findrec()->charptr;
if(data==NULL) {
msg("EOF in archive file?");
break;
}
written = endofrecs()->charptr - data;
if(written>size)
written=size;
errno=0;
check=fwrite(data,sizeof(char), written, msg_file);
userec((union record *)(data+written - 1));
if(check!=written) {
msg_perror("only wrote %ld of %ld bytes to file %s",check, written,head->header.name);
skip_file((long)(size)-written);
break;
}
}
if(f_multivol)
save_name = 0;
saverec((union record **) 0); /* Unsave it */
fputc('\n',msg_file);
fflush(msg_file);
return;
}
saverec((union record **) 0); /* Unsave it */
/* Check to see if we have an extended header to skip over also */
if (head->header.isextended)
isextended = 1;
/* Skip past the header in the archive */
userec(head);
/*
* If we needed to skip any extended headers, do so now, by
* reading extended headers and skipping past them in the
* archive.
*/
if (isextended) {
/* register union record *exhdr;
for (;;) {
exhdr = findrec();
if (!exhdr->ext_hdr.isextended) {
userec(exhdr);
break;
}
userec(exhdr);
}*/
skip_extended_headers();
}
if(f_multivol)
save_name=head->header.name;
/* Skip to the next header on the archive */
skip_file((long) hstat.st_size);
if(f_multivol)
save_name = 0;
}
/*
* 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 record 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;
long from_oct();
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 record...whole record 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 should NOT be called twice for the same record, since the
* two calls might use different "wantug" values and thus might end up with
* different uid/gid for the two calls. If anybody wants the uid/gid they
* should decode it first, and other callers should decode it without uid/gid
* before calling a routine, e.g. print_header, that assumes decoded data.
*/
decode_header(header, st, stdp, wantug)
register union record *header;
register struct stat *st;
int *stdp;
int wantug;
{
long from_oct();
st->st_mode = from_oct(8, header->header.mode);
st->st_mtime = from_oct(1+12, header->header.mtime);
if(f_gnudump) {
st->st_atime = from_oct(1+12, header->header.atime);
st->st_ctime = from_oct(1+12, header->header.ctime);
}
if (0==strcmp(header->header.magic, TMAGIC)) {
/* Unix Standard tar archive */
*stdp = 1;
if (wantug) {
#ifdef NONAMES
st->st_uid = from_oct(8, header->header.uid);
st->st_gid = from_oct(8, header->header.gid);
#else
st->st_uid = finduid(header->header.uname);
st->st_gid = findgid(header->header.gname);
#endif
}
switch (header->header.linkflag) {
case LF_BLK: case LF_CHR:
st->st_rdev = 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_rdev = 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.
*
* Plain and fancy file header block logging.
* Non-verbose just prints the name, e.g. for "tar t" or "tar x".
* This should just contain file names, so it can be fed back into tar
* with xargs or the "-T" option. The verbose option can give a bunch
* of info, one line per file. I doubt anybody tries to parse its
* format, or if they do, they shouldn't. Unix tar is pretty random here
* anyway.
*
* Note that print_header uses the globals ,
*
* and should be cleaned up. FIXME.
*/
#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;
char *name;
extern long baserec;
if(f_sayblock)
fprintf(msg_file,"rec %10d: ",baserec + (ar_record - ar_block));
/* annofile(msg_file, (char *)NULL); */
if (f_verbose <= 1) {
/* Just the fax, mam. */
char *name;
name=quote_copy_string(head->header.name);
if(name==0)
name=head->header.name;
fprintf(msg_file, "%s\n", name);
if(name!=head->header.name)
free(name);
} else {
/* File type and modes */
modes[0] = '?';
switch (head->header.linkflag) {
case LF_VOLHDR:
modes[0]='V';
break;
case LF_MULTIVOL:
modes[0]='M';
break;
case LF_SPARSE:
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_DUMPDIR: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] = 'p'; break;
case LF_CONTIG: modes[0] = 'C'; 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 && head_standard) {
user = head->header.uname;
} else {
user = uform;
(void)sprintf(uform, "%d", (int)hstat.st_uid);
}
if (*head->header.gname && head_standard) {
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_rdev),
minor(hstat.st_rdev));
break;
case LF_SPARSE:
(void)sprintf(size, "%ld",
from_oct(1+12, head->header.realsize));
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;
name = quote_copy_string(head->header.name);
if(!name)
name=head->header.name;
fprintf(msg_file, "%s %s/%s %*s%s %s %s %.*s",
modes,
user,
group,
ugswidth - pad,
"",
size,
timestamp+4, timestamp+20,
sizeof(head->header.name),
name);
if(name!=head->header.name)
free(name);
switch (head->header.linkflag) {
case LF_SYMLINK:
name=quote_copy_string(head->header.linkname);
if(!name)
name=head->header.linkname;
fprintf(msg_file, " -> %s\n", name);
if(name!=head->header.linkname)
free(name);
break;
case LF_LINK:
name=quote_copy_string(head->header.linkname);
if(!name)
name=head->header.linkname;
fprintf(msg_file, " link to %s\n", head->header.linkname);
if(name!=head->header.linkname)
free(name);
break;
default:
fprintf(msg_file, " unknown file type '%c'\n",
head->header.linkflag);
break;
case LF_OLDNORMAL:
case LF_NORMAL:
case LF_SPARSE:
case LF_CHR:
case LF_BLK:
case LF_DIR:
case LF_FIFO:
case LF_CONTIG:
case LF_DUMPDIR:
putc('\n', msg_file);
break;
case LF_VOLHDR:
fprintf(msg_file, "--Volume Header--\n");
break;
case LF_MULTIVOL:
fprintf(msg_file, "--Continued at byte %ld--\n",from_oct(1+12,head->header.offset));
break;
}
}
fflush(msg_file);
}
/*
* 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];
char *name;
extern long baserec;
if (f_verbose > 1) {
/* File type and modes */
modes[0] = 'd';
demode((unsigned)mode, modes+1);
if(f_sayblock)
fprintf(msg_file,"rec %10d: ",baserec + (ar_record - ar_block));
/* annofile(msg_file, (char *)NULL); */
name=quote_copy_string(pathname);
if(!name)
name=pathname;
fprintf(msg_file, "%s %*s %.*s\n",
modes,
ugswidth+DATEWIDTH,
"Creating directory:",
length,
pathname);
if(name!=pathname)
free(name);
}
}
/*
* Skip over
*/
void
skip_file(size)
register long size;
{
union record *x;
extern long save_totsize;
extern long save_sizeleft;
if(f_multivol) {
save_totsize=size;
save_sizeleft=size;
}
while (size > 0) {
x = findrec();
if (x == NULL) { /* Check it... */
msg("Unexpected EOF on archive file");
exit(EX_BADARCH);
}
userec(x);
size -= RECORDSIZE;
if(f_multivol)
save_sizeleft-=RECORDSIZE;
}
}
void
skip_extended_headers()
{
register union record *exhdr;
for (;;) {
exhdr = findrec();
if (!exhdr->ext_hdr.isextended) {
userec(exhdr);
break;
}
}
userec(exhdr);
}
/*
* 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';
}
Gnutar-1.08/names.c 600 10 5 6042 4403300713 7112 /* Look up user and/or group names.
Copyright (C) 1988 Free Software Foundation
This file is part of GNU Tar.
GNU Tar 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 1, or (at your option)
any later version.
GNU Tar 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 Tar; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
/*
* Look up user and/or group names.
*
* This file should be modified for non-unix systems to do something
* reasonable.
*
* @(#)names.c 1.3 10/30/87 - gnu
*/
#include
#include "tar.h"
extern char *strncpy();
#ifndef NONAMES
/* Whole module goes away if NONAMES defined. Otherwise... */
#include
#include
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 )
/*
* 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
Gnutar-1.08/diffarch.c 600 10 5 35255 4676036013 7620 /* Diff files from a tar archive.
Copyright (C) 1988 Free Software Foundation
This file is part of GNU Tar.
GNU Tar 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 1, or (at your option)
any later version.
GNU Tar 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 Tar; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
/*
* Diff files from a tar archive.
*
* Written 30 April 1987 by John Gilmore, ihnp4!hoptoad!gnu.
*
* @(#) diffarch.c 1.10 87/11/11 - gnu
*/
#include
#include
#include
#include
#ifdef BSD42
#include
#endif
#ifdef COHERENT
#include
#include
#else
#ifdef MSDOS
#include
#ifndef USG
#include
#endif
#endif
#endif /*COHERENT*/
#ifdef USG
#include
#endif
/* Some systems don't have these #define's -- we fake it here. */
#ifndef O_RDONLY
#define O_RDONLY 0
#endif
#ifndef O_NDELAY
#define O_NDELAY 0
#endif
#ifndef S_IFLNK
#define lstat stat
#endif
extern int errno; /* From libc.a */
extern char *valloc(); /* From libc.a */
#include "tar.h"
#include "port.h"
#include "rmt.h"
extern union record *head; /* Points to current tape header */
extern struct stat hstat; /* Stat struct corresponding */
extern int head_standard; /* Tape header is in ANSI format */
extern void print_header();
extern void skip_file();
extern void skip_extended_headers();
extern FILE *msg_file;
int now_verifying = 0; /* Are we verifying at the moment? */
char *diff_name; /* head->header.name */
int diff_fd; /* Descriptor of file we're diffing */
char *diff_buf = 0; /* Pointer to area for reading
file contents into */
char *diff_dir; /* Directory contents for LF_DUMPDIR */
int different = 0;
/*struct sp_array *sparsearray;
int sp_ar_size = 10;*/
/*
* Initialize for a diff operation
*/
diff_init()
{
/*NOSTRICT*/
diff_buf = (char *) valloc((unsigned)blocksize);
if (!diff_buf) {
msg("could not allocate memory for diff buffer of %d bytes\n",
blocksize);
exit(EX_ARGSBAD);
}
}
/*
* Diff a file against the archive.
*/
void
diff_archive()
{
register char *data;
int check, namelen;
int err;
long offset;
struct stat filestat;
char linkbuf[NAMSIZ+3];
int compare_chunk();
int compare_dir();
dev_t dev;
ino_t ino;
char *get_dir_contents();
long from_oct();
long lseek();
errno = EPIPE; /* FIXME, remove perrors */
saverec(&head); /* Make sure it sticks around */
userec(head); /* And go past it in the archive */
decode_header(head, &hstat, &head_standard, 1); /* Snarf fields */
/* Print the record from 'head' and 'hstat' */
if (f_verbose) {
if(now_verifying)
fprintf(msg_file,"Verify ");
print_header();
}
diff_name = head->header.name;
switch (head->header.linkflag) {
default:
msg("Unknown file type '%c' for %s, diffed as normal file\n",
head->header.linkflag, diff_name);
/* FALL THRU */
case LF_OLDNORMAL:
case LF_NORMAL:
case LF_SPARSE:
case LF_CONTIG:
/*
* Appears to be a file.
* See if it's really a directory.
*/
namelen = strlen(diff_name)-1;
if (diff_name[namelen] == '/')
goto really_dir;
if(do_stat(&filestat)) {
if (head->header.isextended)
skip_extended_headers();
skip_file((long)hstat.st_size);
different++;
goto quit;
}
if ((filestat.st_mode & S_IFMT) != S_IFREG) {
fprintf(msg_file, "%s: not a regular file\n",
diff_name);
skip_file((long)hstat.st_size);
different++;
goto quit;
}
filestat.st_mode &= ~S_IFMT;
if (filestat.st_mode != hstat.st_mode)
sigh("mode");
if (filestat.st_uid != hstat.st_uid)
sigh("uid");
if (filestat.st_gid != hstat.st_gid)
sigh("gid");
if (filestat.st_mtime != hstat.st_mtime)
sigh("mod time");
if (head->header.linkflag != LF_SPARSE &&
filestat.st_size != hstat.st_size) {
sigh("size");
skip_file((long)hstat.st_size);
goto quit;
}
diff_fd = open(diff_name, O_NDELAY|O_RDONLY);
if (diff_fd < 0) {
msg_perror("cannot open %s",diff_name);
if (head->header.isextended)
skip_extended_headers();
skip_file((long)hstat.st_size);
different++;
goto quit;
}
/*
* Need to treat sparse files completely differently here.
*/
if (head->header.linkflag == LF_SPARSE) {
/* long end_nulls;
end_nulls = from_oct(1+12, head->header.ending_blanks);*/
diff_sparse_files(hstat.st_size);
}
else
wantbytes((long)(hstat.st_size),compare_chunk);
check = close(diff_fd);
if (check < 0) {
msg_perror("Error while closing %s",diff_name);
}
quit:
break;
case LF_LINK:
if(do_stat(&filestat))
break;
dev = filestat.st_dev;
ino = filestat.st_ino;
err = stat(head->header.linkname, &filestat);
if (err < 0) {
if (errno==ENOENT) {
fprintf(msg_file, "%s: does not exist\n",diff_name);
} else {
msg_perror("cannot stat file %s",diff_name);
}
different++;
break;
}
if(filestat.st_dev!=dev || filestat.st_ino!=ino) {
fprintf(msg_file, "%s not linked to %s\n",diff_name,head->header.linkname);
break;
}
break;
#ifdef S_IFLNK
case LF_SYMLINK:
check = readlink(diff_name, linkbuf,
(sizeof linkbuf)-1);
if (check < 0) {
if (errno == ENOENT) {
fprintf(msg_file,
"%s: no such file or directory\n",
diff_name);
} else {
msg_perror("cannot read link %s",diff_name);
}
different++;
break;
}
linkbuf[check] = '\0'; /* Null-terminate it */
if (strncmp(head->header.linkname, linkbuf, check) != 0) {
fprintf(msg_file, "%s: symlink differs\n",
head->header.linkname);
different++;
}
break;
#endif
case LF_CHR:
hstat.st_mode |= S_IFCHR;
goto check_node;
#ifdef S_IFBLK
/* If local system doesn't support block devices, use default case */
case LF_BLK:
hstat.st_mode |= S_IFBLK;
goto check_node;
#endif
#ifdef S_IFIFO
/* If local system doesn't support FIFOs, use default case */
case LF_FIFO:
hstat.st_mode |= S_IFIFO;
hstat.st_rdev = 0; /* FIXME, do we need this? */
goto check_node;
#endif
check_node:
/* FIXME, deal with umask */
if(do_stat(&filestat))
break;
if(hstat.st_rdev != filestat.st_rdev) {
fprintf(msg_file, "%s: device numbers changed\n", diff_name);
different++;
break;
}
if(hstat.st_mode != filestat.st_mode) {
fprintf(msg_file, "%s: mode or device-type changed\n", diff_name);
different++;
break;
}
break;
case LF_DUMPDIR:
data=diff_dir=get_dir_contents(diff_name,0);
wantbytes((long)(hstat.st_size),compare_dir);
free(data);
/* FALL THROUGH */
case LF_DIR:
/* Check for trailing / */
namelen = strlen(diff_name)-1;
really_dir:
while (namelen && diff_name[namelen] == '/')
diff_name[namelen--] = '\0'; /* Zap / */
if(do_stat(&filestat))
break;
if((filestat.st_mode&S_IFMT)!=S_IFDIR) {
fprintf(msg_file, "%s is no longer a directory\n",diff_name);
different++;
break;
}
if((filestat.st_mode&~S_IFMT) != hstat.st_mode)
sigh("mode");
break;
case LF_VOLHDR:
break;
case LF_MULTIVOL:
namelen = strlen(diff_name)-1;
if (diff_name[namelen] == '/')
goto really_dir;
if(do_stat(&filestat))
break;
if ((filestat.st_mode & S_IFMT) != S_IFREG) {
fprintf(msg_file, "%s: not a regular file\n",
diff_name);
skip_file((long)hstat.st_size);
different++;
break;
}
filestat.st_mode &= ~S_IFMT;
offset = from_oct(1+12, head->header.offset);
if (filestat.st_size != hstat.st_size + offset) {
sigh("size");
skip_file((long)hstat.st_size);
different++;
break;
}
diff_fd = open(diff_name, O_NDELAY|O_RDONLY);
if (diff_fd < 0) {
msg_perror("cannot open file %s",diff_name);
skip_file((long)hstat.st_size);
different++;
break;
}
err = lseek(diff_fd, offset, 0);
if(err!=offset) {
msg_perror("cannot seek to %ld in file %s",offset,diff_name);
different++;
break;
}
wantbytes((long)(hstat.st_size),compare_chunk);
check = close(diff_fd);
if (check < 0) {
msg_perror("Error while closing %s",diff_name);
}
break;
}
/* We don't need to save it any longer. */
saverec((union record **) 0); /* Unsave it */
}
int
compare_chunk(bytes,buffer)
long bytes;
char *buffer;
{
int err;
err=read(diff_fd,diff_buf,bytes);
if(err!=bytes) {
if(err<0) {
msg_perror("can't read %s",diff_name);
} else {
fprintf(msg_file,"%s: could only read %d of %d bytes\n",diff_name,err,bytes);
}
different++;
return -1;
}
if(bcmp(buffer,diff_buf,bytes)) {
fprintf(msg_file, "%s: data differs\n",diff_name);
different++;
return -1;
}
return 0;
}
int
compare_dir(bytes,buffer)
long bytes;
char *buffer;
{
if(bcmp(buffer,diff_dir,bytes)) {
fprintf(msg_file, "%s: data differs\n",diff_name);
different++;
return -1;
}
diff_dir+=bytes;
return 0;
}
/*
* Sigh about something that differs.
*/
sigh(what)
char *what;
{
fprintf(msg_file, "%s: %s differs\n",
diff_name, what);
}
verify_volume()
{
int status;
#ifdef MTIOCTOP
struct mtop t;
int er;
#endif
if(!diff_buf)
diff_init();
#ifdef MTIOCTOP
t.mt_op = MTBSF;
t.mt_count = 1;
if((er=rmtioctl(archive,MTIOCTOP,&t))<0) {
if(errno!=EIO || (er=rmtioctl(archive,MTIOCTOP,&t))<0) {
#endif
if(rmtlseek(archive,0L,0)!=0) {
/* Lseek failed. Try a different method */
msg_perror("Couldn't rewind archive file for verify");
return;
}
#ifdef MTIOCTOP
}
}
#endif
ar_reading=1;
now_verifying = 1;
fl_read();
for(;;) {
status = read_header();
if(status==0) {
unsigned n;
n=0;
do {
n++;
status=read_header();
} while(status==0);
msg("VERIFY FAILURE: %d invalid header%s detected!",n,n==1?"":"s");
}
if(status==2 || status==EOF)
break;
diff_archive();
}
ar_reading=0;
now_verifying = 0;
}
int do_stat(statp)
struct stat *statp;
{
int err;
err = f_follow_links ? stat(diff_name, statp) : lstat(diff_name, statp);
if (err < 0) {
if (errno==ENOENT) {
fprintf(msg_file, "%s: does not exist\n",diff_name);
} else
msg_perror("can't stat file %s",diff_name);
/* skip_file((long)hstat.st_size);
different++;*/
return 1;
} else
return 0;
}
/*
* JK
* Diff'ing a sparse file with its counterpart on the tar file is a
* bit of a different story than a normal file. First, we must know
* what areas of the file to skip through, i.e., we need to contruct
* a sparsearray, which will hold all the information we need. We must
* compare small amounts of data at a time as we find it.
*/
diff_sparse_files(filesize)
int filesize;
{
int sparse_ind = 0;
char *buf;
int buf_size = RECORDSIZE;
union record *datarec;
int err;
long numbytes;
int amt_read = 0;
int size = filesize;
buf = (char *) malloc(buf_size * sizeof (char));
fill_in_sparse_array();
while (size > 0) {
datarec = findrec();
if (!sparsearray[sparse_ind].numbytes)
break;
/*
* 'numbytes' is nicer to write than
* 'sparsearray[sparse_ind].numbytes' all the time ...
*/
numbytes = sparsearray[sparse_ind].numbytes;
lseek(diff_fd, sparsearray[sparse_ind].offset, 0);
/*
* take care to not run out of room in our buffer
*/
while (buf_size < numbytes) {
buf = (char *) realloc(buf, buf_size * 2 * sizeof(char));
buf_size *= 2;
}
while (numbytes > RECORDSIZE) {
if ((err = read(diff_fd, buf, RECORDSIZE)) != RECORDSIZE) {
if (err < 0)
msg_perror("can't read %s", diff_name);
else
fprintf(msg_file, "%s: could only read %d of %d bytes\n",
err, numbytes);
break;
}
if (bcmp(buf, datarec->charptr, RECORDSIZE)) {
different++;
break;
}
numbytes -= err;
size -= err;
userec(datarec);
datarec = findrec();
}
if ((err = read(diff_fd, buf, numbytes)) != numbytes) {
if (err < 0)
msg_perror("can't read %s", diff_name);
else
fprintf(msg_file, "%s: could only read %d of %d bytes\n",
err, numbytes);
break;
}
if (bcmp(buf, datarec->charptr, numbytes)) {
different++;
break;
}
/* amt_read += numbytes;
if (amt_read >= RECORDSIZE) {
amt_read = 0;
userec(datarec);
datarec = findrec();
}*/
userec(datarec);
sparse_ind++;
size -= numbytes;
}
/*
* if the number of bytes read isn't the
* number of bytes supposedly in the file,
* they're different
*/
/* if (amt_read != filesize)
different++;*/
userec(datarec);
if (different)
fprintf(msg_file, "%s: data differs\n", diff_name);
}
/*
* JK
* This routine should be used more often than it is ... look into
* that. Anyhow, what it does is translate the sparse information
* on the header, and in any subsequent extended headers, into an
* array of structures with true numbers, as opposed to character
* strings. It simply makes our life much easier, doing so many
* comparisong and such.
*/
fill_in_sparse_array()
{
int ind;
/*
* allocate space for our scratch space; it's initially
* 10 elements long, but can change in this routine if
* necessary
*/
sp_array_size = 10;
sparsearray = (struct sp_array *) malloc(sp_array_size * sizeof(struct sp_array));
/*
* there are at most five of these structures in the header
* itself; read these in first
*/
for (ind = 0; ind < SPARSE_IN_HDR; ind++) {
if (!head->header.sp[ind].numbytes)
break;
sparsearray[ind].offset =
from_oct(1+12, head->header.sp[ind].offset);
sparsearray[ind].numbytes =
from_oct(1+12, head->header.sp[ind].numbytes);
}
/*
* if the header's extended, we gotta read in exhdr's till
* we're done
*/
if (head->header.isextended) {
/* how far into the sparsearray we are 'so far' */
static int so_far_ind = SPARSE_IN_HDR;
union record *exhdr;
for (;;) {
exhdr = findrec();
for (ind = 0; ind < SPARSE_EXT_HDR; ind++) {
if (ind+so_far_ind > sp_array_size-1) {
/*
* we just ran out of room in our
* scratch area - realloc it
*/
sparsearray = (struct sp_array *)
realloc(sparsearray,
sp_array_size*2*sizeof(struct sp_array));
sp_array_size *= 2;
}
/*
* convert the character strings into longs
*/
sparsearray[ind+so_far_ind].offset =
from_oct(1+12, exhdr->ext_hdr.sp[ind].offset);
sparsearray[ind+so_far_ind].numbytes =
from_oct(1+12, exhdr->ext_hdr.sp[ind].numbytes);
}
/*
* if this is the last extended header for this
* file, we can stop
*/
if (!exhdr->ext_hdr.isextended)
break;
else {
so_far_ind += SPARSE_EXT_HDR;
userec(exhdr);
}
}
/* be sure to skip past the last one */
userec(exhdr);
}
}
Gnutar-1.08/port.c 600 10 5 64307 4676573216 7051 /* Supporting routines which may sometimes be missing.
Copyright (C) 1988 Free Software Foundation
This file is part of GNU Tar.
GNU Tar 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 1, or (at your option)
any later version.
GNU Tar 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 Tar; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
/*
* @(#)port.c 1.15 87/11/05 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
#include
#include
#include
#include
#ifdef MSDOS
# include
#elif defined(COHERENT)
# include
#else
# include
#endif
#include "tar.h"
#include "port.h"
#if defined(COHERENT)
#define off_t daddr_t
#endif
#if defined(USG)
#include
#else
extern size_t strlen();
#endif
extern long baserec;
/*
* Some people (e.g. V7) don't have a #define for these.
*/
#ifndef O_BINARY
#define O_BINARY 0
#endif
#ifndef O_RDONLY
#define O_RDONLY 0
#endif
#ifndef NULL
#define NULL 0
#endif
/* JF: modified so all configuration information can appear here, instead of
being scattered through the file. Add all the machine-dependent #ifdefs
here */
#undef WANT_DUMB_GETDATE/* WANT_DUMB_GETDATE --> getdate() */
#undef WANT_VALLOC /* WANT_VALLOC --> valloc() */
#undef WANT_MKDIR /* WANT_MKDIR --> mkdir() rmdir() */
#undef WANT_STRING /* WANT_STRING --> index() bcopy() bzero() bcmp() */
#undef WANT_BZERO /* WANT_BZERO --> bzero() bcmp() execlp() */
/* EMUL_OPEN3 --> open3() */
#undef WANT_BSTUFF /* WANT_BSTUFF --> bcopy() bzero() bcmp() */
#undef WANT_MKNOD /* WANT_MKNOD --> mknod() link() chown() geteuid() */
#undef WANT_UTILS /* WANT_UTILS --> panic() ck_*() *_buffer()
merge_sort() quote_copy_string() un_quote_string() */
#undef WANT_CK_PIPE /* WANT_CK_PIPE --> ck_pipe() */
#undef WANT_GETWD /* WANT_GETWD --> getwd() */
#undef WANT_STRSTR /* WANT_STRSTR --> strstr() */
#undef WANT_FTRUNCATE /* WANT_FRTUNCATE --> frtruncate() */
/* Define only ONE of these four . . . */
#undef DOPRNT_MSG /* Define this one if you have _doprnt() and
no varargs support */
#undef VARARGS_MSG /* Define this one if you have varargs.h and
vfprintf() */
#undef STDC_MSG /* Define this one if you are using ANSI C and
and have vfprintf() */
#undef LOSING_MSG /* Define this one if you don't have any of the
above */
#ifdef USG
#define WANT_STRING
#define WANT_VALLOC
#if defined(sgi) && defined(mips)
#define WANG_GETWD
#endif
#endif
#ifdef COHERENT
#define WANT_MKDIR
#define WANT_BSTUFF
#define WANT_VALLOC
#define WANT_FTRUNCATE
#endif
#ifdef MINIX
#define WANT_BZERO
#endif
#ifdef MSDOS
#define WANT_STRING
#define WANT_MKNOD
#define WANT_UTILS
#define WANT_VALLOC
char TTY_NAME[] = "con";
#else /* not MSDOS */
char TTY_NAME[] ="/dev/tty";
#ifdef BSD42
/* BSD systems should do this even if __STDC__, because
we might be using an ANSI compiler without an ANSI library. */
#define DOPRNT_MSG
#else /* not BSD */
#if __STDC__
#define STDC_MSG
#else /* not ANSI C */
#define LOSING_MSG
#endif /* not ANSI C */
#endif /* not BSD */
#define WANT_UTILS
#define WANT_CK_PIPE
#define WANT_STRSTR
#endif /* not MSDOS */
/* End of system-dependent #ifdefs */
#ifdef WANT_DUMB_GETDATE
/* JF a getdate() routine takes a date/time/etc and turns it into a time_t */
/* This one is a quick hack I wrote in about five minutes to see if the N
option works. Someone should replace it with one that works */
/* This getdate takes an arg of the form mm/dd/yyyy hh:mm:ss and turns it
into a time_t . Its not well tested or anything. . . */
/* In general, you should use the getdate() supplied in getdate.y */
#define OFF_FROM GMT 18000 /* Change for your time zone! */
time_t
getdate(str)
char *str;
{
int month,day,year,hour,minute,second;
time_t ret;
int n;
#define SECS_PER_YEAR (365L*SECS_PER_DAY)
#define SECS_PER_LEAP_YEAR (366L*SECS_PER_DAY)
#define SECS_PER_DAY (24L*60*60)
static int days_per_month[2][12] = {
31,28,31,30,31,30,31,31,30,31,30,31,
31,29,31,30,31,30,31,31,30,31,30,31
};
static int days_per_year[2]={365,366};
month=day=year=hour=minute=second=0;
n=sscanf(str,"%d/%d/%d %d:%d:%d",&month,&day,&year,&hour,&minute,&second);
if(n<3)
return 0;
if(year<100)
year+=1900;
if(year<1970)
return 0;
ret=0;
ret+=OFF_FROM_GMT;
for(n=1970;n
ret+=SECS_PER_LEAP_YEAR;
else
ret+=SECS_PER_YEAR;
month--;
for(n=0;n
ret+=SECS_PER_DAY*days_per_month[1][n];
else
ret+=SECS_PER_DAY*days_per_month[0][n];
}
ret+=SECS_PER_DAY*(day-1);
ret+=second+minute*60+hour*60*60;
return ret;
}
#endif
#ifdef WANT_VALLOC
/*
* 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));
}
#endif
/*
* 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
*
* Modified by rmtodd@uokmax 6-28-87 -- when making an already existing dir,
* subroutine didn't return EEXIST. It does now.
*/
/*
* Make a directory. Compatible with the mkdir() system call on 4.2BSD.
*/
#ifdef WANT_MKDIR
int
mkdir(dpath, dmode)
char *dpath;
int dmode;
{
int cpid, status;
struct stat statbuf;
extern int errno;
if (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;
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;
}
int
rmdir(dpath)
char *dpath;
{
int cpid, status;
struct stat statbuf;
extern int errno;
if (stat(dpath,&statbuf) != 0) {
/* Stat just set errno. We don't have to */
return -1;
}
switch (cpid = fork()) {
case -1: /* Error in fork() */
return(-1); /* Errno is set already */
case 0: /* Child process */
execl("/bin/rmdir", "rmdir", 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 WANT_STRING
/*
* Translate V7 style into Sys V style.
*/
#include
#include
char *
index (s, c)
char *s;
int c;
{
return (strchr (s, c));
}
char *
rindex(s,c)
char *s;
int c;
{
return strrchr(s,c);
}
void
bcopy (s1, s2, n)
char *s1, *s2;
int n;
{
(void) memcpy (s2, s1, n);
}
void
bzero (s1, n)
char *s1;
int n;
{
(void) memset(s1, 0, n);
}
int
bcmp(s1, s2, n)
char *s1, *s2;
int n;
{
return memcmp(s1, s2, n);
}
#endif
#ifdef WANT_BSTUFF
/*
* Provide bxxx as above, for but not rindex,etc.
*/
char *memcpy();
char *memset();
int *memcmp();
void
bcopy (s1, s2, n)
char *s1, *s2;
int n;
{
(void) memcpy (s2, s1, n);
}
void
bzero (s1, n)
char *s1;
int n;
{
(void) memset(s1, 0, n);
}
int
bcmp(s1, s2, n)
char *s1, *s2;
int n;
{
return memcmp(s1, s2, n);
}
#endif
#ifdef WANT_BZERO
/* Minix has bcopy but not bzero, and no memset. Thanks, Andy. */
void
bzero (s1, n)
register char *s1;
register int n;
{
while (n--) *s1++ = '\0';
}
/* It also has no bcmp() */
int
bcmp (s1, s2, n)
register char *s1,*s2;
register int n;
{
for ( ; n-- ; ++s1, ++s2) {
if (*s1 != *s2) return *s1 - *s2;
}
return 0;
}
/*
* Groan, Minix doesn't have execlp either!
*
* execlp(file,arg0,arg1...argn,(char *)NULL)
* exec a program, automatically searching for the program through
* all the directories on the PATH.
*
* This version is naive about variable argument lists, it assumes
* a straightforward C calling sequence. If your system has odd stacks
* *and* doesn't have execlp, YOU get to fix it.
*/
int
execlp(filename, arg0)
char *filename, *arg0;
{
register char *p, *path;
register char *fnbuffer;
char **argstart = &arg0;
struct stat statbuf;
extern char **environ;
extern int errno;
extern char *malloc(), *getenv(), *index();
if ((p = getenv("PATH")) == NULL) {
/* couldn't find path variable -- try to exec given filename */
return execve(filename, argstart, environ);
}
/*
* make a place to build the filename. We malloc larger than we
* need, but we know it will fit in this.
*/
fnbuffer = malloc( strlen(p) + 1 + strlen(filename) );
if (fnbuffer == NULL) {
errno = ENOMEM;
return -1;
}
/*
* try each component of the path to see if the file's there
* and executable.
*/
for (path = p ; path ; path = p) {
/* construct full path name to try */
if ((p = index(path,':')) == NULL) {
strcpy(fnbuffer, path);
} else {
strncpy(fnbuffer, path, p-path);
fnbuffer[p-path] = '\0';
p++; /* Skip : for next time */
}
if (strlen(fnbuffer) != 0)
strcat(fnbuffer,"/");
strcat(fnbuffer,filename);
/* check to see if file is there and is a normal file */
if (stat(fnbuffer, &statbuf) < 0) {
if (errno == ENOENT)
continue; /* file not there,keep on looking */
else
goto fail; /* failed for some reason, return */
}
if ( (statbuf.st_mode & S_IFMT) != S_IFREG) continue;
if (execve(fnbuffer, argstart, environ) < 0
&& errno != ENOENT
&& errno != ENOEXEC) {
/* failed, for some other reason besides "file
* not found" or "not a.out format"
*/
goto fail;
}
/*
* If we got error ENOEXEC, the file is executable but is
* not an object file. Try to execute it as a shell script,
* returning error if we can't execute /bin/sh.
*
* FIXME, this code is broken in several ways. Shell
* scripts should not in general be executed by the user's
* SHELL variable program. On more mature systems, the
* script can specify with #!/bin/whatever. Also, this
* code clobbers argstart[-1] if the exec of the shell
* fails.
*/
if (errno == ENOEXEC) {
char *shell;
/* Try to execute command "sh arg0 arg1 ..." */
if ((shell = getenv("SHELL")) == NULL)
shell = "/bin/sh";
argstart[-1] = shell;
argstart[0] = fnbuffer;
execve(shell, &argstart[-1], environ);
goto fail; /* Exec didn't work */
}
/*
* If we succeeded, the execve() doesn't return, so we
* can only be here is if the file hasn't been found yet.
* Try the next place on the path.
*/
}
/* all attempts failed to locate the file. Give up. */
errno = ENOENT;
fail:
free(fnbuffer);
return -1;
}
#endif
#ifdef EMUL_OPEN3
#include "open3.h"
/*
* open3 -- routine to emulate the 3-argument open system
* call that is present in most modern Unix systems.
* This version attempts to support all the flag bits except for O_NDELAY
* and O_APPEND, which are silently ignored. The emulation is not as efficient
* as the real thing (at worst, 4 system calls instead of one), but there's
* not much I can do about that.
*
* Written 6/10/87 by rmtodd@uokmax
*
* open3(path, flag, mode)
* Attempts to open the file specified by
* the given pathname. The following flag bits (#defined in tar.h)
* specify options to the routine:
* O_RDONLY file open for read only
* O_WRONLY file open for write only
* O_RDWR file open for both read & write
* (Needless to say, you should only specify one of the above).
* O_CREAT file is created with specified mode if it needs to be.
* O_TRUNC if file exists, it is truncated to 0 bytes
* O_EXCL used with O_CREAT--routine returns error if file exists
* Function returns file descriptor if successful, -1 and errno if not.
*/
/*
* array to give arguments to access for various modes
* FIXME, this table depends on the specific integer values of O_XXX,
* and also contains integers (args to 'access') that should be #define's.
*/
static int modes[] =
{
04, /* O_RDONLY */
02, /* O_WRONLY */
06, /* O_RDWR */
06, /* invalid but we'd better cope -- O_WRONLY+O_RDWR */
};
/* Shut off the automatic emulation of open(), we'll need it. */
#undef open
int
open3(path, flags, mode)
char *path;
int flags, mode;
{
extern int errno;
int exists = 1;
int call_creat = 0;
int fd;
/*
* We actually do the work by calling the open() or creat() system
* call, depending on the flags. Call_creat is true if we will use
* creat(), false if we will use open().
*/
/*
* See if the file exists and is accessible in the requested mode.
*
* Strictly speaking we shouldn't be using access, since access checks
* against real uid, and the open call should check against euid.
* Most cases real uid == euid, so it won't matter. FIXME.
* FIXME, the construction "flags & 3" and the modes table depends
* on the specific integer values of the O_XXX #define's. Foo!
*/
if (access(path,modes[flags & 3]) < 0) {
if (errno == ENOENT) {
/* the file does not exist */
exists = 0;
} else {
/* probably permission violation */
if (flags & O_EXCL) {
/* Oops, the file exists, we didn't want it. */
/* No matter what the error, claim EEXIST. */
errno = EEXIST;
}
return -1;
}
}
/* if we have the O_CREAT bit set, check for O_EXCL */
if (flags & O_CREAT) {
if ((flags & O_EXCL) && exists) {
/* Oops, the file exists and we didn't want it to. */
errno = EEXIST;
return -1;
}
/*
* If the file doesn't exist, be sure to call creat() so that
* it will be created with the proper mode.
*/
if (!exists) call_creat = 1;
} else {
/* If O_CREAT isn't set and the file doesn't exist, error. */
if (!exists) {
errno = ENOENT;
return -1;
}
}
/*
* If the O_TRUNC flag is set and the file exists, we want to call
* creat() anyway, since creat() guarantees that the file will be
* truncated and open()-for-writing doesn't.
* (If the file doesn't exist, we're calling creat() anyway and the
* file will be created with zero length.)
*/
if ((flags & O_TRUNC) && exists) call_creat = 1;
/* actually do the call */
if (call_creat) {
/*
* call creat. May have to close and reopen the file if we
* want O_RDONLY or O_RDWR access -- creat() only gives
* O_WRONLY.
*/
fd = creat(path,mode);
if (fd < 0 || (flags & O_WRONLY)) return fd;
if (close(fd) < 0) return -1;
/* Fall out to reopen the file we've created */
}
/*
* calling old open, we strip most of the new flags just in case.
*/
return open(path, flags & (O_RDONLY|O_WRONLY|O_RDWR|O_BINARY));
}
#endif
#ifdef WANT_MKNOD
/* Fake mknod by complaining */
int
mknod(path, mode, dev)
char *path;
unsigned short mode;
dev_t dev;
{
extern int errno;
int fd;
errno = ENXIO; /* No such device or address */
return -1; /* Just give an error */
}
/* Fake links by copying */
int
link(path1, path2)
char *path1;
char *path2;
{
char buf[256];
int ifd, ofd;
int nrbytes;
int nwbytes;
fprintf(stderr, "%s: %s: cannot link to %s, copying instead\n",
tar, path1, path2);
if ((ifd = open(path1, O_RDONLY|O_BINARY)) < 0)
return -1;
if ((ofd = creat(path2, 0666)) < 0)
return -1;
setmode(ofd, O_BINARY);
while ((nrbytes = read(ifd, buf, sizeof(buf))) > 0) {
if ((nwbytes = write(ofd, buf, nrbytes)) != nrbytes) {
nrbytes = -1;
break;
}
}
/* Note use of "|" rather than "||" below: we want to close
* the files even if an error occurs.
*/
if ((nrbytes < 0) | (0 != close(ifd)) | (0 != close(ofd))) {
unlink(path2);
return -1;
}
return 0;
}
/* everyone owns everything on MS-DOS (or is it no one owns anything?) */
int
chown(path, uid, gid)
char *path;
int uid;
int gid;
{
return 0;
}
int
geteuid()
{
return 0;
}
#endif /* WANT_MKNOD */
#ifdef WANT_UTILS
/* Stash argv[0] here so panic will know what the program is called */
char *myname = 0;
void
panic(s)
char *s;
{
if(myname)
fprintf(stderr,"%s:",myname);
fprintf(stderr,s);
putc('\n',stderr);
exit(12);
}
char *
ck_realloc(ptr,size)
char *ptr;
size_t size;
{
char *ret;
char *realloc();
ret=realloc(ptr,size);
if(ret==0)
panic("Couldn't re-allocate memory");
return ret;
}
/* Implement a variable sized buffer of 'stuff'. We don't know what it is,
nor do we care, as long as it doesn't mind being aligned on a char boundry.
*/
struct buffer {
int allocated;
int length;
char *b;
};
#define MIN_ALLOCATE 50
char *
init_buffer()
{
struct buffer *b;
char *ck_malloc();
b=(struct buffer *)ck_malloc(sizeof(struct buffer));
b->allocated=MIN_ALLOCATE;
b->b=(char *)ck_malloc(MIN_ALLOCATE);
b->length=0;
return (char *)b;
}
void
flush_buffer(bb)
char *bb;
{
struct buffer *b;
b=(struct buffer *)bb;
free(b->b);
b->b=0;
b->allocated=0;
b->length=0;
free(b);
}
void
add_buffer(bb,p,n)
char *bb;
char *p;
int n;
{
struct buffer *b;
b=(struct buffer *)bb;
if(b->length+n>b->allocated) {
b->allocated*=2;
b->b=(char *)ck_realloc(b->b,b->allocated);
}
bcopy(p,b->b+b->length,n);
b->length+=n;
}
char *
get_buffer(bb)
char *bb;
{
struct buffer *b;
b=(struct buffer *)bb;
return b->b;
}
char *
merge_sort(list,n,off,cmp)
char *list;
int (*cmp)();
unsigned n;
int off;
{
char *ret;
char *alist,*blist;
unsigned alength,blength;
char *tptr;
int tmp;
char **prev;
#define NEXTOF(ptr) (* ((char **)(((char *)(ptr))+off) ) )
if(n==1)
return list;
if(n==2) {
if((*cmp)(list,NEXTOF(list))>0) {
ret=NEXTOF(list);
NEXTOF(ret)=list;
NEXTOF(list)=0;
return ret;
}
return list;
}
alist=list;
alength=(n+1)/2;
blength=n/2;
for(tptr=list,tmp=(n-1)/2;tmp;tptr=NEXTOF(tptr),tmp--)
;
blist=NEXTOF(tptr);
NEXTOF(tptr)=0;
alist=merge_sort(alist,alength,off,cmp);
blist=merge_sort(blist,blength,off,cmp);
prev = &ret;
for(;alist && blist;) {
if((*cmp)(alist,blist)<0) {
tptr=NEXTOF(alist);
*prev = alist;
prev = &(NEXTOF(alist));
alist=tptr;
} else {
tptr=NEXTOF(blist);
*prev = blist;
prev = &(NEXTOF(blist));
blist=tptr;
}
}
if(alist)
*prev = alist;
else
*prev = blist;
return ret;
}
char *
ck_malloc(size)
size_t size;
{
char *ret;
char *malloc();
ret=malloc(size);
if(ret==0)
panic("Couldn't allocate memory");
return ret;
}
void
ck_close(fd)
int fd;
{
if(close(fd)<0) {
msg_perror("can't close a file #%d",fd);
exit(EX_SYSTEM);
}
}
#include
/* Quote_copy_string is like quote_string, but instead of modifying the
string in place, it malloc-s a copy of the string, and returns that.
If the string does not have to be quoted, it returns the NULL string.
The allocated copy can, of course, be freed with free() after the
caller is done with it.
*/
char *
quote_copy_string(string)
char *string;
{
char *from_here;
char *to_there = 0;
char *copy_buf = 0;
int c;
int copying = 0;
from_here=string;
while(*from_here) {
c= *from_here++;
if(c=='\\') {
if(!copying) {
int n;
n=(from_here-string)-1;
copying++;
copy_buf=(char *)malloc(n+3+strlen(from_here)*4);
if(!copy_buf)
return 0;
bcopy(string,copy_buf,n);
to_there=copy_buf+n;
}
*to_there++='\\';
*to_there++='\\';
} else if(isprint(c)) {
if(copying)
*to_there++= c;
} else {
if(!copying) {
int n;
n=(from_here-string)-1;
copying++;
copy_buf=(char *)malloc(n+3+strlen(from_here)*4);
if(!copy_buf)
return 0;
bcopy(string,copy_buf,n);
to_there=copy_buf+n;
}
*to_there++='\\';
if(c=='\n') *to_there++='n';
else if(c=='\t') *to_there++='t';
else if(c=='\f') *to_there++='f';
else if(c=='\b') *to_there++='b';
else if(c=='\r') *to_there++='r';
else if(c=='\177') *to_there++='?';
else {
to_there[0]=(c>>6)+'0';
to_there[1]=((c>>3)&07)+'0';
to_there[2]=(c&07)+'0';
to_there+=3;
}
}
}
if(copying) {
*to_there='\0';
return copy_buf;
}
return (char *)0;
}
/* Un_quote_string takes a quoted c-string (like those produced by
quote_string or quote_copy_string and turns it back into the
un-quoted original. This is done in place.
*/
/* There is no un-quote-copy-string. Write it yourself */
char *un_quote_string(string)
char *string;
{
char *ret;
char *from_here;
char *to_there;
int tmp;
ret=string;
to_there=string;
from_here=string;
while(*from_here) {
if(*from_here!='\\') {
if(from_here!=to_there)
*to_there++= *from_here++;
else
from_here++,to_there++;
continue;
}
switch(*++from_here) {
case '\\':
*to_there++= *from_here++;
break;
case 'n':
*to_there++= '\n';
from_here++;
break;
case 't':
*to_there++= '\t';
from_here++;
break;
case 'f':
*to_there++= '\f';
from_here++;
break;
case 'b':
*to_there++= '\b';
from_here++;
break;
case 'r':
*to_there++= '\r';
from_here++;
break;
case '?':
*to_there++= 0177;
from_here++;
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
tmp= *from_here - '0';
from_here++;
if(*from_here<'0' || *from_here>'7') {
*to_there++= tmp;
break;
}
tmp= tmp*8 + *from_here-'0';
from_here++;
if(*from_here<'0' || *from_here>'7') {
*to_there++= tmp;
break;
}
tmp=tmp*8 + *from_here-'0';
from_here++;
*to_there=tmp;
break;
default:
ret=0;
*to_there++='\\';
*to_there++= *from_here++;
break;
}
}
*to_there++='\0';
return ret;
}
#endif
#ifdef WANT_CK_PIPE
void ck_pipe(pipes)
int *pipes;
{
if(pipe(pipes)<0) {
msg_perror("can't open a pipe");
exit(EX_SYSTEM);
}
}
#endif
#ifdef WANT_GETWD
char *
getwd(path)
char *path;
{
FILE *fp;
FILE *popen();
fp=popen("pwd","r");
if(!fp)
return 0;
if(!fgets(path,100,fp))
return 0;
if(!pclose(fp))
return 0;
return path;
}
#endif /* WANT_CK_PIPE */
#ifdef WANT_STRSTR
/*
* strstr - find first occurrence of wanted in s
*/
char * /* found string, or NULL if none */
strstr(s, wanted)
char *s;
char *wanted;
{
register char *scan;
register size_t len;
register char firstc;
extern int strcmp();
/*
* The odd placement of the two tests is so "" is findable.
* Also, we inline the first char for speed.
* The ++ on scan has been moved down for optimization.
*/
firstc = *wanted;
len = strlen(wanted);
for (scan = s; *scan != firstc || strncmp(scan, wanted, len) != 0; )
if (*scan++ == '\0')
return (char *)0;
return scan;
}
#endif
#ifdef WANT_FTRUNCATE
int
ftruncate(fd, length)
int fd;
off_t length;
{
errno = EIO;
return -1;
}
#endif
extern FILE *msg_file;
#ifdef STDC_MSG
#include
void
msg(char *str,...)
{
va_list args;
va_start(args,str);
fflush(msg_file);
fprintf(stderr,"%s: ",tar);
if(f_sayblock)
fprintf(stderr,"rec %d: ",baserec + (ar_record - ar_block));
vfprintf(stderr,str,args);
va_end(args);
putc('\n',stderr);
fflush(stderr);
}
void
msg_perror(char *str,...)
{
va_list args;
int save_e;
extern int errno;
save_e=errno;
fflush(msg_file);
fprintf(stderr,"%s: ",tar);
if(f_sayblock)
fprintf(stderr,"rec %d: ",baserec + (ar_record - ar_block));
va_start(args,str);
vfprintf(stderr,str,args);
va_end(args);
errno=save_e;
perror(" ");
fflush(stderr);
}
#endif
#ifdef VARARGS_MSG
#include
void msg(str,va_alist)
char *str;
va_dcl
{
va_list args;
fflush(msg_file);
fprintf(stderr,"%s: ",tar);
if(f_sayblock)
fprintf(stderr,"rec %d: ",baserec + (ar_record - ar_block));
va_start(args);
vfprintf(stderr,str,args);
va_end(args);
putc('\n',stderr);
fflush(stderr);
}
void msg_perror(str,va_alist)
char *str;
va_dcl
{
va_list args;
int save_e;
extern int errno;
save_e=errno;
fflush(msg_file);
fprintf(stderr,"%s: ",tar);
if(f_sayblock)
fprintf(stderr,"rec %d: ",baserec + (ar_record - ar_block));
va_start(args);
vfprintf(stderr,str,args);
va_end(args);
errno=save_e;
perror(" ");
fflush(stderr);
}
#endif
#ifdef DOPRNT_MSG
void msg(str,args)
char *str;
int args;
{
fflush(msg_file);
fprintf(stderr,"%s: ",tar);
if(f_sayblock)
fprintf(stderr,"rec %d: ",baserec + (ar_record - ar_block));
_doprnt(str, &args, stderr);
putc('\n',stderr);
fflush(stderr);
}
void msg_perror(str,args)
char *str;
{
int save_e;
extern int errno;
save_e=errno;
fflush(msg_file);
fprintf(stderr,"%s: ",tar);
if(f_sayblock)
fprintf(stderr,"rec %d: ",baserec + (ar_record - ar_block));
_doprnt(str, &args, stderr);
errno=save_e;
perror(" ");
fflush(stderr);
}
#endif
#ifdef LOSING_MSG
void msg(str,a1,a2,a3,a4,a5,a6)
char *str;
{
fflush(msg_file);
fprintf(stderr,"%s: ",tar);
if(f_sayblock)
fprintf(stderr,"rec %d: ",baserec + (ar_record - ar_block));
fprintf(stderr,str,a1,a2,a3,a4,a5,a6);
putc('\n',stderr);
fflush(stderr);
}
void msg_perror(str,a1,a2,a3,a4,a5,a6)
char *str;
{
int save_e;
extern int errno;
save_e=errno;
fflush(msg_file);
fprintf(stderr,"%s: ",tar);
if(f_sayblock)
fprintf(stderr,"rec %d: ",baserec + (ar_record - ar_block));
fprintf(stderr,str,a1,a2,a3,a4,a5,a6);
fprintf(stderr,": ");
errno=save_e;
perror(" ");
}
#endif
Gnutar-1.08/wildmat.c 600 10 5 10156 4412273475 7507 /* Wildcard matching routines.
Copyright (C) 1988 Free Software Foundation
This file is part of GNU Tar.
GNU Tar 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 1, or (at your option)
any later version.
GNU Tar 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 Tar; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
/*
* @(#)wildmat.c 1.3 87/11/06
*
From: [email protected] (Rich Salz)
Newsgroups: net.sources
Subject: Small shell-style pattern matcher
Message-ID: <[email protected]>
Date: 27 Nov 86 00:06:40 GMT
There have been several regular-expression subroutines and one or two
filename-globbing routines in mod.sources. They handle lots of
complicated patterns. This small piece of code handles the *?[]\
wildcard characters the way the standard Unix(tm) shells do, with the
addition that "[^.....]" is an inverse character class -- it matches
any character not in the range ".....". Read the comments for more
info.
For my application, I had first ripped off a copy of the "glob" routine
from within the find source, but that code is bad news: it recurses
on every character in the pattern. I'm putting this replacement in the
public domain. It's small, tight, and iterative. Compile with -DTEST
to get a test driver. After you're convinced it works, install in
whatever way is appropriate for you.
I would like to hear of bugs, but am not interested in additions; if I
were, I'd use the code I mentioned above.
*/
/*
** Do shell-style pattern matching for ?, \, [], and * characters.
** Might not be robust in face of malformed patterns; e.g., "foo[a-"
** could cause a segmentation violation.
**
** Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986.
*/
/*
* Modified 6Nov87 by John Gilmore (hoptoad!gnu) to return a "match"
* if the pattern is immediately followed by a "/", as well as \0.
* This matches what "tar" does for matching whole subdirectories.
*
* The "*" code could be sped up by only recursing one level instead
* of two for each trial pattern, perhaps, and not recursing at all
* if a literal match of the next 2 chars would fail.
*/
#define TRUE 1
#define FALSE 0
static int
Star(s, p)
register char *s;
register char *p;
{
while (wildmat(s, p) == FALSE)
if (*++s == '\0')
return(FALSE);
return(TRUE);
}
int
wildmat(s, p)
register char *s;
register char *p;
{
register int last;
register int matched;
register int reverse;
for ( ; *p; s++, p++)
switch (*p) {
case '\\':
/* Literal match with following character; fall through. */
p++;
default:
if (*s != *p)
return(FALSE);
continue;
case '?':
/* Match anything. */
if (*s == '\0')
return(FALSE);
continue;
case '*':
/* Trailing star matches everything. */
return(*++p ? Star(s, p) : TRUE);
case '[':
/* [^....] means inverse character class. */
if (reverse = p[1] == '^')
p++;
for (last = 0400, matched = FALSE; *++p && *p != ']'; last = *p)
/* This next line requires a good C compiler. */
if (*p == '-' ? *s <= *++p && *s >= last : *s == *p)
matched = TRUE;
if (matched == reverse)
return(FALSE);
continue;
}
/* For "tar" use, matches that end at a slash also work. --hoptoad!gnu */
return(*s == '\0' || *s == '/');
}
#ifdef TEST
#include
extern char *gets();
main()
{
char pattern[80];
char text[80];
while (TRUE) {
printf("Enter pattern: ");
if (gets(pattern) == NULL)
break;
while (TRUE) {
printf("Enter text: ");
if (gets(text) == NULL)
exit(0);
if (text[0] == '\0')
/* Blank line; go back and get a new pattern. */
break;
printf(" %d\n", wildmat(text, pattern));
}
}
exit(0);
}
#endif /* TEST */
Gnutar-1.08/getopt.c 600 10 5 36320 4543627443 7354 /* Getopt for GNU.
Copyright (C) 1987, 1989 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */
/* 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 _POSIX_OPTION_ORDER 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
/* If compiled with GNU C, use the built-in alloca */
#ifdef __GNUC__
#define alloca __builtin_alloca
#else /* not __GNUC__ */
#ifdef sparc
#include
#else
char *alloca ();
#endif
#endif /* not __GNUC__ */
#ifdef USG
#define bcopy(s, d, l) memcpy((d), (s), (l))
#define index strchr
#endif
char *getenv ();
char *index ();
char *malloc ();
/* 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 = 0;
/* 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. */
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;
/* 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
_POSIX_OPTION_ORDER 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.
PERMUTE is the default. We permute the contents of `argv' as we scan,
so that eventually all the 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 zero.
Using `-' as the first character of the list of option characters
requests 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;
/* Describe the long-named options requested by the application.
_GETOPT_LONG_OPTIONS is a vector of `struct option' terminated by an
element containing a name which is zero.
The field `has_arg' is 1 if the option takes an argument,
2 if it takes an optional argument. */
struct option
{
char *name;
int has_arg;
int *flag;
int val;
};
struct option *_getopt_long_options;
/* Name of long-named option actually found. */
char *_getopt_option_name;
/* Index in _GETOPT_LONG_OPTIONS of the long-named option actually found.
Only valid if getopt returns 0, meaning a long-named option was found. */
int option_index;
/* 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 nonopts_size
= (last_nonopt - first_nonopt) * sizeof (char *);
char **temp = (char **) alloca (nonopts_size);
/* Interchange the two blocks of data in argv. */
bcopy (&argv[first_nonopt], temp, nonopts_size);
bcopy (&argv[last_nonopt], &argv[first_nonopt],
(optind - last_nonopt) * sizeof (char *));
bcopy (temp, &argv[first_nonopt + optind - last_nonopt],
nonopts_size);
/* Update records for the slots the non-options now occupy. */
first_nonopt += (optind - last_nonopt);
last_nonopt = optind;
}
/* 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 `-', it requests a different method of handling the
non-option ARGV-elements. See the comments about RETURN_IN_ORDER, above.
Long-named options are marked 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. */
int
getopt (argc, argv, optstring)
int argc;
char **argv;
char *optstring;
{
optarg = 0;
/* Initialize the internal data when the first call is made.
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. */
if (optind == 0)
{
first_nonopt = last_nonopt = optind = 1;
nextchar = 0;
/* Determine how to handle the ordering of options and nonoptions. */
if (optstring[0] == '-')
ordering = RETURN_IN_ORDER;
else if (getenv ("_POSIX_OPTION_ORDER") != 0)
ordering = REQUIRE_ORDER;
else
ordering = PERMUTE;
}
if (nextchar == 0 || *nextchar == 0)
{
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 (argv);
else if (last_nonopt != optind)
first_nonopt = optind;
/* Now skip any additional non-options
and extend the range of non-options previously skipped. */
while (optind < argc
&& (argv[optind][0] != '-'
|| argv[optind][1] == 0)
&& (_getopt_long_options == 0
|| argv[optind][0] != '+'
|| argv[optind][1] == 0))
optind++;
last_nonopt = optind;
}
/* 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 (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)
&& (_getopt_long_options == 0
|| argv[optind][0] != '+' || argv[optind][1] == 0))
{
if (ordering == REQUIRE_ORDER)
return EOF;
optarg = argv[optind++];
return 0;
}
/* We have found another option-ARGV-element.
Start decoding its characters. */
nextchar = argv[optind] + 1;
}
if (_getopt_long_options != 0 && argv[optind][0] == '+')
{
struct option *p;
char *s = nextchar;
int exact = 0;
int ambig = 0;
struct option *pfound = 0;
int indfound;
while (*s && *s != '=') s++;
/* Test all options for either exact match or abbreviated matches. */
for (p = _getopt_long_options, option_index = 0; p->name;
p++, option_index++)
if (!strncmp (p->name, nextchar, s - nextchar))
{
if (s - nextchar == strlen (p->name))
{
/* Exact match found. */
pfound = p;
indfound = option_index;
exact = 1;
break;
}
else if (pfound == 0)
{
/* First nonexact match found. */
pfound = p;
indfound = option_index;
}
else
/* Second nonexact match found. */
ambig = 1;
}
if (ambig && !exact)
{
fprintf (stderr, "%s: option `+%s' is ambiguous\n",
argv[0], nextchar);
nextchar += strlen (nextchar);
return '?';
}
if (pfound != 0)
{
option_index = indfound;
optind++;
if (*s)
{
if (pfound->has_arg > 0)
optarg = s + 1;
else
{
fprintf (stderr, "%s: option `+%s' doesn't allow an argument\n",
argv[0], pfound->name);
nextchar += strlen (nextchar);
return '?';
}
}
else if (pfound->has_arg)
{
if (optind < argc)
optarg = argv[optind++];
else if (pfound->has_arg != 2)
{
fprintf (stderr, "%s: option `+%s' requires an argument\n",
argv[0], pfound->name);
nextchar += strlen (nextchar);
return '?';
}
}
_getopt_option_name = pfound->name;
nextchar += strlen (nextchar);
if (pfound->flag)
*(pfound->flag) = pfound->val;
return 0;
}
if (opterr != 0)
fprintf (stderr, "%s: unrecognized option `+%s'\n",
argv[0], nextchar);
nextchar += strlen (nextchar);
return '?';
}
/* Look at and handle the next option-character. */
{
char c = *nextchar++;
char *temp = index (optstring, c);
/* Increment `optind' when we start to process its last character. */
if (*nextchar == 0)
optind++;
if (temp == 0 || c == ':')
{
if (opterr != 0)
{
if (c < 040 || c >= 0177)
fprintf (stderr, "%s: unrecognized option, character code 0%o\n",
argv[0], c);
else
fprintf (stderr, "%s: unrecognized option `-%c'\n",
argv[0], 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 = 0;
nextchar = 0;
}
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 != 0)
fprintf (stderr, "%s: no argument for `-%c' option\n",
argv[0], c);
c = '?';
}
else
/* We already incremented `optind' once;
increment it again when taking next ARGV-elt as argument. */
optarg = argv[optind++];
nextchar = 0;
}
}
return c;
}
}
#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;
{
char c;
int digit_optind = 0;
while (1)
{
int this_option_optind = optind;
if ((c = getopt (argc, argv, "abc:d:0123456789")) == 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");
}
return 0;
}
#endif /* TEST */
Gnutar-1.08/getopt1.c 600 10 5 5237 4523413214 7403 /* Getopt for GNU.
Copyright (C) 1987, 1989 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "getopt.h"
int
getopt_long (argc, argv, options, long_options, opt_index)
int argc;
char **argv;
char *options;
struct option *long_options;
int *opt_index;
{
int val;
_getopt_long_options = long_options;
val = getopt (argc, argv, options);
if (val == 0)
*opt_index = option_index;
return val;
}
#ifdef TEST
#include
int
main (argc, argv)
int argc;
char **argv;
{
char c;
int digit_optind = 0;
while (1)
{
int this_option_optind = optind;
char *name = '\0';
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;
if (option_index)
{
printf ("option %s", (long_options[option_index]).name);
if (optarg)
printf (" with arg %s", optarg);
printf ("\n");
}
else
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");
}
return 0;
}
#endif /* TEST */
Gnutar-1.08/alloca.c 600 10 5 12540 4676057614 7307 /*
alloca -- (mostly) portable public-domain implementation -- D A Gwyn
last edit: 86/05/30 rms
include config.h, since on VMS it renames some symbols.
Use xmalloc instead of malloc.
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.
It should work under any C implementation that uses an
actual procedure stack (as opposed to a linked list of
frames). 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.
*/
#ifndef lint
static char SCCSid[] = "@(#)alloca.c 1.1"; /* for the "what" utility */
#endif
#ifdef emacs
#include "config.h"
#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 */
#ifdef X3J11
typedef void *pointer; /* generic pointer type */
#else
typedef char *pointer; /* generic pointer type */
#endif
#define NULL 0 /* null pointer constant */
extern void free();
extern pointer xmalloc();
/*
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 (/* void */)
{
static char *addr = NULL; /* address of first
`dummy', once known */
auto char dummy; /* to get stack address */
if (addr == NULL)
{ /* initial entry */
addr = &dummy;
find_stack_direction (); /* recurse once */
}
else /* second entry */
if (&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;
/*
alloca( size ) returns 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.
*/
static header *last_alloca_header = NULL; /* -> last alloca header */
pointer
alloca (size) /* returns pointer to storage */
unsigned size; /* # bytes to allocate */
{
auto char probe; /* probes stack depth: */
register char *depth = &probe;
#if STACK_DIRECTION == 0
if (STACK_DIR == 0) /* unknown growth direction */
find_stack_direction ();
#endif
/* Reclaim garbage, defined as all alloca()ed storage that
was allocated from deeper in the stack than currently. */
{
register header *hp; /* traverses linked list */
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 */
}
if (size == 0)
return NULL; /* no allocation required */
/* Allocate combined header + user data storage. */
{
register pointer new = xmalloc (sizeof (header) + size);
/* address of header */
((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));
}
}
pointer xmalloc(n)
unsigned int n;
{
extern pointer malloc();
pointer cp;
static char mesg[] = "xmalloc: no memory!\n";
cp = malloc(n);
if (! cp) {
write (2, mesg, sizeof(mesg) - 1);
exit(1);
}
return cp;
}
Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!
This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.
But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/