[comp.os.minix] POSIX compatible tar

n62@nikhefh.nikhef.nl (Klamer Schutte) (09/22/89)

Here is a new version of tar(1).

This version is to be POSIX compatible.
Changes to the older tar:
  -- Can use - as filename for stdin/stdout (Done by Dale Schumacher).
  -- Can tar links, fifo's and block/character special files.
  -- Update the timestamp of the new files.

The file tar.h is copied from the POSIX draft, I suggest placing him in 
/usr/include.
The file getcwd.c is not written by me. It is included because it is
not in the standard minix(-st) distribution. It should be in libc.a .

This tar is only tested on minix-st. PC-minix should not give any 
problems.

Since tar is now able to handle links, a complete tree with links
can be moved by:
$ (cd src; tar c -) | (cd dest; tar x -)

When super-user tar will also fix the user name to the old one.

Bugs:
  The prefix field in the header is not used (so filenames longer than
  100 characters will not be handled correctly).
  When listing an old tar file regular files will be misunderstood.
  You can do the same but not catching this bug by using:
$ tar x[v] /dev/null old.tar
  Not extensively tested. Please report bugs. Fixes will be appreciated.

Klamer Schutte.
Schutte@nikhef.nl
(.signature at end)
#! /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
#	tar.h
#	tar.c
#	getcwd.c
# This archive created: Fri Sep 22 13:36:49 1989
export PATH; PATH=/bin:$PATH
echo shar: extracting "'README'" '(1162 characters)'
if test -f 'README'
then
	echo shar: will not over-write existing file "'README'"
