[comp.sys.m6809] Latest 'Shar' for OS-9

@lsuc.uucp () (07/25/88)

     This is the latest version of Shar from BIX.  It incorporates
patches by Peter Dibble to make and unpack the latest sharfiles
which don't always have a line prefix if the line is empty.
Other patches are by RJ Fehl to fix various things I broke when
I first ported it from OSK downward to OS-9, and also by myself
for other problems that arose during the port to 6809.  Documentation
is a bit better and the sources are a tad clearer than before as
well.

Cheers! -- Jim O.

With thanks to the whole BIX crew! :-)

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:    Shell Archiver
#	Run the following text with /bin/sh to create:
#	Shar.doc
#	make.os9
#	make.osk
#	shar.h
#	subs.h
#	shar.c
#	sh.arch.c
#	sh.getpat.c
#	sh.index.c
#	sh.shar.c
# By:	Jim Omura ()
cat << \SHAR_EOF > Shar.doc
'shar09.shar'

     SHAR is a utility which combines printable ASCII files
into a single archive file.

     To understand SHAR, you should first understand what a
"sharfile" is.  This may sound backwards, but there is a reason.
Sharfiles existed before SHAR.  This is not magic.  A sharfile
is an Unix convention.  SHAR is short for "shell archive" or
because the standar Unix shell is called sh, "sh archive".
If you are using Unix, you don't need SHAR.  A shell archive
could be constructed by the standard Unix utilities and a
shell script.  Furthermore, to unpack a sharfile you would
only need to type in the filename.  All the embedded commands
would be sufficient to create the files properly.

     So SHAR is primarily a utility for non-Unix computer
users who wish to use a SHAR format to archive files or to
unpack sharfiles from other computers, most of which are
probably running Unix.  Since SHAR is a loose convention rather
than a fixed format, there are many types of sharfiles.  This
utility will unpack many types of sharfiles, but not all.
Similarly, most Shar-like utilities (and of course the standard
Unix 'sh') will unpack sharfiles created with this utility.
But some utilities may have problems with the files this
utility creates.  There are not guarantees of portability.

Revision History:

88/07/24  Patches by RJ Fehl incorporated fixing an old bug.

88/06/22  Patch by Peter Dibble to handle sharfiles without
          line prefixing and other minor changes by Jim Omura.

88/01/27  Ported to OS-9 6809 by Jim Omura.
          This is a *partially* tested version of Shar.
It will create a sharfile and unpack a "shar"files created by
itself, the 68K version, and many other Sharfiles, but not all
the options have been confirmed.

87/05/22  OS-9 version patch by Dieter Stoll to fix 'mkdir' error
          report.

Installation:

OS-9 users (6809 and 68K):

     If you have 'make' and are using OS-9 6809, you can compile
this with 'make -f=make.os9'.  If you are using OS-9 68K you should
be able to modify this make fairly easily.

     If you do not have 'make' you will have to add

#define OS9

to the top of 'shar.h'.  If your 'make' utility defaults
to defining OS9, you should remove the '-dOS9' from the
CFLAGS variable in the 'makefile'.

Other Systems:

NOTE:  The following will no longer work because I have broken
       the program down into smaller source modules.  I have
included these installation instructions to help in the creation
of the necessary 'makefile's. -- Jim O.

        AMIGA:  cc -DAMIGA shar.c
                ln -o shar shar.o -lc

        VMS:    cc/DEFINE=VMS shar.c
                link shar,sys$library:vaxcrtl/lib
                shar :== $dev:[dir]shar

        U**X:   cc -O -DULTRIX -o shar shar.c -lc


General:

	This is an early version of Wecker's Shar.c with "first stage"
porting to OS-9 68K completed.  Use the Unix instructions for now.
The code should be redone in some areas for cleaner logic, but
has been tested and will create a 'shar' file.  Furthermore, it
will unpack many types of shar files.  The only problem I've
uncovered is that it will overwrite existing files without giving
an error.

	Many thanks to Gary Kendall without whom I would have abandonned
the effort.

Cheers! -- Jim Omura

	The following are all the instructions I received:

