[comp.os.minix] New compression program: COMIC

ast@cs.vu.nl (Andy Tanenbaum) (07/04/90)

Since we are on the subject of compression, here is another compression
program written by one of my students, Jan Mark Wams.  I haven't had time
to look at it, but he's a bright fellow.  Please give it a try and post
results. I would normally have sent this to the referees list, but I am leaving
for England Friday 6 July, and won't be back until the 14th, at which time
I grab 40 winks and am back to the airport again. I'll really be back in 
August.

Andy Tanenbaum (ast@cs.vu.nl)

-------------------------------------------------------------------------
	This is the comic 1.0B compression program. COMIC stands for 
	COmpress MInix C-source. It does however compress other data
	quite well. It is a true UN*X program in spirit: it can read from
	pipes, etc. It's pure C (except for the minix .s file, containing
	a fast memrchr() routine.) and (thus) runs on UN*X, AMOEBA, etc.

	It's performance is better than (16-bit) compress.

	It's performance is slightly better than lharc, but compressing
	takes more time. Decompressing is fast. The program isn't yet
	optimal, (It could use some dynamic huffman coding) and should
	become even better in time.

	There are no restrictions on using this program. The next table
	says it all. 


                      | COMPRESS        | LHARC         | COMIC
----------------------+-----------------+---------------+-------------
                      |                 |               |
 COMPRESSION          | GOOD            | BETTER        | BEST
                      |                 |               |
 USAGE                | SIMPLE          | COMPLEX       | SIMPLE
                      |                 |               |
 WORKS WITH PIPES     | YES             | NO            | YES
                      |                 |               |
 FILE INFO SAVED      | NO              | YES           | NO
                      |                 |               |
 MAKES BACKUPS        | NO              | ALWAYS        | NO
                      |                 |               |
 COMPRESSION SPEED    | FAST            | NOT TOO FAST  | SLOW
                      |                 |               |
 DECOMPRESSON SPEED   | FAST            | FAST          | FAST
                      |                 |               |           
 MINIX VERSION        | 13-bit          | YES           | YES
                      |                 |               |
 LINTABLE             | A BIT           | A BIT         | YES
                      |                 |               |
 VERBOSE OUTPUT       | OPTION          | SUPRESSABLE   | OPTIONS
                      |                 |               |
 CRC-CHECK            | NO              | DON'T KNOW    | NO
                      |                 |               |
 BYTE ORDER PORBLEMS  | FIXED           | FIXED         | NON
                      |                 |               |
 WELL KNOWN           | YES             | SOME WHAT     | NOT YET ;-)
                      |                 |               |
 OTHER OS             | OTHER SOURCE    | OTHER SOURCE  | SAME SOURCE
                      |                 |               |           
 SUFFIX               | .Z              | .lhz          | -X          
                      |                 |               |           

Simple comparison:

       142368 foo.a
        72907 foo.a.13Z
        68614 foo.Z.a
        69321 foo.a.Z
        50668 foo.lzh
        48286 foo-X.a
        47331 foo.a-X

Foo.a being:

	lharc.c
	lhdir.c
	lhio.c
	lzhuf.c
	lharc.o
	lhio.o
	lzhuf.o

	Since COMIC will compress beter than compress, and runs in 
	64K, it is very usable under (PC|XT|MAC)MINIX.

	All the service LHarc offers is nice, but it doesn't leave
	the user much of a choice. If I want optimal compression,
	I use (t|sh)ar and comic, if I want optimal flexiability,
	I use comic and (t|sh)ar so as to keep the files separated.
	And the CRC-check service, is nice, but using crc under 
	MINIX (UN*X, etc.) is an option here.

	I short my opinion is, LHarc is much too (do it yourself-) DOSy.
	I perfer:
			xcat foo | more		# I know about pipes.
	over:
			lharc pvmore foo	# I know about the pv option.


				 (:>	jms@cs.vu.nl  [Jan mark Wams]
				(_)
			========""======


: This is a shar archive.  Extract with sh, not csh.
: This archive ends with exit, so do not worry about trailing junk.
: --------------------------- cut here --------------------------
PATH=/bin:/usr/bin:/usr/ucb
echo Extracting 'READ_ME'
sed 's/^X//' > 'READ_ME' << '+ END-OF-FILE ''READ_ME'
X
XThis dir contains the source files for COMIC 1.0B
X
X		COMIC stands for COmpress MInix C.
X
XIt contains huffman tables (tables.h) that are tuned to the tippical MINIX
XC-program source.
X
XUsage: comic [-cfvvV?] [<file>[-X]...]
XUsage: decomic [-cfvV?] [<file>[-X]...]
XUsage: xcat [-vV?] [<file>[-X]...]
X
XEg.:
X	comic -V		# print the version number of comic.
X	comic -vv <file>	# compress the file, and print the savings.
X	comic -df <file>	# decomic the file overwriting <file>.
X	comic -c <file>		# comic the file to stout.
X	decomic			# decomic stdin.
X	xcat foo		# decomic foo-X to stout.
X
XLink comic to decomic, and decomic will act like comic -d.
XLink comic to xcat, and decomic will act like comic -cd.
X
XTo make comic type 'make unix' or 'make gcc' or 'make minix' etc.
X
XQuestions: email jsm@cs.vu.nl
X
+ END-OF-FILE READ_ME
chmod 'u=rw,g=r,o=r' 'READ_ME'
set `wc -c 'READ_ME'`
count=$1
case $count in
797)	:;;
*)	echo 'Bad character count in ''READ_ME' >&2
		echo 'Count should be 797' >&2
