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