Source file: /~heha/hs/gputils64-210929.zip/libgputils/gparchive.cpp

/* GNU PIC archive functions
   Copyright 2001-2005	Craig Franklin
*/

#include "stdhdr.h"
#include "libgputils.h"

/*------------------------------------------------------------------------------------------------*/

/* FIXME: member headers always start on an even-byte boundary. A newline
   character is often used to fill the gap. */

unsigned gp_archive_count_members(const gp_archive_t *Archive) {
  unsigned number = 0;

  /* If present, skip the symbol index. */
  if (gp_archive_have_index(Archive)) {
    Archive = Archive->next;
  }

  while (Archive) {
    number++;
    Archive = Archive->next;
  }

  return number;
}

/*------------------------------------------------------------------------------------------------*/

FUNC(char*) gp_archive_member_name(const gp_archive_t *Archive) {
  char  name[AR_MEM_NAME_SIZ];
  char *end;

  sscanf(Archive->header.ar_name, "%255s/", name);
  end = strchr(name, '/');
  if (end) {
    *end = '\0';
  }

  return GP_Strdup(name);
}

/*------------------------------------------------------------------------------------------------*/

FUNC(void) gp_archive_list_members(const gp_archive_t *Archive) {
  char    name[AR_MEM_NAME_SIZ];
  char   *end;
  int     date;
  time_t  time;
  int     size;

  /* If present, skip the symbol index. */
  if (gp_archive_have_index(Archive)) {
    Archive = Archive->next;
  }

  while (Archive) {
    sscanf(Archive->header.ar_name, "%255s/", name);
    sscanf(Archive->header.ar_date, "%il", &date);
    sscanf(Archive->header.ar_size, "%il", &size);
    end = strchr(&name[0], '/');
    if (end) {
      *end = '\0';
    }
    time = date;
    printf("%-24s  %06i bytes  %s", name, size, ctime(&time));
    Archive = Archive->next;
  }
}

/*------------------------------------------------------------------------------------------------*/

FUNC(gp_archive_t*) gp_archive_find_member(gp_archive_t *Archive, const char *Object_name) {
  char          name[AR_MEM_NAME_SIZ];
  char         *end;
  gp_archive_t *found;

  /* If present, skip the symbol index. */
  if (gp_archive_have_index(Archive)) {
    Archive = Archive->next;
  }

  found = NULL;
  while (Archive) {
    sscanf(Archive->header.ar_name, "%255s/", name);
    end = strrchr(name, '/');
    if (end) {
      *end = '\0';
    }

    if (strcmp(Object_name, name) == 0) {
      found = Archive;
      break;
    }
    Archive = Archive->next;
  }

  return found;
}

/*------------------------------------------------------------------------------------------------*/

void
gp_archive_free_member(gp_archive_t *Archive)
{
  if (Archive->data.file) {
    free(Archive->data.file);
  }

  if (Archive) {
    free(Archive);
  }
}

/*------------------------------------------------------------------------------------------------*/

bool
gp_archive_rename_member(gp_archive_t *Archive, const char *Object_name_old, const char *Object_name_new)
{
  gp_archive_t *object_old;
  gp_archive_t *object_new;

  object_new = gp_archive_find_member(Archive, Object_name_new);
  if (object_new) {
    /* With such name already exists a object. */
    return false;
  }

  object_old = gp_archive_find_member(Archive, Object_name_old);
  assert(object_old);

  memset(object_old->header.ar_name, ' ', sizeof(object_old->header.ar_name));
  gp_arch_strncpy(object_old->header.ar_name, Object_name_new,  sizeof(object_old->header.ar_name));

  return true;
}

/*------------------------------------------------------------------------------------------------*/

FUNC(gp_archive_t*) gp_archive_delete_member(gp_archive_t *Archive, const char *Object_name) {
  gp_archive_t *object;
  gp_archive_t *list;

  object = gp_archive_find_member(Archive, Object_name);
  assert(object);

  if (object == Archive) {
    /* the first object in the list is being deleted */
    Archive = Archive->next;
  }
  else {
    /* locate and remove the member */
    list = Archive;
    while (list) {
      if (list->next == object) {
        list->next = object->next;
        break;
      }
      list = list->next;
    }
  }

  gp_archive_free_member(object);

  return Archive;
}

/*------------------------------------------------------------------------------------------------*/

