Exemple #1
0
/* do what is asked for, for the path name */
static void
handle_pathname(char *path)
{
	char *opath = path, *s = NULL;
	ssize_t len;
	int slen;
	struct stat sb;

	/* check for stdout/stdin */
	if (path[0] == '-' && path[1] == '\0') {
		if (dflag)
			handle_stdin();
		else
			handle_stdout();
		return;
	}

retry:
	if (stat(path, &sb) != 0 || (fflag == 0 && cflag == 0 &&
	    lstat(path, &sb) != 0)) {
		/* lets try <path>.gz if we're decompressing */
		if (dflag && s == NULL && errno == ENOENT) {
			len = strlen(path);
			slen = suffixes[0].ziplen;
			s = malloc(len + slen + 1);
			if (s == NULL)
				maybe_err("malloc");
			memcpy(s, path, len);
			memcpy(s + len, suffixes[0].zipped, slen + 1);
			path = s;
			goto retry;
		}
		maybe_warn("can't stat: %s", opath);
		goto out;
	}

	if (S_ISDIR(sb.st_mode)) {
#ifndef SMALL
		if (rflag)
			handle_dir(path);
		else
#endif
			maybe_warnx("%s is a directory", path);
		goto out;
	}

	if (S_ISREG(sb.st_mode))
		handle_file(path, &sb);
	else
		maybe_warnx("%s is not a regular file", path);

out:
	if (s)
		free(s);
}
Exemple #2
0
static off_t
unxz(int i, int o, char *pre, size_t prelen, off_t *bytes_in)
{
	lzma_stream strm = LZMA_STREAM_INIT;
	static const int flags = LZMA_TELL_UNSUPPORTED_CHECK|LZMA_CONCATENATED;
	lzma_ret ret;
	lzma_action action = LZMA_RUN;
	off_t bytes_out, bp;
	uint8_t ibuf[BUFSIZ];
	uint8_t obuf[BUFSIZ];

	if (bytes_in == NULL)
		bytes_in = &bp;

	strm.next_in = ibuf;
	memcpy(ibuf, pre, prelen);
	strm.avail_in = read(i, ibuf + prelen, sizeof(ibuf) - prelen);
	if (strm.avail_in == (size_t)-1)
		maybe_err("read failed");
	strm.avail_in += prelen;
	*bytes_in = strm.avail_in;

	if ((ret = lzma_stream_decoder(&strm, UINT64_MAX, flags)) != LZMA_OK)
		maybe_errx("Can't initialize decoder (%d)", ret);

	strm.next_out = NULL;
	strm.avail_out = 0;
	if ((ret = lzma_code(&strm, LZMA_RUN)) != LZMA_OK)
		maybe_errx("Can't read headers (%d)", ret);

	bytes_out = 0;
	strm.next_out = obuf;
	strm.avail_out = sizeof(obuf);

	for (;;) {
		if (strm.avail_in == 0) {
			strm.next_in = ibuf;
			strm.avail_in = read(i, ibuf, sizeof(ibuf));
			switch (strm.avail_in) {
			case (size_t)-1:
				maybe_err("read failed");
				/*NOTREACHED*/
			case 0:
				action = LZMA_FINISH;
				break;
			default:
				*bytes_in += strm.avail_in;
				break;
			}
		}

		ret = lzma_code(&strm, action);

		// Write and check write error before checking decoder error.
		// This way as much data as possible gets written to output
		// even if decoder detected an error.
		if (strm.avail_out == 0 || ret != LZMA_OK) {
			const size_t write_size = sizeof(obuf) - strm.avail_out;

			if (write(o, obuf, write_size) != (ssize_t)write_size)
				maybe_err("write failed");

			strm.next_out = obuf;
			strm.avail_out = sizeof(obuf);
			bytes_out += write_size;
		}

		if (ret != LZMA_OK) {
			if (ret == LZMA_STREAM_END) {
				// Check that there's no trailing garbage.
				if (strm.avail_in != 0 || read(i, ibuf, 1))
					ret = LZMA_DATA_ERROR;
				else {
					lzma_end(&strm);
					return bytes_out;
				}
			}

			const char *msg;
			switch (ret) {
			case LZMA_MEM_ERROR:
				msg = strerror(ENOMEM);
				break;

			case LZMA_FORMAT_ERROR:
				msg = "File format not recognized";
				break;

			case LZMA_OPTIONS_ERROR:
				// FIXME: Better message?
				msg = "Unsupported compression options";
				break;

			case LZMA_DATA_ERROR:
				msg = "File is corrupt";
				break;

			case LZMA_BUF_ERROR:
				msg = "Unexpected end of input";
				break;

			case LZMA_MEMLIMIT_ERROR:
				msg = "Reached memory limit";
				break;

			default:
				maybe_errx("Unknown error (%d)", ret);
				break;
			}
			maybe_errx("%s", msg);

		}
	}
}
/*
 * uncompress input to output then close the input.  return the
 * uncompressed size written, and put the compressed sized read
 * into `*gsizep'.
 */