esac
echo Extracting 'Makefile'
sed 's/^X//' > 'Makefile' << '+ END-OF-FILE ''Makefile'
X#
X# Makefile for comic
X#
X# (p) Jan-Mark Wams 					(email: jsm@cs.vu.nl)
X#
X
XCFLAGS=-DNDEBUG -O
Xo=s
X
XOBJS=bits.$o \
X	comic.$o \
X	decode.$o \
X	encode.$o \
X	getinput.$o \
X	gettoken.$o \
X	header.$o \
X	huffman.$o
X
XCOMIC=comic
X
X# For speed or mem savage, use exec below
XRM=/bin/rm
XMAKE=/bin/make
XECHO=/usr/bin/echo
X
Xcomic: minix
X	@$(ECHO) comic done...
X
Xclean: 
X	@$(RM) -f core a.out *.o *.obj $(OBJS)
X
Xgcc:
X	@$(MAKE) GCC CC='gcc' 'CFLAGS=-DGCC $(CFLAGS)' 'o=o'
X
Xminix:
X	@$(MAKE) MINIX 'CFLAGS=-DMINIX $(CFLAGS)' 'o=s'
X
Xpro:
X	@$(MAKE) UNIX 'CFLAGS=-DUNIX -p $(CFLAGS)' 'o=o'
X
Xunix:
X	@$(MAKE) UNIX 'CFLAGS=-DUNIX $(CFLAGS)' 'o=o'
X
Xamoeba:
X	@$(MAKE) AMOEBA CC='amcc' 'CFLAGS=-DAMOEBA $(CFLAGS)' 'o=o'
X
Xc89:
X	@$(MAKE) C89 CC='c89' 'CFLAGS=-wo -DC89 $(CFLAGS)' 'o=o'
X
Xm68:
X	@$(MAKE) M68 'CFLAGS=-DM68 $(CFLAGS)' 'o=o'
X
Xdos:	
X	@$(MAKE) DOS CC='TCC' 'CFLAGS=-DDOS $(CFLAGS)' 'o=OBJ'
X
X
XC89: memrchr89.$o $(OBJS)
X	$(CC) $(CFLAGS) -o $(COMIC) memrchr89.o $(OBJS)
X
XDOS: cmemrchr.$o $(OBJS)
X	$(CC) $(CFLAGS) -o $(COMIC).exe memrchr.obj $(OBJS)
X
XGCC M68 UNIX AMOEBA: cmemrchr.$o $(OBJS)
X	$(CC) $(CFLAGS) -o $(COMIC) cmemrchr.$o $(OBJS)
X
XMINIX: memrchr.$o $(OBJS)
X	$(CC) $(CFLAGS) -o $(COMIC) memrchr.$o $(OBJS)
X
Xbits.$o: bits.h comic.h bits.c
X	$(CC) $(CFLAGS) -c bits.c
X
Xcomic.$o: buffer.h comic.h comic.c
X	$(CC) $(CFLAGS) -c comic.c
X
Xdecode.$o: bits.h buffer.h comic.h huffman.h decode.c header.h
X	$(CC) $(CFLAGS) -c decode.c
X
Xencode.$o: bits.h buffer.h comic.h huffman.h encode.c header.h
X	$(CC) $(CFLAGS) -c encode.c
X
Xgetinput.$o: buffer.h comic.h getinput.c
X	$(CC) $(CFLAGS) -c getinput.c
X
Xgettoken.$o: buffer.h comic.h gettoken.c
X	$(CC) $(CFLAGS) -c gettoken.c
X
Xhuffman.$o: comic.h huffman.h tables.h huffman.c
X	$(CC) $(CFLAGS) -c huffman.c
X
Xcmemrchr.$o: comic.h
X	$(CC) $(CFLAGS) -O -c cmemrchr.c
X
Xheader.$o: comic.h header.h header.c
X	$(CC) $(CFLAGS) -c header.c
X
Xmemrchr89.s: memrchr.s
X	as2nas memrchr.s > memrchr89.s
X
Xmemrchr89.o: memrchr89.s
X	$(CC) $(CFLAGS) -c memrchr89.s
+ END-OF-FILE Makefile
chmod 'u=rw,g=r,o=r' 'Makefile'
set `wc -c 'Makefile'`
count=$1
case $count in
1964)	:;;
*)	echo 'Bad character count in ''Makefile' >&2
		echo 'Count should be 1964' >&2
esac
echo Extracting 'bits.c'
sed 's/^X//' > 'bits.c' << '+ END-OF-FILE ''bits.c'
X/*
X** Part of the comic package. (p) Jan-Mark Wams 	(email: jms@cs.vu.nl)
X**
X** Bits.c: Read and write bits on stdin and out.
X**
X**	Just define DEBUG to get characters '1' and '0'.
X**	The bits are put on a file, first bit in the high bit of the
X**	first byte. `Flus_bits ()' will add a `1' and padd the byte
X**	up to a full byte with zero's. When reading back the last bit
X**	in the last byte is the eof indicator.
X*/
X
X#include <errno.h>
X#include "comic.h"
X#include "bits.h"
X
X#ifdef DEBUG		/* Use characters '1' and '0'. */
X
X/*
X** Putbit: put a bit on stdout.
X*/
Xvoid putbit (bit)
Xint bit;
X{
X  assert (bit == 1 || bit == 0);
X
X  Nout += 1;
X  putchar ('0' + bit);
X}
X
X/*
X** Getbit: get a bit from stdin.
X*/
Xint getbit ()
X{
X  switch (getchar ()) {
X    case '0' : return 0;
X    case '1' : return 1;
X    case EOF : return EOF;
X    default : return 2;
X  }
X}    
X
X/*
X** Init_bits: Init the bit package.
X*/
Xvoid init_bits ()
X{
X  /* Noting to do in DEBUG mode. */
X}
X
X
X/*
X** Flush_bits: Flush the buffered bits to stdout.
X*/
Xvoid flush_bits ()
X{
X  fflush (stdout);
X}
X
X
X#else			/* No debug, so use 8 bits per byte. */
X
X#define IO_BUFF_SIZE	1024
Xstatic char io_buff [IO_BUFF_SIZE];
Xstatic int io_index = 0;
Xstatic int eof_index = 0;
Xstatic int put_bits = 1;
Xstatic int get_bits = 0;
X
X
X#define next()		(io_buff + (io_index++))	/* The next char. */
X#define nonext()	(io_index == eof_index)		/* There is no next. */
X#define io_buff_full	(io_index == IO_BUFF_SIZE)	/* Buffer full. */
X
X
X/*
X** fatal: Print a error on stderr, and exit.
X*/
Xstatic void fatal (failed)
Xchar *failed;
X{
X  fprintf (stderr, "%s: Fatal couldn't %s.", pname, failed);
X  if (errno != 0)
X	perror(" Perror");	/* Prints EOLN to ;-( */
X  else
X	printf ("\n");
X  exit (-1);
X}
X
X
X/*
X** Write_io_buff: to stdout.
X*/
Xstatic void write_io_buff ()
X{
X#ifdef DOS
X  (void) setmode (1, O_BINARY);		/* Fails some times. ;-( */
X#endif /* DOS */
X  if (write (1, io_buff, io_index) != io_index)
X	fatal ("write");
X  Nout += io_index;
X  io_index = 0;
X}
X
X/*
X** Read_io_buff: from stdin.
X*/
Xstatic void read_io_buff ()
X{
X#ifdef DOS
X  (void) setmode (0, O_BINARY);		/* Fails for me. ;-( */
X#endif /* DOS */
X
X  if ((eof_index = read (0, io_buff, IO_BUFF_SIZE)) < 0)
X	fatal ("read");
X
X  Nin += eof_index;
X
X  io_index = 0;
X}
X
X
X/*
X** Putbit: put a bit on stdout.
X*/
Xvoid putbit (bit)
Xint bit;
X{
X  assert (bit == 1 || bit == 0);
X
X  put_bits = (put_bits << 1) | bit;
X
X  if (put_bits > 0xFF) {
X	*next() = put_bits;
X	if (io_buff_full)
X		write_io_buff ();
X	put_bits = 1;		/* Flag. */
X  }
X}
X
X
X/*
X** Getbit: get a bit from stdin.
X*/
Xint getbit ()
X{
X  if ((get_bits & 0x7F) == 0) {
X	if (get_bits == 0) {		/* First time only. */
X		read_io_buff ();
X		if (nonext ()) {
X			fprintf (stderr, "%s: Warning empty file.\n", pname);
X			fflush (stderr);
X			return EOF;
X		}
X	}
X
X	if (nonext ()) return EOF;
X	get_bits = (0xFF & (int)*next ()) << 1;
X	if (nonext ()) read_io_buff ();
X	if (nonext ()) {
X		if (get_bits == 0x100) return EOF;/* Bad luck, think ;-) */
X	}
X	else
X		get_bits |= 1;	/* Last byte has own EOF marker. */
X  }
X  else
X	get_bits <<= 1;
X
X  return (get_bits >> 8) & 1;
X}    
X
X/*
X** Init_bits: Init the bits package.
X*/
Xvoid init_bits ()
X{
X  io_index = 0;		/* Not ness, but nice. */
X  eof_index = 0;
X  put_bits = 1;		/* Very ness. */
X  get_bits = 0;
X}
X
X
X/*
X** Flush_bits: Flush the buffered bits to stdout.
X*/
Xvoid flush_bits ()
X{
X  int i;
X  putbit (1);				/* Eof marker. */
X  for (i = 0; i < 7; i++) putbit (0);	/* Fillup byte. */
X  write_io_buff ();
X}
X
X#endif /* DEBUG */		/* Next routines just call get/putbit(). */
X
X/*
X** Putnbit: put low `n' bits from code on stream.
X*/
Xvoid putnbit (code, n)
Xlong code;
Xint  n;
X{
X  int uggy_buggy;	/* The C compiler doesn't handle longs properly */
X
X  assert (n >= 0 && n < sizeof (long) * 8);
X
X  while (n -- > 0) {
X	uggy_buggy = (int)(code >> n) & 1;
X	putbit (uggy_buggy);
X  }
X}
X
X/*
X** Getnbit: Get `n' bits form stdin.
X*/
Xint getnbit (n)
Xint n;
X{
X  int ret = 0;		/* Return value. */
X
X  assert (n > 0 && n < sizeof (int) * 8);
X
X  while (n -- > 0) {
X	ret <<= 1;
X	switch (getbit ()) {
X	  case 1:
X		ret |= 1;
X		break;
X	  case 0:
X		break;
X	  case EOF:
X		fprintf (stderr, "%s: Unexpected EOF\n", pname); 
X		exit (-1);
X	  default:
X		fprintf (stderr, "%s: funny bit\n", pname);
X		exit (-1);
X	}
X  }
X  return ret;
X}
X
X/* That's all folks. */
+ END-OF-FILE bits.c
chmod 'u=rw,g=r,o=r' 'bits.c'
set `wc -c 'bits.c'`
count=$1
case $count in
4290)	:;;
*)	echo 'Bad character count in ''bits.c' >&2
		echo 'Count should be 4290' >&2
