[comp.sys.next] icrwebster.c for use with NCSA Telnet 2.3

eps@toaster.SFSU.EDU (Eric P. Scott) (10/21/90)

Hey Macintosh-lovers!  (Yeah, both of you!)

Last year I submitted my first "Mac-to-NeXT connectivity product"--
tifftoeps.  I've just hacked it into a Webster dictionary lookup
that displays the pictures too!  (I wouldn't need all this code if
NeXT's TIFF routines would read their own files instead of
complaining about NX_COMPRESSION_NOT_YET_SUPPORTED.)  Actually,
this won't display ALL the pictures, since some of them are EPSF
instead of TIFF, and I don't use the Window Server for imaging...
but the Mac's screen resolution IS less than 150dpi, so I don't
think anyone would object...  :-)

This is for Mac II-family machines with 256-color RGB displays
and NCSA Telnet 2.3 or later (this includes derivatives such as
BYU Telnet).  (NCSA Telnet is in the Public Domain, and available
for anonymous FTP from zaphod.ncsa.uiuc.edu.  If you have an
Ethernet (or AppleTalk-to-Ethernet via Shiva FastPath or Cayman
GatorBox) connection and you don't have NCSA Telnet, what are
you waiting for?  It's even better with Apple's MacTCP...)

Compile with:
cc -s -o icrwebster -O -bsd icrwebster.c -ltext -lsys_s

					-=EPS=-
-------
/*
 * icrwebster - display webster's images with NCSA Telnet 2.3
 * Eric P. Scott, San Francisco State University, October 1990
 *
 * Copyright 1990 by Eric P. Scott except as noted below.
 *
 * Credit where credit is due-ware: recognize what I've
 * contributed, take responsibility for what you do with/to it.
 * (i.e. don't pass my work off as your own, or vice-versa.)
 * You are granted a nonexclusive, royalty-free license to use
 * and adapt this software, and you may redistribute it in whole
 * or in part, in source and/or binary forms.
 *
 *
 * This is an exercise in code reusability; several existing
 * code fragments were blended together to produce this
 * monstrosity.  It's not intended to be elegant, it's just a
 * little experiment I thought I'd share with the net.community.
 *
 * You need a Mac II with an 8-bit color display to try this--
 * see Chapter 7 in the NCSA Telnet 2.3 documentation.
 */
#include <stdio.h>
#include <text/webster.h>
#ifndef lint
static char sccsid[]="@(#)icrwebster  1.0  (SFSU) 10/20/90";
#endif
ReferenceBook *book;
int thesaurus=0;
char *wname;
main(argc, argv)
int argc;
char *argv[];
{
	char *malloc(), *rindex();
	void lookup();
	extern int optind;
	extern char *optarg;
	register int i;
	char line[1024];

	dictionaryFoldSenses=60;
	while ((i=getopt(argc, argv, "tw:"))!=EOF) switch (i) {
	case 't':
		thesaurus++;
		break;
	case 'w':
		if (*optarg>='0'&&*optarg<='9') {
			dictionaryFoldSenses=atoi(optarg);
			break;
		}
		/*FALL THROUGH*/
	default:
		(void)fprintf(stderr,
			"Usage: %s [options] [word [...]]\n", *argv);
		(void)fputs("\t-t  use Thesaurus\n\t-w# set width\n", stderr);
		exit(0);
		break;
	}
#ifdef __STRICT_BSD__
	(void)setbuffer(stdout, malloc(4096), 4096);
#else
	setvbuf(stdout, (char *)NULL, _IOFBF, 4096);
#endif
	if (optind<argc) do {
		if (wname=rindex(argv[optind], '/')) {
			wname++;
			(void)image(argv[optind]);
		}
		else lookup(argv[optind]);
	} while (++optind<argc);
	else for (;;) {
		(void)fputs("Word: ", stderr); fflush(stderr);
		if (!fgets(line, sizeof line, stdin)||line[0]=='\n') break;
		line[strlen(line)-1]='\0';
		lookup(line);
	}
	if (book) (void)referenceClose(book);
	exit(0);
}

