[BACK]Return to arch.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / make

Diff for /src/usr.bin/make/arch.c between version 1.63 and 1.64

version 1.63, 2007/09/16 10:14:26 version 1.64, 2007/09/16 11:27:05
Line 113 
Line 113 
 #define MACHINE_ARCH TARGET_MACHINE_ARCH  #define MACHINE_ARCH TARGET_MACHINE_ARCH
 #endif  #endif
   
 static struct ohash       archives;   /* Archives we've already examined.  */  static struct ohash archives;   /* Archives we've already examined.  */
   
 typedef struct Arch_ {  typedef struct Arch_ {
     struct ohash   members;    /* All the members of this archive, as          struct ohash members;   /* All the members of this archive, as
                                * struct arch_member entries.  */                                   * struct arch_member entries.  */
     char          name[1];    /* Archive name.  */          char name[1];           /* Archive name. */
 } Arch;  } Arch;
   
 /* Used to get to ar's field sizes.  */  /* Used to get to ar's field sizes.  */
Line 129 
Line 129 
 /* Each archive member is tied to an arch_member structure,  /* Each archive member is tied to an arch_member structure,
  * suitable for hashing.  */   * suitable for hashing.  */
 struct arch_member {  struct arch_member {
     TIMESTAMP     mtime;        /* Member modification date.  */          TIMESTAMP mtime;        /* Member modification date.  */
     char          date[AR_DATE_SIZE+1];          char date[AR_DATE_SIZE+1];
                                 /* Same, before conversion to numeric value.  */                                  /* Same, before conversion to numeric value.  */
     char          name[1];      /* Member name.  */          char name[1];           /* Member name.  */
 };  };
   
 static struct ohash_info members_info = {  static struct ohash_info members_info = {
     offsetof(struct arch_member, name), NULL,          offsetof(struct arch_member, name), NULL,
     hash_alloc, hash_free, element_alloc          hash_alloc, hash_free, element_alloc
 };  };
   
 static struct ohash_info arch_info = {  static struct ohash_info arch_info = {
     offsetof(Arch, name), NULL, hash_alloc, hash_free, element_alloc          offsetof(Arch, name), NULL, hash_alloc, hash_free, element_alloc
 };  };
   
   
Line 164 
Line 164 
   
 #ifdef SVR4ARCHIVES  #ifdef SVR4ARCHIVES
 struct SVR4namelist {  struct SVR4namelist {
     char          *fnametab;  /* Extended name table strings */          char *fnametab;         /* Extended name table strings */
     size_t        fnamesize;  /* Size of the string table */          size_t fnamesize;       /* Size of the string table */
 };  };
   
 static const char *svr4list = "Archive list";  static const char *svr4list = "Archive list";
Line 176 
Line 176 
 static struct arch_member *  static struct arch_member *
 new_arch_member(struct ar_hdr *hdr, const char *name)  new_arch_member(struct ar_hdr *hdr, const char *name)
 {  {
     const char *end = NULL;          const char *end = NULL;
     struct arch_member *n;          struct arch_member *n;
   
     n = ohash_create_entry(&members_info, name, &end);          n = ohash_create_entry(&members_info, name, &end);
     /* XXX ar entries are NOT null terminated.  */          /* XXX ar entries are NOT null terminated.      */
     memcpy(n->date, &(hdr->ar_date), AR_DATE_SIZE);          memcpy(n->date, &(hdr->ar_date), AR_DATE_SIZE);
     n->date[AR_DATE_SIZE] = '\0';          n->date[AR_DATE_SIZE] = '\0';
     /* Don't compute mtime before it is needed. */          /* Don't compute mtime before it is needed. */
     ts_set_out_of_date(n->mtime);          ts_set_out_of_date(n->mtime);
     return n;          return n;
 }  }
   
 static TIMESTAMP  static TIMESTAMP
 mtime_of_member(struct arch_member *m)  mtime_of_member(struct arch_member *m)
 {  {
     if (is_out_of_date(m->mtime))          if (is_out_of_date(m->mtime))
         ts_set_from_time_t((time_t) strtol(m->date, NULL, 10), m->mtime);                  ts_set_from_time_t((time_t) strtol(m->date, NULL, 10),
     return m->mtime;                      m->mtime);
           return m->mtime;
 }  }
   
 #ifdef CLEANUP  #ifdef CLEANUP
Line 206 
Line 207 
 static void  static void
 ArchFree(Arch *a)  ArchFree(Arch *a)
 {  {
     struct arch_member *mem;          struct arch_member *mem;
     unsigned int i;          unsigned int i;
   
     /* Free memory from hash entries */          /* Free memory from hash entries */
     for (mem = ohash_first(&a->members, &i); mem != NULL;          for (mem = ohash_first(&a->members, &i); mem != NULL;
         mem = ohash_next(&a->members, &i))                  mem = ohash_next(&a->members, &i))
         free(mem);                  free(mem);
   
     ohash_delete(&a->members);          ohash_delete(&a->members);
     free(a);          free(a);
 }  }
 #endif  #endif
   