else
sed 's/^X//' << \SHAR_EOF > 'README'
XHere is a new version of tar(1).
X
XThis version is to be POSIX compatible.
XChanges to the older tar:
X  -- Can use - as filename for stdin/stdout (Done by Dale Schumacher).
X  -- Can tar links, fifo's and block/character special files.
X  -- Update the timestamp of the new files.
X
XThe file tar.h is copied from the POSIX draft, I suggest placing him in 
X/usr/include.
XThe file getcwd.c is not written by me. It is included because it is
Xnot in the standard minix(-st) distribution. It should be in libc.a .
X
XThis tar is only tested on minix-st. PC-minix should not give any 
Xproblems.
X
XSince tar is now able to handle links, a complete tree with links
Xcan be moved by:
X$ (cd src; tar c -) | (cd dest; tar x -)
X
XWhen super-user tar will also fix the user name to the old one.
X
XBugs:
X  The prefix field in the header is not used (so filenames longer than
X  100 characters will not be handled correctly).
X  When listing an old tar file regular files will be misunderstood.
X  You can do the same but not catching this bug by using:
X$ tar x[v] /dev/null old.tar
X  Not extensively tested. Please report bugs. Fixes will be appreciated.
X
XKlamer Schutte.
XSchutte@nikhef.nl
SHAR_EOF
if test 1162 -ne "`wc -c < 'README'`"
then
	echo shar: error transmitting "'README'" '(should have been 1162 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'tar.h'" '(1390 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/* 
X * tar.h -- Standard Archive Format
X * USTAR - Uniform Standard Tape Archive
X *
X * As published in POSIX standard IEEE Std 1003.1 (draft 1986)
X *
X * Klamer Schutte 3/'89
X */
X
X#define TBLOCK 		512
X#define NAMSIZ		100
X#define PFXSIZ		155
X
X#define TMODLEN 	8
X#define TUIDLEN		8
X#define TGIDLEN		8
X#define TSIZLEN		12
X#define TMTMLEN		12
X#define TCKSLEN		8
X
X#define TMAGIC		"ustar"
X#define TMAGLEN		6
X#define TVERSION	"00"
X#define TVERSLEN	2
X#define TUNMLEN		32
X#define TGNMLEN		32
X#define TDEVLEN		8
X
X#define REGTYPE		'0'
X#define AREGTYPE	'\0'
X#define LNKTYPE		'1'
X#define SYMTYPE		'2'
X#define CHRTYPE		'3'
X#define BLKTYPE		'4'
X#define DIRTYPE		'5'
X#define FIFOTYPE	'6'
X#define CONTTYPE	'7'
X
X#define TSUID		04000
X#define TSGID		02000
X#define TSVTX		01000
X
X#define TUREAD		00400
X#define TUWRITE		00200
X#define TUEXEC		00100
X#define TGREAD		00040
X#define TGWRITE		00020
X#define TGEXEC		00010
X#define TOREAD		00004
X#define TOWRITE		00002
X#define TOEXEC		00001
X
Xunion hblock {
X	char	dummy[TBLOCK];
X	struct header {
X		char	name[NAMSIZ];
X		char	mode[TMODLEN];
X		char	uid[TUIDLEN];
X		char	gid[TGIDLEN];
X		char	size[TSIZLEN];
X		char	mtime[TMTMLEN];
X		char	chksum[TCKSLEN];
X		char	typeflag;
X		char	linkname[NAMSIZ];
X		char	magic[TMAGLEN];
X		char	version[TVERSLEN];
X		char	uname[TUNMLEN];
X		char	gname[TGNMLEN];
X		char	devmajor[TDEVLEN];
X		char	devminor[TDEVLEN];
X		char	prefix[PFXSIZ];
X	} dbuf;
X};
SHAR_EOF
if test 1390 -ne "`wc -c < 'tar.h'`"
then
	echo shar: error transmitting "'tar.h'" '(should have been 1390 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'tar.c'" '(17200 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/* tar - tape archiver			Author: Michiel Huisjes */
X
X/* Usage: tar [cxt][vo][F] tapefile [files]
X *
X * attempt to make tar to conform to POSIX 1003.1
X * disclaimer: based on an old (1986) POSIX draft.
X * Klamer Schutte, 20/9/89
X *
X * Bugs:
X *  verbose mode is not reporting consistent
X *  code needs cleanup
X *  prefix field is not used
X *	(add you favorite bug here (or two (or three (or ...))))
X */
X
X#include <stdio.h>	/* need NULL */
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <sys/dir.h>
X#include <pwd.h>
X#include <grp.h>
X
X#include "tar.h"
X#define	POSIX_COMP	/* POSIX compatible */
X
X#ifdef S_IFIFO
X#define	HAVE_FIFO	/* have incorporated Simon Pooles' changes */
X#endif
X
Xtypedef char BOOL;
X#define TRUE	1
X#define FALSE	0
X
X#define HEADER_SIZE	TBLOCK
X#define NAME_SIZE	NAMSIZ
X/* #define BLOCK_BOUNDARY	 20 -- not in POSIX ! */
X
Xtypedef union hblock	HEADER;
X
X/* make the MINIX member names overlap to the POSIX names */
X#define	m_name		name
X#define m_mode		mode
X#define m_uid		uid
X#define m_gid		gid
X#define m_size		size
X#define	m_time		mtime
X#define	m_checksum	chksum
X#define	m_linked	typeflag
X#define	m_link		linkname
X#define	hdr_block	dummy
X#define	m		header
X#define	member		dbuf
X
X#if 0	/* original structure -- see tar.h for new structure */
Xtypedef union {
X  char hdr_block[HEADER_SIZE];
X  struct m {
X	char m_name[NAME_SIZE];
X	char m_mode[8];
X	char m_uid[8];
X	char m_gid[8];
X	char m_size[12];
X	char m_time[12];
X	char m_checksum[8];
X	char m_linked;
X	char m_link[NAME_SIZE];
X  } member;
X} HEADER;
X#endif
X
X/* structure used to note links */
Xstruct link
X{	int		dev, ino;
X	char		name[NAMSIZ];
X	struct link	*next;
X}	*link_top = NULL;
X
XHEADER header;
X
X#define INT_TYPE	(sizeof(header.member.m_uid))
X#define LONG_TYPE	(sizeof(header.member.m_size))
X
X#define MKDIR		"/bin/mkdir"
X
X#define NIL_HEADER	((HEADER *) 0)
X#define NIL_PTR		((char *) 0)
X#define BLOCK_SIZE	TBLOCK
X
X#define flush()		print(NIL_PTR)
X
XBOOL show_fl, creat_fl, ext_fl;
X
Xint tar_fd;
X/* char usage[] = "Usage: tar [cxt] tarfile [files]."; */
Xchar usage[] = "Usage: tar [cxt][vo][F] tarfile [files].";
Xchar io_buffer[BLOCK_SIZE];
Xchar path[NAME_SIZE];
Xchar pathname[NAME_SIZE];
Xint force_flag = 0;
X#ifdef ORIGINAL_DEFAULTS
Xint chown_flag = 1;
Xint verbose_flag = 1;
X#else
Xint chown_flag = 0;
Xint verbose_flag = 0;
X#endif
X
Xint total_blocks;
Xlong convert();
X
X#define block_size()	(int) ((convert(header.member.m_size, LONG_TYPE) \
X			+ (long) BLOCK_SIZE - 1) / (long) BLOCK_SIZE)
X
Xerror(s1, s2)
Xchar *s1, *s2;
X{
X  string_print(NIL_PTR, "%s %s\n", s1, s2 ? s2 : "");
X  flush();
X  exit(1);
X}
X
Xmain(argc, argv)
Xint argc;
Xregister char *argv[];
X{
X  register char *ptr;
X  int i;
X
X  if (argc < 3)
X	error(usage, NIL_PTR);
X  
X  for (ptr = argv[1]; *ptr; ptr++) {
X	switch (*ptr) {
X		case 'c' :
X			creat_fl = TRUE;
X			break;
X		case 'x' :
X			ext_fl = TRUE;
X			break;
X		case 't' :
X			show_fl = TRUE;
X			break;
X		case 'v' :			/* verbose output  -Dal */
X			verbose_flag = !verbose_flag;
X			break;
X		case 'o' :			/* chown/chgrp files  -Dal */
X			chown_flag = TRUE;
X			break;
X		case 'F' :			/* IGNORE ERRORS  -Dal */
X			force_flag = TRUE;
X			break;
X		case 'f':			/* standard U*IX usage -KS */
X			break;
X		default :
X			error(usage, NIL_PTR);
X	}
X  }
X
X  if (creat_fl + ext_fl + show_fl != 1) 
X	error(usage, NIL_PTR);
X  
X  if (strcmp(argv[2], "-") == 0)	/* only - means stdin/stdout - KS */
X	tar_fd = creat_fl ? 1 : 0;	/* '-' means used stdin/stdout  -Dal */
X  else
X	tar_fd = creat_fl ? creat(argv[2], 0644) : open(argv[2], 0);
X
X  if (tar_fd < 0)
X	error("Cannot open ", argv[2]);
X
X  if (creat_fl) {
X	for (i = 3; i < argc; i++) {
X		add_file(argv[i]);
X		path[0] = '\0';
X	}
X	adjust_boundary();
X  }
X  else
X	tarfile();
X
X  flush();
X  exit(0);
X}
X
XBOOL get_header()
X{
X  register int check;
X
X  mread(tar_fd, &header, sizeof(header));
X  if (header.member.m_name[0] == '\0')
X	return FALSE;
X
X  if (force_flag)		/* skip checksum verification  -Dal */
X	return TRUE;
X
X  check = (int) convert(header.member.m_checksum, INT_TYPE);
X  
X  if (check != checksum())
X	error("Tar: header checksum error.", NIL_PTR);
X
X  return TRUE;
X}
X
Xtarfile()
X{
X  register char *ptr;
X  register char *mem_name;
X
X  while (get_header()) {
X	mem_name = header.member.m_name;
X	if (ext_fl) {
X		if (is_dir(mem_name)) {
X			for (ptr = mem_name; *ptr; ptr++)
X				;
X			*(ptr - 1) = '\0';
X			mkdir(mem_name);
X		}
X		else
X			extract(mem_name);
X	}
X	else  {
X		string_print(NIL_PTR, "%s%s", mem_name,
X				(verbose_flag ? " " : "\n"));
X		switch(header.dbuf.typeflag)
X		{ case '1':
X			verbose_print("linked to", header.dbuf.linkname );
X			break;
X		case '5':
X			verbose_print("","directory");
X			break;
X		case '6':
X			verbose_print("","fifo");
X			break;
X		case '3':
X		case '4':
X			if (verbose_flag)
X				string_print(NIL_PTR, 
X				    "%s special file major %s minor %s\n",
X				    (header.dbuf.typeflag == '3' ?
X				    "character" : "block" ),
X				    header.dbuf.devmajor, header.dbuf.devminor );
X			break;
X		case '0':
X		case 0:
X			if (verbose_flag)
X				string_print(NIL_PTR, "%d tape blocks\n",
X							block_size());
X			skip_entry();
X			break;
X		default:
X			string_print(NIL_PTR,"not recogised item %d\n",
X				header.dbuf.typeflag );
X		}
X	}
X	flush();
X  }
X}
X
Xskip_entry()
X{
X  register int blocks = block_size();
X
X  while (blocks--)
X	(void) read(tar_fd, io_buffer, BLOCK_SIZE);
X}
X
Xextract(file)
Xregister char *file;
X{
X  register int fd;
X
X  switch( header.dbuf.typeflag )
X  { case '1':		/* Link */
X	if (link(header.member.m_link, file) < 0)
X		string_print(NIL_PTR, "Cannot link %s to %s\n",
X						    header.member.m_link, file);
X	else if (verbose_flag)
X		string_print(NIL_PTR, "Linked %s to %s\n",
X						    header.member.m_link, file);
X	return;
X  case '5':		/* directory */
X	/* warning -- should not occur because name should end with a / */
X	if (mkdir(file) == 0)
X	{	
X  		do_chown( file );
X	}
X	return;
X  case '3':		/* character special */
X  case '4':		/* block special */
X	{	int	dmajor, dminor, mode;
X
X		dmajor = (int)convert(header.dbuf.devmajor, INT_TYPE);
X		dminor = (int)convert(header.dbuf.devminor, INT_TYPE);
X		mode = (header.dbuf.typeflag == '3' ? S_IFCHR : S_IFBLK);
X		if (mknod( file, mode, (dmajor << 8 | dminor)) == 0)
X		{
X			if (verbose_flag)
X				string_print( NIL_PTR, 
X				    "made %s special file major %s minor %s\n",
X				    (header.dbuf.typeflag == '3' ?
X					"character" : "block" ),
X				    header.dbuf.devmajor, header.dbuf.devminor );
X	  		do_chown( file );
X		}
X		return;
X	}
X  case '2':		/* symbolic link */
X  case '7':		/* contiguous file -- what is this (KS) */
X	print("Not implemented file type\n");
X	return;	/* not implemented, but break out */		
X#ifdef HAVE_FIFO
X  case '6':		/* fifo */
X	if (mkfifo(file,0) == 0)	/* is chmod'ed in do_chown */
X	{	
X  		do_chown( file );
X		verbose_print("made fifo", file );
X	}
X	else
X		string_print(NIL_PTR, "Can't make fifo %s\n", file );
X	return;
X#endif
X  }
X
X  /* security change: creat with mode 0600, chown and then chmod -- KS */
X  if ((fd = creat(file, 0600)) < 0) {
X	string_print(NIL_PTR, "Cannot create %s\n", file);
X	return;
X  }
X
X  copy(file, tar_fd, fd, convert(header.member.m_size, LONG_TYPE));
X  (void) close(fd);
X
X  do_chown( file );
X}
X
Xdo_chown( file )
Xchar	*file;
X{	int uid = 0, gid = 0;
X
X  if(!chown_flag) {		/* set correct owner and group  -Dal */
X	if (strcmp(TMAGIC,header.dbuf.magic))
X	{	struct passwd	*pwd, *getpwnam();
X		struct group	*grp, *getgrnam();
X
X		pwd = getpwnam(header.dbuf.uname);
X		if (pwd != NULL) uid = pwd->pw_uid;
X		grp = getgrnam(header.dbuf.gname);
X		if (grp != NULL) gid = grp->gr_gid;
X	} else {
X		uid = (int)convert(header.member.m_uid, INT_TYPE);
X		gid = (int)convert(header.member.m_gid, INT_TYPE);
X	}
X	chown(file, uid, gid );  
X  }
X  chmod(file, (int)convert(header.member.m_mode, INT_TYPE));
X
X  /* should there be a timestamp if the chown failes? -- KS */
X  timestamp(file);
X
X}
X
Xtimestamp( file )
Xchar	*file;
X{
X	long	times[2];
X	
X	times[0] = times[1] = (long)convert(header.dbuf.mtime);
X	utime( file, times );
X}
X
Xcopy(file, from, to, bytes)
Xchar *file;
Xint from, to;
Xregister long bytes;
X{
X  register int rest;
X  int blocks = (int) ((bytes + (long) BLOCK_SIZE - 1) / (long) BLOCK_SIZE);
X
X  if (verbose_flag)
X	string_print(NIL_PTR, "%s, %d tape blocks\n", file, blocks);
X
X  while (blocks--) {
X	(void) read(from, io_buffer, BLOCK_SIZE);
X	rest = (bytes > (long) BLOCK_SIZE) ? BLOCK_SIZE : (int) bytes;
X	mwrite(to, io_buffer, (to == tar_fd) ? BLOCK_SIZE : rest);
X	bytes -= (long) rest;
X  }
X}
X
Xlong convert(str, type)
Xchar str[];
Xint type;
X{
X  register long ac = 0L;
X  register int i;
X
X  for (i = 0; i < type; i++) {
X	if (str[i] >= '0' && str[i] <= '7') {
X		ac <<= 3;
X		ac += (long) (str[i] - '0');
X	} else	/* don't want to generate an error, but what? -- KS */
X		return ac;
X  }
X
X  return ac;
X}
X
Xmkdir(dir_name)
Xchar *dir_name;
X{
X  register int pid, w;
X
X  if ((dir_name[0] == '.') && ((dir_name[1] == '\0') || (dir_name[1] == '.')))
X	return;
X
X  if ((pid = fork()) < 0)
X	error("Cannot fork().", NIL_PTR);
X  
X  if (pid == 0) {
X	execl(MKDIR, "mkdir", dir_name, (char *) 0);
X	error("Cannot find mkdir.", NIL_PTR);
X  }
X
X  do {
X	w = wait((int *) 0);
X  } while (w != -1 && w != pid);
X}
X
Xchecksum()
X{
X  register char *ptr = header.member.m_checksum;
X  register int ac = 0;
X
X  while (ptr < &header.member.m_checksum[INT_TYPE])
X	*ptr++ = ' ';
X
X  ptr = header.hdr_block;
X  while (ptr < &header.hdr_block[BLOCK_SIZE])
X	ac += *ptr++;
X
X  return ac;
X}
X
Xis_dir(file)
Xregister char *file;
X{
X  while (*file++ != '\0')
X	;
X
X  return (*(file - 2) == '/');
X}
X
X
Xchar *path_name(file)
Xregister char *file;
X{
X
X  string_print(pathname, "%s%s", path, file);
X  return pathname;
X}
X
Xadd_path(name)
Xregister char *name;
X{
X  register char *path_ptr = path;
X
X  while (*path_ptr)
X	path_ptr++;
X  
X  if (name == NIL_PTR) {
X	while (*path_ptr-- != '/')
X		;
X	while (*path_ptr != '/' && path_ptr != path)
X		path_ptr--;
X	if (*path_ptr == '/')
X		path_ptr++;
X	*path_ptr = '\0';
X  }
X  else {
X	while (*name) {
X		if (path_ptr == &path[NAME_SIZE])
X			error("Pathname too long", NIL_PTR);
X		*path_ptr++ = *name++;
X	}
X	*path_ptr++ = '/';
X	*path_ptr = '\0';
X  }
X}
X
Xadd_file(file)
Xregister char *file;
X{
X  struct stat st;
X  struct direct dir;
X  register int fd = -1;
X  char namebuf[16];	/* -Dal */
X  char	cwd[129];	/* -KS */
X
X  if (stat(file, &st) < 0) {
X	string_print(NIL_PTR, "Cannot find %s\n", file);
X	return;
X  }
X  if ((fd = add_open(file,&st)) < 0) {
X	string_print(NIL_PTR, "Cannot open %s\n", file);
X	return;
X  }
X
X  make_header(path_name(file), &st);
X  switch(st.st_mode & S_IFMT)
X  { case S_IFREG:
X	header.dbuf.typeflag = '0';
X	string_print(header.member.m_checksum, "%I ", checksum());
X	mwrite(tar_fd, &header, sizeof(header));
X	copy(path_name(file), fd, tar_fd, st.st_size);
X	break;
X  case S_IFDIR:
X	header.dbuf.typeflag = '5';
X	string_print(header.member.m_checksum, "%I ", checksum());
X	mwrite(tar_fd, &header, sizeof(header));
X	if (NULL == getcwd( cwd, 129 ))
X		string_print(NIL_PTR, "Error: cannot getcwd()\n" );
X	else if (chdir(file) < 0)
X		string_print(NIL_PTR, "Cannot chdir to %s\n", file);
X	else {
X		is_added( file );
X		verbose_print("read directory", file );
X		add_path(file);
X		mread(fd, &dir, sizeof(dir));		/* "." */
X		mread(fd, &dir, sizeof(dir));		/* ".." */
X		while (read(fd, &dir, sizeof(dir)) == sizeof(dir))
X			if (dir.d_ino) {
X				strncpy(namebuf, dir.d_name, 14);
X				namebuf[14] = '\0';
X				add_file(namebuf);
X			}
X		chdir(cwd);
X		add_path(NIL_PTR);
X		*file = 0;
X	}
X	break;
X#ifdef HAVE_FIFO
X  case S_IFIFO:
X	header.dbuf.typeflag = '6';
X	verbose_print("read fifo", file );
X	string_print(header.member.m_checksum, "%I ", checksum());
X	mwrite(tar_fd, &header, sizeof(header));
X	break;
X#endif
X  case S_IFBLK:
X	header.dbuf.typeflag = '4';
X	if (verbose_flag)
X		string_print(NIL_PTR, 
X			"read block device %s major %s minor %s\n",
X			file, header.dbuf.devmajor, header.dbuf.devminor );
X	string_print(header.member.m_checksum, "%I ", checksum());
X	mwrite(tar_fd, &header, sizeof(header));
X	break;
X  case S_IFCHR:
X	header.dbuf.typeflag = '3';
X	if (verbose_flag)
X		string_print(NIL_PTR, 
X			"read character device %s major %s minor %s\n",
X			file, header.dbuf.devmajor, header.dbuf.devminor );
X	string_print(header.member.m_checksum, "%I ", checksum());
X	mwrite(tar_fd, &header, sizeof(header));
X	break;
X  case -1 & S_IFMT:
X	header.dbuf.typeflag = '1';
X	if (verbose_flag)
X		string_print(NIL_PTR, "linked %s to %s\n", 
X			header.dbuf.linkname, file );
X	string_print(header.member.m_checksum, "%I ", checksum());
X	mwrite(tar_fd, &header, sizeof(header));
X	break;
X  default:
X	string_print("Tar: %s unknown file type. Not added.\n", file);
X	*file = 0;
X  }
X
X  flush();
X  is_added( &st, file );
X  add_close(fd);
X}
X
Xverbose_print( s1, s2 )
Xchar	*s1, *s2;
X{
X	if (verbose_flag)
X		string_print(NIL_PTR, "%s: %s\n", s1, s2 );
X}
X
Xadd_close( fd )
Xint	fd;
X{
X	if (fd != 0)
X		close( fd );
X}
X
Xadd_open( file, st )
Xchar	*file;
Xstruct stat	*st;
X{
X	int	fd;
X	if (((st->st_mode & S_IFMT) != S_IFREG) && 
X		((st->st_mode & S_IFMT) != S_IFDIR))
X		return 0;
X	fd = open(file, 0);
X	return fd;
X}
X	
Xmake_header(file, st)
Xchar *file;
Xregister struct stat *st;
X{
X  register char *ptr = header.member.m_name;
X  char 	*is_linked();
X  struct passwd	*pwd, *getpwuid();
X  struct group	*grp, *getgrgid();
X
X  clear_header();
X
X  while (*ptr++ = *file++)
X	;
X
X  if ((st->st_mode & S_IFMT) == S_IFDIR) {	/* fixed test  -Dal */
X	*(ptr - 1) = '/';
X  }
X  
X  string_print(header.member.m_mode, "%I ", st->st_mode & 07777);
X  string_print(header.member.m_uid, "%I ", st->st_uid);
X  string_print(header.member.m_gid, "%I ", st->st_gid);
X  if ((st->st_mode & S_IFMT) == S_IFREG)
X  	string_print(header.member.m_size, "%L ", st->st_size);
X  else
X	strncpy(header.dbuf.size, "0", TSIZLEN );
X  string_print(header.member.m_time, "%L ", st->st_mtime);
X  /*  header.member.m_linked = ''; */
X  if ((ptr = is_linked(st)) != NULL)
X  {	strncpy( header.dbuf.linkname, ptr, NAMSIZ );
X	st->st_mode = -1;	/* invalid value */
X  }
X  strncpy(header.dbuf.magic, TMAGIC, TMAGLEN );
X  header.dbuf.version[0] = 0;
X  header.dbuf.version[1] = 0;
X  pwd = getpwuid( st->st_uid );
X  strncpy(header.dbuf.uname, (pwd!=NULL?pwd->pw_name:"nobody"), TUNMLEN );
X  grp = getgrgid( st->st_gid );
X  strncpy(header.dbuf.gname, (grp!=NULL?grp->gr_name:"nobody"), TGNMLEN );
X  if (st->st_mode & (S_IFBLK | S_IFCHR))
X  {	string_print(header.dbuf.devmajor, "%I ", (st->st_rdev >> 8));
X  	string_print(header.dbuf.devminor, "%I ", (st->st_rdev & 0xFF));
X  }
X  header.dbuf.prefix[0] = 0;
X}
X
Xis_added( st, file )
Xstruct stat	*st;
Xchar	*file;
X{
X	struct link	*new;
X	char	*malloc();
X
X	if (*file == 0)
X		return;	
X	new = (struct link *) malloc(sizeof(struct link));
X	if (new == NULL)
X        {	print("Out of memory\n");
X		return;
X	}
X	new->next = link_top;
X	new->dev = st->st_dev;
X	new->ino = st->st_ino;
X	strncpy( new->name, path_name(file), NAMSIZ );
X	link_top = new;
X}
X
Xchar * is_linked( st )
Xstruct stat	*st;
X{
X	struct link *cur = link_top;
X
X	while( cur != NULL )
X		if ((cur->dev == st->st_dev) && (cur->ino == st->st_ino))
X			return cur->name;
X		else
X			cur = cur->next;
X	return NULL;
X}
X
Xclear_header()
X{
X  register char *ptr = header.hdr_block;
X
X  while (ptr < &header.hdr_block[BLOCK_SIZE])
X	*ptr++ = '\0';
X}
X
Xadjust_boundary()
X{
X  clear_header();
X  mwrite(tar_fd, &header, sizeof(header));
X#ifndef POSIX_COMP
X  while (total_blocks++ < BLOCK_BOUNDARY)
X	mwrite(tar_fd, &header, sizeof(header));
X#else
X  mwrite(tar_fd, &header, sizeof(header));
X#endif
X  (void) close(tar_fd);
X}
X
Xmread(fd, address, bytes)
Xint fd, bytes;
Xchar *address;
X{
X  if (read(fd, address, bytes) != bytes)
X	error("Tar: read error.", NIL_PTR);
X}
X
Xmwrite(fd, address, bytes)
Xint fd, bytes;
Xchar *address;
X{
X  if (write(fd, address, bytes) != bytes)
X	error("Tar: write error.", NIL_PTR);
X
X  total_blocks++;
X}
X
Xchar output[BLOCK_SIZE];
Xprint(str)		/* changed to use stderr rather than stdout  -Dal */
Xregister char *str;
X{
X  static int index = 0;
X
X  if (str == NIL_PTR) {
X	write(2, output, index);
X	index = 0;
X	return;
X  }
X
X  while (*str) {
X	output[index++] = *str++;
X	if (index == BLOCK_SIZE) {
X		write(2, output, BLOCK_SIZE);
X		index = 0;
X	}
X  }
X}
X
Xchar *num_out(number)
Xregister long number;
X{
X  static char num_buf[12];
X  register int i;
X
X  for (i = 11; i--; ) {
X	num_buf[i] = (number & 07) + '0';
X	number >>= 3;
X  }
X
X  return num_buf;
X}
X
X/* VARARGS */
Xstring_print(buffer, fmt, args)
Xchar *buffer;
Xregister char *fmt;
Xint args;
X{
X  register char *buf_ptr;
X  char *scan_ptr;
X  char buf[NAME_SIZE];
X  char *argptr = (char *)&args;
X  BOOL pr_fl, i;
X
X  if (pr_fl = (buffer == NIL_PTR))
X	buffer = buf;
X
X  buf_ptr = buffer;
X  while (*fmt) {
X	if (*fmt == '%') {
X		fmt++;
X		switch (*fmt++) {
X			case 's': 
X				scan_ptr = *((char **)argptr);
X				argptr += sizeof(char *);
X				break;
X			case 'I': 
X				scan_ptr = num_out((long) *((int *)argptr));
X				argptr += sizeof(int);
X				for (i = 0; i < 5; i++)
X					scan_ptr++;
X				break;
X			case 'L': 
X				scan_ptr = num_out(*((long *) argptr));
X				argptr += sizeof(long);
X				break;
X			case 'd' :
X				scan_ptr = num_out((long) *((int *)argptr));
X				argptr += sizeof(int);
X				while (*scan_ptr == '0')
X					scan_ptr++;
X				scan_ptr--;
X				break;
X			default: 
X				scan_ptr = "";
X		}
X		while (*buf_ptr++ = *scan_ptr++)
X			;
X		buf_ptr--;
X	}
X	else
X		*buf_ptr++ = *fmt++;
X  }
X  *buf_ptr = '\0';
X
X  if (pr_fl)
X	print(buffer);
X}
X
SHAR_EOF
if test 17200 -ne "`wc -c < 'tar.c'`"
then
	echo shar: error transmitting "'tar.c'" '(should have been 17200 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'getcwd.c'" '(2876 characters)'