void lookup(word)
char *word;
{
	register int i;
	int n;
	Definition *D[16];	/* how big is big enough? */

	if (!book) {
		if (!(book=referenceOpen(thesaurus ? "Webster-Thesaurus" :
			"Webster-Dictionary"))) {
			fputs("referenceOpen failed!\n", stderr);
			exit(1);
		}
	}
	bzero((char *)D, sizeof D);
	n=getDefinitions(word, book, 1, D, sizeof D/sizeof D[0]-1);
	if (n==0) {
		(void)fputs("No definition for ", stdout);
		(void)fputs(word, stdout);
		(void)fputs(".\n", stdout); (void)fflush(stdout);
		return;
	}
	i=0; do {
		if (i>1) putchar('\n');
		if (D[i]->section>0&&!thesaurus) (void) printf("%% %s\n\n",
			websterSectionName[D[i]->section]);
/*
 * The webster routines in libtext.a (1.0/1.0a) are really braindamaged.
 * I can't make use of font information since there's no setting for
 * dictionaryOutputFormat that doesn't trash stuff I need (why isn't
 * there a W_RAW???) and I don't feel like picking though the database
 * by hand.  If you want a decent webster program, check out Steve
 * Hayman's--his way is faster, too.  (And it does spell checking!)
 */
		(void)putDefinition(D[i], 0);
		putchar('\n');
		if (D[i]->picture&&
			D[i]->picture[strlen(D[i]->picture)-1]=='f') {
			(void)fflush(stdout);
			wname=D[i]->dotted ? D[i]->dotted : D[i]->entry;
			(void)image(D[i]->picture);
		}
	} while (++i<n);
	(void)fflush(stdout);
	freeDefinitions(D);
}