Line 227 
Line 228 
     Lst nodeLst,                    /* Lst on which to place the nodes */      Lst nodeLst,                    /* Lst on which to place the nodes */
     SymTable *ctxt)                 /* Context in which to expand variables */      SymTable *ctxt)                 /* Context in which to expand variables */
 {  {
     char            *cp;            /* Pointer into line */          char *cp;               /* Pointer into line */
     GNode           *gn;            /* New node */          GNode *gn;              /* New node */
     char            *libName;       /* Library-part of specification */          char *libName;          /* Library-part of specification */
     char            *memberName;    /* Member-part of specification */          char *memberName;       /* Member-part of specification */
     char            nameBuf[MAKE_BSIZE]; /* temporary place for node name */          char nameBuf[MAKE_BSIZE]; /* temporary place for node name */
     char            saveChar;       /* Ending delimiter of member-name */          char saveChar;          /* Ending delimiter of member-name */
     bool            subLibName;     /* true if libName should have/had          bool subLibName;        /* true if libName should have/had
                                      * variable substitution performed on it */                                   * variable substitution performed on it */
   
     libName = *linePtr;          libName = *linePtr;
   
     subLibName = false;          subLibName = false;
   
     for (cp = libName; *cp != '(' && *cp != '\0';) {          for (cp = libName; *cp != '(' && *cp != '\0';) {
         if (*cp == '$') {                  if (*cp == '$') {
             if (!Var_ParseSkip(&cp, ctxt))                          if (!Var_ParseSkip(&cp, ctxt))
                 return false;                                  return false;
             subLibName = true;                          subLibName = true;
         } else                  } else
             cp++;                          cp++;
     }          }
   
     *cp++ = '\0';          *cp++ = '\0';
     if (subLibName)          if (subLibName)
         libName = Var_Subst(libName, ctxt, true);                  libName = Var_Subst(libName, ctxt, true);
   
     for (;;) {          for (;;) {
         /* First skip to the start of the member's name, mark that                  /* First skip to the start of the member's name, mark that
          * place and skip to the end of it (either white-space or                   * place and skip to the end of it (either white-space or
          * a close paren).  */                   * a close paren).  */
         bool doSubst = false; /* true if need to substitute in memberName */                  bool doSubst = false;   /* true if need to substitute in
                                            * memberName */
   
         while (isspace(*cp))                  while (isspace(*cp))
             cp++;                          cp++;
         memberName = cp;                  memberName = cp;
         while (*cp != '\0' && *cp != ')' && !isspace(*cp)) {                  while (*cp != '\0' && *cp != ')' && !isspace(*cp)) {
             if (*cp == '$') {                          if (*cp == '$') {
                 if (!Var_ParseSkip(&cp, ctxt))                                  if (!Var_ParseSkip(&cp, ctxt))
                     return false;                                          return false;
                 doSubst = true;                                  doSubst = true;
             } else                          } else
                 cp++;                                  cp++;
         }                  }
   
         /* If the specification ends without a closing parenthesis,                  /* If the specification ends without a closing parenthesis,
          * chances are there's something wrong (like a missing backslash),                   * chances are there's something wrong (like a missing
          * so it's better to return failure than allow such things to                   * backslash), so it's better to return failure than allow such
          * happen.  */                   * things to happen.  */
         if (*cp == '\0') {                  if (*cp == '\0') {
             printf("No closing parenthesis in archive specification\n");                          printf("No closing parenthesis in archive specification\n");
             return false;                          return false;
         }                  }
   
         /* If we didn't move anywhere, we must be done.  */                  /* If we didn't move anywhere, we must be done.  */
         if (cp == memberName)                  if (cp == memberName)
             break;                          break;
   
         saveChar = *cp;                  saveChar = *cp;
         *cp = '\0';                  *cp = '\0';
   
         /* XXX: This should be taken care of intelligently by                  /* XXX: This should be taken care of intelligently by
          * SuffExpandChildren, both for the archive and the member portions.  */                   * SuffExpandChildren, both for the archive and the member
                    * portions.  */
   
         /* If member contains variables, try and substitute for them.                  /* If member contains variables, try and substitute for them.
          * This will slow down archive specs with dynamic sources, of course,                   * This will slow down archive specs with dynamic sources, of
          * since we'll be (non-)substituting them three times, but them's                   * course, since we'll be (non-)substituting them three times,
          * the breaks -- we need to do this since SuffExpandChildren calls                   * but them's the breaks -- we need to do this since
          * us, otherwise we could assume the thing would be taken care of                   * SuffExpandChildren calls us, otherwise we could assume the
          * later.  */                   * thing would be taken care of later.  */
         if (doSubst) {                  if (doSubst) {
             char    *buf;                          char *buf;
             char    *sacrifice;                          char *sacrifice;
             char    *oldMemberName = memberName;                          char *oldMemberName = memberName;
             size_t  length;                          size_t length;
   
             memberName = Var_Subst(memberName, ctxt, true);                          memberName = Var_Subst(memberName, ctxt, true);
   
             /* Now form an archive spec and recurse to deal with nested                          /* Now form an archive spec and recurse to deal with
              * variables and multi-word variable values.... The results                           * nested variables and multi-word variable values....
              * are just placed at the end of the nodeLst we're returning.  */                           * The results are just placed at the end of the
             length = strlen(memberName)+strlen(libName)+3;                           * nodeLst we're returning.  */
             buf = sacrifice = emalloc(length);                          length = strlen(memberName)+strlen(libName)+3;
                           buf = sacrifice = emalloc(length);
   
             snprintf(buf, length, "%s(%s)", libName, memberName);                          snprintf(buf, length, "%s(%s)", libName, memberName);
   
             if (strchr(memberName, '$') &&                          if (strchr(memberName, '$') &&
                 strcmp(memberName, oldMemberName) == 0) {                                  strcmp(memberName, oldMemberName) == 0) {
                 /* Must contain dynamic sources, so we can't deal with it now.                                  /* Must contain dynamic sources, so we can't
                  * Just create an ARCHV node for the thing and let                                   * deal with it now.  Just create an ARCHV node
                  * SuffExpandChildren handle it...  */                                   * for the thing and let SuffExpandChildren
                 gn = Targ_FindNode(buf, TARG_CREATE);                                   * handle it...  */
                                   gn = Targ_FindNode(buf, TARG_CREATE);
   
                 if (gn == NULL) {                                  if (gn == NULL) {
                     free(buf);                                          free(buf);
                     return false;                                          return false;
                                   } else {
                                           gn->type |= OP_ARCHV;
                                           Lst_AtEnd(nodeLst, gn);
                                   }
                           } else if (!Arch_ParseArchive(&sacrifice, nodeLst,
                               ctxt)) {
                                   /* Error in nested call -- free buffer and
                                    * return false ourselves.  */
                                   free(buf);
                                   return false;
                           }
                           /* Free buffer and continue with our work.      */
                           free(buf);
                   } else if (Dir_HasWildcards(memberName)) {
                           LIST members;
                           char *member;
   
                           Lst_Init(&members);
   
                           Dir_Expand(memberName, dirSearchPath, &members);
                           while ((member = (char *)Lst_DeQueue(&members))
                               != NULL) {
                                   snprintf(nameBuf, MAKE_BSIZE, "%s(%s)",
                                       libName, member);
                                   free(member);
                                   gn = Targ_FindNode(nameBuf, TARG_CREATE);
                                   /* We've found the node, but have to make sure
                                    * the rest of the world knows it's an archive
                                    * member, without having to constantly check
                                    * for parentheses, so we type the thing with
                                    * the OP_ARCHV bit before we place it on the
                                    * end of the provided list.  */
                                   gn->type |= OP_ARCHV;
                                   Lst_AtEnd(nodeLst, gn);
                           }
                 } else {                  } else {
                     gn->type |= OP_ARCHV;                          snprintf(nameBuf, MAKE_BSIZE, "%s(%s)", libName,
                     Lst_AtEnd(nodeLst, gn);                              memberName);
                           gn = Targ_FindNode(nameBuf, TARG_CREATE);
                           /* We've found the node, but have to make sure the rest
                            * of the world knows it's an archive member, without
                            * having to constantly check for parentheses, so we
                            * type the thing with the OP_ARCHV bit before we place
                            * it on the end of the provided list.  */
                           gn->type |= OP_ARCHV;
                           Lst_AtEnd(nodeLst, gn);
                 }                  }
             } else if (!Arch_ParseArchive(&sacrifice, nodeLst, ctxt)) {                  if (doSubst)
                 /* Error in nested call -- free buffer and return false                          free(memberName);
                  * ourselves.  */  
                 free(buf);  
                 return false;  
             }  
             /* Free buffer and continue with our work.  */  
             free(buf);  
         } else if (Dir_HasWildcards(memberName)) {  
             LIST  members;  
             char  *member;  
   
             Lst_Init(&members);                  *cp = saveChar;
   
             Dir_Expand(memberName, dirSearchPath, &members);  
             while ((member = (char *)Lst_DeQueue(&members)) != NULL) {  
                 snprintf(nameBuf, MAKE_BSIZE, "%s(%s)", libName, member);  
                 free(member);  
                 gn = Targ_FindNode(nameBuf, TARG_CREATE);  
                 /* We've found the node, but have to make sure the rest of  
                  * the world knows it's an archive member, without having  
                  * to constantly check for parentheses, so we type the  
                  * thing with the OP_ARCHV bit before we place it on the  
                  * end of the provided list.  */  
                 gn->type |= OP_ARCHV;  
                 Lst_AtEnd(nodeLst, gn);  
             }  
         } else {  
             snprintf(nameBuf, MAKE_BSIZE, "%s(%s)", libName, memberName);  
             gn = Targ_FindNode(nameBuf, TARG_CREATE);  
             /* We've found the node, but have to make sure the rest of the  
              * world knows it's an archive member, without having to  
              * constantly check for parentheses, so we type the thing with  
              * the OP_ARCHV bit before we place it on the end of the  
              * provided list.  */  
             gn->type |= OP_ARCHV;  
             Lst_AtEnd(nodeLst, gn);  
         }          }
         if (doSubst)  
             free(memberName);  
   
         *cp = saveChar;          /* If substituted libName, free it now, since we need it no longer.  */
     }          if (subLibName)
                   free(libName);
   
     /* If substituted libName, free it now, since we need it no longer.  */          /* We promised the pointer would be set up at the next non-space, so
     if (subLibName)           * we must advance cp there before setting *linePtr... (note that on
         free(libName);           * entrance to the loop, cp is guaranteed to point at a ')') */
           do {
                   cp++;
           } while (isspace(*cp));
   
     /* We promised the pointer would be set up at the next non-space, so          *linePtr = cp;
      * we must advance cp there before setting *linePtr... (note that on          return true;
      * entrance to the loop, cp is guaranteed to point at a ')') */  
     do {  
         cp++;  
     } while (isspace(*cp));  
   
     *linePtr = cp;  
     return true;  
 }  }
   
 /* Helper function: ar fields are not null terminated.  */  /* Helper function: ar fields are not null terminated.  */
 static long  static long
 field2long(const char *field, size_t length)  field2long(const char *field, size_t length)
 {  {
     static char enough[32];          static char enough[32];
   
     assert(length < sizeof(enough));          assert(length < sizeof(enough));
     memcpy(enough, field, length);          memcpy(enough, field, length);
     enough[length] = '\0';          enough[length] = '\0';
     return strtol(enough, NULL, 10);          return strtol(enough, NULL, 10);
 }  }
   
 static Arch *  static Arch *
 read_archive(const char *archive, const char *earchive)  read_archive(const char *archive, const char *earchive)
 {  {
     FILE *        arch;       /* Stream to archive */          FILE *arch;             /* Stream to archive */
     char          magic[SARMAG];          char magic[SARMAG];
     Arch          *ar;          Arch *ar;
 #ifdef SVR4ARCHIVES  #ifdef SVR4ARCHIVES
     struct SVR4namelist list;          struct SVR4namelist list;
   
     list.fnametab = NULL;          list.fnametab = NULL;
 #endif  #endif
   
     /* When we encounter an archive for the first time, we read its          /* When we encounter an archive for the first time, we read its
      * whole contents, to place it in the cache.  */           * whole contents, to place it in the cache.  */
     arch = fopen(archive, "r");          arch = fopen(archive, "r");
     if (arch == NULL)          if (arch == NULL)
         return NULL;                  return NULL;
   
     /* Make sure this is an archive we can handle.  */          /* Make sure this is an archive we can handle.  */
     if ((fread(magic, SARMAG, 1, arch) != 1) ||          if ((fread(magic, SARMAG, 1, arch) != 1) ||
         (strncmp(magic, ARMAG, SARMAG) != 0)) {              (strncmp(magic, ARMAG, SARMAG) != 0)) {
             fclose(arch);                  fclose(arch);
             return NULL;                  return NULL;
     }          }
   
     ar = ohash_create_entry(&arch_info, archive, &earchive);          ar = ohash_create_entry(&arch_info, archive, &earchive);
     ohash_init(&ar->members, 8, &members_info);          ohash_init(&ar->members, 8, &members_info);
   
     for (;;) {          for (;;) {
         size_t          n;                  size_t n;
         struct ar_hdr   arHeader;/* Archive-member header for reading archive */                  struct ar_hdr arHeader;
         off_t           size;   /* Size of archive member */                                  /* Archive-member header for reading archive */
         char            buffer[PATH_MAX];                  off_t size;     /* Size of archive member */
         char            *memberName;                  char buffer[PATH_MAX];
                   char *memberName;
                                 /* Current member name while hashing. */                                  /* Current member name while hashing. */
         char            *cp;    /* Useful character pointer */                  char *cp;
   
         memberName = buffer;                  memberName = buffer;
         n = fread(&arHeader, 1, sizeof(struct ar_hdr), arch);                  n = fread(&arHeader, 1, sizeof(struct ar_hdr), arch);
   
         /*  Whole archive read ok.  */                  /*  Whole archive read ok.  */
         if (n == 0 && feof(arch)) {                  if (n == 0 && feof(arch)) {
 #ifdef SVR4ARCHIVES  #ifdef SVR4ARCHIVES
             efree(list.fnametab);                          efree(list.fnametab);
 #endif  #endif
             fclose(arch);                          fclose(arch);
             return ar;                          return ar;
         }                  }
         if (n < sizeof(struct ar_hdr))                  if (n < sizeof(struct ar_hdr))
             break;                          break;
   
         if (memcmp(arHeader.ar_fmag, ARFMAG, sizeof(arHeader.ar_fmag)) != 0) {                  if (memcmp(arHeader.ar_fmag, ARFMAG, sizeof(arHeader.ar_fmag))
             /* The header is bogus.  */                      != 0) {
             break;                          /* The header is bogus.  */
         } else {                          break;
             /* We need to advance the stream's pointer to the start of the                  } else {
              * next header.  Records are padded with newlines to an even-byte                          /* We need to advance the stream's pointer to the start
              * boundary, so we need to extract the size of the record and                           * of the next header.  Records are padded with
              * round it up during the seek.  */                           * newlines to an even-byte boundary, so we need to
             size = (off_t) field2long(arHeader.ar_size,                           * extract the size of the record and round it up
                 sizeof(arHeader.ar_size));                           * during the seek.  */
                           size = (off_t) field2long(arHeader.ar_size,
                               sizeof(arHeader.ar_size));
   
             (void)memcpy(memberName, arHeader.ar_name, AR_NAME_SIZE);                          (void)memcpy(memberName, arHeader.ar_name,
             /* Find real end of name (strip extranous ' ')  */                              AR_NAME_SIZE);
             for (cp = memberName + AR_NAME_SIZE - 1; *cp == ' ';)                          /* Find real end of name (strip extranous ' ')  */
                 cp--;                          for (cp = memberName + AR_NAME_SIZE - 1; *cp == ' ';)
             cp[1] = '\0';                                  cp--;
                           cp[1] = '\0';
   
 #ifdef SVR4ARCHIVES  #ifdef SVR4ARCHIVES
             /* SVR4 names are slash terminated.  Also svr4 extended AR format.                          /* SVR4 names are slash terminated.  Also svr4 extended
              */                           * AR format.
             if (memberName[0] == '/') {                           */
                 /* SVR4 magic mode.  */                          if (memberName[0] == '/') {
                 memberName = ArchSVR4Entry(&list, memberName, size, arch);                                  /* SVR4 magic mode.  */
                 if (memberName == NULL)         /* Invalid data */                                  memberName = ArchSVR4Entry(&list, memberName,
                     break;                                      size, arch);
                 else if (memberName == svr4list)/* List of files entry */                                  if (memberName == NULL) /* Invalid data */
                     continue;                                          break;
                 /* Got the entry.  */                                  else if (memberName == svr4list)
                 /* XXX this assumes further processing, such as AR_EFMT1,                                      /* List of files entry */
                  * also applies to SVR4ARCHIVES.  */                                          continue;
             }                                  /* Got the entry.  */
             else {                                  /* XXX this assumes further processing, such as
                 if (cp[0] == '/')                                   * AR_EFMT1, also applies to SVR4ARCHIVES.  */
                     cp[0] = '\0';                          }
             }                          else {
                                   if (cp[0] == '/')
                                           cp[0] = '\0';
                           }
 #endif  #endif
   
 #ifdef AR_EFMT1  #ifdef AR_EFMT1
             /* BSD 4.4 extended AR format: #1/<namelen>, with name as the                          /* BSD 4.4 extended AR format: #1/<namelen>, with name
              * first <namelen> bytes of the file.  */                           * as the first <namelen> bytes of the file.  */
             if (memcmp(memberName, AR_EFMT1, sizeof(AR_EFMT1) - 1) == 0 &&                          if (memcmp(memberName, AR_EFMT1, sizeof(AR_EFMT1) - 1)
                 isdigit(memberName[sizeof(AR_EFMT1) - 1])) {                              == 0 && isdigit(memberName[sizeof(AR_EFMT1) - 1])) {
   
                 int elen = atoi(memberName + sizeof(AR_EFMT1)-1);                                  int elen = atoi(memberName +
                                       sizeof(AR_EFMT1)-1);
   
                 if (elen <= 0 || elen >= PATH_MAX)                                  if (elen <= 0 || elen >= PATH_MAX)
                         break;                                          break;
                 memberName = buffer;                                  memberName = buffer;
                 if (fread(memberName, elen, 1, arch) != 1)                                  if (fread(memberName, elen, 1, arch) != 1)
                         break;                                          break;
                 memberName[elen] = '\0';                                  memberName[elen] = '\0';
                 if (fseek(arch, -elen, SEEK_CUR) != 0)                                  if (fseek(arch, -elen, SEEK_CUR) != 0)
                         break;                                          break;
                 if (DEBUG(ARCH) || DEBUG(MAKE))                                  if (DEBUG(ARCH) || DEBUG(MAKE))
                     printf("ArchStat: Extended format entry for %s\n",                                          printf("ArchStat: Extended format entry for %s\n",
                         memberName);                                              memberName);
             }                          }
 #endif  #endif
   
             ohash_insert(&ar->members,                          ohash_insert(&ar->members,
                 ohash_qlookup(&ar->members, memberName),                              ohash_qlookup(&ar->members, memberName),
                     new_arch_member(&arHeader, memberName));                                  new_arch_member(&arHeader, memberName));
                   }
                   if (fseek(arch, (size + 1) & ~1, SEEK_CUR) != 0)
                           break;
         }          }
         if (fseek(arch, (size + 1) & ~1, SEEK_CUR) != 0)  
             break;  
     }  
   
     fclose(arch);          fclose(arch);
     ohash_delete(&ar->members);          ohash_delete(&ar->members);
 #ifdef SVR4ARCHIVES  #ifdef SVR4ARCHIVES
     efree(list.fnametab);          efree(list.fnametab);
 #endif  #endif
     free(ar);          free(ar);
     return NULL;          return NULL;
 }  }
   
 /*-  /*-
Line 547 
Line 565 
     bool          hash)       /* true if archive should be hashed if not      bool          hash)       /* true if archive should be hashed if not
                                * already so. */                                 * already so. */
 {  {
     FILE *        arch;       /* Stream to archive */          FILE *arch;             /* Stream to archive */
     Arch          *ar;        /* Archive descriptor */          Arch *ar;               /* Archive descriptor */
     unsigned int  slot;       /* Place of archive in the archives hash */          unsigned int slot;      /* Place of archive in the archives hash */
     const char    *end = NULL;          const char *end = NULL;
     const char    *cp;          const char *cp;
     TIMESTAMP     result;          TIMESTAMP result;
   
     ts_set_out_of_date(result);          ts_set_out_of_date(result);
     /* Because of space constraints and similar things, files are archived          /* Because of space constraints and similar things, files are archived
      * using their final path components, not the entire thing, so we need           * using their final path components, not the entire thing, so we need
      * to point 'member' to the final component, if there is one, to make           * to point 'member' to the final component, if there is one, to make
      * the comparisons easier...  */           * the comparisons easier...  */
     cp = strrchr(member, '/');          cp = strrchr(member, '/');
     if (cp != NULL)          if (cp != NULL)
         member = cp + 1;                  member = cp + 1;
   
     /* Try to find archive in cache.  */          /* Try to find archive in cache.  */
     slot = ohash_qlookupi(&archives, archive, &end);          slot = ohash_qlookupi(&archives, archive, &end);
     ar = ohash_find(&archives, slot);          ar = ohash_find(&archives, slot);
   
     /* If not found, get it now.  */          /* If not found, get it now.  */
     if (ar == NULL) {          if (ar == NULL) {
         if (!hash) {                  if (!hash) {
             /* Quick path:  no need to hash the whole archive, just use                      /* Quick path:  no need to hash the whole archive, just use
              * ArchFindMember to get the member's header and close the stream                       * ArchFindMember to get the member's header and close the
              * again.  */                       * stream again.  */
             struct ar_hdr       arHeader;                      struct ar_hdr arHeader;
   
             arch = ArchFindMember(archive, member, &arHeader, "r");                      arch = ArchFindMember(archive, member, &arHeader, "r");
   
             if (arch != NULL) {                      if (arch != NULL) {
                 fclose(arch);                              fclose(arch);
                 ts_set_from_time_t( (time_t)strtol(arHeader.ar_date, NULL, 10),                              ts_set_from_time_t(
                     result);                                  (time_t)strtol(arHeader.ar_date, NULL, 10),
             }                                  result);
             return result;                      }
                       return result;
                   }
                   ar = read_archive(archive, end);
                   if (ar != NULL)
                           ohash_insert(&archives, slot, ar);
         }          }
         ar = read_archive(archive, end);  
         if (ar != NULL)  
             ohash_insert(&archives, slot, ar);  
     }  
   
     /* If archive was found, get entry we seek.  */          /* If archive was found, get entry we seek.  */
     if (ar != NULL) {          if (ar != NULL) {
         struct arch_member *he;                  struct arch_member *he;
         end = NULL;                  end = NULL;
   
         he = ohash_find(&ar->members, ohash_qlookupi(&ar->members, member, &end));                  he = ohash_find(&ar->members,
         if (he != NULL)  
             return mtime_of_member(he);  
         else {  
             if ((size_t)(end - member) > AR_NAME_SIZE) {  
                 /* Try truncated name.  */  
                 end = member + AR_NAME_SIZE;  
                 he = ohash_find(&ar->members,  
                     ohash_qlookupi(&ar->members, member, &end));                      ohash_qlookupi(&ar->members, member, &end));
                 if (he != NULL)                  if (he != NULL)
                     return mtime_of_member(he);                          return mtime_of_member(he);
             }                  else {
                           if ((size_t)(end - member) > AR_NAME_SIZE) {
                                   /* Try truncated name.  */
                                   end = member + AR_NAME_SIZE;
                                   he = ohash_find(&ar->members,
                                       ohash_qlookupi(&ar->members, member, &end));
                                   if (he != NULL)
                                           return mtime_of_member(he);
                           }
                   }
         }          }
     }          return result;
     return result;  
 }  }
   
 #ifdef SVR4ARCHIVES  #ifdef SVR4ARCHIVES
