ar.c -- main file
***********************************************************/
+static char *version = "0.01";
+
static char *usage =
"ar -- compression archiver -- written by Haruhiko Okumura\n"
" PC-VAN:SCIENCE CompuServe:74050,1022\n"
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
+#include <errno.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <utime.h>
#include "ar.h"
+extern char *basename(const char *);
+
+struct lha_method methods[] = {
+ /* id, dicbit, pbit, maxmatch */
+ /* note: dicbit == 0 means no compress */
+ {"-lh0-", 0, 0, 0}, /* 0: no compress */
+ {"-lh1-", 12, 0, 60}, /* 1: 2^12 = 4KB dynamic huffman (LHarc) */
+ {"-lh2-", 13, 0,256}, /* 2: 2^13 = 8KB dynamic huffman */
+ {"-lh3-", 13, 0,256}, /* 3: 2^13 = 8KB static huffman */
+ {"-lh4-", 12, 4,256}, /* 4: 2^12 = 4KB static huffman (pos and len)*/
+ {"-lh5-", 13, 4,256}, /* 5: 2^13 = 8KB static huffman (pos and len)*/
+ {"-lh6-", 15, 5,256}, /* 6: 2^15 = 32KB static huffman (pos and len)*/
+ {"-lh7-", 16, 5,256}, /* 7: 2^16 = 64KB static huffman (pos and len)*/
+ {"-lzs-", 11, 0, 17}, /* 8: 2^11 = 2KB (LArc) */
+ {"-lz5-", 12, 0, 17}, /* 9: 2^12 = 4KB (LArc) */
+ {"-lz4-", 0, 0, 0}, /* 1: no compress (LArc) */
+ {"-lhd-", 0, 0, 0}, /* 1: directory */
+};
+
+struct lha_opts opts;
+
+struct lha_method *
+which_method(char *id)
+{
+ int i;
+
+ for (i = 0; i < sizeof(methods)/sizeof(methods[0]); i++) {
+ if (strncmp(id, methods[i].id, sizeof(methods[0].id)) == 0) {
+ return &methods[i];
+ }
+ }
+ return NULL;
+}
+
#define FNAME_MAX (255 - 25) /* max strlen(filename) */
-#define namelen header[19]
-#define filename ((char *)&header[20])
int unpackable; /* global, set in io.c */
ulong compsize, origsize; /* global */
-static uchar buffer[DICSIZ];
-static uchar header[255];
-static uchar headersize, headersum;
-static uint file_crc;
static char *temp_name;
+static void
+print_usage()
+{
+ printf("%s", usage);
+ exit(0);
+}
+
+static void
+print_version()
+{
+ printf("version %s\n", version);
+ exit(0);
+}
+
static uint
ratio(ulong a, ulong b)
{ /* [(1000a + [b/2]) / b] */
return (uint) ((a + (b >> 1)) / b);
}
+static char*
+os_string(char id)
+{
+ int i;
+ static struct os {
+ char id;
+ char *name;
+ } os_types[] = {
+ {'M', "MS-DOS"}, /* Microsoft */
+ {'U', "Unix"}, /* Unix or POSIX compliant OS */
+ {'J', "Java"}, /* Sun Microsystems */
+ {'\0', "generic"},
+ {'w', "Win9x"}, /* reserved by UNLHA32.DLL */
+ {'W', "WinNT"}, /* reserved by UNLHA32.DLL */
+ {'2', "OS/2"}, /* IBM OS/2 */
+ {'9', "OS9"}, /* unknown */
+ {'K', "OS/68K"}, /* unknown */
+ {'3', "OS/386"}, /* unknown */
+ {'H', "Human"}, /* SHARP Human68K */
+ {'C', "CP/M"}, /* Digital Research */
+ {'F', "FLEX"}, /* unknown */
+ {'m', "Mac"}, /* Apple */
+ {'R', "Runser"}, /* unknown */
+ {'T', "TownsOS"}, /* Fujitsu FM-TOWNS */
+ {'X', "XOSK"}, /* unknown */
+ };
+
+ for (i = 0; i < sizeof(os_types)/sizeof(os_types[0]); i++) {
+ if (id == os_types[i].id)
+ return os_types[i].name;
+ }
+
+ return "Unknown";
+}
+
static void
-put_to_header(int i, int n, ulong x)
+put_header(char *buf, int *i, int n, uint32_t x)
{
while (--n >= 0) {
- header[i++] = (uchar) ((uint) x & 0xFF);
+ buf[(*i)++] = (uchar) (x & 0xFF);
x >>= 8;
}
}
-static ulong
-get_from_header(int i, int n)
+static void
+put_header_tmp(char *buf, int i, int n, uint32_t x)
+{
+ put_header(buf, &i, n, x);
+}
+
+void
+put_string(char *buf, int *n, size_t size, char *p)
+{
+ memcpy(buf + *n, p, size);
+ *n += size;
+}
+
+static uint32_t
+get_header(char *buf, int *i, int size)
{
- ulong s;
+ uint32_t s;
+ int n;
s = 0;
- while (--n >= 0)
- s = (s << 8) + header[i + n]; /* little endian */
+ for (n = size-1; n >= 0; n--) {
+ s = (s << 8) + (unsigned char)buf[*i + n]; /* little endian */
+ }
+ *i += size;
return s;
}
+void
+get_string(char *buf, int *i, size_t size, char *p)
+{
+ memcpy(p, buf + *i, size);
+ *i += size;
+}
+
static uint
-calc_headersum(void)
+calc_headersum(char *buf, int size)
{
int i;
uint s;
s = 0;
- for (i = 0; i < headersize; i++)
- s += header[i];
+ for (i = 0; i < size; i++)
+ s += buf[i];
return s & 0xFF;
}
+#if 0
+int
+get_byte(char *buf)
+{
+ return *(unsigned char*)buf;
+}
+
+uint16_t
+get_word(char *buf)
+{
+ return get_byte(buf) | (get_byte(buf+1) << 8);
+}
+
+uint32_t
+get_dword(char *buf)
+{
+ return get_byte(buf) |
+ (get_byte(buf+1) << 8) |
+ (get_byte(buf+2) << 16) |
+ (get_byte(buf+3) << 24);
+}
+
+void
+get_char(char *buf, char *p, size_t size)
+{
+ memcpy(p, buf, size);
+}
+
+void
+put_byte(char *buf, int c)
+{
+ *buf = (unsigned char)(c & 0xff);
+}
+
+void
+put_word(char *buf, uint16_t c)
+{
+ put_byte(buf, c);
+ put_byte(buf+1, c>>8);
+}
+
+void
+put_dword(char *buf, uint32_t c)
+{
+ put_byte(buf, c);
+ put_byte(buf+1, c>>8);
+ put_byte(buf+2, c>>16);
+ put_byte(buf+3, c>>24);
+}
+
+void
+put_char(char *buf, char *p, size_t size)
+{
+ memcpy(buf, p, size);
+}
+
+#endif
+time_t
+ftime_to_time_t(uint32_t ftime)
+{
+ struct tm tm;
+ /* ftime is time structure on MS-DOS
+
+ 32 24 16 8
+ 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
+ |-------------|-------|---------|---------|-----------|---------|
+ year(-1980) month day hour minute second/2
+ (1-127) (1-12) (1-31) (0-23) (0-59) (0-29)
+ */
+
+ memset(&tm, 0, sizeof(tm));
+ tm.tm_year = (ftime >> 25) + 1980 - 1900;
+ tm.tm_mon = ((ftime >> 21) & 0x0f) - 1;
+ tm.tm_mday = (ftime >> 16) & 0x1f;
+ tm.tm_hour = (ftime >> 11) & 0x1f;
+ tm.tm_min = (ftime >> 5) & 0x3f;
+ tm.tm_sec = (ftime & 0x1f) * 2;
+
+ return mktime(&tm);
+}
+
+uint32_t
+time_t_to_ftime(time_t t)
+{
+ struct tm tm;
+ /* ftime is time structure on MS-DOS
+
+ 32 24 16 8
+ 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
+ |-------------|-------|---------|---------|-----------|---------|
+ year(-1980) month day hour minute second/2
+ (1-127) (1-12) (1-31) (0-23) (0-59) (0-29)
+ */
+
+#if HAVE_LOCALTIME_R
+ localtime_r(&t, &tm);
+#else
+ tm = *localtime(&t);
+#endif
+
+ return (uint32_t)(tm.tm_year + 1900 - 1980) << 25
+ | (uint32_t)(tm.tm_mon + 1) << 21
+ | (uint32_t)(tm.tm_mday) << 16
+ | (uint32_t)(tm.tm_hour) << 11
+ | (uint32_t)(tm.tm_min) << 5
+ | (uint32_t)(tm.tm_sec / 2);
+}
+
+
+/*
+ * level 0 header
+ *
+ *
+ * offset size field name
+ * ----------------------------------
+ * 0 1 header size [*1]
+ * 1 1 header sum
+ * ---------------------------------------
+ * 2 5 method ID ^
+ * 7 4 packed size [*2] |
+ * 11 4 original size |
+ * 15 2 time |
+ * 17 2 date |
+ * 19 1 attribute | [*1] header size (X+Y+22)
+ * 20 1 level (0x00 fixed) |
+ * 21 1 name length |
+ * 22 X pathname |
+ * X +22 2 file crc (CRC-16) |
+ * X +24 Y ext-header(old style) v
+ * -------------------------------------------------
+ * X+Y+24 data ^
+ * : | [*2] packed size
+ * : v
+ * -------------------------------------------------
+ *
+ * ext-header(old style)
+ * 0 1 ext-type ('U')
+ * 1 1 minor version
+ * 2 4 UNIX time
+ * 6 2 mode
+ * 8 2 uid
+ * 10 2 gid
+ *
+ * attribute (MS-DOS)
+ * bit1 read only
+ * bit2 hidden
+ * bit3 system
+ * bit4 volume label
+ * bit5 directory
+ * bit6 archive bit (need to backup)
+ *
+ */
static int
-read_header(void)
+read_header_lv0(FILE *fp, char *buf, struct lzh_header *h)
{
- headersize = (uchar) fgetc(arcfile);
- if (headersize == 0)
- return 0; /* end of archive */
- headersum = (uchar) fgetc(arcfile);
- fread_crc(header, headersize, arcfile); /* CRC not used */
- if (calc_headersum() != headersum)
+ int headersize;
+ int headersum;
+ int ext_headersize;
+ int pos = 0;
+ int ext_size;
+
+ headersize = get_header(buf, &pos, 1);
+ headersum = get_header(buf, &pos, 1);
+
+ fread_crc(buf + 21, headersize - (21 - pos), fp); /* CRC not used */
+
+ if (calc_headersum(buf+pos, headersize) != headersum)
error("Header sum error");
- compsize = get_from_header(5, 4);
- origsize = get_from_header(9, 4);
- file_crc = (uint) get_from_header(headersize - 5, 2);
- filename[namelen] = '\0';
+
+ get_string(buf, &pos, 5, h->method);
+ h->compsize = get_header(buf, &pos, 4);
+ h->origsize = get_header(buf, &pos, 4);
+ h->mtime = ftime_to_time_t(get_header(buf, &pos, 4));
+ /* attrib = */ get_header(buf, &pos, 1);
+ h->level = get_header(buf, &pos, 1); /* header level */
+ h->namelen = get_header(buf, &pos, 1);
+ if (pos + h->namelen > headersize+2) {
+ warn("path name is too long");
+ h->namelen = headersize+2-pos;
+ }
+ get_string(buf, &pos, h->namelen, h->filename);
+ h->filename[h->namelen] = 0;
+ h->file_crc = get_header(buf, &pos, 2);
+ h->os_id = 0; /* generic */
+
+ ext_size = headersize - pos;
+
+ if (ext_size > 0) {
+ if (ext_size > 1) {
+ h->os_id = get_header(buf, &pos, 1);
+ ext_size--;
+ }
+ if (ext_size > 1) {
+ get_header(buf, &pos, 1); /* minor version */
+ ext_size--;
+ }
+ if (ext_size > 4) {
+ h->mtime = get_header(buf, &pos, 4);
+ ext_size -= 4;
+ }
+ if (ext_size > 2) {
+ get_header(buf, &pos, 2); /* mode */
+ ext_size -= 2;
+ }
+ if (ext_size > 2) {
+ get_header(buf, &pos, 2); /* uid */
+ ext_size -= 2;
+ }
+ if (ext_size > 2) {
+ get_header(buf, &pos, 2); /* gid */
+ ext_size -= 2;
+ }
+ }
+
return 1; /* success */
}
-static void
-write_header(void)
+/*
+ * level 1 header
+ *
+ *
+ * offset size field name
+ * -----------------------------------
+ * 0 1 header size [*1]
+ * 1 1 header sum
+ * -------------------------------------
+ * 2 5 method ID ^
+ * 7 4 skip size [*2] |
+ * 11 4 original size |
+ * 15 2 time |
+ * 17 2 date |
+ * 19 1 attribute (0x20 fixed) | [*1] header size (X+Y+25)
+ * 20 1 level (0x01 fixed) |
+ * 21 1 name length |
+ * 22 X filename |
+ * X+ 22 2 file crc (CRC-16) |
+ * X+ 24 1 OS ID |
+ * X +25 Y ??? |
+ * X+Y+25 2 next-header size v
+ * -------------------------------------------------
+ * X+Y+27 Z ext-header ^
+ * : |
+ * ----------------------------------- | [*2] skip size
+ * X+Y+Z+27 data |
+ * : v
+ * -------------------------------------------------
+ *
+ */
+static int
+read_header_lv1(FILE *fp, char *buf, struct lzh_header *h)
+{
+ int headersize;
+ int headersum;
+ int ext_headersize;
+ char dirname[1024] = "";
+ int dirnamelen = 0;
+ int pos = 0;
+
+ headersize = get_header(buf, &pos, 1);
+ headersum = get_header(buf, &pos, 1);
+
+ fread_crc(buf + 21, headersize - (21 - 2), fp); /* CRC not used */
+
+ if (calc_headersum(&buf[pos], headersize) != headersum)
+ error("Header sum error");
+
+ get_string(buf, &pos, 5, h->method);
+ h->compsize = get_header(buf, &pos, 4);
+ h->origsize = get_header(buf, &pos, 4);
+ h->mtime = ftime_to_time_t(get_header(buf, &pos, 4));
+ get_header(buf, &pos, 1); /* attribute */
+ h->level = get_header(buf, &pos, 1); /* header level */
+ h->namelen = get_header(buf, &pos, 1);
+ get_string(buf, &pos, h->namelen, h->filename);
+ h->filename[h->namelen] = 0;
+ h->file_crc = get_header(buf, &pos, 2);
+ h->os_id = get_header(buf, &pos, 1);
+
+ ext_headersize = get_header(buf, &pos, 2);
+
+ while (ext_headersize != 0) {
+ char extbuf[4096];
+ uchar ext_type;
+ int extpos = 0;
+
+ h->compsize -= ext_headersize;
+
+ if (fread(extbuf, ext_headersize, 1, fp) != 1) {
+ error("can't read ext header");
+ }
+
+ ext_type = get_header(extbuf, &extpos, 1);
+ switch (ext_type) {
+ case 1:
+ /* filename header */
+ h->namelen = ext_headersize - 3;
+ get_string(extbuf, &extpos, h->namelen, h->filename);
+ h->filename[h->namelen] = 0;
+ break;
+ case 2:
+ /* dirname header */
+ dirnamelen = ext_headersize - 3;
+ get_string(extbuf, &extpos, dirnamelen, dirname);
+ dirname[dirnamelen] = 0;
+ break;
+ case 0x54:
+ h->mtime = get_header(extbuf, &extpos, 4);
+ break;
+ default:
+ break;
+ }
+ extpos = ext_headersize - 2;
+ ext_headersize = get_header(extbuf, &extpos, 2);
+ }
+
+ if (dirnamelen > 0 && dirname[dirnamelen-1] != '/') {
+ dirname[dirnamelen++] = '/';
+ }
+
+ strcat(dirname, h->filename);
+ h->namelen = strlen(dirname);
+ strcpy(h->filename, dirname);
+
+ return 1; /* success */
+}
+
+/*
+ * level 2 header
+ *
+ *
+ * offset size field name
+ * --------------------------------------------------
+ * 0 2 total header size [*1] ^
+ * ----------------------- |
+ * 2 5 method ID |
+ * 7 4 packed size [*2] |
+ * 11 4 original size |
+ * 15 4 time |
+ * 19 1 RESERVED (0x20 fixed) | [*1] total header size
+ * 20 1 level (0x02 fixed) | (X+26+(1))
+ * 21 2 file crc (CRC-16) |
+ * 23 1 OS ID |
+ * 24 2 next-header size |
+ * ----------------------------------- |
+ * 26 X ext-header |
+ * : |
+ * ----------------------------------- |
+ * X +26 (1) padding v
+ * -------------------------------------------------
+ * X +26+(1) data ^
+ * : | [*2] packed size
+ * : v
+ * -------------------------------------------------
+ *
+ */
+static int
+read_header_lv2(FILE *fp, char *buf, struct lzh_header *h)
{
- fputc(headersize, outfile);
- /* We've destroyed file_crc by null-terminating filename. */
- put_to_header(headersize - 5, 2, (ulong) file_crc);
- fputc(calc_headersum(), outfile);
- fwrite_crc(header, headersize, outfile); /* CRC not used */
+ int headersize;
+ int headersum;
+ int ext_headersize;
+ int remainder;
+ char dirname[1024] = "";
+ int dirnamelen = 0;
+ int pos = 0;
+
+ headersize = get_header(buf, &pos, 2);
+
+ fread_crc(buf + 21, 26 - 21, fp); /* CRC not used */
+
+ get_string(buf, &pos, 5, h->method);
+ h->compsize = get_header(buf, &pos, 4);
+ h->origsize = get_header(buf, &pos, 4);
+ h->mtime = get_header(buf, &pos, 4);
+ get_header(buf, &pos, 1); /* attrib */
+ h->level = get_header(buf, &pos, 1); /* header level */
+ h->file_crc = get_header(buf, &pos, 2);
+ h->os_id = get_header(buf, &pos, 1);
+
+ ext_headersize = get_header(buf, &pos, 2);
+
+ remainder = headersize - pos;
+
+ while (ext_headersize != 0) {
+ char extbuf[4096];
+ uchar ext_type;
+ int extpos = 0;
+
+ remainder -= ext_headersize;
+
+ if (fread(extbuf, ext_headersize, 1, fp) != 1) {
+ error("can't read ext header");
+ }
+ ext_type = get_header(extbuf, &extpos, 1);
+ switch (ext_type) {
+ case 0:
+ /* header crc */
+ break;
+ case 1:
+ /* filename header */
+ h->namelen = ext_headersize - 3;
+ get_string(extbuf, &extpos, h->namelen, h->filename);
+ h->filename[h->namelen] = 0;
+ break;
+ case 2:
+ /* dirname header */
+ dirnamelen = ext_headersize - 3;
+ get_string(extbuf, &extpos, dirnamelen, dirname);
+ dirname[dirnamelen] = 0;
+ break;
+ default:
+ break;
+ }
+ extpos = ext_headersize - 2;
+ ext_headersize = get_header(extbuf, &extpos, 2);
+ }
+
+ if (dirnamelen > 0 && dirname[dirnamelen-1] != '/') {
+ dirname[dirnamelen++] = '/';
+ }
+
+ strcat(dirname, h->filename);
+ h->namelen = strlen(dirname);
+ strcpy(h->filename, dirname);
+
+ while (remainder > 0) {
+ fgetc(fp); /* skip padding */
+ remainder--;
+ }
+
+ return 1; /* success */
+}
+
+static int
+read_header(FILE *fp, struct lzh_header *h)
+{
+ char buf[4096];
+ int ret;
+
+ ret = fgetc(fp);
+ buf[0] = (uchar)ret;
+ if (buf[0] == 0 || ret == EOF)
+ return 0; /* end of archive */
+ fread_crc(buf + 1, 21 - 1, fp);
+ switch (buf[20]) {
+ case 0:
+ return read_header_lv0(fp, buf, h);
+ break;
+ case 1:
+ return read_header_lv1(fp, buf, h);
+ break;
+ case 2:
+ return read_header_lv2(fp, buf, h);
+ break;
+ default:
+ error("unknown level (%d)\n", buf[20]);
+ break;
+ }
+
+ return 1; /* success */
+}
+
+
+void
+write_header_lv0(FILE *fp, struct lzh_header *h)
+{
+ char buf[4096];
+ int sum;
+ int headersize;
+ int pos = 0;
+
+ headersize = 22;
+ if (!opts.generic)
+ headersize += 12; /* extended header size */
+
+ if (headersize + h->namelen > 255) {
+ warn("path name is too long");
+ h->namelen = 255 - headersize;
+ headersize = 255;
+ }
+ else {
+ headersize += h->namelen;
+ }
+
+ put_header(buf, &pos, 1, headersize);
+ put_header(buf, &pos, 1, 0); /* dummy */
+
+ put_string(buf, &pos, 5, h->method);
+ put_header(buf, &pos, 4, h->compsize); /* packed size */
+ put_header(buf, &pos, 4, h->origsize); /* original size */
+ put_header(buf, &pos, 4, time_t_to_ftime(h->mtime)); /* ftime */
+ put_header(buf, &pos, 1, 0x20); /* attribute */
+ put_header(buf, &pos, 1, 0); /* level */
+ put_header(buf, &pos, 1, h->namelen); /* length of pathname */
+ put_string(buf, &pos, h->namelen, h->filename);
+ put_header(buf, &pos, 2, h->file_crc);
+
+ if (!opts.generic) {
+ /* extended header for Unix */
+ put_header(buf, &pos, 1, 'U'); /* OS type */
+ put_header(buf, &pos, 1, '\0'); /* minor version */
+ put_header(buf, &pos, 4, h->mtime); /* time_t */
+ put_header(buf, &pos, 2, 0100000); /* mode */
+ put_header(buf, &pos, 2, 0); /* uid */
+ put_header(buf, &pos, 2, 0); /* gid */
+ }
+
+ sum = calc_headersum(buf+2, headersize);
+ put_header_tmp(buf, 1, 1, sum);
+
+ fwrite_crc(buf, headersize+2, fp);
+}
+
+void
+write_header_lv1(FILE *fp, struct lzh_header *h)
+{
+ char buf[4096];
+ int sum;
+ int headersize;
+ int extsize = 0;
+ int pos = 0;
+ int extpos;
+ char *dirname, *fname;
+ int dirnamelen;
+
+ fname = basename(h->filename);
+ dirname = h->filename;
+ dirnamelen = fname - dirname;
+ h->namelen = strlen(fname);
+ printf("namelen = %d\n", h->namelen);
+
+ headersize = 25;
+
+ put_header(buf, &pos, 1, 0); /* dummy */
+ put_header(buf, &pos, 1, 0); /* dummy */
+ put_string(buf, &pos, 5, h->method);
+ put_header(buf, &pos, 4, h->compsize); /* packed size */
+ put_header(buf, &pos, 4, h->origsize); /* original size */
+ put_header(buf, &pos, 4, time_t_to_ftime(h->mtime)); /* ftime */
+ put_header(buf, &pos, 1, 0x20); /* attribute */
+ put_header(buf, &pos, 1, 1); /* level */
+ if (headersize + h->namelen > 255)
+ put_header(buf, &pos, 1, 0); /* length of pathname */
+ else {
+ put_header(buf, &pos, 1, h->namelen); /* length of pathname */
+ put_string(buf, &pos, h->namelen, fname);
+ headersize += h->namelen;
+ }
+ put_header_tmp(buf, 0, 1, headersize); /* header size */
+ put_header(buf, &pos, 2, h->file_crc);
+ if (opts.generic)
+ put_header(buf, &pos, 1, '\0');
+ else
+ put_header(buf, &pos, 1, 'U');
+
+ extpos = pos;
+ put_header(buf, &pos, 2, 7); /* next header size */
+ put_header(buf, &pos, 1, 0x54); /* time stamp */
+ put_header(buf, &pos, 4, h->mtime); /* time_t */
+
+ if (h->namelen > 0) {
+ put_header(buf, &pos, 2, 3 + h->namelen);
+ put_header(buf, &pos, 1, 1); /* 0x01: filename header */
+ put_string(buf, &pos, h->namelen, fname); /* filename */
+ }
+
+ if (dirnamelen > 0) {
+ put_header(buf, &pos, 2, 3 + dirnamelen);
+ put_header(buf, &pos, 1, 2); /* 0x02: dirname header */
+ put_string(buf, &pos, dirnamelen, dirname); /* dirname */
+ }
+
+ extsize = pos - extpos;
+ put_header(buf, &pos, 2, 0); /* next header size (end of header) */
+
+ put_header_tmp(buf, 7, 4, h->compsize+extsize); /* packed size */
+
+ sum = calc_headersum(buf+2, headersize);
+ put_header_tmp(buf, 1, 1, sum);
+
+ fwrite_crc(buf, headersize+2+extsize, fp);
+}
+
+void
+write_header_lv2(FILE *fp, struct lzh_header *h)
+{
+ char buf[4096], *crcptr;
+ int headersize;
+ extern ushort crctable[];
+ char dirname[1024] = "", *fname;
+ int dirnamelen, len;
+ int pos = 0;
+
+ put_header(buf, &pos, 2, 0); /* dummy */
+ put_string(buf, &pos, 5, h->method);
+ put_header(buf, &pos, 4, h->compsize); /* packed size */
+ put_header(buf, &pos, 4, h->origsize); /* original size */
+ put_header(buf, &pos, 4, h->mtime); /* time_t */
+ put_header(buf, &pos, 1, 0x20); /* DOS attribute (0x20 fixed) */
+ put_header(buf, &pos, 1, 2); /* level */
+ put_header(buf, &pos, 2, h->file_crc);
+ if (opts.generic)
+ put_header(buf, &pos, 1, '\0');
+ else
+ put_header(buf, &pos, 1, 'U');
+
+ put_header(buf, &pos, 2, 5);
+ put_header(buf, &pos, 1, 0); /* 0x00: header crc */
+ crcptr = &buf[pos];
+ put_header(buf, &pos, 2, 0); /* crc (dummy) */
+
+ fname = basename(h->filename);
+ len = strlen(fname);
+
+ put_header(buf, &pos, 2, 3 + len);
+ put_header(buf, &pos, 1, 1); /* 0x01: filename header */
+ put_string(buf, &pos, len, fname); /* filename */
+
+ {
+ char *ptr;
+
+ ptr = strrchr(h->filename, '/');
+ if (ptr) {
+ /* 0123 */
+ /* abc/ */
+ /* 3 - 0 = 3 */
+ dirnamelen = ptr - h->filename;
+ strncpy(dirname, h->filename, dirnamelen);
+ dirname[dirnamelen+ 1] = 0;
+ }
+ }
+
+ if (*dirname) {
+ put_header(buf, &pos, 2, 3 + dirnamelen);
+ put_header(buf, &pos, 1, 2); /* 0x02: dirname header */
+ put_string(buf, &pos, dirnamelen, dirname); /* dirname */
+ }
+
+ put_header(buf, &pos, 2, 0); /* next header size (end of header) */
+
+ /* padding */
+ if (pos % 256 == 0) {
+ put_header(buf, &pos, 1, 0);
+ }
+ headersize = pos;
+
+ put_header_tmp(buf, 0, 2, headersize);
+
+ {
+ int i;
+
+ crc = INIT_CRC;
+ for (i = 0; i < headersize; i++)
+ UPDATE_CRC(buf[i]);
+ put_header_tmp(crcptr, 0, 2, crc);
+ }
+
+ fwrite_crc(buf, headersize, fp);
+}
+
+void
+write_header(FILE *fp, struct lzh_header *h)
+{
+ switch (h->level) {
+ case 0:
+ write_header_lv0(fp, h);
+ break;
+ case 1:
+ write_header_lv1(fp, h);
+ break;
+ case 2:
+ write_header_lv2(fp, h);
+ break;
+ default:
+ error("unknown level (%d)", h->level);
+ break;
+ }
}
static void
-skip(void)
+skip(FILE *fp, struct lzh_header *h)
{
- fseek(arcfile, compsize, SEEK_CUR);
+ int i;
+ if (opts.archive_to_stdio)
+ for (i = 0; i < h->compsize; i++)
+ fgetc(fp);
+ else
+ fseek(fp, h->compsize, SEEK_CUR);
}
static void
-copy(void)
+copy(FILE *arcfile, FILE *outfile, struct lzh_header *h)
{
uint n;
+ uchar buffer[MAXDICSIZ];
- write_header();
- while (compsize != 0) {
- n = (uint) ((compsize > DICSIZ) ? DICSIZ : compsize);
+ write_header(outfile, h);
+ while (h->compsize != 0) {
+ n = (uint) ((h->compsize > sizeof(buffer)) ? sizeof(buffer) : h->compsize);
if (fread((char *) buffer, 1, n, arcfile) != n)
error("Can't read");
if (fwrite((char *) buffer, 1, n, outfile) != n)
error("Can't write");
- compsize -= n;
+ h->compsize -= n;
}
}
store(void)
{
uint n;
+ uchar buffer[MAXDICSIZ];
origsize = 0;
crc = INIT_CRC;
- while ((n = fread((char *) buffer, 1, DICSIZ, infile)) != 0) {
+ while ((n = fread((char *) buffer, 1, sizeof(buffer), infile)) != 0) {
fwrite_crc(buffer, n, outfile);
origsize += n;
}
}
static int
-add(int replace_flag)
+add_dir(int replace_flag, struct lzh_header *h)
{
long headerpos, arcpos;
uint r;
- if ((infile = fopen(filename, "rb")) == NULL) {
- fprintf(stderr, "Can't open %s\n", filename);
+ h->origsize = h->compsize = 0;
+ h->file_crc = INIT_CRC;
+
+ headerpos = ftell(outfile);
+ write_header(outfile, h);
+ arcpos = ftell(outfile);
+
+ if (opts.quiet < 2)
+ printf(" %d.%d%%\n", r / 10, r % 10);
+ return 1; /* success */
+}
+
+static int
+add_1(int replace_flag, struct lzh_header *h)
+{
+ long headerpos, arcpos;
+ uint r;
+
+ if ((infile = fopen(h->filename, "rb")) == NULL) {
+ fprintf(stderr, "Can't open %s\n", h->filename);
return 0; /* failure */
}
if (replace_flag) {
- printf("Replacing %s ", filename);
- skip();
+ if (opts.quiet < 2)
+ printf("Replacing %s ", h->filename);
}
- else
- printf("Adding %s ", filename);
+ else {
+ if (opts.quiet < 2)
+ printf("Adding %s ", h->filename);
+ }
+
headerpos = ftell(outfile);
- namelen = strlen(filename);
- headersize = 25 + namelen;
- memcpy(header, "-lh5-", 5); /* compress */
- write_header(); /* temporarily */
+ write_header(outfile, h);
arcpos = ftell(outfile);
+
origsize = compsize = 0;
- unpackable = 0;
crc = INIT_CRC;
- encode();
+ if (opts.nocompress) {
+ unpackable = 1;
+ }
+ else {
+ unpackable = 0;
+ encode();
+ }
+
if (unpackable) {
- header[3] = '0'; /* store */
+ memcpy(h->method, "-lh0-", sizeof(h->method)); /* store */
rewind(infile);
fseek(outfile, arcpos, SEEK_SET);
store();
}
- file_crc = crc ^ INIT_CRC;
+ h->file_crc = crc ^ INIT_CRC;
fclose(infile);
- put_to_header(5, 4, compsize);
- put_to_header(9, 4, origsize);
- memcpy(header + 13, "\0\0\0\0\x20\x01", 6);
- memcpy(header + headersize - 3, "\x20\0\0", 3);
+
+ h->compsize = compsize;
+ h->origsize = origsize;
+
fseek(outfile, headerpos, SEEK_SET);
- write_header(); /* true header */
+ write_header(outfile, h);
fseek(outfile, 0L, SEEK_END);
r = ratio(compsize, origsize);
- printf(" %d.%d%%\n", r / 10, r % 10);
+ if (opts.quiet < 2)
+ printf(" %d.%d%%\n", r / 10, r % 10);
return 1; /* success */
}
+static int
+add(int replace_flag, char *filename, int namelen)
+{
+ struct lzh_header h;
+ struct stat st;
+
+ memset(&h, 0, sizeof(h));
+
+ h.level = opts.header_level;
+
+ strcpy(h.filename, filename);
+ h.namelen = namelen;
+
+ stat(h.filename, &st);
+
+ h.mtime = st.st_mtime;
+ if (S_ISDIR(st.st_mode)) {
+ DIR *dir;
+ struct dirent *ent;
+
+ memcpy(h.method, "-lhd-", sizeof(h.method)); /* directory */
+ add_dir(replace_flag, &h);
+
+ dir = opendir(h.filename);
+ if (dir == NULL)
+ error("cannot open directory: \"%s\"", h.filename);
+
+ while ((ent = readdir(dir)) != 0) {
+ char filename[1024];
+
+ if (string_equal(ent->d_name, ".") ||
+ string_equal(ent->d_name, ".."))
+ continue;
+
+ h.namelen = path_addsep(h.filename, sizeof(h.filename));
+
+ string_cat(filename, sizeof(filename),
+ h.filename, ent->d_name, NULL);
+
+ add(replace_flag, filename, strlen(filename));
+ }
+ closedir(dir);
+ }
+ else {
+ memcpy(h.method, opts.method->id, sizeof(h.method)); /* compress */
+ add_1(replace_flag, &h);
+ }
+}
+
int
get_line(char *s, int n)
{
}
static void
-extract(int to_file)
+extract(int to_file, struct lzh_header *h)
{
- int n, method;
- uint ext_headersize;
+ int n;
+ uchar buffer[MAXDICSIZ];
+
+ outfile = NULL;
if (to_file) {
- while ((outfile = fopen(filename, "wb")) == NULL) {
- fprintf(stderr, "Can't open %s\nNew filename: ", filename);
- if (get_line(filename, FNAME_MAX) == 0) {
- fprintf(stderr, "Not extracted\n");
- skip();
- return;
+ if (memcmp(h->method, "-lhd-", sizeof(h->method)) == 0) {
+ /* directory */
+ if (mkdir(h->filename, 0777) == -1) {
+ if (errno != EEXIST)
+ error("cannot make directory \"%s\"", opts.outdir);
}
- namelen = strlen(filename);
}
- printf("Extracting %s ", filename);
+ else {
+ /* regular file */
+ if (file_exists(h->filename)) {
+ if (!opts.force_extract) {
+ message("'%s' has been already exist. skip", h->filename);
+ skip(arcfile, h);
+ return;
+ }
+ }
+ while ((outfile = fopen(h->filename, "wb")) == NULL) {
+ fprintf(stderr, "Can't open %s\nNew filename: ", h->filename);
+ if (get_line(h->filename, FNAME_MAX) == 0) {
+ fprintf(stderr, "Not extracted\n");
+ skip(arcfile, h);
+ return;
+ }
+ h->namelen = strlen(h->filename);
+ }
+ }
+ if (opts.quiet < 2)
+ printf("Extracting %s ", h->filename);
}
else {
outfile = stdout;
- printf("===== %s =====\n", filename);
+ if (opts.quiet < 2)
+ printf("===== %s =====\n", h->filename);
}
crc = INIT_CRC;
- method = header[3];
- header[3] = ' ';
- if (!strchr("045", method) || memcmp("-lh -", header, 5)) {
- fprintf(stderr, "Unknown method: %u\n", method);
- skip();
+ opts.method = which_method(h->method);
+ if (opts.method == NULL) {
+ fprintf(stderr, "Unknown method: %.5s\n", h->method);
+ skip(arcfile, h);
}
else {
- ext_headersize = (uint) get_from_header(headersize - 2, 2);
- while (ext_headersize != 0) {
- fprintf(stderr, "There's an extended header of size %u.\n",
- ext_headersize);
- compsize -= ext_headersize;
- if (fseek(arcfile, ext_headersize - 2, SEEK_CUR))
- error("Can't read");
- ext_headersize = fgetc(arcfile);
- ext_headersize += (uint) fgetc(arcfile) << 8;
- }
crc = INIT_CRC;
- if (method != '0')
+ if (opts.method->dicbit != 0)
decode_start();
- while (origsize != 0) {
- n = (uint) ((origsize > DICSIZ) ? DICSIZ : origsize);
- if (method != '0')
+ while (h->origsize != 0) {
+ n = (uint) ((h->origsize > MAXDICSIZ) ? MAXDICSIZ : h->origsize);
+ if (opts.method->dicbit != 0)
decode(n, buffer);
else if (fread((char *) buffer, 1, n, arcfile) != n)
error("Can't read");
fwrite_crc(buffer, n, outfile);
- if (outfile != stdout)
- putc('.', stderr);
- origsize -= n;
+ if (outfile != stdout && opts.quiet < 1) {
+ putc('.', stdout);
+ }
+ h->origsize -= n;
}
}
- if (to_file)
- fclose(outfile);
- else
- outfile = NULL;
- printf("\n");
- if ((crc ^ INIT_CRC) != file_crc)
- fprintf(stderr, "CRC error\n");
+
+ if ((crc ^ INIT_CRC) != h->file_crc)
+ error("CRC error");
+
+ if (to_file) {
+ fprintf(stdout, "\n");
+ if (outfile) {
+ struct utimbuf ut;
+
+ fclose(outfile);
+
+ ut.actime = ut.modtime = h->mtime;
+ utime(h->filename, &ut);
+ }
+ }
+ outfile = NULL;
}
static void
list_start(void)
{
- printf("Filename Original Compressed Ratio CRC Method\n");
+ if (opts.quiet < 2)
+ printf("%-14.14s %-7.7s %10.10s %10.10s %-5.5s %-4.4s %-5.5s %-4.4s\n",
+ "Filename",
+ "OS",
+ "Original",
+ "Compressed",
+ "Ratio",
+ "CRC",
+ "Method",
+ "Lv");
}
static void
-list(void)
+list(struct lzh_header *h)
{
uint r;
- printf("%-14s", filename);
- if (namelen > 14)
+ printf("%-14.*s", h->namelen, h->filename);
+ if (h->namelen > 14)
printf("\n ");
- r = ratio(compsize, origsize);
- printf(" %10lu %10lu %u.%03u %04X %5.5s\n",
- origsize, compsize, r / 1000, r % 1000, file_crc, header);
+ r = ratio(h->compsize, h->origsize);
+ printf(" %-7s %10lu %10lu %u.%03u %04x %-6.6s [%d]\n",
+ os_string(h->os_id),
+ h->origsize,
+ h->compsize,
+ r / 1000,
+ r % 1000,
+ h->file_crc,
+ h->method,
+ h->level);
}
static int
}
static int
-search(int argc, char *argv[])
+search(int argc, char *argv[], struct lzh_header *h)
{
int i;
- if (argc == 3)
- return 1;
- for (i = 3; i < argc; i++)
- if (match(filename, argv[i]))
- return 1;
+ if (argc == 0)
+ return -1;
+ for (i = 0; i < argc; i++)
+ if (argv[i] && match(h->filename, argv[i]))
+ return i+1;
return 0;
}
static void
exitfunc(void)
{
- fclose(outfile);
remove(temp_name);
}
+#include "getopt_long.h"
+
+int
+parse_args(int argc, char **argv)
+{
+ int c;
+
+ for (;;) {
+ /* int this_option_optind = optind ? optind : 1; */
+ int option_index = 0;
+
+ enum {
+ LHA_OPT_HELP = 128,
+ LHA_OPT_VERSION,
+ };
+
+ static struct option long_options[] = {
+ /* name, has_arg, *flag, val */
+ /* has_arg:
+ no_argument (0)
+ required_argument (1)
+ optional_argument (2)
+ flag:
+ NULL: getopt_long() return val
+ non-NULL: getopt_long() return 0, and *flag set val.
+ */
+ {"help", no_argument, NULL, LHA_OPT_HELP},
+ {"version", no_argument, NULL, LHA_OPT_VERSION},
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long(argc, argv, "012fgo[567]q[012]vw:z",
+ long_options, &option_index);
+
+ if (c == -1) break; /* end of parsing options */
+
+ switch (c) {
+ case '?':
+ print_usage();
+ break;
+ case 0:
+ /* set value by long option */
+ break;
+ case '0': case '1': case '2':
+ /* header level */
+ opts.header_level = c - '0';
+ break;
+ case 'f':
+ opts.force_extract = 1;
+ break;
+ case 'g':
+ opts.generic = 1;
+ opts.header_level = 0;
+ break;
+ case 'o':
+ /* compress method */
+ {
+ int idx = 1; /* -o means -lh1- method */
+
+ if (optarg)
+ idx = *optarg - '0'; /* -lh[567]- method */
+
+ opts.method = &methods[idx];
+ }
+ break;
+ case 'q':
+ /* quiet mode */
+ opts.quiet = 2; /* -q is equivalent to -q2 */
+ if (optarg)
+ opts.quiet = *optarg - '0';
+ break;
+ case 'v':
+ /* verbose mode */
+ opts.verbose = 1;
+ break;
+
+ case 'w':
+ /* extract directory */
+ if (!optarg)
+ error("extract directory does not specified for `-w'");
+ if (*optarg == '=')
+ optarg++;
+
+ opts.outdir = optarg;
+ break;
+ case 'z': /* no compress */
+ opts.nocompress = 1;
+ break;
+ case LHA_OPT_HELP:
+ print_usage();
+ break;
+ case LHA_OPT_VERSION:
+ print_version();
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+FILE *
+open_tempfile()
+{
+ temp_name = tmpnam(NULL);
+ outfile = fopen(temp_name, "wb");
+ if (outfile == NULL)
+ error("Can't open temporary file");
+ atexit(exitfunc);
+
+ return outfile;
+}
+
int
main(int argc, char *argv[])
{
- int i, j, cmd, count, nfiles, found, done;
+ int i, cmd, count, nfiles, found, done;
+ char *archive_file;
+ struct lzh_header h;
+ int arc_count;
- /* Check command line arguments. */
- if (argc < 3
- || argv[1][1] != '\0' || !strchr("AXRDPL", cmd = toupper(argv[1][0]))
- || (argc == 3 && strchr("AD", cmd)))
- error(usage);
+ INITIALIZE_OPTS(opts);
- /* Wildcards used? */
- for (i = 3; i < argc; i++)
- if (strpbrk(argv[i], "*?"))
- break;
- if (cmd == 'A' && i < argc)
- error("Filenames may not contain '*' and '?'");
- if (i < argc)
- nfiles = -1; /* contains wildcards */
- else
- nfiles = argc - 3; /* number of files to process */
+ if (argv[1] == 0)
+ print_usage();
+
+ /*take a command character */
+ {
+ char *arg1;
- /* Open archive. */
- arcfile = fopen(argv[2], "rb");
- if (arcfile == NULL && cmd != 'A')
- error("Can't open archive '%s'", argv[2]);
+ arg1 = argv[1];
+ if (arg1[0] == '-')
+ arg1++;
+ if (arg1[0] == 0)
+ print_usage();
- /* Open temporary file. */
- if (strchr("ARD", cmd)) {
- temp_name = tmpnam(NULL);
- outfile = fopen(temp_name, "wb");
- if (outfile == NULL)
- error("Can't open temporary file");
- atexit(exitfunc);
+ cmd = *arg1;
+ if (arg1[1] == 0) {
+ /* -<cmd> -<opts> ... */
+ argv++;
+ argc--;
+ }
+ else {
+ /* -<cmd><opts> => -<opts> */
+ *arg1 = '-';
+ }
}
- else
- temp_name = NULL;
+
+ parse_args(argc, argv);
+ argv += optind;
+ argc -= optind;
+
+ archive_file = argv[0];
+
+ if (strcmp(archive_file, "-") == 0)
+ opts.archive_to_stdio = 1;
+
+ argv++;
+ argc--;
+
+ temp_name = NULL;
make_crctable();
- count = done = 0;
+ count = done = nfiles = 0;
- if (cmd == 'A') {
- for (i = 3; i < argc; i++) {
- for (j = 3; j < i; j++)
- if (strcmp(argv[j], argv[i]) == 0)
- break;
- if (j == i) {
- strcpy(filename, argv[i]);
- if (add(0))
- count++;
- else
- argv[i][0] = 0;
+ switch (cmd) {
+ case 'a':
+ case 'u':
+ case 'c':
+ if (opts.archive_to_stdio)
+ opts.quiet = 2;
+
+ outfile = open_tempfile();
+ if (*argv == 0)
+ error("archived files are not specified.");
+
+ if (!opts.archive_to_stdio && (cmd == 'a' || cmd == 'u')) {
+ if (file_exists(archive_file)) {
+ arcfile = fopen(archive_file, "rb");
+ if (arcfile == NULL)
+ error("Can't open archive '%s'", archive_file);
+
+ break;
}
- else
- nfiles--;
}
- if (count == 0 || arcfile == NULL)
- done = 1;
+ for (i = 0; i < argc; i++) {
+ add(0, argv[i], strlen(argv[i]));
+ }
+
+ fputc(0, outfile); /* end of archive */
+ if (ferror(outfile))
+ error("Can't write");
+ fclose(outfile);
+ if (opts.archive_to_stdio) {
+ if (move_file_to_stream(temp_name, stdout) == -1)
+ error("fail to move_file_to_stream(): %s -> %s",temp_name,"stdout");
+ }
+ else {
+ unlink(archive_file);
+ if (xrename(temp_name, archive_file) == -1)
+ error("fail to rename(): %s -> %s",temp_name,archive_file);
+ }
+ exit(0);
+ break;
+ case 'r':
+ case 'd':
+ if (argc == 0) {
+ message("No files given in argument, do nothing.");
+ exit(0);
+ }
+ outfile = open_tempfile();
+ case 'x':
+ case 'p':
+ case 'l':
+ case 'v':
+ /* Open archive. */
+ if (opts.archive_to_stdio) {
+ arcfile = stdin;
+ }
+ else {
+ arcfile = fopen(archive_file, "rb");
+ }
+ if (arcfile == NULL)
+ error("Can't open archive '%s'", archive_file);
+
+ break;
+ default:
+ print_usage();
+ break;
+ }
+
+ /* change directory to extract dir */
+ if (cmd == 'x') {
+ if (opts.outdir) {
+ if (mkdir(opts.outdir, 0777) == -1) {
+ if (errno != EEXIST)
+ error("cannot make directory \"%s\"", opts.outdir);
+ }
+
+ if (chdir(opts.outdir) == -1)
+ error("cannot change directory \"%s\"", opts.outdir);
+ }
}
- while (!done && read_header()) {
- found = search(argc, argv);
+ arc_count = 0;
+
+ while (!done && read_header(arcfile, &h)) {
+
+ arc_count++;
+
+ compsize = h.compsize;
+ origsize = h.origsize;
+
+ found = search(argc, argv, &h);
switch (cmd) {
- case 'R':
- if (found) {
- if (add(1))
+ case 'a':
+ case 'u':
+ if (found>0) {
+ argv[found-1] = 0;
+
+ if (cmd == 'u' && h.mtime > file_mtime(h.filename)) {
+ copy(arcfile, outfile, &h);
+ break;
+ }
+
+ if (add(1, h.filename, h.namelen)) {
+ skip(arcfile, &h);
count++;
+ }
else
- copy();
+ copy(arcfile, outfile, &h);
}
else
- copy();
+ copy(arcfile, outfile, &h);
break;
- case 'A':
- case 'D':
+ case 'd':
if (found) {
- count += (cmd == 'D');
- skip();
+ count++;
+ message("'%s' deleted", h.filename);
+ skip(arcfile, &h);
}
else
- copy();
+ copy(arcfile, outfile, &h);
break;
- case 'X':
- case 'P':
- if (found) {
- extract(cmd == 'X');
+ case 'x':
+ case 'p':
+ if (found != 0) {
+ extract(cmd == 'x', &h);
if (++count == nfiles)
done = 1;
}
else
- skip();
+ skip(arcfile, &h);
break;
- case 'L':
- if (found) {
+ case 'l':
+ case 'v':
+ if (found != 0) {
if (count == 0)
list_start();
- list();
+ list(&h);
if (++count == nfiles)
done = 1;
}
- skip();
+ skip(arcfile, &h);
break;
}
}
- if (temp_name != NULL && count != 0) {
+ if (cmd == 'a' || cmd == 'u') {
+ for (i = 0; i < argc; i++) {
+ if (argv[i]) {
+ count++;
+ add(0, argv[i], strlen(argv[i]));
+ }
+ }
+ }
+
+ if (cmd != 'p') {
+ if (opts.quiet < 2)
+ printf(" %d files\n", count);
+ }
+
+ if (count > 0 && (cmd == 'd' || cmd == 'a' || cmd == 'u')) {
fputc(0, outfile); /* end of archive */
- if (ferror(outfile) || fclose(outfile) == EOF)
+ if (ferror(outfile))
error("Can't write");
- remove(argv[2]);
- rename(temp_name, argv[2]);
+ if (!opts.archive_to_stdio)
+ unlink(archive_file);
+ fclose(outfile);
+ fclose(arcfile);
+ if (cmd == 'd') {
+ if (arc_count > count) {
+ if (xrename(temp_name, archive_file) == -1)
+ error("fail to rename(): %s -> %s",temp_name,archive_file);
+ }
+ else {
+ message("The archive file \"%s\" was removed because it would be empty.", archive_file);
+ }
+ }
+ else {
+ if (xrename(temp_name, archive_file) == -1)
+ error("fail to rename(): %s -> %s",temp_name,archive_file);
+ }
+ exit(0);
}
-
- printf(" %d files\n", count);
return EXIT_SUCCESS;
}