Creating a shar file:

        AMIGA:  shar >foo.shar -a makefile *.h *.c

        VMS:    create a batch job that executes:
                $ shar -a makefile *.h *.c
                edit the batch .LOG file (which is your shar file)

        U**X:   shar -a makefile *.h *.c >foo.shar

        OS-9    shar file1 file2 file3 >foo.shar

        OSK     shar makefile *.h *.c >foo.shar

                (P.S. the -a switch means do all options)

Extracting from a shar file:

        AMIGA:  shar -u foo.shar

        VMS:    shar -u foo.shar

        U**X:   shar -u foo.shar        or    sh foo.shar

Just typing "shar" will yield a usage line. (Doc: thanks for the code to
base this off of).

        OS-9:   shar -u foo.shar

        OSK:    shar -u foo.shar

For OS-9 and OSK: shar -?
gives usage.

SHAR_EOF
cat << \SHAR_EOF > make.os9
# makefile for OS-9 6809 version of 'shar'
#
# 'sh.index.c' is not used by OS-9 versions

CFLAGS = -dOS9 -r

CFILES = shar.c sh.arch.c sh.getpat.c sh.shar.c
HFILES = /dd/defs/stdio.h /dd/defs/ctype.h  /dd/defs/modes.h /dd/defs/time.h
RFILES = shar.r sh.arch.r sh.getpat.r sh.shar.r

shar: $(RFILES)
  cc1 $(RFILES) -f=shar -m=1k -e=2
  del c.com

$(RFILES): $(HFILES)
  cc1 $*.c $(CFLAGS)

shar.r: shar.c shar.h
  cc1 shar.c $(CFLAGS)

sh.arch.r: sh.arch.c subs.h
  cc1 sh.arch.c $(CFLAGS)

sh.getpat.r: sh.getpat.c subs.h
  cc1 sh.getpat.c $(CFLAGS)

sh.shar.r: sh.shar.c subs.h
  cc1 sh.shar.c $(CFLAGS)
SHAR_EOF
cat << \SHAR_EOF > make.osk
# makefile for OS-9 68000 version of 'shar'
#
# 'sh.index.c' is not used by OS-9 versions

CFLAGS =  -r

CFILES = shar.c sh.arch.c sh.getpat.c sh.shar.c
HFILES = /dd/defs/stdio.h /dd/defs/ctype.h  /dd/defs/modes.h /dd/defs/time.h
RFILES = shar.r sh.arch.r sh.getpat.r sh.shar.r

shar: $(RFILES)
  cc $(RFILES) -f=shar -m=1k -e=2

$(RFILES): $(HFILES)
  cc $*.c $(CFLAGS)

shar.r: shar.c shar.h
  cc shar.c $(CFLAGS)

sh.arch.r: sh.arch.c subs.h
  cc sh.arch.c $(CFLAGS)

sh.getpat.r: sh.getpat.c subs.h
  cc sh.getpat.c $(CFLAGS)

sh.shar.r: sh.shar.c subs.h
  cc sh.shar.c $(CFLAGS)
SHAR_EOF
cat << \SHAR_EOF > shar.h
/* shar.h : part of 'shar' */

#include <stdio.h>

#ifdef  AMIGA
#include <exec/types.h>
extern char *getenv(),*scdir(),*malloc(),*index();
#endif

#ifdef OSK

#include <ctype.h>
#include <time.h>
#include <modes.h>

#define void  int
#define fputc putc

extern char *getenv(), *scdir(), *malloc(), *index();
struct      sgtbuf timebuf, *tpntr;
char        datestr[9];

#endif 

#ifdef OS9

#include <ctype.h>
#include <time.h>
#include <modes.h>

#define void  int
#define fputc putc

extern char *getenv(), *scdir(), *malloc(), *index();
struct      sgtbuf timebuf, *tpntr;
char        datestr[9];

#endif

#ifdef  ULTRIX
#include <sys/types.h>
extern char *getenv(),*scdir(),*malloc(),*index();
#endif

#ifdef  VMS
#include <types.h>
extern char *getenv(),*scdir(),*malloc();
#endif



/* Common Definitions: */

#define BADCH   ((int)'?')
#define EMSG    ""

#define rescanopts()    (optind = 1)