FUNC(gp_archive_t*) gp_archive_add_member(gp_archive_t *Archive, const char *File_name, const char *Object_name) {
  gp_archive_t *old_member;
  gp_archive_t *list;
  int           timer;

  gp_binary_t*new_object = gp_read_file(File_name);
  gp_archive_t*new_member = new gp_archive_t;
  new_member->next = NULL;

  /* Point the archive member file to the object file. The object is never
     freed, so this is ok. It will be cleaned up later. */
  new_member->data = *new_object;

  /* fill in the archive header */
  memset(&new_member->header, ' ', AR_HDR_SIZ); /* fill the header with space */

  timer = (int)time(NULL);

  char name[AR_MEM_NAME_SIZ];
  snprintf(name, sizeof(name), "%s/", Object_name);
  char date[AR_MEM_DATE_SIZ];
  snprintf(date, sizeof(date), "%il", timer);
  char size[AR_MEM_FSIZE_SIZ];
  snprintf(size, sizeof(size), "%" OFF_FMTu, new_object->size);

  gp_arch_strncpy(new_member->header.ar_name, name,  sizeof(new_member->header.ar_name));
  gp_arch_strncpy(new_member->header.ar_date, date,  sizeof(new_member->header.ar_date));
  gp_arch_strncpy(new_member->header.ar_size, size,  sizeof(new_member->header.ar_size));
  gp_arch_strncpy(new_member->header.ar_fmag, ARMAG, sizeof(new_member->header.ar_fmag));

  old_member = gp_archive_find_member(Archive, Object_name);

  if (old_member) {
    /* the object already exists, replace it */
    Archive = gp_archive_delete_member(Archive, Object_name);
  }

  if (Archive == NULL) {
    /* the list is empty */
    Archive = new_member;
  }
  else {
    /* append the pnew object to the end of the list */
    list = Archive;
    while (list->next) {
      list = list->next;
    }
    list->next = new_member;
  }

  return Archive;
}

/*------------------------------------------------------------------------------------------------*/

FUNC(bool) gp_archive_extract_member(gp_archive_t *Archive, const char *Object_name) {
  gp_archive_t *object;
  char          file_name[AR_MEM_NAME_SIZ];
  FILE         *output_file;
  int           size;

  object = gp_archive_find_member(Archive, Object_name);
  assert(object);

  /* if the object doesn't have an extension, add one.  This is done for
     some libs generated with other tools.  It should not be necessary
     for libs generated by gplib. */
  snprintf(file_name, sizeof(file_name), "%s%s", Object_name, (strrchr(Object_name, '.') == NULL) ? ".o" : "");

  output_file = fopen(file_name, "wb");
  if (output_file == NULL) {
    perror(file_name);
    return false;
  }

  sscanf(object->header.ar_size, "%il", &size);
  fwrite(object->data.file, 1, size, output_file);

  fclose(output_file);

  /* FIXME: put the proper date on the output file */

  return true;
}

/*------------------------------------------------------------------------------------------------*/

FUNC(bool) gp_archive_write(gp_archive_t *Archive, const char *Archive_name) {
  FILE *output_file;
  int   size;

  assert(Archive);

  output_file = fopen(Archive_name, "wb");
  if (output_file == NULL) {
    perror(Archive_name);
    return false;
  }

  fputs(ARMAG, output_file); /* write the archive magic number */

  while (Archive) {
    fwrite(&Archive->header, 1, AR_HDR_SIZ, output_file);
    sscanf(Archive->header.ar_size, "%il", &size);
    fwrite(Archive->data.file, 1, size, output_file);
    Archive = Archive->next;
  }

  fclose(output_file);
  return true;
}

/*------------------------------------------------------------------------------------------------*/

/* Update the offset numbers for the archive, this is only required
   if a symbol index is created. */

void
gp_archive_update_offsets(gp_archive_t *Archive)
{
  unsigned offset = SARMAG;
  int          size = 0;

  while (Archive) {
    Archive->offset = offset;
    sscanf(Archive->header.ar_size, "%il", &size);
    offset += AR_HDR_SIZ + size;
    Archive = Archive->next;
  }
}

/*------------------------------------------------------------------------------------------------*/

/* Read a coff archive and store it in memory. */