esac
echo Extracting 'cmemrchr.c'
sed 's/^X//' > 'cmemrchr.c' << '+ END-OF-FILE ''cmemrchr.c'
X/*
X** Part of the comic package. (p) Jan-Mark Wams 	(email: jms@cs.vu.nl)
X**
X** Cmemrchr.c: Find the first occurance of c from start to end.
X** 	For non optimizing compilers, write the routine in MC.
X*/
X
X#include "comic.h"
X
X/*
X** Memrchr: Look back in memory for a char. Return (char *)NULL if not found.
X**	Write this routine in MC if the C compiler does no optimizing.
X*/
Xchar *memrchr (start, stop, ch)
Xchar *start, *stop, ch;		/* Note: stop is `char *', not `index_t.' */
X{
X  char tmp = *stop;		/* Save old value. */
X  *stop = ch;			/* Put in sentinel. */
X
XLOOP:
X	if (*start == ch) goto FOUND;
X	start -= 1;
X	if (*start == ch) goto FOUND;
X	start -= 1;
X	if (*start == ch) goto FOUND;
X	start -= 1;
X	if (*start == ch) goto FOUND;
X	start -= 1;
X	if (*start == ch) goto FOUND;
X	start -= 1;
X	if (*start == ch) goto FOUND;
X	start -= 1;
X	if (*start == ch) goto FOUND;
X	start -= 1;
X	if (*start == ch) goto FOUND;
X	start -= 1;
X	if (*start == ch) goto FOUND;
X	start -= 1;
X	if (*start == ch) goto FOUND;
X	start -= 1;
X	if (*start == ch) goto FOUND;
X	start -= 1;
X	if (*start == ch) goto FOUND;
X	start -= 1;
X	if (*start == ch) goto FOUND;
X	start -= 1;
X	if (*start == ch) goto FOUND;
X	start -= 1;
X	if (*start == ch) goto FOUND;
X	start -= 1;
X	if (*start == ch) goto FOUND;
X	start -= 1;
X	goto LOOP;
X
XFOUND:
X	*stop = tmp;				/* Remove in sentinel. */
X	if (start == stop) goto NOTFOUND;
X	return start;
X
XNOTFOUND:
X	return (char *) NULL;
X}
+ END-OF-FILE cmemrchr.c
chmod 'u=rw,g=r,o=r' 'cmemrchr.c'
set `wc -c 'cmemrchr.c'`
count=$1
case $count in
1420)	:;;
*)	echo 'Bad character count in ''cmemrchr.c' >&2
		echo 'Count should be 1420' >&2