Line 635 
Line 655 
 {  {
 #define ARLONGNAMES1 "/"  #define ARLONGNAMES1 "/"
 #define ARLONGNAMES2 "ARFILENAMES"  #define ARLONGNAMES2 "ARFILENAMES"
     size_t entry;          size_t entry;
     char *ptr, *eptr;          char *ptr, *eptr;
   
     assert(name[0] == '/');          assert(name[0] == '/');
     name++;          name++;
     /* First comes a table of archive names, to be used by subsequent calls.  */          /* First comes a table of archive names, to be used by subsequent
     if (memcmp(name, ARLONGNAMES1, sizeof(ARLONGNAMES1) - 1) == 0 ||           * calls.  */
         memcmp(name, ARLONGNAMES2, sizeof(ARLONGNAMES2) - 1) == 0) {          if (memcmp(name, ARLONGNAMES1, sizeof(ARLONGNAMES1) - 1) == 0 ||
               memcmp(name, ARLONGNAMES2, sizeof(ARLONGNAMES2) - 1) == 0) {
   
         if (l->fnametab != NULL) {                  if (l->fnametab != NULL) {
             if (DEBUG(ARCH))                          if (DEBUG(ARCH))
                 printf("Attempted to redefine an SVR4 name table\n");                                  printf("Attempted to redefine an SVR4 name table\n");
             return NULL;                          return NULL;
         }                  }
   
         l->fnametab = emalloc(size);                  l->fnametab = emalloc(size);
         l->fnamesize = size;                  l->fnamesize = size;
   
         if (fread(l->fnametab, size, 1, arch) != 1) {                  if (fread(l->fnametab, size, 1, arch) != 1) {
             if (DEBUG(ARCH))                          if (DEBUG(ARCH))
                 printf("Reading an SVR4 name table failed\n");                                  printf("Reading an SVR4 name table failed\n");
             return NULL;                          return NULL;
         }                  }
   
         eptr = l->fnametab + size;                  eptr = l->fnametab + size;
         for (entry = 0, ptr = l->fnametab; ptr < eptr; ptr++)                  for (entry = 0, ptr = l->fnametab; ptr < eptr; ptr++)
             switch (*ptr) {                          switch (*ptr) {
             case '/':                          case '/':
                 entry++;                                  entry++;
                 *ptr = '\0';                                  *ptr = '\0';
                 break;                                  break;
   
             case '\n':                          case '\n':
                 break;                                  break;
   
             default:                          default:
                 break;                                  break;
             }                          }
         if (DEBUG(ARCH))                  if (DEBUG(ARCH))
             printf("Found svr4 archive name table with %lu entries\n",                          printf("Found svr4 archive name table with %lu entries\n",
                         (u_long)entry);                              (u_long)entry);
         return (char *)svr4list;                  return (char *)svr4list;
     }          }
     /* Then the names themselves are given as offsets in this table.  */          /* Then the names themselves are given as offsets in this table.  */
     if (*name == ' ' || *name == '\0')          if (*name == ' ' || *name == '\0')
         return NULL;                  return NULL;
   
     entry = (size_t) strtol(name, &eptr, 0);          entry = (size_t) strtol(name, &eptr, 0);
     if ((*eptr != ' ' && *eptr != '\0') || eptr == name) {          if ((*eptr != ' ' && *eptr != '\0') || eptr == name) {
                   if (DEBUG(ARCH))
                           printf("Could not parse SVR4 name /%s\n", name);
                   return NULL;
           }
           if (entry >= l->fnamesize) {
                   if (DEBUG(ARCH))
                           printf("SVR4 entry offset /%s is greater than %lu\n",
                              name, (u_long)l->fnamesize);
                   return NULL;
           }
   
         if (DEBUG(ARCH))          if (DEBUG(ARCH))
             printf("Could not parse SVR4 name /%s\n", name);                  printf("Replaced /%s with %s\n", name, l->fnametab + entry);
         return NULL;  
     }  
     if (entry >= l->fnamesize) {  
         if (DEBUG(ARCH))  
             printf("SVR4 entry offset /%s is greater than %lu\n",  
                    name, (u_long)l->fnamesize);  
         return NULL;  
     }  
   
     if (DEBUG(ARCH))          return l->fnametab + entry;
         printf("Replaced /%s with %s\n", name, l->fnametab + entry);  
   
     return l->fnametab + entry;  
 }  }
 #endif  #endif
   