if test -f 'getcwd.c'
then
	echo shar: will not over-write existing file "'getcwd.c'"
else
sed 's/^X//' << \SHAR_EOF > 'getcwd.c'
X#include "lib.h"
X
X/*  getcwd(3)
X *
X *  Author: Terrence W. Holm          Aug. 1988
X *
X *  Directly derived from Adri Koppes' pwd(1).
X */
X
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <sys/dir.h>
X#include <errno.h>
X
X#define  NULL         (char *) 0
X#define  O_RDONLY     0
X#define  DIRECT_SIZE  ((int)sizeof (struct direct))
X#define  PATH_MAX     127
X
X#ifdef __STDC__
Xextern char *rindex(CONST char *, int);
X#else
Xextern char *rindex();
X#endif
X
Xextern int errno;
X
X
Xchar *getcwd( buffer, size )
X  char *buffer;
X  int   size;
X
X  {
X  static char path[ PATH_MAX + 1 ];
X  struct stat current;
X
X  if ( buffer == NULL  ||  size == 0 )
X    {
X    errno = EINVAL;
X    return( NULL );
X    }
X
X  path[0] = '\0';
X
X  /*  Get the inode for the current directory  */
X   
X  if ( stat( ".", &current ) == -1 )
X    return( NULL );
X
X  if ( (current.st_mode & S_IFMT) != S_IFDIR )
X    return( NULL );
X
X
X  /*  Run backwards up the directory tree, grabbing 	*/
X  /*  directory names on the way.			*/
X
X  while (1)
X    {
X    struct stat parent;
X    struct direct d;
X    int same_device = 0;
X    int found = 0;
X    int fd;
X
X    /*  Get the inode for the parent directory  */
X
X    if ( chdir( ".." ) == -1 )
X	return( NULL );
X
X    if ( stat( ".", &parent ) == -1 )
X	return( NULL );
X
X    if ( (parent.st_mode & S_IFMT) != S_IFDIR )
X	return( NULL );
X
X    if ( current.st_dev == parent.st_dev )
X	same_device = 1;
X
X
X    /*  At the root, "." is the same as ".."  */
X
X    if ( same_device  &&  current.st_ino == parent.st_ino )
X	break;
X
X
X    /*  Search the parent directory for the current entry  */
X
X    if ( (fd = open( ".", O_RDONLY )) == -1 )
X	return( NULL );
X
X    while ( ! found  &&  read(fd, &d, DIRECT_SIZE) == DIRECT_SIZE )
X	{
X	if ( same_device )
X	    {
X	    if ( current.st_ino == d.d_ino )
X		found = 1;
X	    }
X	else
X	    {
X	    static char temp_name[ DIRSIZ + 1 ];
X	    static struct stat dir_entry;
X
X	    temp_name[0] = '\0';
X	    strncat( temp_name, d.d_name, DIRSIZ );
X
X	    if ( stat( temp_name, &dir_entry ) == -1 )
X		{
X		close( fd );
X		return( NULL );
X		}
X
X	    if ( current.st_dev == dir_entry.st_dev  &&
X	         current.st_ino == dir_entry.st_ino )
X		found = 1;
X	    }
X	}
X
X    close( fd );
X
X    if ( ! found )
X    	return( NULL );
X
X    if ( strlen(path) + DIRSIZ + 1 > PATH_MAX )
X	{
X	errno = ERANGE;
X	return( NULL );
X	}
X
X    strcat( path, "/" );
X    strncat( path, d.d_name, DIRSIZ );
X 
X    current.st_dev = parent.st_dev;
X    current.st_ino = parent.st_ino;
X    }
X
X
X  /*  Copy the reversed path name into <buffer>  */
X
X  if ( strlen(path) + 1 > size )
X    {
X    errno = ERANGE;
X    return( NULL );
X    }
X
X  if ( strlen(path) == 0 )
X    {
X    strcpy( buffer, "/" );
X    return( buffer );
X    }
X
X  *buffer = '\0';
X
X  {
X  char *r;
X
X  while ( (r = rindex( path, '/' )) != NULL )
X    {
X    strcat( buffer, r );
X    *r = '\0';
X    }
X  }
X
X  return( chdir( buffer ) ? NULL : buffer );
X  }
SHAR_EOF
if test 2876 -ne "`wc -c < 'getcwd.c'`"
then
	echo shar: error transmitting "'getcwd.c'" '(should have been 2876 characters)'
