Merge branch 'upstream'
[debian/pax] / tar.c
diff --git a/tar.c b/tar.c
index af7f2141f8a4614ce3d795d2dc570637e34fd17c..97639346d557bddd16a12bf7c7b9cedc5cceb397 100644 (file)
--- a/tar.c
+++ b/tar.c
@@ -1,4 +1,4 @@
-/*     $OpenBSD: tar.c,v 1.13 1998/09/26 21:29:41 millert Exp $        */
+/*     $OpenBSD: tar.c,v 1.41 2006/03/04 20:24:55 otto Exp $   */
 /*     $NetBSD: tar.c,v 1.5 1995/03/21 09:07:49 cgd Exp $      */
 
 /*-
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *     This product includes software developed by the University of
- *     California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
+ * 3. Neither the name of the University nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
  *
@@ -40,9 +36,9 @@
 
 #ifndef lint
 #if 0
-static char sccsid[] = "@(#)tar.c      8.2 (Berkeley) 4/18/94";
+static const char sccsid[] = "@(#)tar.c        8.2 (Berkeley) 4/18/94";
 #else
-static char rcsid[] = "$OpenBSD: tar.c,v 1.13 1998/09/26 21:29:41 millert Exp $";
+static const char rcsid[] = "$OpenBSD: tar.c,v 1.41 2006/03/04 20:24:55 otto Exp $";
 #endif
 #endif /* not lint */
 
@@ -63,18 +59,26 @@ static char rcsid[] = "$OpenBSD: tar.c,v 1.13 1998/09/26 21:29:41 millert Exp $"
  * Routines for reading, writing and header identify of various versions of tar
  */
 
-static u_long tar_chksm __P((register char *, register int));
-static char *name_split __P((register char *, register int));
-static int ul_oct __P((u_long, register char *, register int, int));
-#ifndef NET2_STAT
-static int uqd_oct __P((u_quad_t, register char *, register int, int));
+static size_t expandname(char *, size_t, char **, const char *, size_t);
+static u_long tar_chksm(char *, int);
+static char *name_split(char *, int);
+static int ul_oct(u_long, char *, int, int);
+#ifndef LONG_OFF_T
+static int uqd_oct(u_quad_t, char *, int, int);
 #endif
 
+static uid_t uid_nobody;
+static uid_t uid_warn;
+static gid_t gid_nobody;
+static gid_t gid_warn;
+
 /*
  * Routines common to all versions of tar
  */
 
 static int tar_nodir;                  /* do not write dirs under old tar */
+char *gnu_name_string;                 /* GNU ././@LongLink hackery name */
+char *gnu_link_string;                 /* GNU ././@LongLink hackery link */
 
 /*
  * tar_endwr()
@@ -83,13 +87,8 @@ static int tar_nodir;                        /* do not write dirs under old tar */
  *     0 if ok, -1 otherwise (what wr_skip returns)
  */
 
-#ifdef __STDC__
 int
 tar_endwr(void)
-#else
-int
-tar_endwr()
-#endif
 {
        return(wr_skip((off_t)(NULLCNT*BLKMULT)));
 }
@@ -101,13 +100,8 @@ tar_endwr()
  *     size of trailer (2 * BLKMULT)
  */
 
-#ifdef __STDC__
 off_t
 tar_endrd(void)
-#else
-off_t
-tar_endrd()
-#endif
 {
        return((off_t)(NULLCNT*BLKMULT));
 }
@@ -123,18 +117,10 @@ tar_endrd()
  *     could never contain a header.
  */
 
-#ifdef __STDC__
-int
-tar_trail(register char *buf, register int in_resync, register int *cnt)
-#else
 int
