version 1.69, 2007/09/17 09:28:36 |
version 1.70, 2007/09/17 10:06:44 |
|
|
#include "ohash.h" |
#include "ohash.h" |
#include "config.h" |
#include "config.h" |
#include "defines.h" |
#include "defines.h" |
|
#include "buf.h" |
#include "dir.h" |
#include "dir.h" |
#include "direxpand.h" |
#include "direxpand.h" |
#include "arch.h" |
#include "arch.h" |
|
|
#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; |
|
|
|
|
/* 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 |
/* Same, before conversion to numeric value. */ |
* value. */ |
char name[1]; /* Member name. */ |
char name[1]; /* Member name. */ |
}; |
}; |
|
|
static struct ohash_info members_info = { |
static struct ohash_info members_info = { |
|
|
(defined(__OpenBSD__) && defined(__ELF__)) |
(defined(__OpenBSD__) && defined(__ELF__)) |
#define SVR4ARCHIVES |
#define SVR4ARCHIVES |
#endif |
#endif |
|
static bool parse_archive(Buffer, const char **, Lst, SymTable *); |
|
static void add_archive_node(Lst, const char *); |
|
|
#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"; |
|
|
} |
} |
#endif |
#endif |
|
|
|
bool |
|
Arch_ParseArchive(const char **line, Lst nodes, SymTable *ctxt) |
|
{ |
|
bool result; |
|
BUFFER expand; |
|
|
|
Buf_Init(&expand, MAKE_BSIZE); |
|
result = parse_archive(&expand, line, nodes, ctxt); |
|
Buf_Destroy(&expand); |
|
return result; |
|
} |
|
|
/* Side-effects: Some nodes may be created. */ |
static void |
bool |
add_archive_node(Lst nodes, const char *name) |
Arch_ParseArchive(char **linePtr, /* Pointer to start of specification */ |
|
Lst nodeLst, /* Lst on which to place the nodes */ |
|
SymTable *ctxt) /* Context in which to expand variables */ |
|
{ |
{ |
char *cp; /* Pointer into line */ |
GNode *gn; |
GNode *gn; /* New node */ |
|
char *libName; /* Library-part of specification */ |
|
char *memberName; /* Member-part of specification */ |
|
char nameBuf[MAKE_BSIZE]; /* temporary place for node name */ |
|
char saveChar; /* Ending delimiter of member-name */ |
|
bool subLibName; /* true if libName should have/had |
|
* variable substitution performed on it */ |
|
|
|
libName = *linePtr; |
gn = Targ_FindNode(name, TARG_CREATE); |
|
gn->type |= OP_ARCHV; |
|
Lst_AtEnd(nodes, gn); |
|
} |
|
|
subLibName = false; |
static bool |
|
parse_archive(Buffer expand, const char **linePtr, Lst nodeLst, SymTable *ctxt) |
|
{ |
|
const char *cp; /* Pointer into line */ |
|
const char *lib; /* Library-part of specification */ |
|
const char *elib; |
|
const char *member; /* Member-part of specification */ |
|
const char *emember; |
|
bool subst_lib; |
|
|
for (cp = libName; *cp != '(' && *cp != '\0';) { |
/* figure out the library name part */ |
|
lib = *linePtr; |
|
subst_lib = false; |
|
|
|
for (cp = lib; *cp != '(' && *cp != '\0';) { |
if (*cp == '$') { |
if (*cp == '$') { |
if (!Var_ParseSkip(&cp, ctxt)) |
if (!Var_ParseSkip(&cp, ctxt)) |
return false; |
return false; |
subLibName = true; |
subst_lib = true; |
} else |
} else |
cp++; |
cp++; |
} |
} |
|
|
*cp++ = '\0'; |
elib = cp; |
if (subLibName) |
if (subst_lib) { |
libName = Var_Subst(libName, ctxt, true); |
lib = Var_Substi(lib, elib, ctxt, true); |
|
elib = lib + strlen(lib); |
|
} |
|
|
|
cp++; |
|
/* iterate on members, that may be separated by spaces */ |
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 |
bool subst_member = false; |
* memberName */ |
|
|
|
while (isspace(*cp)) |
while (isspace(*cp)) |
cp++; |
cp++; |
memberName = cp; |
member = 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; |
subst_member = true; |
} else |
} else |
cp++; |
cp++; |
} |
} |
|
|
} |
} |
|
|
/* 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 == member) |
break; |
break; |
|
|
saveChar = *cp; |
emember = cp; |
*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 |
* SuffExpandChildren, both for the archive and the member |
|
|
* but them's the breaks -- we need to do this since |
* but them's the breaks -- we need to do this since |
* SuffExpandChildren calls us, otherwise we could assume the |
* SuffExpandChildren calls us, otherwise we could assume the |
* thing would be taken care of later. */ |
* thing would be taken care of later. */ |
if (doSubst) { |
if (subst_member) { |
char *buf; |
const char *oldMemberName = member; |
char *sacrifice; |
const char *result; |
char *oldMemberName = memberName; |
|
size_t length; |
|
|
|
memberName = Var_Subst(memberName, ctxt, true); |
member = Var_Substi(member, emember, ctxt, true); |
|
|
/* Now form an archive spec and recurse to deal with |
/* Now form an archive spec and recurse to deal with |
* nested variables and multi-word variable values.... |
* nested variables and multi-word variable values.... |
* The results are just placed at the end of the |
* The results are just placed at the end of the |
* nodeLst we're returning. */ |
* nodeLst we're returning. */ |
length = strlen(memberName)+strlen(libName)+3; |
Buf_Addi(expand, lib, elib); |
buf = sacrifice = emalloc(length); |
Buf_AddChar(expand, '('); |
|
Buf_AddString(expand, member); |
|
Buf_AddChar(expand, ')'); |
|
result = Buf_Retrieve(expand); |
|
|
snprintf(buf, length, "%s(%s)", libName, memberName); |
if (strchr(member, '$') && |
|
memcmp(member, oldMemberName, |
if (strchr(memberName, '$') && |
emember - oldMemberName) == 0) { |
strcmp(memberName, oldMemberName) == 0) { |
|
/* Must contain dynamic sources, so we can't |
/* Must contain dynamic sources, so we can't |
* deal with it now. Just create an ARCHV node |
* deal with it now. let SuffExpandChildren |
* for the thing and let SuffExpandChildren |
* handle it later */ |
* handle it... */ |
add_archive_node(nodeLst, result); |
gn = Targ_FindNode(buf, TARG_CREATE); |
} else if (!Arch_ParseArchive(&result, nodeLst, ctxt)) |
|
|
if (gn == NULL) { |
|
free(buf); |
|
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; |
return false; |
} |
Buf_Reset(expand); |
/* Free buffer and continue with our work. */ |
} else if (Dir_HasWildcardsi(member, emember)) { |
free(buf); |
LIST members; |
} else if (Dir_HasWildcards(memberName)) { |
char *m; |
LIST members; |
|
char *member; |
|
|
|
Lst_Init(&members); |
Lst_Init(&members); |
|
|
Dir_Expand(memberName, defaultPath, &members); |
Dir_Expandi(member, emember, defaultPath, &members); |
while ((member = (char *)Lst_DeQueue(&members)) |
while ((m = (char *)Lst_DeQueue(&members)) != NULL) { |
!= NULL) { |
Buf_Addi(expand, lib, elib); |
snprintf(nameBuf, MAKE_BSIZE, "%s(%s)", |
Buf_AddChar(expand, '('); |
libName, member); |
Buf_AddString(expand, m); |
free(member); |
Buf_AddChar(expand, ')'); |
gn = Targ_FindNode(nameBuf, TARG_CREATE); |
free(m); |
/* We've found the node, but have to make sure |
add_archive_node(nodeLst, Buf_Retrieve(expand)); |
* the rest of the world knows it's an archive |
Buf_Reset(expand); |
* 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 { |
snprintf(nameBuf, MAKE_BSIZE, "%s(%s)", libName, |
Buf_Addi(expand, lib, elib); |
memberName); |
Buf_AddChar(expand, '('); |
gn = Targ_FindNode(nameBuf, TARG_CREATE); |
Buf_Addi(expand, member, emember); |
/* We've found the node, but have to make sure the rest |
Buf_AddChar(expand, ')'); |
* of the world knows it's an archive member, without |
add_archive_node(nodeLst, Buf_Retrieve(expand)); |
* having to constantly check for parentheses, so we |
Buf_Reset(expand); |
* 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) |
if (subst_member) |
free(memberName); |
free((char *)member); |
|
|
*cp = saveChar; |
|
} |
} |
|
|
/* If substituted libName, free it now, since we need it no longer. */ |
if (subst_lib) |
if (subLibName) |
free((char *)lib); |
free(libName); |
|
|
|
/* We promised the pointer would be set up at the next non-space, so |
/* We promised the pointer would be set up at the next non-space, so |
* we must advance cp there before setting *linePtr... (note that on |
* we must advance cp there before setting *linePtr... (note that on |
|
|
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 |
|
|
|
|
for (;;) { |
for (;;) { |
size_t n; |
size_t n; |
struct ar_hdr arHeader; |
struct ar_hdr arHeader; /* Archive-member header */ |
/* Archive-member header for reading archive */ |
off_t size; /* Size of archive member */ |
off_t size; /* Size of archive member */ |
|
char buffer[PATH_MAX]; |
char buffer[PATH_MAX]; |
char *memberName; |
char *memberName; /* Current member name while hashing. */ |
/* Current member name while hashing. */ |
|
char *cp; |
char *cp; |
|
|
memberName = buffer; |
memberName = buffer; |
|
|
|
|
if (memcmp(arHeader.ar_fmag, ARFMAG, sizeof(arHeader.ar_fmag)) |
if (memcmp(arHeader.ar_fmag, ARFMAG, sizeof(arHeader.ar_fmag)) |
!= 0) { |
!= 0) { |
/* The header is bogus. */ |
/* header is bogus. */ |
break; |
break; |
} else { |
} else { |
/* We need to advance the stream's pointer to the start |
/* We need to advance the stream's pointer to the start |
|
|
/* SVR4 magic mode. */ |
/* SVR4 magic mode. */ |
memberName = ArchSVR4Entry(&list, memberName, |
memberName = ArchSVR4Entry(&list, memberName, |
size, arch); |
size, arch); |
if (memberName == NULL) /* Invalid data */ |
if (memberName == NULL) |
|
/* Invalid data */ |
break; |
break; |
else if (memberName == svr4list) |
else if (memberName == svr4list) |
/* List of files entry */ |
/* List of files entry */ |
continue; |
continue; |
/* Got the entry. */ |
/* Got the entry. */ |
/* XXX this assumes further processing, such as |
/* XXX this assumes further processing, such as |
|
|
*/ |
*/ |
static TIMESTAMP |
static TIMESTAMP |
ArchMTimeMember( |
ArchMTimeMember( |
const char *archive, /* Path to the archive */ |
const char *archive, /* Path to the archive */ |
const char *member, /* Name of member. If it is a path, only the |
const char *member, /* Name of member. If it is a path, only the |
* last component is used. */ |
* last component is used. */ |
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; |
|
|
struct arch_member *he; |
struct arch_member *he; |
end = NULL; |
end = NULL; |
|
|
he = ohash_find(&ar->members, |
he = ohash_find(&ar->members, ohash_qlookupi(&ar->members, |
ohash_qlookupi(&ar->members, member, &end)); |
member, &end)); |
if (he != NULL) |
if (he != NULL) |
return mtime_of_member(he); |
return mtime_of_member(he); |
else { |
else { |
|
|
if (entry >= l->fnamesize) { |
if (entry >= l->fnamesize) { |
if (DEBUG(ARCH)) |
if (DEBUG(ARCH)) |
printf("SVR4 entry offset /%s is greater than %lu\n", |
printf("SVR4 entry offset /%s is greater than %lu\n", |
name, (u_long)l->fnamesize); |
name, (u_long)l->fnamesize); |
return NULL; |
return NULL; |
} |
} |
|
|
|
|
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; |
|
|
|
|
/* 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, |
if (memcmp(arHeaderPtr->ar_fmag, ARFMAG, |
|
|
*/ |
*/ |
if (memberName[0] == '/') { |
if (memberName[0] == '/') { |
/* svr4 magic mode. */ |
/* svr4 magic mode. */ |
memberName = ArchSVR4Entry(&list, |
memberName = ArchSVR4Entry(&list, arHeaderPtr->ar_name, |
arHeaderPtr->ar_name, size, arch); |
size, arch); |
if (memberName == NULL) /* Invalid data */ |
if (memberName == NULL) |
|
/* Invalid data */ |
break; |
break; |
else if (memberName == svr4list) |
else if (memberName == svr4list) |
/* List of files entry */ |
/* List of files entry */ |
continue; |
continue; |
/* Got the entry. */ |
/* Got the entry. */ |
if (strcmp(memberName, member) == 0) { |
if (strcmp(memberName, member) == 0) { |
|
|
|
|
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", |
snprintf(arHeader.ar_date, sizeof(arHeader.ar_date), |
(long) timestamp2time_t(now)); |
"%-12ld", (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); |
|
|
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 |
} |
} |
|
|