FUNC(gp_archive_t*) gp_archive_read(const char *File_name) {
  gp_archive_t *list;
  ar_hdr_t      tmp_header;
  fpos_t        position;
  int           object_size;

  FILE*infile = fopen(File_name, "rb");
  if (!infile) {
    perror(File_name);
    exit(1);
  }

  /* read the magic number */
  char buffer[SARMAG + 1];
  if (fread(buffer, 1, SARMAG, infile) != SARMAG || strncmp(buffer, ARMAG, SARMAG)) {
    fclose(infile);
    return NULL;
  }
  gp_archive_t*archive = NULL;
  while (!feof(infile)) {
    /* allocate space for the next archive member */
    gp_archive_t*pnew = new gp_archive_t;
    pnew->next = NULL;

    /* read the archive header */
    if (fread(&pnew->header, 1, AR_HDR_SIZ, infile) != AR_HDR_SIZ) {
      gp_error("bad archive \"%s\"", File_name);
    }

    /* read the object file or symbol index into memory */
    sscanf(pnew->header.ar_size, "%il", &object_size);
    pnew->data.size = object_size;
    pnew->data.file = new uint8_t[object_size];
    if ((int)fread(pnew->data.file, sizeof(char), object_size, infile) != object_size) {
      gp_error("bad archive \"%s\"", File_name);
    }

    /* insert the pnew member in the archive list */
    if (!archive) archive = pnew;	/* this is the first entry */
    else list->next = pnew;
    list = pnew;

    /* Some malformed libs have a couple of extra bytes on the end. The while eof
       test passes, but there are no other members. Test for it. */
    fgetpos(infile, &position);
    if (fread(&tmp_header, 1, AR_HDR_SIZ, infile) != AR_HDR_SIZ) {
      break;
    }
    fsetpos(infile, &position);
  }

  gp_archive_update_offsets(archive);

  fclose(infile);

  return archive;
}

/*------------------------------------------------------------------------------------------------*/

/* Determine if the archive has a symbol index. */

FUNC(bool) gp_archive_have_index(const gp_archive_t *Archive)
{
  return (((Archive) && (Archive->header.ar_name[0] == '/')) ? true : false);
}

/*------------------------------------------------------------------------------------------------*/

/* Remove the symbol index if one exists. */

FUNC(gp_archive_t*) gp_archive_remove_index(gp_archive_t *Archive) {
  gp_archive_t *member;

  /* If present, remove the symbol index. */
  if (gp_archive_have_index(Archive)) {
    member  = Archive;
    Archive = Archive->next;
    gp_archive_free_member(member);
  }

  return Archive;
}

/*------------------------------------------------------------------------------------------------*/

/* Create a symbol index for the archive. This is always done to make sure duplicate
   symbols don't get into the library. This data can and should be stored in the file.
   The only reason not to is if you need compatibility with other tools. */

FUNC(void) gp_archive_make_index(gp_archive_t *Archive, symbol_table_t *Definition) {
  gp_object_t *object;
  char         name[AR_MEM_NAME_SIZ];
  char        *end;

  /* If present, skip the symbol index. */
  if (gp_archive_have_index(Archive)) {
    Archive = Archive->next;
  }

  while (Archive) {
    sscanf(Archive->header.ar_name, "%255s/", name);
    end = strchr(&name[0], '/');
    if (end) {
      *end = '\0';
    }

    object = gp_convert_file(name, &Archive->data);
    assert(object);
    gp_cofflink_add_symbols(Definition, NULL, object);
    Archive = Archive->next;
  }
}

/*------------------------------------------------------------------------------------------------*/

/* Add the symbol index to the archive. */

