rhg@cpsolv.CPS.COM (Richard H. Gumpertz) (09/09/90)
Submitted-by: rhg@cpsolv.cps.com Archive-name: shar3.43/part02 ---- Cut Here and feed the following to sh ---- #!/bin/sh # This is part 02 of shar3.43 # ============= 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.43"; Xchar RCS_ID[] = "$Header: /u/rhg/src/shar/shar.c,v 3.43 90/08/13 07:50:34 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/*:08-06-1990-00:40-rhg@cps.com-revised Cut message to be more explanatory */ X/*:08-05-1990-14:04-rhg@cps.com-merged Rname into walktree */ X/*:08-05-1990-12:11-rhg@cps.com-added walktree & support for sharing dirs */ X/*:08-05-1990-09:05-rhg@cps.com-change -Bn, -t, and -b to -bn, -T, and -B */ X/*:08-04-1990-15:31-rhg@cps.com-added -Bn to set compress -bn (default 12) */ X/*:08-04-1990-15:31-rhg@cps.com-changed shar3_???_.tmp to _shar_???_.tmp */ X/*:08-04-1990-15:22-rhg@cps.com-added check for "exit 0" under OptPREFIX */ X/*:08-04-1990-14:32-rhg@cps.com-added -m to generate TOUCH (default off) */ X/*:08-04-1990-14:18-rhg@cps.com-reversed the meaning of -x and deleted -O */ X/*:06-14-1990-14:48-rhg@cps.com-made Split and eXists compatible. X/*:06-14-1990-14:18-rhg@cps.com-made -x the default and added -O X/*:06-14-1990-12:44-rhg@cps.com-always terminate the && and report failures X/*:06-14-1990-12:28-rhg@cps.com-clear mkdir_already between -l files X/*:06-14-1990-12:14-rhg@cps.com-change PREFIX from a #define to an int variable. X/*:04-19-1990-22:49-rhg@cps.com-get rid of "set" so "sh sharfil -c" will work */ X/*:04-19-1990-21:52-rhg@cps.com-add -F to clear OptPREFIX */ X/*:04-18-1990-08:49-rhg@cps.com-add OptPREFIX (for now, always on) */ X/*:07-09-1990-19:24-wht@n4hgf-back to fgrep amc -- fits more -m touches */ X/*:07-01-1990-18:37-wht@n4hgf-wait() needed after fork() */ X/*:05-19-1990-02:47-wht@n4hgf-change fgrep amc to mmdd */ X/*:05-16-1990-01:53-wht@n4hgf-Archive-name had extra period sometimes */ X/*:05-10-1990-20:39-wht@n4hgf-altos does not not like at-sign in filenames */ X/*:05-10-1990-13:38-wht@n4hgf-add -V Vanilla mode */ X/*:05-07-1990-00:06-wht@n4hgf-test all mallocs for Purity Of Essence */ X/*:05-07-1990-00:06-wht@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@n4hgf-3.20 rhg@cps.com did all the NICE work */ X/*:04-17-1990-14:30-rhg@cps.com-pretty up if-then-else-fi in shar file */ X/*:04-17-1990-12:13-rhg@cps.com-add Split and renamed old -l to -L */ X/*:04-17-1990-12:13-rhg@cps.com-add -c option to shar file execution */ X/*:04-17-1990-11:20-rhg@cps.com-simplify TOUCH logic in shar file */ X/*:04-17-1990-10:27-rhg@cps.com-create setTOUCH to avoid duplicate code */ X/*:04-17-1990-04:43-rhg@cps.com-add missing && to commands in shar file(s) */ X/*:04-17-1990-02:03-rhg@cps.com-add Compress */ X/*:04-16-1990-17:08-rhg@cps.com-add AvoidPipes as well as code to use pipes */ X/*:04-03-1990-20:09-wht@n4hgf-3.11 */ X/*:04-01-1990-13:20-pat@rwing-correct case on M option in getopt() call */ X/*:04-01-1990-13:50-pat@rwing-change defaults on -v, -w to be on */ X/*:03-29-1990-18:23-wht@n4hgf-add automatic sequent support */ X/*:03-28-1990-15:56-wht@n4hgf-add mode and length net.bandwidth chrome */ X/*:03-28-1990-14:23-wht@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@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@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@gatech.edu) Warren Tucker X * X * 3.11 - Fri Apr 6 14:21:51 EDT 1990 X * With due deference to davidsen@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@n4hgf (Warren Tucker) X * To: davidsen@sixhub X * Subject: shar 3.11 X * X-Bang-Reply-to: gatech!n4hgf!wht -or- emory!tridom!n4hgf!wht X * Reply-to: wht%n4hgf@gatech.edu X * Message-Id: <9004061514.AA01781@n4hgf.UUCP> X * X * 1. changes suggested by pat@rwing (Pat Myrto) and silvert@cs.dal.ca X * (Bill Silvert) X * 2. fixes to who_am_i code in who@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@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{ Xregister unsigned ftype = mode & S_IFMT; 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#include <dirent.h> /* Doug Gwyn's dirent routines */ XDIR *opendir(); Xstruct dirent *readdir(); Xint closedir(); 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 struct dirent *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 if(closedir(dirp)) X { X fprintf(stderr,"shar: unable to close directory %s",file); X return(1); X } 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 (shar %s)\n", X sharname,revision); X else X fprintf(fpout,"# This is a shell archive (shar %s)\n",revision); 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 if(!(filename_base = strrchr(filename,'/'))) X filename_base = filename; X else X ++filename_base; 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 STRNCMP_IS_FAST X && strncmp(line,"exit 0",6)) /* See "unshar -e" */ X#else /* STRNCMP_IS_FAST */ X && (line[0] != 'e' || strncmp(line,"exit 0",6))) /* unshar -e */ X#endif /* STRNCMP_IS_FAST */ 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 if(!(filename_base = strrchr(filename,'/'))) X filename_base = filename; X else X ++filename_base; X X fputs("#!/bin/sh\n",fpout); X fprintf(fpout,"# this is %s (part %d of %s)\n", X filename_base, 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 36835 -eq "$Wc_c" || echo 'shar.c: original size 36835, current size' "$Wc_c" fi exit 0 -- ========================================================================== | Richard H. Gumpertz rhg@CPS.COM (913) 642-1777 or (816) 891-3561 | | Computer Problem Solving, 8905 Mohawk Lane, Leawood, Kansas 66206-1749 | ==========================================================================