esac
echo Extracting 'comic.c'
sed 's/^X//' > 'comic.c' << '+ END-OF-FILE ''comic.c'
X/*
X** Part of the comic package. (p) Jan-Mark Wams 	(email: jms@cs.vu.nl)
X**
X** Comic.c: COmpress MInix C (main routine)
X**
X** `Comic' is tuned to compress the minix source (kernel, fs, mm, commands)
X** it might be usable for other minix c programs, it might even be usable
X** for c programs or plain text, but it will most sertenly flunk on 
X** compressing other data.
X*/
X
X#ifdef DOS
X# define TTY	"CON"		/* The console device under SM-DOS. */
X# define isatty(i)	1	/* Everything is a tty under MES-DOS. */
X#else
X# define TTY	"/dev/tty"
X#endif /* DOS */
X#include <string.h>		/* Included for strxxx (). */
X#include <ctype.h>		/* For isupper() and tolower (). */
X#include "comic.h"
X#include "buffer.h"
X
X#define streq(s1,s2)	(strcmp (s1, s2) == 0)
X/*
X** Max length for file names.
X*/
X#define NAMELEN_MAX	30	/* 30 Sould be einnough. */
X#define FILE_SUFFIX	"-X"	/* Suffix for comiced files. */
X
X/*
X** Global variables.
X*/
Xchar *pname;				/* Name of program. */
Xstatic pair_t pairs [2];		/* Buffer for pair descriptors. */
Xpair_p p0 = &(pairs [0]);		/* Short hands. */
Xpair_p p1 = &(pairs [1]);
Xchar MallocBuff  [BUFF_SIZE + 1];	/* The Buffer and 1 Sentinel. */
Xchar *Blow_sent = MallocBuff;
Xchar *Buff = MallocBuff + 1;		/* Scip sentinel. */
Xchar *Bend;				/* Pointer to the last char in Buff. */
Xchar *Bp = MallocBuff + 1;		/* Pointer to unproccessed char. */
Xchar *Binputend = MallocBuff + 1;	/* End of read ahead input. */
Xint  Bsize;
Xlong Nin, Nout;				/* Total # of bytes read/written. */
Xint v_flag = 0;				/* Verbose flag given. */
X
Xstatic int f_flag = 0;			/* Force flag given. */
Xstatic int c_flag = 0;			/* Cat flag given. */
X
X/*
X** Basename: Do a check on name, only comic and decomic are allowed.
X*/
Xstatic char *basename (name)
Xchar *name;
X{
X  char *p;
X#ifdef DOS
X  /*  
X  ** DOS needs special atention. Upercase letters and leading path 
X  ** must be removed. 
X  */
X  p = strrchr (name, '\\');
X  if (p != (char *) NULL) 
X	name = p + 1;			/* Kill path prefix. */
X  for (p = name; *p != '\0'; p++)
X	if (isupper (*p)) 
X		*p = tolower (*p);	/* Make name lowercase. */
X#else
X  p = strrchr (name, '/');
X  if (p != (char *) NULL) 
X	name = p + 1;			/* Kill path prefix. */
X#endif
X  if (strncmp (name, "[de]comic", 9) == 0) {
X	printf ("WHOEHAHAHAHAHAHAHAHAHAHAHAHA.\n");
X	exit (-99);
X  }
X  if (! streq (name, "comic")
X   && ! streq (name, "decomic")
X   && ! streq (name, "xcat")) {
X	fprintf (stderr, "This program must be called [de]comic ");
X	fprintf (stderr, "or xcat, not %s.\n", name);
X#ifndef DEBUG
X	exit (1);		/* In debug mode, allow other names. */
X#endif
X  }
X  return name;
X}
X
X
X/*
X** Newname: generate new output name according to the given name.
X*/
Xstatic char *newname (fname)
Xchar *fname;
X{
X  static char ret_name [NAMELEN_MAX];	/* Buffer for return value. */
X  char *p;
X
X  /*
X  ** Check the tail. If the suffix is presend, just return it.
X  */
X  if ((p = strrchr (fname, FILE_SUFFIX [0])) != (char *)NULL
X  && streq (p, FILE_SUFFIX))
X	return fname;
X
X  strncpy (ret_name, fname, NAMELEN_MAX);
X  ret_name [(NAMELEN_MAX - 1) - strlen (FILE_SUFFIX)] = '\0'; /* Truckate. */
X#ifdef DOS
X  /*
X  ** Truncate suffix to be able to hold the FILE_SUFFIX.
X  ** If there is no suffix, add a "." string.
X  */
X  if ((p = strrchr (ret_name, '.')) != (char *)NULL)
X	p [4 - strlen (FILE_SUFFIX)] = '\0';	/* truncate. */
X  else 
X	strcat (ret_name, ".");
X#endif /* DOS */
X  strcat (ret_name, FILE_SUFFIX);
X
X  return ret_name;
X}
X
X/*
X** Orgname: generate original name according to the given name.
X*/
Xstatic char *orgname (fname)
Xchar *fname;
X{
X  static char ret_name [NAMELEN_MAX];	/* Buffer for return value. */
X  char *p;
X
X  strncpy (ret_name, fname, NAMELEN_MAX);
X  ret_name [NAMELEN_MAX - 1] = '\0';	/* Terminate. */
X
X  /*
X  ** If the suffix is there delete it.
X  ** If this is a DOS machine, and the file name has no suffix, kill the dot.
X  */
X  if ((p = strrchr (ret_name, FILE_SUFFIX [0])) != (char *)NULL
X  && streq (p, FILE_SUFFIX)) {
X	*p = '\0';
X#ifdef DOS
X	if (*--p == '.')
X		*p = '\0';
X#endif /* DOS */
X  }
X  return ret_name;
X}
X
X/*
X** Stdin_out: Set stdin and stdout to newin and newout.
X**	Return succes or fail.
X*/
Xstatic int stdin_out (newin, newout)
Xchar *newin, *newout;
X{
X  FILE *tty;			/* File pointer for "/dev/tty" */
X  static char buf [2];
X  char *ans = buf;		/* To contain the answer, "y" or "n" */
X
X  /*
X  ** Reopen stdin.
X  */
X  if (freopen (newin, "r", stdin) == (FILE *)NULL) {
X	fprintf (stderr, "%s: Can't open \"%s\" for reading", pname, newin);
X	perror (" Perror");
X	fflush (stderr);
X	return 0;		/* Report error. */
X  }
X
X  /*
X  ** If the cat flag is set, don't reopen stdout.
X  */
X  if (c_flag)
X	  return 1;		/* Return success. */
X
X  /*
X  ** Check if we will overwrite a file. (Unless the -f flag was given.)
X  */
X  if (! f_flag && access (newout, 0) == 0) {
X	if (! isatty (2) || (tty = fopen (TTY, "r")) == (FILE *)NULL) {
X		fprintf (stderr, "%s: Won't overwrite %s\n", pname, newout);
X		fflush (stderr);
X		return 0;
X	}
X	fprintf (stderr, "%s: Overwrite %s (y/n)? ", pname, newout);
X	fflush (stderr);
X	ans = fgets (ans, 2, tty);
X	fclose (tty);
X	if (ans == (char *)NULL || *ans != 'y')
X		return 0;
X  }
X
X  /*
X  ** Reopen stdout.
X  */
X  if (freopen (newout, "w", stdout) == (FILE *)NULL) {
X	fprintf (stderr, "%s: Can't open \"%s\" for writing", pname, newout);
X	perror (" Perror");
X	fflush (stderr);
X	return 0;		/* Report fail. */
X  }
X
X  return 1;		/* Return success. */
X}
X
X/*
X** Vinfo: Give verbose information about this program.
X*/
Xstatic void vinfo ()
X{
X  fprintf (stderr, "This is comic Version 1.0B (p) Jan-Mark (jms@cs.vu.nl)\n");
X  fprintf (stderr, "Compilation flags: ");
X#ifdef __STDC__
X# if ! __STDC__
X  fprintf (stderr, "!");
X# endif
X  fprintf (stderr, "__STDC__ ");
X#endif
X#ifdef C89
X  fprintf (stderr, "C89 ");
X#endif
X#ifdef MINIX
X  fprintf (stderr, "MINIX ");
X#endif
X#ifdef UNIX
X  fprintf (stderr, "UNIX ");
X#endif
X#ifdef AMOEBA
X  fprintf (stderr, "AMOEBA ");
X#endif
X#ifdef M68
X  fprintf (stderr, "M68 ");
X#endif
X#ifdef GCC
X  fprintf (stderr, "GCC ");
X#endif
X#ifdef DOS
X  fprintf (stderr, "DOS ");
X#endif
X#ifdef DEBUG
X  fprintf (stderr, "DEBUG ");
X#endif
X#ifdef NDEBUG
X  fprintf (stderr, "NDEBUG ");
X#endif
X#ifdef _POSIX_SOURCE
X  fprintf (stderr, "_POSIX_SOURCE ");
X#endif
X  fprintf (stderr, "OFFSET_BITS == %d\n", OFFSET_BITS);
X}
X
X/*
X** Usage: print the usage of comic.
X*/
Xstatic void usage ()
X{
X  fprintf (stderr, "Usage: %s [-", pname);
X  if (streq (pname, "decomic"))
X	fprintf (stderr, "cfv");
X  else if (streq (pname, "xcat"))
X	fprintf (stderr, "v");
X  else
X	fprintf (stderr, "cfvv", pname);
X#ifdef DEBUG
X  fprintf (stderr, "v");			/* Debug mode has extra v. */
X#endif 
X  fprintf (stderr, "V?] [<file>[-X]...]\n");
X}
X
X/*
X** main: Parce arguments, call propper (en/de) code routine. 
X*/
Xvoid main (argc, argv)
Xint argc;
Xchar **argv;
X{
X  char *p;		/* Used for flag processing. */
X  int d_flag = 0;	/* Decompress flag. */
X
X  pname = basename (*argv);
X
X  if (streq (pname, "decomic"))	/* If this program is called */
X	d_flag = -1;		/* decomic set the decode flag. */
X
X  if (streq (pname, "xcat")) {	/* If this program is called */
X	d_flag = -1;		/* xcat set the decode */
X	c_flag = -1;		/* and cat flag. */
X  }
X
X  while (argc > 1 && argv [1][0] == '-' && argv [1][1] != '\0') {
X
X	argc --; argv ++;	/* Next please. */
X
X	for (p = *argv + 1; *p != '\0'; p++) {
X		switch (*p) {
X		case 'd': d_flag = 1; break;	/* decode flag on. */
X		case 'v': v_flag ++; break;	/* Verbose flag inc. */
X		case 'f': f_flag = 1; break;	/* Force flag on. */
X		case 'c': c_flag = 1; break;	/* Force flag on. */
X		case '?': usage ();		/* Usage and fall. */
X		case 'V': vinfo (); exit (0);	/* Give verbose info. */
X		default :
X		    fprintf (stderr, "-%c ignored\n", *p);
X		    fflush (stderr);
X		}
X	}
X  }
X
X#ifndef DEBUG
X  if (v_flag && ! isatty (2))	/* Don't allow vv flag if stderr, is no tty. */
X	v_flag = 1;
X#endif
X
X  if (argc == 1 && (c_flag == 1 || f_flag)) {	/* No args makes -cf silly. */
X	putc ('-', stderr);
X	if (c_flag == 1) putc ('c', stderr);
X	if (f_flag) putc ('f', stderr);
X	fprintf (stderr, " ignored\n");
X	fflush (stderr);
X  }
X  else if (argc > 1 && c_flag && f_flag) {	/* -c makes -f silly ;-) */
X	fprintf (stderr, "-f ignored\n");
X	fflush (stderr);
X  }
X
X  if (argc == 1) {		/* No args, do stin to stout. */
X  	if (d_flag)
X		decode ();
X	else 
X		encode ();
X  }
X
X  while (argc > 1) {
X
X	argc --; argv ++;	/* Next please */
X
X	if (d_flag) {	/* Decode. */
X
X		if ( ! stdin_out (newname (*argv), orgname (*argv)))
X			continue;	/* If not reopened, do next file. */
X		if (v_flag) {
X			fprintf (stderr, "%s: ", newname (*argv));
X			fflush (stderr);
X		}
X		decode ();	/* Decode stdin. */
X		if (v_flag) {
X			fprintf (stderr, "decomiced to ");
X			if (c_flag)
X				fprintf (stderr, "stdout\n");
X			else
X				fprintf (stderr, "%s\n", *argv);
X			fflush (stderr);
X		}
X	}
X	else /* No d_flag */ {	/* Encode. */
X
X		if (streq (*argv, newname (*argv))) {
X			fprintf (stderr, "%s: %s already has %s suffix\n", 
X				  pname, *argv, FILE_SUFFIX);
X			fflush (stderr);
X			continue;
X		}
X		if ( ! stdin_out (orgname (*argv), newname (*argv)))
X			continue;	/* If not reopened, do next file. */
X		if (v_flag) {
X			fprintf (stderr, "%s: ", orgname (*argv));
X			fflush (stderr);
X		}
X		encode ();		/* Put data on stdout. */
X	}
X  }
X}
+ END-OF-FILE comic.c
chmod 'u=rw,g=r,o=r' 'comic.c'
set `wc -c 'comic.c'`
count=$1
case $count in
9217)	:;;
*)	echo 'Bad character count in ''comic.c' >&2
		echo 'Count should be 9217' >&2
