version 1.63, 2007/09/16 10:14:26 |
version 1.64, 2007/09/16 11:27:05 |
|
|
#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. */ |
|
|
/* 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 |
}; |
}; |
|
|
|
|
|
|
|
|
#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"; |
|
|
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 |
|
|
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 |
|
|
|
|
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; |
} |
} |
|
|
/*- |
/*- |
|
|
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 |
|
|
{ |
{ |
#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 |
|
|
|
|
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 |
|
|
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); |
} |
} |
} |
} |
|
|
/* |
/* |
|
|
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*/ |
|
|
/* ^ 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 |
} |
} |
|
|
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 |
|
|
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); |
} |
} |
|
|
/*- |
/*- |
|
|
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; |
|
} |
} |