ericr@ipmoea.UUCP (Eric Roskos) (08/26/87)
Recently I posted a set of changes to Minix that are needed to compile it with MSC. Included in these changes were some changes to "ar.c". I have subsequently found some other problems with the version I posted; it extracts files correctly, but will not create new archives correctly. There were several reasons for this: 1) DOS writes text files with CR/LF separating lines, whereas Minix uses newlines instead. I had fixed this for extraction, but unfortunately when you do a "stat" under DOS, it gives you the size of the file including CR's, eventhough when you read the file, the CR's will be stripped out. The result is that the archive file was not readable after being created, because the size in the header for each file was wrong. This new version reads the file to see the size; this means the file has to be read twice, but it was the simplest way to fix it, and it only doubles the time to add new files, which turns out not to be so bad. (Not ideal, but not too bad; and you do have to read the whole file to find out how many CR's are in there.) 2) DOS's 'setargv.obj' code, which expands wildcards, generates filenames in uppercase. This means all your archived files will have uppercase names if you later extract them under Minix, whereas the Minix equivalents have lowercase names. So I changed this version to convert all DOS filenames to lowercase. There are actually some other potential filename problems under DOS; I fixed these when I ported John Gilmore's "tar" program to DOS back in February, and the code to handle all the cases is very large, so I didn't put it into ar, since there is no present need for it. The changed "ar" works correctly for the files in the library archive file, which is what you need it for. Following is the file. I did not post "diffs" because I don't have a context diff on my PC, and the file is small... does anyone have a public-domain context diff for DOS, Minix, or Unix [Unix is a trademark of AT&T]? If so, I'll port it if no one already has... Anyway, following is the corrected ar.c: ----- cut here ----- /* 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 * * WARNING: the current version of ar, when compiled under DOS with MSC, * will *not* work correctly if the files being archived are not text files! * This is because the files are currently opened in O_TEXT mode, so that * they can appear in the archive file as Minix-compatible files. */ #include "sys/types.h" #include "sys/stat.h" #include "fcntl.h" #include "signal.h" #include "stdio.h" #ifdef MSDOS #include <assert.h> #else #define O_BINARY 0 /* special DOS mode bits */ #endif #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 (4 * 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; { perror("ar"); 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 = open(name, O_RDWR|O_CREAT|O_TRUNC|O_BINARY, 0644)) < 0) error(TRUE, "Cannot creat ", name); magic = MAGIC_NUMBER; mwrite(fd, &magic, sizeof(magic)); return fd; } if ((fd = open(name, mode|O_BINARY)) < 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); } #ifdef MSDOS /* * count the number of bytes in file f. This is done because, with DOS, * the results of the stat call may not reflect the actual file size, due * to conversion of CR/LF pairs to plain LF's. The *only* way to find this * out is to read the whole file in order to let the MSC library routines * determine how many characters will actually be in the file... */ long countbytes(f) int f; { long cb; int n; char buf[512]; assert(lseek(f, 0L, 1) == 0L); for (cb=0; (n=read(f,buf,sizeof(buf))) > 0; ) cb += n; lseek(f, 0L, 0); assert(lseek(f, 0L, 1) == 0L); return(cb); } #endif add(name, fd, mess) char *name; int fd; char mess; { static MEMBER member; register int read_chars; struct stat status; int src_fd; #ifdef MSDOS strlwr(name); #endif 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; } #ifdef MSDOS status.st_size = countbytes(src_fd); #endif 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; } 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(" "); }