#define SED "sed 's/^%s//'"    /* used to remove prefix from lines */
#define USAGE "[-u archive] [[-a] [-p prefix] [-d delim] [-bcv] files > archive]"
#define OPTSTRING "u:ap:d:bcv"

/* End of shar.h */
SHAR_EOF
cat << \SHAR_EOF > subs.h
/* Subs.h */

/* Note:  This file is *not* the same as 'shar.h', but
 * similar.
 */

#include <stdio.h>

#ifdef  AMIGA
#include <exec/types.h>
extern char *getenv(),*scdir(),*malloc(),*index();
#endif

#ifdef OSK

#include <ctype.h>
#include <time.h>
#include <modes.h>

#define     void    int
#define     fputc   putc

extern char   *getenv(),*scdir(),*malloc(),*index();
extern struct sgtbuf timebuf, *tpntr;
extern char   datestr[9];

#endif 

#ifdef OS9

#include <ctype.h>
#include <time.h>
#include <modes.h>

#define void int
#define fputc putc

extern char   *getenv(), *scdir(), *malloc(), *index();
extern struct sgtbuf timebuf, *tpntr;
extern char   datestr[9];

#endif

#ifdef  ULTRIX
#include <sys/types.h>
extern char *getenv(),*scdir(),*malloc(),*index();
#endif

#ifdef  VMS
#include <types.h>
extern char *getenv(),*scdir(),*malloc();
#endif



/* Common Definitions: */

#define BADCH   ((int)'?')
#define EMSG    ""

#define rescanopts()    (optind = 1)

extern int     optind,             /* index into parent argv vector */
        optopt;                 /* character checked for validity */
extern long    fsize;                  /* length of file */
extern char    *optarg;                /* argument associated with option */
extern char    *sav[100];              /* saved file names */
extern int     savind;                 /* save index */

/* OPTIONS */
extern int     Verbose;           /* provide append/extract feedback */
extern int     Basename;          /* extract into basenames */
extern int     Count;             /* count characters to check transfer */
extern char    *Prefix;        /* line prefix to avoid funny chars */
extern int     UnShar;            /* do we unshar an input file? */

#define SED "sed 's/^%s//'"    /* used to remove prefix from lines */
#define USAGE "[-u archive] [[-a] [-p prefix] [-d delim] [-bcv] files > archive]"
#define OPTSTRING "u:ap:d:bcv"

SHAR_EOF
cat << \SHAR_EOF > shar.c
/* Shar puts readable text files together in a package
   from which they are easy to extract.

    v 880127 Jim Omura, BIX  First OS-9 6809 port.

    v 870421 Jim Omura, BIX  First OS-9 68K port.

    v 860712 D. Wecker for ULTRIX and the AMIGA
        - stripped down.. does patterns but no directories
        - added a -u (unshar) switch
 */

#include "shar.h"

/* Declare Globals: */

int     optind = 1,             /* index into parent argv vector */
        optopt;                 /* character checked for validity */
long    fsize;                  /* length of file */
char    *optarg;                /* argument associated with option */
char    *sav[100];              /* saved file names */
int     savind;                 /* save index */

/* OPTIONS */
int     Verbose = 0;           /* provide append/extract feedback */
int     Basename = 0;          /* extract into basenames */
int     Count = 0;             /* count characters to check transfer */
char    *Delim;                /* put after each file */
char    Filter[100];           /* used to extract archived files */
char    *Prefix = NULL;        /* line prefix to avoid funny chars */
int     UnShar = 0;            /* do we unshar an input file? */

