mwalls@unirot.UUCP (monty walls) (01/03/88)
---------------------------------------------------------------------------- Enclosed is a complete mostly V7 compatible version of 'ar'. I wrote this after getting frustrated with the old version of 'ar' while doing library maintence. It handles all the V7 options. Have fun with it. -Monty Walls -------------------------------cut-here------------------------------------- echo x - ar.man gres '^X' '' > ar.man << '/' XNAME X ar X XSYNOPSIS X ar 'qmrdtpxvuaibcl' [posname] archive [file] ... X XDESCRIPTION X 'Ar' maintains unix V7 compatible archives. It allows groups of X files to be combined into a single archive file. It is normally X used to maintain object libraries. X X one of: qrqtpmx X X q: quickly append to the end of the archive file. 'Ar' does X not check for duplicate members. X X m: move named files. 'Ar' expects 'a', 'b', or 'i' to be X specified also. If 'posname' is not specified 'Ar' will X move named files to the end of the archive. X X r: replace (append when not in archive). if 'u' is also X specified then 'Ar' will only replace if new version is newer. X if 'a' or 'b' or 'i' are specified 'posname' is expected and X any new members will be placed either after ('a') or before X ('b' or 'i') 'posname'. X X d: delete. 'Ar' will delete the name members. X t: print contents of archive X p: print named files X x: extract X X optionally concatencated with one or more of: vuaibcl X X l: local temporary file for work instead of /tmp/ar.$$$$$ X v: verbose X a: after 'posname' X b: before 'posname' X i: before 'posname' X c: create (suppresses creation message) X u: replace only if dated later than member in archive X X XFILES X /tmp/ar.1.* Temporaries X XBUGS X 'Ar' will happily add multiple copies of a file to the archive. X 'Ar' will use the first occurence of a member only for X 'x', 'd', 'p', 'm', and 'r'. X / echo x - ar.h gres '^X' '' > ar.h << '/' X/* ar.c header file V7 */ X X#define ARMAG 0177545l Xstruct ar_hdr { X char ar_name[14]; X long ar_date; /* not Intel format */ X char ar_uid; X char ar_gid; X int ar_mode; X long ar_size; /* not Intel format */ X}; / echo x - ar.c gres '^X' '' > ar.c << '/' X/* ar - archiver Author: Michiel Huisjes */ X/* V7 upgrade Author: Monty Walls */ X X/* X * Usage: ar 'key' [posname] archive [file] ... X * X * where 'key' is one of: qrqtpmx X * X * q: quickly append to the end of the archive file X * m: move named files X * r: replace (append when not in archive) X * d: delete X * t: print contents of archive X * p: print named files X * x: extract X * X * concatencated with one or more of: vuaibcl X * X * l: local temporary file for work instead of /tmp/ar.$$$$$ X * v: verbose X * a: after 'posname' X * b: before 'posname' X * i: before 'posname' X * c: create (suppresses creation message) X * u: replace only if dated later than member in archive X */ X X/* mods: X * 1.2 upgrade. X * local directory support (mrw). X * full V7 functionality + complete rewrite (mrw). X * X * notes: X * pdp11 long format & Intel long format are different. X */ X X/* include files */ X#include <stdio.h> X#include <stat.h> X#include <signal.h> X#include <ar.h> X X/* macro functions */ X#define FOREVER (32766) X#define odd(nr) (nr & 1) X#define even(nr) (odd(nr) ? nr + 1 : nr) X#define quit(pid,sig) (kill(pid,sig),sleep(FOREVER)) X#ifndef tell X# define tell(f) (lseek(f, 0l, 1)) X#endif X X/* option switches */ X/* major options */ X#define EXTRACT 0x01 X#define REPLACE 0x02 X#define PRINT 0x04 X#define TABLE 0x08 X#define DELETE 0x10 X#define APPEND 0x20 X#define MOVE 0x40 X X/* minor options */ X#define BEFORE 0x01 X#define AFTER 0x02 X#define LOCAL 0x01 X#define VERBOSE 0x01 X#define CREATE 0x01 X#define ONLY 0x01 X X/* mode bits maps */ X#define EXEC_OWNER 00001 X#define EXEC_GROUP 00010 X#define EXEC_ALL 00100 X#define READ_OWNER 00004 X#define READ_GROUP 00040 X#define READ_ALL 00400 X#define WRITE_OWNER 00002 X#define WRITE_GROUP 00020 X#define WRITE_ALL 00200 X#define SET_UID 04000 X#define SET_GID 02000 X X/* global defines */ X#define BUFFERSIZE 4096 X#define WRITE 2 /* both read & write */ X#define READ 0 X#define MAGICSIZE sizeof(short) /* size of magic number in file */ X X/* option switches */ Xchar verbose = 0; Xchar local = 0; Xchar create = 0; Xchar only = 0; Xchar major = 0; Xchar minor = 0; X X/* global variables */ Xchar *tmp1; Xchar *tmp2; Xchar *progname; Xchar *posname = NULL; Xchar *afile; Xchar buffer[BUFFERSIZE]; Xlong pos_offset = -1; Xint mypid; X X/* keep track of member moves using this struct */ Xstruct mov_list { X long pos; X struct mov_list *next; X} *moves = NULL; X X/* forward declarations and external references */ Xextern char *malloc(); Xextern char *mktemp(), *rindex(); Xextern int strcmp(); Xextern print_date(); Xextern user_abort(), usage(); Xextern long lseek(); X Xint Xmain(argc, argv) Xint argc; Xchar **argv; X{ X int ac, opts_seen = 0, rc; X char *av; X X progname = argv[0]; X if (argc < 3) X usage(); X X for (av = argv[1]; *av; ++av) { X switch (*av) { /* major option */ X case 'q': X major |= APPEND; X ++opts_seen; X break; X case 'r': X major |= REPLACE; X ++opts_seen; X break; X case 'x': X major |= EXTRACT; X ++opts_seen; X break; X case 'p': X major |= PRINT; X ++opts_seen; X break; X case 'm': X major |= MOVE; X ++opts_seen; X break; X case 'd': X major |= DELETE; X ++opts_seen; X break; X case 't': X major |= TABLE; X ++opts_seen; X break; X case 'l': X local |= LOCAL; X break; X case 'a': X minor |= AFTER; X break; X case 'i': X case 'b': X minor |= BEFORE; X break; X case 'v': X verbose |= VERBOSE; X break; X case 'c': X create |= CREATE; X break; X case 'u': X only |= ONLY; X break; X default: X usage(); X } X } X X if (opts_seen != 1) X usage(); X X /* now do edits on options */ X if (!(major & (REPLACE | MOVE))) { X if (minor & (AFTER | BEFORE)) X usage(); X } X else if (major & MOVE) { X if (!(minor & (AFTER | BEFORE))) X usage(); X } X else if (only & ONLY) X if (!(major & REPLACE)) X usage(); X X if (local) X tmp1 = mktemp("./ar.1.XXXXXX"); X else X tmp1 = mktemp("/tmp/ar.1.XXXXXX"); X X /* now if minor says AFTER or BEFORE - then get posname */ X if (minor & (AFTER | BEFORE) && argc >= 4) { X posname = argv[2]; X afile = argv[3]; X ac = 4; X } X else { X posname = (char *)NULL; X afile = argv[2]; X ac = 3; X } X X /* exit logic consists of doing a kill on my pid to insure that we */ X /* get the current clean up and exit logic */ X mypid = getpid(); X signal(SIGINT, user_abort); X X switch (major) { X case REPLACE: X case DELETE: X case MOVE: X ar_members(ac, argc, argv); X break; X case EXTRACT: X case TABLE: X case PRINT: X ar_common(ac, argc, argv); X break; X case APPEND: X append_members(ac, argc, argv); X break; X default: X usage(); X } X X for (rc = 0; ac < argc; ++ac) { X if (*argv[ac] != '\0') { X /* no processing done on this name */ X fprintf(stderr,"Error %s: %s not found in ar\n", progname, argv[ac]); X rc = 1; X } X } X fflush(stdout); X exit(rc); X} X Xusage() X{ X fprintf(stderr,"Usage: %s [qrxdpmt][abivulc] [posname] afile name ... \n",progname); X exit(1); X} X Xuser_abort() X{ X unlink(tmp1); X exit(1); X} X Xinsert_abort() X{ X unlink(tmp1); X unlink(tmp2); X exit(1); X} X Xmwrite(fd, address, bytes) Xint fd; Xregister char *address; Xregister int bytes; X{ X if (write(fd, address, bytes) != bytes) { X fprintf(stderr," Error: %s - Write error\n",progname); X quit(mypid, SIGINT); X } X} X Xlong Xswap(l) Xlong l; X{ X union { X struct { X int word1, word2; X } words; X long n; X } u_in, u_out; X X u_in.n = l; X u_out.words.word1 = u_in.words.word2; X u_out.words.word2 = u_in.words.word1; X return (u_out.n); X X} X Xaddmove(pos) Xlong pos; X{ X struct mov_list *newmove; X X newmove = (struct mov_list *)malloc(sizeof(struct mov_list)); X newmove->pos = pos; X newmove->next = moves; X moves = newmove; X} X Xstruct ar_hdr * Xget_member(fd) Xint fd; X{ X int ret; X static struct ar_hdr member; X X if ((ret = read(fd, &member, sizeof(struct ar_hdr))) <= 0) X return ((struct ar_hdr *)NULL); X if (ret != sizeof(struct ar_hdr)) { X fprintf(stderr,"Error: ar corrupted archive ar\n"); X quit(mypid,SIGINT); X } X X /* the archive long format is pdp11 not intel X * therefore we must reformat them for our internal use X */ X X member.ar_date = swap(member.ar_date); X member.ar_size = swap(member.ar_size); X return (&member); X} X Xint Xopen_archive(filename, opt, to_create) Xchar *filename; Xint opt; X{ X static unsigned short magic; X int fd; X X /* to_create can have values of 0,1,2 */ X /* 0 - don't create a file. */ X /* 1 - create file but use create switch message mode */ X /* 2 - create file but don't talk about it */ X X if (to_create) { X if ((fd = creat(filename, 0644)) < 0) { X fprintf(stderr, "Error: %s can not create %s\n",progname, filename); X quit(mypid,SIGINT); X } X if (!create && to_create == 1) fprintf(stderr, "%s:%s created\n", progname, filename); X magic = ARMAG; X mwrite(fd, &magic, MAGICSIZE); X return (fd); X } X else { X if ((fd = open(filename, opt)) < 0) { X if (opt == WRITE) X return (open_archive(filename, opt, 1)); X else { X fprintf(stderr, "Error: %s can not open %s\n",progname, filename); X quit(mypid,SIGINT); X } X } X /* now check the magic number for ar V7 file */ X lseek(fd, 0l, 0); X read(fd, &magic, MAGICSIZE); X if (magic != ARMAG) { X fprintf(stderr, "Error: not %s V7 format - %s\n",progname, filename); X quit(mypid,SIGINT); X } X if (major & APPEND) X lseek(fd, 0l, 2); /* seek eof position */ X X return (fd); X } X} X X Xint Xrebuild(fd, tempfd) Xregister int fd, tempfd; X{ X register int n; X X /* after we have built the archive to a temporary file and */ X /* everything has worked out- we copy the archive back to */ X /* original file */ X X signal(SIGINT, SIG_IGN); X close(fd); X close(tempfd); X fd = open_archive(afile, WRITE, 2); X tempfd = open_archive(tmp1, WRITE, 0); X while ((n = read(tempfd, buffer, BUFFERSIZE)) > 0) X mwrite(fd, buffer, n); X close(tempfd); X unlink(tmp1); X return (fd); X} X Xprint_mode(mode) Xshort mode; X{ X char g_ex, o_ex, all_ex; X char g_rd, o_rd, all_rd; X char g_wr, o_wr, all_wr; X X g_ex = EXEC_GROUP & mode ? 'x' : '-'; X o_ex = EXEC_OWNER & mode ? 'x' : '-'; X all_ex = EXEC_ALL & mode ? 'x' : '-'; X X g_ex = SET_GID & mode ? 's' : g_ex; X o_ex = SET_UID & mode ? 's' : o_ex; X X g_rd = READ_GROUP & mode ? 'r' : '-'; X o_rd = READ_OWNER & mode ? 'r' : '-'; X all_rd = READ_ALL & mode ? 'r' : '-'; X X g_wr = WRITE_GROUP & mode ? 'w' : '-'; X o_wr = WRITE_OWNER & mode ? 'w' : '-'; X all_wr = WRITE_ALL & mode ? 'w' : '-'; X X fprintf(stdout,"%c%c%c",o_rd, o_wr, o_ex); X fprintf(stdout,"%c%c%c",g_rd, g_wr, g_ex); X fprintf(stdout,"%c%c%c",all_rd, all_wr, all_ex); X} X Xprint_header(member) Xstruct ar_hdr *member; X{ X if (verbose) { X print_mode(member->ar_mode); X fprintf(stdout,"%3.3d",member->ar_uid); X fprintf(stdout,"/%-3.3d ",member->ar_gid); X fprintf(stdout,"%5.5d",member->ar_size); X print_date(member->ar_date); X } X fprintf(stdout,"%-14.14s\n",member->ar_name); X} X Xprint(fd,member) Xint fd; Xstruct ar_hdr *member; X{ X int outfd; X register int cnt, ret; X int do_align; X X if (major & EXTRACT) { X if ((outfd = creat(member->ar_name,0666)) < 0) { X fprintf(stderr,"Error: %s could not creat %s\n",progname, member->ar_name); X quit(mypid,SIGINT); X } X if (verbose) X fprintf(stdout,"x %s\n",member->ar_name); X } X else X outfd = fileno(stdout); X X for (cnt = member->ar_size; cnt > 0; cnt -= ret) { X ret = read(fd, buffer, (cnt < BUFFERSIZE ? cnt : BUFFERSIZE)); X if (ret > 0) X write(outfd,buffer, ret); X } X if (odd(member->ar_size)) X lseek(fd,1l,1); /* realign ourselves */ X X if (major & EXTRACT) { X close(outfd); X chmod(member->ar_name, member->ar_mode); X } X} X X/* copy a given member from fd1 to fd2 */ Xcopy_member(infd, outfd, member) Xint infd, outfd; Xstruct ar_hdr *member; X{ X int n, cnt; X long m, size; X X /* save copies for our use */ X m = size = member->ar_size; X X /* format for disk usage */ X member->ar_size = swap(member->ar_size); X member->ar_date = swap(member->ar_date); X X mwrite(outfd, member, sizeof(struct ar_hdr)); X for (; m > 0; m -= n) { X cnt = (m < BUFFERSIZE ? m : BUFFERSIZE); X if ((n = read(infd, buffer, cnt)) != cnt) { X fprintf(stderr,"Error: %s - read error on %s\n",progname, member->ar_name); X quit(mypid, SIGINT); X } X mwrite(outfd, buffer, n); X } X if (odd(size)) { /* pad to word boundary */ X mwrite(outfd, buffer, 1); X lseek(infd,1l,1); /* realign reading fd */ X } X} X X/* insert at current offset - name file */ Xinsert(fd, name, mess, oldmember) Xint fd; Xchar *name, *mess; Xstruct ar_hdr *oldmember; X{ X static struct ar_hdr member; X static struct stat status; X int in_fd; X X if (stat(name, &status) < 0) { X fprintf(stderr,"Error: %s cannot find file %s\n",progname,name); X quit(mypid,SIGINT); X } X else if ((in_fd = open(name, READ)) < 0) { X fprintf(stderr,"Error: %s cannot open file %s\n",progname,name); X quit(mypid,SIGINT); X } X strcpy(member.ar_name, basename(name)); X member.ar_uid = status.st_uid; X member.ar_gid = status.st_gid; X member.ar_mode = status.st_mode & 07777; X member.ar_date = status.st_mtime; X member.ar_size = status.st_size; X if (only & ONLY) X if (oldmember != (struct ar_hdr *)NULL) X if (member.ar_date <= oldmember->ar_date) { X close(in_fd); X if (verbose) fprintf(stdout, "not %s - %s\n",mess, name); X return (-1); X } X X copy_member(in_fd, fd, &member); X if (verbose) X fprintf(stdout, "%s - %s\n",mess, name); X close(in_fd); X return (1); X} X Xint Xar_move(oldfd, arfd,mov) Xint oldfd, arfd; Xstruct mov_list *mov; X{ X long pos; X int cnt, want, a, newfd; X struct ar_hdr *member; X X if (local) X tmp2 = mktemp("./ar.2.XXXXXX"); X else X tmp2 = mktemp("/tmp/ar.2.XXXXXX"); X X close(oldfd); /* close old temp file */ X signal(SIGINT, insert_abort); /* set new signal handler */ X newfd = open_archive(tmp2, WRITE, 2); /* open new tmp file */ X oldfd = open_archive(tmp1, WRITE, 0); /* reopen old tmp file */ X X /* copy archive till we get to pos_offset */ X for (pos = pos_offset; pos > 0; pos -= cnt) { X want = (pos < BUFFERSIZE ? pos : BUFFERSIZE); X if ((cnt = read(oldfd, buffer, want)) > 0) X mwrite(newfd, buffer, cnt); X } X /* if minor = 'a' then skip over posname */ X if (minor & AFTER) { X if ((member = get_member(oldfd)) != NULL) X copy_member(oldfd, newfd, member); X } X /* move members in the library */ X while (mov != NULL) { X lseek(arfd, mov->pos, 0); X if ((member = get_member(arfd)) != NULL) X copy_member(arfd, newfd, member); X mov = mov->next; X if (verbose) fprintf(stdout, "m - %s\n", member->ar_name); X } X X /* copy rest of library into new tmp file */ X while ((member = get_member(oldfd)) != NULL) X copy_member(oldfd, newfd, member); X X /* detach old temp file */ X close(oldfd); X unlink(tmp1); X X /* change context temp file */ X tmp1 = tmp2; X return (newfd); X} X Xint Xar_insert(oldfd, ac, argc, argv) Xint oldfd; Xint ac, argc; Xchar **argv; X{ X long pos; X int cnt, want, a, newfd; X struct ar_hdr *member; X X if (local) X tmp2 = mktemp("./ar.2.XXXXXX"); X else X tmp2 = mktemp("/tmp/ar.2.XXXXXX"); X X close(oldfd); /* close old temp file */ X signal(SIGINT, insert_abort); /* set new signal handler */ X newfd = open_archive(tmp2, WRITE, 2); /* open new tmp file */ X oldfd = open_archive(tmp1, WRITE, 0); /* reopen old tmp file */ X X /* copy archive till we get to pos_offset */ X for (pos = pos_offset; pos > 0; pos -= cnt) { X want = (pos < BUFFERSIZE ? pos : BUFFERSIZE); X if ((cnt = read(oldfd, buffer, want)) > 0) X mwrite(newfd, buffer, cnt); X } X /* if minor = 'a' then skip over posname */ X if (minor & AFTER) { X if ((member = get_member(oldfd)) != NULL) X copy_member(oldfd, newfd, member); X } X X /* copy new members into the library */ X for (a = ac+1; a <= argc; ++a) X if (argv[a-1] && *argv[a-1] != '\0') { X insert(newfd, argv[a-1], "a", (struct ar_hdr *)NULL); X *argv[a-1] = '\0'; X } X X /* copy rest of library into new tmp file */ X while ((member = get_member(oldfd)) != NULL) X copy_member(oldfd, newfd, member); X X /* detach old temp file */ X close(oldfd); X unlink(tmp1); X X /* change context temp file */ X tmp1 = tmp2; X return (newfd); X} X Xar_common(ac, argc, argv) Xint ac, argc; Xchar **argv; X{ X int a, fd; X struct ar_hdr *member; X X fd = open_archive(afile, READ, 0); X while ((member = get_member(fd)) != NULL) { X if (ac < argc) { X for (a = ac+1; a <= argc; ++a) { X if (strcmp(basename(argv[a-1]),member->ar_name) == 0) { X if (major & TABLE) X print_header(member); X else if (major & (PRINT | EXTRACT)) X print(fd, member); X *argv[a-1] = '\0'; X break; X } X else if (major & (PRINT | EXTRACT)) X lseek(fd, (long)even(member->ar_size), 1); X } X } X else { X if (major & TABLE) X print_header(member); X else if (major & (PRINT | EXTRACT)) X print(fd, member); X } X if (major & TABLE) X lseek(fd, (long)even(member->ar_size), 1); X } X} X Xar_members(ac, argc, argv) Xint ac, argc; Xchar **argv; X{ X int a, fd, tempfd, rc; X struct ar_hdr *member; X long *lpos; X X fd = open_archive(afile, WRITE, 0); X tempfd = open_archive(tmp1, WRITE, 2); X while ((member = get_member(fd)) != NULL) { X X /* if posname specified check for our member */ X /* if our member save his starting pos in our working file*/ X if (posname && strcmp(posname, member->ar_name) == 0) X pos_offset = tell(tempfd) - MAGICSIZE; X X if (ac < argc) { /* we have a list of members to check */ X for (a = ac+1; a <= argc; ++a) X if (strcmp(basename(argv[a-1]),member->ar_name) == 0) { X if (major & REPLACE) { X if (insert(tempfd,argv[a-1],"r", member) < 0) X copy_member(fd, tempfd, member); X else X lseek(fd, (long)even(member->ar_size), 1); X } X else if (major & MOVE) { X /* cheat by saving pos in archive */ X addmove((tell(fd) - sizeof(struct ar_hdr))); X lseek(fd, (long)even(member->ar_size), 1); X } X *argv[a-1] = '\0'; X break; X } X } X if (ac >= argc || a > argc) /*nomatch on a member name */ X copy_member(fd, tempfd, member); X else if (major & DELETE) { X if (verbose) fprintf(stdout,"d - %s\n",member->ar_name); X lseek(fd, (long)even(member->ar_size), 1); X } X } X if (major & MOVE) { X if (posname == NULL) X pos_offset = lseek(fd, 0l, 2); X else if (pos_offset == (-1)) { X fprintf(stderr,"Error: %s cannot find file %s\n",progname,posname); X quit(mypid,SIGINT); X } X tempfd = ar_move(tempfd, fd, moves); X } X else if (major & REPLACE) { X /* take care to add left overs */ X /* if the posname is not found we just add to end of ar */ X if (posname && pos_offset != (-1)) { X tempfd = ar_insert(tempfd, ac, argc, argv); X } X else { X for (a = ac+1; a <= argc; ++a) X if (*argv[a-1]) { X insert(tempfd, argv[a-1], "a", (struct ar_hdr *)NULL); X *argv[a-1] = '\0'; X } X } X } X fd = rebuild(fd, tempfd); X close(fd); X} X Xappend_members(ac, argc, argv) Xint ac, argc; Xchar **argv; X{ X int a, fd; X struct ar_hdr *member; X X /* quickly append members don't worry about dups in ar */ X fd = open_archive(afile, WRITE, 0); X if (ac < argc) { X if (odd(lseek(fd, 0l, 2))) X mwrite(fd, buffer, 1); X /* while not end of member list insert member at end */ X for (a = ac+1; a <= argc; ++a) { X insert(fd, argv[a-1], "a", (struct ar_hdr *)NULL); X *argv[a-1] = '\0'; X } X } X close(fd); X} / echo x - basename.c gres '^X' '' > basename.c << '/' X#include <stdio.h> X Xchar *basename(path) Xchar *path; X{ X register char *ptr = path; X register char *last = (char *)NULL; X X while (*ptr != '\0') { X if (*ptr == '/') X last = ptr; X ptr++; X } X if (last == (char *)NULL) X return path; X if (*(last + 1) == '\0') { X *last = '\0'; X return basename(path); X } X return last + 1; X} / echo x - date.c gres '^X' '' > date.c << '/' X#include <stdio.h> X X#define MINUTE 60L X#define HOUR (60L * MINUTE) X#define DAY (24L * HOUR) X#define YEAR (365L * DAY) X#define LYEAR (366L * DAY) X Xint mo[] = { X 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 X}; X Xchar *moname[] = { X " Jan ", " Feb ", " Mar ", " Apr ", " May ", " Jun ", X " Jul ", " Aug ", " Sep ", " Oct ", " Nov ", " Dec " X}; X X/* Print the date. This only works from 1970 to 2099. */ Xprint_date(t) Xlong t; X{ X int i, year, day, month, hour, minute; X long length, time(), original; X X year = 1970; X original = t; X while (t > 0) { X length = (year % 4 == 0 ? LYEAR : YEAR); X if (t < length) X break; X t -= length; X year++; X } X X /* Year has now been determined. Now the rest. */ X day = (int) (t / DAY); X t -= (long) day * DAY; X hour = (int) (t / HOUR); X t -= (long) hour * HOUR; X minute = (int) (t / MINUTE); X X /* Determine the month and day of the month. */ X mo[1] = (year % 4 == 0 ? 29 : 28); X month = 0; X i = 0; X while (day >= mo[i]) { X month++; X day -= mo[i]; X i++; X } X X /* At this point, 'year', 'month', 'day', 'hour', 'minute' ok */ X fprintf(stdout, "%s%2.2d ",moname[month],++day); X if (time((long *)NULL) - original >= YEAR / 2L) X fprintf(stdout,"%4.4D ",(long)year); X else X fprintf(stdout,"%02.2d:%02.2d ",hour, minute); X} X / -------------------------------end-here-------------------------------------- Monty Walls, Tech. Support. MIS Oklahoma Tax Commission 2501 N. Lincoln OKC, OK, 73194 4 4