fi
fi # end of overwriting check
#	End of shell archive
exit 0
-- 
____________________Yes, mail address changed again :-(_________________________
Klamer Schutte        mcvax!nikhefh!{n62,Schutte}        {Schutte,n62}@nikhef.nl

ast@cs.vu.nl (Andy Tanenbaum) (09/23/89)

In article <261@nikhefh.nikhef.nl> Schutte@nikhefh.nikhef.nl (Klamer Schutte) writes:
>Here is a new version of tar(1).
>This version is to be POSIX compatible.

Thank you very much!  (I find it characteristic of our age be to thanking
someone who is a couple of miles away, here in Amsterdam, by posting a
message for 10,000 people worldwide to see, but I greatly appreciate having
POSIX compatible stuff.)

I just learned that Draft 9 of P1003.2 (shell & utilities) has been sent to
IEEE for distribution.  It can be ordered from IEEE's office in New York City.

Andy Tanenbaum (ast@cs.vu.nl)

marks@mgse.UUCP (Mark Seiffert) (09/26/89)

In article <261@nikhefh.nikhef.nl> Schutte@nikhefh.nikhef.nl (Klamer Schutte) writes:
>Here is a new version of tar(1).
>

I do not have minix yet, i have the book, but there seem
to be a few utilites missing, not enough to bother typing it it, when
i get a job again i hope to get minix (unless i can find someone to
give me a copy in the meantime), what i can't understand yet is how
you people plan on backing up your systems. Is there tape backup
software for the PC or ST versions yet? What would be the best way
to have this tar program prompt the user to switch disks (or tape)
when it is full? Under Xenix tar the 'k' option followed by a numeric
argument tells tar how big a volume is and the user is prompted for
another volume when the current one is full. Is this POSIX compliant?
how type code would be used in the headerblock, is that what CONTTYPE
is for?

Here is a shar file with a note of what i have done, and a diff of the
changes i made. I think this will all be portable to Minix, please let
me know if it is not.

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then feed it
# into a shell via "sh file" or similar.  To overwrite existing files,
# type "sh file -c".
# The tool that generated this appeared in the comp.sources.unix newsgroup;
# send mail to comp-sources-unix@uunet.uu.net if you want that tool.
# If this archive is complete, you will see the following message at the end:
#		"End of shell archive."
# Contents:  note.mgs tar.c.diff
# Wrapped by root@mgse on Mon Sep 25 12:35:06 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'note.mgs' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'note.mgs'\"
else
echo shar: Extracting \"'note.mgs'\" \(1480 characters\)
sed "s/^X//" >'note.mgs' <<'END_OF_FILE'
XThis is to document the changes i have made to the POSIX tar utility
Xposted by n62@nikhefh.nikhef.nl (Klamer Schutte). Some stuff was added
Xso it would compile under SCO Xenix 286 rel. 2.2.1, most notably was
Xthe need to a mkfifo() function. I have noted three bugs so far, and
Xfixed two. The tar.c.diff contains the patches i have made so far. I
Xwould love to find out just what POSIX means as far as a tar utility
Xis concerned.
X
X1)      tar will back itself up, should check archive inode num(&dev)
Xand then check the target inode number. We should issue a warning
Xand ignore target.
X
X	Made changes to main() to get the archive inode number and
Xdevice with a stat() call. this is saved in the global variables
Xar_inode and ar_dev. The type declarations for ar_inode and ar_dev
Xare from the SCO Xenix stat.h man page, I am not sure that this is
Xportable to Minix.  Changes were made to add_file() to check the
Xtarget files device and inode against the archive numbers. The
Xarchive inode (ar_inode) is initialized to zero (0), since the root
Xinode is 2 this should not cause a problem. I seem to remember
Xsomething about inode 0 or 1 being for the superblock or boot block.
XIf the archive file is stdout, ar_inode and ar_dev remain at zero.
X
X2)      tar will not notice that a file has changed size while it
Xwas being backed up. should issue warning.
X
X3)      the 'f' option was not documented in usage[].
X
X	changed both usage[] defines. Why are there two (one is
Xcommented out)?