int iwidth, ilen, bits, invert, compression;
long stripoff;
int image(tfile)
char *tfile;
{
	void NULLDecode(), NeXTDecode();
	FILE *tf;
	register int i;
	register long value;
	long sig, ifdoff;
	short ifditems;
	struct ifdent {
		short ifd_tag;
		short ifd_type;
		long ifd_len;
		union {
			unsigned long ifd_long;
			unsigned short ifd_short;
			unsigned char ifd_byte;
		} ifd_off;
	} *ifdp;

	if (!(tf=fopen(tfile, "r"))) {
		perror(tfile);
		return(1);
	}
	if (fread((char *)&sig, sizeof sig, 1, tf)!=1) {
		perror("fread");
	fatal:
		fclose(tf);
		return(1);
	}
	if (sig!=0x4d4d002a) {	/* big-endian */
		(void)fprintf(stderr, "%s: that's not a TIFF file!\n", tfile);
		goto fatal;
	}
	iwidth=0; ilen=0; bits=0; invert=255; compression=1;
	stripoff=0L;
	if (fread((char *)&ifdoff, sizeof ifdoff, 1, tf)!=1) {
		perror("fread");
		goto fatal;
	}
	if (fseek(tf, ifdoff, 0)<0L) {
		perror("fseek");
		goto fatal;
	}
	if (fread((char *)&ifditems, sizeof ifditems, 1, tf)!=1) {
		perror("fread");
		goto fatal;
	}
	if (!(ifdp=(struct ifdent *)malloc(ifditems*sizeof (struct ifdent)))) {
		perror("malloc");
		goto fatal;
	}
	if (fread((char *)ifdp, sizeof (struct ifdent), ifditems, tf)!=
		ifditems) {
		perror("fread");
		goto fatal;
	}
	for (i=0;i<ifditems;i++) {
		switch (ifdp->ifd_type) {
		case 1:
			value=ifdp->ifd_off.ifd_byte;
			break;
		case 3:
			value=ifdp->ifd_off.ifd_short;
			break;
		case 4:
			value=ifdp->ifd_off.ifd_long;
			break;
		default: /* ifdp->ifd_off.ifd_long is offset */
			value=0L;	/* do The Wrong Thing(tm) */
			break;
		}
		switch (ifdp->ifd_tag) {
		case 256:	/* ImageWidth */
			iwidth=value;
			break;
		case 257:	/* ImageLength */
			ilen=value;
			break;
		case 258:	/* BitsPerSample */
			bits=value;
			break;
		case 259:	/* Compression */
			if (value!=1L&&value!=32766L) {
				(void)fprintf(stderr,
					"%s: unsupported Compression=%ld\n",
					tfile, value);
				goto fatal;
			}
			compression=value;
			break;
		case 262:	/* PhotometricInterpretation */
			switch (value) {
			case 0L:
				invert=255;
				break;
			case 1L:
				invert=0;
				break;
			default:
				(void)fprintf(stderr,
"%s: unsupported PhotometricInterpretation=%ld\n", tfile , value);
				switch ((int)value) {
				case 5:	/* grayscale alpha */
				case 6:	/* RGB alpha */
				case 7:	/* CMY or CMYK alpha */
				case 14: /* 8-bit Pcolor alpha */
					(void)fputs("\tI don't grok alpha\n",
						stderr);
					break;
				case 2:	/* RGB */
				case 3:	/* CMY or CMYK */
				case 10: /* 8-bit Pcolor */
					(void)fputs("\tNo color support!\n",
						stderr);
					break;
				}
				goto fatal;
			}
			break;
		case 273:	/* StripOffsets */
			stripoff=value;
			break;
		case 277:	/* SamplesPerPixel */
			if (value!=1L) {
				(void)fprintf(stderr,
"%s: unsupported SamplesPerPixel=%ld\n", tfile, value);
				goto fatal;
			}
			break;
#ifdef NOTDEF
		case 284:	/* PlanarConfiguration */
			/* don't care when SamplesPerPixel==1 */
			break;
#endif
		default:
			break;
		}
		ifdp++;
	}
	if (iwidth<=0) {
		(void)fprintf(stderr, "%s: missing ImageWidth\n", tfile);
		goto fatal;
	}
	if (ilen<=0) {
		(void)fprintf(stderr, "%s: missing ImageLength\n", tfile);
		goto fatal;
	}
	if (bits<=0) {
		(void)fprintf(stderr, "%s: missing BitsPerSample\n", tfile);
		goto fatal;
	}
	if (stripoff<=0L) {
		(void)fprintf(stderr, "%s: missing StripOffsets\n", tfile);
		goto fatal;
	}
	if (fseek(tf, stripoff, 0L)<0L) {
		perror("fseek");
		goto fatal;
	}
#ifdef DEBUG
	(void)fprintf(stderr, "imaging %dx%dx%d\n", iwidth, ilen, bits);
#endif
	if (compression==1) NULLDecode(tf);
	else if (compression==32766) {
		if (bits==2) NeXTDecode(tf);
		else (void)fprintf(stderr,
"%s: NeXT compressions only supported for 2-bit images\n", tfile);
	}
	(void)fclose(tf);
	return(0);
}

void NULLDecode(tf)
FILE *tf;
{
	void output();
	char buf[284];
	int scanline, row;

	scanline = ((long)iwidth*bits+7L)/8L;
	for (row=0; row<ilen; row++) {
		(void)fread(buf, 1, scanline, tf);
		output((unsigned char *)buf, row);
	}
}