Line 726 
Line 747 
     struct ar_hdr *arHeaderPtr,/* Pointer to header structure to be filled in */      struct ar_hdr *arHeaderPtr,/* Pointer to header structure to be filled in */
     const char    *mode)      /* mode for opening the stream */      const char    *mode)      /* mode for opening the stream */
 {  {
     FILE *        arch;       /* Stream to archive */          FILE *arch;       /* Stream to archive */
     char          *cp;          char *cp;
     char          magic[SARMAG];          char magic[SARMAG];
     size_t        length;          size_t length;
 #ifdef SVR4ARCHIVES  #ifdef SVR4ARCHIVES
     struct SVR4namelist list;          struct SVR4namelist list;
   
     list.fnametab = NULL;          list.fnametab = NULL;
 #endif  #endif
   
     arch = fopen(archive, mode);          arch = fopen(archive, mode);
     if (arch == NULL)          if (arch == NULL)
         return NULL;                  return NULL;
   
     /* Make sure this is an archive we can handle.  */          /* Make sure this is an archive we can handle.  */
     if (fread(magic, SARMAG, 1, arch) != 1 ||          if (fread(magic, SARMAG, 1, arch) != 1 ||
         strncmp(magic, ARMAG, SARMAG) != 0) {              strncmp(magic, ARMAG, SARMAG) != 0) {
             fclose(arch);                  fclose(arch);
             return NULL;                  return NULL;
     }          }
   
     /* Because of space constraints and similar things, files are archived          /* Because of space constraints and similar things, files are archived
      * using their final path components, not the entire thing, so we need           * using their final path components, not the entire thing, so we need
      * to point 'member' to the final component, if there is one, to make           * to point 'member' to the final component, if there is one, to make
      * the comparisons easier...  */           * the comparisons easier...  */
     cp = strrchr(member, '/');          cp = strrchr(member, '/');
     if (cp != NULL)          if (cp != NULL)
         member = cp + 1;                  member = cp + 1;
   
     length = strlen(member);          length = strlen(member);
     if (length >= AR_NAME_SIZE)          if (length >= AR_NAME_SIZE)
         length = AR_NAME_SIZE;                  length = AR_NAME_SIZE;
   
     /* Error handling is simpler than for read_archive, since we just          /* Error handling is simpler than for read_archive, since we just
      * look for a given member.  */           * look for a given member.  */
     while (fread(arHeaderPtr, sizeof(struct ar_hdr), 1, arch) == 1) {          while (fread(arHeaderPtr, sizeof(struct ar_hdr), 1, arch) == 1) {
         off_t             size;       /* Size of archive member */                  off_t size;     /* Size of archive member */
         char              *memberName;                  char *memberName;
   
         if (memcmp(arHeaderPtr->ar_fmag, ARFMAG, sizeof(arHeaderPtr->ar_fmag) )                  if (memcmp(arHeaderPtr->ar_fmag, ARFMAG,
             != 0)                      sizeof(arHeaderPtr->ar_fmag) ) != 0)
              /* The header is bogus, so the archive is bad.  */                           /* The header is bogus, so the archive is bad.  */
              break;                           break;
   
         memberName = arHeaderPtr->ar_name;                  memberName = arHeaderPtr->ar_name;
         if (memcmp(member, memberName, length) == 0) {                  if (memcmp(member, memberName, length) == 0) {
             /* If the member's name doesn't take up the entire 'name' field,                          /* If the member's name doesn't take up the entire
              * we have to be careful of matching prefixes. Names are space-                           * 'name' field, we have to be careful of matching
              * padded to the right, so if the character in 'name' at the end                           * prefixes. Names are space- padded to the right, so
              * of the matched string is anything but a space, this isn't the                           * if the character in 'name' at the end of the matched
              * member we sought.  */                           * string is anything but a space, this isn't the
                            * member we sought.  */
 #ifdef SVR4ARCHIVES  #ifdef SVR4ARCHIVES
             if (length < sizeof(arHeaderPtr->ar_name) &&                          if (length < sizeof(arHeaderPtr->ar_name) &&
                 memberName[length] == '/')                              memberName[length] == '/')
                 length++;                                  length++;
 #endif  #endif
             if (length == sizeof(arHeaderPtr->ar_name) ||                          if (length == sizeof(arHeaderPtr->ar_name) ||
                 memberName[length] == ' ') {                              memberName[length] == ' ') {
 #ifdef SVR4ARCHIVES  #ifdef SVR4ARCHIVES
                 efree(list.fnametab);                                  efree(list.fnametab);
 #endif  #endif
                 return arch;                                  return arch;
             }                          }
         }                  }
   
         size = (off_t) field2long(arHeaderPtr->ar_size,                  size = (off_t) field2long(arHeaderPtr->ar_size,
             sizeof(arHeaderPtr->ar_size));                      sizeof(arHeaderPtr->ar_size));
   
 #ifdef SVR4ARCHIVES  #ifdef SVR4ARCHIVES
             /* svr4 names are slash terminated. Also svr4 extended AR format.                      /* svr4 names are slash terminated. Also svr4 extended AR
              */                       * format.
             if (memberName[0] == '/') {                       */
                 /* svr4 magic mode.  */                      if (memberName[0] == '/') {
                 memberName = ArchSVR4Entry(&list, arHeaderPtr->ar_name, size,                              /* svr4 magic mode.  */
                     arch);                              memberName = ArchSVR4Entry(&list,
                 if (memberName == NULL)         /* Invalid data */                                  arHeaderPtr->ar_name, size, arch);
                     break;                              if (memberName == NULL)     /* Invalid data */
                 else if (memberName == svr4list)/* List of files entry */                                      break;
                     continue;                              else if (memberName == svr4list)
                 /* Got the entry.  */                                  /* List of files entry */
                 if (strcmp(memberName, member) == 0) {                                      continue;
                     efree(list.fnametab);                              /* Got the entry.  */
                     return arch;                              if (strcmp(memberName, member) == 0) {
                 }                                      efree(list.fnametab);
             }                                      return arch;
                               }
                       }
 #endif  #endif
   
 #ifdef AR_EFMT1  #ifdef AR_EFMT1
         /* BSD 4.4 extended AR format: #1/<namelen>, with name as the                  /* BSD 4.4 extended AR format: #1/<namelen>, with name as the
          * first <namelen> bytes of the file.  */                   * first <namelen> bytes of the file.  */
         if (memcmp(memberName, AR_EFMT1, sizeof(AR_EFMT1) - 1) == 0 &&                  if (memcmp(memberName, AR_EFMT1, sizeof(AR_EFMT1) - 1) == 0 &&
             isdigit(memberName[sizeof(AR_EFMT1) - 1])) {                      isdigit(memberName[sizeof(AR_EFMT1) - 1])) {
             char          ename[PATH_MAX];                          char ename[PATH_MAX];
   
             int elength = atoi(memberName + sizeof(AR_EFMT1)-1);                          int elength = atoi(memberName + sizeof(AR_EFMT1)-1);
   
             if (elength <= 0 || elength >= PATH_MAX)                          if (elength <= 0 || elength >= PATH_MAX)
                 break;                                  break;
             if (fread(ename, elength, 1, arch) != 1)                          if (fread(ename, elength, 1, arch) != 1)
                 break;                                  break;
             if (fseek(arch, -elength, SEEK_CUR) != 0)                          if (fseek(arch, -elength, SEEK_CUR) != 0)
                 break;                                  break;
             ename[elength] = '\0';                          ename[elength] = '\0';
             if (DEBUG(ARCH) || DEBUG(MAKE))                          if (DEBUG(ARCH) || DEBUG(MAKE))
                 printf("ArchFind: Extended format entry for %s\n", ename);                                  printf("ArchFind: Extended format entry for %s\n", ename);
             /* Found as extended name.  */                          /* Found as extended name.      */
             if (strcmp(ename, member) == 0) {                          if (strcmp(ename, member) == 0) {
 #ifdef SVR4ARCHIVES  #ifdef SVR4ARCHIVES
                 efree(list.fnametab);                                  efree(list.fnametab);
 #endif  #endif
                 return arch;                                  return arch;
                           }
                 }                  }
         }  
 #endif  #endif
         /* This isn't the member we're after, so we need to advance the                  /* This isn't the member we're after, so we need to advance the
          * stream's pointer to the start of the next header.  */                   * stream's pointer to the start of the next header.  */
         if (fseek(arch, (size + 1) & ~1, SEEK_CUR) != 0)                  if (fseek(arch, (size + 1) & ~1, SEEK_CUR) != 0)
             break;                          break;
     }          }
   
     /* We did not find the member, or we ran into an error while reading          /* We did not find the member, or we ran into an error while reading
      * the archive.  */           * the archive.  */
 #ifdef SVRARCHIVES  #ifdef SVRARCHIVES
     efree(list.fnametab);          efree(list.fnametab);
 #endif  #endif
     fclose(arch);          fclose(arch);
     return NULL;          return NULL;
 }  }
   
 static void  static void