END_OF_FILE
echo shar: NEWLINE appended to \"'note.mgs'\"
if test 1481 -ne `wc -c <'note.mgs'`; then
    echo shar: \"'note.mgs'\" unpacked with wrong size!
fi
# end of 'note.mgs'
fi
if test -f 'tar.c.diff' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'tar.c.diff'\"
else
echo shar: Extracting \"'tar.c.diff'\" \(2537 characters\)
sed "s/^X//" >'tar.c.diff' <<'END_OF_FILE'
X14a15,21
X> /*
X> 1)	tar will back itself up, should check archive inode num(&dev) and
X> 		then check the target inode number. In verbose mode, issue
X> 		warning, in all cases ignore target.
X> 		marks@mgse	Mon Sep 25 10:38:58 CDT 1989
X> 	added global varaibles, made changes to main() and add_file();
X> 		maks@mgse Mon Sep 25 12:09:20 CDT 1989
X15a23,31
X> 2)	tar will not notice that a file has changed size while it was being
X> 		backed up. should issue warning.
X> 		marks@mgse	Mon Sep 25 10:38:58 CDT 1989
X> 
X> 3)	the 'f' option was not documented in usage[].
X> 		marks@mgse	Mon Sep 25 12:03:20 CDT 1989
X> 	changed both usage[] defines. Why are there two (one is commented out)?
X> */
X> 
X29a46,51
X> #if	defined(M_XENIX)		/* is this Xenix? */
X> #ifdef	HAVE_FIFO			/* yes, should we include FIFO code? */
X> #define	NEED_MKFIFO			/* yes, we don't have mkfifo()	*/
X> #endif
X> #endif				/* marks@mgse Mon Sep 25 10:06:08 CDT 1989 */
X> 
X94,95c116,117
X< /* char usage[] = "Usage: tar [cxt] tarfile [files]."; */
X< char usage[] = "Usage: tar [cxt][vo][F] tarfile [files].";
X---
X> /* char usage[] = "Usage: tar [cxt][f] tarfile [files]."; */
X> char usage[] = "Usage: tar [cxt][vo][F][f] tarfile [files].";
X107a130,133
X> /* make sure we don't tar ourselves. marks@mgse Mon Sep 25 12:06:28 CDT 1989 */
X> ino_t	ar_inode;				/* archive inode number	*/
X> dev_t	ar_dev;					/* archive device number */
X> 
X126a153
X>   struct stat st;
X170a198,206
X> 	ar_inode = ar_dev = 0;			/* init ar_inode & ar_dev */
X> 
X> 	if (tar_fd > 1 && stat(argv[2], &st) < 0)
X> 		error("Can't stat ", argv[2]);	/* will never be here, right? */
X> 	else {				/* get archive inode & device	*/
X> 		ar_inode= st.st_ino;		/* save files inode	*/
X> 		ar_dev	= st.st_dev;		/* save files device	*/
X> 	}			/* marks@mgse Mon Sep 25 11:30:45 CDT 1989 */
X> 
X488a525,527
X> /*
X>  *	add a file to the archive
X> */
X496a536
X>   char	*getcwd();		/* marks@mgse Mon Sep 25 10:06:08 CDT 1989 */
X501a542,547
X> 
X>   if (st.st_dev == ar_dev && st.st_ino == ar_inode) {
X> 	string_print(NIL_PTR, "Cannot tar current archive file (%s)\n", file);
X> 	return;
X>   }				/* marks@mgse Mon Sep 25 12:06:28 CDT 1989 */
X> 
X597a644,646
X> /*
X>  *	open file 'file' to be added to archive, return file descripto
X> */
X819a869,876
X> #ifdef	NEED_MKFIFO			/* do we need a mkfifo() function? */
X> mkfifo(filename, perms)
X> char	*filename;			/* name of fifo special file	*/
X> int	perms;				/* perms for file	*/
X> {
X> 	return( mknod(filename, S_IFIFO | perms, 0) );
X> }
X> #endif				/* marks@mgse Mon Sep 25 10:06:08 CDT 1989 */