esac
echo Extracting 'decode.c'
sed 's/^X//' > 'decode.c' << '+ END-OF-FILE ''decode.c'
X/*
X** Part of the comic package. (p) Jan-Mark Wams 	(email: jms@cs.vu.nl)
X**
X** Decode.c: decode stdin to stdout.
X**	Get a bit, if it's a 0, decode a pair, if it's a 1, decode a char.
X**	If it's no 1 nor a 0, fly the broomstick. *8-)
X*/
X
X#include "comic.h"
X#include "buffer.h"
X#include "huffman.h"	/* Data structures of huffman trees and codes. */
X#include "bits.h"
X#include "header.h"
X
X/*
X** De_pair: Read offset and length, and put substring on stout.
X*/
Xstatic void de_pair ()
X{
X  int len, off = Hdecode (Hoff);
X  char *p;
X
X  off = off << LOW_OFFSET_BITS;
X  off |= getnbit (LOW_OFFSET_BITS);
X  off += OFFSET_MIN;
X  len = Hdecode (Hlen) + LENGTH_MIN;
X#ifdef DEBUG
X  if (v_flag > 1) 
X	printf ("<%d,%d>\"", off, len);
X#endif
X  p = Bsub (Bp, off);
X  while (len --) {
X	putchar (*p);
X	*Bp = *p;
X	p = Bsucc (p);
X	Bp = Bsucc (Bp);
X  }
X#ifdef DEBUG
X  if (v_flag > 1)
X	printf ("\"\n");
X#endif
X}
X
X/*
X** De_char: Get a huffman encoded char and put in on stdout.
X*/
Xstatic void de_char ()
X{
X  int c = Hdecode (Hchar);
X  putchar (c);
X#ifdef DEBUG
X  if (v_flag > 1) 
X	putchar ('\n');
X#endif
X  *Bp = c;
X  Bp = Bsucc (Bp);
X}
X
X
X/*
X** Decode: decode stdin. Read a bit from stdin, if it's a 0, decode a pair,
X**	if it's a 1 decode a char, if it's EOF, return.
X*/
Xvoid decode ()
X{
X  Bsize = OFFSET_SIZE + OFFSET_MIN;
X  Bend = &(Buff [OFFSET_MAX]);
X  Bp = Binputend = Buff;	/* Not ness, but.. */
X  Nin = 0; Nout = 0;
X  Bclear ();			/* Clear the buffer. */
X  Hinit ();			/* Init the huffman tables. */
X  init_bits ();			/* Init the bit package. */
X
X  if (! magic_ok ()) {
X	fprintf (stderr, "%s: Wrong magic in header\n", pname);
X	exit (-1);
X  }
X
X  for (;;) {
X	switch (getbit ()) {
X	  case 0:
X		de_pair();
X		break;
X	  case 1:
X		de_char();
X		break; 
X	  case EOF:
X		fflush (stdout);
X		return;			/* EOF, were done. */
X	  default:
X		fprintf (stderr, "%s: Funny bit\n", pname);
X		exit (-1);
X	}
X  }
X}
+ END-OF-FILE decode.c
chmod 'u=rw,g=r,o=r' 'decode.c'
set `wc -c 'decode.c'`
count=$1
case $count in
1874)	:;;
*)	echo 'Bad character count in ''decode.c' >&2
		echo 'Count should be 1874' >&2
