1 /*
2  * Program to unsqueeze files formed by sq.com
3  *
4  * Formerly written in BDS C and quite pupular as a CP/M tool
5  * in the earlier eighties.
6  *
7  * Ported to modern c compilers and z88dk by Stefano Bodrato
8  *
9  *
10  * $Id: usq.c,v 1.2 2012-03-29 15:28:36 stefano Exp $
11  *
12  * --------------------------------------------------------------
13  *
14  * Build (z88dk - OSCA):
15  * zcc +osca -ousq.exe -lflosxdos usq.c
16  *
17  * Build (z88dk - CP/M):
18  * zcc +osca -ousq.com usq.c
19  *
20  * Build (gcc):
21  * gcc -ousq usq.c
22  *
23  * --------------------------------------------------------------
24  *
25  * Useage:
26  *
27  *	usq [-count] [-fcount] [file1] [file2] ... [filen]
28  *
29  * where file1 through filen represent one or more files to be compressed,
30  * and the following options may be specified:
31  *
32  *	-count		Previewing feature: redirects output
33  * 			files to standard output with parity stripped
34  *			and unprintables except CR, LF, TAB and  FF
35  *			converted to periods. Limits each file
36  *			to first count lines.
37  *			Defaults to console, but see below how
38  *			to capture all in one file for further
39  *			processing, such as by PIP.
40  *			Count defaults to a very high value.
41  *			No CRC check is performed when previewing.
42  *			Use drive: to cancel this.
43  *
44  *	-fcount		Same as -count except formfeed
45  *			appended to preview of each file.
46  *			Example: -f10.
47  *
48  *	-pcount		wait for keypress every 23 lines ("more" msg)
49  *			Example: -p200 .. goes on for approx. 9 pages of text
50  *
51  * If no such items are given on the command line you will be
52  * prompted for commands (one at a time). An empty command
53  * terminates the program.
54  *
55  * The unsqueezed file name is recorded in the squeezed file.
56  *
57  */
58 /* CHANGE HISTORY:
59  * 1.3	Close inbuff to avoid exceeding maximum number of
60  *	open files. Includes rearranging error exits.
61  * 1.4	Add -count option to allow quick inspection of files.
62  * 1.5  Break up long lines of introductory text
63  * 1.5  -count no longer appends formfeed to preview of each file.
64  *	-fcount (-f10, -F10) does append formfeed.
65  * 1.6  Modified to work correctly under MP/M II (DIO.C change) and
66  *      signon message shortened.
67  * 2.0	Modified to work with CI-C86 compiler (CP/M-86 and MS-DOS)
68  * 2.1  Modified for use in MLINK
69  * 2.2  Modified for use with optimizing CI-C86 compiler (MS-DOS)
70  * 3.0  Generalized for use under UNIX
71  * 3.1  Found release date: 12/19/84
72  * 3.2  More generalized for use under modern UNIX and z88dk
73  *
74  */
75 
76 
77 #define SQMAIN
78 
79 #include <stdio.h>
80 #include <stdlib.h>
81 #include <fcntl.h>
82 #include <ctype.h>
83 #include <string.h>
84 #include "sqcom.h"
85 
86 #ifdef __OSCA__
87 #include "flos.h"
88 #undef gets(a)
89 #define gets(a) flos_get_input_string(a,16); putchar('\n');
90 #endif
91 
92 #define VERSION "3.2   23/03/2012"
93 
94 #define LARGE 30000
95 
96 /* Decoding tree */
97 struct {
98 	int children[2];	/* left, right */
99 } dnode[NUMVALS - 1];
100 
101 int bpos;	/* last bit position read */
102 int curin;	/* last byte value read */
103 
104 /* Variables associated with repetition decoding */
105 int repct;	/*Number of times to retirn value*/
106 int value;	/*current byte value or EOF */
107 
108 /* This must follow all include files */
109 unsigned int dispcnt;	/* How much of each file to preview */
110 char	ffflag;		/* should formfeed separate preview from different files */
111 char	pgflag;		/* Pager flag, wait for keypress */
112 
113 
114 /* get 16-bit word from file */
getw16(FILE * iob)115 int getw16(FILE *iob)
116 {
117 int temp;
118 
119 temp = getc(iob);		/* get low order byte */
120 temp |= getc(iob) << 8;
121 if (temp & 0x8000) temp |= (~0) << 15;	/* propogate sign for big ints */
122 return temp;
123 
124 }
125 
126 /* get 16-bit (unsigned) word from file */
getx16(FILE * iob)127 int getx16(FILE *iob)
128 {
129 int temp;
130 
131 temp = getc(iob);		/* get low order byte */
132 return temp | (getc(iob) << 8);
133 
134 }
135 
136 
137 /* initialize decoding functions */
138 
init_cr()139 void init_cr()
140 {
141 	repct = 0;
142 }
143 
init_huff()144 void init_huff()
145 {
146 	bpos = 99;	/* force initial read */
147 }
148 
149 
150 /* Decode file stream into a byte level code with only
151  * repetition encoding remaining.
152  */
153 
getuhuff(FILE * ib)154 int getuhuff(FILE *ib)
155 {
156 	int i;
157 	int bitval;
158 
159 	/* Follow bit stream in tree to a leaf*/
160 	i = 0;	/* Start at root of tree */
161 	do {
162 		if(++bpos > 7) {
163 			if((curin = getc(ib)) == ERROR)
164 				return ERROR;
165 			bpos = 0;
166 			/* move a level deeper in tree */
167 			i = dnode[i].children[1 & curin];
168 		} else
169 			i = dnode[i].children[1 & (curin >>= 1)];
170 	} while(i >= 0);
171 
172 	/* Decode fake node index to original data value */
173 	i = -(i + 1);
174 	/* Decode special endfile token to normal EOF */
175 	i = (i == SPEOF) ? EOF : i;
176 	return i;
177 }
178 
179 /* Get bytes with decoding - this decodes repetition,
180  * calls getuhuff to decode file stream into byte
181  * level code with only repetition encoding.
182  *
183  * The code is simple passing through of bytes except
184  * that DLE is encoded as DLE-zero and other values
185  * repeated more than twice are encoded as value-DLE-count.
186  */
187 
getcr(FILE * ib)188 int getcr(FILE *ib)
189 {
190 	int c;
191 
192 	if(repct > 0) {
193 		/* Expanding a repeated char */
194 		--repct;
195 		return value;
196 	} else {
197 		/* Nothing unusual */
198 		if((c = getuhuff(ib)) != DLE) {
199 			/* It's not the special delimiter */
200 			value = c;
201 			if(value == EOF)
202 				repct = LARGE;
203 			return value;
204 		} else {
205 			/* Special token */
206 			if((repct = getuhuff(ib)) == 0)
207 				/* DLE, zero represents DLE */
208 				return DLE;
209 			else {
210 				/* Begin expanding repetition */
211 				repct -= 2;	/* 2nd time */
212 				return value;
213 			}
214 		}
215 	}
216 }
217 
218 
219 
unsqueeze(char * infile)220 void unsqueeze(char *infile)
221 {
222 	FILE *inbuff, *outbuff;	/* file buffers */
223 	int i, c;
224 	char cc;
225 	int lines=0;
226 
227 	char *p;
228 	unsigned int filecrc;	/* checksum */
229 	int numnodes;		/* size of decoding tree */
230 	char outfile[128];	/* output file name */
231 	unsigned int linect;	/* count of number of lines previewed */
232 	char obuf[128];		/* output buffer */
233 	int oblen;		/* length of output buffer */
234 	static char errmsg[] = "ERROR - write failure in %s\n";
235 
236 	if(!(inbuff=fopen(infile, "rb"))) {
237 		printf("Can't open %s\n", infile);
238 		return;
239 	}
240 	/* Initialization */
241 	linect = 0;
242 	crc = 0;
243 	init_cr();
244 	init_huff();
245 
246 	/* Process header */
247 	if(getx16(inbuff) != RECOGNIZE) {
248 		printf("%s is not a squeezed file\n", infile);
249 		goto closein;
250 	}
251 
252 	filecrc = getw16(inbuff);
253 
254 	/* Get original file name */
255 	p = outfile;			/* send it to array */
256 	do {
257 		*p = getc(inbuff);
258 	} while(*p++ != '\0');
259 
260 	printf("%s -> %s: ", infile, outfile);
261 
262 
263 	numnodes = getw16(inbuff);
264 
265 	if(numnodes < 0 || numnodes >= NUMVALS) {
266 		printf("%s has invalid decode tree size\n", infile);
267 		goto closein;
268 	}
269 
270 	/* Initialize for possible empty tree (SPEOF only) */
271 	dnode[0].children[0] = -(SPEOF + 1);
272 	dnode[0].children[1] = -(SPEOF + 1);
273 
274 	/* Get decoding tree from file */
275 	for(i = 0; i < numnodes; ++i) {
276 		dnode[i].children[0] = getw16(inbuff);
277 		dnode[i].children[1] = getw16(inbuff);
278 	}
279 
280 	if(dispcnt) {
281 		/* Use standard output for previewing */
282 		putchar('\n');
283 		while(((c = getcr(inbuff)) != EOF) && (linect < dispcnt)) {
284 			cc = 0x7f & c;	/* strip parity */
285 			if((cc < ' ') || (cc > '~'))
286 				/* Unprintable */
287 				switch(cc) {
288 				case '\r':	/* return */
289 					/* newline will generate CR-LF */
290 					goto next;
291 				case '\n':	/* newline */
292 					{ ++linect; ++lines;}
293 				case '\f':	/* formfeed */
294 				case '\t':	/* tab */
295 					break;
296 				default:
297 					cc = '.';
298 				}
299 			putchar(cc);
300 			if ((pgflag)&&(lines>23)) {
301 				printf (" --more-- ");
302 				while ((c=fgetc(stdin))==0) {}
303 				putchar('\n');
304 				lines=0;
305 			}
306 		next: ;
307 		}
308 		if(ffflag)
309 			putchar('\f');	/* formfeed */
310 	} else {
311 		/* Create output file */
312 		if(!(outbuff=fopen(outfile, "wb"))) {
313 			printf("Can't create %s\n", outfile);
314 			goto closeall;
315 		}
316 		printf("unsqueezing,");
317 		/* Get translated output bytes and write file */
318 		oblen = 0;
319 		while((c = getcr(inbuff)) != EOF) {
320 			crc += c;
321 			obuf[oblen++] = c;
322 			if (oblen >= sizeof(obuf)) {
323 				if(!fwrite(obuf, sizeof(obuf), 1, outbuff)) {
324 					printf(errmsg, outfile);
325 					goto closeall;
326 				}
327 				oblen = 0;
328 			}
329 		}
330 		if (oblen && !fwrite(obuf, oblen, 1, outbuff)) {
331 			printf(errmsg, outfile);
332 			goto closeall;
333 		}
334 
335 		if((filecrc && 0xFFFF) != (crc && 0xFFFF))
336 			printf("ERROR - checksum error in %s\n", outfile);
337 		else	printf(" done.\n");
338 
339 	closeall:
340 		fclose(outbuff);
341 	}
342 
343 closein:
344 	fclose(inbuff);
345 }
346 
347 #ifdef __OSCA__
348 /*
349  * Wildcard comparison tool
350  * Found in the BDS C sources, (wildexp..),written by Leor Zolman.
351  * contributed by: W. Earnest, Dave Hardy, Gary P. Novosielski, Bob Mathias and others
352  *
353 */
354 
match(char * wildnam,char * filnam)355 int match(char *wildnam, char *filnam)
356 {
357    char c;
358    while (c = *wildnam++)
359 	if (c == '?')
360 		if ((c = *filnam++) && c != '.')
361 			continue;
362 		else
363 			return FALSE;
364 	else if (c == '*')
365 	{
366 		while (c = *wildnam)
367 		{ 	wildnam++;
368 			if (c == '.') break;
369 		}
370 		while (c = *filnam)
371 		{	filnam++;
372 			if (c == '.') break;
373 		}
374 	}
375 	else if (c == *filnam++)
376 	 	continue;
377 	else return FALSE;
378 
379    if (!*filnam)
380 	return TRUE;
381    else
382 	return FALSE;
383 }
384 #endif
385 
386 
obey(char * p)387 void obey(char *p)
388 {
389 	char *q, cc;
390 	int x;
391 
392 	if(*p == '-') {
393 		if(ffflag = ((*(p+1) == 'F') || (*(p+1) == 'f')))
394 			++p;
395 		if(pgflag = ((*(p+1) == 'P') || (*(p+1) == 'p')))
396 			++p;
397 		/* Set number of lines of each file to view */
398 		dispcnt = 65535;	/* default */
399 		if(*(p+1))
400 			if((dispcnt = atoi(p + 1)) == 0)
401 				printf("\nBAD COUNT %s", p + 1);
402 		return;
403 	}
404 
405 	/* Check for ambiguous (wild-card) name */
406 	for(q = p; *q != '\0'; ++q)
407 		if(*q == '*' || *q == '?') {
408 		#ifdef __OSCA__
409 			if ((x=dir_move_first())!=0) return;
410 			while (x == 0) {
411 				if (match(p,dir_get_entry_name()))
412 					obey(dir_get_entry_name());
413 				x = dir_move_next();
414 			}
415 		#else
416 			printf("\nAmbiguous name %s ignored", p);
417 		#endif
418 			return;
419 	}
420 
421 	unsqueeze(p);
422 }
423 
main(int argc,char * argv[])424 int main(int argc, char *argv[])
425 {
426 	int i,c;
427 	char inparg[128];	/* parameter from input */
428 
429 	dispcnt = 0;	/* Not in preview mode */
430 
431 	printf("File unsqueezer version %s (original author: R. Greenlaw)\n\n", VERSION);
432 
433 	/* Process the parameters in order */
434 	for(i = 1; i < argc; ++i)
435 		obey(argv[i]);
436 
437 	if(argc < 2) {
438 		printf("Enter file names, one line at a time, or type <RETURN> to quit.\n");
439 		do {
440 			gets(inparg);
441 			if(inparg[0] != '\0')
442 				obey(inparg);
443 		} while(inparg[0] != '\0');
444 	}
445 }
446 
447