1857 lines
56 KiB (Stored with Git LFS)
Bash
1857 lines
56 KiB (Stored with Git LFS)
Bash
#! /bin/sh
|
||
# This is a shell archive. Remove anything before this line, then unpack
|
||
# it by saving it into a file and typing "sh file". To overwrite existing
|
||
# files, type "sh file -c". You can also feed this as standard input via
|
||
# unshar, or by typing "sh <file", e.g.. If this archive is complete, you
|
||
# will see the following message at the end:
|
||
# "End of shell archive."
|
||
# Contents: README buffer.man Makefile buffer.c sem.c sem.h COPYING
|
||
# Wrapped by lmjm@toucan.doc.ic.ac.uk on Tue Jul 29 11:24:59 1997
|
||
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
|
||
if test -f 'README' -a "${1}" != "-c" ; then
|
||
echo shar: Will not clobber existing file \"'README'\"
|
||
else
|
||
echo shar: Extracting \"'README'\" \(2208 characters\)
|
||
sed "s/^X//" >'README' <<'END_OF_FILE'
|
||
XThis is a program designed to speed up writing tapes on remote tape
|
||
Xdrives. Requirements are shared memory and locks which normally
|
||
Xmeans that these are supported in your kernel.
|
||
X
|
||
XBuffer has been tested under SunOS 4.0.*, SunOS 4.1.*, Solarix, HP-UX 7.0,
|
||
Xand Gould UTX 2.1A (sv universe).
|
||
X
|
||
XThe program splits itself into two processes. The first process reads
|
||
X(and reblocks) from stdin into a shared memory buffer. The second
|
||
Xwrites from the shared memory buffer to stdout. Doing it this way
|
||
Xmeans that the writing side effectly sits in a tight write loop and
|
||
Xdoesn't have to wait for input. Similarly for the input side. It is
|
||
Xthis waiting that slows down other reblocking processes, like dd.
|
||
X
|
||
XI run an archive and need to write large chunks out to tape regularly
|
||
Xwith an ethernet in the way. Using 'buffer' in a command like:
|
||
X
|
||
X tar cvf - stuff | rsh somebox buffer -o /dev/rst8
|
||
X
|
||
Xis a factor of 5 faster than the best alternative, gnu tar with its
|
||
Xremote tape option:
|
||
X
|
||
X tar cvf somebox:/dev/rst8 stuff
|
||
X
|
||
XWe have been using buffer here at Imperial for a couple of years now
|
||
Xfor writing tar tapes and the main system dumps.
|
||
X
|
||
XThanks to Kevin Twidle <kpt@doc.ic.ac.uk> for the -p and -B code.
|
||
XThanks to Bard Isley <brad@slammer.atl.ga.us> for fixes to the
|
||
X read loop/SIGCHLD handling.
|
||
XThanks to PerSteinar.Iversen@fi.uib.no for the DEC Alpha patches.
|
||
XThanks to kargard@ampex.com (Erik L. Kargard) for the AMPEX enhancements.
|
||
X
|
||
XINSTALLATION:
|
||
X Check that your kernel supports shared memory and semaphores.
|
||
X A quick way to check is to build buffer and run it.
|
||
X If it says "couldn't create shared memory segment" you probably
|
||
X need to reconfigure and rebuild your kernel.
|
||
X
|
||
X To install edit the Makefile and tailor the variables to
|
||
X your local systems. Then type make.
|
||
X
|
||
XDISCLAIMER:
|
||
X This package is under the GNU GENERAL PUBLIC LICENSE!
|
||
X In addtion under NO circumstances can I, or Imperial College,
|
||
X be held liable for any event caused by the running or storing
|
||
X of this program or its documentation.
|
||
X
|
||
XLee McLoughlin. Phone: +44 171 594 8388
|
||
XIC-Parc, William Penney Lab, Fax: +44 171 594 8449
|
||
XImperial College, London, SW7 2BZ, UK Email: L.McLoughlin@doc.ic.ac.uk
|
||
END_OF_FILE
|
||
if test 2208 -ne `wc -c <'README'`; then
|
||
echo shar: \"'README'\" unpacked with wrong size!
|
||
fi
|
||
# end of 'README'
|
||
fi
|
||
if test -f 'buffer.man' -a "${1}" != "-c" ; then
|
||
echo shar: Will not clobber existing file \"'buffer.man'\"
|
||
else
|
||
echo shar: Extracting \"'buffer.man'\" \(5087 characters\)
|
||
sed "s/^X//" >'buffer.man' <<'END_OF_FILE'
|
||
X.\" Buffer. Very fast reblocking filter speedy writing of tapes.
|
||
X.\" Copyright (C) 1990,1991 Lee McLoughlin
|
||
X.\"
|
||
X.\" This program is free software; you can redistribute it and/or modify
|
||
X.\" it under the terms of the GNU General Public License as published by
|
||
X.\" the Free Software Foundation; either version 1, or (at your option)
|
||
X.\" any later version.
|
||
X.\"
|
||
X.\" This program is distributed in the hope that it will be useful,
|
||
X.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
X.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
X.\" GNU General Public License for more details.
|
||
X.\"
|
||
X.\" You should have received a copy of the GNU General Public License
|
||
X.\" along with this program; if not, write to the Free Software
|
||
X.\" Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||
X.\"
|
||
X.\" Lee McLoughlin.
|
||
X.\" Dept of Computing, Imperial College,
|
||
X.\" 180 Queens Gate, London, SW7 2BZ, UK.
|
||
X.\"
|
||
X.\" Email: L.McLoughlin@doc.ic.ac.uk
|
||
X.TH BUFFER 1 "14 May 1990"
|
||
X.SH NAME
|
||
Xbuffer \- very fast reblocking program
|
||
X.SH SYNTAX
|
||
X.B buffer
|
||
X[\fB\-S size\fP] [\fB\-b blocks\fP] [\fB\-s size\fP] [\fB\-m size\fP]
|
||
X[\fB\-p percentage\fP] [\fB\-u microseconds\fP] [\fB-B\fR] [\fB-t\fR]
|
||
X[\fB-Z\fR] [\fB-i filename\fR] [\fB-o filename\fR]
|
||
X.SH OPTIONS
|
||
X.TP 5
|
||
X.B \-i filename
|
||
XUse the given file as the input file. The default is stdin.
|
||
X.TP
|
||
X.B \-o filename
|
||
XUse the given file as the output file. The default is stdout.
|
||
X.TP
|
||
X.B \-S size
|
||
XAfter every chunk this size has been writen print out how much been writen so far.
|
||
XBy default this is not set.
|
||
X.TP
|
||
X.B \-s size
|
||
XSize in bytes of each block. The default blocksize is 10k to match
|
||
Xthe normal output of the
|
||
X.I tar(1)
|
||
Xprogram.
|
||
X.TP
|
||
X.B \-z size
|
||
XCombines the
|
||
X.B \-S
|
||
Xand
|
||
X.B \-s
|
||
Xflags.
|
||
X.TP
|
||
X.B \-b blocks
|
||
XNumber of blocks to allocate to shared memory circular buffer.
|
||
XDefaults to the number required to fill up the shared memory requested.
|
||
X.TP
|
||
X.B \-m size
|
||
XMaximum size of the shared memory chunk to allocate for the circular
|
||
Xqueue. Defaults to one megabyte.
|
||
X.TP
|
||
X.B \-p percentage
|
||
XOnly start a write when the given percentage of the internal queue is
|
||
Xfull. A percentage around 75 often proves best. Defaults to zero.
|
||
X.TP
|
||
X.B \-u microseconds
|
||
XAfter every write pause for this many microseconds. Defaults to zero.
|
||
X(Suprisingly a small sleep, 100 usecs, after each write can greatly enhance
|
||
Xthroughput on some drives.)
|
||
X.TP
|
||
X.B \-B
|
||
XForce each block writen to be padded out to the blocksize. This is needed by some tape
|
||
Xand cartridge drives. Defaults to unpadded. This only affects the
|
||
Xlast block writen.
|
||
X.TP
|
||
X.B \-t
|
||
XOn exiting print to stderr a brief message showing the total number of
|
||
Xbytes written.
|
||
X.TP
|
||
X.B \-Z
|
||
XIf reading/writing directly to a character device (like a tape drive)
|
||
Xthen after each gigabyte perform an lseek to the start of the file.
|
||
XUse this flag with extreme care. If can only be used on devices where
|
||
Xan lseek does not rewind the tape but does reset the kernels position
|
||
Xflags. It is used to allow more than 2 gigabytes to be written.
|
||
X.PP
|
||
XSizes are a number with an optional trailing character. A 'b'
|
||
Xmultiplies the size by 512, a 'k' by 1024 and an 'm' by a meg.
|
||
X.SH DESCRIPTION
|
||
X.I Buffer
|
||
Xreads from standard input reblocking to the given blocksize and writes
|
||
Xeach block to standard output.
|
||
X.PP
|
||
XInternally
|
||
X.I buffer
|
||
Xis a pair of processes communicating via a large circular queue held
|
||
Xin shared memory. The reader process only has to block when the queue
|
||
Xis full and the writer process when the queue is empty.
|
||
X.I Buffer
|
||
Xis designed to try and keep the writer side continuously busy so that
|
||
Xit can stream when writing to tape drives. When used to write tapes
|
||
Xwith an intervening network
|
||
X.I buffer
|
||
Xcan result in a considerable increase in throughput.
|
||
X.PP
|
||
XThe default settings for
|
||
X.I buffer
|
||
Xare normally good enough. If you are a heavy tape user then it is
|
||
Xworth your while trying out various different combinations of options.
|
||
XIn particular running a
|
||
X.I buffer
|
||
Xat both ends of the pipe can provide a substantial increase (see last
|
||
Xexample below).
|
||
X.SH EXAMPLES
|
||
X.br
|
||
X$ \fBbuffer < /etc/termcap > /dev/rst8\fP
|
||
X.br
|
||
X.sp
|
||
X$ \fBtar cf - . | rsh somehost 'buffer > /dev/rst8'\fP
|
||
X.br
|
||
X.sp
|
||
X$ \fBdump fu - | rsh somehost 'buffer -s 16k > /dev/nrst8'\fP
|
||
X.br
|
||
X$ \fBtar cf - . | buffer |
|
||
X.br
|
||
X\ \ \ rsh somehost 'buffer -S 500K -p 75 > /dev/rst0'\fP
|
||
X.SH BUGS
|
||
XInternally, for printing purposes, buffer counts in terms of the
|
||
Xnumber of kilobytes output. If the blocksize you use is not a whole
|
||
Xnumber of kilobytes then the numbers printed will be inaccurate.
|
||
X
|
||
X.SH THANKS
|
||
XThanks to Kevin Twidle <kpt@doc.ic.ac.uk> for a lot of early
|
||
Xsuggestions and patches to make it work with non-tar/dump tapes to
|
||
Xexabyte drives.
|
||
X
|
||
XThanks to Andi Karrer <karrer@bernina.ethz.ch>, Rumi Zahir
|
||
X<rumi@iis.ethz.ch> and Christoph Wicki <wicki@iis.ethz.ch> for patches
|
||
Xto make buffer work when trying to write single tape files of greater
|
||
Xthan 2 gigabytes.
|
||
X
|
||
X.SH COPYRIGHT
|
||
X.if n Copyright (C) 1990, 1991 by Lee McLoughlin.
|
||
X.if t Copyright \(co 1990, 1991 by Lee McLoughlin.
|
||
X.SH SEE ALSO
|
||
Xdd(1), tar(1), rsh(1)
|
||
END_OF_FILE
|
||
if test 5087 -ne `wc -c <'buffer.man'`; then
|
||
echo shar: \"'buffer.man'\" unpacked with wrong size!
|
||
fi
|
||
# end of 'buffer.man'
|
||
fi
|
||
if test -f 'Makefile' -a "${1}" != "-c" ; then
|
||
echo shar: Will not clobber existing file \"'Makefile'\"
|
||
else
|
||
echo shar: Extracting \"'Makefile'\" \(1121 characters\)
|
||
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
|
||
X# Make the buffer program
|
||
X
|
||
X# You might need to add the following to CGFLAGS:
|
||
X#
|
||
X# Add -DSYS5 for A System 5 (USG) version of Unix
|
||
X# You should also add -DSYS5 for Ultrix, AIX, and Solaris.
|
||
X# Add -DDEF_SHMEM=n if you can only have n bytes of shared memory
|
||
X# (eg: -DDEF_SHMEM=524288 if you can only have half a meg.)
|
||
X# Add -DAMPEX to change the default settings suitable for the high capacity
|
||
X# Ampex drives, such as the DST 310.
|
||
X
|
||
XCC=gcc
|
||
XCFLAGS=-Wall
|
||
X
|
||
X# Where to install buffer and its manual pages
|
||
XINSTBIN=/usr/local/bin
|
||
XINSTMAN=/usr/man/manl
|
||
X# The manual page section (normally l or 1)
|
||
XS=l
|
||
X
|
||
XRM=/bin/rm
|
||
XALL=README buffer.man Makefile buffer.c sem.c sem.h COPYING
|
||
X
|
||
Xall: buffer
|
||
X
|
||
Xbuffer: buffer.o sem.o
|
||
X $(CC) -o buffer $(CFLAGS) buffer.o sem.o
|
||
X
|
||
Xclean:
|
||
X $(RM) -f *.o core buffer .merrs
|
||
X
|
||
Xinstall: buffer
|
||
X rm -f $(INSTBIN)/buffer
|
||
X cp buffer $(INSTBIN)/buffer
|
||
X chmod 111 $(INSTBIN)/buffer
|
||
X rm -f $(INSTMAN)/buffer.$S
|
||
X cp buffer.man $(INSTMAN)/buffer.$S
|
||
X chmod 444 $(INSTMAN)/buffer.$S
|
||
X
|
||
Xbuffer.tar: $(ALL)
|
||
X $(RM) -f buffer.tar
|
||
X tar cvf buffer.tar $(ALL)
|
||
X
|
||
Xbuffer.shar: $(ALL)
|
||
X $(RM) -f buffer.shar
|
||
X shar $(ALL) > buffer.shar
|
||
END_OF_FILE
|
||
if test 1121 -ne `wc -c <'Makefile'`; then
|
||
echo shar: \"'Makefile'\" unpacked with wrong size!
|
||
fi
|
||
# end of 'Makefile'
|
||
fi
|
||
if test -f 'buffer.c' -a "${1}" != "-c" ; then
|
||
echo shar: Will not clobber existing file \"'buffer.c'\"
|
||
else
|
||
echo shar: Extracting \"'buffer.c'\" \(21928 characters\)
|
||
sed "s/^X//" >'buffer.c' <<'END_OF_FILE'
|
||
X/*
|
||
X Buffer. Very fast reblocking filter speedy writing of tapes.
|
||
X Copyright (C) 1990,1991 Lee McLoughlin
|
||
X
|
||
X This program is free software; you can redistribute it and/or modify
|
||
X it under the terms of the GNU General Public License as published by
|
||
X the Free Software Foundation; either version 1, or (at your option)
|
||
X any later version.
|
||
X
|
||
X This program is distributed in the hope that it will be useful,
|
||
X but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
X MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
X GNU General Public License for more details.
|
||
X
|
||
X You should have received a copy of the GNU General Public License
|
||
X along with this program; if not, write to the Free Software
|
||
X Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||
X
|
||
X Lee McLoughlin.
|
||
X Dept of Computing, Imperial College,
|
||
X 180 Queens Gate, London, SW7 2BZ, UK.
|
||
X
|
||
X Email: L.McLoughlin@doc.ic.ac.uk
|
||
X*/
|
||
X
|
||
X/* This is a reblocking process, designed to try and read from stdin
|
||
X * and write to stdout - but to always try and keep the writing side
|
||
X * busy. It is meant to try and stream tape writes.
|
||
X *
|
||
X * This program runs in two parts. The reader and the writer. They
|
||
X * communicate using shared memory with semaphores locking the access.
|
||
X * The shared memory implements a circular list of blocks of data.
|
||
X *
|
||
X * L.McLoughlin, Imperial College, 1990
|
||
X *
|
||
X * $Log: buffer-1.19.shar,v $
|
||
X * Revision 1.1 2016/02/07 13:48:09 stephdl
|
||
X * * Sun Feb 7 2016 stephane de Labrusse <stephdl@de-labrusse.fr> 1.19-7.sme
|
||
X * - Build for sme10
|
||
X *
|
||
X * Revision 1.19 1995/08/24 17:46:28 lmjm
|
||
X * Be more careful abour EINTR errors
|
||
X * Ingnore child processes dying.
|
||
X *
|
||
X * Revision 1.18 1993/08/25 19:07:31 lmjm
|
||
X * Added Brad Isleys patchs to read/sigchld handling.
|
||
X *
|
||
X * Revision 1.17 1993/06/04 10:26:39 lmjm
|
||
X * Cleaned up error reporting.
|
||
X * Spot when the child terminating is not mine but inherited from via exec.
|
||
X * Use only one semaphore group.
|
||
X * Print out why writer died on error.
|
||
X *
|
||
X * Revision 1.16 1993/05/28 10:47:32 lmjm
|
||
X * Debug shutdown sequence.
|
||
X *
|
||
X * Revision 1.15 1992/11/23 23:32:58 lmjm
|
||
X * Oops! This should be outside the ifdef
|
||
X *
|
||
X * Revision 1.14 1992/11/23 23:29:58 lmjm
|
||
X * allow MAX_BLOCKSIZE and DEF_SHMEM to be configured
|
||
X *
|
||
X * Revision 1.13 1992/11/23 23:22:29 lmjm
|
||
X * Printf's use %lu where appropriate.
|
||
X *
|
||
X * Revision 1.12 1992/11/23 23:17:55 lmjm
|
||
X * Got rid of floats and use Kbyte counters instead.
|
||
X *
|
||
X * Revision 1.11 1992/11/03 23:11:51 lmjm
|
||
X * Forgot Andi Karrer on the patch list.
|
||
X *
|
||
X * Revision 1.10 1992/11/03 22:58:41 lmjm
|
||
X * Cleaned up the debugging prints.
|
||
X *
|
||
X * Revision 1.9 1992/11/03 22:53:00 lmjm
|
||
X * Corrected stdin, stout and showevery use.
|
||
X *
|
||
X * Revision 1.8 1992/11/03 22:41:34 lmjm
|
||
X * Added 2Gig patches from:
|
||
X * Andi Karrer <karrer@bernina.ethz.ch>
|
||
X * Rumi Zahir <rumi@iis.ethz.ch>
|
||
X * Christoph Wicki <wicki@iis.ethz.ch>
|
||
X *
|
||
X * Revision 1.7 1992/07/23 20:42:03 lmjm
|
||
X * Added 't' option to print total writen at end.
|
||
X *
|
||
X * Revision 1.6 1992/04/07 19:57:30 lmjm
|
||
X * Added Kevins -B and -p options.
|
||
X * Turn off buffering to make -S output appear ok.
|
||
X * Added GPL.
|
||
X *
|
||
X * Revision 1.5 90/07/22 18:46:38 lmjm
|
||
X * Added system 5 support.
|
||
X *
|
||
X * Revision 1.4 90/07/22 18:29:48 lmjm
|
||
X * Updated arg handling to be more consistent.
|
||
X * Make sofar printing size an option.
|
||
X *
|
||
X * Revision 1.3 90/05/15 23:27:46 lmjm
|
||
X * Added -S option (show how much has been writen).
|
||
X * Added -m option to specify how much shared memory to grab.
|
||
X * Now tries to fill this with blocks.
|
||
X * reader waits for writer to terminate and then frees the shared mem and sems.
|
||
X *
|
||
X * Revision 1.2 90/01/20 21:37:59 lmjm
|
||
X * Reset default number of blocks and blocksize for best thruput of
|
||
X * standard tar 10K Allow.
|
||
X * blocks number of blocks to be changed.
|
||
X * Don't need a hole in the circular queue since the semaphores prevent block
|
||
X * clash.
|
||
X *
|
||
X * Revision 1.1 90/01/17 11:30:23 lmjm
|
||
X * Initial revision
|
||
X *
|
||
X */
|
||
X#include <unistd.h>
|
||
X#include <stdio.h>
|
||
X#include <signal.h>
|
||
X#include <fcntl.h>
|
||
X#include <errno.h>
|
||
X#include <sys/types.h>
|
||
X#include <sys/stat.h>
|
||
X#include <sys/ipc.h>
|
||
X#include <sys/shm.h>
|
||
X#include <sys/sem.h>
|
||
X#include <sys/wait.h>
|
||
X#include "sem.h"
|
||
X
|
||
X#ifndef lint
|
||
Xstatic char *rcsid = "$Header: /cvs/smeserver/rpms/buffer/sme10/buffer-1.19.shar,v 1.1 2016/02/07 13:48:09 stephdl Exp $";
|
||
X#endif
|
||
X
|
||
X#ifndef __alpha
|
||
Xextern char *shmat();
|
||
X#endif /* __alpha */
|
||
X
|
||
X/* General macros */
|
||
X#define TRUE 1
|
||
X#define FALSE 0
|
||
X#define K *1024
|
||
X#define M *1024*1024
|
||
X
|
||
X/* Some forward declarations */
|
||
Xvoid byee();
|
||
Xvoid start_reader_and_writer();
|
||
Xvoid parse_args();
|
||
Xvoid set_handlers();
|
||
Xvoid buffer_allocate();
|
||
Xvoid report_proc();
|
||
Xint do_size();
|
||
Xvoid get_buffer();
|
||
Xvoid reader();
|
||
Xvoid writer();
|
||
Xvoid writer_end();
|
||
Xvoid wait_for_writer_end();
|
||
Xvoid get_next_free_block();
|
||
Xvoid test_writer();
|
||
Xint fill_block();
|
||
Xvoid get_next_filled_block();
|
||
Xint data_to_write();
|
||
Xvoid write_blocks_to_stdout();
|
||
Xvoid write_block_to_stdout();
|
||
Xvoid pr_out();
|
||
Xvoid end_writer();
|
||
X
|
||
X/* When showing print a note every this many bytes writen */
|
||
Xint showevery = 0;
|
||
X#define PRINT_EVERY 10 K
|
||
X
|
||
X/* Pause after every write */
|
||
Xunsigned write_pause;
|
||
X
|
||
X/* This is the inter-process buffer - it implements a circular list
|
||
X * of blocks. */
|
||
X
|
||
X#ifdef AMPEX
|
||
X#define MAX_BLOCKSIZE (4 M)
|
||
X#define DEF_BLOCKSIZE MAX_BLOCKSIZE
|
||
X#define DEF_SHMEM (32 M)
|
||
X#endif
|
||
X
|
||
X
|
||
X#ifndef MAX_BLOCKSIZE
|
||
X#define MAX_BLOCKSIZE (512 K)
|
||
X#endif
|
||
X#ifndef DEF_BLOCKSIZE
|
||
X#define DEF_BLOCKSIZE (10 K)
|
||
X#endif
|
||
X
|
||
Xint blocksize = DEF_BLOCKSIZE;
|
||
X
|
||
X/* Which process... in error reports*/
|
||
Xchar *proc_string = "buffer";
|
||
X
|
||
X/* Numbers of blocks in the queue.
|
||
X */
|
||
X#define MAX_BLOCKS 2048
|
||
Xint blocks = 1;
|
||
X/* Circular increment of a buffer index */
|
||
X#define INC(i) (((i)+1) == blocks ? 0 : ((i)+1))
|
||
X
|
||
X/* Max amount of shared memory you can allocate - can't see a way to look
|
||
X * this up.
|
||
X */
|
||
X#ifndef DEF_SHMEM
|
||
X#define DEF_SHMEM (1 K K)
|
||
X#endif
|
||
Xint max_shmem = DEF_SHMEM;
|
||
X
|
||
X/* Just a flag to show unfilled */
|
||
X#define NONE (-1)
|
||
X
|
||
X/* the shared memory id of the buffer */
|
||
Xint buffer_id = NONE;
|
||
Xstruct block {
|
||
X int bytes;
|
||
X char *data;
|
||
X} *curr_block;
|
||
X
|
||
X#define NO_BUFFER ((struct buffer *)-1)
|
||
Xstruct buffer {
|
||
X /* Id of the semaphore group */
|
||
X int semid;
|
||
X
|
||
X /* writer will hang trying to lock this till reader fills in a block */
|
||
X int blocks_used_lock;
|
||
X /* reader will hang trying to lock this till writer empties a block */
|
||
X int blocks_free_lock;
|
||
X
|
||
X int next_block_in;
|
||
X int next_block_out;
|
||
X
|
||
X struct block block[ MAX_BLOCKS ];
|
||
X
|
||
X /* These actual space for the blocks is here - the array extends
|
||
X * pass 1 */
|
||
X char data_space[ 1 ];
|
||
X} *pbuffer = NO_BUFFER;
|
||
Xint buffer_size;
|
||
X
|
||
Xint fdin = 0;
|
||
Xint fdout = 1;
|
||
Xint in_ISCHR = 0;
|
||
Xint out_ISCHR = 0;
|
||
Xint padblock = FALSE;
|
||
Xint writer_pid = 0;
|
||
Xint reader_pid = 0;
|
||
Xint free_shm = 1;
|
||
Xint percent = 0;
|
||
Xint debug = 0;
|
||
Xint Zflag = 0;
|
||
Xint writer_status = 0;
|
||
Xchar *progname = "buffer";
|
||
X
|
||
Xchar print_total = 0;
|
||
X/* Number of K output */
|
||
Xunsigned long outk = 0;
|
||
X
|
||
Xint
|
||
Xmain( argc, argv )
|
||
X int argc;
|
||
X char **argv;
|
||
X{
|
||
X parse_args( argc, argv );
|
||
X
|
||
X set_handlers();
|
||
X
|
||
X buffer_allocate();
|
||
X
|
||
X start_reader_and_writer();
|
||
X
|
||
X byee( 0 );
|
||
X
|
||
X /* NOTREACHED */
|
||
X exit( 0 );
|
||
X}
|
||
X
|
||
Xvoid
|
||
Xparse_args( argc, argv )
|
||
X int argc;
|
||
X char **argv;
|
||
X{
|
||
X int c;
|
||
X int iflag = 0;
|
||
X int oflag = 0;
|
||
X int zflag = 0;
|
||
X extern char *optarg;
|
||
X char blocks_given = FALSE;
|
||
X struct stat buf;
|
||
X
|
||
X
|
||
X while( (c = getopt( argc, argv, "BS:Zdm:s:b:p:u:ti:o:z:" )) != -1 ){
|
||
X switch( c ){
|
||
X case 't': /* Print to stderr the total no of bytes writen */
|
||
X print_total++;
|
||
X break;
|
||
X case 'u': /* pause after write for given microseconds */
|
||
X write_pause = atoi( optarg );
|
||
X break;
|
||
X case 'B': /* Pad last block */
|
||
X padblock = TRUE;
|
||
X break;
|
||
X case 'Z': /* Zero by lseek on the tape device */
|
||
X Zflag = TRUE;
|
||
X break;
|
||
X case 'i': /* Input file */
|
||
X iflag++;
|
||
X if( iflag > 1 ){
|
||
X report_proc();
|
||
X fprintf( stderr, "-i given twice\n" );
|
||
X byee( -1 );
|
||
X }
|
||
X if( (fdin = open( optarg, O_RDONLY )) < 0 ){
|
||
X report_proc();
|
||
X perror( "cannot open input file" );
|
||
X fprintf( stderr, "filename: %s\n", optarg );
|
||
X byee ( -1 );
|
||
X }
|
||
X break;
|
||
X case 'o': /* Output file */
|
||
X oflag++;
|
||
X if( oflag > 1 ){
|
||
X report_proc();
|
||
X fprintf( stderr, "-o given twice\n" );
|
||
X byee( -1 );
|
||
X }
|
||
X if( (fdout = open( optarg, O_WRONLY | O_CREAT | O_TRUNC, 0666 )) < 0 ){
|
||
X report_proc();
|
||
X perror( "cannot open output file" );
|
||
X fprintf( stderr, "filename: %s\n", optarg );
|
||
X byee ( -1 );
|
||
X }
|
||
X break;
|
||
X case 'S':
|
||
X /* Show every once in a while how much is printed */
|
||
X showevery = do_size( optarg );
|
||
X if( showevery <= 0 )
|
||
X showevery = PRINT_EVERY;
|
||
X break;
|
||
X case 'd': /* debug */
|
||
X debug++;
|
||
X if( debug == 1 ){
|
||
X setbuf( stdout, NULL );
|
||
X setbuf( stderr, NULL );
|
||
X fprintf( stderr, "debugging turned on\n" );
|
||
X }
|
||
X break;
|
||
X case 'm':
|
||
X /* Max size of shared memory lump */
|
||
X max_shmem = do_size( optarg );
|
||
X
|
||
X if( max_shmem < (sizeof( struct buffer ) + (blocksize * blocks)) ){
|
||
X fprintf( stderr, "max_shmem %d too low\n", max_shmem );
|
||
X byee( -1 );
|
||
X }
|
||
X break;
|
||
X case 'b':
|
||
X /* Number of blocks */
|
||
X blocks_given = TRUE;
|
||
X blocks = atoi( optarg );
|
||
X if( (blocks <= 0) || (MAX_BLOCKS < blocks) ){
|
||
X fprintf( stderr, "blocks %d out of range\n", blocks );
|
||
X byee( -1 );
|
||
X }
|
||
X break;
|
||
X case 'p': /* percent to wait before dumping */
|
||
X percent = atoi( optarg );
|
||
X
|
||
X if( (percent < 0) || (100 < percent) ){
|
||
X fprintf( stderr, "percent %d out of range\n", percent );
|
||
X byee( -1 );
|
||
X }
|
||
X if( debug )
|
||
X fprintf( stderr, "percent set to %d\n", percent );
|
||
X break;
|
||
X case 'z':
|
||
X zflag++;
|
||
X /* FALL THRU */
|
||
X case 's': /* Size of a block */
|
||
X blocksize = do_size( optarg );
|
||
X
|
||
X if( (blocksize <= 0) || (MAX_BLOCKSIZE < blocksize) ){
|
||
X fprintf( stderr, "blocksize %d out of range\n", blocksize );
|
||
X byee( -1 );
|
||
X }
|
||
X break;
|
||
X default:
|
||
X fprintf( stderr, "Usage: %s [-B] [-t] [-S size] [-m memsize] [-b blocks] [-p percent] [-s blocksize] [-u pause] [-i infile] [-o outfile] [-z size]\n",
|
||
X progname );
|
||
X fprintf( stderr, "-B = blocked device - pad out last block\n" );
|
||
X fprintf( stderr, "-t = show total amount writen at end\n" );
|
||
X fprintf( stderr, "-S size = show amount writen every size bytes\n" );
|
||
X fprintf( stderr, "-m size = size of shared mem chunk to grab\n" );
|
||
X fprintf( stderr, "-b num = number of blocks in queue\n" );
|
||
X fprintf( stderr, "-p percent = don't start writing until percent blocks filled\n" );
|
||
X fprintf( stderr, "-s size = size of a block\n" );
|
||
X fprintf( stderr, "-u usecs = microseconds to sleep after each write\n" );
|
||
X fprintf( stderr, "-i infile = file to read from\n" );
|
||
X fprintf( stderr, "-o outfile = file to write to\n" );
|
||
X fprintf( stderr, "-z size = combined -S/-s flag\n" );
|
||
X byee( -1 );
|
||
X }
|
||
X }
|
||
X
|
||
X if (zflag) showevery = blocksize;
|
||
X
|
||
X /* If -b was not given try and work out the max buffer size */
|
||
X if( !blocks_given ){
|
||
X blocks = (max_shmem - sizeof( struct buffer )) / blocksize;
|
||
X if( blocks <= 0 ){
|
||
X fprintf( stderr, "Cannot handle blocks that big, aborting!\n" );
|
||
X byee( -1 );
|
||
X }
|
||
X if( MAX_BLOCKS < blocks ){
|
||
X fprintf( stderr, "Cannot handle that many blocks, aborting!\n" );
|
||
X byee( -1 );
|
||
X }
|
||
X }
|
||
X
|
||
X /* check if fdin or fdout are character special files */
|
||
X if( fstat( fdin, &buf ) != 0 ){
|
||
X report_proc();
|
||
X perror( "can't stat input file" );
|
||
X byee( -1 );
|
||
X }
|
||
X in_ISCHR = S_ISCHR( buf.st_mode );
|
||
X if( fstat( fdout, &buf ) != 0 ){
|
||
X report_proc();
|
||
X perror( "can't stat output file" );
|
||
X byee( -1 );
|
||
X }
|
||
X out_ISCHR = S_ISCHR( buf.st_mode );
|
||
X}
|
||
X
|
||
X/* The interrupt handler */
|
||
Xvoid
|
||
Xshutdown()
|
||
X{
|
||
X static int shutting;
|
||
X if( shutting ){
|
||
X if( debug )
|
||
X fprintf( stderr, "%s: ALREADY SHUTTING!\n", proc_string );
|
||
X return;
|
||
X }
|
||
X shutting = 1;
|
||
X if( debug )
|
||
X fprintf( stderr, "%s: shutdown on signal\n", proc_string );
|
||
X
|
||
X byee( -1 );
|
||
X}
|
||
X
|
||
X/* Shutdown because the child has ended */
|
||
Xvoid
|
||
Xchild_shutdown()
|
||
X{
|
||
X /* Find out which child has died. (They may not be my
|
||
X * children if buffer was exec'd on top of something that had
|
||
X * childred.)
|
||
X */
|
||
X int deadpid;
|
||
X
|
||
X while( (deadpid = waitpid( -1, &writer_status, WNOHANG )) &&
|
||
X deadpid != -1 && deadpid != 0 ){
|
||
X if( debug > 2 )
|
||
X fprintf( stderr, "child_shutdown %d: 0x%04x\n", deadpid, writer_status );
|
||
X if( deadpid == writer_pid ){
|
||
X if( debug > 2 )
|
||
X fprintf( stderr, "writer has ended\n" );
|
||
X writer_pid = 0;
|
||
X byee( 0 );
|
||
X }
|
||
X }
|
||
X}
|
||
X
|
||
Xvoid
|
||
Xset_handlers()
|
||
X{
|
||
X if( debug )
|
||
X fprintf( stderr, "%s: setting handlers\n", proc_string );
|
||
X
|
||
X signal( SIGHUP, shutdown );
|
||
X signal( SIGINT, shutdown );
|
||
X signal( SIGQUIT, shutdown );
|
||
X signal( SIGTERM, shutdown );
|
||
X#ifdef SIGCHLD
|
||
X signal( SIGCHLD, child_shutdown );
|
||
X#else
|
||
X#ifdef SIGCLD
|
||
X signal( SIGCLD, child_shutdown );
|
||
X#endif
|
||
X#endif
|
||
X}
|
||
X
|
||
Xvoid
|
||
Xbuffer_allocate()
|
||
X{
|
||
X /* Allow for the data space */
|
||
X buffer_size = sizeof( struct buffer ) +
|
||
X ((blocks * blocksize) - sizeof( char ));
|
||
X
|
||
X /* Create the space for the buffer */
|
||
X buffer_id = shmget( IPC_PRIVATE,
|
||
X buffer_size,
|
||
X IPC_CREAT|S_IREAD|S_IWRITE );
|
||
X if( buffer_id < 0 ){
|
||
X report_proc();
|
||
X perror( "couldn't create shared memory segment" );
|
||
X byee( -1 );
|
||
X }
|
||
X
|
||
X get_buffer();
|
||
X
|
||
X if( debug )
|
||
X fprintf( stderr, "%s pbuffer is 0x%08x, buffer_size is %d [%d x %d]\n",
|
||
X proc_string,
|
||
X (char *)pbuffer, buffer_size, blocks, blocksize );
|
||
X
|
||
X#ifdef SYS5
|
||
X memset( (char *)pbuffer, '\0', buffer_size );
|
||
X#else
|
||
X bzero( (char *)pbuffer, buffer_size );
|
||
X#endif
|
||
X pbuffer->semid = -1;
|
||
X pbuffer->blocks_used_lock = -1;
|
||
X pbuffer->blocks_free_lock = -1;
|
||
X
|
||
X pbuffer->semid = new_sems( 2 ); /* Get a read and a write sem */
|
||
X pbuffer->blocks_used_lock = 0;
|
||
X /* Start it off locked - it is unlocked when a buffer gets filled in */
|
||
X lock( pbuffer->semid, pbuffer->blocks_used_lock );
|
||
X
|
||
X pbuffer->blocks_free_lock = 1;
|
||
X /* start this off so lock() can be called on it for each block
|
||
X * till all the blocks are used up */
|
||
X sem_set( pbuffer->semid, pbuffer->blocks_free_lock, blocks - 1 );
|
||
X
|
||
X /* Detattach the shared memory so the fork doesnt do anything odd */
|
||
X shmdt( (char *)pbuffer );
|
||
X pbuffer = NO_BUFFER;
|
||
X}
|
||
X
|
||
Xvoid
|
||
Xbuffer_remove()
|
||
X{
|
||
X static char removing = FALSE;
|
||
X
|
||
X /* Avoid accidental recursion */
|
||
X if( removing )
|
||
X return;
|
||
X removing = TRUE;
|
||
X
|
||
X /* Buffer not yet created */
|
||
X if( buffer_id == NONE )
|
||
X return;
|
||
X
|
||
X /* There should be a buffer so this must be after its detached it
|
||
X * but before the fork picks it up */
|
||
X if( pbuffer == NO_BUFFER )
|
||
X get_buffer();
|
||
X
|
||
X if( debug )
|
||
X fprintf( stderr, "%s: removing semaphores and buffer\n", proc_string );
|
||
X remove_sems( pbuffer->semid );
|
||
X
|
||
X if( shmctl( buffer_id, IPC_RMID, (struct shmid_ds *)0 ) == -1 ){
|
||
X report_proc();
|
||
X perror( "failed to remove shared memory buffer" );
|
||
X }
|
||
X}
|
||
X
|
||
Xvoid
|
||
Xget_buffer()
|
||
X{
|
||
X int b;
|
||
X
|
||
X /* Grab the buffer space */
|
||
X pbuffer = (struct buffer *)shmat( buffer_id, (char *)0, 0 );
|
||
X if( pbuffer == NO_BUFFER ){
|
||
X report_proc();
|
||
X perror( "failed to attach shared memory" );
|
||
X byee( -1 );
|
||
X }
|
||
X
|
||
X /* Setup the data space pointers */
|
||
X for( b = 0; b < blocks; b++ )
|
||
X pbuffer->block[ b ].data =
|
||
X &pbuffer->data_space[ b * blocksize ];
|
||
X
|
||
X}
|
||
X
|
||
Xvoid
|
||
Xstart_reader_and_writer()
|
||
X{
|
||
X fflush( stdout );
|
||
X fflush( stderr );
|
||
X
|
||
X if( (writer_pid = fork()) == -1 ){
|
||
X report_proc();
|
||
X perror( "unable to fork" );
|
||
X byee( -1 );
|
||
X }
|
||
X else if( writer_pid == 0 ){
|
||
X free_shm = 0;
|
||
X proc_string = "buffer (writer)";
|
||
X reader_pid = getppid();
|
||
X
|
||
X /* Never trust fork() to propogate signals - reset them */
|
||
X set_handlers();
|
||
X
|
||
X writer();
|
||
X }
|
||
X else {
|
||
X proc_string = "buffer (reader)";
|
||
X reader();
|
||
X
|
||
X wait_for_writer_end();
|
||
X }
|
||
X}
|
||
X
|
||
X/* Read from stdin into the buffer */
|
||
Xvoid
|
||
Xreader()
|
||
X{
|
||
X if( debug )
|
||
X fprintf( stderr, "R: Entering reader\n" );
|
||
X
|
||
X get_buffer();
|
||
X
|
||
X while( 1 ){
|
||
X get_next_free_block();
|
||
X if( ! fill_block() )
|
||
X break;
|
||
X }
|
||
X
|
||
X if( debug )
|
||
X fprintf( stderr, "R: Exiting reader\n" );
|
||
X}
|
||
X
|
||
Xvoid
|
||
Xget_next_free_block()
|
||
X{
|
||
X test_writer();
|
||
X
|
||
X /* Maybe wait till there is room in the buffer */
|
||
X lock( pbuffer->semid, pbuffer->blocks_free_lock );
|
||
X
|
||
X curr_block = &pbuffer->block[ pbuffer->next_block_in ];
|
||
X
|
||
X pbuffer->next_block_in = INC( pbuffer->next_block_in );
|
||
X}
|
||
X
|
||
Xint
|
||
Xfill_block()
|
||
X{
|
||
X int bytes;
|
||
X char *start;
|
||
X int toread;
|
||
X static char eof_reached = 0;
|
||
X
|
||
X if( eof_reached ){
|
||
X curr_block->bytes = 0;
|
||
X unlock( pbuffer->semid, pbuffer->blocks_used_lock );
|
||
X return 0;
|
||
X }
|
||
X
|
||
X start = curr_block->data;
|
||
X toread = blocksize;
|
||
X
|
||
X /* Fill the block with input. This reblocks the input. */
|
||
X while( toread != 0 ){
|
||
X bytes = read( fdin, start, toread );
|
||
X if( bytes <= 0 ){
|
||
X /* catch interrupted system calls for death
|
||
X * of children in pipeline */
|
||
X if( bytes < 0 && errno == EINTR )
|
||
X continue;
|
||
X break;
|
||
X }
|
||
X start += bytes;
|
||
X toread -= bytes;
|
||
X }
|
||
X
|
||
X if( bytes == 0 )
|
||
X eof_reached = 1;
|
||
X
|
||
X if( bytes < 0 ){
|
||
X report_proc();
|
||
X perror( "failed to read input" );
|
||
X byee( -1 );
|
||
X }
|
||
X
|
||
X /* number of bytes available. Zero will be taken as eof */
|
||
X if( !padblock || toread == blocksize )
|
||
X curr_block->bytes = blocksize - toread;
|
||
X else {
|
||
X if( toread ) bzero( start, toread );
|
||
X curr_block->bytes = blocksize;
|
||
X }
|
||
X
|
||
X if( debug > 1 )
|
||
X fprintf( stderr, "R: got %d bytes\n", curr_block->bytes );
|
||
X
|
||
X unlock( pbuffer->semid, pbuffer->blocks_used_lock );
|
||
X
|
||
X return curr_block->bytes;
|
||
X}
|
||
X
|
||
X/* Write the buffer to stdout */
|
||
Xvoid
|
||
Xwriter()
|
||
X{
|
||
X int filled = 0;
|
||
X int maxfilled = (blocks * percent) / 100;
|
||
X int first_block;
|
||
X
|
||
X if( debug )
|
||
X fprintf( stderr, "\tW: Entering writer\n blocks = %d\n maxfilled = %d\n",
|
||
X blocks,
|
||
X maxfilled );
|
||
X
|
||
X get_buffer();
|
||
X
|
||
X while( 1 ){
|
||
X if( !filled )
|
||
X first_block = pbuffer->next_block_out;
|
||
X get_next_filled_block();
|
||
X if( !data_to_write() )
|
||
X break;
|
||
X
|
||
X filled++;
|
||
X if( debug > 1 )
|
||
X fprintf( stderr, "W: filled = %d\n", filled );
|
||
X if( filled >= maxfilled ){
|
||
X if( debug > 1 )
|
||
X fprintf( stderr, "W: writing\n" );
|
||
X write_blocks_to_stdout( filled, first_block );
|
||
X filled = 0;
|
||
X }
|
||
X }
|
||
X
|
||
X write_blocks_to_stdout( filled, first_block );
|
||
X
|
||
X if( showevery ){
|
||
X pr_out();
|
||
X fprintf( stderr, "\n" );
|
||
X }
|
||
X
|
||
X if( print_total ){
|
||
X fprintf( stderr, "Kilobytes Out %lu\n", outk );
|
||
X }
|
||
X
|
||
X if( debug )
|
||
X fprintf( stderr, "\tW: Exiting writer\n" );
|
||
X}
|
||
X
|
||
Xvoid
|
||
Xget_next_filled_block()
|
||
X{
|
||
X /* Hang till some data is available */
|
||
X lock( pbuffer->semid, pbuffer->blocks_used_lock );
|
||
X
|
||
X curr_block = &pbuffer->block[ pbuffer->next_block_out ];
|
||
X
|
||
X pbuffer->next_block_out = INC( pbuffer->next_block_out );
|
||
X}
|
||
X
|
||
Xint
|
||
Xdata_to_write()
|
||
X{
|
||
X return curr_block->bytes;
|
||
X}
|
||
X
|
||
Xvoid
|
||
Xwrite_blocks_to_stdout( filled, first_block )
|
||
X int filled;
|
||
X int first_block;
|
||
X{
|
||
X pbuffer->next_block_out = first_block;
|
||
X
|
||
X while( filled-- ){
|
||
X curr_block = &pbuffer->block[ pbuffer->next_block_out ];
|
||
X pbuffer->next_block_out = INC( pbuffer->next_block_out );
|
||
X write_block_to_stdout();
|
||
X }
|
||
X}
|
||
X
|
||
Xvoid
|
||
Xwrite_block_to_stdout()
|
||
X{
|
||
X static unsigned long out = 0;
|
||
X static unsigned long last_gb = 0;
|
||
X static unsigned long next_k = 0;
|
||
X int written;
|
||
X
|
||
X if( next_k == 0 && showevery ){
|
||
X if( debug > 3 )
|
||
X fprintf( stderr, "W: next_k = %lu showevery = %d\n", next_k, showevery );
|
||
X showevery = showevery / 1024;
|
||
X next_k = showevery;
|
||
X }
|
||
X
|
||
X if( (written = write( fdout, curr_block->data, curr_block->bytes )) != curr_block->bytes ){
|
||
X report_proc();
|
||
X perror( "write of data failed" );
|
||
X fprintf( stderr, "bytes to write=%d, bytes written=%d, total written %10luK\n", curr_block->bytes, written, outk );
|
||
X byee( -1 );
|
||
X }
|
||
X
|
||
X if( write_pause ){
|
||
X usleep( write_pause );
|
||
X }
|
||
X
|
||
X out = curr_block->bytes / 1024;
|
||
X outk += out;
|
||
X last_gb += out;
|
||
X
|
||
X /*
|
||
X * on character special devices (tapes), do an lseek() every 1 Gb,
|
||
X * to overcome the 2Gb limit. This resets the file offset to
|
||
X * zero, but -- at least on exabyte SCSI drives -- does not perform
|
||
X * any actual action on the tape.
|
||
X */
|
||
X if( Zflag && last_gb >= 1 K K ){
|
||
X last_gb = 0;
|
||
X if( in_ISCHR )
|
||
X (void) lseek( fdin, 0, SEEK_SET);
|
||
X if( out_ISCHR )
|
||
X (void) lseek( fdout, 0, SEEK_SET);
|
||
X }
|
||
X if( showevery ){
|
||
X if( debug > 3 )
|
||
X fprintf( stderr, "W: outk = %lu, next_k = %lu\n",
|
||
X outk, next_k );
|
||
X if( outk >= next_k ){
|
||
X pr_out();
|
||
X next_k += showevery;
|
||
X }
|
||
X }
|
||
X
|
||
X unlock( pbuffer->semid, pbuffer->blocks_free_lock );
|
||
X}
|
||
X
|
||
X
|
||
Xvoid
|
||
Xbyee( exit_val )
|
||
X int exit_val;
|
||
X{
|
||
X if( writer_pid != 0 ){
|
||
X if( exit_val != 0 ){
|
||
X /* I am shutting down due to an error.
|
||
X * Shut the writer down or else it will try to access
|
||
X * the freed up locks */
|
||
X end_writer();
|
||
X }
|
||
X wait_for_writer_end();
|
||
X }
|
||
X
|
||
X if( free_shm ){
|
||
X buffer_remove();
|
||
X }
|
||
X
|
||
X#ifdef SIGCHLD
|
||
X signal( SIGCHLD, SIG_IGN );
|
||
X#else
|
||
X#ifdef SIGCLD
|
||
X signal( SIGCLD, SIG_IGN );
|
||
X#endif
|
||
X#endif
|
||
X
|
||
X /* If the child died or was killed show this in the exit value */
|
||
X if( writer_status ){
|
||
X if( WEXITSTATUS( writer_status ) || WIFSIGNALED( writer_status ) ){
|
||
X if( debug )
|
||
X fprintf( stderr, "writer died badly: 0x%04x\n", writer_status );
|
||
X exit( -2 );
|
||
X }
|
||
X }
|
||
X
|
||
X exit( exit_val );
|
||
X}
|
||
X
|
||
X/* Kill off the writer */
|
||
Xvoid
|
||
Xend_writer()
|
||
X{
|
||
X if( writer_pid )
|
||
X kill( writer_pid, SIGHUP );
|
||
X}
|
||
X
|
||
Xvoid
|
||
Xwait_for_writer_end()
|
||
X{
|
||
X int deadpid;
|
||
X
|
||
X /* Now wait for the writer to finish */
|
||
X while( writer_pid && ((deadpid = wait( &writer_status )) != writer_pid) &&
|
||
X deadpid != -1 )
|
||
X ;
|
||
X}
|
||
X
|
||
Xvoid
|
||
Xtest_writer()
|
||
X{
|
||
X /* Has the writer gone unexpectedly? */
|
||
X if( writer_pid == 0 ){
|
||
X fprintf( stderr, "writer has died unexpectedly\n" );
|
||
X byee( -1 );
|
||
X }
|
||
X}
|
||
X
|
||
X/* Given a string of <num>[<suff>] returns a num
|
||
X * suff =
|
||
X * m/M for 1meg
|
||
X * k/K for 1k
|
||
X * b/B for 512
|
||
X */
|
||
Xint
|
||
Xdo_size( arg )
|
||
X char *arg;
|
||
X{
|
||
X char format[ 20 ];
|
||
X int ret;
|
||
X
|
||
X *format = '\0';
|
||
X sscanf( arg, "%d%s", &ret, format );
|
||
X
|
||
X switch( *format ){
|
||
X case 'm':
|
||
X case 'M':
|
||
X ret = ret K K;
|
||
X break;
|
||
X case 'k':
|
||
X case 'K':
|
||
X ret = ret K;
|
||
X break;
|
||
X case 'b':
|
||
X case 'B':
|
||
X ret *= 512;
|
||
X break;
|
||
X }
|
||
X
|
||
X return ret;
|
||
X}
|
||
X
|
||
Xvoid
|
||
Xpr_out()
|
||
X{
|
||
X fprintf( stderr, " %10luK\r", outk );
|
||
X}
|
||
X
|
||
X#ifdef SYS5
|
||
X#include <sys/time.h>
|
||
X
|
||
X#ifndef __alpha
|
||
Xbzero( b, l )
|
||
X char *b;
|
||
X unsigned l;
|
||
X{
|
||
X memset( b, '\0', l );
|
||
X}
|
||
X#endif /* __alpha */
|
||
X
|
||
Xusleep_back()
|
||
X{
|
||
X}
|
||
X
|
||
Xvoid
|
||
Xusleep( u )
|
||
X unsigned u;
|
||
X{
|
||
X struct itimerval old, t;
|
||
X signal( SIGALRM, usleep_back );
|
||
X t.it_interval.tv_sec = 0;
|
||
X t.it_interval.tv_usec = 0;
|
||
X t.it_value.tv_sec = u / 1000000;
|
||
X t.it_value.tv_usec = u % 1000000;
|
||
X setitimer( ITIMER_REAL, &t, &old );
|
||
X pause();
|
||
X setitimer( ITIMER_REAL, &old, NULL );
|
||
X}
|
||
X#endif
|
||
X
|
||
X/* Called before error reports */
|
||
Xvoid
|
||
Xreport_proc()
|
||
X{
|
||
X fprintf( stderr, "%s: ", proc_string );
|
||
X}
|
||
END_OF_FILE
|
||
if test 21928 -ne `wc -c <'buffer.c'`; then
|
||
echo shar: \"'buffer.c'\" unpacked with wrong size!
|
||
fi
|
||
# end of 'buffer.c'
|
||
fi
|
||
if test -f 'sem.c' -a "${1}" != "-c" ; then
|
||
echo shar: Will not clobber existing file \"'sem.c'\"
|
||
else
|
||
echo shar: Extracting \"'sem.c'\" \(3087 characters\)
|
||
sed "s/^X//" >'sem.c' <<'END_OF_FILE'
|
||
X/*
|
||
X Buffer. Very fast reblocking filter speedy writing of tapes.
|
||
X Copyright (C) 1990,1991 Lee McLoughlin
|
||
X
|
||
X This program is free software; you can redistribute it and/or modify
|
||
X it under the terms of the GNU General Public License as published by
|
||
X the Free Software Foundation; either version 1, or (at your option)
|
||
X any later version.
|
||
X
|
||
X This program is distributed in the hope that it will be useful,
|
||
X but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
X MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
X GNU General Public License for more details.
|
||
X
|
||
X You should have received a copy of the GNU General Public License
|
||
X along with this program; if not, write to the Free Software
|
||
X Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||
X
|
||
X Lee McLoughlin.
|
||
X Dept of Computing, Imperial College,
|
||
X 180 Queens Gate, London, SW7 2BZ, UK.
|
||
X
|
||
X Email: L.McLoughlin@doc.ic.ac.uk
|
||
X*/
|
||
X
|
||
X/* This is a simple module to provide an easier to understand interface to
|
||
X * semaphores */
|
||
X
|
||
X#include <stdio.h>
|
||
X#include <sys/types.h>
|
||
X#include <sys/stat.h>
|
||
X#include <sys/ipc.h>
|
||
X#include <sys/sem.h>
|
||
X#include <errno.h>
|
||
X#include "sem.h"
|
||
X
|
||
X#if defined(SYS5) || defined(ultrix) || defined(_AIX)
|
||
Xunion semun {
|
||
X int val;
|
||
X struct semid_ds *buf;
|
||
X ushort *array;
|
||
X};
|
||
X#endif
|
||
X
|
||
X/* IMPORTS */
|
||
X
|
||
X/* Used to print error messages */
|
||
Xextern void report_proc();
|
||
X
|
||
X/* Used to end the program - on error */
|
||
Xextern void byee();
|
||
X
|
||
X
|
||
X
|
||
X/* Set a semaphore to a particular value - meant to be used before
|
||
X * first lock/unlock */
|
||
Xvoid
|
||
Xsem_set( sem_id, semn, val )
|
||
X int sem_id;
|
||
X int semn;
|
||
X int val;
|
||
X{
|
||
X union semun arg;
|
||
X extern int errno;
|
||
X
|
||
X arg.val = val;
|
||
X
|
||
X errno = 0;
|
||
X semctl( sem_id, semn, SETVAL, arg );
|
||
X if( errno != 0 ){
|
||
X report_proc();
|
||
X perror( "internal error, sem_set" );
|
||
X byee( -1 );
|
||
X }
|
||
X}
|
||
X
|
||
Xint
|
||
Xnew_sems( nsems )
|
||
X int nsems;
|
||
X{
|
||
X int sem;
|
||
X int i;
|
||
X
|
||
X sem = semget( IPC_PRIVATE, nsems, IPC_CREAT|S_IREAD|S_IWRITE );
|
||
X if( sem < 0 ){
|
||
X report_proc();
|
||
X perror( "internal error, couldn't create semaphore" );
|
||
X byee( -1 );
|
||
X }
|
||
X
|
||
X for( i = 0; i < nsems; i++ ){
|
||
X sem_set( sem, i, 1 );
|
||
X }
|
||
X
|
||
X return sem;
|
||
X}
|
||
X
|
||
Xstatic
|
||
Xdo_sem( sem_id, pbuf, err )
|
||
X int sem_id;
|
||
X struct sembuf *pbuf;
|
||
X char *err;
|
||
X{
|
||
X /* This just keeps us going in case of EINTR */
|
||
X while( 1 ){
|
||
X if( semop( sem_id, pbuf, 1 ) == -1 ){
|
||
X if( errno == EINTR ){
|
||
X continue;
|
||
X }
|
||
X report_proc();
|
||
X fprintf( stderr, "internal error pid %d, lock id %d\n",
|
||
X getpid(), sem_id );
|
||
X perror( err );
|
||
X byee( -1 );
|
||
X }
|
||
X return;
|
||
X }
|
||
X}
|
||
X
|
||
Xvoid
|
||
Xlock( sem_id, semn )
|
||
X int sem_id;
|
||
X int semn;
|
||
X{
|
||
X struct sembuf sembuf;
|
||
X
|
||
X sembuf.sem_num = semn;
|
||
X sembuf.sem_op = -1;
|
||
X sembuf.sem_flg = 0;
|
||
X
|
||
X do_sem( sem_id, &sembuf, "lock error" );
|
||
X}
|
||
X
|
||
Xvoid
|
||
Xunlock( sem_id, semn )
|
||
X int sem_id;
|
||
X int semn;
|
||
X{
|
||
X struct sembuf sembuf;
|
||
X
|
||
X sembuf.sem_num = semn;
|
||
X sembuf.sem_op = 1;
|
||
X sembuf.sem_flg = 0;
|
||
X
|
||
X do_sem( sem_id, &sembuf, "unlock error" );
|
||
X}
|
||
X
|
||
Xvoid
|
||
Xremove_sems( sem_id )
|
||
X int sem_id;
|
||
X{
|
||
X if( sem_id == -1 )
|
||
X return;
|
||
X
|
||
X if( semctl( sem_id, 0, IPC_RMID, NULL ) == -1 ){
|
||
X report_proc();
|
||
X perror( "internal error, failed to remove semaphore" );
|
||
X }
|
||
X}
|
||
END_OF_FILE
|
||
if test 3087 -ne `wc -c <'sem.c'`; then
|
||
echo shar: \"'sem.c'\" unpacked with wrong size!
|
||
fi
|
||
# end of 'sem.c'
|
||
fi
|
||
if test -f 'sem.h' -a "${1}" != "-c" ; then
|
||
echo shar: Will not clobber existing file \"'sem.h'\"
|
||
else
|
||
echo shar: Extracting \"'sem.h'\" \(1189 characters\)
|
||
sed "s/^X//" >'sem.h' <<'END_OF_FILE'
|
||
X/*
|
||
X Buffer. Very fast reblocking filter speedy writing of tapes.
|
||
X Copyright (C) 1990,1991 Lee McLoughlin
|
||
X
|
||
X This program is free software; you can redistribute it and/or modify
|
||
X it under the terms of the GNU General Public License as published by
|
||
X the Free Software Foundation; either version 1, or (at your option)
|
||
X any later version.
|
||
X
|
||
X This program is distributed in the hope that it will be useful,
|
||
X but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
X MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
X GNU General Public License for more details.
|
||
X
|
||
X You should have received a copy of the GNU General Public License
|
||
X along with this program; if not, write to the Free Software
|
||
X Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||
X
|
||
X Lee McLoughlin.
|
||
X Dept of Computing, Imperial College,
|
||
X 180 Queens Gate, London, SW7 2BZ, UK.
|
||
X
|
||
X Email: L.McLoughlin@doc.ic.ac.uk
|
||
X*/
|
||
X
|
||
X/* This is a simple module to provide an easier to understand interface to
|
||
X * semaphores */
|
||
X
|
||
X/* Allocate new semaphores */
|
||
Xint new_sems();
|
||
X
|
||
X/* Perform actions on semaphores */
|
||
Xvoid sem_set();
|
||
Xvoid lock();
|
||
Xvoid unlock();
|
||
Xvoid remove_sems();
|
||
END_OF_FILE
|
||
if test 1189 -ne `wc -c <'sem.h'`; then
|
||
echo shar: \"'sem.h'\" unpacked with wrong size!
|
||
fi
|
||
# end of 'sem.h'
|
||
fi
|
||
if test -f 'COPYING' -a "${1}" != "-c" ; then
|
||
echo shar: Will not clobber existing file \"'COPYING'\"
|
||
else
|
||
echo shar: Extracting \"'COPYING'\" \(17982 characters\)
|
||
sed "s/^X//" >'COPYING' <<'END_OF_FILE'
|
||
X GNU GENERAL PUBLIC LICENSE
|
||
X Version 2, June 1991
|
||
X
|
||
X Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||
X 675 Mass Ave, Cambridge, MA 02139, USA
|
||
X Everyone is permitted to copy and distribute verbatim copies
|
||
X of this license document, but changing it is not allowed.
|
||
X
|
||
X Preamble
|
||
X
|
||
X The licenses for most software are designed to take away your
|
||
Xfreedom to share and change it. By contrast, the GNU General Public
|
||
XLicense is intended to guarantee your freedom to share and change free
|
||
Xsoftware--to make sure the software is free for all its users. This
|
||
XGeneral Public License applies to most of the Free Software
|
||
XFoundation's software and to any other program whose authors commit to
|
||
Xusing it. (Some other Free Software Foundation software is covered by
|
||
Xthe GNU Library General Public License instead.) You can apply it to
|
||
Xyour programs, too.
|
||
X
|
||
X When we speak of free software, we are referring to freedom, not
|
||
Xprice. Our General Public Licenses are designed to make sure that you
|
||
Xhave the freedom to distribute copies of free software (and charge for
|
||
Xthis service if you wish), that you receive source code or can get it
|
||
Xif you want it, that you can change the software or use pieces of it
|
||
Xin new free programs; and that you know you can do these things.
|
||
X
|
||
X To protect your rights, we need to make restrictions that forbid
|
||
Xanyone to deny you these rights or to ask you to surrender the rights.
|
||
XThese restrictions translate to certain responsibilities for you if you
|
||
Xdistribute copies of the software, or if you modify it.
|
||
X
|
||
X For example, if you distribute copies of such a program, whether
|
||
Xgratis or for a fee, you must give the recipients all the rights that
|
||
Xyou have. You must make sure that they, too, receive or can get the
|
||
Xsource code. And you must show them these terms so they know their
|
||
Xrights.
|
||
X
|
||
X We protect your rights with two steps: (1) copyright the software, and
|
||
X(2) offer you this license which gives you legal permission to copy,
|
||
Xdistribute and/or modify the software.
|
||
X
|
||
X Also, for each author's protection and ours, we want to make certain
|
||
Xthat everyone understands that there is no warranty for this free
|
||
Xsoftware. If the software is modified by someone else and passed on, we
|
||
Xwant its recipients to know that what they have is not the original, so
|
||
Xthat any problems introduced by others will not reflect on the original
|
||
Xauthors' reputations.
|
||
X
|
||
X Finally, any free program is threatened constantly by software
|
||
Xpatents. We wish to avoid the danger that redistributors of a free
|
||
Xprogram will individually obtain patent licenses, in effect making the
|
||
Xprogram proprietary. To prevent this, we have made it clear that any
|
||
Xpatent must be licensed for everyone's free use or not licensed at all.
|
||
X
|
||
X The precise terms and conditions for copying, distribution and
|
||
Xmodification follow.
|
||
X
|
||
X GNU GENERAL PUBLIC LICENSE
|
||
X TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||
X
|
||
X 0. This License applies to any program or other work which contains
|
||
Xa notice placed by the copyright holder saying it may be distributed
|
||
Xunder the terms of this General Public License. The "Program", below,
|
||
Xrefers to any such program or work, and a "work based on the Program"
|
||
Xmeans either the Program or any derivative work under copyright law:
|
||
Xthat is to say, a work containing the Program or a portion of it,
|
||
Xeither verbatim or with modifications and/or translated into another
|
||
Xlanguage. (Hereinafter, translation is included without limitation in
|
||
Xthe term "modification".) Each licensee is addressed as "you".
|
||
X
|
||
XActivities other than copying, distribution and modification are not
|
||
Xcovered by this License; they are outside its scope. The act of
|
||
Xrunning the Program is not restricted, and the output from the Program
|
||
Xis covered only if its contents constitute a work based on the
|
||
XProgram (independent of having been made by running the Program).
|
||
XWhether that is true depends on what the Program does.
|
||
X
|
||
X 1. You may copy and distribute verbatim copies of the Program's
|
||
Xsource code as you receive it, in any medium, provided that you
|
||
Xconspicuously and appropriately publish on each copy an appropriate
|
||
Xcopyright notice and disclaimer of warranty; keep intact all the
|
||
Xnotices that refer to this License and to the absence of any warranty;
|
||
Xand give any other recipients of the Program a copy of this License
|
||
Xalong with the Program.
|
||
X
|
||
XYou may charge a fee for the physical act of transferring a copy, and
|
||
Xyou may at your option offer warranty protection in exchange for a fee.
|
||
X
|
||
X 2. You may modify your copy or copies of the Program or any portion
|
||
Xof it, thus forming a work based on the Program, and copy and
|
||
Xdistribute such modifications or work under the terms of Section 1
|
||
Xabove, provided that you also meet all of these conditions:
|
||
X
|
||
X a) You must cause the modified files to carry prominent notices
|
||
X stating that you changed the files and the date of any change.
|
||
X
|
||
X b) You must cause any work that you distribute or publish, that in
|
||
X whole or in part contains or is derived from the Program or any
|
||
X part thereof, to be licensed as a whole at no charge to all third
|
||
X parties under the terms of this License.
|
||
X
|
||
X c) If the modified program normally reads commands interactively
|
||
X when run, you must cause it, when started running for such
|
||
X interactive use in the most ordinary way, to print or display an
|
||
X announcement including an appropriate copyright notice and a
|
||
X notice that there is no warranty (or else, saying that you provide
|
||
X a warranty) and that users may redistribute the program under
|
||
X these conditions, and telling the user how to view a copy of this
|
||
X License. (Exception: if the Program itself is interactive but
|
||
X does not normally print such an announcement, your work based on
|
||
X the Program is not required to print an announcement.)
|
||
X
|
||
XThese requirements apply to the modified work as a whole. If
|
||
Xidentifiable sections of that work are not derived from the Program,
|
||
Xand can be reasonably considered independent and separate works in
|
||
Xthemselves, then this License, and its terms, do not apply to those
|
||
Xsections when you distribute them as separate works. But when you
|
||
Xdistribute the same sections as part of a whole which is a work based
|
||
Xon the Program, the distribution of the whole must be on the terms of
|
||
Xthis License, whose permissions for other licensees extend to the
|
||
Xentire whole, and thus to each and every part regardless of who wrote it.
|
||
X
|
||
XThus, it is not the intent of this section to claim rights or contest
|
||
Xyour rights to work written entirely by you; rather, the intent is to
|
||
Xexercise the right to control the distribution of derivative or
|
||
Xcollective works based on the Program.
|
||
X
|
||
XIn addition, mere aggregation of another work not based on the Program
|
||
Xwith the Program (or with a work based on the Program) on a volume of
|
||
Xa storage or distribution medium does not bring the other work under
|
||
Xthe scope of this License.
|
||
X
|
||
X 3. You may copy and distribute the Program (or a work based on it,
|
||
Xunder Section 2) in object code or executable form under the terms of
|
||
XSections 1 and 2 above provided that you also do one of the following:
|
||
X
|
||
X a) Accompany it with the complete corresponding machine-readable
|
||
X source code, which must be distributed under the terms of Sections
|
||
X 1 and 2 above on a medium customarily used for software interchange; or,
|
||
X
|
||
X b) Accompany it with a written offer, valid for at least three
|
||
X years, to give any third party, for a charge no more than your
|
||
X cost of physically performing source distribution, a complete
|
||
X machine-readable copy of the corresponding source code, to be
|
||
X distributed under the terms of Sections 1 and 2 above on a medium
|
||
X customarily used for software interchange; or,
|
||
X
|
||
X c) Accompany it with the information you received as to the offer
|
||
X to distribute corresponding source code. (This alternative is
|
||
X allowed only for noncommercial distribution and only if you
|
||
X received the program in object code or executable form with such
|
||
X an offer, in accord with Subsection b above.)
|
||
X
|
||
XThe source code for a work means the preferred form of the work for
|
||
Xmaking modifications to it. For an executable work, complete source
|
||
Xcode means all the source code for all modules it contains, plus any
|
||
Xassociated interface definition files, plus the scripts used to
|
||
Xcontrol compilation and installation of the executable. However, as a
|
||
Xspecial exception, the source code distributed need not include
|
||
Xanything that is normally distributed (in either source or binary
|
||
Xform) with the major components (compiler, kernel, and so on) of the
|
||
Xoperating system on which the executable runs, unless that component
|
||
Xitself accompanies the executable.
|
||
X
|
||
XIf distribution of executable or object code is made by offering
|
||
Xaccess to copy from a designated place, then offering equivalent
|
||
Xaccess to copy the source code from the same place counts as
|
||
Xdistribution of the source code, even though third parties are not
|
||
Xcompelled to copy the source along with the object code.
|
||
X
|
||
X 4. You may not copy, modify, sublicense, or distribute the Program
|
||
Xexcept as expressly provided under this License. Any attempt
|
||
Xotherwise to copy, modify, sublicense or distribute the Program is
|
||
Xvoid, and will automatically terminate your rights under this License.
|
||
XHowever, parties who have received copies, or rights, from you under
|
||
Xthis License will not have their licenses terminated so long as such
|
||
Xparties remain in full compliance.
|
||
X
|
||
X 5. You are not required to accept this License, since you have not
|
||
Xsigned it. However, nothing else grants you permission to modify or
|
||
Xdistribute the Program or its derivative works. These actions are
|
||
Xprohibited by law if you do not accept this License. Therefore, by
|
||
Xmodifying or distributing the Program (or any work based on the
|
||
XProgram), you indicate your acceptance of this License to do so, and
|
||
Xall its terms and conditions for copying, distributing or modifying
|
||
Xthe Program or works based on it.
|
||
X
|
||
X 6. Each time you redistribute the Program (or any work based on the
|
||
XProgram), the recipient automatically receives a license from the
|
||
Xoriginal licensor to copy, distribute or modify the Program subject to
|
||
Xthese terms and conditions. You may not impose any further
|
||
Xrestrictions on the recipients' exercise of the rights granted herein.
|
||
XYou are not responsible for enforcing compliance by third parties to
|
||
Xthis License.
|
||
X
|
||
X 7. If, as a consequence of a court judgment or allegation of patent
|
||
Xinfringement or for any other reason (not limited to patent issues),
|
||
Xconditions are imposed on you (whether by court order, agreement or
|
||
Xotherwise) that contradict the conditions of this License, they do not
|
||
Xexcuse you from the conditions of this License. If you cannot
|
||
Xdistribute so as to satisfy simultaneously your obligations under this
|
||
XLicense and any other pertinent obligations, then as a consequence you
|
||
Xmay not distribute the Program at all. For example, if a patent
|
||
Xlicense would not permit royalty-free redistribution of the Program by
|
||
Xall those who receive copies directly or indirectly through you, then
|
||
Xthe only way you could satisfy both it and this License would be to
|
||
Xrefrain entirely from distribution of the Program.
|
||
X
|
||
XIf any portion of this section is held invalid or unenforceable under
|
||
Xany particular circumstance, the balance of the section is intended to
|
||
Xapply and the section as a whole is intended to apply in other
|
||
Xcircumstances.
|
||
X
|
||
XIt is not the purpose of this section to induce you to infringe any
|
||
Xpatents or other property right claims or to contest validity of any
|
||
Xsuch claims; this section has the sole purpose of protecting the
|
||
Xintegrity of the free software distribution system, which is
|
||
Ximplemented by public license practices. Many people have made
|
||
Xgenerous contributions to the wide range of software distributed
|
||
Xthrough that system in reliance on consistent application of that
|
||
Xsystem; it is up to the author/donor to decide if he or she is willing
|
||
Xto distribute software through any other system and a licensee cannot
|
||
Ximpose that choice.
|
||
X
|
||
XThis section is intended to make thoroughly clear what is believed to
|
||
Xbe a consequence of the rest of this License.
|
||
X
|
||
X 8. If the distribution and/or use of the Program is restricted in
|
||
Xcertain countries either by patents or by copyrighted interfaces, the
|
||
Xoriginal copyright holder who places the Program under this License
|
||
Xmay add an explicit geographical distribution limitation excluding
|
||
Xthose countries, so that distribution is permitted only in or among
|
||
Xcountries not thus excluded. In such case, this License incorporates
|
||
Xthe limitation as if written in the body of this License.
|
||
X
|
||
X 9. The Free Software Foundation may publish revised and/or new versions
|
||
Xof the General Public License from time to time. Such new versions will
|
||
Xbe similar in spirit to the present version, but may differ in detail to
|
||
Xaddress new problems or concerns.
|
||
X
|
||
XEach version is given a distinguishing version number. If the Program
|
||
Xspecifies a version number of this License which applies to it and "any
|
||
Xlater version", you have the option of following the terms and conditions
|
||
Xeither of that version or of any later version published by the Free
|
||
XSoftware Foundation. If the Program does not specify a version number of
|
||
Xthis License, you may choose any version ever published by the Free Software
|
||
XFoundation.
|
||
X
|
||
X 10. If you wish to incorporate parts of the Program into other free
|
||
Xprograms whose distribution conditions are different, write to the author
|
||
Xto ask for permission. For software which is copyrighted by the Free
|
||
XSoftware Foundation, write to the Free Software Foundation; we sometimes
|
||
Xmake exceptions for this. Our decision will be guided by the two goals
|
||
Xof preserving the free status of all derivatives of our free software and
|
||
Xof promoting the sharing and reuse of software generally.
|
||
X
|
||
X NO WARRANTY
|
||
X
|
||
X 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||
XFOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||
XOTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||
XPROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||
XOR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||
XMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||
XTO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||
XPROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||
XREPAIR OR CORRECTION.
|
||
X
|
||
X 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||
XWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||
XREDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||
XINCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||
XOUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||
XTO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||
XYOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||
XPROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||
XPOSSIBILITY OF SUCH DAMAGES.
|
||
X
|
||
X END OF TERMS AND CONDITIONS
|
||
X
|
||
X Appendix: How to Apply These Terms to Your New Programs
|
||
X
|
||
X If you develop a new program, and you want it to be of the greatest
|
||
Xpossible use to the public, the best way to achieve this is to make it
|
||
Xfree software which everyone can redistribute and change under these terms.
|
||
X
|
||
X To do so, attach the following notices to the program. It is safest
|
||
Xto attach them to the start of each source file to most effectively
|
||
Xconvey the exclusion of warranty; and each file should have at least
|
||
Xthe "copyright" line and a pointer to where the full notice is found.
|
||
X
|
||
X <one line to give the program's name and a brief idea of what it does.>
|
||
X Copyright (C) 19yy <name of author>
|
||
X
|
||
X This program is free software; you can redistribute it and/or modify
|
||
X it under the terms of the GNU General Public License as published by
|
||
X the Free Software Foundation; either version 2 of the License, or
|
||
X (at your option) any later version.
|
||
X
|
||
X This program is distributed in the hope that it will be useful,
|
||
X but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
X MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
X GNU General Public License for more details.
|
||
X
|
||
X You should have received a copy of the GNU General Public License
|
||
X along with this program; if not, write to the Free Software
|
||
X Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||
X
|
||
XAlso add information on how to contact you by electronic and paper mail.
|
||
X
|
||
XIf the program is interactive, make it output a short notice like this
|
||
Xwhen it starts in an interactive mode:
|
||
X
|
||
X Gnomovision version 69, Copyright (C) 19yy name of author
|
||
X Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||
X This is free software, and you are welcome to redistribute it
|
||
X under certain conditions; type `show c' for details.
|
||
X
|
||
XThe hypothetical commands `show w' and `show c' should show the appropriate
|
||
Xparts of the General Public License. Of course, the commands you use may
|
||
Xbe called something other than `show w' and `show c'; they could even be
|
||
Xmouse-clicks or menu items--whatever suits your program.
|
||
X
|
||
XYou should also get your employer (if you work as a programmer) or your
|
||
Xschool, if any, to sign a "copyright disclaimer" for the program, if
|
||
Xnecessary. Here is a sample; alter the names:
|
||
X
|
||
X Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||
X `Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||
X
|
||
X <signature of Ty Coon>, 1 April 1989
|
||
X Ty Coon, President of Vice
|
||
X
|
||
XThis General Public License does not permit incorporating your program into
|
||
Xproprietary programs. If your program is a subroutine library, you may
|
||
Xconsider it more useful to permit linking proprietary applications with the
|
||
Xlibrary. If this is what you want to do, use the GNU Library General
|
||
XPublic License instead of this License.
|
||
END_OF_FILE
|
||
if test 17982 -ne `wc -c <'COPYING'`; then
|
||
echo shar: \"'COPYING'\" unpacked with wrong size!
|
||
fi
|
||
# end of 'COPYING'
|
||
fi
|
||
echo shar: End of shell archive.
|
||
exit 0
|