esac
echo Extracting 'encode.c'
sed 's/^X//' > 'encode.c' << '+ END-OF-FILE ''encode.c'
X/*
X** Part of the comic package. (p) Jan-Mark Wams 	(email: jms@cs.vu.nl)
X**
X** Encode.c: parce stdin, write huffman codes and bits on stdout.
X**	Get next two longest strings, if the second one is longer,
X**	or the first one isn't long innough, encode a char, else
X**	encode a pair, (offset, lenght).
X*/
X
X#include "comic.h"
X#include "buffer.h"
X#include "huffman.h"
X#include "bits.h"
X#include "header.h"
X
X/*
X** Outpair: Output part p0's length and offset.
X*/
Xstatic void outpair ()
X{
X  int offset = Bdelta (Bp, p0-> start) - OFFSET_MIN;
X  int length = p0-> length;
X
X  assert (offset >= 0);
X  assert (offset < OFFSET_SIZE);
X  assert (length >= LENGTH_MIN);
X  assert (length - LENGTH_MIN < HUFFMAN_SIZE);
X
X  putbit (0);						/* Put a 0. */
X  Hencode (Hoff, (offset >> LOW_OFFSET_BITS));		/* Put offset high. */
X  putnbit ((long)offset, LOW_OFFSET_BITS);		/* Put offset low. */ 
X  Hencode (Hlen, length - LENGTH_MIN);	/* Put index length. */
X
X#ifdef DEBUG
X  if (v_flag > 2) {
X	fprintf (stderr, "(%d,%d)", offset + OFFSET_MIN, length);
X	putc ('"', stderr);
X	while (length -- > 0) {
X		putc (*Bp, stderr);
X		Bp = Bsucc (Bp);
X	}
X	fprintf (stderr, "\"\n");
X	fflush (stderr);
X  }
X  else
X#endif /* DEBUG */
X	Bp = Badd (Bp, length);
X}
X
X/*
X** Outchar: Output char *Bp.
X*/
Xstatic void outchar ()
X{
X  putbit (1);					/* Put a 1. */
X  Hencode (Hchar, (0xFF & (int)*Bp));	/* Put this char. */
X#ifdef DEBUG
X  if (v_flag > 2) {
X	putc ('`', stderr);
X	putc (*Bp, stderr);
X	putc ('\'', stderr);
X	putc ('\n', stderr);
X  }
X#endif /* DEBUG */
X  Bp = Bsucc (Bp); 			/* Increase Bp. */
X}
X
X/*
X** Pr_ratio: print the compression ratio.
X**	Divide Nin and Nout by 2 as long as they are to big, to 
X**	prevent overflowing the rat variable.
X*/
Xstatic void pr_ratio ()
X{
X  long rat;
X  while (Nin > 10000 || Nout > 10000) { /* Get smaller value. */
X	Nin >>= 1;
X	Nout >>= 1;
X  }
X  rat = ((Nin - Nout) * 10000) / Nin;
X  if (rat < 0 && !v_flag) {
X	fprintf (stderr, "%s: Warning ", pname);
X  }
X  if (rat < 0 || v_flag) {
X	fprintf (stderr, "compression: %d", (int)(rat / 100));
X	fprintf (stderr, ".%02d%%\n", abs ((int)(rat % 100)));
X	fflush (stderr);
X  }
X}
X
X/*
X** Encode: parce stdin, write huffman codes and bits on stdout.
X*/
Xvoid encode ()
X{
X  /* 
X  ** Pairout indicates p0 is not to short, and p1 isn't loger than p0.
X  */
X  int pairout = 1;
X
X  Bsize = BUFF_SIZE;
X  Bend = &(Buff [BUFF_MAX]);
X  Bp = Binputend = Buff;	/* Not ness, but.. */
X  Nin = 0; Nout = 0;
X  Bclear ();			/* Clear the buffer. */
X  Hinit ();			/* Init the huffman tables. */
X  init_bits ();			/* Init the bit package. */
X
X  put_magic ();		/* Put magic on stdout. */
X
X  while (getinput () != EOF) {
X	gettoken (pairout);			/* Find matching strings. */
X	pairout =  p0-> length >= LENGTH_MIN	
X		&& p0-> length >= p1-> length;	/* See if it's long innough. */
X	if (pairout)
X		outpair ();			/* If so, ouput it's pair. */
X	else
X		outchar ();			/* Else do one char. */
X  }
X  flush_bits ();		/* Flush buffered bits. */
X  if (Nin == 0) {
X	  fprintf (stderr, "%s: Warning empty file comiced\n", pname);
X	  fflush (stderr);
X  }
X  else {
X	if (v_flag)
X		pr_ratio ();
X  }
X}
+ END-OF-FILE encode.c
chmod 'u=rw,g=r,o=r' 'encode.c'
set `wc -c 'encode.c'`
count=$1
case $count in
3093)	:;;
*)	echo 'Bad character count in ''encode.c' >&2
		echo 'Count should be 3093' >&2
esac
echo Extracting 'getinput.c'
sed 's/^X//' > 'getinput.c' << '+ END-OF-FILE ''getinput.c'
X/*
X** Part of the comic package. (p) Jan-Mark Wams 	(email: jms@cs.vu.nl)
X**
X** Getinput.c: Get input into the buffer.
X**	Read stdin till Blookupend. This code look's complicated, but it
X**	isn't; Most of it is about printing.
X*/
X
X#include <sys/types.h>          /* Included for stat (). */
X#include <sys/stat.h>           /* Idem. */
X
X#include "comic.h"
X#include "buffer.h"
X
X#ifdef DOS
X# define isatty(i)	0	/* Dos setting. */
X#endif /* DOS */
X
X/*
X** getinput: Get input buffer full, return EOF if end of file, SPC other wise.
X*/
Xint getinput ()
X{
X  static unsigned long fsize = 0L;
X  static struct stat st [1];
X  static int printed = -1;		/* -1 Means don't print. */
X  int percent, c;
X  char *stop = Blookupstart (), *Oinputend = Binputend;
X
X  if (v_flag > 1 && Nin == 0 && ! isatty (0) && fstat (0, st) == 0) {
X        fsize = (unsigned long) (st [0].st_size);
X	if (fsize != 0L) {
X		fprintf (stderr, "  0%%");
X		fflush (stderr);
X	}
X	printed = 0;
X  }
X
X  while (Binputend != stop && (c = getchar ()) != EOF) {
X	*Binputend = c;
X	Binputend = Bsucc (Binputend);	/* Increase the input end marker. */
X  }
X  Nin += Bdelta (Binputend, Oinputend);	/* Count the number of char's read. */
X
X  if (Nin < fsize) {	/* Note 0 <=  Nin < fsize A thus 0 < fsize */
X	percent = 100 - (int)((fsize - Nin) / (fsize / 100));
X	if (percent > printed) {
X		fprintf (stderr, "\b\b\b\b%3d%%", percent);
X		fflush (stderr);
X		printed = percent;
X	}
X  }
X
X  if (Bp == Binputend) {
X	assert (Oinputend == Binputend);
X	assert (fsize == 0 || Nin == fsize);
X	if (printed != -1) {
X		fprintf (stderr, "\b\b\b\b");
X		fflush (stderr);
X	}
X	return EOF;
X  }
X  else
X	return (int) ' ';
X}
+ END-OF-FILE getinput.c
chmod 'u=rw,g=r,o=r' 'getinput.c'
set `wc -c 'getinput.c'`
count=$1
case $count in
1640)	:;;
*)	echo 'Bad character count in ''getinput.c' >&2
		echo 'Count should be 1640' >&2