int main(argc, argv)
int argc;
char **argv;    
{

/* Declare Local Constants: */

    char *def_delim = "SHAR_EOF\0";    /* Default Delim string */
    char *def_filt = "cat\0";          /* Default Filter string */

/* Declare Local Variables: */

    auto char *ppchFiles[256];
    register int  C;
    register char **ppchList;
    register int errflg;

/* Reference the Globals: */

    extern char Filter[100];
    extern char *Delim;

/* Initialize Variables: */

    strncpy(Filter,def_filt,4);
    Delim = def_delim;

    errflg = 0;
/*    **ppchList = ppchFiles;    /* This may be wrong -- Jim O. */
    ppchList = ppchFiles;    /* RJF 88-06-24 */

    while(EOF != (C = getopt(argc, argv, OPTSTRING))) {
        switch(C) {
        case 'v': 
            Verbose++; 
            break;
        case 'c': 
            Count++; 
            break;
        case 'b': 
            Basename++; 
            break;
        case 'd': 
            Delim = optarg; 
            break;
        case 'a': /* all the options */
            optarg = "XX";
            Verbose++;
            Count++;
            Basename++;
            /* fall through to set prefix */
        case 'p': 
            (void) sprintf(Filter, SED, Prefix = optarg); 
            break;
        case 'u':
            UnShar++;
            dounshar(optarg);
            break;
        default: 
            errflg++;
        }
    }

    if (UnShar) exit(0);

    C = getarg(argc, argv);
    if (errflg || EOF == C) {
        if (EOF == C)
            fprintf(stderr, "shar: No input files\n");
        fprintf(stderr, "usage: shar %s\n", USAGE);
        exit(1);
    }

    savind = 0;
    do {
        if (getpat(optarg)) exit(2);
    } 
    while (EOF != (C = getarg(argc, argv)));

    sav[savind] = 0;
    header(sav);
    for (ppchList = sav; *ppchList; ++ppchList) shar(*ppchList);
    puts("#\tEnd of shell archive");
    puts("exit 0");
    exit(0);
}

/*
 * get option letter from argument vector
 */
 
int getopt(nargc, nargv, ostr)
int nargc;
char **nargv, *ostr;

{
    register char    *oli;        /* option letter list index */
    static char    *place = EMSG;    /* option letter processing */

    if(!*place)
    {            /* update scanning pointer */
        if(optind >= nargc || *(place = nargv[optind]) != '-' || !*++place)
        {
            return(EOF);
        }
        if (*place == '-')
        {    /* found "--" */
            ++optind;
            return EOF;
        }
    }                /* option letter okay? */
    if ((optopt = (int)*place++) == (int)':' || !(oli = index(ostr,optopt)))
    {
        if(!*place) ++optind;
        tell(*nargv,": illegal option -- ");
    }
    if (*++oli != ':')
    {        /* don't need argument */
        optarg = NULL;
        if (!*place)
            ++optind;
    } 
    else
    {                /* need an argument */
        if (*place) {            /* no white space */
            optarg = place;
        } 
        else if (nargc <= ++optind) {    /* no arg */
            place = EMSG;
            tell(*nargv,": option requires an argument -- ");
        } 
        else {
            optarg = nargv[optind];    /* white space */
        }
        place = EMSG;
        ++optind;
    }
    return optopt;            /* dump back option letter */
} /* End of getopt() */

int
getarg(nargc, nargv)
int nargc;
char **nargv;
{
    if (nargc <= optind) {
        optarg = (char *) 0;
        return EOF;
    } 
    else {
        optarg = nargv[optind++];
        return 0;
    }
} /* End of getarg() */

