summaryrefslogtreecommitdiff
path: root/arscan.c
diff options
context:
space:
mode:
Diffstat (limited to 'arscan.c')
-rw-r--r--arscan.c477
1 files changed, 477 insertions, 0 deletions
diff --git a/arscan.c b/arscan.c
new file mode 100644
index 0000000..798e56b
--- /dev/null
+++ b/arscan.c
@@ -0,0 +1,477 @@
+/* Library function for scanning an archive file.
+ Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
+
+ This program 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 2, or (at your option)
+ any later version.
+
+ This program 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 this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* On the sun386i and in System V rel 3, ar.h defines two different archive
+ formats depending upon whether you have defined PORTAR (normal) or PORT5AR
+ (System V Release 1). There is no default, one or the other must be defined
+ to have a nonzero value. */
+
+#if (defined(sun386) || defined(USGr3) || defined(HPUX) \
+ && !defined(PORTAR) && !defined(PORT5AR))
+#define PORTAR 1
+#endif
+
+#include <ar.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef USG
+#include <fcntl.h>
+#else
+#include <sys/file.h>
+#endif
+
+#if (defined (STDC_HEADERS) || defined (__GNU_LIBRARY__) \
+ || defined (POSIX))
+#include <stdlib.h>
+#include <string.h>
+#define ANSI_STRING
+#else /* No standard headers. */
+
+#ifdef USG
+
+#include <string.h>
+#include <memory.h>
+#define ANSI_STRING
+
+#else /* Not USG. */
+#include <strings.h>
+
+#ifndef bcmp
+extern int bcmp ();
+#endif
+#ifndef bzero
+extern void bzero ();
+#endif
+#ifndef bcopy
+extern void bcopy ();
+#endif
+
+#endif /* USG. */
+
+extern char *malloc (), *realloc ();
+extern void free ();
+
+#endif /* Standard headers. */
+
+#ifdef ANSI_STRING
+#define index(s, c) strchr((s), (c))
+#define rindex(s, c) strrchr((s), (c))
+
+#define bcmp(s1, s2, n) memcmp ((s1), (s2), (n))
+#define bzero(s, n) memset ((s), 0, (n))
+#define bcopy(s, d, n) memcpy ((d), (s), (n))
+#endif ANSI_STRING
+#undef ANSI_STRING
+
+
+#ifndef AIAMAG
+#if (defined(APOLLO) || defined(HPUX) || defined(hpux) || \
+ (PORTAR == 1 && (defined(USGr3) || defined(u3b2) || defined(sun386))))
+#define AR_NAMELEN 14
+#define AR_TRAILING_SLASH /* Member names have a trailing slash. */
+#else
+#define AR_NAMELEN 15
+#endif
+#else /* AIX. */
+#define AR_NAMELEN 255
+#endif
+
+#if defined(__GNU_LIBRARY__) || defined(POSIX) || defined(_IBMR2)
+#include <unistd.h>
+#else
+extern int read (), close (), write (), fstat ();
+extern long int lseek (), atol ();
+extern int atoi ();
+#endif
+
+/* Takes three arguments ARCHIVE, FUNCTION and ARG.
+
+ Open the archive named ARCHIVE, find its members one by one,
+ and for each one call FUNCTION with the following arguments:
+ archive file descriptor for reading the data,
+ member name,
+ member header position in file,
+ member data position in file,
+ member data size,
+ member date,
+ member uid,
+ member gid,
+ member protection mode,
+ ARG.
+
+ The descriptor is poised to read the data of the member
+ when FUNCTION is called. It does not matter how much
+ data FUNCTION reads.
+
+ If FUNCTION returns nonzero, we immediately return
+ what FUNCTION returned.
+
+ Returns -1 if archive does not exist,
+ Returns -2 if archive has invalid format.
+ Returns 0 if have scanned successfully. */
+
+long int
+ar_scan (archive, function, arg)
+ char *archive;
+ long int (*function) ();
+ long int arg;
+{
+#ifdef AIAMAG
+ FL_HDR fl_header;
+#endif
+ register int desc = open (archive, O_RDONLY, 0);
+ if (desc < 0)
+ return -1;
+#ifdef SARMAG
+ {
+ char buf[SARMAG];
+ register int nread = read (desc, buf, SARMAG);
+ if (nread != SARMAG || bcmp (buf, ARMAG, SARMAG))
+ {
+ (void) close (desc);
+ return -2;
+ }
+ }
+#else
+#ifdef AIAMAG
+ {
+ register int nread = read (desc, &fl_header, FL_HSZ);
+ if (nread != FL_HSZ || bcmp (fl_header.fl_magic, AIAMAG, SAIAMAG))
+ {
+ (void) close (desc);
+ return -2;
+ }
+ }
+#else
+ {
+#ifndef M_XENIX
+ int buf;
+#else
+ unsigned short int buf;
+#endif
+ register int nread = read(desc, &buf, sizeof (buf));
+ if (nread != sizeof (buf) || buf != ARMAG)
+ {
+ (void) close (desc);
+ return -2;
+ }
+ }
+#endif
+#endif
+
+ /* Now find the members one by one. */
+ {
+#ifdef SARMAG
+ register long int member_offset = SARMAG;
+#else
+#ifdef AIAMAG
+ long int member_offset;
+ long int last_member_offset;
+
+ sscanf (fl_header.fl_fstmoff, "%12ld", &member_offset);
+ sscanf (fl_header.fl_lstmoff, "%12ld", &last_member_offset);
+#else
+#ifndef M_XENIX
+ register long int member_offset = sizeof (int);
+#else /* Xenix. */
+ register long int member_offset = sizeof (unsigned short int);
+#endif /* Not Xenix. */
+#endif
+#endif
+
+ while (1)
+ {
+ register int nread;
+ struct ar_hdr member_header;
+#ifdef AIAMAG
+ char name[AR_NAMELEN + 1];
+ int name_len;
+ long int dateval;
+ int uidval, gidval;
+ long int data_offset;
+#else
+ char name[sizeof member_header.ar_name + 1];
+#endif
+ long int eltsize;
+ int eltmode;
+ long int fnval;
+
+ if (lseek (desc, member_offset, 0) < 0)
+ {
+ (void) close (desc);
+ return -2;
+ }
+
+#ifdef AIAMAG
+#define AR_MEMHDR \
+ (sizeof (member_header) - sizeof (member_header._ar_name))
+ nread = read (desc, (char *) &member_header, AR_MEMHDR);
+
+ if (nread != AR_MEMHDR)
+ {
+ (void) close (desc);
+ return -2;
+ }
+
+ sscanf (member_header.ar_namlen, "%4d", &name_len);
+ nread = read (desc, name, name_len);
+
+ if (nread != name_len)
+ {
+ (void) close (desc);
+ return -2;
+ }
+
+ name[name_len] = 0;
+
+ sscanf (member_header.ar_date, "%12ld", &dateval);
+ sscanf (member_header.ar_uid, "%12d", &uidval);
+ sscanf (member_header.ar_gid, "%12d", &gidval);
+ sscanf (member_header.ar_mode, "%12o", &eltmode);
+ sscanf (member_header.ar_size, "%12ld", &eltsize);
+
+ if ((data_offset = member_offset + AR_MEMHDR + name_len + 2) % 2)
+ ++data_offset;
+
+ fnval =
+ (*function) (desc, name, member_offset, data_offset, eltsize,
+ dateval, uidval, gidval,
+ eltmode, arg);
+
+#else
+ nread = read (desc, (char *) &member_header, sizeof (struct ar_hdr));
+ if (nread == 0)
+ /* No data left means end of file; that is OK. */
+ break;
+
+ if (nread != sizeof (member_header)
+#ifdef ARFMAG
+ || bcmp (member_header.ar_fmag, ARFMAG, 2)
+#endif
+ )
+ {
+ (void) close (desc);
+ return -2;
+ }
+
+ bcopy (member_header.ar_name, name, sizeof member_header.ar_name);
+ {
+ register char *p = name + sizeof member_header.ar_name;
+ while (p > name && *--p == ' ')
+ *p = '\0';
+#ifdef AR_TRAILING_SLASH
+ if (*p == '/')
+ *p = '\0';
+#endif
+ }
+
+#ifndef M_XENIX
+ sscanf (member_header.ar_mode, "%o", &eltmode);
+ eltsize = atol (member_header.ar_size);
+#else /* Xenix. */
+ eltmode = (unsigned short int) member_header.ar_mode;
+ eltsize = member_header.ar_size;
+#endif /* Not Xenix. */
+
+ fnval =
+ (*function) (desc, name, member_offset,
+ member_offset + sizeof (member_header), eltsize,
+#ifndef M_XENIX
+ atol (member_header.ar_date),
+ atoi (member_header.ar_uid),
+ atoi (member_header.ar_gid),
+#else /* Xenix. */
+ member_header.ar_date,
+ member_header.ar_uid,
+ member_header.ar_gid,
+#endif /* Not Xenix. */
+ eltmode, arg);
+
+#endif /* Not AIAMAG */
+
+ if (fnval)
+ {
+ (void) close (desc);
+ return fnval;
+ }
+
+#ifdef AIAMAG
+ if (member_offset == last_member_offset) /* end of chain? */
+ break;
+
+ sscanf (member_header.ar_nxtmem, "%12ld", &member_offset);
+
+ if (lseek (desc, member_offset, 0) != member_offset)
+ {
+ (void) close (desc);
+ return -2;
+ }
+#else
+ member_offset += sizeof (member_header) + eltsize;
+ if (member_offset & 1) member_offset++;
+#endif
+ }
+ }
+
+ close (desc);
+ return 0;
+}
+
+/* Return nonzero iff NAME matches MEM. If NAME is longer than
+ sizeof (struct ar_hdr.ar_name), MEM may be the truncated version. */
+
+int
+ar_name_equal (name, mem)
+ char *name, *mem;
+{
+ char *p;
+
+ p = rindex (name, '/');
+ if (p != 0)
+ name = p + 1;
+
+#ifndef APOLLO
+
+ if (!strncmp (name, mem, AR_NAMELEN))
+ return 1;
+
+ if (!strncmp (name, mem, AR_NAMELEN - 2))
+ {
+ unsigned int namelen, memlen;
+
+ namelen = strlen (name);
+ memlen = strlen (mem);
+
+ if (memlen == AR_NAMELEN
+ && mem[AR_NAMELEN - 2] == '.' && mem[AR_NAMELEN - 1] == 'o'
+ && name[namelen - 2] == '.' && name[namelen -1] == 'o')
+ return 1;
+ }
+ return 0;
+
+#else /* APOLLO. */
+ return !strcmp (name, mem);
+#endif
+}
+
+/* ARGSUSED */
+static long int
+ar_member_pos (desc, mem, hdrpos, datapos, size, date, uid, gid, mode, name)
+ int desc;
+ char *mem;
+ long int hdrpos, datapos, size, date;
+ int uid, gid, mode;
+ char *name;
+{
+ if (!ar_name_equal (name, mem))
+ return 0;
+ return hdrpos;
+}
+
+/* Set date of member MEMNAME in archive ARNAME to current time.
+ Returns 0 if successful,
+ -1 if file ARNAME does not exist,
+ -2 if not a valid archive,
+ -3 if other random system call error (including file read-only),
+ 1 if valid but member MEMNAME does not exist. */
+
+int
+ar_member_touch (arname, memname)
+ char *arname, *memname;
+{
+ register long int pos = ar_scan (arname, ar_member_pos, (long int) memname);
+ register int fd;
+ struct ar_hdr ar_hdr;
+ register int i;
+ extern int errno;
+ struct stat statbuf;
+
+ if (pos < 0)
+ return (int) pos;
+ if (!pos)
+ return 1;
+
+ fd = open (arname, O_RDWR, 0666);
+ if (fd < 0)
+ return -3;
+ /* Read in this member's header */
+ if (lseek (fd, pos, 0) < 0)
+ goto lose;
+ if (sizeof ar_hdr != read (fd, (char *) &ar_hdr, sizeof ar_hdr))
+ goto lose;
+ /* Write back the header, thus touching the archive file. */
+ if (lseek (fd, pos, 0) < 0)
+ goto lose;
+ if (sizeof ar_hdr != write (fd, (char *) &ar_hdr, sizeof ar_hdr))
+ goto lose;
+ /* The file's mtime is the time we we want. */
+ fstat (fd, &statbuf);
+#if defined(ARFMAG) || defined(AIAMAG)
+ /* Advance member's time to that time */
+ for (i = 0; i < sizeof ar_hdr.ar_date; i++)
+ ar_hdr.ar_date[i] = ' ';
+ sprintf (ar_hdr.ar_date, "%ld", (long int) statbuf.st_mtime);
+#ifdef AIAMAG
+ ar_hdr.ar_date[strlen(ar_hdr.ar_date)] = ' ';
+#endif
+#else
+ ar_hdr.ar_date = statbuf.st_mtime;
+#endif
+ /* Write back this member's header */
+ if (lseek (fd, pos, 0) < 0)
+ goto lose;
+ if (sizeof ar_hdr != write (fd, (char *) &ar_hdr, sizeof ar_hdr))
+ goto lose;
+ close (fd);
+ return 0;
+
+ lose:
+ i = errno;
+ close (fd);
+ errno = i;
+ return -3;
+}
+
+#ifdef TEST
+
+long int
+describe_member (desc, name, hdrpos, datapos, size, date, uid, gid, mode)
+ int desc;
+ char *name;
+ long int hdrpos, datapos, size, date;
+ int uid, gid, mode;
+{
+ extern char *ctime ();
+
+ printf ("Member %s: %ld bytes at %ld (%ld).\n", name, size, hdrpos, datapos);
+ printf (" Date %s", ctime (&date));
+ printf (" uid = %d, gid = %d, mode = 0%o.\n", uid, gid, mode);
+
+ return 0;
+}
+
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ ar_scan (argv[1], describe_member);
+ return 0;
+}
+
+#endif /* TEST. */