esac
echo Extracting 'gettoken.c'
sed 's/^X//' > 'gettoken.c' << '+ END-OF-FILE ''gettoken.c'
X/*
X** Part of the comic package. (p) Jan-Mark Wams 	(email: jms@cs.vu.nl)
X**
X** Gettoken.c: Find the best (logest) part to output.
X**	We try to find the longest part in the lookup buffer, matching the
X**	input. Well actually, we look for two srings, one, matching the 
X**	current input, starting at the current buffer start, and one 
X**	matching the string starting at the next input byte, (asif we 
X**	decoded a char.) This just incase, decoding two pairs, could have
X**	been done, using one char and pair. (This will save some %s extra.)
X**	To show what I mean, consider;
X**
X**					xxxxx.AxAxxxxx
X**	This would be parced 		x xxxx . A x Ax xxxx 
X**	but xxxx costs (about) as 
X**	mutch as xxxxx. So 		x xxxx . A x A xxxxx
X**	will be better.
X**	
X*/
X
X#include "comic.h"
X#include "buffer.h"
X
X#define Bsameon(p,q,offset)		/* see if p [offset] == q [offset]. */\
X	(*Badd (p, offset) == *Badd (q, offset))
X
X/*
X** Eqlen: Return the number of bytes, p[i] en q[i] are the same.
X*/
Xstatic int eqlen (p, q, stop)
Xchar *p;
Xregister char *q;
Xchar *stop;
X{
X  register char *r = p;
X
X  assert (p != q);
X
X  while (*r == *q && r != stop) {
X	r = Bsucc (r);
X	q = Bsucc (q);
X  }
X  return Bdelta (r, p);
X}
X
X
X/*
X** getp1: Find only p1's best values.
X*/
Xstatic void getp1 ()
X{
X  char *start1 = Bp;
X  char *stop1 = Bsucc (Blookupstart ());
X  char *istart1 = Bsucc (Bp);
X  char *iend1 = Binputend;
X
X  int len;
X
X  if (Bdelta (iend1, istart1) > LENGTH_MAX) {
X	iend1 = Bpred (iend1);
X  }
X
X  p1-> length = 0;
X  *Blow_sent = *istart1;	/* Set sentenial. */
X
X  while ((start1 = memrchr (start1, stop1, *istart1)) != (char *)NULL) {
X	if (start1 == Blow_sent) {
X		start1 = Bend;		/* Circular. */
X		continue;
X	}
X	if (Bsameon (start1, istart1, p1-> length)	/* uggy speed up. */
X	&&  (len = eqlen (istart1, start1, iend1)) > p1-> length) {
X		p1-> length = len;
X		p1-> start = start1;
X	}
X	start1 -= 1;
X  }
X}
X
X
X/*
X** get2pairs: Find two pairs, p0 and p1.
X*/
Xstatic void get2pairs ()
X{
X  char *start0;
X  char *istart0 = Bp;
X  char *iend0 = Binputend;
X
X  char *start1 = Bp;
X  char *stop1 = Bsucc (Blookupstart ());
X  char *istart1 = Bsucc (Bp);
X  char *iend1 = Binputend;
X
X  int len;
X
X  if (Bdelta (iend0, istart0) > LENGTH_MAX) {
X	iend0 = Bpred (iend0);
X	iend0 = Bpred (iend0);
X  }
X
X  if (Bdelta (iend1, istart1) > LENGTH_MAX) {
X	iend1 = Bpred (iend1);
X  }
X
X  p0-> length = 0;
X  p1-> length = 0;
X  *Blow_sent = *istart1;	/* Set sentenial. */
X
X  while ((start1 = memrchr (start1, stop1, *istart1)) != (char *)NULL) {
X	if (start1 == Blow_sent) {
X		start1 = Bend;		/* Circular. */
X		continue;
X	}
X	start0 = Bpred (start1);
X	if (*start0 == *istart0) {
X		if (Bsameon (start0, istart0, p0-> length) /* Speed only. */
X		&&  (len = eqlen (istart0, start0, iend0)) > p0-> length) {
X			p0-> length = len;
X			p0-> start = start0;
X		}
X	}
X	else {
X		if (Bsameon (start1, istart1, p1-> length) /* Speed only. */
X		&& (len = eqlen (istart1, start1, iend1)) > p1-> length) {
X			p1-> length = len;
X			p1-> start = start1;
X		}
X	}
X	start1 -= 1;
X  }
X}
X
X
X/*
X** gettoken: Find the best (logest) part to output.
X*/
Xvoid gettoken (p0used)
Xint p0used;		/* Indicate if p0 was used. */
X{
X  if (Bdelta (Binputend, Bp) < LENGTH_MIN) {	/* Don't bother. */
X	p0-> length = 0;			/* Not innough chars left. */
X	return;					/* So return. */
X  }
X
X  if (p0used) {				/* Get new p0 and p1. */
X	get2pairs ();
X  }
X  else {				/* Copy p1 to p0 and get new p1. */
X	p0-> start = p1-> start;
X	p0-> length = p1-> length;
X	getp1 ();
X  }
X}
+ END-OF-FILE gettoken.c
chmod 'u=rw,g=r,o=r' 'gettoken.c'
set `wc -c 'gettoken.c'`
count=$1
case $count in
3450)	:;;
*)	echo 'Bad character count in ''gettoken.c' >&2
		echo 'Count should be 3450' >&2
esac
echo Extracting 'header.c'
sed 's/^X//' > 'header.c' << '+ END-OF-FILE ''header.c'
X/*
X** Part of the comic package. (p) Jan-Mark Wams 	(email: jms@cs.vu.nl)
X**
X** Header.c: header handeling routines.
X**	Magic routines only, but for 2.0 the header will become
X**	more inportante.
X*/
X
X#include "comic.h"
X#include "header.h"
X
X/*
X** Magic_ok: Check the first bytes on stdin. They should be ok.
X**	Return True if magic is ok, else False.
X*/
Xint magic_ok ()
X{
X  char c;
X
X  if (read (0, &c, 1) != 1) {
X	fprintf (stderr, "%s: Can't read header\n", pname);
X	exit (-1);
X  }
X  if (c != MAGIC1) return 0;	/* First byte has to be ok. */
X
X  if (read (0, &c, 1) != 1) {
X	fprintf (stderr, "%s: Can't read headers 2nd byte\n", pname);
X	exit (-1);
X  }
X
X  if ((c & SUFFIX_BIT) != 0) {
X	fprintf (stderr, "%s: Version 1.0 can't handle dos suffix\n", pname);
X	exit (-1);
X  }
X  if ((c & DYNAMIC_BIT) != 0) {
X	fprintf (stderr, "%s: Version 1.0 can't handle dynamic bit\n", pname);
X	exit (-1);
X  }
X  if ((c & EXTEND_BIT) != 0) {
X	fprintf (stderr, "%s: Version 1.0 can't handle extend bit\n", pname);
X	exit (-1);
X  }
X
X  if (c != (MAGIC2 | STATIC_BIT)) return 0;	/* 1.0 static only ;-) */
X
X  return 1;
X}
X
X/*
X** Put_magic: Write magic header on stdout.
X*/
Xvoid put_magic ()
X{
X  char c1 = MAGIC1, c2 = (MAGIC2 | STATIC_BIT);
X  if (write (1, &c1, 1) != 1 || write (1, &c2, 1) != 1) {
X	fprintf (stderr, "%s: Can't write header\n", pname);
X	exit (-1);
X  }
X  Nout += 2;
X}
+ END-OF-FILE header.c
chmod 'u=rw,g=r,o=r' 'header.c'
set `wc -c 'header.c'`
count=$1
case $count in
1356)	:;;
*)	echo 'Bad character count in ''header.c' >&2
		echo 'Count should be 1356' >&2
