[comp.sources.mac] Tar 1.1 Source

cruff@ncar.ucar.edu (Craig Ruff) (05/12/88)

[Tar 1.1 Source - part 1 of 3]

[Moderator's Note:  The Tar 1.1 binary will be posted in comp.binaries.mac.]

---
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	README
#	Makefile
#	tar.h
#	tar.c
#	tar.resource
#	buffer.c
# This archive created: Wed May 11 07:24:03 1988
# By:	Roger L. Long (bytebug@dhw68k.cts.com)
export PATH; PATH=/bin:$PATH
echo shar: extracting "'README'" '(355 characters)'
if test -f 'README'
then
	echo shar: will not over-write existing file "'README'"
else
sed 's/^X//' << \SHAR_EOF > 'README'
X		Tar for the Macintosh
X
XThese files make up the sources of tar for the Macintosh.  I used Aztec C
Xv3.4b to compile the program.  I then used ResEdit to copy the (compiled)
Xresources to the application.  The file tar.resource contains the decompiled
Xresources.  This was done with ResTools 3.00.  You can use that program
Xor Rez to rebuild the resources.
SHAR_EOF
if test 355 -ne "`wc -c < 'README'`"
then
	echo shar: error transmitting "'README'" '(should have been 355 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'Makefile'" '(244 characters)'
if test -f 'Makefile'
then
	echo shar: will not over-write existing file "'Makefile'"
else
sed 's/^X//' << \SHAR_EOF > 'Makefile'
XOBJECTS = buffer.o create.o dialog.o dir.o extract.o list.o menu.o tar.o window.o
X
Xtar:	$(OBJECTS) hdrs.dmp
X	ln -W -o tar -M ../lib3/sacroot.o $(OBJECTS) -lc
X
X$(OBJECTS): hdrs.dmp
X	cc +Ihdrs.dmp -o $@ $*.c
X
Xhdrs.dmp: tar.h
X	cc +Hhdrs.dmp tar.h
SHAR_EOF
if test 244 -ne "`wc -c < 'Makefile'`"
then
	echo shar: error transmitting "'Makefile'" '(should have been 244 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'tar.h'" '(3783 characters)'
if test -f 'tar.h'
then
	echo shar: will not over-write existing file "'tar.h'"
else
sed 's/^X//' << \SHAR_EOF > 'tar.h'
X#include <types.h>
X#include <quickdraw.h>
X#include <font.h>
X#include <event.h>
X#include <control.h>
X#include <window.h>
X#include <menu.h>
X#include <textedit.h>
X#include <dialog.h>
X#include <desk.h>
X#include <toolutil.h>
X#include <osutil.h>
X#include <scrap.h>
X#include <packages.h>
X#include <list.h>
X#include <pb.h>
X#include <memory.h>
X#include <print.h>
X#include <syserr.h>
X#include <setjmp.h>
X
X#ifndef NIL
X#define NIL	(0L)
X#endif
X
X#ifndef EOF
X#define EOF	(-1L)
X#endif
X
X#define DIRECTORY(pb)	(((pb).ioFlAttrib & 0x10) == 0x10)
X
X/*
X * Character definitions
X */
X#define ENTER	0x03
X#define BS	0x08
X#define TAB	0x09
X#define	RETURN	0x0d
X
X/*
X * Difference between Mac and Unix times
X */
X#define TIMEDIFF	0x7c25b080
X
X/*
X * Global Variables
X */
Xextern Boolean	autoPage;
Xextern Boolean	cvtNl;
Xextern Boolean	doneFlag;
Xextern Boolean	doPrint;
Xextern Boolean	ignorez;
Xextern Boolean	menusOK;
Xextern Boolean	pOpen;
X
Xextern char	fdCreator[];
Xextern char	fdType[];
Xextern char	header[];
X
Xextern jmp_buf	errJmp;
X
Xextern THPrint	prRecHdl;
X
X/*
X * Standard File and GetDir saved outputs
X */
Xextern char	*arName;
Xextern short	arVRefNum;
Xextern long	dirDirID;
Xextern short	dirVRefNum;
X
X/*
X * External routines
X */
Xextern Boolean	GetDir();
Xextern Boolean	MenuInit();
Xextern Boolean	PrSetup();
Xextern Boolean	WindInit();
X
X/*
X * Remainder taken from:
X * Header file for public domain tar (tape archive) program.
X *
X * @(#)tar.h 1.20 86/10/29	Public Domain.
X *
X * Created 25 August 1985 by John Gilmore, ihnp4!hoptoad!gnu.
X */
X
X/*
X * Header block on tape.
X *
X * I'm going to use traditional DP naming conventions here.
X * A "block" is a big chunk of stuff that we do I/O on.
X * A "record" is a piece of info that we care about.
X * Typically many "record"s fit into a "block".
X */
X#define	RECORDSIZE	512
X#define	NAMSIZ	100
X#define	TUNMLEN	32
X#define	TGNMLEN	32
X
Xunion record {
X	char		charptr[RECORDSIZE];
X	struct header {
X		char	name[NAMSIZ];
X		char	mode[8];
X		char	uid[8];
X		char	gid[8];
X		char	size[12];
X		char	mtime[12];
X		char	chksum[8];
X		char	linkflag;
X		char	linkname[NAMSIZ];
X		char	magic[8];
X		char	uname[TUNMLEN];
X		char	gname[TGNMLEN];
X		char	devmajor[8];
X		char	devminor[8];
X	} header;
X};
X
X/* The checksum field is filled with this while the checksum is computed. */
X#define	CHKBLANKS	"        "	/* 8 blanks, no null */
X
X/* The magic field is filled with this if uname and gname are valid. */
X#define	TMAGIC		"ustar  "	/* 7 chars and a null */
X
X/* The linkflag defines the type of file */
X#define	LF_OLDNORMAL	'\0'		/* Normal disk file, Unix compat */
X#define	LF_NORMAL	'0'		/* Normal disk file */
X#define	LF_LINK		'1'		/* Link to previously dumped file */
X#define	LF_SYMLINK	'2'		/* Symbolic link */
X#define	LF_CHR		'3'		/* Character special file */
X#define	LF_BLK		'4'		/* Block special file */
X#define	LF_DIR		'5'		/* Directory */
X#define	LF_FIFO		'6'		/* FIFO special file */
X#define	LF_CONTIG	'7'		/* Contiguous file */
X/* Further link types may be defined later. */
X
X/*
X * Global variables
X */
Xextern int		blocking;	/* Size of each block, in records */
Xextern int		blockSize;	/* Size of each block, in bytes */
Xextern Boolean		reblock;
Xextern Boolean		oldArch;
X
X/*
X * We now default to Unix Standard format rather than 4.2BSD tar format.
X * The code can actually produce all three:
X *	standard	ANSI standard
X *	oldarch		V7
X *	neither		4.2BSD
X * but we don't bother, since 4.2BSD can read ANSI standard format anyway.
X * The only advantage to the "neither" option is that we can cmp(1) our
X * output to the output of 4.2BSD tar, for debugging.
X */
X#define	standard	(!oldArch)
X
Xextern short	archive;	/* File descriptor for archive file */
X
X/*
X * Declarations of functions available to the world.
X */
Xunion record	*FindRec();
Xvoid		UseRec();
Xunion record	*EndOfRecs();
XBoolean		OpenArchive();
SHAR_EOF
if test 3783 -ne "`wc -c < 'tar.h'`"
then
	echo shar: error transmitting "'tar.h'" '(should have been 3783 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'tar.c'" '(2669 characters)'
if test -f 'tar.c'
then
	echo shar: will not over-write existing file "'tar.c'"
else
sed 's/^X//' << \SHAR_EOF > 'tar.c'
X/*
X * Macintosh TAR
X *
X * Written by Craig Ruff
X *
X * Main routine and global variables.
X */
X
X#include "tar.h"
X#include <segment.h>
X
X#ifdef DEBUG
X#include <resource.h>
X#endif
X
X#define FSFCBLen	(*(short *) 0x3f6)
X
X/*
X * Global variable declarations
X */
XBoolean autoPage = FALSE;	/* List by screenfulls */
XBoolean cvtNl = FALSE;		/* Convert newline to return and back */
XBoolean doneFlag = FALSE;	/* Quit item selected? */
XBoolean doPrint = FALSE;	/* List to printer */
XBoolean ignorez = FALSE;	/* Ignore zero blocks */
XBoolean	pOpen = FALSE;		/* Printer open? */
XBoolean reblock = FALSE;	/* Read enough for a full block size buffer */
XBoolean oldArch = FALSE;	/* Old tar compatible */
X
XTHPrint	prRecHdl;		/* Printer record (when printing) */
X
Xchar	*arName;		/* Archive file name */
Xshort	arVRefNum;		/* Archive file VRefNum */
X
Xshort	archive;		/* File descriptor for archive file */
Xint	blocking;		/* Size of each block, in records */
Xint	blockSize;		/* Size of each block, in bytes */
X
Xchar	fdCreator[4];		/* Finder Creator */
Xchar	fdType[4];		/* Finder Type */
Xchar	header[128];		/* Common header for listings */
Xjmp_buf	errJmp;			/* For error exits */
X
Xmain() {
X	WindowPtr	wind;
X	EventRecord	e;
X
X	/*
X	 * Standard Mac Initializations
X	 */
X	InitGraf(&thePort);
X	InitFonts();
X	FlushEvents(everyEvent, 0);
X	InitWindows();
X	InitMenus();
X	TEInit();
X	InitDialogs(NIL);
X	InitCursor();
X
X#ifdef DEBUG
X	/*
X	 * So we don't keep copying resources into our application.
X	 */
X	if (OpenResFile("\Psys:C:tar:tar.rsrc") == -1) {
X		OSAlert("\Pmain", "\PDebug OpenResFile", NIL, 0);
X		ExitToShell();
X	}
X#endif
X
X	/* Check for HFS running */
X	if (FSFCBLen <= 0) {
X		HFSAlert();
X		ExitToShell();
X	}
X
X	if (MenuInit())
X		ExitToShell();
X
X	blocking = 20;
X	blockSize = blocking * RECORDSIZE;
X	strncpy(fdCreator, "????", 4);
X	strncpy(fdType, "TEXT", 4);
X	if ((prRecHdl = (THPrint) NewHandle((long) sizeof(TPrint))) == NIL) {
X		OSAlert("\Pmain", "\PNewHandle returned NIL", NIL, MemError());
X		ExitToShell();
X	}
X
X	sprintf(header, "T     Size Date              Name%*s", 40, "");
X	do {
X		SystemTask();
X		/* Should never have a window open while a DA is running */
X		if (!menusOK && (FrontWindow() == NIL))
X			DDaMenus();
X
X		if (GetNextEvent(everyEvent, &e))
X			switch (e.what) {
X			case mouseDown:
X				switch (FindWindow(pass(e.where), &wind)) {
X				case inSysWindow:
X					SystemClick(&e, wind);
X					break;
X
X				case inMenuBar:
X					MenuCmd(MenuSelect(pass(e.where)));
X					break;
X
X				}
X				break;
X
X			case keyDown:
X			case autoKey:
X				if (e.modifiers & cmdKey)
X					MenuCmd(MenuKey(e.message & charCodeMask));
X				break;
X			}
X
X	} while (!doneFlag);
X
X	if (pOpen)
X		PrClose();
X
X	ExitToShell();
X}
SHAR_EOF
if test 2669 -ne "`wc -c < 'tar.c'`"
then
	echo shar: error transmitting "'tar.c'" '(should have been 2669 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'tar.resource'" '(10649 characters)'
if test -f 'tar.resource'
then
	echo shar: will not over-write existing file "'tar.resource'"
else
sed 's/^X//' << \SHAR_EOF > 'tar.resource'
Xresource  'PICT' (128, purgeable)
X{
X    474,
X    {7, 7, 114, 254},
X    {17; 1; 160; 0; 130; 161; 0; 150; 0; 6; 5; 0; 0
X    ; 0; 2; 152; 161; 0; 154; 0; 8; 255; 252; 0; 0
X    ; 0; 69; 0; 0; 160; 0; 152; 1; 0; 10; 0; 7
X    ; 0; 7; 0; 114; 0; 254; 3; 0; 2; 13; 0; 12
X    ; 43; 11; 22; 21; 84; 97; 114; 32; 102; 111; 114; 32
X    ; 116; 104; 101; 32; 77; 97; 99; 105; 110; 116; 111; 115
X    ; 104; 160; 0; 153; 160; 0; 151; 161; 0; 150; 0; 6
X    ; 5; 0; 0; 0; 2; 152; 161; 0; 154; 0; 8; 0
X    ; 26; 0; 0; 0; 120; 0; 0; 160; 0; 152; 13; 0
X    ; 10; 42; 27; 34; 87; 114; 105; 116; 116; 101; 110; 32
X    ; 98; 121; 32; 67; 114; 97; 105; 103; 32; 82; 117; 102
X    ; 102; 32; 119; 105; 116; 104; 32; 101; 120; 99; 101; 114
X    ; 112; 116; 41; 191; 7; 115; 32; 102; 114; 111; 109; 13
X    ; 160; 0; 153; 161; 0; 154; 0; 8; 0; 14; 0; 0
X    ; 0; 120; 0; 0; 160; 0; 152; 40; 0; 61; 0; 11
X    ; 34; 74; 111; 104; 110; 32; 71; 105; 108; 109; 111; 114
X    ; 101; 39; 115; 32; 112; 117; 98; 108; 105; 99; 32; 100
X    ; 111; 109; 97; 105; 110; 32; 116; 97; 114; 32; 112; 41
X    ; 191; 8; 114; 111; 103; 114; 97; 109; 46; 13; 160; 0
X    ; 153; 161; 0; 154; 0; 8; 0; 2; 0; 0; 0; 120
X    ; 0; 0; 160; 0; 152; 40; 0; 73; 0; 11; 1; 13
X    ; 160; 0; 153; 161; 0; 154; 0; 8; 255; 246; 0; 0
X    ; 0; 120; 0; 0; 160; 0; 152; 42; 12; 34; 84; 104
X    ; 105; 115; 32; 112; 114; 111; 103; 114; 97; 109; 32; 105
X    ; 115; 32; 112; 117; 98; 108; 105; 99; 32; 100; 111; 109
X    ; 97; 105; 110; 46; 32; 32; 78; 111; 41; 190; 9; 32
X    ; 102; 101; 101; 32; 109; 97; 121; 13; 160; 0; 153; 161
X    ; 0; 154; 0; 8; 255; 234; 0; 0; 0; 120; 0; 0
X    ; 160; 0; 152; 40; 0; 97; 0; 11; 34; 98; 101; 32
X    ; 99; 104; 97; 114; 103; 101; 100; 32; 102; 111; 114; 32
X    ; 97; 32; 99; 111; 112; 121; 32; 111; 102; 32; 116; 104
X    ; 105; 115; 32; 112; 114; 111; 103; 41; 186; 8; 114; 97
X    ; 109; 32; 97; 110; 100; 13; 160; 0; 153; 161; 0; 154
X    ; 0; 8; 255; 222; 0; 0; 0; 120; 0; 0; 160; 0
X    ; 152; 40; 0; 109; 0; 11; 34; 100; 105; 115; 116; 114
X    ; 105; 98; 117; 116; 105; 111; 110; 32; 109; 97; 121; 32
X    ; 110; 111; 116; 32; 98; 101; 32; 114; 101; 115; 116; 114
X    ; 105; 99; 116; 101; 100; 41; 184; 1; 46; 160; 0; 153
X    ; 160; 0; 151; 160; 0; 131; 255}
X};
X
Xuserdefined resource  'TAR ' (0)
X{
X    long:0x8546172;
X    long:0x2076312e;
X    byte:49};
X
Xresource  'BNDL' (128)
X{
X    'TAR ',     0,
X    {
X    'ICN#', {0, 128; 1, 129};
X    'FREF', {0, 128; 1, 129}
X    }
X};
X
Xresource  'FREF' (128)
X{
X    'APPL',
X    0,
X    ""
X};
X
Xresource  'FREF' (129)
X{
X    'TARF',
X    1,
X    ""
X};
X
Xresource  'ICN#' (128)
X{
X    0xffff 0xffff 0x8000 0x0001 
X    0x8780 0x01e1 0x8840 0x0211 
X    0x97a0 0x0409 0xafd0 0x0805 
X    0xafd0 0x08c5 0xafd0 0x08c5 
X    0xafd0 0x0805 0x97a0 0x0409 
X    0x8840 0x0211 0x8f80 0x01e1 
X    0x8400 0x0041 0x8200 0x0081 
X    0x810f 0xf101 0x8088 0x1201 
X    0x8048 0x1401 0x8027 0xe801 
X    0x801f 0xf001 0x8000 0x0001 
X    0x8000 0x0001 0x8000 0x0001 
X    0x80f9 0xcf01 0x8022 0x2881 
X    0x8022 0x2881 0x8023 0xef01 
X    0x8022 0x2901 0x8022 0x2881 
X    0x8022 0x2881 0x8000 0x0001 
X    0x8000 0x0001 0xffff 0xffff ;
X
X    0xffff 0xffff 0xffff 0xffff 
X    0xffff 0xffff 0xffff 0xffff 
X    0xffff 0xffff 0xffff 0xffff 
X    0xffff 0xffff 0xffff 0xffff 
X    0xffff 0xffff 0xffff 0xffff 
X    0xffff 0xffff 0xffff 0xffff 
X    0xffff 0xffff 0xffff 0xffff 
X    0xffff 0xffff 0xffff 0xffff 
X    0xffff 0xffff 0xffff 0xffff 
X    0xffff 0xffff 0xffff 0xffff 
X    0xffff 0xffff 0xffff 0xffff 
X    0xffff 0xffff 0xffff 0xffff 
X    0xffff 0xffff 0xffff 0xffff 
X    0xffff 0xffff 0xffff 0xffff 
X    0xffff 0xffff 0xffff 0xffff 
X    0xffff 0xffff 0xffff 0xffff 
X};
X
Xresource  'ICN#' (129)
X{
X    0x0000 0x0000 0x0000 0x0000 
X    0x000f 0xf000 0x0030 0x0c00 
X    0x00c0 0x0300 0x0100 0x0080 
X    0x0200 0x0040 0x0400 0x0020 
X    0x0800 0x0010 0x0800 0x0010 
X    0x1000 0x0008 0x1000 0x0008 
X    0x21f3 0x9e04 0x2044 0x5104 
X    0x2044 0x5104 0x2047 0xde04 
X    0x2044 0x5204 0x2044 0x5104 
X    0x2044 0x5104 0x2000 0x0004 
X    0x1000 0x0008 0x1000 0x0008 
X    0x0800 0x0010 0x0800 0x0010 
X    0x0400 0x0020 0x0200 0x0040 
X    0x0100 0x0080 0x00c0 0x0300 
X    0x0030 0x0c00 0x000f 0xfffc 
X    0x0000 0x0000 0x0000 0x0000 ;
X
X    0x0000 0x0000 0x0000 0x0000 
X    0x000f 0xf000 0x003f 0xfc00 
X    0x00ff 0xff00 0x01ff 0xff80 
X    0x03ff 0xffc0 0x07ff 0xffe0 
X    0x0fff 0xfff0 0x0fff 0xfff0 
X    0x1fff 0xfff8 0x1fff 0xfff8 
X    0x3fff 0xfffc 0x3fff 0xfffc 
X    0x3fff 0xfffc 0x3fff 0xfffc 
X    0x3fff 0xfffc 0x3fff 0xfffc 
X    0x3fff 0xfffc 0x3fff 0xfffc 
X    0x1fff 0xfff8 0x1fff 0xfff8 
X    0x0fff 0xfff0 0x0fff 0xfff0 
X    0x07ff 0xffe0 0x03ff 0xffc0 
X    0x01ff 0xff80 0x00ff 0xff00 
X    0x003f 0xfc00 0x000f 0xfffc 
X    0x0000 0x0000 0x0000 0x0000 
X};
X
Xresource  'WIND' (129)
X{
X    {40, 20, 320, 480},
X    1,
X    visible,
X    noGoAway,
X    0x0,
X    ""
X};
X
Xresource  'ALRT' (129, purgeable)
X{
X    {50, 65, 140, 385},
X    192,
X    OK,visible,0,
X    OK,visible,0,
X    OK,visible,0,
X    OK,visible,0
X};
X
Xresource  'ALRT' (130, purgeable)
X{
X    {50, 65, 200, 385},
X    193,
X    OK,visible,0,
X    OK,visible,0,
X    OK,visible,0,
X    OK,visible,0
X};
X
Xresource  'ALRT' (131, purgeable)
X{
X    {50, 65, 200, 385},
X    194,
X    OK,visible,0,
X    OK,visible,0,
X    OK,visible,0,
X    OK,visible,0
X};
X
Xresource  'ALRT' (132, purgeable)
X{
X    {50, 65, 140, 385},
X    195,
X    OK,visible,0,
X    OK,visible,0,
X    OK,visible,0,
X    OK,visible,0
X};
X
Xresource  'ALRT' (133, purgeable)
X{
X    {50, 65, 140, 385},
X    196,
X    OK,visible,0,
X    OK,visible,0,
X    OK,visible,0,
X    OK,visible,0
X};
X
Xresource  'ALRT' (134, purgeable)
X{
X    {50, 65, 140, 385},
X    197,
X    OK,visible,0,
X    OK,visible,0,
X    OK,visible,0,
X    OK,visible,0
X};
X
Xresource  'DITL' (129, purgeable)
X{
X  {
X    {15, 200, 37, 260},
X      Button {enabled, "OK"};
X    {50, 200, 74, 260},
X      Button {enabled, "Cancel"};
X    {40, 80, 55, 121},
X      EditText {enabled, ""};
X    {15, 40, 35, 159},
X      StaticText {enabled, "Enter block size:"}
X  }
X};
X
Xresource  'DITL' (192, purgeable)
X{
X  {
X    {60, 124, 80, 184},
X      Button {enabled, "OK"};
X    {9, 67, 49, 256},
X      StaticText {enabled, "Block size must be between 1 and 128 inclusive!"}
X  }
X};
X
Xresource  'DITL' (130, purgeable)
X{
X  {
X    {35, 15, 56, 160},
X      Button {enabled, ""};
X    {179, 256, 200, 336},
X      Button {enabled, "Cancel"};
X    {55, 247, 75, 341},
X      StaticText {disabled, ""};
X    {67, 456, 85, 536},
X      Button {disabled, "Eject"};
X    {93, 456, 111, 536},
X      Button {disabled, "Drive"};
X    {65, 15, 210, 230},
X      Useritem {enabled, 7215};
X    {141, 252, 142, 339},
X      Useritem {disabled, 8274};
X    {35, 168, 56, 228},
X      Button {enabled, "UP"};
X    {151, 256, 172, 336},
X      Button {enabled, "Open"};
X    {7, 15, 28, 339},
X      StaticText {enabled, "Select directory ^0:"};
X    {83, 256, 104, 336},
X      Button {enabled, "Drive"};
X    {111, 256, 132, 336},
X      Button {enabled, "Eject"}
X  }
X};
X
Xresource  'DITL' (193, purgeable)
X{
X  {
X    {121, 126, 141, 186},
X      Button {enabled, "OK"};
X    {10, 68, 57, 311},
X      StaticText {enabled, "^0: ^1"};
X    {60, 69, 91, 313},
X      StaticText {enabled, "^2"};
X    {96, 80, 116, 200},
X      StaticText {enabled, "OS Error: ^3"}
X  }
X};
X
Xresource  'DITL' (194, purgeable)
X{
X  {
X    {122, 123, 142, 183},
X      Button {enabled, "OK"};
X    {9, 99, 28, 289},
X      StaticText {enabled, "^0: ^1"};
X    {35, 100, 91, 289},
X      StaticText {enabled, "^2"};
X    {95, 101, 115, 289},
X      StaticText {enabled, "^3"}
X  }
X};
X
Xresource  'DITL' (195, purgeable)
X{
X  {
X    {60, 124, 80, 184},
X      Button {enabled, "OK"};
X    {9, 92, 49, 221},
X      StaticText {enabled, "Macintosh Tar only works with HFS."}
X  }
X};
X
Xresource  'DITL' (196)
X{
X  {
X    {63, 130, 83, 190},
X      Button {enabled, "OK"};
X    {9, 81, 56, 266},
X      StaticText {enabled, "Skipping archive file (it is within the directory being archived)."}
X  }
X};
X
Xresource  'DITL' (197)
X{
X  {
X    {63, 130, 83, 190},
X      Button {enabled, "OK"};
X    {9, 81, 56, 266},
X      StaticText {enabled, "Overrun of stack in window print (can't continue)!"}
X  }
X};
X
Xresource  'DITL' (131, purgeable)
X{
X  {
X    {78, 43, 98, 103},
X      Button {enabled, "OK"};
X    {79, 182, 99, 242},
X      Button {enabled, "Cancel"};
X    {13, 114, 33, 174},
X      EditText {enabled, ""};
X    {45, 114, 65, 174},
X      EditText {enabled, ""};
X    {13, 38, 33, 98},
X      StaticText {enabled, "Creator:"};
X    {44, 58, 65, 100},
X      StaticText {enabled, "Type:"}
X  }
X};
X
Xresource  'DITL' (128, purgeable)
X{
X  {
X    {130, 74, 150, 134},
X      Button {enabled, "OK"};
X    {7, 7, 114, 254},
X      Picture {enabled, 128}
X  }
X};
X
Xresource  'DLOG' (128, purgeable)
X{
X    {68, 112, 240, 380},
X    1,
X    visible,
X    noGoAway,
X    0x0,
X    128,
X    "About TAR"
X};
X
Xresource  'DLOG' (130, purgeable)
X{
X    {60, 75, 275, 423},
X    1,
X    invisible,
X    noGoAway,
X    0x0,
X    130,
X    ""
X};
X
Xresource  'DLOG' (129, purgeable)
X{
X    {44, 75, 144, 375},
X    1,
X    visible,
X    noGoAway,
X    0x0,
X    129,
X    "Block Size"
X};
X
Xresource  'DLOG' (131, purgeable)
X{
X    {44, 75, 144, 375},
X    1,
X    visible,
X    goAway,
X    0x0,
X    131,
X    "New Dialog"
X};
X
Xresource  'MENU' (128, preLoad)
X{
X    128,
X    0,
X    0xfffffffb,
X    enabled,
X    "\024",
X    {
X    "About Tar...", noIcon, noKey, noMark, plain;
X    "-", noIcon, noKey, noMark, plain
X    }
X};
X
Xresource  'MENU' (129)
X{
X    129,
X    0,
X    0xffffff6f,
X    enabled,
X    "File",
X    {
X    "Create...", noIcon, noKey, noMark, plain;
X    "Extract...", noIcon, noKey, noMark, plain;
X    "List...", noIcon, noKey, noMark, plain;
X    "-", noIcon, noKey, noMark, plain;
X    "Close", noIcon, noKey, noMark, plain;
X    "Page Setup...", noIcon, noKey, noMark, plain;
X    "-", noIcon, noKey, noMark, plain;
X    "Quit", noIcon, noKey, noMark, plain
X    }
X};
X
Xresource  'MENU' (130, preLoad)
X{
X    130,
X    0,
X    0xfffffffb,
X    enabled,
X    "Edit",
X    {
X    "Undo", noIcon, 'Z', noMark, plain;
X    "-", noIcon, noKey, noMark, plain;
X    "Cut", noIcon, 'X', noMark, plain;
X    "Copy", noIcon, 'C', noMark, plain;
X    "Paste", noIcon, 'V', noMark, plain;
X    "Clear", noIcon, noKey, noMark, plain
X    }
X};
X
Xresource  'MENU' (131)
X{
X    131,
X    0,
X    0xffffff6f,
X    enabled,
X    "Options",
X    {
X    "Convert Newlines", noIcon, noKey, noMark, plain;
X    "Old Tar Compatible", noIcon, noKey, noMark, plain;
X    "Block Size...", noIcon, noKey, noMark, plain;
X    "-", noIcon, noKey, noMark, plain;
X    "Page Screen Listings", noIcon, noKey, noMark, plain;
X    "List to Printer", noIcon, noKey, noMark, plain;
X    "-", noIcon, noKey, noMark, plain;
X    "Set Creator/Type...", noIcon, noKey, noMark, plain
X    }
X};
X
SHAR_EOF
if test 10649 -ne "`wc -c < 'tar.resource'`"
then
	echo shar: error transmitting "'tar.resource'" '(should have been 10649 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'buffer.c'" '(6263 characters)'
if test -f 'buffer.c'
then
	echo shar: will not over-write existing file "'buffer.c'"
else
sed 's/^X//' << \SHAR_EOF > 'buffer.c'
X/*
X * Macintosh Tar
X *
X * Modified by Craig Ruff for use on the Macintosh.
X */
X/*
X * Buffer management for public domain tar.
X *
X * Written by John Gilmore, ihnp4!hoptoad!gnu, on 25 August 1985.
X *
X * @(#) buffer.c 1.14 10/28/86 Public Domain - gnu
X *
X */
X
X#include "tar.h"
X
Xunion record	*arBlock;	/* Start of block of archive */
Xunion record	*arRecord;	/* Current record of archive */
Xunion record	*arLast;	/* Last+1 record of archive block */
Xchar		arReading;	/* 0 writing, !0 reading archive */
X
X/*
X * The record pointed to by save_rec should not be overlaid
X * when reading in a new tape block.  Copy it to record_save_area first, and
X * change the pointer in *save_rec to point to record_save_area.
X * Saved_recno records the record number at the time of the save.
X * This is used by annofile() to print the record number of a file's
X * header record.
X */
Xstatic union record **saveRec;
Xstatic union record recordSaveArea;
Xstatic int	    savedRecno;
X
X/*
X * Record number of the start of this block of records
X */
Xstatic int	baseRec;
X
X/*
X * Return the location of the next available input or output record.
X */
Xunion record *
XFindRec()
X{
X	if (arRecord == arLast) {
X		FlushArchive();
X		if (arRecord == arLast)
X			return((union record *) NIL);	/* EOF */
X	}
X
X	return(arRecord);
X}
X
X/*
X * Indicate that we have used all records up thru the argument.
X * (should the arg have an off-by-1? XXX FIXME)
X */
Xvoid
XUseRec(rec)
Xunion record *rec;
X{
X	while (rec >= arRecord)
X		arRecord++;
X	/*
X	 * Do NOT flush the archive here.  If we do, the same
X	 * argument to userec() could mean the next record (if the
X	 * input block is exactly one record long), which is not what
X	 * is intended.
X	 */
X	if (arRecord > arLast) {
X		PgmAlert("\PUseRec", "\ParRecord > arLast", NIL, NIL);
X		longjmp(errJmp, 1);
X	}
X}
X
X/*
X * Return a pointer to the end of the current records buffer.
X * All the space between findrec() and endofrecs() is available
X * for filling with data, or taking data from.
X */
Xunion record *
XEndOfRecs()
X{
X	return(arLast);
X}
X
X/*
X * Open an archive file.  The argument specifies whether we are
X * reading or writing.
X */
XBoolean
XOpenArchive(read)
Xint	read;
X{
X	OSErr	err;
X	char	*routine = "\POpenArchive";
X
X	/*
X	 * Get a block buffer for use later
X	 */
X	arBlock = (union record *) NewPtr((Size) blockSize);
X	if (arBlock == NIL) {
X		OSAlert(routine, "\RNewPtr", NIL, MemError());
X		return(TRUE);
X	}
X
X	arRecord = arBlock;
X	arLast   = arBlock + blocking;
X
X	/*
X	 * Try and open the archive file.
X	 */
X	if (read) {
X		err = FSOpen(arName, arVRefNum, &archive);
X
X	} else {
X		if ((err = Create(arName, arVRefNum, (OSType)'TAR ', (OSType)'TARF'))
X				== noErr) {
X			err = FSOpen(arName, arVRefNum, &archive);
X		}
X	}
X
X	if (err != noErr) {
X		OSAlert(routine, "\PFSOpen or FSCreate", arName, err);
X		DisposPtr(arBlock);
X		archive = 0;
X		return(TRUE);
X	}
X
X	arReading = read;
X	if (read) {
X		arLast = arBlock;		/* Set up for 1st block = # 0 */
X		FlushArchive();
X	}
X
X	return(FALSE);
X}
X
X/*
X * Remember a union record * as pointing to something that we
X * need to keep when reading onward in the file.  Only one such
X * thing can be remembered at once, and it only works when reading
X * an archive.
X */
XSaveRec(pointer)
Xunion record	**pointer;
X{
X	saveRec = pointer;
X	savedRecno = baseRec + arRecord - arBlock;
X}
X
X/*
X * Perform a write to flush the buffer.
X */
XFlWrite()
X{
X	OSErr	err;
X	long	count;
X
X	count = blockSize;
X	err = FSWrite(archive, &count, arBlock->charptr);
X	if ((err == noErr) && (count == blockSize))
X		return;
X
X	/* FIXME, multi volume support on write goes here */
X	OSAlert("\PFLWrite", "\PFSRead", NIL, err);
X	longjmp(errJmp, 1);
X}
X
X/*
X * Perform a read to flush the buffer.
X */
XFlRead()
X{
X	OSErr	err;		/* Result from system call */
X	long	count;
X	int	left;		/* Bytes left */
X	char	*more;		/* Pointer to next byte to read */
X	char	*routine = "\PFlRead";
X
X	/*
X	 * If we are about to wipe out a record that
X	 * somebody needs to keep, copy it out to a holding
X	 * area and adjust somebody's pointer to it.
X	 */
X	if (saveRec &&
X	    *saveRec >= arRecord &&
X	    *saveRec < arLast) {
X		recordSaveArea = **saveRec;
X		*saveRec = &recordSaveArea;
X	}
X
X	count = blockSize;
X	err = FSRead(archive, &count, arBlock->charptr);
X	if ((err == noErr) && (count == blockSize))
X		return;
X
X	else if ((err != noErr) && (err != eofErr)) {
X		OSAlert("\PReadError", "\PFSRead", NIL, err);
X		longjmp(errJmp, 1);
X	}
X
X	more = arBlock->charptr + count;
X	left = blockSize - count;
X
Xagain:
X	if (0 == (((unsigned)left) % RECORDSIZE)) {
X		/* FIXME, for size=0, multi vol support */
X		/* On the first block, warn about the problem */
X		if (!reblock && baseRec == 0) {
X			char	buf[80];
X
X			sprintf(&buf[1], "Blocksize = %ld records",
X					count / (long) RECORDSIZE);
X			buf[0] = strlen(&buf[1]);
X			PgmAlert(routine, buf, NIL, NIL);
X		}
X
X		arLast = arBlock + ((unsigned)(blockSize - left))/RECORDSIZE;
X		return;
X	}
X
X	if (reblock) {
X		/*
X		 * User warned us about this.  Fix up.
X		 */
X		if (left > 0) {
X			count = left;
X			err = FSRead(archive, &count, more);
X			if ((err != noErr) && (err != eofErr)) {
X				OSAlert("\PReadError", "\PFSRead", NIL, err);
X				longjmp(errJmp, 1);
X			}
X			if ((count == 0) || (err = eofErr)) {
X				PgmAlert(routine, "\PEof not on block boundary",
X						NIL, NIL);
X				longjmp(errJmp, 1);
X			}
X			left -= count;
X			more += count;
X			goto again;
X		}
X	} else {
X		PgmAlert(routine, "\PDid not read blocksize bytes", NIL, NIL);
X		longjmp(errJmp, 1);
X	}
X}
X
X/*
X * Flush the current buffer to/from the archive.
X */
XFlushArchive()
X{
X	baseRec += arLast - arBlock;/* Keep track of block #s */
X	arRecord = arBlock;		/* Restore pointer to start */
X	arLast = arBlock + blocking;	/* Restore pointer to end */
X
X	if (!arReading) 
X		FlWrite();
X	else
X		FlRead();
X}
X
X/*
X * Close the archive file.
X */
XCloseArchive()
X{
X	if (!arReading)
X		FlushArchive();
X
X	DisposPtr(arBlock);
X	if (archive != 0)
X		(void) FSClose(archive);
X
X	archive = 0;
X}
X
X/*
X * bcopy and bzero compatability routines.
X * These could (should) potentially be done with the Mac traps.
X */
Xchar *
Xbcopy(s1, s2, n)
Xchar *s1, *s2;
Xregister int n;
X{
X	register char *s = s1;
X	register char *d = s2;
X
X	while (--n >= 0)
X		*d++ = *s++;
X
X	return(s1);
X}
X
Xvoid
Xbzero (s1, n)
Xregister char *s1;
Xregister int n;
X{
X	while (--n >= 0)
X		*s1++ = 0;
X}
SHAR_EOF
if test 6263 -ne "`wc -c < 'buffer.c'`"
then
	echo shar: error transmitting "'buffer.c'" '(should have been 6263 characters)'
fi
fi # end of overwriting check
#	End of shell archive
exit 0
--- end of part 1 ---

cruff@ncar.ucar.edu (Craig Ruff) (05/13/88)

[Tar 1.1 Source - part 2 of 3]

---
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	create.c
#	dialog.c
#	dir.c
# This archive created: Wed May 11 07:24:05 1988
# By:	Roger L. Long (bytebug@dhw68k.cts.com)
export PATH; PATH=/bin:$PATH
echo shar: extracting "'create.c'" '(10693 characters)'
if test -f 'create.c'
then
	echo shar: will not over-write existing file "'create.c'"
else
sed 's/^X//' << \SHAR_EOF > 'create.c'
X/*
X * Macintosh Tar
X *
X * Modifed by Craig Ruff for use on the Macintosh.
X */
X/*
X * Create a tar archive.
X *
X * Written 25 Aug 1985 by John Gilmore, ihnp4!hoptoad!gnu.
X *
X * @(#)create.c 1.19 9/9/86 Public Domain - gnu
X */
X
X#include "tar.h"
X
Xunion record		*StartHeader();
Xextern union record	*head;
X
X/*
X * Fake stat struct for use with existing code
X */
Xextern struct stat {
X	long	st_size;
X	long	st_mtime;
X} hstat;
X
Xvoid	FinishHeader();
Xvoid	ToOct();
XBoolean	FillName();
X
X/*
X * Used to save pathname info while descending the directory hierarchy.
X */
Xstruct PathInfo {
X	struct PathInfo	*next;
X	char		name[32];
X};
Xtypedef struct PathInfo PathInfo;
XPathInfo	pathHead;
X
X/*
X * ArCreate - manage the creation of an archive
X *
X *	Asks for the archive name, creates the archive and then
X *	loops asking for directories to add to the archive.
X */
XArCreate() {
X	Point		where;
X	SFReply		reply;
X	CInfoPBRec	pb;
X	CursHandle	cursor;
X
X	/*
X	 * Put up a standard file dialog asking for the archive file name.
X	 */
X	where.h = where.v = 75;
X	SFPutFile(pass(where), "\PName of TAR file", NIL, NIL, &reply);
X	if (!reply.good)
X		return;
X
X	arVRefNum = reply.vRefNum;
X	arName = reply.fName;
X	if (OpenArchive(0))	/* Open for writing */
X		return;
X
X	if (setjmp(errJmp))
X		goto done;
X
X	/*
X	 * Ask for directories to add to the archive.
X	 * Note that this is WHOLE directories.
X	 */
X	while (GetDir("\Pto archive")) {
X		/*
X		 * Get the catalog info for the selected directory.
X		 */
X		pathHead.next = NIL;
X		pb.ioCompletion = NIL;
X		pb.ioNamePtr = pathHead.name;
X		pb.ioVRefNum = dirVRefNum;
X		pb.u.hfi.ioDirID = dirDirID;
X		pb.ioFDirIndex = -1;
X		if (PBGetCatInfo(&pb, FALSE) != noErr) {
X			OSAlert("\PArCreate", "\PPBGetCatInfo", pathHead.name,
X					pb.ioResult);
X			break;
X
X		} else {
X			/*
X			 * Add the directory to the archive,
X			 * while printing the files being added.
X			 */
X			if (WindInit())
X				goto done;
X
X			TextFace(underlineStyle);
X			WPrintf(header);
X			TextFace(0);
X			if ((cursor = GetCursor(watchCursor)) != NIL)
X				SetCursor(*cursor);
X
X			DumpDir(&pb, &pathHead);
X			SetCursor(&arrow);
X			WindEnd(autoPage);
X			FlushEvents(everyEvent, 0);
X		}
X
X	}
X
X	WriteEot();
Xdone:
X	CloseArchive();
X}
X
X/*
X * DumpDir - add a directory (possibly recursively) to the archive
X *
X *	Exits via a longjmp on unrecoverable error
X *	Returns normally otherwise
X */
XDumpDir(dir, path)
XCInfoPBRec	*dir;
XPathInfo	*path;
X{
X	union record	*header;
X	int		i;
X	CInfoPBRec	pb;
X	WDPBRec		wdpb;
X	PathInfo	file;
X	char		*routine = "\PDumpDir";
X	EventRecord	e;
X
X	/*
X	 * Output directory header record with permissions
X	 * FIXME, do this AFTER files, to avoid R/O dir problems?
X	 * If Unix Std format, don't put / on end of dir name
X	 * If old archive format, don't write record at all.
X	 */
X	if (!oldArch) {
X		/*
X		 * If people could really read standard archives,
X		 * this should be:		(FIXME)
X		 * header = start_header(f_standard? p: namebuf, statbuf);
X		 * but since they'd interpret LF_DIR records as
X		 * regular files, we'd better put the / on the name.
X		 */
X		header = StartHeader(dir);
X		if (header == NIL) {
X			longjmp(errJmp, 1);
X		}
X
X		if (standard) {
X			header->header.linkflag = LF_DIR;
X		}
X
X		FinishHeader(header);	/* Done with directory header */
X		head = header;
X		PrintHeader();
X	}
X
X	/*
X	 * Open this directory as a working directory so we can
X	 * access files without the whole pathname (which might be too long).
X	 */
X	file.next = NIL;
X	path->next = &file;
X	wdpb.ioCompletion = NIL;
X	wdpb.ioNamePtr = NIL;
X	wdpb.ioVRefNum = dir->ioVRefNum;
X	wdpb.ioWDProcID = 'TAR ';
X	wdpb.ioWDDirID = dir->u.di.ioDrDirID;
X	if (PBOpenWD(&wdpb, FALSE) != noErr) {
X		OSAlert(routine, "\PPBOpenWD", NIL, wdpb.ioResult);
X		longjmp(errJmp, 1);
X	}
X
X	/*
X	 * Check all entries in the directory.
X	 * Add regular files, recurse on subdirectories.
X	 */
X	pb.ioCompletion = NIL;
X	pb.ioNamePtr = file.name;
X	pb.ioVRefNum = wdpb.ioVRefNum;
X	for (i = 1; ; i++) {
X		SystemTask();
X		if (EventAvail(keyDownMask, &e) && (e.what != nullEvent)) {
X			if (
X				(e.modifiers & cmdKey) &&
X				((e.message & charCodeMask) == '.')
X			) {
X				GetNextEvent(keyDownMask, &e);
X				break;
X			}
X		}
X
X		pb.u.hfi.ioDirID = 0;
X		pb.ioFDirIndex = i;
X		if (PBGetCatInfo(&pb, FALSE) != noErr) {
X			if (pb.ioResult == fnfErr)
X				break;
X
X			OSAlert(routine, "\PPBGetCatInfo", NIL, pb.ioResult);
X			longjmp(errJmp, 1);
X		}
X
X		if ((unsigned char) file.name[0] > 32) {
X			/*
X			 * Sanity check, we have overwritten our stack!
X			 */
X			PgmAlert(routine, "\PName too long", file.name, NIL);
X			longjmp(errJmp, 1);
X		}
X
X		if (DIRECTORY(pb)) {
X			DumpDir(&pb, &file);
X
X		} else {
X			if (pb.ioFRefNum == archive) {
X				/*
X				 * DO NOT add the archive to itself!
X				 */
X				ArSkipAlert();
X				continue;
X			}
X
X			DumpFile(&pb, &file);
X		}
X	}
X
X	/*
X	 * Done with this directory, make sure we don't run out
X	 * of working directories.
X	 */
X	PBCloseWD(&wdpb, FALSE);
X	path->next = NIL;
X}
X
X/*
X * DumpFile - Dump a single file.
X *
X *	Exits via longjmp on unrecoverable error.
X *	Result is 1 for success, 0 for failure.
X */
XDumpFile(file, path)
XCInfoPBRec	*file;
XPathInfo	*path;
X{
X	union record	*header;
X	register char	*p;
X	char		*buf;
X	short		f;		/* File descriptor */
X	HPrmBlkRec	fpb;
X	long		bufsize, count, i;
X	register long	sizeleft;
X	register union record 	*start;
X	OSErr		err;
X	char		*routine = "\PDumpFile";
X
X	if ((header = StartHeader(file)) == NIL)
X		longjmp(errJmp, 1);
X
X	FinishHeader(header);
X	/*
X	 * Get the size of the file.
X	 * Don't bother opening it if it is zero length.
X	 */
X	head = header;
X	hstat.st_size = file->u.hfi.ioFlLgLen;
X	PrintHeader();
X	if ((sizeleft = file->u.hfi.ioFlLgLen) == 0)
X		return;
X
X	fpb.ioCompletion = NIL;
X	fpb.ioNamePtr = file->ioNamePtr;
X	fpb.ioVRefNum = file->ioVRefNum;
X	fpb.u.hfp.ioDirID = 0;
X	fpb.u.iop.ioPermssn = fsRdPerm;
X	fpb.u.iop.ioMisc = NIL;
X	if (PBHOpen(&fpb, FALSE) != noErr) {
X		OSAlert(routine, "\PPBHOpen", file->ioNamePtr, fpb.ioResult);
X		longjmp(errJmp, 1);
X	}
X
X	/*
X	 * Dump the file to the archive.
X	 * Note: this only dumps the data fork!
X	 */
X	while (sizeleft > 0) {
X		start = FindRec();
X		bufsize = EndOfRecs()->charptr - start->charptr;
X		buf = start->charptr;
X	again:
X		count = (sizeleft < bufsize) ? sizeleft : bufsize;
X		fpb.u.iop.ioBuffer = buf;
X		fpb.u.iop.ioReqCount = count;
X		fpb.u.iop.ioPosMode = fsAtMark;
X		fpb.u.iop.ioPosOffset = 0;
X		if (PBRead(&fpb, FALSE) != noErr) {
X			OSAlert(routine, "\PPBRead", file->ioNamePtr, fpb.ioResult);
X			longjmp(errJmp, 1);
X		}
X
X		count = fpb.u.iop.ioActCount;
X		if (cvtNl) {
X			/*
X			 * Convert returns to newlines for Unix compat.
X			 */
X			for (i = count, p = buf; --i >= 0; p++)
X				if (*p == '\r')
X					*p = '\n';
X		}
X
X		sizeleft -= count;
X		UseRec(start + (count - 1) / RECORDSIZE);
X	}
X	PBClose(&fpb, FALSE);
X
X	/* Clear last block garbage to zeros, FIXME */
X}
X
X
X/*
X * Make a header block for the file  name  whose stat info is  st .
X * Return header pointer for success, NULL if the name is too long.
X */
Xunion record *
XStartHeader(pb)
XCInfoPBRec	*pb;
X{
X	register union record *header;
X	Boolean	directory = DIRECTORY(*pb);
X
X	header = (union record *) FindRec();
X	bzero(header->charptr, sizeof(union record)); /* XXX speed up */
X	/*
X	 * Generate the pathname, make sure we don't overflow
X	 * the field in the tar header.
X	 */
X	if (FillName(header, directory)) {
X		char	buf[NAMSIZ + 1];
X
X		buf[0] = NAMSIZ;
X		bcopy(header->header.name, &buf[1], NAMSIZ);
X		PgmAlert("\PStartHeader", "\PName too long", buf, NIL);
X		return(NIL);
X	}
X
X	/*
X	 * Fake the file mode, uid, gid.
X	 * Convert from Mac based time to Unix based time.
X	 */
X	ToOct((directory) ? 0755L : 0644L, 8,  header->header.mode);
X	ToOct(0L, 8,  header->header.uid);
X	ToOct(0L, 8,  header->header.gid);
X	ToOct((directory) ? 0L : pb->u.hfi.ioFlLgLen, 1+12,
X			header->header.size);
X	ToOct((directory) ? pb->u.di.ioDrMdDat - TIMEDIFF :
X			    pb->u.hfi.ioFlMdDat - TIMEDIFF,
X			1+12, header->header.mtime);
X	/* header->header.linkflag is left as null */
X	return(header);
X}
X
X/* 
X * Finish off a filled-in header block and write it out.
X */
Xvoid
XFinishHeader(header)
Xregister union record *header;
X{
X	register int	i;
X	register long	sum;
X	register char	*p;
X
X	bcopy(CHKBLANKS, header->header.chksum, sizeof(header->header.chksum));
X
X	sum = 0;
X	p = header->charptr;
X	for (i = sizeof(union record); --i >= 0; ) {
X		/*
X		 * We can't use unsigned char here because of old compilers,
X		 * e.g. V7.
X		 */
X		sum += 0xFF & *p++;
X	}
X
X	/*
X	 * Fill in the checksum field.  It's formatted differently
X	 * from the other fields:  it has [6] digits, a null, then a
X	 * space -- rather than digits, a space, then a null.
X	 * We use to_oct then write the null in over to_oct's space.
X	 * The final space is already there, from checksumming, and
X	 * to_oct doesn't modify it.
X	 *
X	 * This is a fast way to do:
X	 * (void) sprintf(header->header.chksum, "%6o", sum);
X	 */
X	ToOct((long) sum, 8, header->header.chksum);
X	header->header.chksum[6] = '\0';	/* Zap the space */
X	UseRec(header);
X	return;
X}
X
X
X/*
X * Quick and dirty octal conversion.
X * Converts long "value" into a "digs"-digit field at "where",
X * including a trailing space and room for a null.  "digs"==3 means
X * 1 digit, a space, and room for a null.
X *
X * We assume the trailing null is already there and don't fill it in.
X * This fact is used by start_header and finish_header, so don't change it!
X *
X * This should be equivalent to:
X *	(void) sprintf(where, "%*lo ", digs-2, value);
X * except that sprintf fills in the trailing null and we don't.
X */
Xvoid
XToOct(value, digs, where)
Xregister long	value;
Xregister int	digs;
Xregister char	*where;
X{
X	--digs;				/* Trailing null slot is left alone */
X	where[--digs] = ' ';		/* Put in the space, though */
X
X	/* Produce the digits -- at least one */
X	do {
X		where[--digs] = '0' + (value & 7);	/* one octal digit */
X		value >>= 3;
X	} while (digs > 0 && value != 0);
X
X	/* Leading spaces, if necessary */
X	while (digs > 0)
X		where[--digs] = ' ';
X
X}
X
X/*
X * Write the EOT block(s).
X */
XWriteEot()
X{
X	union record *p;
X
X	p = FindRec();
X	bzero(p->charptr, RECORDSIZE);
X	UseRec(p);
X	/* FIXME, only one EOT block should be needed. */
X	p = FindRec();
X	bzero(p->charptr, RECORDSIZE);
X	UseRec(p);
X}
X
X/*
X * FillName - generate the file or directory pathname
X *
X *	Converts to Unix style pathnames.
X *	Appends a '/' for a directory.
X */
XBoolean
XFillName(header, directory)
Xregister union record *header;
XBoolean	directory;
X{
X	register PathInfo *p;
X	char *q;
X
X	q = header->header.name;
X	for (p = &pathHead; p != NIL; p = p->next) {
X		sprintf(q, "%.*s%c", p->name[0], &p->name[1],
X				 (p->next == NIL) ? '\0' : '/');
X
X		q += p->name[0] + 1;
X	}
X
X	if (directory) {
X		*(q - 1) = '/';
X		*q = '\0';
X	}
X
X	return((q - header->header.name) > NAMSIZ);
X}
SHAR_EOF
if test 10693 -ne "`wc -c < 'create.c'`"
then
	echo shar: error transmitting "'create.c'" '(should have been 10693 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'dialog.c'" '(7050 characters)'
if test -f 'dialog.c'
then
	echo shar: will not over-write existing file "'dialog.c'"
else
sed 's/^X//' << \SHAR_EOF > 'dialog.c'
X/*
X * Macintosh Tar
X *
X * The routines in this file deal with the dialogs presented to the user.
X *
X * Written by Craig Ruff.
X *
X * Note that the dialog dealing with directory selection is in dir.c.
X */
X
X#include "tar.h"
X#include <resource.h>
X#include <segment.h>
X
X/*
X * Dialog definitions
X */
X#define aboutID		128		/* The "About ..." dialog */
X
X#define blockID		129		/* The block size dialog */
X#define sizeItem	3
X
X#define typeID		131		/* Creator/Type dialog */
X#define creatorItem	3
X#define typeItem	4
X
X/*
X * Alert Definitions
X */
X#define badBlockID	129		/* Block size incorrect alert */
X#define osErrID		130		/* Generic OS Error alert */
X#define pgmErrID	131		/* Program logic error alert */
X#define hfsID		132		/* Must run under HFS alert */
X#define askipID		133		/* Skipping archive file note */
X#define stkErrID	134		/* Stack error alert */
X
X/*
X * AboutFilter - manage the "About ..." dialog
X *
X *	Returns when the mouse or a key is pressed anywhere.
X */
Xpascal Boolean
XAboutFilter(theDialog, theEvent, itemHit)
XDialogPtr	theDialog;
XEventRecord	*theEvent;
Xshort		*itemHit;
X{
X	switch (theEvent->what) {
X	case keyDown:
X	case mouseDown:
X		*itemHit = 1;
X		return(TRUE);
X		break;
X
X	default:
X		return(FALSE);
X		break;
X	}
X}
X
X/*
X * DoAboutBox - put the "About ..." dialog on the screen
X */
XDoAboutBox() {
X	int		itemHit;
X	WindowPtr	myWindow;
X
X	myWindow = GetNewDialog(aboutID, NIL, (WindowPtr) -1);
X	do {
X		ModalDialog(AboutFilter, &itemHit);
X	} while (itemHit != 1);
X	DisposDialog(myWindow);
X}
X
X/*
X * ValidBlockSize - checks the user entered blocksize for validity.
X *
X *	Returns TRUE if ok, FALSE if bad.
X */
XBoolean
XValidBlockSize(theDialog)
XDialogPtr	theDialog;
X{
X	long	n;
X	short	type;
X	Handle	hdl;
X	Rect	box;
X	char	text[256];
X
X	GetDItem(theDialog, sizeItem, &type, &hdl, &box);
X	GetIText(hdl, text);
X	StringToNum(text, &n);
X
X	/*
X	 * These limits are somewhat arbitrary.
X	 */
X	if ((n < 1) || (n > 128)) {
X		NoteAlert(badBlockID, NIL);
X		SelIText(theDialog, sizeItem, 0, 32767);
X		return(FALSE);
X	}
X
X	blocking = (int) n;
X	blockSize = blocking * RECORDSIZE;
X	return(TRUE);
X}
X
XRect		okBox;		/* ok Box location (gotten once for speed) */
XControlHandle	okHdl;		/* ok Box handle */
X
X/*
X * BlockFilter - manage user events during the block size dialog
X *
X *	Allows numeric entry and editing, validates size.
X *	Returns TRUE if the dialog should exit, FALSE to continue.
X */
Xpascal Boolean
XBlockFilter(theDialog, theEvent, itemHit)
XDialogPtr	theDialog;
XEventRecord	*theEvent;
Xshort		*itemHit;
X{
X	long	t;
X	Point	lpt;
X	GrafPtr	savedPort;
X
X	switch (theEvent->what) {
X	case keyDown:
X		switch ((char) (theEvent->message & charCodeMask)) {
X		case RETURN:
X		case ENTER:
X			goto validate;
X			break;
X
X		case '0': case '1': case '2': case '3': case '4':
X		case '5': case '6': case '7': case '8': case '9':
X		case TAB: case  BS:
X			break;
X
X		default:
X			SysBeep(5);
X			theEvent->what = nullEvent;
X			break;
X		}
X		break;
X
X	case mouseDown:
X		pass(lpt) = pass(theEvent->where);
X		GetPort(&savedPort);
X		SetPort((GrafPtr) theDialog);
X		GlobalToLocal(&lpt);
X		SetPort(savedPort);
X		if (PtInRect(pass(lpt), &okBox)) {
Xvalidate:		if (ValidBlockSize(theDialog)) {
X				*itemHit = OK;
X				HiliteControl(okHdl, 1);
X				Delay(8L, &t);
X				HiliteControl(okHdl, 0);
X				return(TRUE);
X			} else
X				theEvent->what = nullEvent;
X		}
X		break;
X	}
X
X	return(FALSE);
X}
X
X/*
X * CreatorTypeFilter - manage user events during the Creator/Type dialog
X *
X */
Xpascal Boolean
XCreatorTypeFilter(theDialog, theEvent, itemHit)
XDialogPtr	theDialog;
XEventRecord	*theEvent;
Xshort		*itemHit;
X{
X	short	type;
X	Handle	hdl;
X	Rect	box;
X	char	text[256];
X	int	n;
X	long	t;
X	Point	lpt;
X	GrafPtr	savedPort;
X
X	switch (theEvent->what) {
X	case keyDown:
X		switch ((char) (theEvent->message & charCodeMask)) {
X		case RETURN:
X		case ENTER:
X			goto done;
X			break;
X
X		default:
X			break;
X		}
X		break;
X
X	case mouseDown:
X		pass(lpt) = pass(theEvent->where);
X		GetPort(&savedPort);
X		SetPort((GrafPtr) theDialog);
X		GlobalToLocal(&lpt);
X		SetPort(savedPort);
X		if (PtInRect(pass(lpt), &okBox)) {
Xdone:
X			GetDItem(theDialog, creatorItem, &type, &hdl, &box);
X			GetIText(hdl, text);
X			n = (unsigned char) text[0];
X			if (n > 4)
X				n = 4;
X
X			strncpy(fdCreator, &text[1], n);
X			GetDItem(theDialog, typeItem, &type, &hdl, &box);
X			GetIText(hdl, text);
X			n = (unsigned char) text[0];
X			if (n > 4)
X				n = 4;
X
X			strncpy(fdType, &text[1], n);
X			*itemHit = OK;
X			HiliteControl(okHdl, 1);
X			Delay(8L, &t);
X			HiliteControl(okHdl, 0);
X			return(TRUE);
X		}
X		break;
X	}
X
X	return(FALSE);
X}
X
X/*
X * DoBlockSize - put the block size dialog on the screen
X *
X *	Puts the current block size into the edit field.
X */
XDoBlockSize() {
X	int		itemHit;
X	DialogPtr	theDialog;
X	Handle		hdl;
X	short		type;
X	Rect		box;
X	char		text[256];
X
X	theDialog = GetNewDialog(blockID, NIL, (WindowPtr) -1);
X	GetDItem(theDialog, sizeItem, &type, &hdl, &box);
X	NumToString((long) blocking, text);
X	SetIText(hdl, text);
X	SelIText(theDialog, sizeItem, 0, 32767);
X	GetDItem(theDialog, OK, &type, &okHdl, &okBox);
X	do {
X		ModalDialog(BlockFilter, &itemHit);
X	} while ((itemHit != OK) && (itemHit != Cancel));
X	DisposDialog(theDialog);
X}
X
X/*
X * DoCreatorType - put the set Creator/Type dialog on the screen
X *
X *	Puts the current Creator and Type into the edit field.
X */
XDoCreatorType() {
X	int		itemHit;
X	DialogPtr	theDialog;
X	Handle		hdl;
X	short		type;
X	Rect		box;
X	char		text[256];
X
X	theDialog = GetNewDialog(typeID, NIL, (WindowPtr) -1);
X	GetDItem(theDialog, creatorItem, &type, &hdl, &box);
X	text[0] = 4;
X	strncpy(&text[1], fdCreator, 4);
X	SetIText(hdl, text);
X	GetDItem(theDialog, typeItem, &type, &hdl, &box);
X	text[0] = 4;
X	strncpy(&text[1], fdType, 4);
X	SetIText(hdl, text);
X	SelIText(theDialog, creatorItem, 0, 32767);
X	GetDItem(theDialog, OK, &type, &okHdl, &okBox);
X	do {
X		ModalDialog(CreatorTypeFilter, &itemHit);
X	} while ((itemHit != OK) && (itemHit != Cancel));
X
X	DisposDialog(theDialog);
X}
X
X/*
X * OSAlert - put a generic OS Error Alert on the screen
X *
X *	Used for errors not handled in another way (yet).
X */
XOSAlert(p0, p1, p2, err)
Xchar	*p0, *p1, *p2;
XOSErr	err;
X{
X	char	buf[256];
X
X	/*
X	 * Manage the contingency of being called for resources missing!
X	 */
X	if (GetResource('ALRT', osErrID) == NIL) {
X		GrafPtr	wmPort;
X		long	t;
X		Rect	r;
X
X		GetWMgrPort(&wmPort);
X		SetPort(wmPort);
X		SetRect(&r, 140, 150, 360, 180);
X		EraseRect(&r);
X		MoveTo(150,170);
X		DrawString("\PPANIC! Resources Missing!");
X		Delay(600L, &t);
X		ExitToShell();
X	}
X
X	NumToString((long) err, buf);
X	ParamText(p0, p1, p2, buf);
X	StopAlert(osErrID, NIL);
X}
X
X/*
X * PgmAlert - put a program logic alert on the screen
X */
XPgmAlert(p0, p1, p2, p3)
Xchar	*p0, *p1, *p2, *p3;
X{
X	ParamText(p0, p1, p2, p3);
X	StopAlert(pgmErrID, NIL);
X}
X
X/*
X * HFSAlert - put a "HFS only" alert on the screen
X */
XHFSAlert() {
X	StopAlert(hfsID, NIL);
X}
X
X/*
X * ArSkipAlert - put a "skipping archive file" note on the screen
X */
XArSkipAlert() {
X	NoteAlert(askipID, NIL);
X}
X
X/*
X * StkErrAlert - put a stack corrupted alert on the screen
X */
XStkErrAlert() {
X	StopAlert(stkErrID, NIL);
X	ExitToShell();
X}
SHAR_EOF
if test 7050 -ne "`wc -c < 'dialog.c'`"
then
	echo shar: error transmitting "'dialog.c'" '(should have been 7050 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'dir.c'" '(11757 characters)'
if test -f 'dir.c'
then
	echo shar: will not over-write existing file "'dir.c'"
else
sed 's/^X//' << \SHAR_EOF > 'dir.c'
X/*
X * Macintosh Tar
X *
X * dir.c - routines dealing with directory selection
X *
X * Written by Craig Ruff
X */
X
X#include "tar.h"
X
X#define ROOTDIR		2		/* WATCH OUT!  Subject to change? */
X
X#define dirID		130		/* Directory selection dialog */
X#define selectItem	1		/* Use this directory */
X#define vnameItem	3		/* Volume name */
X#define boxItem		6		/* Directory list box */
X#define lineItem	7		/* User line item */
X#define upItem		8		/* Go to parent directory */
X#define	openItem	9		/* Open a subdirectory */
X#define driveItem	11		/* Select drive item */
X#define ejectItem	12		/* Eject disk item */
X
XListHandle	dirList;		/* Current directory list */
XRect		dirListRect;		/* Where is the list displayed? */
Xshort		dirVRefNum;		/* Selected directory VRefNum */
Xlong		dirDirID;		/* Selected directory DirID */
XRect		selectRect;		/* Rectangle of selectItem */
Xshort		volIndex;		/* Current volume index (for driveItem) */
XBoolean		ejectable;		/* Is the current volume ejectable? */
XPoint		lineLeft;
XPoint		lineRight;
X
X/*
X * DirFilter - handle events for the select directory dialog
X *
X *	Returns TRUE if button pressed, FALSE if to continue
X */
Xpascal Boolean
XDirFilter(theDialog, theEvent, itemHit)
XDialogPtr	theDialog;
XEventRecord	*theEvent;
Xshort		*itemHit;
X{
X	long	t;
X	Point	lpt;
X	GrafPtr	savedPort;
X
X	switch (theEvent->what) {
X	case keyDown:
X		/*
X		 * Default button for return or enter is Select
X		 */
X		if (((t = theEvent->message & charCodeMask) == RETURN) ||
X				(t == ENTER)) {
X			*itemHit = selectItem;
X			return(TRUE);
X		}
X		break;
X
X	case mouseDown:
X		/*
X		 * If we get a click in the directory list, send
X		 * it to the list manager, then pretend we got an
X		 * open button press.
X		 */
X		GetPort(&savedPort);
X		SetPort(theDialog);
X		pass(lpt) = pass(theEvent->where);
X		GlobalToLocal(&lpt);
X		SetPort(savedPort);
X		if (PtInRect(pass(lpt), &dirListRect)) {
X			if (LClick(pass(lpt), theEvent->modifiers, dirList)) {
X				*itemHit = openItem;
X				return(TRUE);
X			}
X			theEvent->what = nullEvent;
X		}
X		break;
X	}
X
X	return(FALSE);
X}
X
X/*
X * DirListProc - handle user defined redrawing of the dialog
X */
Xpascal
XDirListProc(theDialog, itemNo)
XDialogPtr	theDialog;
Xshort		itemNo;
X{
X	Rect		r;
X	PenState	pn;
X
X	/*
X	 * Highlight the default choice for return or enter, "Select".
X	 */
X	GetPenState(&pn);
X	PenSize(3, 3);
X	SetRect(&r, selectRect.left - 4, selectRect.top - 4,
X			selectRect.right + 4, selectRect.bottom + 4);
X	FrameRoundRect(&r, 16, 16);
X
X	/*
X	 * Draw the line separating Drive and Eject from Cancel and Open
X	 */
X	PenSize(1,1);
X	MoveTo(lineLeft.h, lineLeft.v);
X	LineTo(lineRight.h, lineRight.v);
X	SetPenState(&pn);
X
X	/*
X	 * Put a box around the directory list.
X	 * Do not erase the scroll bar on the right.
X	 * Then tell the list manager to update.
X	 */
X	SetRect(&r, dirListRect.left - 1, dirListRect.top - 1,
X			dirListRect.right + 1, dirListRect.bottom + 1);
X	FrameRect(&r);
X	SetRect(&r, dirListRect.left, dirListRect.top,
X			dirListRect.right - 15, dirListRect.bottom);
X	EraseRect(&r);
X	LUpdate(theDialog->visRgn, dirList);
X}
X
X/*
X * FillDirList - get all of the subdirectory names in this directory
X *
X *	Returns TRUE if an error was found, FALSE otherwise.
X */
XBoolean
XFillDirList(dir, parent, dname)
Xlong		dir;
Xlong		*parent;
Xchar		*dname;
X{
X	short		i, index;
X	CInfoPBRec	pb;
X	WDPBRec		wdpb;
X	char		name[64];
X	Point		cell;
X	char		*routine = "\PFillDirList";
X
X	/*
X	 * Open this directory to get the catalog info.
X	 */
X	wdpb.ioCompletion = NIL;
X	wdpb.ioNamePtr = NIL;
X	wdpb.ioVRefNum = dirVRefNum;
X	wdpb.ioWDProcID = 'TAR ';
X	wdpb.ioWDDirID = dir;
X	if (PBOpenWD(&wdpb, FALSE) != noErr) {
X		OSAlert(routine, "\PPBOpenWD", NIL, wdpb.ioResult);
X		return(TRUE);
X	}
X
X	/*
X	 * We will get this directory's name for the select button.
X	 */
X	pb.ioCompletion = NIL;
X	pb.ioNamePtr = dname;
X	pb.ioVRefNum = dirVRefNum;
X	pb.u.hfi.ioDirID = dir;
X	pb.ioFDirIndex = -1;
X	if (PBGetCatInfo(&pb, FALSE) != noErr) {
X		OSAlert(routine, "\PPBGetCatInfo(1)", NIL, pb.ioResult);
X		return(TRUE);
X	}
X
X	/*
X	 * Save the parent's DirID.
X	 * Loop until all directories have been found.
X	 */
X	*parent = pb.u.di.ioDrParID;
X	pb.ioNamePtr = name;
X	pb.ioVRefNum = wdpb.ioVRefNum;
X	for (i = 1, index = 0; ; i++) {
X		pb.u.hfi.ioDirID = 0;
X		pb.ioFDirIndex = i;
X		if (PBGetCatInfo(&pb, FALSE) != noErr) {
X			if (pb.ioResult == fnfErr)
X				break;
X
X			OSAlert(routine, "\PPBGetCatInfo(2)", NIL, pb.ioResult);
X			return(TRUE);
X		}
X
X		if (!DIRECTORY(pb))
X			continue;
X
X		/*
X		 * Add another row to the list if we are beyond the end
X		 * of the current list.
X		 * Remember the DirID for possible later selection.
X		 */
X		if (index >= (**dirList).dataBounds.bottom)
X			(void) LAddRow(1, index, dirList);
X
X		cell.h = 0;
X		cell.v = index++;
X		LSetCell(&name[1], (unsigned char) name[0], pass(cell), dirList);
X		cell.h = 1;
X		LSetCell(&pb.u.di.ioDrDirID, sizeof(long), pass(cell), dirList);
X	}
X
X	/*
X	 * Make sure we're not left in never-never land when
X	 * the directory list is redisplayed.
X	 * Free up any unused list rows.
X	 */
X	LScroll(-9999, -9999, dirList);
X	if (index < (**dirList).dataBounds.bottom)
X		LDelRow((**dirList).dataBounds.bottom - index, index, dirList);
X
X	PBCloseWD(&wdpb, FALSE);
X}
X
X/*
X * IsEjectable - check if a volume is ejectable
X *
X *	Returns TRUE or FALSE.
X *
X *	I think there should be an easier way to do this!  Like an additional
X *	flag in the GetVInfo call or something.
X */
XBoolean
XIsEjectable(volpb)
XHPrmBlkPtr	volpb;
X{
X	int		drvnum = volpb->u.hvp.ioVDrvInfo;
X	QHdrPtr		drvq;
X	struct DrvQEl	*drv;
X
X	/*
X	 * This volume is not in a drive!  Therefore is already ejected!
X	 */
X	if (drvnum == 0)
X		return(FALSE);
X
X	/*
X	 * Search the drive queue for our volume's drive.
X	 * Check the hidden flags.  Talk about hidden!
X	 */
X	drvq = GetDrvQHdr();
X	for (drv = (struct DrvQEl *) drvq->qHead;
X			drv != (struct DrvQEl *) drvq->qTail;
X			drv = drv->qLink)
X		if (drv->dQDrive == drvnum)
X			break;
X
X	if ((drv == (struct DrvQEl *) drvq->qTail) && (drv->dQDrive != drvnum))
X		return(FALSE);
X
X	return ((((long *) drv)[-1] & 0x80000) ? FALSE : TRUE);
X}
X
X#define SysMap	(*(short *) 0xa58)
X
X/*
X * DirInit - set up some stuff for the dialog
X *
X *	Returns TRUE if an error was found, FALSE otherwise.
X */
XDirInit(vName)
Xchar	*vName;
X{
X	char		*routine = "\PDirInit";
X	WDPBRec		pb;
X	HPrmBlkRec	volpb;
X	OSErr		err;
X
X	/*
X	 * Find out where we are currently.
X	 */
X	pb.ioCompletion = NIL;
X	pb.ioNamePtr = NIL;
X	if (PBHGetVol(&pb, FALSE) != noErr) {
X		OSAlert(routine, "\PPBHGetVol", NIL, pb.ioResult);
X		return(TRUE);
X	}
X
X	/*
X	 * Remember that, and figure out which volume index is ours.
X	 */
X	dirVRefNum = pb.ioWDVRefNum;
X	dirDirID = pb.ioWDDirID;
X	for (volIndex = 1; ; volIndex++) {
X		volpb.ioCompletion = NIL;
X		volpb.ioNamePtr = vName;
X		volpb.ioVRefNum = NIL;
X		volpb.u.hvp.ioVolIndex = volIndex;
X		if ((err = PBHGetVInfo(&volpb, FALSE)) != noErr) {
X			OSAlert(routine, "\PPBHGetVInfo", 
X				(err == nsvErr) ?
X					"\PCould not find default volume" :
X					NIL,
X				volpb.ioResult);
X			return(TRUE);
X		}
X
X		if (volpb.ioVRefNum == pb.ioWDVRefNum) {
X			ejectable = IsEjectable(&volpb);
X			break;
X		}
X	}
X
X	return(FALSE);
X}
X
X/*
X * GetNextVol - get the next volume in the volume list (for the drive button)
X *
X *	Returns the volume reference number.
X */
Xshort
XGetNextVol(vName)
Xchar	*vName;
X{
X	HPrmBlkRec	volpb;
X	int		found = FALSE;
X
X	do {
X		volpb.ioCompletion = NIL;
X		volpb.ioNamePtr = vName;
X		volpb.ioVRefNum = NIL;
X		volpb.u.hvp.ioVolIndex = ++volIndex;
X		PBHGetVInfo(&volpb, FALSE);
X		if (volpb.ioResult == nsvErr) {
X			/*
X			 * Reached the end of the list, start again.
X			 */
X			volIndex = 0;
X			continue;
X
X		}
X		
X		if (volpb.ioResult != noErr) {
X			OSAlert("\PGetNextVol", "\PPBHGetVInfo", NIL,
X					volpb.ioResult);
X			return(-32768);
X		}
X
X		if (volpb.u.hvp.ioVSigWord != 0x4244)
X			continue;	/* Ignore non-HFS volumes */
X
X		found = TRUE;
X		ejectable = IsEjectable(&volpb);
X	} while (!found);
X
X	return(volpb.ioVRefNum);
X}
X
X/*
X * GetDir - manage the directory selection dialog
X */
XBoolean
XGetDir(text)
Xchar	*text;
X{
X	short		itemHit;
X	DialogPtr	theDialog;
X	Handle		dhdl, vhdl;
X	ControlHandle	ehdl;
X	short		len, type;
X	Rect		dataBounds, r;
X	Point		cell, size;
X	long		parent;
X	GrafPtr		savedPort;
X	char		dirName[32], volName[32];
X	Boolean		selection;
X	char		*routine = "\PGetDir";
X	ParamBlkRec	pb;
X
X	/*
X	 * Read in the selection dialog.
X	 * Set the function specific message.
X	 */
X	theDialog = GetNewDialog(dirID, NIL, (WindowPtr) -1);
X	ParamText(text, NIL, NIL, NIL);
X
X	/*
X	 * Get the directory list information.
X	 * We have all the user item drawing done when the list is updated too.
X	 * The list boundaries are setup.  Leave room for the scroll bar.
X	 * Only one column of the list is displayed.  The other is used
X	 * to save the DirID.
X	 */
X	GetDItem(theDialog, boxItem, &type, &dhdl, &dirListRect);
X	SetDItem(theDialog, boxItem, type, (Handle) DirListProc, &dirListRect);
X	dataBounds.top = dataBounds.left = 0;
X	dataBounds.bottom = 0;
X	dataBounds.right = 2;
X	dirListRect.right -= 15;
X	size.h = dirListRect.right - dirListRect.left;
X	size.v = 0;
X	dirList = LNew(&dirListRect, &dataBounds, pass(size), 0, theDialog,
X			FALSE, FALSE, FALSE, TRUE);
X	if (dirList == NIL) {
X		PgmAlert(routine, "\PLNew NIL return", NIL, NIL);
X		itemHit = 0;
X		goto done;
X	}
X
X	/*
X	 * Ok, now include the scroll bar when the mouse is pressed (later).
X	 * Only allow a single directory to be selected at a time.
X	 */
X	dirListRect.right += 15;
X	(**dirList).selFlags = lOnlyOne;
X	if (DirInit(volName)) {
X		itemHit = 0;
X		goto done;
X	}
X
X	/*
X	 * Save the control handles for "Eject" and the volume name
X	 * for either enabling/disabling and name setting respectively.
X	 * Save the control handle for the "Select" button so the
X	 * current directory's name can be put into it.
X	 */
X	GetDItem(theDialog, lineItem, &type, &ehdl, &r);
X	lineLeft.h = r.left;
X	lineRight.v = lineLeft.v = r.top;
X	lineRight.h = r.right;
X	GetDItem(theDialog, ejectItem, &type, &ehdl, &r);
X	GetDItem(theDialog, vnameItem, &type, &vhdl, &r);
X	SetIText(vhdl, volName);
X	GetDItem(theDialog, selectItem, &type, &dhdl, &selectRect);
X	ShowWindow(theDialog);
X	cell.v = 0;
X
X	/*
X	 * Wait until either the user selects a directory or cancels.
X	 */
X	do {
X		/*
X		 * Don't update the list on the screen while filling it.
X		 * This looks sloppy.
X		 */
X		LDoDraw(FALSE, dirList);
X		if (FillDirList(dirDirID, &parent, dirName)) {
X			itemHit = 0;
X			goto done;
X		}
X
X		LDoDraw(TRUE, dirList);
X		SetCTitle((ControlHandle) dhdl, dirName);
X		HiliteControl(ehdl, ejectable ? 0 : 255);
X
X		/*
X		 * Make sure the directory list IS redrawn.
X		 */
X		GetPort(&savedPort);
X		SetPort(theDialog);
X		InvalRect(&theDialog->portRect);
X		SetPort(savedPort);
X
X		/*
X		 * No directory is selected initially.
X		 */
X		cell.h = 0;
X		LSetSelect(FALSE, pass(cell), dirList);
X		ModalDialog(DirFilter, &itemHit);
X		cell.v = 0;
X
X		/*
X		 * See if anything was really selected.
X		 */
X		selection =  LGetSelect(TRUE, &cell, dirList);
X		switch (itemHit) {
X		case openItem:
X			if (!selection)
X				break;
X
X			/*
X			 * Ok, use this directory now.
X			 */
X			cell.h = 1;
X			len = sizeof(dirDirID);
X			LGetCell(&dirDirID, &len, pass(cell), dirList);
X			break;
X
X		case upItem:
X			if (dirDirID == ROOTDIR)
X				break;
X
X			dirDirID = parent;
X			break;
X
X		case driveItem:
X		case ejectItem:
X			dirDirID = ROOTDIR;
X			if ((dirVRefNum = GetNextVol(volName)) == 0)
X				return(FALSE);
X
X			SetIText(vhdl, volName);
X			if (itemHit == driveItem)
X				break;
X
X			pb.ioCompletion = NIL;
X			pb.ioNamePtr = NIL;
X			pb.ioVRefNum = dirVRefNum;
X			if (PBEject(&pb, FALSE) != noErr)
X				OSAlert(routine, "\PPBEject", NIL, pb.ioResult);
X
X			break;
X		}
X	} while ((itemHit != selectItem) && (itemHit != Cancel));
X
Xdone:
X	LDispose(dirList);
X	DisposDialog(theDialog);
X	return((itemHit == selectItem) ? TRUE : FALSE);
X}
SHAR_EOF
if test 11757 -ne "`wc -c < 'dir.c'`"
then
	echo shar: error transmitting "'dir.c'" '(should have been 11757 characters)'
fi
fi # end of overwriting check
#	End of shell archive
exit 0
--- end of part 2 ---

cruff@ncar.ucar.edu (Craig Ruff) (05/14/88)

[Tar 1.1 Source - part 3 of 3]

---
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	extract.c
#	list.c
#	menu.c
#	window.c
# This archive created: Wed May 11 07:24:07 1988
# By:	Roger L. Long (bytebug@dhw68k.cts.com)
export PATH; PATH=/bin:$PATH
echo shar: extracting "'extract.c'" '(7771 characters)'
if test -f 'extract.c'
then
	echo shar: will not over-write existing file "'extract.c'"
else
sed 's/^X//' << \SHAR_EOF > 'extract.c'
X/*
X * Macintosh Tar
X *
X * Modified by Craig Ruff for use on the Macintosh.
X */
X/*
X * Extract files from a tar archive.
X *
X * Written 19 Nov 1985 by John Gilmore, ihnp4!hoptoad!gnu.
X *
X * @(#) extract.c 1.17 86/10/29 Public Domain - gnu
X */
X#include "tar.h"
X
Xextern union record	*head;		/* Points to current tape header */
Xextern struct stat {
X	long	st_size;
X	long	st_mtime;
X} hstat;				/* Fake stat struct for compat. */
X
Xvoid ExtractArchive();
Xextern void PrintHeader();
Xextern void SkipFile();
X
Xint MakeDirs();			/* Makes required directories */
X
X/*
X * Extract - extract the entire archive
X */
XExtract() {
X	Point		where;
X	SFReply		reply;
X	WDPBRec		wdpb;
X
X	/*
X	 * Use the standard file dialog to select the archive.
X	 */
X	where.h = where.v = 75;
X	SFGetFile(pass(where), "\PSelect TAR file", NIL, 0, NIL, NIL, &reply);
X	if (!reply.good)
X		return;
X
X	/*
X	 * Remember the VRefNum and Name for OpenArchive.
X	 * Find out where to put the extracted files.
X	 */
X	arVRefNum = reply.vRefNum;
X	arName = reply.fName;
X	if (GetDir("\Pto put extracted files") == FALSE)
X		return;
X
X	/*
X	 * Open the target directory as a base to put everything.
X	 */
X	wdpb.ioCompletion = NIL;
X	wdpb.ioNamePtr = NIL;
X	wdpb.ioVRefNum = dirVRefNum;
X	wdpb.ioWDProcID = 'TAR ';
X	wdpb.ioWDDirID = dirDirID;
X	if (PBOpenWD(&wdpb, FALSE) != noErr) {
X		OSAlert("\PExtract", "\PPBOpenWD", NIL, wdpb.ioResult);
X		return;
X	}
X
X	/*
X	 * Extract and print the files as found in the archive.
X	 */
X	dirVRefNum = wdpb.ioVRefNum;
X	if (WindInit())
X		goto done;
X
X	TextFace(underlineStyle);
X	WPrintf(header);
X	TextFace(0);
X
X	if (setjmp(errJmp) == 0)
X		ReadAnd(ExtractArchive);
X	else
X		CloseArchive();
X
X	WindEnd(autoPage);
Xdone:
X	(void) PBCloseWD(&wdpb, FALSE);
X}
X
X/*
X * Extract a file from the archive.
X */
Xvoid
XExtractArchive()
X{
X	register char	*data;
X	register char	*p;
X	CInfoPBRec	pb;
X	HPrmBlkRec	dpb;
X	HPrmBlkRec	fpb;
X	int	fd, i, namelen;
X	long	check, written;
X	long	size;
X	OSErr	err;
X	char	macName[256];
X	char	*routine = "\PExtractArchive";
X
X	SaveRec(&head);			/* Make sure it sticks around */
X	UseRec(head);			/* And go past it in the archive */
X	DecodeHeader(head, &hstat, 1);	/* Snarf fields */
X
X	/* Print the record from 'head' and 'hstat' */
X	PrintHeader();
X
X	switch (head->header.linkflag) {
X	default:
X		WPrintf("Unknown file type %d for %s",
X			head->header.linkflag, head->header.name);
X		break;
X
X	case LF_OLDNORMAL:
X	case LF_NORMAL:
X		/*
X		 * Appears to be a file.
X		 * See if it's really a directory.
X		 */
X		namelen = strlen(head->header.name) - 1;
X		if (head->header.name[namelen] == '/')
X			goto really_dir;
X
X		FixName(head->header.name, macName);
X	again_file:
X		fpb.ioCompletion = NIL;
X		fpb.ioNamePtr = macName;
X		fpb.ioVRefNum = dirVRefNum;
X		fpb.u.hfp.ioDirID = 0;
X		err = PBHCreate(&fpb, FALSE);
X		if (err == noErr) {
X			fpb.ioCompletion = NIL;
X			fpb.ioNamePtr = macName;
X			fpb.ioVRefNum = dirVRefNum;
X			fpb.u.hfp.ioDirID = 0;
X			fpb.u.hfp.ioFDirIndex = 0;
X			if (PBHGetFInfo(&fpb, FALSE)) {
X				OSAlert(routine, "\PPBHGetFInfo", NIL, fpb.ioResult);
X				goto doNext;
X			}
X
X			strncpy(&fpb.u.hfp.ioFlFndrInfo.fdCreator, fdCreator, 4);
X			strncpy(&fpb.u.hfp.ioFlFndrInfo.fdType, fdType, 4);
X			fpb.ioCompletion = NIL;
X			fpb.ioNamePtr = macName;
X			fpb.ioVRefNum = dirVRefNum;
X			fpb.u.hfp.ioDirID = 0;
X			if (PBHSetFInfo(&fpb, FALSE)) {
X				OSAlert(routine, "\PPBHSetFInfo", NIL, fpb.ioResult);
X				goto doNext;
X			}
X
X			fpb.u.iop.ioPermssn = fsWrPerm;
X			fpb.u.iop.ioMisc = NIL;
X			err = PBHOpen(&fpb, FALSE);
X		}
X
X		if (err != noErr) {
X			if (MakeDirs(macName))
X				goto again_file;
X
X			PgmAlert(routine, "\PCould not make file", macName, NIL);
X			SkipFile((long)hstat.st_size);
X			break;
X		}
X
X		/*
X		 * Note that this only extracts data forks!
X		 */
X		for (size = hstat.st_size;
X		     size > 0;
X		     size -= written) {
X			/*
X			 * Locate data, determine max length
X			 * writeable, write it, record that
X			 * we have used the data, then check
X			 * if the write worked.
X			 */
X			data = FindRec()->charptr;
X			written = EndOfRecs()->charptr - data;
X			if (written > size)
X				written = size;
X
X			/*
X			 * Convert newlines to return for Mac compatability.
X			 */
X			if (cvtNl) {
X				for (i = written, p = data; --i >= 0; p++)
X					if (*p == '\n')
X						*p = '\r';
X			}
X
X			check = written;
X			fpb.u.iop.ioBuffer = data;
X			fpb.u.iop.ioReqCount = check;
X			fpb.u.iop.ioPosMode = fsAtMark;
X			fpb.u.iop.ioPosOffset = 0;
X			err = PBWrite(&fpb, FALSE);
X			if (err != noErr) {
X				OSAlert(routine, "\PPBWrite", NIL, err);
X				goto doNext;
X			}
X
X			check = fpb.u.iop.ioActCount;
X			/*
X			 * The following is in violation of strict
X			 * typing, since the arg to userec
X			 * should be a struct rec *.  FIXME.
X			 */
X			UseRec(data + written - 1);
X			if (check == written) continue;
X			/*
X			 * Error in writing to file.
X			 * Print it, skip to next file in archive.
X			 */
X			PgmAlert(routine, "\PWrite short", NIL, NIL);
X		doNext:
X			PBClose(&fpb, FALSE);
X			SkipFile((long)(size - written));
X			goto quit;
X		}
X
X		PBClose(&fpb, FALSE);
X	quit:
X		break;
X
X	case LF_DIR:
X		/* Check for trailing / */
X		namelen = strlen(head->header.name)-1;
X	really_dir:
X		while (namelen && head->header.name[namelen] == '/')
X			head->header.name[namelen--] = '\0';	/* Zap / */
X		
X		FixName(head->header.name, macName);
X		/* FALL THROUGH */
X	again_dir:
X		dpb.ioCompletion = NIL;
X		dpb.ioNamePtr = macName;
X		dpb.ioVRefNum = dirVRefNum;
X		dpb.u.hfp.ioDirID = 0;
X		err = PBDirCreate(&dpb, FALSE);
X		if ((err != noErr) && (err != dupFNErr)) {
X			if (MakeDirs(macName))
X				goto again_dir;
X
X			PgmAlert(routine, "\PCould not make directory",
X					macName, NIL);
X		}
X		break;
X
X	}
X
X	/* We don't need to save it any longer. */
X	SaveRec((union record **) 0);	/* Unsave it */
X}
X
X/*
X * After a file/link/symlink/dir creation has failed, see if
X * it's because some required directory was not present, and if
X * so, create all required dirs.
X */
Xint
XMakeDirs(pathname)
Xchar *pathname;
X{
X	int	madeone = 0;		/* Did we do anything yet? */
X	int	i, savedLen;
X	OSErr	err;
X	HPrmBlkRec	pb;
X
X	savedLen = pathname[0] & 0xff;
X	/*
X	 * skip initial ':'
X	 *
X	 * Note that the directory name has already been converted to Mac style.
X	 */
X	for (i = 2; i < savedLen; i++) {
X		while ((i < savedLen) && (pathname[i] != ':'))
X			i++;
X
X		if (i == savedLen)
X			break;
X
X		pathname[0] = i++ - 1;
X		pb.ioCompletion = NIL;
X		pb.ioNamePtr = pathname;
X		pb.ioVRefNum = dirVRefNum;
X		pb.u.hfp.ioDirID = 0;
X		err = PBDirCreate(&pb, FALSE);
X		if (err == dupFNErr) {
X			continue;
X
X		} else if (err != noErr) {
X			OSAlert("\PMakeDirs", "\PPBDirCreate", pathname,
X					pb.ioResult);
X			return(0);
X
X		} else {
X			madeone++;		/* Remember if we made one */
X			continue;
X		}
X	}
X
X	pathname[0] = savedLen;
X	return(madeone);		/* Tell them to retry if we made one */
X}
X
X/*
X * FixName - convert to a Mac style pathname
X *
X *	Conversions:
X *		.	->		(Stay at this directory level)
X *		..	-> ::		(Up a directory level)
X *		.xxxx	-> _xxxx	(Don't get in trouble with device names)
X *		xx:xx	-> xx/xx	(Don't get in trouble with directory delims)
X */
XFixName(tar, mac)
Xregister char	*tar;
Xchar	*mac;
X{
X	char		*end = tar + strlen(tar);
X	register char	*p = mac + 1;
X	register char	*next;
X
X	for (next = tar; tar < end; next++) {
X		/*
X		 * Swallow up all contiguous '/' characters.
X		 */
X		while (*next && (*next == '/'))
X			next++;
X
X		/*
X		 * Find the entire name up until the next '/'.
X		 */
X		tar = next;
X		while (*next && (*next != '/'))
X			next++;
X
X		*next = 0;
X		*p++ = ':';
X		if (*tar == '.')
X			switch (*(tar + 1)) {
X			case '\0':
X				p--;
X				continue;
X
X			case '.':
X				if (*(tar + 2) == 0)
X					continue;
X				/* else FALL THROUGH */
X
X			default:
X				*tar = '_';
X			}
X
X		while (tar < next) {
X			if (*tar == ':')
X				*tar = '/';
X			*p++ = *tar++;
X		}
X	}
X
X	*mac = p - mac - 1;
X}
SHAR_EOF
if test 7771 -ne "`wc -c < 'extract.c'`"
then
	echo shar: error transmitting "'extract.c'" '(should have been 7771 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'list.c'" '(7209 characters)'
if test -f 'list.c'
then
	echo shar: will not over-write existing file "'list.c'"
else
sed 's/^X//' << \SHAR_EOF > 'list.c'
X/*
X * Macintosh Tar
X *
X * Modified by Craig Ruff for use on the Macintosh.
X */
X/*
X * List a tar archive.
X *
X * Also includes support routines for reading a tar archive.
X *
X * Pubic Domain version written 26 Aug 1985 by John Gilmore (ihnp4!hoptoad!gnu).
X *
X * @(#)list.c 1.18 9/23/86 Public Domain - gnu
X */
X#include "tar.h"
X
Xchar *ctime();				/* From libc.a */
X
X#define	isodigit(c)	( ((c) >= '0') && ((c) <= '7') )
X#define isspace(c)	((c) == ' ')
X
Xlong FromOct();			/* Decode octal number */
X
Xunion record *head;		/* Points to current archive header */
Xstruct stat {
X	long	st_size;
X	long	st_mtime;
X} hstat;			/* Fake stat struct for compat. */
X
Xvoid ListArchive();
Xvoid PrintHeader();
Xvoid ReadAnd();
Xvoid SkipFile();
X
X/*
X * List - list an archive file
X */
XList() {
X	Point		where;
X	SFReply		reply;
X	Boolean		oldAutoPage = autoPage;
X
X	/*
X	 * Use standard file to get the archive file.
X	 * Always do a screen at a time if to the screen.
X	 */
X	where.h = where.v = 75;
X	SFGetFile(pass(where), "\PName of TAR file:", NIL, -1, NIL, NIL, &reply);
X	if (!reply.good)
X		return;
X
X	arVRefNum = reply.vRefNum;
X	arName = reply.fName;
X	if (WindInit())
X		return;
X
X	autoPage = TRUE;
X	TextFace(underlineStyle);
X	WPrintf(header);
X	TextFace(0);
X
X	if (setjmp(errJmp) == 0)
X		ReadAnd(ListArchive);
X	else
X		CloseArchive();
X
X	WindEnd(TRUE);
X	autoPage = oldAutoPage;
X}
X
X/*
X * Main loop for reading an archive.
X */
Xvoid
XReadAnd(doSomething)
Xvoid (*doSomething)();
X{
X	int 		status = 1;
X	int 		prevStatus;
X	EventRecord	e;
X	CursHandle	cursor;
X
X	if ((cursor = GetCursor(watchCursor)) != NIL)
X		SetCursor(*cursor);
X
X	OpenArchive(1);		/* Open for reading */
X
X	for(;;) {
X		SystemTask();
X		if (EventAvail(keyDownMask, &e) && (e.what != nullEvent)) {
X			if (
X				(e.modifiers & cmdKey) &&
X				((e.message & charCodeMask) == '.')
X			) {
X				GetNextEvent(keyDownMask, &e);
X				break;
X			}
X		}
X
X		prevStatus = status;
X		status = ReadHeader();
X		switch (status) {
X		case 1:			/* Valid header */
X			/* We should decode next field (mode) first... */
X			/* Ensure incoming names are null terminated. */
X			head->header.name[NAMSIZ-1] = '\0';
X			(*doSomething)();
X			continue;
X
X			/*
X			 * If the previous header was good, tell them
X			 * that we are skipping bad ones.
X			 */
X		case 0:			/* Invalid header */
X		case0:
X			UseRec(head);
X			if (prevStatus == 1) {
X				PgmAlert("\PReadAnd",
X						"\PSkipping to next file header...",
X						NIL, NIL);
X			}
X			continue;
X
X		case 2:			/* Block of zeroes */
X			if (ignorez)	
X				goto case0;	/* Just skip if asked */
X			/* FALL THRU */
X		case (int) EOF:		/* End of archive */
X			break;
X		}
X		break;
X	}
X
X	CloseArchive();
X	SetCursor(&arrow);
X}		
X
X
X/*
X * Print a header record, based on tar options.
X */
Xvoid
XListArchive()
X{
X	long t;
X
X	/* Save the record */
X	SaveRec(&head);
X
X	/*
X	 * Print the header record.
X	 * Don't sling the names too fast!
X	 */
X	PrintHeader();
X	if (!autoPage)
X		Delay(60L, &t);
X
X	/* Skip past it in the archive */
X	SaveRec((union record **) 0);	/* Unsave it */
X	UseRec(head);
X
X	/* Skip to the next header on the archive */
X	SkipFile((long)hstat.st_size);
X}
X
X
X/*
X * Read a record that's supposed to be a header record.
X * Return its address in "head", and if it is good, the file's
X * size in hstat.st_size.
X *
X * Return 1 for success, 0 if the checksum is bad, EOF on eof,
X * 2 for a block full of zeros (EOF marker).
X *
X * You must always userec(head) to skip past the header which this
X * routine reads.
X */
Xint
XReadHeader()
X{
X	register int	i;
X	register long	sum, recsum;
X	register char	*p;
X	register union record *header;
X
X	header = FindRec();
X	head = header;		/* This is our current header */
X	if (NIL == header)
X		return(EOF);
X
X	recsum = FromOct(8,  header->header.chksum);
X	sum = 0;
X	p = header->charptr;
X	for (i = sizeof(*header); --i >= 0;) {
X		/*
X		 * We can't use unsigned char here because of old compilers,
X		 * e.g. V7.
X		 */
X		sum += 0xFF & *p++;
X	}
X
X	/* Adjust checksum to count the "chksum" field as blanks. */
X	for (i = sizeof(header->header.chksum); --i >= 0;)
X		sum -= 0xFF & header->header.chksum[i];
X	sum += ' ' * sizeof(header->header.chksum);
X
X	if (sum == recsum) {
X		/*
X		 * Good record.  Decode file size and return.
X		 */
X		hstat.st_size = FromOct(1+12, header->header.size);
X		return(1);
X	}
X
X	if (sum == 8 * ' ') {
X		/*
X		 * This is a zeroed block...whole block is 0's except
X		 * for the 8 blanks we faked for the checksum field.
X		 */
X		return(2);
X	}
X
X	return(0);
X}
X
X/* 
X * Decode things from a file header record into a "struct stat".
X *
X * read_header() has already decoded the checksum and length, so we don't.
X *
X * If wantug != 0, we want the uid/group info decoded from Unix Standard
X * tapes (for extraction).  If == 0, we are just printing anyway, so save time.
X */
XDecodeHeader(header, st, wantug)
Xregister union record	*header;
Xregister struct stat	*st;
Xint	wantug;
X{
X	st->st_mtime = FromOct(1+12, header->header.mtime);
X}
X
X/*
X * Quick and dirty octal conversion.
X *
X * Result is -1 if the field is invalid (all blank, or nonoctal).
X */
Xlong
XFromOct(digs, where)
Xregister int	digs;
Xregister char	*where;
X{
X	register long	value;
X
X	while (isspace(*where)) {		/* Skip spaces */
X		where++;
X		if (--digs <= 0)
X			return(-1);		/* All blank field */
X	}
X
X	value = 0;
X	while (digs > 0 && isodigit(*where)) {	/* Scan til nonoctal */
X		value = (value << 3) | (*where++ - '0');
X		--digs;
X	}
X
X	if (digs > 0 && *where && !isspace(*where))
X		return(-1);			/* Ended on non-space/nul */
X
X	return(value);
X}
X
X/*
X * Actually print it.
X */
X#define	UGSWIDTH	9	/* min width of User, group, size */
X#define	DATEWIDTH	19	/* Last mod date */
Xstatic int ugswidth = UGSWIDTH;	/* Max width encountered so far */
X
Xvoid
XPrintHeader()
X{
X	char mode;
X	char *timestamp;
X	char *user, *group;
X	char size[12];		/* Holds a formatted long */
X	long longie;		/* To make ctime() call portable */
X	int	pad;
X
X	DecodeHeader(head, &hstat, 0);
X	/* File type and mode */
X	mode = '?';
X	switch (head->header.linkflag) {
X	case LF_NORMAL:
X	case LF_OLDNORMAL:
X		mode = 'F'; 
X		if ('/' == head->header.name[strlen(head->header.name)-1])
X			mode = 'D';
X		break;
X
X	case LF_DIR:
X		mode = 'D';
X		break;
X	}
X
X	/* 
X	 * Convert to Mac based time from Unix based time.
X	 */
X	longie = hstat.st_mtime + TIMEDIFF;
X
X	timestamp = ctime(&longie);
X	timestamp[16] = '\0';
X	timestamp[24] = '\0';
X
X	/* Format the file size or major/minor device numbers */
X	switch (head->header.linkflag) {
X	default:
X		(void) sprintf(size, "?????");
X		break;
X
X	case LF_DIR:
X		(void) sprintf(size, "%.*s", UGSWIDTH, "");
X		break;
X
X	case LF_OLDNORMAL:
X	case LF_NORMAL:
X		(void) sprintf(size, "%ld", hstat.st_size);
X		break;
X	}
X
X
X	/* Figure out padding and print the whole line. */
X	pad = strlen(size) + 1;
X	if (pad > ugswidth)
X		ugswidth = pad;
X
X	WPrintf("%c %*s%s %s %s %.*s", mode, ugswidth - pad, "", size,
X		timestamp+4, timestamp+20, sizeof(head->header.name),
X			      head->header.name);
X}
X
X/*
X * Skip over <size> bytes of data in records in the archive.
X */
Xvoid
XSkipFile(size)
Xregister long size;
X{
X	union record *x;
X
X	while (size > 0) {
X		x = FindRec();
X		if (x == NIL) {	/* Check it... */
X			PgmAlert("\PSkipFile", "\PUnexpected EOF on archive file",
X					NIL, NIL);
X			longjmp(errJmp, 1);
X		}
X		UseRec(x);
X		size -= RECORDSIZE;
X	}
X}
SHAR_EOF
if test 7209 -ne "`wc -c < 'list.c'`"
then
	echo shar: error transmitting "'list.c'" '(should have been 7209 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'menu.c'" '(4610 characters)'
if test -f 'menu.c'
then
	echo shar: will not over-write existing file "'menu.c'"
else
sed 's/^X//' << \SHAR_EOF > 'menu.c'
X/*
X * Macintosh Tar
X *
X * Written by Craig Ruff.
X *
X * Menu handling routines.
X */
X
X#include "tar.h"
X#include <resource.h>
X
X/*
X * Resource IDs for menus
X */
X#define appleID		128
X#define fileID		129
X#define editID		130
X#define optionID	131
X
X/*
X * Index for each menu
X */
X#define appleMenu	0
X#define fileMenu	1
X#define editMenu	2
X#define optionMenu	3
X#define menuCount	4
X
X/*
X * Item in Apple Menu
X */
X#define aboutItem	1
X
X/*
X * Items in File Menu
X */
X#define createItem	1		/* Create archive */
X#define extractItem	2		/* Extract archive */
X#define listItem	3		/* List archive */
X#define closeItem	5		/* Close (only for Desk Acc) */
X#define pgsetItem	6		/* Page setup */
X#define quitItem	8
X
X/*
X * Items in Edit Menu
X *
X * For use by Desk Accessories.
X */
X#define undoItem	1
X#define cutItem		3
X#define copyItem	4
X#define pasteItem	5
X#define clearItem	6
X
X/*
X * Items in Options Menu
X */
X#define convertItem	1		/* Convert newlines */
X#define	oldArchItem	2		/* Old tar compatible archive creation */
X#define blockItem	3		/* Set block size */
X#define pageItem	5		/* Control paging of screen listing */
X#define printItem	6		/* Use printer instead of screen */
X#define typeItem	8		/* Creator/Type dialog */
X
X
X/*
X * Menu related variables
X */
X
XMenuHandle	menus[menuCount];
XBoolean		menusOK;
X
X/*
X * MenuInit - set up menu bar
X *
X *	Reads non-apple menus from resource file.
X */
XBoolean
XMenuInit() {
X	int	i;
X	char	*routine = "\PMenuInit";
X
X	if ((menus[appleMenu] = GetMenu(appleID)) == NIL) {
X		OSAlert(routine, "\PGetMenu Apple Menu", NIL, ResError());
X		return(TRUE);
X	}
X
X	AddResMenu(menus[appleMenu], 'DRVR');
X	if ((menus[fileMenu] = GetMenu(fileID)) == NIL) {
X		OSAlert(routine, "\PGetMenu File Menu", NIL, ResError());
X		return(TRUE);
X	}
X
X	if ((menus[editMenu] = GetMenu(editID)) == NIL) {
X		OSAlert(routine, "\PGetMenu Edit Menu", NIL, ResError());
X		return(TRUE);
X	}
X
X	if ((menus[optionMenu] = GetMenu(optionID)) == NIL) {
X		OSAlert(routine, "\PGetMenu Option Menu", NIL, ResError());
X		return(TRUE);
X	}
X
X
X	CheckItem(menus[optionMenu], pageItem, autoPage);
X	for (i = appleMenu; i < menuCount; i++)
X		InsertMenu(menus[i], 0);
X
X	DDaMenus();
X	DrawMenuBar();
X	return(FALSE);
X}
X
X/*
X * MenuCmd - handle a menu pick or keyboard equivalent
X */
XMenuCmd(result)
Xlong	result;
X{
X	WindowPeek	wp;
X	short		theItem = result & 0xffff;
X	short		theMenu = (result >> 16) & 0xffff;
X	char		name[256];
X	OSErr		err;
X	char		*routine = "\PMenuCmd";
X	GrafPtr		savedPort;
X
X	switch (theMenu) {
X	case appleID:
X		if (theItem == aboutItem)
X			DoAboutBox();
X		else {
X			/*
X			 * Oh boy, set up for a desk accessory.
X			 */
X			GetItem(menus[appleMenu], theItem, name);
X			GetPort(&savedPort);
X			if ((err = ZeroScrap()) != noErr) {
X				OSAlert(routine, "\PZeroScrap", NIL, err);
X				break;
X			}
X
X			if ((err = TEToScrap()) != noErr) {
X				OSAlert(routine, "\PTEToScrap", NIL, err);
X				break;
X			}
X
X			(void) OpenDeskAcc(name);
X			SetPort(savedPort);
X			if (FrontWindow() != NIL) {
X				EDaMenus();
X			}
X		}
X		break;
X
X	case fileID:
X		switch (theItem) {
X		case createItem:
X			ArCreate();
X			break;
X
X		case extractItem:
X			Extract();
X			break;
X
X		case listItem:
X			List();
X			break;
X
X		case closeItem:
X			/*
X			 * If the front window belongs to a Desk Acc, close it!
X			 */
X			if ((wp = (WindowPeek) FrontWindow()) == NIL)
X				break;
X
X			if (wp->windowKind < 0) {
X				CloseDeskAcc(wp->windowKind);
X				DDaMenus();
X			}
X			break;
X
X		case pgsetItem:
X			if (!PrSetup())
X				(void) PrStlDialog(prRecHdl);
X			break;
X
X		case quitItem:
X			doneFlag = TRUE;
X			break;
X		}
X		break;
X
X	case editID:
X		/*
X		 * Send to a Desk Acc.
X		 */
X		(void) SystemEdit(theItem - 1);
X		break;
X
X	case optionID:
X		switch (theItem) {
X		case convertItem:
X			cvtNl ^= TRUE;
X			CheckItem(menus[optionMenu], convertItem, cvtNl);
X			break;
X
X		case oldArchItem:
X			oldArch ^= TRUE;
X			CheckItem(menus[optionMenu], oldArchItem, oldArch);
X			break;
X
X		case blockItem:
X			DoBlockSize();
X			break;
X
X		case pageItem:
X			autoPage ^= TRUE;
X			CheckItem(menus[optionMenu], pageItem, autoPage);
X			break;
X
X		case printItem:
X			doPrint ^= TRUE;
X			CheckItem(menus[optionMenu], printItem, doPrint);
X			break;
X
X		case typeItem:
X			DoCreatorType();
X			break;
X		}
X		break;
X	}
X
X	HiliteMenu(0);
X	FlushEvents(everyEvent, 0);
X}
X
X/*
X * DDaMenus - disable Desk Acc specific menu and menu item
X */
XDDaMenus() {
X	DisableItem(menus[editMenu], 0);
X	DisableItem(menus[fileMenu], closeItem);
X	DrawMenuBar();
X	menusOK = TRUE;
X}
X
X/*
X * EDaMenus - enable Desk Acc specific menu and menu item
X */
XEDaMenus() {
X	EnableItem(menus[editMenu], 0);
X	EnableItem(menus[fileMenu], closeItem);
X	DrawMenuBar();
X	menusOK = FALSE;
X}
SHAR_EOF
if test 4610 -ne "`wc -c < 'menu.c'`"
then
	echo shar: error transmitting "'menu.c'" '(should have been 4610 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'window.c'" '(7222 characters)'
if test -f 'window.c'
then
	echo shar: will not over-write existing file "'window.c'"
else
sed 's/^X//' << \SHAR_EOF > 'window.c'
X/*
X * Macintosh Tar
X *
X * Written by Craig Ruff
X *
X * Window manipulation routines for display (and printer) listings of archives.
X */
X
X#include "tar.h"
X#include <resource.h>
X
X#define dispID	129		/* Display window ID (in resource file) */
X#define LEFTMAR	5
X#define TOPMAR	5
X
XWindowPtr	dispWind;
XBoolean		windOpen = FALSE;
Xshort		fontHeight;
Xshort		bottomMargin;
Xshort		bottomLine;
Xshort		topLine;
Xshort		curLine;
Xshort		wLines;
Xshort		wCurLine = 0;
XRgnHandle 	update;
XTPPrPort	prPort;
XGrafPtr		savedPort;
X
X/*
X * WindInit - either open the window or printer port
X *
X *	Returns TRUE if an error is found, FALSE otherwise.
X *
X *	Separate routines for screen and printer are not really needed.
X */
XBoolean
XWindInit() {
X	FontInfo	fInfo;
X	char		*routine = "\PWindInit";
X
X	if (doPrint) {
X		/*
X		 * We are listing to the printer.
X		 * Make sure we have opened the printer driver.
X		 */
X		if (!pOpen && PrSetup())
X			return(TRUE);
X
X		/*
X		 * Ask about this listings specifics.
X		 */
X		if (PrJobDialog(prRecHdl) == FALSE)
X			return(TRUE);
X
X		if (PrError() != noErr) {
X			OSAlert(routine, "\PPrJobDialog", NIL, PrError());
X			return(TRUE);
X		}
X
X		/*
X		 * Open the printer document (once per listing).
X		 */
X		prPort = PrOpenDoc(prRecHdl, NIL, NIL);
X		if (PrError() != noErr) {
X			OSAlert(routine, "\PPrOpenDoc", NIL, PrError());
X			return(TRUE);
X		}
X
X		/*
X		 * Open the page (once per physical page).
X		 */
X		PrOpenPage(prPort, NIL);
X		if (PrError() != noErr) {
X			OSAlert(routine, "\PPrOpenPage", NIL, PrError());
X			return(TRUE);
X		}
X
X		/*
X		 * Use monaco 9 so our listing lines up properly.
X		 * (Lazy)
X		 */
X		GetPort(&savedPort);
X		SetPort(prPort);
X		TextFont(monaco);
X		TextSize(9);
X
X		/*
X		 * Figure out how many lines fit on a page.
X		 */
X		GetFontInfo(&fInfo);
X		fontHeight = fInfo.ascent + fInfo.descent + fInfo.leading;
X		MoveTo(LEFTMAR, curLine = fontHeight);
X		bottomLine = (((**prRecHdl).prInfo.rPage.bottom / fontHeight) - 1)
X				* fontHeight;
X
X		return(FALSE);
X	}
X
X	/*
X	 * Listing to screen.
X	 * Get a temporary region for scrolling bits.
X	 * Get the window template from the resource file.
X	 */
X	if ((update = NewRgn()) == NIL) {
X		OSAlert(routine, "\PNewRgn", NIL, MemError());
X		return(TRUE);
X	}
X
X	if ((dispWind = GetNewWindow(dispID, NIL, (WindowPtr) -1)) == NIL) {
X		OSAlert(routine, "\PGetNewWindow", NIL, ResError());
X		return(TRUE);
X	}
X
X	/*
X	 * Use monaco 9 so our listing lines up.
X	 */
X	SetPort(dispWind);
X	TextFont(monaco);
X	TextSize(9);
X
X	/*
X	 * Figure out how many lines fit on a page, save this as margin info.
X	 */
X	GetFontInfo(&fInfo);
X	fontHeight = fInfo.ascent + fInfo.descent + fInfo.leading;
X
X	MoveTo(LEFTMAR, curLine = fontHeight);
X	topLine = curLine + fInfo.descent + fInfo.leading;
X	bottomLine = (wLines = dispWind->portRect.bottom / fontHeight) * fontHeight;
X	bottomMargin = bottomLine + fInfo.descent + fInfo.leading;
X
X	wCurLine = 0;
X	windOpen = TRUE;
X	return(FALSE);
X}
X
X/*
X * WindEnd - clean up after a listing to screen or printer
X */
XWindEnd(keyWait)
XBoolean	keyWait;
X{
X	if (doPrint) {
X		TPrStatus	prSt;
X		char		*routine = "\PWindEnd";
X		short		err;
X
X		/*
X		 * Printing, close page and document.
X		 */
X		PrClosePage(prPort);
X		if ((err = PrError()) != noErr) {
X			if (err != iPrAbort)
X				OSAlert(routine, "\PPrClosePage", NIL, PrError());
X			goto done;
X		}
X
X		PrCloseDoc(prPort);
X		if ((err = PrError()) != noErr) {
X			if (err != iPrAbort)
X				OSAlert(routine, "\PPrCloseDoc", NIL, PrError());
X			goto done;
X		}
X
X		/*
X		 * If the printing involved spooling, spool it now.
X		 */
X		if ((**prRecHdl).prJob.bJDocLoop == bSpoolLoop) 
X			PrPicFile(prRecHdl, NIL, NIL, NIL, &prSt);
X
X	done:	SetPort(savedPort);
X		return;
X	}
X
X	/*
X	 * Listing to screen.  See if we should wait for a key press to continue.
X	 */
X	if (keyWait) {
X		EventRecord	e;
X		Boolean		oldAutoPage = autoPage;
X
X		autoPage = FALSE;	/* So we don't get two messages */
X		if (!doPrint)
X			WPrintf("Press any key to continue");
X
X		do {
X			SystemTask();
X			if (GetNextEvent(keyDownMask, &e) == FALSE)
X				e.what = nullEvent;
X		} while (e.what != keyDown);
X
X		autoPage = oldAutoPage;
X	}
X
X	DisposeWindow(dispWind);
X	DisposeRgn(update);
X	windOpen = FALSE;
X}
X
X/*
X * WPrintf - printf to the screen or printer
X *
X * 	Used like you'd expect, but does NOT handle paper motion
X *	characters like newline, etc.
X */
XWPrintf(fmt, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)
Xchar	*fmt;
Xlong	a0, a1, a2, a3, a4, a5, a6, a7, a8, a9;
X{
X	Rect		r;
X	char		buf[256];
X	EventRecord	e;
X	char		*routine = "\PWPrintf";
X	short		err;
X
X	if (!windOpen && !doPrint)
X		return;
X
X	sprintf(buf, fmt, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
X	if (strlen(buf) >= sizeof(buf))
X		StkErrAlert();		/* Sanity check, stack overrun */
X
X	if (doPrint) {
X		/*
X		 * Printing.
X		 */
X		if (curLine > bottomLine) {
X			/*
X			 * At end of page, start a new one.
X			 */
X			PrClosePage(prPort);
X			if ((err = PrError()) != noErr) {
X				if (err != iPrAbort)
X					OSAlert(routine, "\PPrClosePage", NIL,
X							PrError());
X				longjmp(errJmp, 1);
X			}
X
X			PrOpenPage(prPort, NIL);
X			if (PrError() != noErr) {
X				if (err != iPrAbort)
X					OSAlert(routine, "\PPrClosePage", NIL,
X							PrError());
X				longjmp(errJmp, 1);
X			}
X
X			/*
X			 * Yes, we really must reset the font.
X			 */
X			MoveTo(LEFTMAR, curLine = fontHeight);
X			TextFace(underlineStyle);
X			DrawText(header, 0, strlen(header));
X			TextFace(0);
X			MoveTo(LEFTMAR, curLine += fontHeight);
X		}
X
X		DrawText(buf, 0, strlen(buf));
X		MoveTo(LEFTMAR, curLine += fontHeight);
X		return;
X	}
X
X	/*
X	 * On screen.
X	 */
X	SetPort(dispWind);
X	if (autoPage && (++wCurLine == wLines)) {
X		/*
X		 * At end of screen, wait for key press.
X		 */
X		wCurLine = 0;
X		DrawText("Press any key to continue", 0, 25);
X		do {
X			SystemTask();
X			if (GetNextEvent(keyDownMask, &e) == FALSE)
X				e.what = nullEvent;
X		} while (e.what != keyDown);
X
X		SetRect(&r, dispWind->portRect.left, bottomMargin - fontHeight,
X				dispWind->portRect.right, bottomMargin);
X		EraseRect(&r);
X		MoveTo(LEFTMAR, curLine);
X
X	} else {
X		/*
X		 * No auto page, only stop if command S is pressed.
X		 */
X		if (GetNextEvent(keyDownMask, &e)) {
X			if (((e.message & charCodeMask & ~0x20) == 'S')
X						&& (e.modifiers & cmdKey)) {
X				do {
X					/*
X					 * Start again after command Q.
X					 */
X					SystemTask();
X					if (GetNextEvent(keyDownMask, &e) == FALSE)
X						e.message = 0;
X
X					e.message &= charCodeMask & ~0x20;
X				} while (e.message != 'Q');
X			}
X		}
X	}
X
X	if (curLine > bottomLine) {
X		/*
X		 * At end of screen, scroll up.
X		 */
X		SetRect(&r, dispWind->portRect.left, topLine,
X				dispWind->portRect.right, bottomMargin);
X		ScrollRect(&r, 0, -fontHeight, update);
X		SetEmptyRgn(update);
X		MoveTo(LEFTMAR, curLine = bottomLine);
X	}
X
X	DrawText(buf, 0, strlen(buf));
X	MoveTo(LEFTMAR, curLine += fontHeight);
X}
X
X/*
X * PrSetup - make sure the printer driver has been opened.
X *
X *	Returns TRUE if an error is found, FALSE otherwise.
X */
XBoolean
XPrSetup() {
X	char	*routine = "\PPrSetup";
X
X	if (!pOpen) {
X		PrOpen();
X		if (PrError() != noErr) {
X			OSAlert(routine, "\PPrOpen", NIL, PrError());
X			return(TRUE);
X		}
X
X		pOpen = TRUE;
X		PrintDefault(prRecHdl);
X		if (PrError() != noErr) {
X			OSAlert(routine, "\PPrintDefault", NIL, PrError());
X			return(TRUE);
X		}
X	}
X
X	return(FALSE);
X}
SHAR_EOF
if test 7222 -ne "`wc -c < 'window.c'`"
then
	echo shar: error transmitting "'window.c'" '(should have been 7222 characters)'
fi
fi # end of overwriting check
#	End of shell archive
exit 0
--- end of part 3 ---