shar 3.49 (part 2 of 2)
Richard H. Gumpertz
rhg at cpsolv.CPS.COM
Thu Sep 13 06:25:09 AEST 1990
Submitted-by: rhg at cpsolv
Archive-name: shar3.49/part02
---- Cut Here and feed the following to sh ----
#!/bin/sh
# This is part 02 of shar3.49
# ============= shar.c ==============
if test -f 'shar.c' -a X"$1" != X"-c"; then
echo 'x - skipping shar.c (File already exists)'
else
echo 'x - extracting shar.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'shar.c' &&
Xchar *revision = "3.49";
Xchar RCS_ID[] = "$Header: /u/rhg/src/shar/shar.c,v 3.49 90/09/12 15:14:41 rhg Exp $";
X/*
X** shar.c
X
X Defined functions:
X gen_mkdir(path)
X gen_mkdir_script(path)
X setTOUCH()
X walktree(rtn,rootname)
X header(argc,argv)
X helpuser()
X main(argc,argv)
X mode_map(mode,mode_str)
X shar(file,RstrName)
X
X*/
X/*+:EDITS:*/
X/*:09-12-1990-14:24-rhg at cps.com-added missing return(0) to walkdown */
X/*:09-12-1990-14:13-rhg at cps.com-deleted some redundant, unused, code */
X/*:09-12-1990-00:28-rhg at cps.com-added more directions to the shar header */
X/*:09-09-1990-20:12-rhg at cps.com-added CLOSEDIR_VOID */
X/*:09-09-1990-18:42-rhg at cps.com-added check for "From" under OptPREFIX */
X/*:09-09-1990-11:55-rhg at cps.com-modified code under NOT STR(N)CMP_IS_FAST */
X/*:09-08-1990-21:20-rhg at cps.com-added NO_DIRENT for SunOS 3 sys/dir.h */
X/*:09-08-1990-21:04-rhg at cps.com-fixed bug in PREFIX check: strcmp(line,Delim) */
X/*:08-06-1990-00:40-rhg at cps.com-revised Cut message to be more explanatory */
X/*:08-05-1990-14:04-rhg at cps.com-merged Rname into walktree */
X/*:08-05-1990-12:11-rhg at cps.com-added walktree & support for sharing dirs */
X/*:08-05-1990-09:05-rhg at cps.com-change -Bn, -t, and -b to -bn, -T, and -B */
X/*:08-04-1990-15:31-rhg at cps.com-added -Bn to set compress -bn (default 12) */
X/*:08-04-1990-15:31-rhg at cps.com-changed shar3_???_.tmp to _shar_???_.tmp */
X/*:08-04-1990-15:22-rhg at cps.com-added check for "exit 0" under OptPREFIX */
X/*:08-04-1990-14:32-rhg at cps.com-added -m to generate TOUCH (default off) */
X/*:08-04-1990-14:18-rhg at cps.com-reversed the meaning of -x and deleted -O */
X/*:06-14-1990-14:48-rhg at cps.com-made Split and eXists compatible.
X/*:06-14-1990-14:18-rhg at cps.com-made -x the default and added -O
X/*:06-14-1990-12:44-rhg at cps.com-always terminate the && and report failures
X/*:06-14-1990-12:28-rhg at cps.com-clear mkdir_already between -l files
X/*:06-14-1990-12:14-rhg at cps.com-change PREFIX from a #define to an int variable.
X/*:04-19-1990-22:49-rhg at cps.com-get rid of "set" so "sh sharfil -c" will work */
X/*:04-19-1990-21:52-rhg at cps.com-add -F to clear OptPREFIX */
X/*:04-18-1990-08:49-rhg at cps.com-add OptPREFIX (for now, always on) */
X/*:07-09-1990-19:24-wht at n4hgf-back to fgrep amc -- fits more -m touches */
X/*:07-01-1990-18:37-wht at n4hgf-wait() needed after fork() */
X/*:05-19-1990-02:47-wht at n4hgf-change fgrep amc to mmdd */
X/*:05-16-1990-01:53-wht at n4hgf-Archive-name had extra period sometimes */
X/*:05-10-1990-20:39-wht at n4hgf-altos does not not like at-sign in filenames */
X/*:05-10-1990-13:38-wht at n4hgf-add -V Vanilla mode */
X/*:05-07-1990-00:06-wht at n4hgf-test all mallocs for Purity Of Essence */
X/*:05-07-1990-00:06-wht at n4hgf-add -S switch */
X/*:05-05-1990-01:37-relay.EU.net!rivm!a3-dont assume vax is running BSD */
X/*:04-18-1990-02:01-wht at n4hgf-3.20 rhg at cps.com did all the NICE work */
X/*:04-17-1990-14:30-rhg at cps.com-pretty up if-then-else-fi in shar file */
X/*:04-17-1990-12:13-rhg at cps.com-add Split and renamed old -l to -L */
X/*:04-17-1990-12:13-rhg at cps.com-add -c option to shar file execution */
X/*:04-17-1990-11:20-rhg at cps.com-simplify TOUCH logic in shar file */
X/*:04-17-1990-10:27-rhg at cps.com-create setTOUCH to avoid duplicate code */
X/*:04-17-1990-04:43-rhg at cps.com-add missing && to commands in shar file(s) */
X/*:04-17-1990-02:03-rhg at cps.com-add Compress */
X/*:04-16-1990-17:08-rhg at cps.com-add AvoidPipes as well as code to use pipes */
X/*:04-03-1990-20:09-wht at n4hgf-3.11 */
X/*:04-01-1990-13:20-pat at rwing-correct case on M option in getopt() call */
X/*:04-01-1990-13:50-pat at rwing-change defaults on -v, -w to be on */
X/*:03-29-1990-18:23-wht at n4hgf-add automatic sequent support */
X/*:03-28-1990-15:56-wht at n4hgf-add mode and length net.bandwidth chrome */
X/*:03-28-1990-14:23-wht at n4hgf-correct some runtime diagnostics */
X/*:11-14-1989-02:21-wht-SHAR_EOF was botched if last file char not newline */
X/*:11-02-1989-14:11-wht-add touch -am */
X
X/*
X Shar puts readable text files together in a package
X from which they are easy to extract.
X earlier attribution wht at n4hgf has: decvax!microsof!uw-beave!jim
X (James Gosling at CMU)
X*/
X/*
X * I have made several mods to this program:
X *
X * 1) the -----Cut Here-----... now preceds the script.
X * 2) the cat has been changed to a sed which removes a prefix
X * character from the beginning of each line of the extracted
X * file, this prefix character is added to each line of the archived
X * files and is not the same as the first character of the
X * file delimeter.
X * 3) added several options:
X * -c - add the -----Cut Here-----... line.
X * -d'del' - change the file delimeter to del.
X * -s - cause the resulting script to print the wc of
X * the orignal file and the wc of the extracted
X * file.
X *
X * Michael A. Thompson
X * Dalhousie University
X * Halifax, N.S., Canada.
X */
X
X/*
X * I, too, have been hacking this code. This is the version on sixhub
X * bill davidsen (davidsen at sixhub.uucp)
X *
X * - added support for binary files
X * - automatic creation of limited size multiple file archives,
X * each of which may be unpacked separately, and with sequence
X * checking.
X * - support for mixed text and binary files
X * - preserve file permissions
X * - restore to filename rather than pathname
X *
X */
X/*
X * One good hack deserves another ... this version generates shell
X * code which attempts to create missing directories
X * handle deviants sun, vax, pyr (pyramid), SCO XENIX/UNIX automatically
X * for sequent, add -DBSD42
X * force Verbose on
X * if unsharing system's touch Sys V compatible (allows touch -m),
X * restore file dates
X * -n switch puts an alpha "name" in header
X * -a (if also -n) puts "Submitted-by:" & "Archive-name: <name>/part##
X * use getopt
X * as well as some other chrome-plated junque
X * ...!gatech!emory!tridom!wht (wht%n4hgf at gatech.edu) Warren Tucker
X *
X * 3.11 - Fri Apr 6 14:21:51 EDT 1990
X * With due deference to davidsen at sixhub, more changes..... copies
X * of this, like 3.10, were mailed to him:
X * From wht Fri Apr 6 15:14:30 1990 remote from n4hgf
X * Received: by n4hgf.UUCP (smail2.5-UNIX/386 5.3.2)
X * id AA01781; 6 Apr 90 15:14:30 EDT (Fri)
X * Date: Fri, 6 Apr 90 15:14:30 EDT
X * X-Mailer: Mail User's Shell (6.5 4/17/89)
X * From: wht at n4hgf (Warren Tucker)
X * To: davidsen at sixhub
X * Subject: shar 3.11
X * X-Bang-Reply-to: gatech!n4hgf!wht -or- emory!tridom!n4hgf!wht
X * Reply-to: wht%n4hgf at gatech.edu
X * Message-Id: <9004061514.AA01781 at n4hgf.UUCP>
X *
X * 1. changes suggested by pat at rwing (Pat Myrto) and silvert at cs.dal.ca
X * (Bill Silvert)
X * 2. fixes to who_am_i code in who at where.c
X *
X * 3.20 - Wed Apr 18 01:58:32 EDT 1990
X * changes were made per edit notes by
XFrom: gatech!mailrus!uunet!cpsolv.CPS.COM!rhg (Richard H. Gumpertz)
X * ...!gatech!n4hgf!wht (wht%n4hgf at gatech.edu) Warren Tucker
X *
X */
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <time.h>
X#include <sys/stat.h>
X#include <ctype.h>
X
X/* assume system v unless otherwise fixed */
X#if (defined(pyr) || defined(vax) || defined(sequent)) && !defined(BSD42) && !defined(SYS5)
X#define BSD42
X#endif
X#if defined(sun) /* this miscreant doesn't exactly fit BSD or SYSV */
X#undef BSD42
X#undef SYS5
X#endif
X#if !defined(BSD42) && !defined(sun) && !defined(SYS5)
X#define SYS5
X#endif
X
X#if defined(sun) || defined(BSD42)
X#define strchr index
X#define strrchr rindex
X#endif
X
Xchar *strchr();
Xchar *strrchr();
X#ifdef __STDC__ /* my concession to ANSI-pansiness */
Xvoid *malloc();
X#else
Xchar *malloc();
X#endif
XFILE *fdopen();
XFILE *popen();
X
X#define DELIM "SHAR_EOF"/* put after each file */
X#define PREFIX1 'X' /* goes in front of each line */
X#define PREFIX2 'Y' /* goes in front of each line if Delim[0] == PREFIX1 */
X#define WC "wc -c <"
X
Xint PREFIX = PREFIX1; /* Character to get at the beginning of each line */
X
Xint Archive_name = 0; /* option to generate "Archive-name:" headers */
Xint Verbose = 1; /* option to provide append/extract feedback */
Xint Wc_c = 1; /* option to provide wc checking */
Xchar *Delim = DELIM; /* pointer to delimiter string */
Xint OptPREFIX = 1; /* suppress PREFIX unless 1st char forces it */
Xint Cut = 0; /* option to provide cut mark */
Xchar *CutMessage = "---- Cut Here and feed the following to sh ----\n";
Xint Binary = 0; /* flag for binary files */
Xint AvoidPipes = 0; /* use temp file instead of pipe to feed uudecode, etc.
X (better error detection at expense of disk space) */
Xint Vanilla = 0; /* no Brown-Shirt mode */
Xint Touch = 0; /* generate $TOUCH commands */
Xint Compress = 0; /* run input files through compress (requires Binary) */
Xint CompressBits = 12; /* -b option to compress */
Xint Mixed = 0; /* mixed text and binary files */
Xint eXists = 1; /* check if file exists */
Xint InterOW = 0; /* interactive overwrite */
Xint PosParam = 0; /* allow positional parameters */
Xint FileStrip; /* strip directories from filenames */
X#ifdef DEBUG
Xint de_bug = 0; /* switch for debugging on */
X#define DeBug(f,v) if (de_bug) printf(f, v)
X#else /* normal compile */
X#define DeBug(f,v) /* do nothing */
X#endif
X
XFILE *fpout = stdout;
Xint shar();
Xunsigned limit = 0;
Xint Split = 0; /* Split files in the middle */
Xlong ftell();
Xlong TypePos; /* position for archive type message */
Xlong EndHeadPos; /* position for first file in the shar file */
Xchar outname[50]; /* base for output filename */
Xchar filename[50]; /* actual output filename */
Xchar *sharname = (char *)0;
Xchar *submitter = (char *)0;
Xint filenum = 0; /* output file # */
Xstruct stat fst; /* check file type, access */
X
Xmain(argc,argv)
Xchar **argv;
X{
Xint status = 0;
Xint stdin_file_list = 0;
Xchar *oname;
Xint c;
Xextern int optind;
Xextern char *optarg;
X
X while((c = getopt(argc,argv,"VmSvwd:BTCb:xXcfMpPFas:n:l:L:o:h")) != -1)
X {
X switch(c)
X {
X case 'V':
X Vanilla = 1;
X break;
X case 'm':
X Touch = 1;
X break;
X case 'S':
X stdin_file_list = 1;
X break;
X case 'v':
X Verbose = 0;
X break;
X case 'w':
X Wc_c = 0;
X break;
X case 'd':
X Delim = optarg;
X PREFIX = (Delim[0] == PREFIX1 ? PREFIX2 : PREFIX1);
X break;
X case 'B': /* binary files */
X Binary = 1;
X Compress = 0;
X break;
X case 'T': /* text mode */
X Binary = 0;
X Compress = 0;
X break;
X case 'b': /* Compress bits */
X CompressBits = atoi(optarg);
X /* fall through to -C */
X case 'C': /* Compress */
X Binary = 1;
X Compress = 1;
X break;
X case 'x': /* don't worry whether the file exist */
X eXists = 0;
X break;
X case 'X': /* ask the user whether to overwrite existing files */
X InterOW = 1;
X eXists = 1;
X break;
X case 'c':
X Cut = 1;
X break;
X case 'f': /* filenames only */
X FileStrip = 1;
X break;
X case 'M': /* mixed text and binary */
X Mixed = 1;
X break;
X case 'p': /* allow positional parameters */
X PosParam = 1;
X break;
X case 'P': /* use temp files instead of pipes in the shar file */
X AvoidPipes = 1;
X break;
X case 'F': /* force PREFIX to be put out even if not required */
X OptPREFIX = 0;
X break;
X case 'l': /* soft size limit in k */
X if((limit = atoi(optarg)) > 1)
X --limit;
X Split = 0;
X DeBug("Soft limit %dk\n",limit);
X break;
X case 'L': /* hard size limit in k */
X if((limit = atoi(optarg)) > 1)
X --limit;
X Split = (limit != 0);
X AvoidPipes = 1;
X DeBug("Hard limit %dk\n",limit);
X break;
X case 'n': /* name of archive */
X sharname = optarg;
X break;
X case 's': /* submitter */
X submitter = optarg;
X break;
X case 'a': /* generate Archive-name: headers */
X Archive_name = 1;
X break;
X case 'o': /* specify output file */
X oname = optarg;
X strcpy(outname,oname);
X strcat(outname,".");
X filenum = 1;
X strcpy(filename,outname);
X strcat(filename,"01");
X fpout = fopen(filename,"w");
X if(!fpout)
X { /* creation error */
X perror("can't create output file");
X exit(1);
X }
X break;
X#ifdef DEBUG
X case '$': /* totally undocumented $ option, debug on */
X de_bug = 1;
X break;
X#endif
X default: /* invalid option */
X case 'h': /* help */
X helpuser();
X break;
X }
X }
X
X if(Vanilla)
X {
X fprintf(stderr,"Vanilla mode disabling years of progress :-)\n");
X Wc_c = 0;
X OptPREFIX = 0;
X#ifdef V_AVOIDPIPES /* pipes are benign and only used with uudecode anyway */
X AvoidPipes = 1;
X#endif /* V_AVOIDPIPES */
X#ifdef V_NOFORCE /* If the user specifies non-defaults, let him have them */
X#else
X Touch = 0;
X InterOW = 0;
X if(Binary || Mixed || Compress || PosParam)
X fprintf(stderr,"WARNING: non-Text storage options overridden.\n");
X Binary = 0;
X Mixed = 0;
X Compress = 0;
X PosParam = 0;
X#endif /* V_NOFORCE */
X }
X
X if(stdin_file_list)
X {
X char stdin_buf[258];
X argc = 0;
X if(!(argv = (char **)malloc(1024 * sizeof(char *))))
X goto MEMORY_ERROR;
X stdin_buf[0] = 0;
X while(fgets(stdin_buf,sizeof(stdin_buf),stdin))
X {
X if(argc == 1024)
X {
X fprintf(stderr,"max files from stdin is 1024!\n");
X exit(1);
X }
X if(stdin_buf[0])
X stdin_buf[strlen(stdin_buf) - 1] = 0;
X if(!(argv[argc] = malloc(strlen(stdin_buf) + 1)))
X {
XMEMORY_ERROR: /* NOT likely, but free software must pure as snow! */
X fprintf(stderr,"out of memory handling stdin input at %d\n",
X argc);
X exit(1);
X }
X strcpy(argv[argc],stdin_buf);
X ++argc;
X stdin_buf[0] = 0;
X }
X optind = 0;
X }
X
X if(optind >= argc)
X {
X fprintf(stderr,"shar: No input files\n");
X helpuser();
X exit(1);
X }
X
X if(Archive_name && !sharname)
X {
X fprintf(stderr,"shar: -n must accompany -a\n");
X helpuser();
X exit(1);
X }
X
X if(!submitter)
X {
X if(!(submitter = malloc(128)))
X {
X fprintf(stderr,"memory allocation failed\n"); /* NOT likely */
X exit(1);
X }
X who_where(submitter);
X }
X
X if(header(argc-optind,&argv[optind]))
X exit(2);
X
X if(InterOW)
X {
X Verbose = 1;
X fputs("wish=\n",fpout);
X if(Archive_name)
X {
X fprintf(stderr,
X "PLEASE do not submit -X shars to the usenet or other\n");
X fprintf(stderr,
X "public networks. They will cause problems.\n");
X }
X }
X
X EndHeadPos = ftell(fpout);
X
X for( ; optind < argc; ++optind)
X { /* process positional parameters and files */
X if(PosParam)
X { /* allow -B and -T and -C inline */
X if(strcmp(argv[optind],"-B") == 0)
X { /* set binary */
X Binary = 1;
X Compress = 0;
X continue;
X }
X if(strcmp(argv[optind],"-T") == 0)
X { /* set mode text */
X Binary = 0;
X Compress = 0;
X continue;
X }
X if(strcmp(argv[optind],"-C") == 0)
X { /* set compress */
X Binary = 1;
X Compress = 1;
X continue;
X }
X }
X status += walktree(shar,argv[optind]);
X }
X
X /* delete the sequence file, if any */
X if(Split && filenum > 1)
X {
X fputs("rm -f _shar_seq_.tmp\n",fpout);
X fputs("echo You have unpacked the last part\n",fpout);
X if(!Verbose)
X fprintf(stderr,"Created %d files\n",filenum);
X }
X fputs("exit 0\n",fpout);
X exit(status);
X}
X
X/*+-----------------------------------------------------------------------
X mode_map(mode,mode_str) build drwxrwxrwx string
X------------------------------------------------------------------------*/
Xchar *
Xmode_map(mode,mode_str)
Xunsigned short mode;
Xchar *mode_str;
X{
X#ifdef THIS_IS_NOT_NEEDED_FOR_SHAR
Xregister unsigned ftype = mode & S_IFMT;
X#endif
Xregister char *rtn;
Xstatic char result[12];
X
X rtn = (mode_str == (char *)0) ? result : mode_str;
X
X /* drwxrwxrwx */
X /* 0123456789 */
X strcpy(rtn,"----------");
X
X#ifdef THIS_IS_NOT_NEEDED_FOR_SHAR
X switch(ftype)
X {
X case S_IFIFO: *rtn = 'p'; break; /* FIFO (named pipe) */
X case S_IFDIR: *rtn = 'd'; break; /* directory */
X case S_IFCHR: *rtn = 'c'; break; /* character special */
X case S_IFBLK: *rtn = 'b'; break; /* block special */
X case S_IFREG: *rtn = '-'; break; /* regular */
X
X#if defined(sun) | defined(BSD42)
X case S_IFLNK: *rtn = 'l'; break; /* symbolic link */
X case S_IFSOCK: *rtn = 's'; break; /* socket */
X#endif
X
X#if defined (SYS5)
X case S_IFNAM: /* name space entry */
X if(mode & S_INSEM) /* semaphore */
X {
X *rtn = 's';
X break;
X }
X if(mode & S_INSHD) /* shared memory */
X {
X *rtn = 'm';
X break;
X }
X#endif
X
X default: *rtn = '?'; break; /* ??? */
X }
X#endif /* THIS_IS_NOT_NEEDED_FOR_SHAR */
X
X if(mode & 000400) *(rtn + 1) = 'r';
X if(mode & 000200) *(rtn + 2) = 'w';
X if(mode & 000100) *(rtn + 3) = 'x';
X if(mode & 004000) *(rtn + 3) = 's';
X if(mode & 000040) *(rtn + 4) = 'r';
X if(mode & 000020) *(rtn + 5) = 'w';
X if(mode & 000010) *(rtn + 6) = 'x';
X if(mode & 002000) *(rtn + 6) = 's';
X if(mode & 000004) *(rtn + 7) = 'r';
X if(mode & 000002) *(rtn + 8) = 'w';
X if(mode & 000001) *(rtn + 9) = 'x';
X if(mode & 001000) *(rtn + 9) = 't';
X
X return(rtn);
X
X} /* end of mode_map */
X
Xvoid
XsetTOUCH()
X{
X if(Touch)
X {
X fputs("if touch 2>&1 | fgrep 'amc' > /dev/null\n",fpout);
X fputs(" then TOUCH=touch\n",fpout);
X fputs(" else TOUCH=true\n",fpout);
X fputs("fi\n",fpout);
X }
X} /* end of setTOUCH */
X
X#ifdef NO_WALKTREE
X
Xint
Xwalktree(rtn,file) /* dummy walktree */
Xint (*rtn)(/*file,rname*/); /* may also assume fst is set */
Xchar *file;
X{
X register char *rname;
X
X if(stat(file,&fst))
X {
X fprintf(stderr,"shar: Can't access %s\n",file);
X return(1);
X }
X
X if(FileStrip)
X { /* use just the filename */
X rname = file + strlen(file);
X while(rname > file && *rname != '/')
X --rname;
X if(*rname == '/')
X ++rname;
X }
X else
X rname = file;
X if(!strncmp(rname,"./",2) && rname[2])
X rname += 2;
X
X return((*rtn)(file,rname));
X}
X
X#else /* NO_WALKTREE*/
X
X#ifdef NO_DIRENT
X
X#include <sys/dir.h> /* SunOS 3, etc. */
X#define DIRENTRY struct direct
X
X#else /* NO_DIRENT */
X
X#include <dirent.h> /* Doug Gwyn's dirent routines */
X#define DIRENTRY struct dirent
X
X#endif /* NO_DIRENT */
X
XDIR *opendir();
XDIRENTRY *readdir();
X
Xint
Xwalkdown(rtn,file,filelen,rname)
Xint (*rtn)(/*file,rname*/); /* may also assume fst is set */
Xchar *file, *rname; /* *rname must be *file + n where n < filelen */
Xint filelen;
X{
X DIR *dirp;
X DIRENTRY *dp;
X
X if(stat(file,&fst))
X {
X fprintf(stderr,"shar: Can't access %s\n",file);
X return(1);
X }
X
X if((fst.st_mode & S_IFMT) != S_IFDIR)
X return((*rtn)(file,rname));
X
X if(!(dirp = opendir(file)))
X {
X fprintf(stderr,"shar: unable to open directory %s",file);
X return(1);
X }
X
X if(!strcmp(rname,"."))
X rname += 2; /* avoid "./xxx" when sharing "." */
X
X while((dp = readdir(dirp)))
X if (strcmp(dp->d_name,".") && strcmp(dp->d_name,".."))
X {
X int newlen;
X
X if((newlen = filelen + 1 + strlen(dp->d_name)) >= MAXNAMLEN)
X {
X fprintf(stderr,"shar: file name too long: %s/%s\n",
X file,dp->d_name);
X return(1);
X }
X sprintf(file + filelen,"/%s",dp->d_name);
X
X if(walkdown(rtn,file,newlen,rname))
X return(1);
X
X file[filelen] = '\0'; /* in case we print any error messages */
X }
X
X#ifdef CLOSEDIR_VOID
X closedir(dirp);
X#else /* CLOSEDIR_VOID */
X if(closedir(dirp))
X {
X fprintf(stderr,"shar: unable to close directory %s",file);
X return(1);
X }
X#endif /* CLOSEDIR_VOID */
X return(0);
X}
X
Xint
Xwalktree(rtn,rootname) /* real walktree */
Xint (*rtn)(/*file,rname*/); /* may also assume fst is set */
Xchar *rootname;
X{
X char file[MAXNAMLEN];
X int filelen;
X register char *rname;
X
X if((filelen = strlen(rootname)) >= MAXNAMLEN)
X {
X fprintf(stderr,"shar: file name too long: %s\n",rootname);
X return(1);
X }
X strcpy(file,rootname);
X
X if(FileStrip)
X { /* use just the filename */
X rname = file + filelen;
X while(rname > file && *rname != '/')
X --rname;
X if(*rname == '/')
X ++rname;
X }
X else
X rname = file;
X if(!strncmp(rname,"./",2) && rname[2])
X rname += 2;
X
X return(walkdown(rtn,file,filelen,rname));
X}
X
X#endif /* NO_WALKTREE */
X
Xint
Xonecheck(file,rname)
Xchar *file, *rname;
X{
X if(access(file,04))
X {
X fprintf(stderr,"shar: Can't access %s\n",file);
X return(1);
X }
X
X return(0);
X}
X
Xint
Xoneheader(file,rname)
Xchar *file, *rname;
X{
X fprintf(fpout,"# %6ld %s %s\n",fst.st_size,
X mode_map(fst.st_mode & ~(S_ISUID|S_ISGID|S_ISVTX),(char *)0),rname);
X return(0);
X}
X
Xheader(argc,argv)
Xchar **argv;
X{
Xint i;
XFILE *fpsource; /* pipe temp */
Xchar s128[128];
Xlong now;
Xstruct tm *utc;
Xstruct tm *gmtime();
X
X /* see if any conflicting options */
X if(limit && !filenum)
X { /* can't rename what you don't have */
X fprintf(stderr,"Can't use -l or -L option without -o\n");
X helpuser();
X return(1);
X }
X
X for(i = 0; i < argc; i++)
X { /* skip positional parameters */
X if(PosParam &&
X (strcmp(argv[i],"-B") == 0 ||
X strcmp(argv[i],"-T") == 0 ||
X strcmp(argv[i],"-C") == 0))
X continue;
X
X if(walktree(onecheck,argv[i]))
X return(1);
X }
X
X if(Archive_name)
X {
X fprintf(fpout,"Submitted-by: %s\n",submitter);
X fprintf(fpout,"Archive-name: %s%s%02d\n\n",
X sharname,(strchr(sharname,'/')) ? "" : "/part",
X (filenum) ? filenum : 1);
X }
X
X if(Cut)
X fputs(CutMessage,fpout);
X fputs("#!/bin/sh\n",fpout);
X if(sharname)
X fprintf(fpout,"# This is %s, a shell archive (produced by shar %s)\n",
X sharname,revision);
X else
X fprintf(fpout,"# This is a shell archive (produced by shar %s)\n",
X revision);
X fputs("# To extract the files from this archive, save it to a file, remove\n",
X fpout);
X fputs("# everything above the \"!/bin/sh\" line above, and type \"sh file_name\".\n#\n",
X fpout);
X
X time(&now);
X utc = gmtime(&now);
X fprintf(fpout,"# made %02d/%02d/%04d %02d:%02d UTC by %s\n",
X utc->tm_mon + 1,utc->tm_mday,utc->tm_year + 1900,
X utc->tm_hour,utc->tm_min,
X submitter);
X
X#if defined(SYS5)
X if(!(fpsource = popen("/bin/pwd","r")))
X return(-1);
X fgets(s128,sizeof(s128),fpsource);
X s128[strlen(s128) - 1] = 0;
X fclose(fpsource);
X#else
X#if defined(BSD42) || defined(sun)
X getwd(s128);
X#else
X#include "Need_conditional_compile_fix"
X#endif
X#endif
X fprintf(fpout,"# Source directory %s\n",s128);
X
X fprintf(fpout,"#\n# existing files %s\n",
X (eXists) ? "will NOT be overwritten unless -c is specified"
X : ((InterOW) ? "MAY be overwritten"
X : "WILL be overwritten"));
X
X if(InterOW)
X fputs("# The unsharer will be INTERACTIVELY queried.\n",fpout);
X
X if(Vanilla)
X {
X fputs("# This format requires very little intelligence at unshar time.\n",fpout);
X fputs("# ",fpout);
X if(eXists || Split)
X fputs("\"if test\", ",fpout);
X if(Split)
X fputs("\"cat\", \"rm\", ",fpout);
X fputs("\"echo\", \"true\", and \"sed\" may be needed.\n",fpout);
X }
X
X if(Split)
X { /* may be split, explain */
X fputs("#\n",fpout);
X TypePos = ftell(fpout);
X fprintf(fpout,"%-75s\n%-75s\n","#","#");
X }
X
X fputs("#\n# This shar contains:\n",fpout);
X fputs("# length mode name\n",fpout);
X fputs("# ------ ---------- ------------------------------------------\n",
X fpout);
X for(i = 0; i < argc; i++)
X { /* output names of files but not parameters */
X if(PosParam &&
X (strcmp(argv[i],"-B") == 0 ||
X strcmp(argv[i],"-T") == 0 ||
X strcmp(argv[i],"-C") == 0))
X continue;
X if(walktree(oneheader,argv[i]))
X exit(1);
X }
X fputs("#\n",fpout);
X
X setTOUCH();
X
X if(Split)
X { /* now check the sequence */
X fputs("if test -r _shar_seq_.tmp; then\n",fpout);
X fputs("\techo 'Must unpack archives in sequence!'\n",fpout);
X fputs("\techo Please unpack part `cat _shar_seq_.tmp` next\n",fpout);
X fputs("\texit 1\nfi\n",fpout);
X }
X return(0);
X}
X
X#define MAX_MKDIR_ALREADY 128 /* ridiculously enough */
Xchar *mkdir_already[MAX_MKDIR_ALREADY];
Xint mkdir_already_count = 0;
X
Xvoid
Xgen_mkdir(path)
Xchar *path;
X{
Xregister int ialready;
Xchar *cptr;
X
X/* if already generated code for this dir creation, don't do again */
X for(ialready = 0; ialready < mkdir_already_count; ialready++)
X {
X if(!strcmp(path,mkdir_already[ialready]))
X return;
X }
X
X/* haven't done this one */
X if(mkdir_already_count == MAX_MKDIR_ALREADY)
X {
X fprintf(stderr,"too many directories for mkdir generation\n");
X exit(255);
X }
X if(!(cptr = mkdir_already[mkdir_already_count++] = malloc(strlen(path)+1)))
X {
X fprintf(stderr,"out of memory for mkdir generation\n");
X exit(255);
X }
X strcpy(cptr,path);
X
X/* generate the text */
X fprintf(fpout,"if test ! -d '%s'; then\n",path);
X if(Verbose)
X fprintf(fpout," echo 'x - creating directory %s'\n",path);
X fprintf(fpout," mkdir '%s'\n",path);
X fputs("fi\n",fpout);
X
X} /* end of gen_mkdir */
X
Xvoid
Xgen_mkdir_script(path)
Xregister char *path;
X{
Xregister char *cptr;
X
X for(cptr = strchr(path,'/'); cptr; cptr = strchr(cptr + 1,'/'))
X {
X /* avoid empty string if leading or double '/' */
X if(cptr == path || *(cptr - 1) == '/')
X continue;
X /* omit '.' */
X if((*(cptr - 1) == '.') && ((cptr == path + 1) || (*(cptr - 2) == '/')))
X continue;
X *cptr = 0; /* temporarily terminate string */
X gen_mkdir(path);
X *cptr = '/';
X }
X} /* end of gen_mkdir_script */
X
Xint
Xshar(file,RstrName)
Xchar *file, *RstrName;
X{
Xchar line[BUFSIZ];
XFILE *fpsource;
Xlong cursize,remaining,ftell();
Xint split = 0; /* file split flag */
Xchar *filetype; /* text or binary */
Xstruct tm *lt;
Xchar *filename_base;
X
X /* check to see that this is still a regular file and readable */
X if((fst.st_mode & S_IFMT) != S_IFREG)
X { /* this is not a regular file */
X fprintf(stderr,"shar: %s is not a regular file\n",file);
X return(1);
X }
X if(access(file,04))
X {
X fprintf(stderr,"shar: Can't access %s\n",file);
X return(1);
X }
X
X /* if limit set, get the current output length */
X if(limit)
X {
X cursize = ftell(fpout);
X remaining = (limit * 1024L) - cursize;
X DeBug("In shar: remaining size %ld\n",remaining);
X
X if(!Split && cursize > EndHeadPos &&
X (Binary ? fst.st_size + fst.st_size/3 : fst.st_size) > remaining)
X { /* change to another file */
X DeBug("Newfile, remaining %ld, ",remaining);
X DeBug("limit still %d\n",limit);
X
X /* close the "&&" and report an error if any of the above failed */
X fprintf(fpout,"true || echo 'restore of %s failed'\n",RstrName);
X
X fprintf(fpout, "echo End of part %d, continue with part %d\n",
X filenum,filenum + 1);
X fputs("exit 0\n",fpout);
X
X fclose(fpout);
X
X /* Clear mkdir_already in case the user unshars out of order */
X while (mkdir_already_count > 0)
X free(mkdir_already[--mkdir_already_count]);
X
X /* form the next filename */
X sprintf(filename,"%s%02d",outname,++filenum);
X fpout = fopen(filename,"w");
X if(Verbose)
X fprintf(stderr,"Starting file %s\n",filename);
X
X if(Archive_name)
X {
X fprintf(fpout,"Submitted-by: %s\n",submitter);
X fprintf(fpout,"Archive-name: %s%s%02d\n\n",
X sharname,(strchr(sharname,'/')) ? "" : "/part",
X (filenum) ? filenum : 1);
X }
X
X if(Cut)
X fputs(CutMessage,fpout);
X
X fputs("#!/bin/sh\n",fpout);
X fprintf(fpout,"# This is part %02d of %s\n",
X filenum,(sharname) ? sharname : "a multipart archive");
X
X setTOUCH();
X
X EndHeadPos = ftell(fpout);
X }
X }
X
X fprintf(fpout,"# ============= %s ==============\n",RstrName);
X
X gen_mkdir_script(RstrName);
X
X /* if mixed, determine the file type */
X if(Mixed)
X {
X int count;
X sprintf(line,"file %s | egrep -c \"text|shell\"",file);
X fpsource = popen(line,"r");
X fscanf(fpsource,"%d",&count);
X pclose(fpsource);
X Binary = (count != 1);
X }
X
X if(Binary)
X { /* fork a uuencode process */
X static int pid,pipex[2];
X
X pipe(pipex);
X fflush(fpout);
X
X if(pid = fork())
X { /* parent, create a file to read */
X if(pid < 0)
X {
X fprintf(stderr,"could not fork!\n");
X exit(1);
X }
X close(pipex[1]);
X fpsource = fdopen(pipex[0],"r");
X filetype = (Compress ? "Compressed" : "Binary");
X }
X else
X { /* start writing the pipe with encodes */
X FILE *outptr;
X
X if(Compress)
X {
X sprintf(line, "compress -b%d < '%s'", CompressBits, file);
X fpsource = popen(line, "r");
X }
X else
X fpsource = fopen(file, "rb");
X outptr = fdopen(pipex[1],"w");
X fprintf(outptr,"begin 600 %s\n",
X (Compress ? "_shar_cmp_.tmp" : RstrName));
X encode(fpsource,outptr);
X fprintf(outptr,"end\n");
X if(Compress)
X pclose(fpsource);
X else
X {
X fclose(fpsource);
X }
X exit(0);
X }
X }
X else
X {
X fpsource = fopen(file,"r");
X filetype = "Text";
X }
X
X if(fpsource)
X {
X /* protect existing files */
X if(eXists)
X {
X fprintf(fpout,"if test -f '%s' -a X\"$1\" != X\"-c\"; then\n",
X RstrName);
X if(InterOW)
X {
X fputs("\tcase $wish in\n",fpout);
X fprintf(fpout,"\tA*|a*) echo 'x - overwriting %s';;\n",
X RstrName);
X fprintf(fpout,
X "\t*) echo '? - overwrite %s -- [No], [Y]es, [A]ll, [Q]uit? '\n",
X RstrName);
X fputs("\t\tread wish;;\n",fpout);
X fputs("\tesac\n",fpout);
X fputs("\tcase $wish in\n",fpout);
X fputs("\tQ*|q*) echo aborted; exit 86;;\n",fpout);
X fputs("\tA*|a*|Y*|y*) x=Y;;\n",fpout);
X fputs("\t*) x=N;;\n",fpout);
X fputs("\tesac\n",fpout);
X fputs("else\n",fpout);
X fputs("\tx=Y\n",fpout);
X fputs("fi\n",fpout);
X fputs("if test $x != Y; then\n",fpout);
X fprintf(fpout,"\techo 'x - skipping %s'\n",RstrName);
X }
X else
X fprintf(fpout,"\techo 'x - skipping %s (File already exists)'\n",
X RstrName);
X if (Split)
X fputs("\trm -f _shar_wnt_.tmp\n",fpout);
X fputs("else\n",fpout);
X if (Split)
X fputs("> _shar_wnt_.tmp\n",fpout);
X }
X
X fprintf(stderr,"shar: saving %s (%s)\n",file,filetype);
X if(Verbose)
X { /* info on archive and unpack */
X fprintf(fpout,"echo 'x - extracting %s (%s)'\n",
X RstrName,filetype);
X }
X if(Binary)
X { /* run sed through uudecode (via temp file if might get split) */
X fprintf(fpout, "sed 's/^%c//' << '%s' %s &&\n",
X PREFIX,Delim,
X (AvoidPipes ? "> _shar_tmp_.tmp" : "| uudecode"));
X }
X else
X { /* just run it into the file */
X fprintf(fpout,"sed 's/^%c//' << '%s' > '%s' &&\n",
X PREFIX,Delim,RstrName);
X }
X while(fgets(line,BUFSIZ,fpsource))
X { /* output a line and test the length */
X if(OptPREFIX && isgraph(line[0]) && line[0] != PREFIX
X#ifdef STRCMP_IS_FAST
X && strcmp(line,Delim)
X#else /* STRCMP_IS_FAST */
X && (line[0] != Delim[0] || strcmp(line,Delim))
X#endif /* STRCMP_IS_FAST */
X#ifdef STRNCMP_IS_FAST
X && strncmp(line,"exit 0",6) /* See unshar -e: avoid "exit 0" */
X && strncmp(line,"From",4) /* Don't let mail prepend a ">" */
X#else /* STRNCMP_IS_FAST */
X && (line[0] != 'e' /* See unshar -e: avoid "exit 0" */
X || strncmp(line,"exit 0",6))
X && (line[0] != 'F' /* Don't let mail prepend a ">" */
X || strncmp(line,"From",4))
X#endif /* STRNCMP_IS_FAST */
X )
X fputs(line,fpout);
X else
X {
X fprintf(fpout,"%c%s",PREFIX,line);
X --remaining; /* count PREFIX (in case Split is in effect) */
X }
X#ifdef MSDOS /* This probably doesn't work but accounts for some old code */
X if(Split && (remaining -= strlen(line) + 1) < 0) /* 1 extra for CR */
X#else /* MSDOS */
X if(Split && (remaining -= strlen(line)) < 0)
X#endif /* MSDOS */
X { /* change to another file */
X DeBug("Newfile, remaining %ld, ",remaining);
X DeBug("limit still %d\n",limit);
X
X if(line[strlen(line) - 1] != '\n')
X fputc('\n',fpout);
X
X fprintf(fpout,"%s\n",Delim);
X
X /* close the "&&" and report an error if any of the above failed */
X fprintf(fpout,"true || echo 'restore of %s failed'\n",RstrName);
X
X if (eXists)
X fputs("fi\n",fpout);
X
X if(Verbose)
X { /* output some reassurance */
X fprintf(fpout, "echo 'End of %s part %d'\n",
X (sharname) ? sharname : "",filenum);
X fprintf(fpout, "echo 'File %s is continued in part %d'\n",
X RstrName,filenum + 1);
X }
X else
X fprintf(fpout,
X "echo 'End of part %d, continue with part %d'\n",
X filenum,filenum + 1);
X fprintf(fpout,"echo %d > _shar_seq_.tmp\n",filenum + 1);
X fputs("exit 0\n",fpout);
X
X if(filenum == 1)
X { /* rewrite the info lines on the firstheader */
X fseek(fpout,TypePos,0);
X fprintf(fpout,"%-75s\n%-75s\n",
X "# This is part 1 of a multipart archive",
X "# do not concatenate these parts, unpack them in order with /bin/sh");
X }
X fclose(fpout);
X
X /* form the next filename */
X sprintf(filename,"%s%02d",outname,++filenum);
X fpout = fopen(filename,"w");
X
X if(Archive_name)
X {
X fprintf(fpout,"Submitted-by: %s\n",submitter);
X fprintf(fpout,"Archive-name: %s%s%02d\n\n",
X sharname,(strchr(sharname,'/')) ? "" : "/part",
X (filenum) ? filenum : 1);
X }
X
X if(Cut)
X fputs(CutMessage,fpout);
X fputs("#!/bin/sh\n",fpout);
X
X fprintf(fpout,"# this is %s (part %d of %s)\n",
X ((filename_base = strrchr(filename,'/'))
X ? filename_base + 1
X : filename),
X filenum,
X (sharname) ? sharname : "a multipart archive");
X fputs(
X "# do not concatenate these parts, unpack them in order with /bin/sh\n",
X fpout);
X fprintf(fpout,"# file %s continued\n#\n",RstrName);
X
X setTOUCH();
X
X fputs("if test ! -r _shar_seq_.tmp; then\n",fpout);
X fputs("\techo 'Please unpack part 1 first!'\n",fpout);
X fputs("\texit 1\nfi\n",fpout);
X fputs("(read Scheck\n",fpout);
X fprintf(fpout," if test \"$Scheck\" != %d; then\n",filenum);
X fputs("\techo Please unpack part \"$Scheck\" next!\n",
X fpout);
X fputs("\texit 1\n",fpout);
X fputs(" else\n\texit 0\n fi\n",fpout);
X fputs(") < _shar_seq_.tmp || exit 1\n",fpout);
X
X if(eXists)
X if(Verbose)
X { /* keep everybody informed */
X fputs("if test ! -f _shar_wnt_.tmp; then\n",fpout);
X fprintf(fpout,"\techo 'x - still skipping %s'\n",
X RstrName);
X fputs("else\n",fpout);
X }
X else
X fputs("if test -f _shar_wnt_.tmp; then\n",fpout);
X
X if(Verbose)
X { /* keep everybody informed */
X fprintf(stderr,"Starting file %s\n",filename);
X fprintf(fpout,"echo 'x - continuing file %s'\n",RstrName);
X }
X fprintf(fpout,"sed 's/^%c//' << '%s' >> '%s' &&\n",
X PREFIX,Delim,
X (Binary ? "_shar_tmp_.tmp" : RstrName));
X remaining = limit * 1024L;
X split = 1;
X }
X }
X
X (void) fclose(fpsource);
X while(wait((int *)0) >= 0)
X ;
X
X if(line[strlen(line) - 1] != '\n')
X fputc('\n',fpout);
X
X fprintf(fpout,"%s\n",Delim);
X if(split && Verbose)
X fprintf(fpout,"echo 'File %s is complete' &&\n",RstrName);
X
X /* if this file was uuencoded w/Split, decode it and drop the temp */
X if(Binary && AvoidPipes)
X {
X if(Verbose)
X fprintf(fpout,"echo 'uudecoding file %s' &&\n",RstrName);
X fputs("uudecode < _shar_tmp_.tmp && rm -f _shar_tmp_.tmp &&\n",fpout);
X }
X
X /* if this file was compressed, uncompress it and drop the temp */
X if(Compress)
X {
X if(Verbose)
X fprintf(fpout,"echo 'uncompressing file %s' &&\n",RstrName);
X fprintf(fpout,
X "compress -d < _shar_cmp_.tmp > '%s' && rm -f _shar_cmp_.tmp &&\n",
X RstrName);
X }
X
X if(Touch)
X {
X /* set the dates as they were */
X lt = localtime(&fst.st_mtime);
X fprintf(fpout,"$TOUCH -am %02d%02d%02d%02d%02d '%s' &&\n",
X lt->tm_mon + 1,
X lt->tm_mday,
X lt->tm_hour,
X lt->tm_min,
X lt->tm_year,
X RstrName);
X }
X
X if(Vanilla)
X {
X /* close the "&&" and report an error if any of the above failed */
X fprintf(fpout,"true || echo 'restore of %s failed'\n",RstrName);
X }
X else
X {
X /* set the permissions as they were */
X fprintf(fpout,"chmod %04o %s ||\n",
X fst.st_mode & 00777,RstrName);
X
X /* report an error if any of the above failed */
X fprintf(fpout,"echo 'restore of %s failed'\n",RstrName);
X
X if(Wc_c)
X { /* validate the transferred file */
X FILE *pfp;
X char command[BUFSIZ];
X
X sprintf(command,"%s '%s'",WC,file);
X if((pfp = popen(command,"r")))
X {
X char wc[BUFSIZ];
X
X fscanf(pfp,"%s",wc);
X fprintf(fpout,"Wc_c=\"`%s '%s'`\"\n",WC,RstrName);
X fprintf(fpout,"test %s -eq \"$Wc_c\" ||\n",wc);
X fprintf(fpout,
X "\techo '%s: original size %s, current size' \"$Wc_c\"\n",
X RstrName, wc);
X pclose(pfp);
X }
X }
X }
X
X /* if the exists option is in place close the if */
X if(eXists)
X {
X if (Split)
X fputs("rm -f _shar_wnt_.tmp\n",fpout);
X
X fputs("fi\n",fpout);
X }
X
X return(0);
X }
X else
X {
X fprintf(stderr,"shar: Can't open %s (%s): ",file,filetype);
X perror("");
X return(1);
X }
X}
X
X
Xchar *helpinfo[] =
X{
X "-V produce \"vanilla\" shars demanding little of the unshar environment",
X "-v verbose messages OFF while executing",
X "-m restore file modification dates & times with \"touch\" commands",
X "-w don't check with 'wc -c' after unpack",
X "-a generate Submitted-by: & Archive-name: headers",
X "-nXXX use XXX as the name of the archive (documentation)",
X "-s override automatically determined submitter name",
X "-x overwrite existing files without checking if they already exist",
X "-X interactively overwrite existing files (NOT FOR NET SHARS)",
X "-B treat all files as binary, use uuencode",
X "-T treat all files as text (default)",
X "-C compress and uuencode all files",
X "-bXX pass -bXX (default 12) to compress when compressing (implies -C)",
X "-p allow positional parameter options. The options \"-B\" and \"-T\"",
X " and \"-C\" may be embedded, and files to the right of the",
X " option will be processed in the specified mode",
X "-M mixed mode. Determine if the files are text or",
X " binary and archive correctly.",
X "-P use temp files instead of pipes in the shar file",
X "-F force the prefix character on every line (even if not required)",
X "-c start the shar with a cut line",
X "-f restore by filename only, rather than path",
X "-dXXX use XXX to delimit the files in the shar",
X "-oXXX (or -o XXX) output to file XXX.01 thru XXX.nn",
X "-lXX limit output file size to XXk bytes (but don't split files)",
X "-LXX limit output file size to XXk bytes (may split files)",
X "-S read files to wrap from stdin, ignoring argument line",
X "\nThe -S option reads filenames one per line from stdin; input",
X "format must be similar to 'find' output, except that if -p",
X "is specified, -B, -T or -C may be used (on lines by themselves)",
X "e.g., find . -type f -print | sort | shar -C -l50 -o /tmp/big",
X "\nThe 'o' option is required if the 'l' or 'L' option is used",
X "The 'n' option is required if the 'a' option is used",
X "\n-a generates sharname/part## headers. If the -a argument contains",
X "a '/', then /part is not appended",
X "The automatic submitter name is trivial: essentially `whoami`@`uname`",
X (char *)0
X};
X
Xhelpuser()
X{ /* output a command format message */
X register char **ptr;
X fprintf(stderr,
X "shar %s\nusage: shar [ options ] file ...\n shar -S [ options ]\n",
X revision);
X for(ptr = helpinfo; *ptr; ptr++)
X fprintf(stderr,"%s\n",*ptr);
X
X exit(1);
X}
X/* vi: set tabstop=4 shiftwidth=4: */
SHAR_EOF
chmod 0644 shar.c ||
echo 'restore of shar.c failed'
Wc_c="`wc -c < 'shar.c'`"
test 38151 -eq "$Wc_c" ||
echo 'shar.c: original size 38151, current size' "$Wc_c"
fi
exit 0
--
==========================================================================
| Richard H. Gumpertz rhg at CPS.COM (913) 642-1777 or (816) 891-3561 |
| Computer Problem Solving, 8905 Mohawk Lane, Leawood, Kansas 66206-1749 |
==========================================================================
More information about the Alt.sources
mailing list