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 <string.h>
#include <ctype.h>
#include <errno.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <utime.h>
#include "ar.h"
-struct lzh_header {
- char filename[1024];
- int namelen;
- char method[5];
- int compsize;
- int origsize;
- int ftime;
- int file_crc;
- char os_id;
-};
+extern char *basename(const char *);
struct lha_method methods[] = {
- /* id, dicbit, pbit, maxmatch */
- /* note: dicbit == 0 means no compress */
- /*0*/ {"-lh0-", 0, 0, 0}, /* no compress */
- /*1*/ {"-lh1-", 12, 0, 60}, /* 2^12 = 4KB dynamic huffman (LHarc) */
- /*2*/ {"-lh2-", 13, 0,256}, /* 2^13 = 8KB dynamic huffman */
- /*3*/ {"-lh3-", 13, 0,256}, /* 2^13 = 8KB static huffman */
- /*4*/ {"-lh4-", 12, 4,256}, /* 2^12 = 4KB static huffman (pos and len)*/
- /*5*/ {"-lh5-", 13, 4,256}, /* 2^13 = 8KB static huffman (pos and len)*/
- /*6*/ {"-lh6-", 15, 5,256}, /* 2^15 = 32KB static huffman (pos and len)*/
- /*7*/ {"-lh7-", 16, 5,256}, /* 2^16 = 64KB static huffman (pos and len)*/
- /*8*/ {"-lzs-", 11, 0, 17}, /* 2^11 = 2KB (LArc) */
- /*9*/ {"-lz5-", 12, 0, 17}, /* 2^12 = 4KB (LArc) */
- /*10*/{"-lz4-", 0, 0, 0}, /* no compress (LArc) */
- /*11*/{"-lzd-", 0, 0, 0}, /* directory */
+ /* 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;
return NULL;
}
-init_opts()
-{
- opts.nocompress = 0;
- opts.outdir = NULL;
- opts.quiet = 0;
-
- /* default is the -lh5- method */
- opts.method = &methods[5];
-}
-
#define FNAME_MAX (255 - 25) /* max strlen(filename) */
int unpackable; /* global, set in io.c */
ulong compsize, origsize; /* global */
-static uchar headersize;
static char *temp_name;
static void
print_usage()
{
- puts("usage: ...");
+ printf("%s", usage);
+ exit(0);
+}
+
+static void
+print_version()
+{
+ printf("version %s\n", version);
exit(0);
}
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(char *buf, int i, int n, ulong x)
+put_header(char *buf, int *i, int n, uint32_t x)
{
while (--n >= 0) {
- buf[i++] = (uchar) ((uint) x & 0xFF);
+ buf[(*i)++] = (uchar) (x & 0xFF);
x >>= 8;
}
}
-static ulong
-get_from_header(char *buf, int i, int n)
+static void
+put_header_tmp(char *buf, int i, int n, uint32_t x)
{
- ulong s;
+ 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)
+{
+ uint32_t s;
+ int n;
s = 0;
- while (--n >= 0)
- s = (s << 8) + buf[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(char *buf, int size)
{
return s & 0xFF;
}
+#if 0
int
get_byte(char *buf)
{
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(FILE *fp, struct lzh_header *h)
+read_header_lv0(FILE *fp, char *buf, struct lzh_header *h)
{
int headersize;
int headersum;
- char buf[4096];
int ext_headersize;
+ int pos = 0;
+ int ext_size;
- headersize = (uchar) fgetc(fp);
- if (headersize == 0)
- return 0; /* end of archive */
- headersum = (uchar) fgetc(fp);
- fread_crc(buf, headersize, fp); /* CRC not used */
- if (calc_headersum(buf, headersize) != headersum)
+ 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");
+
+ 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 */
+}
+
+/*
+ * 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_char(&buf[0], h->method, 5);
- h->compsize = get_dword(&buf[5]);
- h->origsize = get_dword(&buf[9]);
- h->ftime = get_dword(&buf[13]);
- /* attrib = get_byte(&buf[17]); */
- /* level = get_byte(&buf[18]); */ /* level */
- h->namelen = get_byte(&buf[19]);
- get_char(&buf[20], h->filename, h->namelen);
+ 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_word(&buf[20+h->namelen]);
- h->os_id = get_byte(&buf[20+h->namelen+2]);
+ h->file_crc = get_header(buf, &pos, 2);
+ h->os_id = get_header(buf, &pos, 1);
- ext_headersize = get_word(&buf[20+h->namelen+3]);
+ ext_headersize = get_header(buf, &pos, 2);
while (ext_headersize != 0) {
- fprintf(stderr, "There's an extended header of size %u.\n",
- ext_headersize);
+ char extbuf[4096];
+ uchar ext_type;
+ int extpos = 0;
+
h->compsize -= ext_headersize;
- /* skip ext header */
- if (fseek(arcfile, ext_headersize - 2, SEEK_CUR))
- error("Can't read");
- ext_headersize = fgetc(arcfile);
- ext_headersize += (uint) fgetc(arcfile) << 8;
+ 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)
+{
+ 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(FILE *fp, int headersize, struct lzh_header *h)
+write_header_lv0(FILE *fp, struct lzh_header *h)
{
- char buf[4096], *p = buf;
+ 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_byte(&buf[0], headersize);
- put_byte(&buf[1], 0); /* dummy */
- put_char(&buf[2], h->method, 5);
- put_dword(&buf[7], h->compsize); /* packed size */
- put_dword(&buf[11], h->origsize); /* original size */
- put_dword(&buf[15], h->ftime); /* ftime */
- put_byte(&buf[19], 0x20); /* attribute */
- put_byte(&buf[20], 1); /* level */
- put_byte(&buf[21], h->namelen); /* length of pathname */
- put_char(&buf[22], h->filename, h->namelen);
- put_word(&buf[22+h->namelen], h->file_crc);
- put_byte(&buf[22+h->namelen+2], 'M');
- put_word(&buf[22+h->namelen+3], 0x0000); /* next header size */
+ 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_byte(&buf[1], sum);
+ 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 = xbasename(h->filename);
+ dirname = h->filename;
+ dirnamelen = fname - dirname;
+ h->namelen = strlen(fname);
+
+ 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 = xbasename(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) {
+ 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(FILE *fp, struct lzh_header *h)
{
- fseek(fp, h->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
uint n;
uchar buffer[MAXDICSIZ];
- write_header(outfile, headersize, h);
+ 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)
}
static int
-add(int replace_flag, char *filename)
+add_dir(int replace_flag, struct lzh_header *h)
+{
+ long headerpos, arcpos;
+ uint r;
+
+ 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;
- struct lzh_header h;
- if ((infile = fopen(filename, "rb")) == NULL) {
- fprintf(stderr, "Can't open %s\n", filename);
+ if ((infile = fopen(h->filename, "rb")) == NULL) {
+ fprintf(stderr, "Can't open %s\n", h->filename);
return 0; /* failure */
}
if (replace_flag) {
if (opts.quiet < 2)
- printf("Replacing %s ", filename);
- skip(arcfile, &h);
+ printf("Replacing %s ", h->filename);
}
else {
if (opts.quiet < 2)
- printf("Adding %s ", filename);
+ printf("Adding %s ", h->filename);
}
- strcpy(h.filename, filename);
- h.namelen = strlen(filename);
-
- headersize = 25 + h.namelen;
- memcpy(h.method, opts.method->id, sizeof(h.method)); /* compress */
-
headerpos = ftell(outfile);
- write_header(outfile, headersize, &h);
+ write_header(outfile, h);
arcpos = ftell(outfile);
origsize = compsize = 0;
}
if (unpackable) {
- h.method[3] = '0'; /* store */
+ memcpy(h->method, "-lh0-", sizeof(h->method)); /* store */
rewind(infile);
fseek(outfile, arcpos, SEEK_SET);
store();
}
- h.file_crc = crc ^ INIT_CRC;
+ h->file_crc = crc ^ INIT_CRC;
fclose(infile);
- h.compsize = compsize;
- h.origsize = origsize;
+ h->compsize = compsize;
+ h->origsize = origsize;
fseek(outfile, headerpos, SEEK_SET);
- write_header(outfile, headersize, &h);
+ write_header(outfile, h);
fseek(outfile, 0L, SEEK_END);
r = ratio(compsize, origsize);
if (opts.quiet < 2)
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)
{
extract(int to_file, struct lzh_header *h)
{
int n;
- uint ext_headersize;
uchar buffer[MAXDICSIZ];
+ outfile = NULL;
+
if (to_file) {
- 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;
+ 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);
+ }
+ }
+ 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);
}
- h->namelen = strlen(h->filename);
}
if (opts.quiet < 2)
printf("Extracting %s ", h->filename);
h->origsize -= n;
}
}
- if (to_file)
- fclose(outfile);
- else
- outfile = NULL;
+
if ((crc ^ INIT_CRC) != h->file_crc)
- fprintf(stderr, "CRC error\n");
+ 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)
{
if (opts.quiet < 2)
- printf("Filename Original Compressed Ratio CRC Method\n");
+ 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
if (h->namelen > 14)
printf("\n ");
r = ratio(h->compsize, h->origsize);
- printf(" %10lu %10lu %u.%03u %04X %5.5s\n",
- h->origsize, h->compsize, r / 1000, r % 1000, h->file_crc, h->method);
+ 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
int i;
if (argc == 0)
- return 1;
+ return -1;
for (i = 0; i < argc; i++)
- if (match(h->filename, argv[i]))
- return 1;
+ if (argv[i] && match(h->filename, argv[i]))
+ return i+1;
return 0;
}
static void
exitfunc(void)
{
- fclose(outfile);
remove(temp_name);
}
int c;
for (;;) {
- int this_option_optind = optind ? optind : 1;
+ /* int this_option_optind = optind ? optind : 1; */
int option_index = 0;
enum {
LHA_OPT_HELP = 128,
+ LHA_OPT_VERSION,
};
static struct option long_options[] = {
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, "o[567]q[012]w:z",
+ c = getopt_long(argc, argv, "012fgo[567]q[012]vw:z",
long_options, &option_index);
- if (c == -1) break;
+ if (c == -1) break; /* end of parsing options */
switch (c) {
- case 0: /* set vallue by long option */
+ case '?':
+ print_usage();
break;
- case 'o': /* compress method */
+ 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;
+ int idx = 1; /* -o means -lh1- method */
if (optarg)
idx = *optarg - '0'; /* -lh[567]- method */
- else
- idx = 1; /* -lh1- method */
opts.method = &methods[idx];
}
break;
- case 'q': /* quiet mode */
- opts.quiet = 2; /* level 2 */
+ case 'q':
+ /* quiet mode */
+ opts.quiet = 2; /* -q is equivalent to -q2 */
if (optarg)
opts.quiet = *optarg - '0';
break;
- case 'w': /* extract directory */
+ case 'v':
+ /* verbose mode */
+ opts.verbose = 1;
+ break;
+
+ case 'w':
+ /* extract directory */
if (!optarg)
error("extract directory does not specified for `-w'");
if (*optarg == '=')
case LHA_OPT_HELP:
print_usage();
break;
+ case LHA_OPT_VERSION:
+ print_version();
+ break;
default:
break;
}
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;
- init_opts();
+ INITIALIZE_OPTS(opts);
if (argv[1] == 0)
print_usage();
archive_file = argv[0];
+ if (strcmp(archive_file, "-") == 0)
+ opts.archive_to_stdio = 1;
+
argv++;
argc--;
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;
+ }
+ }
for (i = 0; i < argc; i++) {
- add(0, argv[i]);
+ add(0, argv[i], strlen(argv[i]));
}
fputc(0, outfile); /* end of archive */
if (ferror(outfile))
error("Can't write");
- remove(archive_file);
- if (rename(temp_name, archive_file) == -1)
- error("fail to rename()");
-
+ 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. */
- arcfile = fopen(archive_file, "rb");
+ if (opts.archive_to_stdio) {
+ arcfile = stdin;
+ }
+ else {
+ arcfile = fopen(archive_file, "rb");
+ }
if (arcfile == NULL)
- error("Can't open archive '%s'", argv[2]);
+ error("Can't open archive '%s'", archive_file);
break;
default:
/* change directory to extract dir */
if (cmd == 'x') {
- struct stat *stbuf;
-
if (opts.outdir) {
if (mkdir(opts.outdir, 0777) == -1) {
if (errno != EEXIST)
}
}
+ 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, *argv))
+ 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(arcfile, outfile, &h);
}
else
copy(arcfile, outfile, &h);
break;
- case 'a':
case 'd':
if (found) {
- count += (cmd == 'D');
+ count++;
+ message("'%s' deleted", h.filename);
skip(arcfile, &h);
}
else
break;
case 'x':
case 'p':
- if (found) {
+ if (found != 0) {
extract(cmd == 'x', &h);
if (++count == nfiles)
done = 1;
break;
case 'l':
case 'v':
- if (found) {
+ if (found != 0) {
if (count == 0)
list_start();
list(&h);
}
}
+ 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))
+ error("Can't write");
+ 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);
+ }
return EXIT_SUCCESS;
}