/* This table derived from NCSA Public Domain material. */
static unsigned char rgb[768]={
	255, 255, 255, 255, 255, 204, 255, 255, 153,
	255, 255, 102, 255, 255,  51, 255, 255,   0,
	255, 204, 255, 255, 204, 204, 255, 204, 153,
	255, 204, 102, 255, 204,  51, 255, 204,   0,
	255, 153, 255, 255, 153, 204, 255, 153, 153,
	255, 153, 102, 255, 153,  51, 255, 153,   0,
	255, 102, 255, 255, 102, 204, 255, 102, 153,
	255, 102, 102, 255, 102,  51, 255, 102,   0,
	255,  51, 255, 255,  51, 204, 255,  51, 153,
	255,  51, 102, 255,  51,  51, 255,  51,   0,
	255,   0, 255, 255,   0, 204, 255,   0, 153,
	255,   0, 102, 255,   0,  51, 255,   0,   0,
	204, 255, 255, 204, 255, 204, 204, 255, 153,
	204, 255, 102, 204, 255,  51, 204, 255,   0,
	204, 204, 255, 204, 204, 204, 204, 204, 153,
	204, 204, 102, 204, 204,  51, 204, 204,   0,
	204, 153, 255, 204, 153, 204, 204, 153, 153,
	204, 153, 102, 204, 153,  51, 204, 153,   0,
	204, 102, 255, 204, 102, 204, 204, 102, 153,
	204, 102, 102, 204, 102,  51, 204, 102,   0,
	204,  51, 255, 204,  51, 204, 204,  51, 153,
	204,  51, 102, 204,  51,  51, 204,  51,   0,
	204,   0, 255, 204,   0, 204, 204,   0, 153,
	204,   0, 102, 204,   0,  51, 204,   0,   0,
	153, 255, 255, 153, 255, 204, 153, 255, 153,
	153, 255, 102, 153, 255,  51, 153, 255,   0,
	153, 204, 255, 153, 204, 204, 153, 204, 153,
	153, 204, 102, 153, 204,  51, 153, 204,   0,
	153, 153, 255, 153, 153, 204, 153, 153, 153,
	153, 153, 102, 153, 153,  51, 153, 153,   0,
	153, 102, 255, 153, 102, 204, 153, 102, 153,
	153, 102, 102, 153, 102,  51, 153, 102,   0,
	153,  51, 255, 153,  51, 204, 153,  51, 153,
	153,  51, 102, 153,  51,  51, 153,  51,   0,
	153,   0, 255, 153,   0, 204, 153,   0, 153,
	153,   0, 102, 153,   0,  51, 153,   0,   0,
	102, 255, 255, 102, 255, 204, 102, 255, 153,
	102, 255, 102, 102, 255,  51, 102, 255,   0,
	102, 204, 255, 102, 204, 204, 102, 204, 153,
	102, 204, 102, 102, 204,  51, 102, 204,   0,
	102, 153, 255, 102, 153, 204, 102, 153, 153,
	102, 153, 102, 102, 153,  51, 102, 153,   0,
	102, 102, 255, 102, 102, 204, 102, 102, 153,
	102, 102, 102, 102, 102,  51, 102, 102,   0,
	102,  51, 255, 102,  51, 204, 102,  51, 153,
	102,  51, 102, 102,  51,  51, 102,  51,   0,
	102,   0, 255, 102,   0, 204, 102,   0, 153,
	102,   0, 102, 102,   0,  51, 102,   0,   0,
	 51, 255, 255,  51, 255, 204,  51, 255, 153,
	 51, 255, 102,  51, 255,  51,  51, 255,   0,
	 51, 204, 255,  51, 204, 204,  51, 204, 153,
	 51, 204, 102,  51, 204,  51,  51, 204,   0,
	 51, 153, 255,  51, 153, 204,  51, 153, 153,
	 51, 153, 102,  51, 153,  51,  51, 153,   0,
	 51, 102, 255,  51, 102, 204,  51, 102, 153,
	 51, 102, 102,  51, 102,  51,  51, 102,   0,
	 51,  51, 255,  51,  51, 204,  51,  51, 153,
	 51,  51, 102,  51,  51,  51,  51,  51,   0,
	 51,   0, 255,  51,   0, 204,  51,   0, 153,
	 51,   0, 102,  51,   0,  51,  51,   0,   0,
	  0, 255, 255,   0, 255, 204,   0, 255, 153,
	  0, 255, 102,   0, 255,  51,   0, 255,   0,
	  0, 204, 255,   0, 204, 204,   0, 204, 153,
	  0, 204, 102,   0, 204,  51,   0, 204,   0,
	  0, 153, 255,   0, 153, 204,   0, 153, 153,
	  0, 153, 102,   0, 153,  51,   0, 153,   0,
	  0, 102, 255,   0, 102, 204,   0, 102, 153,
	  0, 102, 102,   0, 102,  51,   0, 102,   0,
	  0,  51, 255,   0,  51, 204,   0,  51, 153,
	  0,  51, 102,   0,  51,  51,   0,  51,   0,
	  0,   0, 255,   0,   0, 204,   0,   0, 153,
	  0,   0, 102,   0,   0,  51, 238,   0,   0,
	221,   0,   0, 187,   0,   0, 170,   0,   0,
	136,   0,   0, 119,   0,   0,  85,   0,   0,
	 68,   0,   0,  34,   0,   0,  17,   0,   0,
	  0, 238,   0,   0, 221,   0,   0, 187,   0,
	  0, 170,   0,   0, 136,   0,   0, 119,   0,
	  0,  85,   0,   0,  68,   0,   0,  34,   0,
	  0,  17,   0,   0,   0, 238,   0,   0, 221,
	  0,   0, 187,   0,   0, 170,   0,   0, 136,
	  0,   0, 119,   0,   0,  85,   0,   0,  68,
	  0,   0,  34,   0,   0,  17, 238, 238, 238,
	221, 221, 221, 187, 187, 187, 170, 170, 170,
	136, 136, 136, 119, 119, 119,  85,  85,  85,
	 68,  68,  68,  34,  34,  34,  17,  17,  17,
	  0,   0,   0
};

