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( ".", ¤t ) == -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