END_OF_FILE
echo shar: NEWLINE appended to \"'tar.c.diff'\"
if test 2538 -ne `wc -c <'tar.c.diff'`; then
    echo shar: \"'tar.c.diff'\" unpacked with wrong size!
fi
# end of 'tar.c.diff'
fi
echo shar: End of shell archive.
exit 0

>Klamer Schutte        mcvax!nikhefh!{n62,Schutte}        {Schutte,n62}@nikhef.nl


-- 
Mark Seiffert,  Metairie, LA.
uucp:           rex!mgse!marks
bitnet:         marks%mgse@REX.CS.TULANE.EDU
internet:       marks%mgse@rex.cs.tulane.edu

meulenbr@cstw01.prl.philips.nl (Frans Meulenbroeks) (09/26/89)

As far as a POSIX compatible tar concerns:
Several moons ago a program called pax was posted to comp.sources.unix.
This program does the following:

---- Start of excerpt ----

    This is version 1.1 of Pax, a public domain archiving utility.  
    
    Pax is an archiving utility that reads and writes tar and cpio formats, 
    both the traditional ones and the extended formats specified in IEEE

    1003.1.  It handles multi-volume archives and automatically determines 
    the format of an archive while reading it.  Three user interfaces are 
    supported: tar, cpio, and pax.  The pax interface was designed by IEEE 
    1003.2 as a compromise in the chronic controversy over which of tar or 
    cpio is best.

    The USENIX Association provided some support for this implementation

    project.  As a result, the Pax utility is being distributed free of 
    charge and may be redistributed by others in either source or binary

    form.  (See the liscensing section for restrictions)