static unsigned char idx1[2]={ 255, 0 }, idx2[4]={ 255, 251, 248, 0 },
	idx4[16]={ 255, 254, 253, 172, 252, 251, 129, 250,
	249, 86, 248, 247, 43, 246, 245, 0 };

int ox, oy;
void output(buf, row)
unsigned char *buf;
int row;
{
	register unsigned char *p, *q;
	register int i, c;
	unsigned char pbuf[1132];
#ifndef NO_RLE
	unsigned char rbuf[1144];
#endif

	if (row==0) {
		(void)printf("\033^W;%d;%d;%d;%d;0;%s^", ox, oy, iwidth, ilen,
			wname);
		ox+=5; oy+=3;
		if (bits<8) {
			(void)printf("\033^M;0;256;768;%s^", wname);
			p=rgb; q=p+sizeof rgb; do {
				if (*p>31&&*p<123) putchar(*p++);
				else {
					putchar((*p>>6)+123);
					putchar((*p++&63)+32);
				}
			} while (p<q);
		}
		(void)fflush(stdout);
	}
	p=buf; q=pbuf;
	switch (bits) {
	case 1:
		for (i=0;i<iwidth;i++) {
			c= *p++^invert;
			*q++ = idx1[(c>>7)&1];
			if (++i>=iwidth) break;
			*q++ = idx1[(c>>6)&1];
			if (++i>=iwidth) break;
			*q++ = idx1[(c>>5)&1];
			if (++i>=iwidth) break;
			*q++ = idx1[(c>>4)&1];
			if (++i>=iwidth) break;
			*q++ = idx1[(c>>3)&1];
			if (++i>=iwidth) break;
			*q++ = idx1[(c>>2)&1];
			if (++i>=iwidth) break;
			*q++ = idx1[(c>>1)&1];
			if (++i>=iwidth) break;
			*q++ = idx1[c&1];
		}
		break;
	case 2:
		for (i=0;i<iwidth;i++) {
			c= *p++^invert;
			*q++ = idx2[(c>>6)&3];
			if (++i>=iwidth) break;
			*q++ = idx2[(c>>4)&3];
			if (++i>=iwidth) break;
			*q++ = idx2[(c>>2)&3];
			if (++i>=iwidth) break;
			*q++ = idx2[c&3];
		}
		break;
	case 4:
		for (i=0;i<iwidth;i++) {
			c= *p++^invert;
			*q++ = idx4[(c>>4)&15];
			if (++i>=iwidth) break;
			*q++ = idx4[c&15];
		}
		break;
	default:
		for (i=0;i<iwidth;i++) *q++ = *p++^invert;
	}
#ifndef NO_RLE
	i = rleit(pbuf, rbuf, iwidth);
	if (i>0) {
		(void)printf("\033^R;0;%d;1;%d;%s^", row, i, wname);
		p=rbuf; q=p+i;
#else
	if (iwidth>0) {
		(void)printf("\033^P;0;%d;1;%d;%s^", row, iwidth, wname);
		p=pbuf; q=p+iwidth;
#endif
		do {
			if (*p>31&&*p<123) putchar(*p++);
			else {
				putchar((*p>>6)+123);
				putchar((*p++&63)+32);
			}
		} while (p<q);
	}
	(void)fflush(stdout);
}

#ifndef NO_RLE
/*  rleit
*  by Tim Krauskopf
*  National Center for Supercomputing Applications
*  University of Illinois, Urbana-Champaign
*
*  This program is in the public domain.
*
*
*  Compress the data to go out with a simple run-length encoded scheme.
*
*/

rleit(buf,bufto,len)
	int len;
	unsigned char *buf,*bufto;
	{
	register unsigned char *p,*q,*cfoll,*clead;
	unsigned char *begp;
	int i;

	p = buf;
	cfoll = bufto;				/* place to copy to */
	clead = cfoll + 1;
	
	begp = p;
	while (len > 0) {			/* encode stuff until gone */
		
		q = p + 1;
		i = len-1;
		while (i && *p == *q && i+120 > len) {
			q++;
			i--;
		}
		
		if (q > p + 2) {		/* three in a row */
			if (p > begp) {
				*cfoll = p - begp;
				cfoll = clead;
			}
			*cfoll++ = 128 | (q-p);	/* len of seq */
			*cfoll++ = *p;		/* char of seq */
			len -= q-p;		/* subtract len of seq */
			p = q;
			clead = cfoll+1;
			begp = p;
		}
		else {
			*clead++ = *p++;	/* copy one char */
			len--;
			if (p > begp + 120) {
				*cfoll = p - begp;
				cfoll = clead++;
				begp = p;
			}
		}
		
	}
/*
*  fill in last bytecount
*/
	if (p > begp) 
		*cfoll = p - begp;
	else
		clead--;			/* don't need count position */
	
	return((int)(clead - bufto));		/* how many stored as encoded */
}
#endif


/*
 * The following adapted from tif_next.c 1.8 5/22/90
 * Copyright (c) 1988, 1990 by Sam Leffler.
 * All rights reserved.
 *
 * This file is provided for unrestricted use provided that this
 * legend is included on all tape media and as a part of the
 * software program in whole or part.  Users may copy, modify or
 * distribute this file at will.
 */

/*
 * NeXT 2-bit Gray Scale Compression Algorithm Support
 */

#define LITERALROW	0x00
#define LITERALSPAN	0x40

void
NeXTDecode(tf)
FILE *tf;
{
	register unsigned char *op;
	register int n;
	unsigned char buf[284];
	int scanline, row;

	scanline = ((long)iwidth*2+7L)/8L;

	for (row=0; row<ilen; row++) {
		switch (n = getc(tf)) {
		case LITERALROW:
			/*
			 * The entire scanline is given as literal values.
			 */
			(void)fread((char *)buf, 1, scanline, tf);
			break;
		case LITERALSPAN: {
			int off;
			/*
			 * Each scanline is assumed to start off as all
			 * white (we assume a PhotometricInterpretation
			 * of ``min-is-black'').
			 */
			op=buf; n=sizeof buf; do *op++ = 0xff; while (--n>0);
			/*
			 * The scanline has a literal span
			 * that begins at some offset.
			 */
			off = getc(tf)*256;
			off += getc(tf);
			n = getc(tf)*256;
			n += getc(tf);
			(void)fread((char *)buf+off, 1, n, tf);
			break;
		}
		default: {
			register int npixels = 0, gray;

			/*
			 * The scanline is composed of a sequence
			 * of constant color ``runs''.  We shift
			 * into ``run mode'' and interpret bytes
			 * as codes of the form <color><npixels>
			 * until we've filled the scanline.
			 */
			op = buf;
			for (;;) {
				gray = (n>>6) & 0x3;
				n &= 0x3f;
				while (n-- > 0) switch (npixels++ & 3) {
				case 0:	op[0]  = gray << 6; break;
				case 1:	op[0] |= gray << 4; break;
				case 2:	op[0] |= gray << 2; break;
				case 3:	*op++ |= gray; break;
				}				
				if (npixels >= iwidth)
					break;
				n = getc(tf);
			}
			break;
		}
		}
		output(buf, row);
	}
}