Line 861 
Line 885 
     FILE *arch;      FILE *arch;
     struct ar_hdr arHeader;      struct ar_hdr arHeader;
   
     arch = ArchFindMember(archive, member, &arHeader, "r+");          arch = ArchFindMember(archive, member, &arHeader, "r+");
     if (arch != NULL) {          if (arch != NULL) {
         snprintf(arHeader.ar_date, sizeof(arHeader.ar_date), "%-12ld", (long)                  snprintf(arHeader.ar_date, sizeof(arHeader.ar_date), "%-12ld",
             timestamp2time_t(now));                      (long) timestamp2time_t(now));
         if (fseek(arch, -sizeof(struct ar_hdr), SEEK_CUR) == 0)                  if (fseek(arch, -sizeof(struct ar_hdr), SEEK_CUR) == 0)
             (void)fwrite(&arHeader, sizeof(struct ar_hdr), 1, arch);                          (void)fwrite(&arHeader, sizeof(struct ar_hdr), 1, arch);
         fclose(arch);                  fclose(arch);
     }          }
 }  }
   
 /*  /*
Line 880 
Line 904 
 void  void
 Arch_Touch(GNode *gn)  Arch_Touch(GNode *gn)
 {  {
     ArchTouch(Varq_Value(ARCHIVE_INDEX, gn), Varq_Value(MEMBER_INDEX, gn));          ArchTouch(Varq_Value(ARCHIVE_INDEX, gn), Varq_Value(MEMBER_INDEX, gn));
 }  }
   
 /*ARGSUSED*/  /*ARGSUSED*/