static off_t
gz_uncompress(int in, int out, char *pre, size_t prelen, off_t *gsizep,
	      const char *filename)
{
	z_stream z;
	char *outbufp, *inbufp;
	off_t out_tot = -1, in_tot = 0;
	uint32_t out_sub_tot = 0;
	enum {
		GZSTATE_MAGIC0,
		GZSTATE_MAGIC1,
		GZSTATE_METHOD,
		GZSTATE_FLAGS,
		GZSTATE_SKIPPING,
		GZSTATE_EXTRA,
		GZSTATE_EXTRA2,
		GZSTATE_EXTRA3,
		GZSTATE_ORIGNAME,
		GZSTATE_COMMENT,
		GZSTATE_HEAD_CRC1,
		GZSTATE_HEAD_CRC2,
		GZSTATE_INIT,
		GZSTATE_READ,
		GZSTATE_CRC,
		GZSTATE_LEN,
	} state = GZSTATE_MAGIC0;
	int flags = 0, skip_count = 0;
	int error = Z_STREAM_ERROR, done_reading = 0;
	uLong crc = 0;
	ssize_t wr;
	int needmore = 0;

#define ADVANCE()       { z.next_in++; z.avail_in--; }

	if ((outbufp = malloc(BUFLEN)) == NULL) {
		maybe_err("malloc failed");
		goto out2;
	}
	if ((inbufp = malloc(BUFLEN)) == NULL) {
		maybe_err("malloc failed");
		goto out1;
	}

	memset(&z, 0, sizeof z);
	z.avail_in = prelen;
	z.next_in = (unsigned char *)pre;
	z.avail_out = BUFLEN;
	z.next_out = (unsigned char *)outbufp;
	z.zalloc = NULL;
	z.zfree = NULL;
	z.opaque = 0;

	in_tot = prelen;
	out_tot = 0;

	for (;;) {
		if ((z.avail_in == 0 || needmore) && done_reading == 0) {
			ssize_t in_size;

			if (z.avail_in > 0) {
				memmove(inbufp, z.next_in, z.avail_in);
			}
			z.next_in = (unsigned char *)inbufp;
			in_size = read(in, z.next_in + z.avail_in,
			    BUFLEN - z.avail_in);

			if (in_size == -1) {
				maybe_warn("failed to read stdin");
				goto stop_and_fail;
			} else if (in_size == 0) {
				done_reading = 1;
			}

			z.avail_in += in_size;
			needmore = 0;

			in_tot += in_size;
		}
		if (z.avail_in == 0) {
			if (done_reading && state != GZSTATE_MAGIC0) {
				maybe_warnx("%s: unexpected end of file",
					    filename);
				goto stop_and_fail;
			}
			goto stop;
		}
		switch (state) {
		case GZSTATE_MAGIC0:
			if (*z.next_in != GZIP_MAGIC0) {
				if (in_tot > 0) {
					maybe_warnx("%s: trailing garbage "
						    "ignored", filename);
					goto stop;
				}
				maybe_warnx("input not gziped (MAGIC0)");
				goto stop_and_fail;
			}
			ADVANCE();
			state++;
			out_sub_tot = 0;
			crc = crc32(0L, Z_NULL, 0);
			break;

		case GZSTATE_MAGIC1:
			if (*z.next_in != GZIP_MAGIC1 &&
			    *z.next_in != GZIP_OMAGIC1) {
				maybe_warnx("input not gziped (MAGIC1)");
				goto stop_and_fail;
			}
			ADVANCE();
			state++;
			break;

		case GZSTATE_METHOD:
			if (*z.next_in != Z_DEFLATED) {
				maybe_warnx("unknown compression method");
				goto stop_and_fail;
			}
			ADVANCE();
			state++;
			break;

		case GZSTATE_FLAGS:
			flags = *z.next_in;
			ADVANCE();
			skip_count = 6;
			state++;
			break;

		case GZSTATE_SKIPPING:
			if (skip_count > 0) {
				skip_count--;
				ADVANCE();
			} else
				state++;
			break;

		case GZSTATE_EXTRA:
			if ((flags & EXTRA_FIELD) == 0) {
				state = GZSTATE_ORIGNAME;
				break;
			}
			skip_count = *z.next_in;
			ADVANCE();
			state++;
			break;

		case GZSTATE_EXTRA2:
			skip_count |= ((*z.next_in) << 8);
			ADVANCE();
			state++;
			break;

		case GZSTATE_EXTRA3:
			if (skip_count > 0) {
				skip_count--;
				ADVANCE();
			} else
				state++;
			break;

		case GZSTATE_ORIGNAME:
			if ((flags & ORIG_NAME) == 0) {
				state++;
				break;
			}
			if (*z.next_in == 0)
				state++;
			ADVANCE();
			break;

		case GZSTATE_COMMENT:
			if ((flags & COMMENT) == 0) {
				state++;
				break;
			}
			if (*z.next_in == 0)
				state++;
			ADVANCE();
			break;

		case GZSTATE_HEAD_CRC1:
			if (flags & HEAD_CRC)
				skip_count = 2;
			else
				skip_count = 0;
			state++;
			break;

		case GZSTATE_HEAD_CRC2:
			if (skip_count > 0) {
				skip_count--;
				ADVANCE();
			} else
				state++;
			break;

		case GZSTATE_INIT:
			if (inflateInit2(&z, -MAX_WBITS) != Z_OK) {
				maybe_warnx("failed to inflateInit");
				goto stop_and_fail;
			}
			state++;
			break;

		case GZSTATE_READ:
			error = inflate(&z, Z_FINISH);
			switch (error) {
			/* Z_BUF_ERROR goes with Z_FINISH... */
			case Z_BUF_ERROR:
				if (z.avail_out > 0 && !done_reading)
					continue;

			case Z_STREAM_END:
			case Z_OK:
				break;

			case Z_NEED_DICT:
				maybe_warnx("Z_NEED_DICT error");
				goto stop_and_fail;
			case Z_DATA_ERROR:
				maybe_warnx("data stream error");
				goto stop_and_fail;
			case Z_STREAM_ERROR:
				maybe_warnx("internal stream error");
				goto stop_and_fail;
			case Z_MEM_ERROR:
				maybe_warnx("memory allocation error");
				goto stop_and_fail;

			default:
				maybe_warn("unknown error from inflate(): %d",
				    error);
			}
			wr = BUFLEN - z.avail_out;

			if (wr != 0) {
				crc = crc32(crc, (const Bytef *)outbufp, (unsigned)wr);
				if (
#ifndef SMALL
				    /* don't write anything with -t */
				    tflag == 0 &&
#endif
				    write(out, outbufp, wr) != wr) {
					maybe_warn("error writing to output");
					goto stop_and_fail;
				}

				out_tot += wr;
				out_sub_tot += wr;
			}

			if (error == Z_STREAM_END) {
				inflateEnd(&z);
				state++;
			}

			z.next_out = (unsigned char *)outbufp;
			z.avail_out = BUFLEN;

			break;
		case GZSTATE_CRC:
			{
				uLong origcrc;

				if (z.avail_in < 4) {
					if (!done_reading) {
						needmore = 1;
						continue;
					}
					maybe_warnx("truncated input");
					goto stop_and_fail;
				}
				origcrc = ((unsigned)z.next_in[0] & 0xff) |
					((unsigned)z.next_in[1] & 0xff) << 8 |
					((unsigned)z.next_in[2] & 0xff) << 16 |
					((unsigned)z.next_in[3] & 0xff) << 24;
				if (origcrc != crc) {
					maybe_warnx("invalid compressed"
					     " data--crc error");
					goto stop_and_fail;
				}
			}

			z.avail_in -= 4;
			z.next_in += 4;

			if (!z.avail_in && done_reading) {
				goto stop;
			}
			state++;
			break;
		case GZSTATE_LEN:
			{
				uLong origlen;

				if (z.avail_in < 4) {
					if (!done_reading) {
						needmore = 1;
						continue;
					}
					maybe_warnx("truncated input");
					goto stop_and_fail;
				}
				origlen = ((unsigned)z.next_in[0] & 0xff) |
					((unsigned)z.next_in[1] & 0xff) << 8 |
					((unsigned)z.next_in[2] & 0xff) << 16 |
					((unsigned)z.next_in[3] & 0xff) << 24;

				if (origlen != out_sub_tot) {
					maybe_warnx("invalid compressed"
					     " data--length error");
					goto stop_and_fail;
				}
			}
				
			z.avail_in -= 4;
			z.next_in += 4;

			if (error < 0) {
				maybe_warnx("decompression error");
				goto stop_and_fail;
			}
			state = GZSTATE_MAGIC0;
			break;
		}
		continue;
stop_and_fail:
		out_tot = -1;
stop:
		break;
	}
	if (state > GZSTATE_INIT)
		inflateEnd(&z);

	free(inbufp);
out1:
	free(outbufp);
out2:
	if (gsizep)
		*gsizep = in_tot;
	return (out_tot);
}
/* compress input to output. Return bytes read, -1 on error */
static off_t
gz_compress(int in, int out, off_t *gsizep, const char *origname, uint32_t mtime)
{
	z_stream z;
	char *outbufp, *inbufp;
	off_t in_tot = 0, out_tot = 0;
	ssize_t in_size;
	int i, error;
	uLong crc;
#ifdef SMALL
	static char header[] = { GZIP_MAGIC0, GZIP_MAGIC1, Z_DEFLATED, 0,
				 0, 0, 0, 0,
				 0, OS_CODE };
#endif

	outbufp = malloc(BUFLEN);
	inbufp = malloc(BUFLEN);
	if (outbufp == NULL || inbufp == NULL) {
		maybe_err("malloc failed");
		goto out;
	}

	memset(&z, 0, sizeof z);
	z.zalloc = Z_NULL;
	z.zfree = Z_NULL;
	z.opaque = 0;

#ifdef SMALL
	memcpy(outbufp, header, sizeof header);
	i = sizeof header;
#else
	if (nflag != 0) {
		mtime = 0;
		origname = "";
	}

	i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c%c%c%c%c%s", 
		     GZIP_MAGIC0, GZIP_MAGIC1, Z_DEFLATED,
		     *origname ? ORIG_NAME : 0,
		     mtime & 0xff,
		     (mtime >> 8) & 0xff,
		     (mtime >> 16) & 0xff,
		     (mtime >> 24) & 0xff,
		     numflag == 1 ? 4 : numflag == 9 ? 2 : 0,
		     OS_CODE, origname);
	if (i >= BUFLEN)     
		/* this need PATH_MAX > BUFLEN ... */
		maybe_err("snprintf");
	if (*origname)
		i++;