esac
echo Extracting 'huffman.c'
sed 's/^X//' > 'huffman.c' << '+ END-OF-FILE ''huffman.c'
X/*
X** Part of the comic package. (p) Jan-Mark Wams 	(email: jms@cs.vu.nl)
X**
X** Huffman.c: huffman routines to get and put huffman codes.
X**	Note: If the Hencode is called for SWITCH codes with a codelength >
X**  	HUFFMAN_BITS, the actual code is used. This is mainly an optimalisation
X**  	for binary chunck's in the text. This could be replaced by dynamic
X**	huffman code. (See "Data Compression" AMC computing Surveys,
X**	Vol.19 No. 3 September 1987). I tryed modified BSTW, but that wasn't
X**	to good.
X*/
X
X#include "comic.h"
X#include "huffman.h"
X
X#define SWITCH 4		/* 1 Might do very well to. */
X
X/*
X** Include the auto generated tables.
X**	There is a big redundency in tables.h, Both encode and decode
X**	tables are included. (But the bit length per code would be innough
X**	but this would mean initializing a lot.)
X**	Ctab tables, have code, length pairs. The low length bits of code
X**	are the huffman code. Tree tables, have 0, 1 pairs. They point
X**	to the next pair or the code.
X*/
X#if OFFSET_BITS == 13
X# include "tables.h"
X#else 
X  error No tables.h for other than 13 bits.
X#endif
X
Xstatic struct ctab_s * ctab_tab [] = { ctab_char, ctab_off, ctab_len };
Xstatic struct tree_s * tree_tab [] = { tree_char, tree_off, tree_len };
Xstatic int Ntolong [3];		/* Ntolong for each table. */
X
X/*
X** Decend: decend down he tree.
X*/
Xstatic int decend (tree)
Xstruct tree_s *tree;
X{
X  int i = (HUFFMAN_SIZE * 2) - 2;
X
X  while (i >= HUFFMAN_SIZE) {
X	switch (getbit ()) {
X	  case EOF: 
X		fprintf (stderr, "%s: Unexpected EOF\n", pname); 
X		exit (-1);
X	  case 0:
X		i = tree[i - HUFFMAN_SIZE]._0;
X		break;
X	  case 1:
X		i = tree[i - HUFFMAN_SIZE]._1;
X		break;
X	  default:
X		fprintf (stderr, "%s: Funny bit\n", pname);
X		exit (-1);
X	}
X  }
X  return i;
X}
X
X
X/*
X** Hinit: Get the propper huffman code tables.
X*/
Xvoid Hinit ()
X{
X  Ntolong [0] = 0;
X  Ntolong [1] = 0;
X  Ntolong [2] = 0;
X
X  /*
X  ** Read in tables if ness. (Version to come.)
X  */
X}
X
X
X/*
X** Hdecode: Get a int using a huffman tree.
X*/
Xint Hdecode (index)
Xint index;
X{
X  struct tree_s *tree = tree_tab [index];
X  struct ctab_s *ctab = ctab_tab [index];
X  int i;
X
X  if (Ntolong [index] > SWITCH) {
X	i = (int) getnbit (HUFFMAN_BITS);
X	if (ctab [i].length > HUFFMAN_BITS)
X		Ntolong [index] = SWITCH << 1;
X	else
X		Ntolong [index] -= 1;
X  }
X  else {
X	i = decend (tree);
X	if (ctab [i].length > HUFFMAN_BITS)
X		Ntolong [index] += 1;
X	else
X		Ntolong [index] = 0;
X  }
X  return i;
X}
X
X
X
X/*
X** Hencode: ouput code i from code table ctab
X*/
Xvoid Hencode (index, i)
Xint index, i;
X{
X  struct ctab_s *ctab = ctab_tab [index];
X  int len = ctab [i].length;
X
X  if (Ntolong [index] > SWITCH) {
X	putnbit ((long)i, HUFFMAN_BITS);
X	if (len > HUFFMAN_BITS)
X		Ntolong [index] = SWITCH << 1;
X	else
X		Ntolong [index] -= 1;
X  }
X  else {
X	putnbit ((long)(ctab [i].code), len);
X	if (len > HUFFMAN_BITS)
X		Ntolong [index] += 1;
X	else
X		Ntolong [index] = 0;
X  }
X}
+ END-OF-FILE huffman.c
chmod 'u=rw,g=r,o=r' 'huffman.c'
set `wc -c 'huffman.c'`
count=$1
case $count in
2881)	:;;
*)	echo 'Bad character count in ''huffman.c' >&2
		echo 'Count should be 2881' >&2
esac
echo Extracting 'memrchr.s'
sed 's/^X//' > 'memrchr.s' << '+ END-OF-FILE ''memrchr.s'
X|  Memrchr: Look for a char. 
X|  (p) Jan-Mark Wams					(email: jms@cs.vu.nl)
X|  Usage memrchr (char *start, char *stop, char c); 
X|  Return value: 0 if char is not found, a pointer to the char other wise. 
X|  Note the lib function `memchr()' uses a count, we a stop pointer. 
X|  
X.define _memrchr	|  Make it a lib function. (Not needed.) 
X.globl _memrchr		|  Make it accessable for others. 
X
X.text
X_memrchr:
X	push	bp		|  C prologe. 
X	mov	bp, sp		|  
X	push	si		|  Get ready  
X	push	di		|  for cdsret 
X	push	cx
X
X	mov	di, 4(bp)	|  Get start 
X	mov	si, 6(bp)	|  Get stop. 
X	movb	al, 8(bp)	|  Get c in al. 
X
X	movb	ah, (si)	|  Save original value
X	movb	(si), al	|  Put sentinel in place.
X
X	std			|  Count down, ie. di-- (NO: pushf, popf!)
X	mov	cx, #-1		|  Set counter to max.
Xrepne	
X	scab			|  Loop till byte found.
X	inc	di		|  Hip hip... ;-(
X
XFOUND:
X	movb	(si), ah	|  Restore original value
X	cmp	di, si		|  End reached? 
X	je	NOTFOUND	|  IF so no match. 
X
X	mov	ax, di		|  return start 
X	j	_cdsret
XNOTFOUND:
X	mov	ax, #0		|  return (char *) NULL 
X_cdsret:
X	pop	cx
X	pop	di
X	pop	si
X	pop	bp
X	ret
X
X
X
X
+ END-OF-FILE memrchr.s
chmod 'u=rw,g=r,o=r' 'memrchr.s'
set `wc -c 'memrchr.s'`
count=$1
case $count in
1088)	:;;
*)	echo 'Bad character count in ''memrchr.s' >&2
		echo 'Count should be 1088' >&2
esac
echo Extracting 'memrchr89.s'
sed 's/^X//' > 'memrchr89.s' << '+ END-OF-FILE ''memrchr89.s'
X# /* call CPP */
X/*   Memrchr: Look for a char.  */
X/*   (p) Jan-Mark Wams				(email: jms@cs.vu.nl) */
X/*   Usage memrchr (char *start, char *stop, char c);  */
X/*   Return value: 0 if char is not found, a pointer to the char other wise.  */
X/*   Note the lib function `memchr()' uses a count, we a stop pointer.  */
X/*    */
X.define _memrchr	/*   Make it a lib function. (Not needed.)  */
X.extern _memrchr		/*   Make it accessable for others.  */
X
X.text
X_memrchr:
X	push	bp		/*   C prologe.  */
X	mov	bp, sp		/*    */
X	push	si		/*   Get ready   */
X	push	di		/*   for cdsret  */
X	push	cx
X
X	mov	di, 4(bp)	/*   Get start  */
X	mov	si, 6(bp)	/*   Get stop.  */
X	movb	al, 8(bp)	/*   Get c in al.  */
X
X	movb	ah, (si)	/*   Save original value */
X	movb	(si), al	/*   Put sentinel in place. */
X
X	std			/*   Count down, ie. di-- */
X	mov	cx, #-1		/*   Set counter to max. */
Xrepne	
X	scasb			/*   Loop till byte found. */
X	inc	di		/*   Hip hip... ;-( */
X
XFOUND:
X	movb	(si), ah	/*   Restore original value */
X	cmp	di, si		/*   End reached?  */
X	je	NOTFOUND	/*   IF so no match.  */
X
X	mov	ax, di		/*   return start  */
X	jmp	_cdsret
XNOTFOUND:
X	mov	ax, #0		/*   return (char *) NULL  */
X_cdsret:
X	pop	cx
X	pop	di
X	pop	si
X	pop	bp
X	ret
X
X
X
X
+ END-OF-FILE memrchr89.s
chmod 'u=rw,g=r,o=r' 'memrchr89.s'
set `wc -c 'memrchr89.s'`
count=$1
case $count in
1219)	:;;
*)	echo 'Bad character count in ''memrchr89.s' >&2
		echo 'Count should be 1219' >&2
esac
exit 0

swh@hpcupt1.HP.COM (Steve Harrold) (07/05/90)

>>> Since we are on the subject of compression, here is another compression
>>> program written by one of my students, Jan Mark Wams.  
-------------

The following header files are missing:

	bits.h
	buffer.h
	comic.h
	header.h
	huffman.h
	tables.h

Could they be posted, please.

croes@fwi.uva.nl (Felix A. Croes) (07/06/90)

In article <7056@star.cs.vu.nl> ast@cs.vu.nl (Andy Tanenbaum) writes:
>Since we are on the subject of compression, here is another compression
>program written by one of my students, Jan Mark Wams.  I haven't had time
>to look at it, but he's a bright fellow.  Please give it a try and post
>results.

the include file "comic.h" is missing.

--

Felix Croes    (croes@fwi.uva.nl)