Line 889 
Line 913 
                      /* ^          Non RANLIBMAG does nothing with it */                       /* ^          Non RANLIBMAG does nothing with it */
 {  {
 #ifdef RANLIBMAG  #ifdef RANLIBMAG
     if (gn->path != NULL) {          if (gn->path != NULL) {
         ArchTouch(gn->path, RANLIBMAG);                  ArchTouch(gn->path, RANLIBMAG);
         set_times(gn->path);                  set_times(gn->path);
     }      }
 #endif  #endif
 }  }
Line 899 
Line 923 
 TIMESTAMP  TIMESTAMP
 Arch_MTime(GNode *gn)  Arch_MTime(GNode *gn)
 {  {
     gn->mtime = ArchMTimeMember(Varq_Value(ARCHIVE_INDEX, gn),          gn->mtime = ArchMTimeMember(Varq_Value(ARCHIVE_INDEX, gn),
              Varq_Value(MEMBER_INDEX, gn),               Varq_Value(MEMBER_INDEX, gn), true);
              true);  
   
     return gn->mtime;          return gn->mtime;
 }  }
   
 TIMESTAMP  TIMESTAMP
 Arch_MemMTime(GNode *gn)  Arch_MemMTime(GNode *gn)
 {  {
     LstNode       ln;          LstNode ln;
   
     for (ln = Lst_First(&gn->parents); ln != NULL; ln = Lst_Adv(ln)) {          for (ln = Lst_First(&gn->parents); ln != NULL; ln = Lst_Adv(ln)) {
         GNode   *pgn;                  GNode *pgn;
         char    *nameStart,                  char *nameStart;
                 *nameEnd;                  char *nameEnd;
   
         pgn = (GNode *)Lst_Datum(ln);                  pgn = (GNode *)Lst_Datum(ln);
   
         if (pgn->type & OP_ARCHV) {                  if (pgn->type & OP_ARCHV) {
             /* If the parent is an archive specification and is being made                          /* If the parent is an archive specification and is
              * and its member's name matches the name of the node we were                           * being made and its member's name matches the name of
              * given, record the modification time of the parent in the                           * the node we were given, record the modification time
              * child. We keep searching its parents in case some other                           * of the parent in the child. We keep searching its
              * parent requires this child to exist...  */                           * parents in case some other parent requires this
             if ((nameStart = strchr(pgn->name, '(') ) != NULL) {                           * child to exist...  */
                 nameStart++;                          if ((nameStart = strchr(pgn->name, '(') ) != NULL) {
                 nameEnd = strchr(nameStart, ')');                                  nameStart++;
             } else                                  nameEnd = strchr(nameStart, ')');
                 nameEnd = NULL;                          } else
                                   nameEnd = NULL;
   
             if (pgn->make && nameEnd != NULL &&                          if (pgn->make && nameEnd != NULL &&
                 strncmp(nameStart, gn->name, nameEnd - nameStart) == 0 &&                              strncmp(nameStart, gn->name, nameEnd - nameStart)
                 gn->name[nameEnd-nameStart] == '\0')                              == 0 && gn->name[nameEnd-nameStart] == '\0')
                     gn->mtime = Arch_MTime(pgn);                                  gn->mtime = Arch_MTime(pgn);
         } else if (pgn->make) {                  } else if (pgn->make) {
             /* Something which isn't a library depends on the existence of                          /* Something which isn't a library depends on the
              * this target, so it needs to exist.  */                           * existence of this target, so it needs to exist.  */
             ts_set_out_of_date(gn->mtime);                          ts_set_out_of_date(gn->mtime);
             break;                          break;
                   }
         }          }
     }          return gn->mtime;
     return gn->mtime;  
 }  }
   
 /* If the system can handle the -L flag when linking (or we cannot find  /* If the system can handle the -L flag when linking (or we cannot find
Line 954 
Line 978 
 void  void
 Arch_FindLib(GNode *gn, Lst path)  Arch_FindLib(GNode *gn, Lst path)
 {  {
     char            *libName;   /* file name for archive */          char *libName;  /* file name for archive */
     size_t          length = strlen(gn->name) + 6 - 2;          size_t length = strlen(gn->name) + 6 - 2;
   
     libName = emalloc(length);          libName = emalloc(length);
     snprintf(libName, length, "lib%s.a", &gn->name[2]);          snprintf(libName, length, "lib%s.a", &gn->name[2]);
   
     gn->path = Dir_FindFile(libName, path);          gn->path = Dir_FindFile(libName, path);
   
     free(libName);          free(libName);
   
     Varq_Set(TARGET_INDEX, gn->name, gn);          Varq_Set(TARGET_INDEX, gn->name, gn);
 }  }
   
 /*-  /*-
Line 1006 
Line 1030 
 Arch_LibOODate(GNode *gn)  Arch_LibOODate(GNode *gn)
 {  {
 #ifdef RANLIBMAG  #ifdef RANLIBMAG
     TIMESTAMP     modTimeTOC;   /* mod time of __.SYMDEF */          TIMESTAMP modTimeTOC;   /* mod time of __.SYMDEF */
 #endif  #endif
   
     if (OP_NOP(gn->type) && Lst_IsEmpty(&gn->children))          if (OP_NOP(gn->type) && Lst_IsEmpty(&gn->children))
         return false;                  return false;
     if (is_strictly_before(now, gn->mtime) || is_strictly_before(gn->mtime, gn->cmtime) ||          if (is_strictly_before(now, gn->mtime) ||
         is_out_of_date(gn->mtime))              is_strictly_before(gn->mtime, gn->cmtime) ||
         return true;              is_out_of_date(gn->mtime))
                   return true;
 #ifdef RANLIBMAG  #ifdef RANLIBMAG
     /* non existent libraries are always out-of-date.  */          /* non existent libraries are always out-of-date.  */
     if (gn->path == NULL)          if (gn->path == NULL)
         return true;                  return true;
     modTimeTOC = ArchMTimeMember(gn->path, RANLIBMAG, false);          modTimeTOC = ArchMTimeMember(gn->path, RANLIBMAG, false);
   
     if (!is_out_of_date(modTimeTOC)) {          if (!is_out_of_date(modTimeTOC)) {
                   if (DEBUG(ARCH) || DEBUG(MAKE))
                           printf("%s modified %s...", RANLIBMAG,
                               Targ_FmtTime(modTimeTOC));
                   return is_strictly_before(modTimeTOC, gn->cmtime);
           }
           /* A library w/o a table of contents is out-of-date.  */
         if (DEBUG(ARCH) || DEBUG(MAKE))          if (DEBUG(ARCH) || DEBUG(MAKE))
             printf("%s modified %s...", RANLIBMAG, Targ_FmtTime(modTimeTOC));                  printf("No t.o.c....");
         return is_strictly_before(modTimeTOC, gn->cmtime);          return true;
     }  
     /* A library w/o a table of contents is out-of-date.  */  
     if (DEBUG(ARCH) || DEBUG(MAKE))  
         printf("No t.o.c....");  
     return true;  
 #else  #else
     return false;          return false;
 #endif  #endif
 }  }
   
 void  void
 Arch_Init(void)  Arch_Init(void)
 {  {
     ohash_init(&archives, 4, &arch_info);          ohash_init(&archives, 4, &arch_info);
 }  }
   
 #ifdef CLEANUP  #ifdef CLEANUP
 void  void
 Arch_End(void)  Arch_End(void)
 {  {
     Arch *e;          Arch *e;
     unsigned int i;          unsigned int i;
   
     for (e = ohash_first(&archives, &i); e != NULL;          for (e = ohash_first(&archives, &i); e != NULL;
         e = ohash_next(&archives, &i))              e = ohash_next(&archives, &i))
             ArchFree(e);                  ArchFree(e);
     ohash_delete(&archives);          ohash_delete(&archives);
 }  }
 #endif  #endif
   
 bool  bool
 Arch_IsLib(GNode *gn)  Arch_IsLib(GNode *gn)
 {  {
     char buf[SARMAG];          char buf[SARMAG];
     int fd;          int fd;
   
     if (gn->path == NULL || (fd = open(gn->path, O_RDONLY)) == -1)          if (gn->path == NULL || (fd = open(gn->path, O_RDONLY)) == -1)
         return false;                  return false;
   
     if (read(fd, buf, SARMAG) != SARMAG) {          if (read(fd, buf, SARMAG) != SARMAG) {
                   (void)close(fd);
                   return false;
           }
   
         (void)close(fd);          (void)close(fd);
         return false;  
     }  
   
     (void)close(fd);          return memcmp(buf, ARMAG, SARMAG) == 0;
   
     return memcmp(buf, ARMAG, SARMAG) == 0;  
 }  }

Legend:
Removed from v.1.63  
changed lines
  Added in v.1.64