dounshar(ArcNam)
char *ArcNam;
{
    register int i,j;
    register FILE *inptr,*outptr;
    auto char line[BUFSIZ];
    int DirNum = -1;
    int Prefix = 0;
    char PrefixStr[10];
    char Dirs[5][40],FilNam[128],Delim[40],ScrStr[128];
    char *ptr;

    if (!(inptr = fopen(ArcNam,"r"))) {
        fprintf(stderr,"shar: Can't open archive '%s'\n", ArcNam);
        return;
    }
    while (fgets(line,BUFSIZ,inptr)) {
        if (strncmp(line,"sed ",4) == 0) {
            Prefix = 0;
            if (!(ptr = index(line,'/'))) goto getfil;
            if (*++ptr == '^') ++ptr;
            while (*ptr != '/') PrefixStr[Prefix++] = *ptr++;
            goto getfil;
        }
        else if (strncmp(line,"cat ",4) == 0) {
            Prefix = 0;
            ;
getfil:

#ifdef    VMS
            strcpy(FilNam,"[");
#else
            FilNam[0] = 0;
#endif

            for (i = 0; i <= DirNum; i++) {

#ifdef    VMS
                strcat(FilNam,".");
                strcat(FilNam,Dirs[i]);
#else
                strcat(FilNam,Dirs[i]);
                strcat(FilNam,"/");
#endif

            }


#ifdef    VMS
            strcat(FilNam,"]");
#endif

            getshpar(line, ">", ScrStr);
            strcat(FilNam,ScrStr);
            getshpar(line,"<<",Delim);
            fprintf(stderr,"Creating %s ...",FilNam);
            outptr = fopen(FilNam,"w");

            while (fgets(line,BUFSIZ,inptr)) {
                if (strncmp(line,Delim,strlen(Delim)) == 0) break;
/* Dibble patch 'os.9/utilities' #51, 1988/06/20 */
                if (outptr)
                    if(strncmp(line,PrefixStr,Prefix) == 0)
                        fputs(&line[Prefix],outptr);
                    else
                        fputs(line,outptr);
            }

            if (outptr) {
                fclose(outptr);
                fprintf(stderr,"...done\n");
            }
            else fprintf(stderr,"...error in creating file\n");
        }
        else if (strncmp(line,"mkdir ",6) == 0) {
            fprintf(stderr,"Need to make directory: %s\n",&line[6]);
        }
        else if (strncmp(line,"chdir ",6) == 0) {
            if (line[6] == '.' && line[7] == '.') DirNum--;
            else strcpy(Dirs[++DirNum],&line[6]);
            if (DirNum < -1) DirNum = -1;
        }
        else if (strncmp(line,"cd ",3) == 0) {
            if (line[3] == '.' && line[4] == '.') DirNum--;
            else strcpy(Dirs[++DirNum],&line[3]);
            if (DirNum < -1) DirNum = -1;
        }
    }
    fclose(inptr);

}   /* End of dounshar() */

getshpar(line,sea,par)
char *line,*sea,*par;
{
    register int i,j,k;
    register char *scr1,*scr2;

    while (*line) {
        scr1 = line;
        scr2 = sea;
        while (*scr1 && *scr2 && *scr1 == *scr2) { 
            scr1++; 
            scr2++; 
        }
        if (*scr2 == 0) {
            if (*scr1 == 0) { 
                *par = 0; 
                return; 
            }
            while ( *scr1 == ' ' || *scr1 == '\t' ||
                *scr1 == '\\' || *scr1 == '\'' || *scr1 == '"') scr1++;
            while ( *scr1 != 0 && *scr1 != ' ' && *scr1 != '\t' &&
                *scr1 != '\\' && *scr1 != '\'' && *scr1 != '"' &&
                *scr1 != '\n' && *scr1 != '\r') *par++ = *scr1++;
            *par = 0;
            return;
        }
        line++;
    }
    *par = 0;

}   /* End of getshpar() */

/* tell() */

int tell(nargv,s)

char *nargv;
char *s;

{
    fputs(nargv,stderr);
    fputs((s),stderr);
    fputc(optopt,stderr);
    fputc('\n',stderr);
    return(BADCH);
}   /* End of tell() */

/* ctime() */

#ifdef OS9
char   *ctime()
{
    extern struct sgtbuf timebuf;
    extern char          datestr[];
sprintf (datestr,"%2d/%2d/%2d\n",timebuf.t_year,timebuf.t_month,timebuf.t_day);
    return(*datestr);
}
#endif

/* End of Shar.c */
SHAR_EOF
cat << \SHAR_EOF > sh.arch.c
/* sh.arch.c */

#include "subs.h"

/* header() */
 
int header(ppchFiles)

char *ppchFiles[];
{
#ifndef OSK
    extern char *ctime();
#endif
    register int i;
    auto long clock;
    register int  problems = 0;
    register char **ppchList;
    register char *pchOrg = "";  /* getenv("ORGANIZATION"); */
    register char *pchName = "Jim Omura"; /* getenv("NAME"); */

    puts("#\tThis is a shell archive.");
    puts("#\tRemove everything above and including the cut line.");
    puts("#\tThen run the rest of the file through sh.");
    puts("#----cut here-----cut here-----cut here-----cut here----#");
    puts("#!/bin/sh");
    puts("# shar:    Shell Archiver");
    puts("#\tRun the following text with /bin/sh to create:");
    for (ppchList = ppchFiles; *ppchList; ++ppchList)
        printf("#\t%s\n", *ppchList);

#ifdef OSK
    getime(&timebuf);   /* Finish this later--Jim O. */
/*  printf("# This archive created: %s\n",ctime()); /* No Ctime() */
#endif
#ifdef OS9
    getime(&timebuf);
    printf("# This archive created: %s\n",ctime());
#endif

#ifdef AMIGA
    (void) time(& clock);
    printf("# This archive created: %s", ctime(&clock));
#endif

    if (pchName)
        printf("# By:\t%s (%s)\n", pchName, pchOrg ? pchOrg : "JO");
    return(0);

} /* End of header() */

