/* * ar.c -- ar clone * Copyright (C) 2013-2015 Mikael Pettersson * * This file is part of pdp10-tools. * * pdp10-tools is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * pdp10-tools is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with pdp10-tools. If not, see . */ #include #include #include #include #include #include #include #include "pdp10-ar.h" #include "pdp10-inttypes.h" #include "pdp10-stdio.h" #define VERSION "pdp10-tools ar version 0.1, built " __DATE__ " " __TIME__ "\n" enum modifier { MOD_create = 1 << 0, /* 'c' */ MOD_newer = 1 << 1, /* 'u' */ MOD_verbose = 1 << 2, /* 'v' */ }; struct strtab { char *bytes; unsigned long len; }; struct arhdr { char ar_name[16]; /* NUL-terminated unless ar_name[0] == '/' */ time_t ar_date; /* File date, decimal seconds since Epoch. */ unsigned int ar_uid; /* User ID. */ unsigned int ar_gid; /* Group ID. */ unsigned int ar_mode; /* File mode. */ unsigned long ar_size; /* File size. */ }; struct member { struct member **prev_next, *next; char *name; /* points into ->arhdr.ar_name, params->strtab, or argv[] */ unsigned long strtaboffset; /* >=0: offset into strtab, -1U: short name in arhdr.ar_name, -2UL: short or long name in argv[] */ unsigned long srcoffset; /* 0: in external file, >0: in old archive */ struct arhdr arhdr; }; struct params { const char *progname; /* argv[0] */ char operation; /* 'd', 'q', 'r', 't', 'x' */ unsigned int modifiers; /* bitmask indexed by enum modifier */ char *arname; PDP10_FILE *pdp10fp; struct strtab strtab; struct member *members_head, **members_tail; }; static void params_init(struct params *params) { memset(params, '\0', sizeof *params); params->progname = NULL; params->arname = NULL; params->pdp10fp = NULL; params->strtab.bytes = NULL; params->members_head = NULL; params->members_tail = ¶ms->members_head; } static struct member *lookup_member(struct params *params, const char *name) { struct member *member; for (member = params->members_head; member; member = member->next) if (strcmp(member->name, name) == 0) return member; return NULL; } static void unlink_member(struct member *member) { *member->prev_next = member->next; if (member->next) member->next->prev_next = member->prev_next; } static void append_member(struct params *params, struct member *member) { member->next = NULL; member->prev_next = params->members_tail; *params->members_tail = member; params->members_tail = &member->next; } static int ar_fgetc(struct params *params, const char *srcname, PDP10_FILE *srcfp) { int ch; ch = pdp10_fgetc(srcfp); if (ch == EOF) { fprintf(stderr, "%s: %s: premature EOF on read\n", params->progname, srcname); return EOF; } return ch; } static int ar_fputc(struct params *params, const char *dstname, PDP10_FILE *dstfp, uint16_t ch) { if (pdp10_fputc(ch, dstfp) < 0) { fprintf(stderr, "%s: %s: failed to write: %s\n", params->progname, dstname, strerror(errno)); return -1; } return 0; } static int ar_fputvec(struct params *params, const char *dstname, PDP10_FILE *dstfp, const pdp10_uint9_t *v, size_t n) { size_t i; for (i = 0; i < n; ++i) if (ar_fputc(params, dstname, dstfp, v[i]) < 0) return -1; return 0; } static int copy_file_data( struct params *params, unsigned long nrbytes, PDP10_FILE *srcfp, const char *srcname, PDP10_FILE *dstfp, const char *dstname) { unsigned int i; for (i = 0; i < nrbytes; ++i) { int ch = ar_fgetc(params, srcname, srcfp); if (ch == EOF) return -1; if (ar_fputc(params, dstname, dstfp, ch) < 0) return -1; } return 0; } static const pdp10_uint9_t armag[PDP10_SARMAG] = PDP10_ARMAG; static int read_armag(struct params *params) { unsigned int i; for (i = 0; i < PDP10_SARMAG; ++i) { int ch = ar_fgetc(params, params->arname, params->pdp10fp); if (ch == EOF) return -1; if (ch != armag[i]) { fprintf(stderr, "%s: %s: file format not recognized\n", params->progname, params->arname); return -1; } } return 0; } static int write_armag(struct params *params, const char *tmparname, PDP10_FILE *tmparfp) { return ar_fputvec(params, tmparname, tmparfp, armag, PDP10_SARMAG); } static const pdp10_uint9_t arfmag[2] = PDP10_ARFMAG; static int read_arhdr(struct params *params, struct arhdr *arhdr) { union { struct pdp10_ar_hdr arhdr; pdp10_uint9_t buf[PDP10_ARHDR_SIZEOF]; } u; unsigned int i; char buf[16]; for (i = 0; i < PDP10_ARHDR_SIZEOF; ++i) { int ch = pdp10_fgetc(params->pdp10fp); if (ch == EOF) { if (i == 0) return 0; fprintf(stderr, "%s: %s: premature EOF in ar_hdr\n", params->progname, params->arname); return -1; } u.buf[i] = ch; } if (u.arhdr.ar_fmag[0] != arfmag[0] || u.arhdr.ar_fmag[1] != arfmag[1]) { fprintf(stderr, "%s: %s: wrong value in ar_fmag\n", params->progname, params->arname); return -1; } for (i = 0; i < 10; ++i) buf[i] = (char)(unsigned char)u.arhdr.ar_size[i]; buf[i] = '\0'; arhdr->ar_size = strtoul(buf, NULL, 10); for (i = 0; i < 8; ++i) buf[i] = (char)(unsigned char)u.arhdr.ar_mode[i]; buf[i] = '\0'; arhdr->ar_mode = strtoul(buf, NULL, 8); for (i = 0; i < 6; ++i) buf[i] = (char)(unsigned char)u.arhdr.ar_gid[i]; buf[i] = '\0'; arhdr->ar_gid = strtoul(buf, NULL, 10); for (i = 0; i < 6; ++i) buf[i] = (char)(unsigned char)u.arhdr.ar_uid[i]; buf[i] = '\0'; arhdr->ar_uid = strtoul(buf, NULL, 10); for (i = 0; i < 12; ++i) buf[i] = (char)(unsigned char)u.arhdr.ar_date[i]; buf[i] = '\0'; arhdr->ar_date = strtoul(buf, NULL, 10); for (i = 0; i < 16; ++i) arhdr->ar_name[i] = (char)(unsigned char)u.arhdr.ar_name[i]; if (arhdr->ar_name[0] != '/') { for (i = 1; i < 15; ++i) if (arhdr->ar_name[i] == '/') break; arhdr->ar_name[i] = '\0'; } return 1; } static int write_arhdr(struct params *params, const char *tmparname, PDP10_FILE *tmparfp, const struct arhdr *arhdr) { union { struct pdp10_ar_hdr arhdr; pdp10_uint9_t buf[PDP10_ARHDR_SIZEOF]; } u; unsigned int i; char buf[16]; u.arhdr.ar_fmag[0] = arfmag[0]; u.arhdr.ar_fmag[1] = arfmag[1]; sprintf(buf, "%lu", arhdr->ar_size); for (i = 0; i < 10 && buf[i] != '\0'; ++i) u.arhdr.ar_size[i] = (unsigned char)buf[i]; for (; i < 10; ++i) u.arhdr.ar_size[i] = ' '; sprintf(buf, "%o", arhdr->ar_mode); for (i = 0; i < 8 && buf[i] != '\0'; ++i) u.arhdr.ar_mode[i] = (unsigned char)buf[i]; for (; i < 8; ++i) u.arhdr.ar_mode[i] = ' '; sprintf(buf, "%u", arhdr->ar_gid); for (i = 0; i < 6 && buf[i] != '\0'; ++i) u.arhdr.ar_gid[i] = (unsigned char)buf[i]; for (; i < 6; ++i) u.arhdr.ar_gid[i] = ' '; sprintf(buf, "%u", arhdr->ar_uid); for (i = 0; i < 6 && buf[i] != '\0'; ++i) u.arhdr.ar_uid[i] = (unsigned char)buf[i]; for (; i < 6; ++i) u.arhdr.ar_uid[i] = ' '; sprintf(buf, "%llu", (unsigned long long)arhdr->ar_date); for (i = 0; i < 12 && buf[i] != '\0'; ++i) u.arhdr.ar_date[i] = (unsigned char)buf[i]; for (; i < 12; ++i) u.arhdr.ar_date[i] = ' '; if (arhdr->ar_name[0] != '/') { for (i = 0; i < 15 && arhdr->ar_name[i] != '\0'; ++i) u.arhdr.ar_name[i] = (unsigned char)arhdr->ar_name[i]; u.arhdr.ar_name[i] = '/'; ++i; } else { for (i = 0; i < 16 && arhdr->ar_name[i] != '\0'; ++i) u.arhdr.ar_name[i] = (unsigned char)arhdr->ar_name[i]; } for (; i < 16; ++i) u.arhdr.ar_name[i] = ' '; if (ar_fputvec(params, tmparname, tmparfp, u.buf, PDP10_ARHDR_SIZEOF) < 0) return -1; return 0; } static int read_arstrtab(struct params *params, const struct arhdr *arhdr) { unsigned int i; params->strtab.len = arhdr->ar_size; params->strtab.bytes = malloc(params->strtab.len); if (!params->strtab.bytes) { fprintf(stderr, "%s: %s: failed to allocate %zu bytes for string table: %s\n", params->progname, params->arname, params->strtab.len, strerror(errno)); return -1; } for (i = 0; i < params->strtab.len; ++i) { int ch = ar_fgetc(params, params->arname, params->pdp10fp); if (ch == EOF) return -1; if (ch == '/') ch = '\0'; params->strtab.bytes[i] = ch; } if ((i & 1) && ar_fgetc(params, params->arname, params->pdp10fp) == EOF) return -1; return 0; } static int write_arstrtab(struct params *params, const char *tmparname, PDP10_FILE *tmparfp) { struct arhdr arhdr; unsigned int i; if (!params->strtab.len) return 0; memset(&arhdr, 0, sizeof arhdr); arhdr.ar_name[0] = '/'; arhdr.ar_name[1] = '/'; arhdr.ar_size = params->strtab.len; if (write_arhdr(params, tmparname, tmparfp, &arhdr) < 0) return -1; for (i = 0; i < params->strtab.len; ++i) { int ch = params->strtab.bytes[i]; if (ch == '\0') ch = '/'; if (ar_fputc(params, tmparname, tmparfp, ch) < 0) return -1; } if ((i & 1) && ar_fputc(params, tmparname, tmparfp, '\n') < 0) return -1; return 0; } static int skip_member(struct params *params, struct arhdr *arhdr) { if (pdp10_fseeko(params->pdp10fp, (arhdr->ar_size + 1) & ~(unsigned long)1, PDP10_SEEK_CUR) < 0) { fprintf(stderr, "%s: %s: failed to fseek to next member: %s\n", params->progname, params->arname, strerror(errno)); return -1; } return 0; } static int read_arsymtab(struct params *params, struct arhdr *arhdr) { /* XXX: symtab is NYI so just seek past this member */ fprintf(stderr, "%s: %s: Warning: skipping symbol table\n", params->progname, params->arname); return skip_member(params, arhdr); } static int read_archive(struct params *params) { struct arhdr arhdr; int status; struct member *member; if (read_armag(params) < 0) return -1; status = read_arhdr(params, &arhdr); if (status <= 0) return status; if (arhdr.ar_name[0] == '/' && arhdr.ar_name[1] == ' ') { status = read_arsymtab(params, &arhdr); if (status < 0) return -1; status = read_arhdr(params, &arhdr); if (status <= 0) return status; } if (arhdr.ar_name[0] == '/' && arhdr.ar_name[1] == '/' && arhdr.ar_name[2] == ' ') { status = read_arstrtab(params, &arhdr); if (status < 0) return -1; status = read_arhdr(params, &arhdr); if (status <= 0) return status; } do { member = malloc(sizeof *member); if (!member) { fprintf(stderr, "%s: %s: failed to allocate %zu bytes for new member\n", params->progname, params->arname, sizeof *member); return -1; } member->arhdr = arhdr; member->srcoffset = pdp10_ftello(params->pdp10fp); if (arhdr.ar_name[0] == '/') { char buf[16]; unsigned int i; if (arhdr.ar_name[1] < '0' || arhdr.ar_name[1] > '9') { fprintf(stderr, "%s: %s: invalid member name '%.*s'\n", params->progname, params->arname, 16, arhdr.ar_name); return -1; } for (i = 0; i < 15; ++i) buf[i] = arhdr.ar_name[i + 1]; buf[i] = '\0'; member->strtaboffset = strtoul(buf, NULL, 10); if (!params->strtab.bytes || member->strtaboffset >= params->strtab.len) { fprintf(stderr, "%s: %s: ar_name '%.*s' out of bounds\n", params->progname, params->arname, 16, arhdr.ar_name); return -1; } } else { member->strtaboffset = -1UL; member->name = member->arhdr.ar_name; } append_member(params, member); if (skip_member(params, &arhdr) < 0) return -1; status = read_arhdr(params, &arhdr); } while (status > 0); return status; } /* * ar t/x code */ static int ar_tx_should_process_member(struct params *params, struct member *member, char **files, int nrfiles) { int i; for (i = 0; i < nrfiles; ++i) if (files[i] && strcmp(member->name, files[i]) == 0) { files[i] = NULL; return 1; } return nrfiles <= 0; } static char *rwx_string(unsigned int m, char *buf) { buf[0] = (m & 4) ? 'r' : '-'; buf[1] = (m & 2) ? 'w' : '-'; buf[2] = (m & 1) ? 'x' : '-'; buf[3] = '\0'; return buf; } static char *date_string(time_t t, char *buf) { struct tm *tm; tm = gmtime(&t); if (!tm) { fprintf(stderr, "gmtime(%lu) failed: %s\n", t, strerror(errno)); exit(1); } /* Mon Day HH:MM YYYY */ strftime(buf, 64, "%b %d %H:%M %Y", tm); return buf; } static int ar_t_member(struct params *params, struct member *member) { char mode_u_buf[4], mode_g_buf[4], mode_o_buf[4], date_buf[64]; if (params->modifiers & MOD_verbose) printf("%s%s%s %d/%d %10lu %s ", rwx_string(member->arhdr.ar_mode >> 6, mode_u_buf), rwx_string(member->arhdr.ar_mode >> 3, mode_g_buf), rwx_string(member->arhdr.ar_mode, mode_o_buf), member->arhdr.ar_uid, member->arhdr.ar_gid, member->arhdr.ar_size, date_string(member->arhdr.ar_date, date_buf)); printf("%s\n", member->name); return 0; } static int ar_x_copy(struct params *params, struct member *member, PDP10_FILE *memberfp) { if (pdp10_fseeko(params->pdp10fp, member->srcoffset, PDP10_SEEK_SET) < 0) { fprintf(stderr, "%s: %s: failed to fseek to member %s: %s\n", params->progname, params->arname, member->name, strerror(errno)); return -1; } return copy_file_data(params, member->arhdr.ar_size, params->pdp10fp, params->arname, memberfp, member->name); } static int ar_x_member(struct params *params, struct member *member) { PDP10_FILE *memberfp; int status; if (params->modifiers & MOD_verbose) printf("x - %s\n", member->name); memberfp = pdp10_fopen(member->name, "wb"); if (!memberfp) { fprintf(stderr, "%s: %s: %s: failed to open: %s\n", params->progname, params->arname, member->name, strerror(errno)); return -1; } status = ar_x_copy(params, member, memberfp); pdp10_fclose(memberfp); if (status < 0) return -1; if (chmod(member->name, member->arhdr.ar_mode & 0777) < 0) { fprintf(stderr, "%s: %s: %s: failed to set mode %o: %s\n", params->progname, params->arname, member->name, member->arhdr.ar_mode & 0777, strerror(errno)); return -1; } return 0; } static int ar_tx(struct params *params, char **files, int nrfiles) { struct member *member; int status; int i; if (nrfiles < 1) { fprintf(stderr, "%s: archive name missing\n", params->progname); return -1; } params->arname = files[0]; ++files; --nrfiles; params->pdp10fp = pdp10_fopen(params->arname, "rb"); if (!params->pdp10fp) { fprintf(stderr, "%s: %s: failed to open: %s\n", params->progname, params->arname, strerror(errno)); return -1; } if (read_archive(params) < 0) return -1; for (member = params->members_head; member; member = member->next) if (ar_tx_should_process_member(params, member, files, nrfiles)) { switch (params->operation) { case 't': status = ar_t_member(params, member); break; case 'x': status = ar_x_member(params, member); break; default: fprintf(stderr, "%s: %s: unexpected operation '%c'\n", params->progname, __FUNCTION__, params->operation); return -1; } if (status < 0) return -1; } if (status < 0) return status; for (i = 0; i < nrfiles; ++i) if (files[i]) { status = -1; fprintf(stderr, "no entry %s in archive\n", files[i]); } return status; } /* * ar d/q/r code */ static int fixup_arstrtab(struct params *params) { unsigned long new_strtab_len; struct member *member; size_t namlen; char *new_strtab_bytes; unsigned long curpos; new_strtab_len = 0; for (member = params->members_head; member; member = member->next) { if (member->strtaboffset == -1UL) continue; namlen = strlen(member->name); if (namlen < 16) { strcpy(member->arhdr.ar_name, member->name); member->name = member->arhdr.ar_name; member->strtaboffset = -1UL; continue; } new_strtab_len += namlen + 2; /* for "\0\n" which is output as "/\n" */ } if (new_strtab_len == 0) { free(params->strtab.bytes); params->strtab.bytes = NULL; params->strtab.len = 0; return 0; } new_strtab_bytes = malloc(new_strtab_len); if (!new_strtab_bytes) { fprintf(stderr, "%s: %s: failed to allocate %lu bytes for updated string table: %s\n", params->progname, params->arname, new_strtab_len, strerror(errno)); return -1; } curpos = 0; for (member = params->members_head; member; member = member->next) { if (member->strtaboffset == -1UL) continue; namlen = strlen(member->name); member->strtaboffset = curpos; strcpy(new_strtab_bytes + curpos, member->name); member->name = new_strtab_bytes + curpos; *(new_strtab_bytes + curpos + namlen + 1) = '\n'; curpos += namlen + 2; } free(params->strtab.bytes); params->strtab.bytes = new_strtab_bytes; params->strtab.len = new_strtab_len; return 0; } static void update_arhdr(struct arhdr *arhdr, struct stat *stbuf) { arhdr->ar_date = stbuf->st_mtime; arhdr->ar_uid = stbuf->st_uid; arhdr->ar_gid = stbuf->st_gid; arhdr->ar_mode = stbuf->st_mode; /* stbuf->st_size is the file size in octets, convert it to the size in nonets; see lib/pdp10-stdio.c:pdp10_fseeko() for the derivation of this formula */ arhdr->ar_size = (stbuf->st_size / 9) * 8 + ((stbuf->st_size % 9) * 8) / 9; } static int ar_d_process_files(struct params *params, char **files, int nrfiles) { struct member *member; int i; char code; code = 0; for (i = 0; i < nrfiles; ++i) { member = lookup_member(params, files[i]); if (!member) { fprintf(stderr, "%s: %s: member %s not found\n", params->progname, params->arname, files[i]); return -1; } unlink_member(member); free(member); code = 'd'; if (params->modifiers & MOD_verbose) printf("%c - %s\n", code, files[i]); } return code; /* >0 if changes, 0 if no changes, -1 if error above */ } static int ar_qr_process_files(struct params *params, char **files, int nrfiles) { struct stat stbuf; struct member *member; int i; char code; code = 0; for (i = 0; i < nrfiles; ++i) { if (stat(files[i], &stbuf) < 0) { fprintf(stderr, "%s: %s: failed to stat: %s\n", params->progname, files[i], strerror(errno)); return -1; } member = lookup_member(params, files[i]); if (member && params->operation != 'q') { if ((params->modifiers & MOD_newer) && stbuf.st_mtime <= member->arhdr.ar_date) continue; code = 'r'; } else { member = malloc(sizeof *member); if (!member) { fprintf(stderr, "%s: %s: failed to allocate %zu bytes for new member\n", params->progname, params->arname, sizeof *member); return -1; } member->name = files[i]; member->strtaboffset = -2UL; append_member(params, member); code = 'a'; } if (params->modifiers & MOD_verbose) printf("%c - %s\n", code, files[i]); member->srcoffset = 0; update_arhdr(&member->arhdr, &stbuf); } return code; /* >0 if changes, 0 if no changes, -1 if error above */ } static char *make_tmparname(struct params *params) { char *last_slash; unsigned int preflen; char *tmparname; if (!params->pdp10fp) return params->arname; last_slash = strrchr(params->arname, '/'); if (last_slash) preflen = last_slash + 1 - params->arname; else preflen = 0; tmparname = malloc(preflen + 5 + 6 + 1); /* artmpXXXXXX\0 */ if (!tmparname) { fprintf(stderr, "%s: %s: failed to allocate %u bytes for temporary archive name: %s\n", params->progname, params->arname, preflen + 5 + 6 + 1, strerror(errno)); return NULL; } sprintf(tmparname, "%.*sartmpXXXXXX", preflen, params->arname); return tmparname; } static PDP10_FILE *make_tmparfp(struct params *params, char *tmparname) { int tmparfd; PDP10_FILE *tmparfp; if (!params->pdp10fp) { tmparfp = pdp10_fopen(params->arname, "wb"); if (!tmparfp) fprintf(stderr, "%s: %s: failed to create: %s\n", params->progname, params->arname, strerror(errno)); if (!(params->modifiers & MOD_create)) printf("%s: creating %s\n", params->progname, params->arname); return tmparfp; } tmparfd = mkstemp(tmparname); if (tmparfd < 0) { fprintf(stderr, "%s: %s: failed to create temporary file: %s\n", params->progname, tmparname, strerror(errno)); return NULL; } tmparfp = pdp10_fdopen(tmparfd, "wb"); if (!tmparfp) fprintf(stderr, "%s: fdopen failed: %s\n", params->progname, strerror(errno)); return tmparfp; } static int write_member(struct params *params, const char *tmparname, PDP10_FILE *tmparfp, struct member *member) { PDP10_FILE *srcfp; const char *srcname; int status; if (member->srcoffset == 0) { srcname = member->name; srcfp = pdp10_fopen(srcname, "rb"); if (!srcfp) { fprintf(stderr, "%s: %s: failed to open: %s\n", params->progname, srcname, strerror(errno)); return -1; } } else { srcname = params->arname; srcfp = params->pdp10fp; if (pdp10_fseeko(srcfp, member->srcoffset, PDP10_SEEK_SET) < 0) { fprintf(stderr, "%s: %s: failed to fseek to member %s: %s\n", params->progname, srcname, member->name, strerror(errno)); return -1; } } if (member->strtaboffset != -1UL) sprintf(member->arhdr.ar_name, "/%lu", member->strtaboffset); status = -1; do { if (write_arhdr(params, tmparname, tmparfp, &member->arhdr) < 0) break; if (copy_file_data(params, member->arhdr.ar_size, srcfp, srcname, tmparfp, tmparname) < 0) break; if ((member->arhdr.ar_size & 1) && ar_fputc(params, tmparname, tmparfp, '\n') < 0) break; status = 0; } while (0); if (member->srcoffset == 0) pdp10_fclose(srcfp); return status; } static int write_archive(struct params *params, const char *tmparname, PDP10_FILE *tmparfp) { struct member *member; struct stat stbuf; if (write_armag(params, tmparname, tmparfp) < 0) return -1; if (write_arstrtab(params, tmparname, tmparfp) < 0) return -1; for (member = params->members_head; member; member = member->next) if (write_member(params, tmparname, tmparfp, member) < 0) return -1; if (!params->pdp10fp) return 0; if (stat(params->arname, &stbuf) < 0) { fprintf(stderr, "%s: %s: failed to stat: %s\n", params->progname, params->arname, strerror(errno)); return -1; } if (chmod(tmparname, stbuf.st_mode) < 0) { fprintf(stderr, "%s: %s: failed to chmod 0%o: %s\n", params->progname, tmparname, stbuf.st_mode, strerror(errno)); return -1; } if (chown(tmparname, stbuf.st_uid, stbuf.st_gid) < 0) { fprintf(stderr, "%s: %s: failed to chown %u/%u: %s\n", params->progname, tmparname, stbuf.st_uid, stbuf.st_gid, strerror(errno)); return -1; } if (unlink(params->arname) < 0) { fprintf(stderr, "%s: %s: failed to unlink: %s\n", params->progname, params->arname, strerror(errno)); return -1; } if (link(tmparname, params->arname) < 0) { fprintf(stderr, "%s: failed to link %s to %s: %s\n", params->progname, tmparname, params->arname, strerror(errno)); return -1; } return 0; } static int ar_dqr(struct params *params, char **files, int nrfiles) { char *tmparname; PDP10_FILE *tmparfp; int status; if (nrfiles < 1) { fprintf(stderr, "%s: archive name missing\n", params->progname); return -1; } params->arname = files[0]; ++files; --nrfiles; params->pdp10fp = pdp10_fopen(params->arname, "rb"); if (params->pdp10fp != NULL) { if (read_archive(params) < 0) return -1; } else if (params->operation == 'd') { fprintf(stderr, "%s: %s: failed to open: %s\n", params->progname, params->arname, strerror(errno)); return -1; } if (params->operation == 'd') status = ar_d_process_files(params, files, nrfiles); else status = ar_qr_process_files(params, files, nrfiles); if (status <= 0) return status; if (fixup_arstrtab(params) < 0) return -1; tmparname = make_tmparname(params); if (!tmparname) return -1; tmparfp = make_tmparfp(params, tmparname); if (!tmparfp) return -1; status = write_archive(params, tmparname, tmparfp); pdp10_fclose(tmparfp); if (params->pdp10fp) { unlink(tmparname); free(tmparname); } return status; } /* * ar d/q/r/t/x dispatcher */ static int ar(struct params *params, char **files, int nrfiles) { switch (params->operation) { case 'd': case 'q': case 'r': return ar_dqr(params, files, nrfiles); case 't': case 'x': return ar_tx(params, files, nrfiles); default: fprintf(stderr, "%s: NYI: operation '%c'\n", params->progname, params->operation); return -1; } } /* * Command-line interface. */ static void usage(const char *progname) { fprintf(stderr, "Usage: %s [-]{d,q,r,t,x}[cuvV] archive [member...]\n", progname); } int main(int argc, char **argv) { struct params params; char *opts; params_init(¶ms); params.progname = argv[0]; if (argc < 2) { usage(params.progname); return 1; } opts = argv[1]; if (*opts == '-') ++opts; for (;; ++opts) { switch (*opts) { case 'd': case 'q': case 'r': case 't': case 'x': params.operation = *opts; continue; case 'c': params.modifiers |= MOD_create; continue; case 'u': params.modifiers |= MOD_newer; continue; case 'v': params.modifiers |= MOD_verbose; continue; case 'V': printf(VERSION); return 0; case '\0': break; default: fprintf(stderr, "%s: invalid option: %c\n", params.progname, *opts); usage(params.progname); return 1; } break; } return ar(¶ms, &argv[2], argc - 2) == 0 ? 0 : 1; }