#endif

	z.next_out = (unsigned char *)outbufp + i;
	z.avail_out = BUFLEN - i;

	error = deflateInit2(&z, numflag, Z_DEFLATED,
			     (-MAX_WBITS), 8, Z_DEFAULT_STRATEGY);
	if (error != Z_OK) {
		maybe_warnx("deflateInit2 failed");
		in_tot = -1;
		goto out;
	}

	crc = crc32(0L, Z_NULL, 0);
	for (;;) {
		if (z.avail_out == 0) {
			if (write(out, outbufp, BUFLEN) != BUFLEN) {
				maybe_warn("write");
				out_tot = -1;
				goto out;
			}

			out_tot += BUFLEN;
			z.next_out = (unsigned char *)outbufp;
			z.avail_out = BUFLEN;
		}

		if (z.avail_in == 0) {
			in_size = read(in, inbufp, BUFLEN);
			if (in_size < 0) {
				maybe_warn("read");
				in_tot = -1;
				goto out;
			}
			if (in_size == 0)
				break;

			crc = crc32(crc, (const Bytef *)inbufp, (unsigned)in_size);
			in_tot += in_size;
			z.next_in = (unsigned char *)inbufp;
			z.avail_in = in_size;
		}

		error = deflate(&z, Z_NO_FLUSH);
		if (error != Z_OK && error != Z_STREAM_END) {
			maybe_warnx("deflate failed");
			in_tot = -1;
			goto out;
		}
	}

	/* clean up */
	for (;;) {
		size_t len;
		ssize_t w;

		error = deflate(&z, Z_FINISH);
		if (error != Z_OK && error != Z_STREAM_END) {
			maybe_warnx("deflate failed");
			in_tot = -1;
			goto out;
		}

		len = (char *)z.next_out - outbufp;

		w = write(out, outbufp, len);
		if (w == -1 || (size_t)w != len) {
			maybe_warn("write");
			out_tot = -1;
			goto out;
		}
		out_tot += len;
		z.next_out = (unsigned char *)outbufp;
		z.avail_out = BUFLEN;

		if (error == Z_STREAM_END)
			break;
	}

	if (deflateEnd(&z) != Z_OK) {
		maybe_warnx("deflateEnd failed");
		in_tot = -1;
		goto out;
	}

	i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c%c%c", 
		 (int)crc & 0xff,
		 (int)(crc >> 8) & 0xff,
		 (int)(crc >> 16) & 0xff,
		 (int)(crc >> 24) & 0xff,
		 (int)in_tot & 0xff,
		 (int)(in_tot >> 8) & 0xff,
		 (int)(in_tot >> 16) & 0xff,
		 (int)(in_tot >> 24) & 0xff);
	if (i != 8)
		maybe_err("snprintf");