-tar_trail(buf, in_resync, cnt)
-       register char *buf;
-       register int in_resync;
-       register int *cnt;
-#endif
+tar_trail(ARCHD *ignore, char *buf, int in_resync, int *cnt)
 {
-       register int i;
+       int i;
 
        /*
         * look for all zero, trailer is two consecutive blocks of zero
@@ -174,25 +160,16 @@ tar_trail(buf, in_resync, cnt)
  *     0 if the number fit into the string, -1 otherwise
  */
 
-#ifdef __STDC__
-static int
-ul_oct(u_long val, register char *str, register int len, int term)
-#else
 static int
-ul_oct(val, str, len, term)
-       u_long val;
-       register char *str;
-       register int len;
-       int term;
-#endif
+ul_oct(u_long val, char *str, int len, int term)
 {
-       register char *pt;
+       char *pt;
 
        /*
         * term selects the appropriate character(s) for the end of the string
         */
        pt = str + len - 1;
-       switch(term) {
+       switch (term) {
        case 3:
                *pt-- = '\0';
                break;
@@ -226,7 +203,7 @@ ul_oct(val, str, len, term)
        return(0);
 }
 
-#ifndef NET2_STAT
+#ifndef LONG_OFF_T
 /*
  * uqd_oct()
  *     convert an u_quad_t to an octal string. one of many oddball field
@@ -238,25 +215,16 @@ ul_oct(val, str, len, term)
  *     0 if the number fit into the string, -1 otherwise
  */
 
-#ifdef __STDC__
-static int
-uqd_oct(u_quad_t val, register char *str, register int len, int term)
-#else
 static int
-uqd_oct(val, str, len, term)
-       u_quad_t val;
-       register char *str;
-       register int len;
-       int term;
-#endif
+uqd_oct(u_quad_t val, char *str, int len, int term)
 {
-       register char *pt;
+       char *pt;
 
        /*
         * term selects the appropriate character(s) for the end of the string
         */
        pt = str + len - 1;
-       switch(term) {
+       switch (term) {
        case 3:
                *pt-- = '\0';
                break;
@@ -294,26 +262,19 @@ uqd_oct(val, str, len, term)
 /*
  * tar_chksm()
  *     calculate the checksum for a tar block counting the checksum field as
- *     all blanks (BLNKSUM is that value pre-calculated, the sume of 8 blanks).
+ *     all blanks (BLNKSUM is that value pre-calculated, the sum of 8 blanks).
  *     NOTE: we use len to short circuit summing 0's on write since we ALWAYS
  *     pad headers with 0.
  * Return:
  *     unsigned long checksum
  */
 
-#ifdef __STDC__
 static u_long
-tar_chksm(register char *blk, register int len)
-#else
-static u_long
-tar_chksm(blk, len)
-       register char *blk;
-       register int len;
-#endif
+tar_chksm(char *blk, int len)
 {
-       register char *stop;
-       register char *pt;
-       u_long chksm = BLNKSUM; /* inital value is checksum field sum */
+       char *stop;
+       char *pt;
+       u_long chksm = BLNKSUM; /* initial value is checksum field sum */
 
        /*
         * add the part of the block before the checksum field
@@ -349,18 +310,11 @@ tar_chksm(blk, len)
  *     0 if a tar header, -1 otherwise
  */
 
-#ifdef __STDC__
 int
-tar_id(register char *blk, int size)
-#else
-int
-tar_id(blk, size)
-       register char *blk;
-       int size;
-#endif
+tar_id(char *blk, int size)
 {
-       register HD_TAR *hd;
-       register HD_USTAR *uhd;
+       HD_TAR *hd;
+       HD_USTAR *uhd;
 
        if (size < BLKMULT)
                return(-1);
@@ -380,6 +334,7 @@ tar_id(blk, size)
                return(-1);
        if (asc_ul(hd->chksum,sizeof(hd->chksum),OCT) != tar_chksm(blk,BLKMULT))
                return(-1);
+       force_one_volume = 1;
        return(0);
 }
 
@@ -390,13 +345,8 @@ tar_id(blk, size)
  *     0 if ok -1 otherwise
  */
 
-#ifdef __STDC__
 int
 tar_opt(void)
-#else
-int
-tar_opt()
-#endif
 {
        OPLIST *opt;
 
@@ -432,39 +382,40 @@ tar_opt()
  *     0
  */
 
-#ifdef __STDC__
 int
-tar_rd(register ARCHD *arcn, register char *buf)
-#else
-int
-tar_rd(arcn, buf)
-       register ARCHD *arcn;
-       register char *buf;
-#endif
+tar_rd(ARCHD *arcn, char *buf)
 {
-       register HD_TAR *hd;
-       register char *pt;
+       HD_TAR *hd;
+       char *pt;
 
        /*
         * we only get proper sized buffers passed to us
         */
        if (tar_id(buf, BLKMULT) < 0)
                return(-1);
+       memset(arcn, 0, sizeof(*arcn));
        arcn->org_name = arcn->name;
        arcn->sb.st_nlink = 1;
-       arcn->pat = NULL;
 
        /*
         * copy out the name and values in the stat buffer
         */
        hd = (HD_TAR *)buf;
-       arcn->nlen = l_strncpy(arcn->name, hd->name, sizeof(arcn->name) - 1);
-       arcn->name[arcn->nlen] = '\0';
+       if (hd->linkflag != LONGLINKTYPE && hd->linkflag != LONGNAMETYPE) {
+               arcn->nlen = expandname(arcn->name, sizeof(arcn->name),
+                   &gnu_name_string, hd->name, sizeof(hd->name));
+               arcn->ln_nlen = expandname(arcn->ln_name, sizeof(arcn->ln_name),
+                   &gnu_link_string, hd->linkname, sizeof(hd->linkname));
+       }
        arcn->sb.st_mode = (mode_t)(asc_ul(hd->mode,sizeof(hd->mode),OCT) &
            0xfff);
        arcn->sb.st_uid = (uid_t)asc_ul(hd->uid, sizeof(hd->uid), OCT);
        arcn->sb.st_gid = (gid_t)asc_ul(hd->gid, sizeof(hd->gid), OCT);
-       arcn->sb.st_size = (size_t)asc_ul(hd->size, sizeof(hd->size), OCT);
+#ifdef LONG_OFF_T
+       arcn->sb.st_size = (off_t)asc_ul(hd->size, sizeof(hd->size), OCT);
+#else
+       arcn->sb.st_size = (off_t)asc_uqd(hd->size, sizeof(hd->size), OCT);
+#endif
        arcn->sb.st_mtime = (time_t)asc_ul(hd->mtime, sizeof(hd->mtime), OCT);
        arcn->sb.st_ctime = arcn->sb.st_atime = arcn->sb.st_mtime;
 
@@ -475,16 +426,13 @@ tar_rd(arcn, buf)
        pt = &(arcn->name[arcn->nlen - 1]);
        arcn->pad = 0;
        arcn->skip = 0;
-       switch(hd->linkflag) {
+       switch (hd->linkflag) {
        case SYMTYPE:
                /*
                 * symbolic link, need to get the link name and set the type in
                 * the st_mode so -v printing will look correct.
                 */
                arcn->type = PAX_SLK;
-               arcn->ln_nlen = l_strncpy(arcn->ln_name, hd->linkname,
-                       sizeof(arcn->ln_name) - 1);
-               arcn->ln_name[arcn->ln_nlen] = '\0';
                arcn->sb.st_mode |= S_IFLNK;
                break;
        case LNKTYPE:
@@ -494,9 +442,6 @@ tar_rd(arcn, buf)
                 */
                arcn->type = PAX_HLK;
                arcn->sb.st_nlink = 2;
-               arcn->ln_nlen = l_strncpy(arcn->ln_name, hd->linkname,
-                       sizeof(arcn->ln_name) - 1);
-               arcn->ln_name[arcn->ln_nlen] = '\0';
 
                /*
                 * no idea of what type this thing really points at, but
@@ -504,6 +449,17 @@ tar_rd(arcn, buf)
                 */
                arcn->sb.st_mode |= S_IFREG;
                break;
+       case LONGLINKTYPE:
+       case LONGNAMETYPE:
+               /*
+                * GNU long link/file; we tag these here and let the
+                * pax internals deal with it -- too ugly otherwise.
+                */
+               arcn->type =
+                   hd->linkflag == LONGLINKTYPE ? PAX_GLL : PAX_GLF;
+               arcn->pad = TAR_PAD(arcn->sb.st_size);
+               arcn->skip = arcn->sb.st_size;
+               break;
        case DIRTYPE:
                /*
                 * It is a directory, set the mode for -v printing
@@ -511,8 +467,6 @@ tar_rd(arcn, buf)
                arcn->type = PAX_DIR;
                arcn->sb.st_mode |= S_IFDIR;
                arcn->sb.st_nlink = 2;
-               arcn->ln_name[0] = '\0';
-               arcn->ln_nlen = 0;
                break;
        case AREGTYPE:
        case REGTYPE:
@@ -532,7 +486,7 @@ tar_rd(arcn, buf)
                } else {
                        /*
                         * have a file that will be followed by data. Set the
-                        * skip value to the size field and caluculate the size
+                        * skip value to the size field and calculate the size
                         * of the padding.
                         */
                        arcn->type = PAX_REG;
@@ -547,7 +501,7 @@ tar_rd(arcn, buf)
         * strip off any trailing slash.
         */
        if (*pt == '/') {
-               *pt = '\0'; 
+               *pt = '\0';
                --arcn->nlen;
        }
        return(0);
@@ -565,23 +519,17 @@ tar_rd(arcn, buf)
  *     data to write after the header, -1 if archive write failed
  */
 
-#ifdef __STDC__
 int
-tar_wr(register ARCHD *arcn)
-#else
-int
-tar_wr(arcn)
-       register ARCHD *arcn;
-#endif
+tar_wr(ARCHD *arcn)
 {
-       register HD_TAR *hd;
+       HD_TAR *hd;
        int len;
        char hdblk[sizeof(HD_TAR)];
 
        /*
         * check for those file system types which tar cannot store
         */
-       switch(arcn->type) {
+       switch (arcn->type) {
        case PAX_DIR:
                /*
                 * user asked that dirs not be written to the archive
@@ -606,7 +554,8 @@ tar_wr(arcn)
        case PAX_HLK:
        case PAX_HRG:
                if (arcn->ln_nlen > sizeof(hd->linkname)) {
-                       paxwarn(1,"Link name too long for tar %s", arcn->ln_name);
+                       paxwarn(1, "Link name too long for tar %s",
+                           arcn->ln_name);
                        return(1);
                }
                break;
@@ -622,32 +571,31 @@ tar_wr(arcn)
        len = arcn->nlen;
        if (arcn->type == PAX_DIR)
                ++len;
-       if (len >= sizeof(hd->name)) {
+       if (len > sizeof(hd->name)) {
                paxwarn(1, "File name too long for tar %s", arcn->name);
                return(1);
        }
 
        /*
-        * copy the data out of the ARCHD into the tar header based on the type
-        * of the file. Remember many tar readers want the unused fields to be
-        * padded with zero. We set the linkflag field (type), the linkname
-        * (or zero if not used),the size, and set the padding (if any) to be
-        * added after the file data (0 for all other types, as they only have
-        * a header)
+        * Copy the data out of the ARCHD into the tar header based on the type
+        * of the file. Remember, many tar readers want all fields to be
+        * padded with zero so we zero the header first.  We then set the
+        * linkflag field (type), the linkname, the size, and set the padding
+        * (if any) to be added after the file data (0 for all other types,
+        * as they only have a header).
         */
+       memset(hdblk, 0, sizeof(hdblk));
        hd = (HD_TAR *)hdblk;
-       l_strncpy(hd->name, arcn->name, sizeof(hd->name) - 1);
-       hd->name[sizeof(hd->name) - 1] = '\0';
+       fieldcpy(hd->name, sizeof(hd->name), arcn->name, sizeof(arcn->name));
        arcn->pad = 0;
 
        if (arcn->type == PAX_DIR) {
                /*
                 * directories are the same as files, except have a filename
-                * that ends with a /, we add the slash here. No data follows,
+                * that ends with a /, we add the slash here. No data follows
                 * dirs, so no pad.
                 */
                hd->linkflag = AREGTYPE;
-               memset(hd->linkname, 0, sizeof(hd->linkname));
                hd->name[len-1] = '/';
                if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), 1))
                        goto out;
@@ -656,8 +604,8 @@ tar_wr(arcn)
                 * no data follows this file, so no pad
                 */
                hd->linkflag = SYMTYPE;
-               l_strncpy(hd->linkname,arcn->ln_name, sizeof(hd->linkname) - 1);
-               hd->linkname[sizeof(hd->linkname) - 1] = '\0';
+               fieldcpy(hd->linkname, sizeof(hd->linkname), arcn->ln_name,
+                   sizeof(arcn->ln_name));
                if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), 1))
                        goto out;
        } else if ((arcn->type == PAX_HLK) || (arcn->type == PAX_HRG)) {
@@ -665,8 +613,8 @@ tar_wr(arcn)
                 * no data follows this file, so no pad
                 */
                hd->linkflag = LNKTYPE;
-               l_strncpy(hd->linkname,arcn->ln_name, sizeof(hd->linkname) - 1);
-               hd->linkname[sizeof(hd->linkname) - 1] = '\0';
+               fieldcpy(hd->linkname, sizeof(hd->linkname), arcn->ln_name,
+                   sizeof(arcn->ln_name));
                if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), 1))
                        goto out;
        } else {
@@ -674,8 +622,7 @@ tar_wr(arcn)
                 * data follows this file, so set the pad
                 */
                hd->linkflag = AREGTYPE;
-               memset(hd->linkname, 0, sizeof(hd->linkname));
-#              ifdef NET2_STAT
+#              ifdef LONG_OFF_T
                if (ul_oct((u_long)arcn->sb.st_size, hd->size,
                    sizeof(hd->size), 1)) {
 #              else
@@ -694,7 +641,7 @@ tar_wr(arcn)
        if (ul_oct((u_long)arcn->sb.st_mode, hd->mode, sizeof(hd->mode), 0) ||
            ul_oct((u_long)arcn->sb.st_uid, hd->uid, sizeof(hd->uid), 0) ||
            ul_oct((u_long)arcn->sb.st_gid, hd->gid, sizeof(hd->gid), 0) ||
-           ul_oct((u_long)arcn->sb.st_mtime, hd->mtime, sizeof(hd->mtime), 1))
+           ul_oct((u_long)(u_int)arcn->sb.st_mtime, hd->mtime, sizeof(hd->mtime), 1))
                goto out;
 
        /*
@@ -732,13 +679,8 @@ tar_wr(arcn)
  *     0 if ok, -1 otherwise
  */
 
-#ifdef __STDC__
 int
 ustar_strd(void)
-#else
-int
-ustar_strd()
-#endif
 {
        if ((usrtb_start() < 0) || (grptb_start() < 0))
                return(-1);
@@ -752,13 +694,8 @@ ustar_strd()
  *     0 if ok, -1 otherwise
  */
 
-#ifdef __STDC__
 int
 ustar_stwr(void)
-#else
-int
-ustar_stwr()
-#endif
 {
        if ((uidtb_start() < 0) || (gidtb_start() < 0))
                return(-1);
@@ -773,17 +710,10 @@ ustar_stwr()
  *     0 if a ustar header, -1 otherwise
  */
 
-#ifdef __STDC__
 int
 ustar_id(char *blk, int size)
-#else
-int
-ustar_id(blk, size)
-       char *blk;
-       int size;
-#endif
 {
-       register HD_USTAR *hd;
+       HD_USTAR *hd;
 
        if (size < BLKMULT)
                return(-1);
@@ -795,7 +725,7 @@ ustar_id(blk, size)
         * programs are fouled up and create archives missing the \0. Last we
         * check the checksum. If ok we have to assume it is a valid header.
         */
-       if (hd->name[0] == '\0')
+       if (hd->prefix[0] == '\0' && hd->name[0] == '\0')
                return(-1);
        if (strncmp(hd->magic, TMAGIC, TMAGLEN - 1) != 0)
                return(-1);
@@ -812,19 +742,12 @@ ustar_id(blk, size)
  *     0
  */
 
-#ifdef __STDC__
 int
-ustar_rd(register ARCHD *arcn, register char *buf)
-#else
-int
-ustar_rd(arcn, buf)
-       register ARCHD *arcn;
-       register char *buf;
-#endif
+ustar_rd(ARCHD *arcn, char *buf)
 {
-       register HD_USTAR *hd;
-       register char *dest;
-       register int cnt = 0;
+       HD_USTAR *hd;
+       char *dest;
+       int cnt = 0;
        dev_t devmajor;
        dev_t devminor;
 
@@ -833,10 +756,9 @@ ustar_rd(arcn, buf)
         */
        if (ustar_id(buf, BLKMULT) < 0)
                return(-1);
+       memset(arcn, 0, sizeof(*arcn));
        arcn->org_name = arcn->name;
        arcn->sb.st_nlink = 1;
-       arcn->pat = NULL;
-       arcn->nlen = 0;
        hd = (HD_USTAR *)buf;
 
        /*
@@ -851,13 +773,21 @@ ustar_rd(arcn, buf)
         */
        dest = arcn->name;
        if (*(hd->prefix) != '\0') {
-               cnt = l_strncpy(dest, hd->prefix, sizeof(hd->prefix));
+               cnt = fieldcpy(dest, sizeof(arcn->name) - 1, hd->prefix,
+                   sizeof(hd->prefix));
                dest += cnt;
                *dest++ = '/';
                cnt++;
+       } else {
+               cnt = 0;
+       }
+
+       if (hd->typeflag != LONGLINKTYPE && hd->typeflag != LONGNAMETYPE) {
+               arcn->nlen = cnt + expandname(dest, sizeof(arcn->name) - cnt,
+                   &gnu_name_string, hd->name, sizeof(hd->name));
+               arcn->ln_nlen = expandname(arcn->ln_name, sizeof(arcn->ln_name),
+                   &gnu_link_string, hd->linkname, sizeof(hd->linkname));
        }
-       arcn->nlen = cnt + l_strncpy(dest, hd->name, sizeof(hd->name));
-       arcn->name[arcn->nlen] = '\0';
 
        /*
         * follow the spec to the letter. we should only have mode bits, strip
@@ -865,7 +795,11 @@ ustar_rd(arcn, buf)
         */
        arcn->sb.st_mode = (mode_t)(asc_ul(hd->mode, sizeof(hd->mode), OCT) &
            0xfff);
-       arcn->sb.st_size = (size_t)asc_ul(hd->size, sizeof(hd->size), OCT);
+#ifdef LONG_OFF_T
+       arcn->sb.st_size = (off_t)asc_ul(hd->size, sizeof(hd->size), OCT);
+#else
+       arcn->sb.st_size = (off_t)asc_uqd(hd->size, sizeof(hd->size), OCT);
+#endif
        arcn->sb.st_mtime = (time_t)asc_ul(hd->mtime, sizeof(hd->mtime), OCT);
        arcn->sb.st_ctime = arcn->sb.st_atime = arcn->sb.st_mtime;
 
@@ -885,8 +819,6 @@ ustar_rd(arcn, buf)
        /*
         * set the defaults, these may be changed depending on the file type
         */
-       arcn->ln_name[0] = '\0';
-       arcn->ln_nlen = 0;
        arcn->pad = 0;
        arcn->skip = 0;
        arcn->sb.st_rdev = (dev_t)0;
@@ -894,7 +826,7 @@ ustar_rd(arcn, buf)
        /*
         * set the mode and PAX type according to the typeflag in the header
         */
-       switch(hd->typeflag) {
+       switch (hd->typeflag) {
        case FIFOTYPE:
                arcn->type = PAX_FIF;
                arcn->sb.st_mode |= S_IFIFO;
@@ -941,15 +873,17 @@ ustar_rd(arcn, buf)
                        arcn->sb.st_mode |= S_IFREG;
                        arcn->sb.st_nlink = 2;
                }
+               break;
+       case LONGLINKTYPE:
+       case LONGNAMETYPE:
                /*
-                * copy the link name
-                *
-                * see comment above (for hd->name) regarding the length
-                * passed to l_strncpy
+                * GNU long link/file; we tag these here and let the
+                * pax internals deal with it -- too ugly otherwise.
                 */
-               arcn->ln_nlen = l_strncpy(arcn->ln_name, hd->linkname,
-                       sizeof(hd->linkname));
-               arcn->ln_name[arcn->ln_nlen] = '\0';
+               arcn->type =
+                   hd->typeflag == LONGLINKTYPE ? PAX_GLL : PAX_GLF;
+               arcn->pad = TAR_PAD(arcn->sb.st_size);
+               arcn->skip = arcn->sb.st_size;
                break;
        case CONTTYPE:
        case AREGTYPE:
@@ -980,17 +914,11 @@ ustar_rd(arcn, buf)
  *     data to write after the header, -1 if archive write failed
  */
 
-#ifdef __STDC__
 int
-ustar_wr(register ARCHD *arcn)
-#else
-int
-ustar_wr(arcn)
-       register ARCHD *arcn;
-#endif
+ustar_wr(ARCHD *arcn)
 {
-       register HD_USTAR *hd;
-       register char *pt;
+       HD_USTAR *hd;
+       char *pt;
        char hdblk[sizeof(HD_USTAR)];
 
        /*
@@ -1018,6 +946,11 @@ ustar_wr(arcn)
                paxwarn(1, "File name too long for ustar %s", arcn->name);
                return(1);
        }
+
+       /*
+        * zero out the header so we don't have to worry about zero fill below
+        */
+       memset(hdblk, 0, sizeof(hdblk));
        hd = (HD_USTAR *)hdblk;
        arcn->pad = 0L;
 
@@ -1030,28 +963,24 @@ ustar_wr(arcn)
                 * occur, we remove the / and copy the first part to the prefix
                 */
                *pt = '\0';
-               l_strncpy(hd->prefix, arcn->name, sizeof(hd->prefix));
+               fieldcpy(hd->prefix, sizeof(hd->prefix), arcn->name,
+                   sizeof(arcn->name));
                *pt++ = '/';
-       } else
-               memset(hd->prefix, 0, sizeof(hd->prefix));
+       }
 
        /*
         * copy the name part. this may be the whole path or the part after
         * the prefix
         */
-       l_strncpy(hd->name, pt, sizeof(hd->name));
+       fieldcpy(hd->name, sizeof(hd->name), pt,
+           sizeof(arcn->name) - (pt - arcn->name));
 
        /*
         * set the fields in the header that are type dependent
         */
-       switch(arcn->type) {
+       switch (arcn->type) {
        case PAX_DIR:
                hd->typeflag = DIRTYPE;
-               memset(hd->linkname, 0, sizeof(hd->linkname));
-               memset(hd->devmajor, 0, sizeof(hd->devmajor));
-               hd->devmajor[0] = '0';
-               memset(hd->devminor, 0, sizeof(hd->devminor));
-               hd->devminor[0] = '0';
                if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), 3))
                        goto out;
                break;
@@ -1061,7 +990,6 @@ ustar_wr(arcn)
                        hd->typeflag = CHRTYPE;
                else
                        hd->typeflag = BLKTYPE;
-               memset(hd->linkname, 0, sizeof(hd->linkname));
                if (ul_oct((u_long)MAJOR(arcn->sb.st_rdev), hd->devmajor,
                   sizeof(hd->devmajor), 3) ||
                   ul_oct((u_long)MINOR(arcn->sb.st_rdev), hd->devminor,
@@ -1071,11 +999,6 @@ ustar_wr(arcn)
                break;
        case PAX_FIF:
                hd->typeflag = FIFOTYPE;
-               memset(hd->linkname, 0, sizeof(hd->linkname));
-               memset(hd->devmajor, 0, sizeof(hd->devmajor));
-               hd->devmajor[0] = '0';
-               memset(hd->devminor, 0, sizeof(hd->devminor));
-               hd->devminor[0] = '0';
                if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), 3))
                        goto out;
                break;
@@ -1086,11 +1009,8 @@ ustar_wr(arcn)
                        hd->typeflag = SYMTYPE;
                else
                        hd->typeflag = LNKTYPE;
-               l_strncpy(hd->linkname,arcn->ln_name, sizeof(hd->linkname));
-               memset(hd->devmajor, 0, sizeof(hd->devmajor));
-               hd->devmajor[0] = '0';
-               memset(hd->devminor, 0, sizeof(hd->devminor));
-               hd->devminor[0] = '0';
+               fieldcpy(hd->linkname, sizeof(hd->linkname), arcn->ln_name,
+                   sizeof(arcn->ln_name));
                if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), 3))
                        goto out;
                break;
@@ -1104,13 +1024,8 @@ ustar_wr(arcn)
                        hd->typeflag = CONTTYPE;
                else
                        hd->typeflag = REGTYPE;
-               memset(hd->linkname, 0, sizeof(hd->linkname));
-               memset(hd->devmajor, 0, sizeof(hd->devmajor));
-               hd->devmajor[0] = '0';
-               memset(hd->devminor, 0, sizeof(hd->devminor));
-               hd->devminor[0] = '0';
                arcn->pad = TAR_PAD(arcn->sb.st_size);
-#              ifdef NET2_STAT
+#              ifdef LONG_OFF_T
                if (ul_oct((u_long)arcn->sb.st_size, hd->size,
                    sizeof(hd->size), 3)) {
 #              else
@@ -1123,20 +1038,46 @@ ustar_wr(arcn)
                break;
        }
 
-       l_strncpy(hd->magic, TMAGIC, TMAGLEN);
-       l_strncpy(hd->version, TVERSION, TVERSLEN);
+       strncpy(hd->magic, TMAGIC, TMAGLEN);
+       strncpy(hd->version, TVERSION, TVERSLEN);
 
        /*
         * set the remaining fields. Some versions want all 16 bits of mode
         * we better humor them (they really do not meet spec though)....
         */
+       if (ul_oct((u_long)arcn->sb.st_uid, hd->uid, sizeof(hd->uid), 3)) {
+               if (uid_nobody == 0) {
+                       if (uid_name("nobody", &uid_nobody) == -1)
+                               goto out;
+               }
+               if (uid_warn != arcn->sb.st_uid) {
+                       uid_warn = arcn->sb.st_uid;
+                       paxwarn(1,
+                           "Ustar header field is too small for uid %lu, "
+                           "using nobody", (u_long)arcn->sb.st_uid);
+               }
+               if (ul_oct((u_long)uid_nobody, hd->uid, sizeof(hd->uid), 3))
+                       goto out;
+       }
+       if (ul_oct((u_long)arcn->sb.st_gid, hd->gid, sizeof(hd->gid), 3)) {
+               if (gid_nobody == 0) {
+                       if (gid_name("nobody", &gid_nobody) == -1)
+                               goto out;
+               }
+               if (gid_warn != arcn->sb.st_gid) {
+                       gid_warn = arcn->sb.st_gid;
+                       paxwarn(1,
+                           "Ustar header field is too small for gid %lu, "
+                           "using nobody", (u_long)arcn->sb.st_gid);
+               }
+               if (ul_oct((u_long)gid_nobody, hd->gid, sizeof(hd->gid), 3))
+                       goto out;
+       }
        if (ul_oct((u_long)arcn->sb.st_mode, hd->mode, sizeof(hd->mode), 3) ||
-           ul_oct((u_long)arcn->sb.st_uid, hd->uid, sizeof(hd->uid), 3)  ||
-           ul_oct((u_long)arcn->sb.st_gid, hd->gid, sizeof(hd->gid), 3) ||
-           ul_oct((u_long)arcn->sb.st_mtime,hd->mtime,sizeof(hd->mtime),3))
+           ul_oct((u_long)(u_int)arcn->sb.st_mtime,hd->mtime,sizeof(hd->mtime),3))
                goto out;
-       l_strncpy(hd->uname,name_uid(arcn->sb.st_uid, 0),sizeof(hd->uname));
-       l_strncpy(hd->gname,name_gid(arcn->sb.st_gid, 0),sizeof(hd->gname));
+       strncpy(hd->uname, name_uid(arcn->sb.st_uid, 0), sizeof(hd->uname));
+       strncpy(hd->gname, name_gid(arcn->sb.st_gid, 0), sizeof(hd->gname));
 
        /*
         * calculate and store the checksum write the header to the archive
@@ -1155,7 +1096,7 @@ ustar_wr(arcn)
        return(1);
 
     out:
-       /*
+       /*
         * header field is out of range
         */
        paxwarn(1, "Ustar header field is too small for %s", arcn->org_name);
@@ -1174,21 +1115,16 @@ ustar_wr(arcn)
  *     the file name is too long
  */
 
-#ifdef __STDC__
-static char *
-name_split(register char *name, register int len)
-#else
 static char *
-name_split(name, len)
-       register char *name;
-       register int len;
-#endif
+name_split(char *name, int len)
 {
-       register char *start;
+       char *start;
 
        /*
         * check to see if the file name is small enough to fit in the name
         * field. if so just return a pointer to the name.
+        * The strings can fill the complete name and prefix fields
+        * without a NUL terminator.
         */
        if (len <= TNMSZ)
                return(name);
@@ -1197,7 +1133,7 @@ name_split(name, len)
 
        /*
         * we start looking at the biggest sized piece that fits in the name
-        * field. We walk foward looking for a slash to split at. The idea is
+        * field. We walk forward looking for a slash to split at. The idea is
         * to find the biggest piece to fit in the name field (or the smallest
         * prefix we can find) (the -1 is correct the biggest piece would
         * include the slash between the two parts that gets thrown away)
@@ -1228,3 +1164,20 @@ name_split(name, len)
         */
        return(start);
 }
+
+static size_t
+expandname(char *buf, size_t len, char **gnu_name, const char *name,
+    size_t limit)
+{
+       size_t nlen;
+
+       if (*gnu_name) {
+               /* *gnu_name is NUL terminated */
+               if ((nlen = strlcpy(buf, *gnu_name, len)) >= len)
+                       nlen = len - 1;
+               free(*gnu_name);
+               *gnu_name = NULL;
+       } else
+               nlen = fieldcpy(buf, len, name, limit);
+       return(nlen);
+}