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