#if 0
	if (in_tot > 0xffffffff)
		maybe_warn("input file size >= 4GB cannot be saved");
#endif
	if (write(out, outbufp, i) != i) {
		maybe_warn("write");
		in_tot = -1;
	} else
		out_tot += i;

out:
	if (inbufp != NULL)
		free(inbufp);
	if (outbufp != NULL)
		free(outbufp);
	if (gsizep)
		*gsizep = out_tot;
	return in_tot;
}
/* split up $GZIP and prepend it to the argument list */
static void
prepend_gzip(char *gzip, int *argc, char ***argv)
{
	char *s, **nargv, **ac;
	int nenvarg = 0, i;

	/* scan how many arguments there are */
	for (s = gzip;;) {
		while (*s == ' ' || *s == '\t')
			s++;
		if (*s == 0)
			goto count_done;
		nenvarg++;
		while (*s != ' ' && *s != '\t')
			if (*s++ == 0)
				goto count_done;
	}
count_done:
	/* punt early */
	if (nenvarg == 0)
		return;

	*argc += nenvarg;
	ac = *argv;

	nargv = (char **)malloc((*argc + 1) * sizeof(char *));
	if (nargv == NULL)
		maybe_err("malloc");

	/* stash this away */
	*argv = nargv;

	/* copy the program name first */
	i = 0;
	nargv[i++] = *(ac++);

	/* take a copy of $GZIP and add it to the array */
	s = strdup(gzip);
	if (s == NULL)
		maybe_err("strdup");
	for (;;) {
		/* Skip whitespaces. */
		while (*s == ' ' || *s == '\t')
			s++;
		if (*s == 0)
			goto copy_done;
		nargv[i++] = s;
		/* Find the end of this argument. */
		while (*s != ' ' && *s != '\t')
			if (*s++ == 0)
				/* Argument followed by NUL. */
				goto copy_done;
		/* Terminate by overwriting ' ' or '\t' with NUL. */
		*s++ = 0;
	}
copy_done:

	/* copy the original arguments and a NULL */
	while (*ac)
		nargv[i++] = *(ac++);
	nargv[i] = NULL;
}
Exemple #6
0
static off_t
unbzip2(int in, int out, char *pre, size_t prelen, off_t *bytes_in)
{
	int		ret, end_of_file, cold = 0;
	off_t		bytes_out = 0;
	bz_stream	bzs;
	static char	*inbuf, *outbuf;

	if (inbuf == NULL)
		inbuf = malloc(BUFLEN);
	if (outbuf == NULL)
		outbuf = malloc(BUFLEN);
	if (inbuf == NULL || outbuf == NULL)
	        maybe_err("malloc");

	bzs.bzalloc = NULL;
	bzs.bzfree = NULL;
	bzs.opaque = NULL;

	end_of_file = 0;
	ret = BZ2_bzDecompressInit(&bzs, 0, 0);
	if (ret != BZ_OK)
	        maybe_errx("bzip2 init");

	/* Prepend. */
	bzs.avail_in = prelen;
	bzs.next_in = pre;

	if (bytes_in)
		*bytes_in = prelen;

	while (ret == BZ_OK) {
	        if (bzs.avail_in == 0 && !end_of_file) {
			ssize_t	n;

	                n = read(in, inbuf, BUFLEN);
	                if (n < 0)
	                        maybe_err("read");
	                if (n == 0)
	                        end_of_file = 1;
	                bzs.next_in = inbuf;
	                bzs.avail_in = n;
			if (bytes_in)
				*bytes_in += n;
	        }

	        bzs.next_out = outbuf;
	        bzs.avail_out = BUFLEN;
	        ret = BZ2_bzDecompress(&bzs);

	        switch (ret) {
	        case BZ_STREAM_END:
	        case BZ_OK:
	                if (ret == BZ_OK && end_of_file) {
				/*
				 * If we hit this after a stream end, consider
				 * it as the end of the whole file and don't
				 * bail out.
				 */
				if (cold == 1)
					ret = BZ_STREAM_END;
				else
					maybe_errx("truncated file");
			}
			cold = 0;
	                if (!tflag && bzs.avail_out != BUFLEN) {
				ssize_t	n;

	                        n = write(out, outbuf, BUFLEN - bzs.avail_out);
	                        if (n < 0)
	                                maybe_err("write");
	                	bytes_out += n;
	                }
			if (ret == BZ_STREAM_END && !end_of_file) {
				if (BZ2_bzDecompressEnd(&bzs) != BZ_OK ||
				    BZ2_bzDecompressInit(&bzs, 0, 0) != BZ_OK)
					maybe_errx("bzip2 re-init");
				cold = 1;
				ret = BZ_OK;
			}
			break;

	        case BZ_DATA_ERROR:
	                maybe_warnx("bzip2 data integrity error");
			break;

	        case BZ_DATA_ERROR_MAGIC:
	                maybe_warnx("bzip2 magic number error");
			break;

	        case BZ_MEM_ERROR:
	                maybe_warnx("bzip2 out of memory");
			break;
		
		default:	
			maybe_warnx("unknown bzip2 error: %d", ret);
			break;
	        }
	}

	if (ret != BZ_STREAM_END || BZ2_bzDecompressEnd(&bzs) != BZ_OK)
	        return (-1);

	return (bytes_out);
}
Exemple #7
0
/* do what is asked for, for the path name */
static void
handle_pathname(char *path)
{
	char *opath = path, *s = NULL;
	ssize_t len;
	int slen;
	struct stat sb;

	/* check for stdout/stdin */
	if (path[0] == '-' && path[1] == '\0') {
		if (dflag)
			handle_stdin();
		else
			handle_stdout();
		return;
	}

#ifdef __APPLE__
	if (zcat && COMPAT_MODE("bin/zcat", "Unix2003")) {
		char *suffix = strrchr(path, '.');
		if (suffix == NULL || strcmp(suffix, Z_SUFFIX) != 0) {
			len = strlen(path);
			slen = sizeof(Z_SUFFIX) - 1;
			s = malloc(len + slen + 1);
			memcpy(s, path, len);
			memcpy(s + len, Z_SUFFIX, slen + 1);
			path = s;
		}
	}
#endif

retry:
	if (stat(path, &sb) != 0 || (fflag == 0 && cflag == 0 &&
	    lstat(path, &sb) != 0)) {
		/* lets try <path>.gz if we're decompressing */
		if (dflag && s == NULL && errno == ENOENT) {
			len = strlen(path);
			slen = suffixes[0].ziplen;
			s = malloc(len + slen + 1);
			if (s == NULL)
				maybe_err("malloc");
			memcpy(s, path, len);
			memcpy(s + len, suffixes[0].zipped, slen + 1);
			path = s;
			goto retry;
		}
#ifdef __APPLE__
		/* Include actual path for clarity. */
		maybe_warn("can't stat: %s (%s)", opath, path);
#else
		maybe_warn("can't stat: %s", opath);
#endif
		goto out;
	}

	if (S_ISDIR(sb.st_mode)) {
#ifndef SMALL
		if (rflag)
			handle_dir(path);
		else
#endif
			maybe_warnx("%s is a directory", path);
		goto out;
	}

	if (S_ISREG(sb.st_mode))
		handle_file(path, &sb);
	else
		maybe_warnx("%s is not a regular file", path);

out:
	if (s)
		free(s);
}