1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
22/*	  All Rights Reserved  	*/
23
24
25/*
26 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
27 * Use is subject to license terms.
28 */
29
30#pragma ident	"%Z%%M%	%I%	%E% SMI"
31
32/*
33 *	Huffman decompressor
34 *	Usage:	pcat filename...
35 *	or	unpack filename...
36 */
37
38#include <setjmp.h>
39#include <signal.h>
40#include <locale.h>
41#include <utime.h>
42#include <sys/param.h>
43#include <sys/acl.h>
44#include <aclutils.h>
45#include <libcmdutils.h>
46
47static struct utimbuf u_times;
48static jmp_buf env;
49static struct	stat status;
50static char	*argv0, *argvk;
51
52/* rmflg, when set it's ok to rm arvk file on caught signals */
53static int	rmflg = 0;
54
55#define	SUF0	'.'
56#define	SUF1	'z'
57#define	US	037
58#define	RS	036
59
60/* variables associated with i/o */
61static char	filename[MAXPATHLEN];
62
63static short	infile;
64static short	outfile;
65static short	inleft;
66static short 	is_eof = 0;
67static char	*inp;
68static char	*outp;
69static char	inbuff[BUFSIZ];
70static char	outbuff[BUFSIZ];
71
72/* the dictionary */
73static long	origsize;
74static short	maxlev;
75static short	intnodes[25];
76static char	*tree[25];
77static char	characters[256];
78static char	*eof;
79
80static void putch(char c);
81static int expand();
82static int decode();
83static int getwdsize();
84static int getch();
85static int getdict();
86
87/* Extended system attribute support */
88
89static int saflg = 0;
90
91
92/* read in the dictionary portion and build decoding structures */
93/* return 1 if successful, 0 otherwise */
94int
95getdict()
96{
97	register int c, i, nchildren;
98
99	/*
100	 * check two-byte header
101	 * get size of original file,
102	 * get number of levels in maxlev,
103	 * get number of leaves on level i in intnodes[i],
104	 * set tree[i] to point to leaves for level i
105	 */
106	eof = &characters[0];
107
108	inbuff[6] = 25;
109	inleft = read(infile, &inbuff[0], BUFSIZ);
110	if (inleft < 0) {
111		(void) fprintf(stderr, gettext(
112		    "%s: %s: read error: "), argv0, filename);
113		perror("");
114		return (0);
115	}
116	if (inbuff[0] != US)
117		goto goof;
118
119	if (inbuff[1] == US) {		/* oldstyle packing */
120		if (setjmp(env))
121			return (0);
122		return (expand());
123	}
124	if (inbuff[1] != RS)
125		goto goof;
126
127	inp = &inbuff[2];
128	origsize = 0;
129	for (i = 0; i < 4; i++)
130		origsize = origsize*256 + ((*inp++) & 0377);
131	maxlev = *inp++ & 0377;
132	if (maxlev > 24) {
133goof:		(void) fprintf(stderr, gettext(
134		    "%s: %s: not in packed format\n"), argv0, filename);
135		return (0);
136	}
137	for (i = 1; i <= maxlev; i++)
138		intnodes[i] = *inp++ & 0377;
139	for (i = 1; i <= maxlev; i++) {
140		tree[i] = eof;
141		for (c = intnodes[i]; c > 0; c--) {
142			if (eof >= &characters[255])
143				goto goof;
144			*eof++ = *inp++;
145		}
146	}
147	*eof++ = *inp++;
148	intnodes[maxlev] += 2;
149	inleft -= inp - &inbuff[0];
150	if (inleft < 0)
151		goto goof;
152
153	/*
154	 * convert intnodes[i] to be number of
155	 * internal nodes possessed by level i
156	 */
157
158	nchildren = 0;
159	for (i = maxlev; i >= 1; i--) {
160		c = intnodes[i];
161		intnodes[i] = nchildren /= 2;
162		nchildren += c;
163	}
164	return (decode());
165}
166
167/* unpack the file */
168/* return 1 if successful, 0 otherwise */
169int
170decode()
171{
172	register int bitsleft, c, i;
173	int j, lev, cont = 1;
174	char *p;
175
176	outp = &outbuff[0];
177	lev = 1;
178	i = 0;
179	while (cont) {
180		if (inleft <= 0) {
181			inleft = read(infile, inp = &inbuff[0], BUFSIZ);
182			if (inleft < 0) {
183				(void) fprintf(stderr, gettext(
184				    "%s: %s: read error: "),
185				    argv0, filename);
186				perror("");
187				return (0);
188			}
189		}
190		if (--inleft < 0) {
191uggh:			(void) fprintf(stderr, gettext(
192			    "%s: %s: unpacking error\n"),
193			    argv0, filename);
194			return (0);
195		}
196		c = *inp++;
197		bitsleft = 8;
198		while (--bitsleft >= 0) {
199			i *= 2;
200			if (c & 0200)
201				i++;
202			c <<= 1;
203			if ((j = i - intnodes[lev]) >= 0) {
204				p = &tree[lev][j];
205				if (p == eof) {
206					c = outp - &outbuff[0];
207					if (write(outfile, &outbuff[0], c)
208					    != c) {
209wrerr:						(void) fprintf(stderr,
210						    gettext(
211						    "%s: %s: write error: "),
212						    argv0, argvk);
213						perror("");
214						return (0);
215					}
216					origsize -= c;
217					if (origsize != 0)
218						goto uggh;
219					return (1);
220				}
221				*outp++ = *p;
222				if (outp == &outbuff[BUFSIZ]) {
223					if (write(outfile, outp = &outbuff[0],
224					    BUFSIZ) != BUFSIZ)
225						goto wrerr;
226					origsize -= BUFSIZ;
227				}
228				lev = 1;
229				i = 0;
230			} else
231				lev++;
232		}
233	}
234	return (1);	/* we won't get here , but lint is pleased */
235}
236
237int
238main(int argc, char *argv[])
239{
240	extern int optind;
241	int i, k;
242	int error;
243	int sep, errflg = 0, pcat = 0;
244	register char *p1, *cp;
245	int fcount = 0;		/* failure count */
246	int max_name;
247	void onsig(int);
248	acl_t *aclp = NULL;
249	int c;
250	char *progname;
251	int sattr_exist = 0;
252	int xattr_exist = 0;
253
254	if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
255#ifdef __STDC__
256		(void) signal((int)SIGHUP, onsig);
257#else
258		(void) signal((int)SIGHUP, onsig);
259#endif
260	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
261#ifdef __STDC__
262		(void) signal((int)SIGINT, onsig);
263#else
264		(void) signal((int)SIGINT, onsig);
265#endif
266	if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
267#ifdef __STDC__
268		(void) signal((int)SIGTERM, onsig);
269#else
270		(void) signal(SIGTERM, onsig);
271#endif
272
273	(void) setlocale(LC_ALL, "");
274#if !defined(TEXT_DOMAIN)
275#define	TEXT_DOMAIN "SYS_TEST"
276#endif
277	(void) textdomain(TEXT_DOMAIN);
278
279	if (progname = strrchr(argv[0], '/'))
280		++progname;
281	else
282		progname = argv[0];
283
284	p1 = *argv;
285	while (*p1++) { };	/* Point p1 to end of argv[0] string */
286	while (--p1 >= *argv)
287		if (*p1 == '/')break;
288	*argv = p1 + 1;
289	argv0 = argv[0];
290	if (**argv == 'p')pcat++;	/* User entered pcat (or /xx/xx/pcat) */
291
292	while ((c = getopt(argc, argv, "/")) != EOF) {
293		if (c == '/') {
294			if (pcat)
295				++errflg;
296			else
297				saflg++;
298		} else
299			++errflg;
300	}
301	/*
302	 * Check for invalid option.  Also check for missing
303	 * file operand, ie: "unpack" or "pcat".
304	 */
305	argc -= optind;
306	argv = &argv[optind];
307	if (errflg || argc < 1) {
308		if (!pcat)
309			(void) fprintf(stderr,
310			    gettext("usage: %s [-/] file...\n"), argv0);
311		else
312			(void) fprintf(stderr, gettext("usage: %s file...\n"),
313			    argv0);
314
315		if (argc < 1) {
316			/*
317			 * return 1 for usage error when no file was specified
318			 */
319			return (1);
320		}
321	}
322	/* loop through the file names */
323	for (k = 0; k < argc; k++) {
324		fcount++;	/* expect the worst */
325		if (errflg) {
326			/*
327			 * invalid option; just count the number of files not
328			 * unpacked
329			 */
330			continue;
331		}
332		/* remove any .z suffix the user may have added */
333		for (cp = argv[k]; *cp != '\0'; ++cp)
334			;
335		if (cp[-1] == SUF1 && cp[-2] == SUF0) {
336			*cp-- = '\0'; *cp-- = '\0'; *cp = '\0';
337		}
338		sep = -1;
339		cp = filename;
340		argvk = argv[k];
341		/* copy argv[k] to filename and count chars in base name */
342		for (i = 0; i < (MAXPATHLEN-3) && (*cp = argvk[i]); i++)
343			if (*cp++ == '/')
344				sep = i;
345		/* add .z suffix to filename */
346		*cp++ = SUF0;
347		*cp++ = SUF1;
348		*cp = '\0';
349		if ((infile = open(filename, O_RDONLY)) == -1) {
350			(void) fprintf(stderr, gettext(
351			    "%s: %s: cannot open: "),
352			    argv0, filename);
353			perror("");
354			goto done;
355		}
356		if (pcat)
357			outfile = 1;	/* standard output */
358		else {
359
360			error = facl_get(infile, ACL_NO_TRIVIAL, &aclp);
361			if (error != 0) {
362				(void) printf(gettext(
363				    "%s: %s: cannot retrieve ACL : %s\n"),
364				    argv0, filename, acl_strerror(error));
365			}
366
367			max_name = pathconf(filename, _PC_NAME_MAX);
368			if (max_name == -1) {
369				/* no limit on length of filename */
370				max_name = _POSIX_NAME_MAX;
371			}
372			if (i >= (MAXPATHLEN-1) || (i - sep - 1) > max_name) {
373				(void) fprintf(stderr, gettext(
374				    "%s: %s: file name too long\n"),
375				    argv0, argvk);
376				goto done;
377			}
378			if (stat(argvk, &status) != -1) {
379				(void) fprintf(stderr, gettext(
380				    "%s: %s: already exists\n"),
381				    argv0, argvk);
382				goto done;
383			}
384			(void) fstat(infile, &status);
385			if (status.st_nlink != 1) {
386				(void) printf(gettext(
387				    "%s: %s: Warning: file has links\n"),
388				    argv0, filename);
389			}
390			if ((outfile = creat(argvk, status.st_mode)) == -1) {
391				(void) fprintf(stderr, gettext(
392				    "%s: %s: cannot create: "),
393				    argv0, argvk);
394				perror("");
395				goto done;
396			}
397			rmflg = 1;
398		}
399
400		if (getdict()) { 	/* unpack */
401			if (pathconf(filename, _PC_XATTR_EXISTS) == 1)
402				xattr_exist = 1;
403			if (saflg && sysattr_support(filename,
404			    _PC_SATTR_EXISTS) == 1)
405				sattr_exist = 1;
406			if (pcat || xattr_exist || sattr_exist) {
407				if (mv_xattrs(progname, filename, argv[k],
408				    sattr_exist, 0)
409				    != 0) {
410					/* Move attributes back ... */
411					xattr_exist = 0;
412					sattr_exist = 0;
413					if (pathconf(argvk, _PC_XATTR_EXISTS)
414					    == 1)
415						xattr_exist = 1;
416					if (saflg && sysattr_support(argvk,
417					    _PC_SATTR_EXISTS) == 1)
418						sattr_exist = 1;
419					if (!pcat && (xattr_exist ||
420					    sattr_exist)) {
421						(void) mv_xattrs(progname,
422						    argv[k], filename,
423						    sattr_exist, 1);
424						(void) unlink(argvk);
425						goto done;
426					}
427				} else {
428					if (!pcat)
429						(void) unlink(filename);
430				}
431			} else if (!pcat)
432				(void) unlink(filename);
433
434			if (!pcat) {
435				(void) printf(gettext("%s: %s: unpacked\n"),
436				    argv0, argvk);
437				/*
438				 * preserve acc & mod dates
439				 */
440				u_times.actime = status.st_atime;
441				u_times.modtime = status.st_mtime;
442				if (utime(argvk, &u_times) != 0) {
443					errflg++;
444					(void) fprintf(stderr, gettext(
445					    "%s: cannot change times on %s: "),
446					    argv0, argvk);
447					perror("");
448				}
449				if (chmod(argvk, status.st_mode) != 0) {
450					errflg++;
451					(void) fprintf(stderr, gettext(
452					"%s: cannot change mode to %o on %s: "),
453					    argv0, (uint_t)status.st_mode,
454					    argvk);
455					perror("");
456				}
457				(void) chown(argvk,
458				    status.st_uid, status.st_gid);
459				if (aclp && (facl_set(outfile, aclp) < 0)) {
460					(void) printf(gettext("%s: cannot "
461					    "set ACL on %s: "), argv0, argvk);
462					perror("");
463				}
464
465				rmflg = 0;
466			}
467			if (!errflg)
468				fcount--; 	/* success after all */
469		}
470done:		(void) close(infile);
471		if (!pcat)
472			(void) close(outfile);
473
474		if (aclp) {
475			acl_free(aclp);
476			aclp = NULL;
477		}
478	}
479	return (fcount);
480}
481
482/*
483 * This code is for unpacking files that
484 * were packed using the previous algorithm.
485 */
486
487static int	Tree[1024];
488
489/* return 1 if successful, 0 otherwise */
490
491int
492expand()
493{
494	int tp, bit;
495	short word;
496	int keysize, i, *t;
497
498	outp = outbuff;
499	inp = &inbuff[2];
500	inleft -= 2;
501
502	origsize = ((long)(unsigned)getwdsize())*256*256;
503	origsize += (unsigned)getwdsize();
504	if (origsize == 0 || is_eof) {
505		(void) fprintf(stderr, gettext(
506		    "%s: %s: not in packed format\n"),
507		    argv0, filename);
508		return (0);
509	}
510	t = Tree;
511	for (keysize = getwdsize(); keysize--; ) {
512		if ((i = getch()) == 0377)
513			*t++ = getwdsize();
514		else {
515				/*
516				 * reached EOF unexpectedly
517				 */
518			if (is_eof) {
519				(void) fprintf(stderr, gettext(
520				    "%s: %s: not in packed format\n"),
521				    argv0, filename);
522				return (0);
523			}
524			*t++ = i & 0377;
525		}
526	}
527		/*
528		 * reached EOF unexpectedly
529		 */
530	if (is_eof) {
531		(void) fprintf(stderr, gettext(
532		    "%s: %s: not in packed format\n"),
533		    argv0, filename);
534		return (0);
535	}
536
537
538	bit = tp = 0;
539	for (;;) {
540		if (bit <= 0) {
541			word = getwdsize();
542			/*
543			 * reached EOF unexpectedly
544			 */
545			if (word == 0 && is_eof && origsize > 0) {
546				(void) fprintf(stderr, gettext(
547				    "%s: %s: not in packed format\n"),
548				    argv0, filename);
549				return (0);
550			}
551			bit = 16;
552		}
553		tp += Tree[tp + (word < 0)];
554		word <<= 1;
555		bit--;
556		if (Tree[tp] == 0) {
557			putch(Tree[tp+1]);
558			tp = 0;
559			if ((origsize -= 1) == 0) {
560				(void) write(outfile, outbuff, outp - outbuff);
561				return (1);
562			}
563		}
564	}
565}
566
567int
568getch()
569{
570	if (inleft <= 0) {
571		inleft = read(infile, inp = inbuff, BUFSIZ);
572		if (inleft < 0) {
573			(void) fprintf(stderr, gettext(
574			    "%s: %s: read error: "),
575			    argv0, filename);
576			perror("");
577			longjmp(env, 1);
578		} else {		/* reached EOF, report it */
579			if (inleft == 0) {
580				is_eof = 1;
581				return (EOF);
582			}
583		}
584	}
585	inleft--;
586	return (*inp++ & 0377);
587}
588
589int
590getwdsize()
591{
592	char c;
593	int d;
594
595	c = getch();
596	d = getch();
597	if (is_eof)
598		return (0);
599	d <<= 8;
600	d |= c & 0377;
601	return (d);
602}
603
604void
605onsig(int sig)
606{
607				/* could be running as unpack or pcat	*/
608				/* but rmflg is set only when running	*/
609				/* as unpack and only when file is	*/
610				/* created by unpack and not yet done	*/
611	if (rmflg == 1)
612		(void) unlink(argvk);
613	/* To quiet lint noise */
614	if (sig == SIGTERM || sig == SIGHUP || sig == SIGINT)
615		exit(1);
616}
617
618void
619putch(char c)
620{
621	int n;
622
623	*outp++ = c;
624	if (outp == &outbuff[BUFSIZ]) {
625		n = write(outfile, outp = outbuff, BUFSIZ);
626		if (n < BUFSIZ) {
627			(void) fprintf(stderr, gettext(
628			    "%s: %s: write error: "),
629			    argv0, argvk);
630			perror("");
631			longjmp(env, 2);
632		}
633	}
634}
635