---- End of excerpt ----

I have not tried to port this program to Minix yet, but it looks like
this is going to be *the* thing.
There is only one disadvantage to the package. It's source is quite
large: somewhere around 260 k including man pages and so on.
However, this could be reduced, since it also includes (as posted) both
Doug Gwyns' directory functions and Henry Spencers' regexp package.

Frans Meulenbroeks        (meulenbr@cst.prl.philips.nl)
	Centre for Software Technology
	( or try: ...!mcvax!phigate!prle!cst!meulenbr)

ast@cs.vu.nl (Andy Tanenbaum) (09/26/89)

In article <1002@mgse.UUCP> marks@mgse.UUCP (Mark Seiffert) writes:
>what i can't understand yet is how
>you people plan on backing up your systems. 

I wrote a little program, backup.c, which I posted a few weeks ago.
Tar is tape oriented, whereas on a PC, floppy disk is the obvious medium
to use.  Why simulate a sequential tape on a random access device?  To
use backup, you give the name of the directory you want to backup, and the
program compares the modification time of the file to see if the backup 
version is still valid.  If not, it copies it to the diskette.  I use this
program all the time, and it works fine.  I have made some changes to it
(e.g., don't backup up core images, etc.) and I will post it with 1.4b.

I also have written a cross referencer and a program to take a patched file
plus the cdif and print the patched file with the changed lines marked with
! and +.  I also have Dick van Veen's man program and some other utilities.
Again, I'll post all this stuff shortly.

Andy Tanenbaum (ast@cs.vu.nl)

saj%yipeia@Sun.COM (Scott A. Jordahl) (09/27/89)

In article <718@prles2.UUCP> meulenbr@cstw01.prl.philips.nl (Frans Meulenbroeks) writes:
>As far as a POSIX compatible tar concerns:
>Several moons ago a program called pax was posted to comp.sources.unix.
>This program does the following:
>
>---- Start of excerpt ----
>
>    This is version 1.1 of Pax, a public domain archiving utility.  
>    
>    Pax is an archiving utility that reads and writes tar and cpio formats, 
>    both the traditional ones and the extended formats specified in IEEE
>
...

>
>---- End of excerpt ----
>
>I have not tried to port this program to Minix yet, but it looks like
>this is going to be *the* thing.
...

>
>Frans Meulenbroeks        (meulenbr@cst.prl.philips.nl)
>	Centre for Software Technology
>	( or try: ...!mcvax!phigate!prle!cst!meulenbr)

Be warned that I just saw a posting from the author of PAX that he is
looking for people to beta test version 2.0.  He also indicated that if
no major problems were found, it would be released for general
distribution very soon.  For those thinking about it, it may be more
advantageous to apply porting efforts to this new release.

-- Scott


/########################################################\
| Scott A. Jordahl                                       |
| UUCP:   saj@yipeia.Eng.Sun.COM                         |
| PHONE:  WK: [415] 336-5463                             |
|         HM: [408] 270-5619                             |
\########################################################/

schreiner@iravcl.ira.uka.de (09/28/89)

Some months ago i ported PAX to MINIX ( ST version ). There were several
bugs, especially in handling links, which i fixed. This version compiles
to less than 64K text. After unsing pax some month without problems, i
posted the fixes to the author of pax. Since then i use pax as tar to
backup (and restore) my harddisk. Pax read or write also compatible tar-
archives which can be read by BSD or SYS V tars. 
Because of crashing my /hd1 i have only the fixed sources here.
If there is enough interest i could post them. ( As far as i can remember
this is allowed. )


	Ralf Wenk,	using a friends account