FUNC(gp_archive_t*) gp_archive_add_index(symbol_table_t *Table, gp_archive_t *Archive) {
  if (!Archive || !Table) return NULL;

  size_t sym_count = gp_sym_get_symbol_count(Table);
  if (!sym_count) return Archive;

  /* Get a sorted list. */
  const symbol_t**lst = gp_sym_clone_symbol_array(Table, gp_sym_compare_fn);
  assert(lst);

  /* determine the symbol index size */
  long index_size = AR_INDEX_NUMBER_SIZ;
  size_t i;
  for (i = 0; i < sym_count; i++) {
    const char*sym_name = gp_sym_get_symbol_name(lst[i]);
    index_size += strlen(sym_name) + 1 + AR_INDEX_OFFSET_SIZ;
  }

  /* create a pnew member for the index and place it in the archive */
  gp_archive_t*new_member = new gp_archive_t;
  new_member->data.file = new uint8_t[index_size];
  new_member->data.size = index_size;
  new_member->next      = NULL;

  /* fill in the archive header */
  memset(&new_member->header, ' ', AR_HDR_SIZ); /* fill the header with space */

  new_member->header.ar_name[0] = '/';
  char size[AR_MEM_FSIZE_SIZ];
  snprintf(size, sizeof(size), "%lil", index_size);

  gp_arch_strncpy(new_member->header.ar_size, size,  sizeof(new_member->header.ar_size));
  gp_arch_strncpy(new_member->header.ar_fmag, ARMAG, sizeof(new_member->header.ar_fmag));

  new_member->next = Archive;
  Archive = new_member;

  /* recalculate the file offsets for the symbol table */
  gp_archive_update_offsets(Archive);

  /* write the number of symbols to the member */
  uint8_t*ptr = Archive->data.file;
  gp_putl32(ptr, (uint32_t)sym_count);
  ptr += 4;

  /* write the offsets to the member */
  for (i = 0; i < sym_count; i++) {
    const gp_coffsymbol_t*var = (const gp_coffsymbol_t*)gp_sym_get_symbol_annotation(lst[i]);
    gp_archive_t*member = gp_archive_find_member(Archive, var->file->filename);
    gp_putl32(ptr, member->offset);
    ptr += 4;
  }

  /* write the symbol names to the member */
  for (i = 0; i < sym_count; i++) {
    const char*sym_name = gp_sym_get_symbol_name(lst[i]);
    size_t sym_name_len = strlen(sym_name) + 1;

    memcpy(ptr, sym_name, sym_name_len);
    ptr += sym_name_len;
  }

  free((void*)lst);
  return Archive;
}

/*------------------------------------------------------------------------------------------------*/

/* Place the symbol from the archive symbol index in the archive symbol table. */

bool
gp_archive_add_symbol(symbol_table_t *Table, const char *Name, gp_archive_t *Member)
{
  symbol_t *sym;

  /* Search the for the symbol. If not found, then add it to the global symbol table. */
  sym = gp_sym_get_symbol(Table, Name);
  if (sym) {
    return true;
  }

  sym = gp_sym_add_symbol(Table, Name);
  gp_sym_annotate_symbol(sym, Member);
  return false;
}

/*------------------------------------------------------------------------------------------------*/

/* This function reads the symbol index in the archive. The symbols are placed in
   the archive symbol table.  This table stores the name of the symbol and
   the archive member that the symbol is defined in. */

FUNC(void) gp_archive_read_index(symbol_table_t *Table, gp_archive_t *Archive) {
  unsigned   number;
  unsigned   i;
  const char    *name;
  const uint8_t *offset;
  unsigned   offset_value;
  gp_archive_t  *list;
  const uint8_t *file;

  assert(gp_archive_have_index(Archive));

  file = Archive->data.file;

  /* read the number of symbols */
  number = gp_getl32(file);

  /* set the pointers to the offsets and symbol names */
  offset = &file[AR_INDEX_NUMBER_SIZ];
  name   = (const char *)(offset + (AR_INDEX_OFFSET_SIZ * number));

  for (i = 0; i < number; i++) {
    /* get the symbol offset from the symbol index */
    offset_value = gp_getl32(offset);

    /* Locate the object file the symbol is defined in. The both should have the same offset. */
    list = Archive;
    while (list) {
      if (list->offset == offset_value) {
        break;
      }
      list = list->next;
    }

    assert(list);

    /* add the symbol to the archive symbol table */
    gp_archive_add_symbol(Table, name, list);

    /* increment the pointers to the next symbol */
    name += strlen(name) + 1;
    offset += AR_INDEX_OFFSET_SIZ;
  }
}

/*------------------------------------------------------------------------------------------------*/

/* Print the archive symbol table. */

FUNC(void) gp_archive_print_table(const symbol_table_t *Table) {
  assert(Table);
  /* Get a sorted list. */
  const symbol_t**lst = gp_sym_clone_symbol_array(Table, gp_sym_compare_fn);
  assert(lst);
  size_t sym_count = gp_sym_get_symbol_count(Table);
  for (size_t i = 0; i < sym_count; i++) {
    /* determine the archive member the symbol is defined in */
    const gp_archive_t*member = (const gp_archive_t*)gp_sym_get_symbol_annotation(lst[i]);
    assert(member);
    /* determine the archive member name (AR_MEM_NAME_SIZ - 1) */
    char name[AR_MEM_NAME_SIZ];
    sscanf(member->header.ar_name, "%255s/", name);
    char*end = strchr(name, '/');
    if (end) *end = 0;
    /* print it */
    printf("%-32s %s\n", gp_sym_get_symbol_name(lst[i]), name);
  }
  free((void*)lst);
}
Detected encoding: ASCII (7 bit)2