/* archive() */

int archive(input, output)

char *input, *output;

{
    extern char Filter[100];
    extern char *Delim;

    auto char line[BUFSIZ];
    register FILE *ioptr;

    ioptr = fopen(input, "r");

    if (ioptr != 0)
    {
        printf("%s << \\%s > %s\n", Filter, Delim, input);

        while(fgets(line, BUFSIZ, ioptr))
        {
            if (Prefix)
            {
                fputs(Prefix, stdout);
            }

            fputs(line, stdout);

            if (Count)
            {
                fsize += strlen(line);
            }
        }   /* End while */
 
        fputs(Delim, stdout);
        fputc('\n', stdout);
        fclose(ioptr);
        return(0);
    } 
    else
    {
        fprintf(stderr, "shar: Can't open '%s'\n", input);
        return(1);
    }
 
} /* End of archive() */
SHAR_EOF
cat << \SHAR_EOF > sh.getpat.c
/* sh.getpat.c */

#include "subs.h"

getpat(pattern)
char *pattern;
{
    register char *ptr;

#ifdef AMIGA
    while (ptr = scdir(pattern)) {
#else
    ptr = pattern;
    {
#endif
        sav[savind] = malloc(strlen(ptr)+1);
        strcpy(sav[savind++],ptr);
#ifdef  OSK
        if (access(ptr,S_IOREAD) || access(ptr,S_IREAD))
        {
/* The logic here is mucked up.  Change it later.--Jim O. */
        }
        else {
#endif
#ifdef OS9
        if ((access(ptr,S_IOREAD)==0)  || (access(ptr,S_IREAD)==0))
        {
        /* do continue */
        }
        else {
#endif
#ifdef AMIGA
/* *** This option for everything *except OS-9 versions -- Jim O. */
        if (access(ptr,4)) {
#endif
            printf("No read access for file: %s\n",ptr);
            return(-1);
        }
    }
    return(0);
} /* End of getpat() */

SHAR_EOF
cat << \SHAR_EOF > sh.index.c
/* sh.index.c */

/* Part of Shar.c
 *
 * Only for VMS version
 */

#ifdef  VMS
char *index(s,c)
char    *s;
char    c;
{
    while (*s != 0 && *s != c) s++;
    if (*s == 0 && *s != c) s = 0;
    return(s);    
} /* end of index() */
#endif
SHAR_EOF
cat << \SHAR_EOF > sh.shar.c
/* sh.shar.c */

#include "subs.h"

void shar(file)

char *file;

{

    register char *basefile;

/*    *basefile = file;          /* This looks wrong?? -- Jim O */
    *basefile = *file;          /* Correction??88/07/24 -- Jim O */

    if (!strcmp(file, "."))
    {
        return;
    }

    fsize = 0;

    if (Basename)
    {
        while(*basefile)
        {
            basefile++;        /* go to end of name */
        }

        while(basefile > file && *(basefile-1) != '/')
        {
            basefile--;
        }
    }

    if (Verbose)
    {
        printf("echo shar: extracting %s\n", basefile);
    }

    if (archive(file, basefile))
    {
        exit(66);
    }

    if (Count)
    {
        printf("if test %ld -ne \"`wc -c %s`\"\n",fsize,basefile);
        printf("then\necho shar: error transmitting %s ",basefile);
        printf("'(should have been %ld characters)'\nfi\n",fsize);
    }

}   /* end of shar() */

/* End of sh.shar.c */

SHAR_EOF
#	End of shell archive
exit 0
-- 
Jim Omura, 2A King George's Drive, Toronto, (416) 652-3880
ihnp4!utzoo!lsuc!jimomura
Byte Information eXchange: jimomura