waterson%netscape.com c60001ca16 Be sure to initialize the data offset in the new state object. Mucho gracias to shaver for the fix.
git-svn-id: svn://10.0.0.236/trunk@31072 18797224-902f-48f8-a5cc-f745e15eee43
1999-05-11 04:39:40 +00:00

699 lines
22 KiB
C

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
/*
* A utility for linking multiple typelib files.
*/
#include "xpt_xdr.h"
#include <stdio.h>
#ifdef XP_MAC
#include <stat.h>
#else
#include <sys/stat.h>
#endif
#include <stdlib.h>
#include <string.h>
#include "prlong.h"
#ifndef NULL
#define NULL (void *) 0
#endif
/* Forward declarations. */
typedef struct fixElement fixElement;
static int compare_IDEs_by_IID(const void *ap, const void *bp);
static int compare_IDE_with_zero(const void *ap);
static int compare_IDEs_by_name(const void *ap, const void *bp);
static int compare_IDEs_by_name_space(const void *ap, const void *bp);
static int compare_strings(const void *ap, const void *bp);
static int compare_fixElements_by_IID(const void *ap, const void *bp);
static int compare_fixElements_by_name(const void *ap, const void *bp);
static int compare_IIDs(const void *ap, const void *bp);
PRBool shrink_IDE_array(XPTInterfaceDirectoryEntry *ide,
int element_to_delete, int num_interfaces);
PRBool update_fix_array(fixElement *fix, int element_to_delete,
int num_interfaces, int replacement);
static int get_new_index(const fixElement *fix, int num_elements,
int target_file, int target_interface);
PRBool copy_IDE(XPTInterfaceDirectoryEntry *from,
XPTInterfaceDirectoryEntry *to);
PRBool copy_fixElement(fixElement *from, fixElement *to);
static void print_IID(struct nsID *iid, FILE *file);
static void xpt_link_usage(char *argv[]);
struct fixElement {
nsID iid;
char* name;
int file_num;
int interface_num;
PRBool is_deleted;
/* These next two variables will point you to the substitute interface
* if this one has been deleted.
*/
int maps_to_file_num;
int maps_to_interface_num;
};
/* Global variables. */
int trueNumberOfInterfaces = 0;
int totalNumberOfInterfaces = 0;
#if defined(XP_MAC) && defined(XPIDL_PLUGIN)
#define main xptlink_main
int xptlink_main(int argc, char *argv[]);
extern size_t mac_get_file_length(const char* filename);
#define get_file_length mac_get_file_length
#else
static size_t get_file_length(const char* filename)
{
struct stat file_stat;
if (stat(filename, &file_stat) != 0) {
perror("FAILED: get_file_length");
exit(1);
}
return file_stat.st_size;
}
#endif /* XP_MAC && XPIDL_PLUGIN */
int
main(int argc, char **argv)
{
XPTState *state;
XPTCursor curs, *cursor = &curs;
XPTHeader *header;
XPTInterfaceDirectoryEntry *newIDE, *IDE_array;
XPTInterfaceDescriptor *id;
XPTTypeDescriptor *td;
XPTAnnotation *ann, *first_ann;
PRUint32 header_sz, len;
size_t flen = 0;
char *head, *data, *whole;
FILE *in, *out;
fixElement *newFix, *fix_array;
int i,j;
int k = 0;
if (argc < 3) {
xpt_link_usage(argv);
return 1;
}
/* We're going to keep annotations now, so we'll start off with one
* that will let people know that the resultant type library file
* was generated by this tool (xpt_link).
*/
first_ann = XPT_NewAnnotation(XPT_ANN_LAST | XPT_ANN_PRIVATE,
XPT_NewStringZ("xpt_link"),
XPT_NewStringZ("This is a linked type library file created by xpt_link."));
for (i=2; i<argc; i++) {
flen = get_file_length(argv[i]);
in = fopen(argv[i], "rb");
if (!in) {
perror("FAILED: fopen");
return 1;
}
whole = malloc(flen);
if (!whole) {
perror("FAILED: malloc for whole");
return 1;
}
if (flen > 0) {
size_t rv = fread(whole, 1, flen, in);
if (rv < 0) {
perror("FAILED: reading typelib file");
return 1;
}
if (rv < flen) {
fprintf(stderr, "short read (%d vs %d)! ouch!\n", rv, flen);
return 1;
}
if (ferror(in) != 0 || fclose(in) != 0) {
perror("FAILED: Unable to read typelib file.\n");
return 1;
}
state = XPT_NewXDRState(XPT_DECODE, whole, flen);
if (!XPT_MakeCursor(state, XPT_HEADER, 0, cursor)) {
perror("FAILED: XPT_MakeCursor");
return 1;
}
if (!XPT_DoHeader(cursor, &header)) {
perror("FAILED: XPT_DoHeader");
return 1;
}
totalNumberOfInterfaces += header->num_interfaces;
if (k == 0) {
IDE_array = XPT_CALLOC(totalNumberOfInterfaces * sizeof(XPTInterfaceDirectoryEntry));
fix_array = XPT_CALLOC(totalNumberOfInterfaces * sizeof(fixElement));
} else {
newIDE = XPT_REALLOC(IDE_array, totalNumberOfInterfaces * sizeof(XPTInterfaceDirectoryEntry));
newFix = XPT_REALLOC(fix_array, totalNumberOfInterfaces * sizeof(fixElement));
if (!newIDE) {
perror("FAILED: XPT_REALLOC of IDE_array");
return 1;
}
if (!newFix) {
perror("FAILED: XPT_REALLOC of newFix");
return 1;
}
IDE_array = newIDE;
fix_array = newFix;
}
for (j=0; j<header->num_interfaces; j++) {
if (!copy_IDE(&header->interface_directory[j],
&IDE_array[k])) {
perror("FAILED: 1st copying of IDE");
return 1;
}
fix_array[k].iid = IDE_array[k].iid;
fix_array[k].name = IDE_array[k].name;
fix_array[k].file_num = i-2;
fix_array[k].interface_num = j+1;
fix_array[k].is_deleted = PR_FALSE;
fix_array[k].maps_to_file_num = i-2;
fix_array[k].maps_to_interface_num = j+1;
k++;
}
/* Copy the annotations.
*/
ann = first_ann;
while (ann->next != NULL) {
ann = ann->next;
}
if (header->annotations != NULL) {
ann->next = header->annotations;
}
XPT_FREEIF(header);
if (state)
XPT_DestroyXDRState(state);
free(whole);
flen = 0;
} else {
fclose(in);
perror("FAILED: file length <= 0");
return 1;
}
}
/* Make sure the last annotation is the only one marked as XP_ANN_LAST.
*/
ann = first_ann;
while (ann->next != NULL) {
ann->flags &= ~XPT_ANN_LAST;
ann = ann->next;
}
ann->flags |= XPT_ANN_LAST;
/* Sort both IDE_array and fix_array by name so we can check for
* name_space::name collisions.
*/
qsort(IDE_array,
totalNumberOfInterfaces,
sizeof(XPTInterfaceDirectoryEntry),
compare_IDEs_by_name);
qsort(fix_array,
totalNumberOfInterfaces,
sizeof(fixElement),
compare_fixElements_by_name);
/* trueNumberOfInterfaces == number of interfaces left after deletions
* are made. Initialize it here to be the same as the total number of
* interfaces we'ce encountered thus far.
*/
trueNumberOfInterfaces = totalNumberOfInterfaces;
/* Iterate through the sorted interfaces. Start at one so we don't
* accidentally walk off the end of the array.
*/
i = 1;
while (i != trueNumberOfInterfaces) {
/* Check for name_space::name collision.
*/
if (compare_IDEs_by_name(&IDE_array[i-1], &IDE_array[i]) == 0 &&
compare_IDEs_by_name_space(&IDE_array[i-1], &IDE_array[i]) == 0) {
/* If one of the interfaces is unresolved, delete that one
* preferentailly.
*/
if (!IDE_array[i-1].interface_descriptor) {
/* Shrink the IDE_array to delete the duplicate interface.
*/
if (!shrink_IDE_array(IDE_array,
i-1,
trueNumberOfInterfaces)) {
perror("FAILED: shrink_IDE_array");
return 1;
}
/* Update the fix array. This involves moving the deleted
* entry to the end of the array (rather than deleting it)
* and mapping it to the "replacement" element so we can
* update interface indices appropriately later.
*/
update_fix_array(fix_array, i-1, totalNumberOfInterfaces, i);
/* Decrement the true number of interfaces since we just
* deleted one. There's more than one way to get out of
* this loop.
*/
trueNumberOfInterfaces--;
} else {
if (!IDE_array[i].interface_descriptor ||
(compare_IIDs(&IDE_array[i-1].iid, &IDE_array[i].iid) == 0)) {
/* Shrink the IDE_array to delete the duplicate interface.
*/
if (!shrink_IDE_array(IDE_array,
i,
trueNumberOfInterfaces)) {
perror("FAILED: shrink_IDE_array");
return 1;
}
/* Update the fix array. This involves moving the deleted
* entry to the end of the array (rather than deleting it)
* and mapping it to the "replacement" element so we can
* update interface indices appropriately later.
*/
update_fix_array(fix_array, i,
totalNumberOfInterfaces, i-1);
/* Decrement the true number of interfaces since we just
* deleted one. There's more than one way to get out of
* this loop.
*/
trueNumberOfInterfaces--;
}
}
} else {
/* Only increment if there was no name_space::name collision.
*/
i++;
}
}
/* Sort the IDE_array (put them in their final order) so that updating
* of indices will be meaningful.
*/
qsort(IDE_array,
trueNumberOfInterfaces,
sizeof(XPTInterfaceDirectoryEntry),
compare_IDEs_by_IID);
/* Sort the fix_array to match the IDE_array.
*/
qsort(fix_array,
trueNumberOfInterfaces,
sizeof(fixElement),
compare_fixElements_by_IID);
/* Iterate through the remaining interfaces (those not deleted)
* looking for references to interfaces (such as id->parent_interface)
* which need an updated index number.
*/
for (i=0; i<trueNumberOfInterfaces; i++) {
/* Define id to save some keystrokes.
*/
id = IDE_array[i].interface_descriptor;
/* Check for unresolved interface.
*/
if (id) {
/* Fix parent_interface first.
*/
if (id->parent_interface && id->parent_interface != 0) {
id->parent_interface =
get_new_index(fix_array, totalNumberOfInterfaces,
fix_array[i].file_num, id->parent_interface);
}
/* Iterate through the method descriptors looking for params of
* type TD_INTERFACE_TYPE.
*/
for (j=0; j<id->num_methods; j++) {
/* Cycle through the params first.
*/
for (k=0; k<id->method_descriptors[j].num_args; k++) {
/* Define td to save some keystrokes.
*/
td = &id->method_descriptors[j].params[k].type;
if (XPT_TDP_TAG(td->prefix) == TD_INTERFACE_TYPE) {
td->type.interface =
get_new_index(fix_array,
totalNumberOfInterfaces,
fix_array[i].file_num,
td->type.interface);
}
}
/* Check the result param too. Define td again to save
* some keystrokes.
*/
td = &id->method_descriptors[j].result->type;
if (XPT_TDP_TAG(td->prefix) == TD_INTERFACE_TYPE) {
td->type.interface =
get_new_index(fix_array, totalNumberOfInterfaces,
fix_array[i].file_num,
td->type.interface);
}
}
}
}
/* Iterate through the array quickly looking for duplicate IIDS.
* This shouldn't happen, i.e. is a failure condition, so bail
* if we find a duplicate. If we have more than one entry, start
* at one so we don't accidentally grep the ether.
*/
if (trueNumberOfInterfaces>1) {
for (i=1; i<trueNumberOfInterfaces; i++) {
/* Only complain if the IIDs are identical and nonzero. */
if (compare_IDEs_by_IID(&IDE_array[i-1], &IDE_array[i]) == 0 &&
compare_IDE_with_zero(&IDE_array[i]) != 0) {
fprintf(stderr, "FATAL ERROR:\n"
"Duplicate IID detected (");
print_IID(&IDE_array[i-1].iid, stderr);
fprintf(stderr, ") in\n"
"interface %s::%s from %s\n"
"and\n"
"interface %s::%s from %s\n",
IDE_array[i-1].name_space ?
IDE_array[i-1].name_space : "",
IDE_array[i-1].name,
argv[fix_array[i-1].file_num+2],
IDE_array[i].name_space ?
IDE_array[i].name_space : "",
IDE_array[i].name,
argv[fix_array[i].file_num+2]);
return 1;
}
}
}
header = XPT_NewHeader((PRUint16)trueNumberOfInterfaces);
header->annotations = first_ann;
for (i=0; i<trueNumberOfInterfaces; i++) {
if (!copy_IDE(&IDE_array[i], &header->interface_directory[i])) {
perror("FAILED: 2nd copying of IDE");
return 1;
}
}
header_sz = XPT_SizeOfHeaderBlock(header);
state = XPT_NewXDRState(XPT_ENCODE, NULL, 0);
if (!state) {
perror("FAILED: error creating XDRState");
return 1;
}
XPT_SetDataOffset(state, header_sz);
if (!XPT_MakeCursor(state, XPT_HEADER, header_sz, cursor)) {
perror("FAILED: error making cursor");
return 1;
}
if (!XPT_DoHeader(cursor, &header)) {
perror("FAILED: error doing Header");
return 1;
}
out = fopen(argv[1], "wb");
if (!out) {
perror("FAILED: fopen");
return 1;
}
XPT_GetXDRData(state, XPT_HEADER, &head, &len);
fwrite(head, len, 1, out);
XPT_GetXDRData(state, XPT_DATA, &data, &len);
fwrite(data, len, 1, out);
if (ferror(out) != 0 || fclose(out) != 0) {
fprintf(stderr, "Error writing file: %s\n", argv[1]);
} else {
/* fprintf(stderr, "File written: %s\n", argv[1]); */
}
if (state)
XPT_DestroyXDRState(state);
return 0;
}
static int
compare_IDEs_by_IID(const void *ap,
const void *bp)
{
const XPTInterfaceDirectoryEntry *ide1 = ap, *ide2 = bp;
return compare_IIDs(&ide1->iid, &ide2->iid);
}
/* For detecting unresolved interfaces. */
const nsID iid_zero = { 0x0, 0x0, 0x0,
{ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 } };
static int
compare_IDE_with_zero(const void *ap)
{
const XPTInterfaceDirectoryEntry *ide1 = ap;
return compare_IIDs(&ide1->iid, &iid_zero);
}
static int
compare_fixElements_by_IID(const void *ap,
const void *bp)
{
const fixElement *fix1 = ap, *fix2 = bp;
return compare_IIDs(&fix1->iid, &fix2->iid);
}
static int
compare_IDEs_by_name(const void *ap,
const void *bp)
{
const XPTInterfaceDirectoryEntry *ide1 = ap, *ide2 = bp;
return compare_strings(ide1->name, ide2->name);
}
static int
compare_IDEs_by_name_space(const void *ap,
const void *bp)
{
const XPTInterfaceDirectoryEntry *ide1 = ap, *ide2 = bp;
return compare_strings(ide1->name_space, ide2->name_space);
}
static int
compare_strings(const void *ap, const void *bp)
{
const char *string1 = ap, *string2 = bp;
if (!string1 && !string2)
return 0;
if (!string1)
return -1;
if (!string2)
return 1;
return strcmp(string1, string2);
}
static int
compare_fixElements_by_name(const void *ap,
const void *bp)
{
const fixElement *fix1 = ap, *fix2 = bp;
return compare_strings(fix1->name, fix2->name);
}
static int
compare_IIDs(const void *ap, const void *bp)
{
const nsID *a = ap, *b = bp;
int i;
#define COMPARE(field) if (a->field > b->field) return 1; \
if (b->field > a->field) return -1;
COMPARE(m0);
COMPARE(m1);
COMPARE(m2);
for (i = 0; i < 8; i++) {
COMPARE(m3[i]);
}
return 0;
#undef COMPARE
}
PRBool
shrink_IDE_array(XPTInterfaceDirectoryEntry *ide, int element_to_delete,
int num_interfaces)
{
int i;
if (element_to_delete >= num_interfaces) {
return PR_FALSE;
}
for (i=element_to_delete+1; i<num_interfaces; i++) {
if (!copy_IDE(&ide[i], &ide[i-1])) {
return PR_FALSE;
}
}
return PR_TRUE;
}
/* update_fix_array marks a fixElement as deleted, updates its mapping
* to point to the "replacement" element, and moves it to the end of
* the array.
*/
PRBool
update_fix_array(fixElement *fix, int element_to_delete,
int num_interfaces, int replacement)
{
fixElement *deleted;
int i;
if (element_to_delete >= num_interfaces) {
return PR_FALSE;
}
deleted = XPT_CALLOC(sizeof(fixElement));
if (!copy_fixElement(&fix[element_to_delete], deleted)) {
return PR_FALSE;
}
deleted->is_deleted = PR_TRUE;
deleted->maps_to_file_num = fix[replacement].file_num;
deleted->maps_to_interface_num = fix[replacement].interface_num;
for (i=element_to_delete+1; i<num_interfaces; i++) {
if (!copy_fixElement(&fix[i], &fix[i-1])) {
return PR_FALSE;
}
}
if (!copy_fixElement(deleted, &fix[num_interfaces-1])) {
return PR_FALSE;
}
return PR_TRUE;
}
/* get_new_index returns the new interface index by walking the fix_array
* until the file_num and interface_num match the target values. If a match
* is found, it checks to see if that element has been deleted. If it has
* been deleted, it follows that elements mapping until it gets to the
* proper interface (recursion). FYI, Indices are one-based; zero
* represents the special case (no parent interface).
*/
static int
get_new_index(const fixElement *fix, int num_elements,
int target_file, int target_interface)
{
int i;
for (i=0; i<num_elements; i++) {
if (fix[i].file_num == target_file &&
fix[i].interface_num == target_interface) {
if (fix[i].is_deleted) {
return get_new_index(fix, num_elements,
fix[i].maps_to_file_num,
fix[i].maps_to_interface_num);
}
return i+1;
}
}
return 0;
}
PRBool
copy_IDE(XPTInterfaceDirectoryEntry *from, XPTInterfaceDirectoryEntry *to)
{
if (!from || !to) {
return PR_FALSE;
}
to->iid = from->iid;
to->name = from->name;
to->name_space = from->name_space;
to->interface_descriptor = from->interface_descriptor;
return PR_TRUE;
}
PRBool
copy_fixElement(fixElement *from, fixElement *to)
{
if (!from || !to) {
return PR_FALSE;
}
to->iid = from->iid;
to->name = from->name;
to->file_num = from->file_num;
to->interface_num = from->interface_num;
to->is_deleted = from->is_deleted;
to->maps_to_file_num = from->maps_to_file_num;
to->maps_to_interface_num = from->maps_to_interface_num;
return PR_TRUE;
}
static void
print_IID(struct nsID *iid, FILE *file)
{
fprintf(file, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
(PRUint32) iid->m0, (PRUint32) iid->m1,(PRUint32) iid->m2,
(PRUint32) iid->m3[0], (PRUint32) iid->m3[1],
(PRUint32) iid->m3[2], (PRUint32) iid->m3[3],
(PRUint32) iid->m3[4], (PRUint32) iid->m3[5],
(PRUint32) iid->m3[6], (PRUint32) iid->m3[7]);
}
static void
xpt_link_usage(char *argv[])
{
fprintf(stdout, "Usage: %s outfile file1.xpt file2.xpt ...\n"
" Links multiple typelib files into one outfile\n", argv[0]);
}