Merge commit 'b1e7e97d3b60469b243b3b2e22c7d8cbd11c7c90'
[unleashed.git] / usr / src / cmd / unpack / unpack.c
blobb95909d4a39908f2e5147fbaf5843ed7bca434ca
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
21 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
22 /* All Rights Reserved */
26 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
27 * Use is subject to license terms.
30 #pragma ident "%Z%%M% %I% %E% SMI"
33 * Huffman decompressor
34 * Usage: pcat filename...
35 * or unpack filename...
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>
47 static struct utimbuf u_times;
48 static jmp_buf env;
49 static struct stat status;
50 static char *argv0, *argvk;
52 /* rmflg, when set it's ok to rm arvk file on caught signals */
53 static int rmflg = 0;
55 #define SUF0 '.'
56 #define SUF1 'z'
57 #define US 037
58 #define RS 036
60 /* variables associated with i/o */
61 static char filename[MAXPATHLEN];
63 static short infile;
64 static short outfile;
65 static short inleft;
66 static short is_eof = 0;
67 static char *inp;
68 static char *outp;
69 static char inbuff[BUFSIZ];
70 static char outbuff[BUFSIZ];
72 /* the dictionary */
73 static long origsize;
74 static short maxlev;
75 static short intnodes[25];
76 static char *tree[25];
77 static char characters[256];
78 static char *eof;
80 static void putch(char c);
81 static int expand();
82 static int decode();
83 static int getwdsize();
84 static int getch();
85 static int getdict();
87 /* Extended system attribute support */
89 static int saflg = 0;
92 /* read in the dictionary portion and build decoding structures */
93 /* return 1 if successful, 0 otherwise */
94 int
95 getdict()
97 register int c, i, nchildren;
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
106 eof = &characters[0];
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);
116 if (inbuff[0] != US)
117 goto goof;
119 if (inbuff[1] == US) { /* oldstyle packing */
120 if (setjmp(env))
121 return (0);
122 return (expand());
124 if (inbuff[1] != RS)
125 goto goof;
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) {
133 goof: (void) fprintf(stderr, gettext(
134 "%s: %s: not in packed format\n"), argv0, filename);
135 return (0);
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++;
147 *eof++ = *inp++;
148 intnodes[maxlev] += 2;
149 inleft -= inp - &inbuff[0];
150 if (inleft < 0)
151 goto goof;
154 * convert intnodes[i] to be number of
155 * internal nodes possessed by level i
158 nchildren = 0;
159 for (i = maxlev; i >= 1; i--) {
160 c = intnodes[i];
161 intnodes[i] = nchildren /= 2;
162 nchildren += c;
164 return (decode());
167 /* unpack the file */
168 /* return 1 if successful, 0 otherwise */
170 decode()
172 register int bitsleft, c, i;
173 int j, lev, cont = 1;
174 char *p;
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);
190 if (--inleft < 0) {
191 uggh: (void) fprintf(stderr, gettext(
192 "%s: %s: unpacking error\n"),
193 argv0, filename);
194 return (0);
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) {
209 wrerr: (void) fprintf(stderr,
210 gettext(
211 "%s: %s: write error: "),
212 argv0, argvk);
213 perror("");
214 return (0);
216 origsize -= c;
217 if (origsize != 0)
218 goto uggh;
219 return (1);
221 *outp++ = *p;
222 if (outp == &outbuff[BUFSIZ]) {
223 if (write(outfile, outp = &outbuff[0],
224 BUFSIZ) != BUFSIZ)
225 goto wrerr;
226 origsize -= BUFSIZ;
228 lev = 1;
229 i = 0;
230 } else
231 lev++;
234 return (1); /* we won't get here , but lint is pleased */
238 main(int argc, char *argv[])
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;
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
273 (void) setlocale(LC_ALL, "");
274 #if !defined(TEXT_DOMAIN)
275 #define TEXT_DOMAIN "SYS_TEST"
276 #endif
277 (void) textdomain(TEXT_DOMAIN);
279 if (progname = strrchr(argv[0], '/'))
280 ++progname;
281 else
282 progname = argv[0];
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) */
292 while ((c = getopt(argc, argv, "/")) != EOF) {
293 if (c == '/') {
294 if (pcat)
295 ++errflg;
296 else
297 saflg++;
298 } else
299 ++errflg;
302 * Check for invalid option. Also check for missing
303 * file operand, ie: "unpack" or "pcat".
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);
315 if (argc < 1) {
317 * return 1 for usage error when no file was specified
319 return (1);
322 /* loop through the file names */
323 for (k = 0; k < argc; k++) {
324 fcount++; /* expect the worst */
325 if (errflg) {
327 * invalid option; just count the number of files not
328 * unpacked
330 continue;
332 /* remove any .z suffix the user may have added */
333 for (cp = argv[k]; *cp != '\0'; ++cp)
335 if (cp[-1] == SUF1 && cp[-2] == SUF0) {
336 *cp-- = '\0'; *cp-- = '\0'; *cp = '\0';
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;
356 if (pcat)
357 outfile = 1; /* standard output */
358 else {
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));
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;
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;
378 if (stat(argvk, &status) != -1) {
379 (void) fprintf(stderr, gettext(
380 "%s: %s: already exists\n"),
381 argv0, argvk);
382 goto done;
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);
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;
397 rmflg = 1;
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;
427 } else {
428 if (!pcat)
429 (void) unlink(filename);
431 } else if (!pcat)
432 (void) unlink(filename);
434 if (!pcat) {
435 (void) printf(gettext("%s: %s: unpacked\n"),
436 argv0, argvk);
438 * preserve acc & mod dates
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("");
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("");
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("");
465 rmflg = 0;
467 if (!errflg)
468 fcount--; /* success after all */
470 done: (void) close(infile);
471 if (!pcat)
472 (void) close(outfile);
474 if (aclp) {
475 acl_free(aclp);
476 aclp = NULL;
479 return (fcount);
483 * This code is for unpacking files that
484 * were packed using the previous algorithm.
487 static int Tree[1024];
489 /* return 1 if successful, 0 otherwise */
492 expand()
494 int tp, bit;
495 short word;
496 int keysize, i, *t;
498 outp = outbuff;
499 inp = &inbuff[2];
500 inleft -= 2;
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);
510 t = Tree;
511 for (keysize = getwdsize(); keysize--; ) {
512 if ((i = getch()) == 0377)
513 *t++ = getwdsize();
514 else {
516 * reached EOF unexpectedly
518 if (is_eof) {
519 (void) fprintf(stderr, gettext(
520 "%s: %s: not in packed format\n"),
521 argv0, filename);
522 return (0);
524 *t++ = i & 0377;
528 * reached EOF unexpectedly
530 if (is_eof) {
531 (void) fprintf(stderr, gettext(
532 "%s: %s: not in packed format\n"),
533 argv0, filename);
534 return (0);
538 bit = tp = 0;
539 for (;;) {
540 if (bit <= 0) {
541 word = getwdsize();
543 * reached EOF unexpectedly
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);
551 bit = 16;
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);
568 getch()
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);
585 inleft--;
586 return (*inp++ & 0377);
590 getwdsize()
592 char c;
593 int d;
595 c = getch();
596 d = getch();
597 if (is_eof)
598 return (0);
599 d <<= 8;
600 d |= c & 0377;
601 return (d);
604 void
605 onsig(int sig)
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);
618 void
619 putch(char c)
621 int n;
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);