/* ar - archiver Author: Michiel Huisjes */ /* * Usage: ar [adprtvx] archive [file] ... * v: verbose * x: extract * a: append * r: replace (append when not in archive) * d: delete * t: print contents of archive * p: print named files */ #include "stat.h" #include "signal.h" #define MAGIC_NUMBER 0177545 #define odd(nr) (nr & 01) #define even(nr) (odd(nr) ? nr + 1 : nr) union swabber { struct sw { short mem_1; short mem_2; } mem; long joined; } swapped; long swap (); typedef struct { char m_name[14]; short m_time_1; short m_time_2; char m_uid; char m_gid; short m_mode; short m_size_1; short m_size_2; } MEMBER; typedef char BOOL; #define FALSE 0 #define TRUE 1 #define READ 0 #define APPEND 2 #define CREATE 1 #define NIL_PTR ((char *) 0) #define NIL_MEM ((MEMBER *) 0) #define NIL_LONG ((long *) 0) #define IO_SIZE (10 * 1024) #define BLOCK_SIZE 1024 #define flush() print(NIL_PTR) #define equal(str1, str2) (!strncmp((str1), (str2), 14)) BOOL verbose; BOOL app_fl; BOOL ex_fl; BOOL show_fl; BOOL pr_fl; BOOL rep_fl; BOOL del_fl; int ar_fd; long mem_time, mem_size; char io_buffer[IO_SIZE]; char terminal[BLOCK_SIZE]; char temp_arch[] = "/tmp/ar.XXXXX"; usage() { error(TRUE, "Usage: ar [adprtxv] archive [file] ...", NIL_PTR); } error(quit, str1, str2) BOOL quit; char *str1, *str2; { write(2, str1, strlen(str1)); if (str2 != NIL_PTR) write(2, str2, strlen(str2)); write(2, "\n", 1); if (quit) { (void) unlink(temp_arch); exit(1); } } char *basename(path) char *path; { register char *ptr = path; register char *last = NIL_PTR; while (*ptr != '\0') { if (*ptr == '/') last = ptr; ptr++; } if (last == NIL_PTR) return path; if (*(last + 1) == '\0') { *last = '\0'; return basename(path); } return last + 1; } open_archive(name, mode) register char *name; register int mode; { unsigned short magic = 0; int fd; if (mode == CREATE) { if ((fd = creat(name, 0644)) < 0) error(TRUE, "Cannot creat ", name); magic = MAGIC_NUMBER; mwrite(fd, &magic, sizeof(magic)); return fd; } if ((fd = open(name, mode)) < 0) { if (mode == APPEND) { (void) close(open_archive(name, CREATE)); error(FALSE, "ar: creating ", name); return open_archive(name, APPEND); } error(TRUE, "Cannot open ", name); } (void) lseek(fd, 0L, 0); (void) read(fd, &magic, sizeof(magic)); if (magic != MAGIC_NUMBER) error(TRUE, name, " is not in ar format."); return fd; } catch() { (void) unlink(temp_arch); exit (2); } main(argc, argv) int argc; char *argv[]; { register char *ptr; int pow, pid; if (argc < 3) usage(); for (ptr = argv[1]; *ptr; ptr++) { switch (*ptr) { case 't' : show_fl = TRUE; break; case 'v' : verbose = TRUE; break; case 'x' : ex_fl = TRUE; break; case 'a' : app_fl = TRUE; break; case 'p' : pr_fl = TRUE; break; case 'd' : del_fl = TRUE; break; case 'r' : rep_fl = TRUE; break; default : usage(); } } if (app_fl + ex_fl + del_fl + rep_fl + show_fl + pr_fl != 1) usage(); if (rep_fl || del_fl) { ptr = &temp_arch[8]; pid = getpid(); pow = 10000; while (pow != 0) { *ptr++ = (pid / pow) + '0'; pid %= pow; pow /= 10; } } signal(SIGINT, catch); get(argc, argv); exit(0); } MEMBER *get_member() { static MEMBER member; register int ret; if ((ret = read(ar_fd, &member, sizeof(MEMBER))) == 0) return NIL_MEM; if (ret != sizeof(MEMBER)) error(TRUE, "Corrupted archive.", NIL_PTR); mem_time = swap (&(member.m_time_1)); mem_size = swap (&(member.m_size_1)); return &member; } long swap (sw_ptr) union swabber *sw_ptr; { swapped.mem.mem_1 = (sw_ptr->mem).mem_2; swapped.mem.mem_2 = (sw_ptr->mem).mem_1; return swapped.joined; } get(argc, argv) int argc; register char *argv[]; { register MEMBER *member; int i = 0; int temp_fd, read_chars; ar_fd = open_archive(argv[2], (show_fl || pr_fl) ? READ : APPEND); if (rep_fl || del_fl) temp_fd = open_archive(temp_arch, CREATE); while ((member = get_member()) != NIL_MEM) { if (argc > 3) { for (i = 3; i < argc; i++) { if (equal(basename(argv[i]), member->m_name)) break; } if (i == argc || app_fl) { if (rep_fl || del_fl) { mwrite(temp_fd, member,sizeof(MEMBER)); copy_member(member, ar_fd, temp_fd); } else { if (app_fl && i != argc) { print(argv[i]); print(": already in archive.\n"); argv[i] = ""; } (void) lseek(ar_fd, even(mem_size),1); } continue; } } if (ex_fl || pr_fl) extract(member); else { if (rep_fl) add(argv[i], temp_fd, 'r'); else if (show_fl) { if (verbose) { print_mode(member->m_mode); if (member->m_uid < 10) print(" "); litoa(0, (long) member->m_uid); print("/"); litoa(0, (long) member->m_gid); litoa(8, mem_size); date(mem_time); } p_name(member->m_name); print("\n"); } else if (del_fl) show('d', member->m_name); (void) lseek(ar_fd, even(mem_size), 1); } argv[i] = ""; } if (argc > 3) { for (i = 3; i < argc; i++) if (argv[i][0] != '\0') { if (app_fl) add(argv[i], ar_fd, 'a'); else if (rep_fl) add(argv[i], temp_fd, 'a'); else { print(argv[i]); print(": not found\n"); } } } flush(); if (rep_fl || del_fl) { signal(SIGINT, SIG_IGN); (void) close(ar_fd); (void) close(temp_fd); ar_fd = open_archive(argv[2], CREATE); temp_fd = open_archive(temp_arch, APPEND); while ((read_chars = read(temp_fd, io_buffer, IO_SIZE)) > 0) mwrite(ar_fd, io_buffer, read_chars); (void) close(temp_fd); (void) unlink(temp_arch); } (void) close(ar_fd); } add(name, fd, mess) char *name; int fd; char mess; { static MEMBER member; register int read_chars; struct stat status; int src_fd; if (stat(name, &status) < 0) { error(FALSE, "Cannot find ", name); return; } else if ((src_fd = open(name, 0)) < 0) { error(FALSE, "Cannot open ", name); return; } strcpy (member.m_name, basename (name)); member.m_uid = status.st_uid; member.m_gid = status.st_gid; member.m_mode = status.st_mode & 07777; (void) swap (&(status.st_mtime)); member.m_time_1 = swapped.mem.mem_1; member.m_time_2 = swapped.mem.mem_2; (void) swap (&(status.st_size)); member.m_size_1 = swapped.mem.mem_1; member.m_size_2 = swapped.mem.mem_2; mwrite (fd, &member, sizeof (MEMBER)); while ((read_chars = read(src_fd, io_buffer, IO_SIZE)) > 0) mwrite(fd, io_buffer, read_chars); if (odd(status.st_size)) mwrite(fd, io_buffer, 1); if (verbose) show(mess, name); (void) close(src_fd); } extract(member) register MEMBER *member; { int fd = 1; if (pr_fl == FALSE && (fd = creat(member->m_name, 0644)) < 0) { error(FALSE, "Cannot create ", member->m_name); return; } if (verbose && pr_fl == FALSE) show('x', member->m_name); copy_member(member, ar_fd, fd); if (fd != 1) (void) close(fd); (void) chmod(member->m_name, member->m_mode); } copy_member(member, from, to) register MEMBER *member; int from, to; { register int rest; BOOL is_odd = odd(mem_size) ? TRUE : FALSE; do { rest = mem_size > (long) IO_SIZE ? IO_SIZE : (int) mem_size; if (read(from, io_buffer, rest) != rest) error(TRUE, "Read error on ", member->m_name); mwrite(to, io_buffer, rest); mem_size -= (long) rest; } while (mem_size != 0L); if (is_odd) { (void) lseek(from, 1L, 1); if (rep_fl || del_fl) (void) lseek(to, 1L, 1); } } print(str) register char *str; { static index = 0; if (str == NIL_PTR) { write(1, terminal, index); index = 0; return; } while (*str != '\0') { terminal[index++] = *str++; if (index == BLOCK_SIZE) flush(); } } print_mode(mode) register int mode; { static char mode_buf[11]; register int tmp = mode; int i; mode_buf[9] = ' '; for (i = 0; i < 3; i++) { mode_buf[i * 3] = (tmp & S_IREAD) ? 'r' : '-'; mode_buf[i * 3 + 1] = (tmp & S_IWRITE) ? 'w' : '-'; mode_buf[i * 3 + 2] = (tmp & S_IEXEC) ? 'x' : '-'; tmp <<= 3; } if (mode & S_ISUID) mode_buf[2] = 's'; if (mode & S_ISGID) mode_buf[5] = 's'; print(mode_buf); } litoa(pad, number) int pad; long number; { static char num_buf[11]; register long digit; register long pow = 1000000000L; int digit_seen = FALSE; int i; for (i = 0; i < 10; i++) { digit = number / pow; if (digit == 0L && digit_seen == FALSE && i != 9) num_buf[i] = ' '; else { num_buf[i] = '0' + (char) digit; number -= digit * pow; digit_seen = TRUE; } pow /= 10L; } for (i = 0; num_buf[i] == ' ' && i + pad < 11; i++) ; print(&num_buf[i]); } mwrite(fd, address, bytes) int fd; register char *address; register int bytes; { if (write(fd, address, bytes) != bytes) error(TRUE, "Write error.", NIL_PTR); } show(c, name) char c; register char *name; { write(1, &c, 1); write(1, " - ", 3); write(1, name, strlen(name)); write(1, "\n", 1); } p_name(mem_name) register char *mem_name; { register int i = 0; char name[15]; for (i = 0; i < 14 && *mem_name; i++) name[i] = *mem_name++; name[i] = '\0'; print(name); } #define MINUTE 60L #define HOUR (60L * MINUTE) #define DAY (24L * HOUR) #define YEAR (365L * DAY) #define LYEAR (366L * DAY) int mo[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; char *moname[] = { " Jan ", " Feb ", " Mar ", " Apr ", " May ", " Jun ", " Jul ", " Aug ", " Sep ", " Oct ", " Nov ", " Dec " }; /* Print the date. This only works from 1970 to 2099. */ date(t) long t; { int i, year, day, month, hour, minute; long length, time(), original; year = 1970; original = t; while (t > 0) { length = (year % 4 == 0 ? LYEAR : YEAR); if (t < length) break; t -= length; year++; } /* Year has now been determined. Now the rest. */ day = (int) (t / DAY); t -= (long) day * DAY; hour = (int) (t / HOUR); t -= (long) hour * HOUR; minute = (int) (t / MINUTE); /* Determine the month and day of the month. */ mo[1] = (year % 4 == 0 ? 29 : 28); month = 0; i = 0; while (day >= mo[i]) { month++; day -= mo[i]; i++; } /* At this point, 'year', 'month', 'day', 'hour', 'minute' ok */ print(moname[month]); day++; if (day < 10) print(" "); litoa(0, (long) day); print(" "); if (time(NIL_LONG) - original >= YEAR / 2L) litoa(1, (long) year); else { if (hour < 10) print("0"); litoa(0, (long) hour); print(":"); if (minute < 10) print("0"); litoa(0, (long) minute); } print(" "); }