/* arsup.c - Archive support for MRI compatibility
   Copyright (C) 1992, 93, 94, 95, 96, 97, 98, 99, 2000
   Free Software Foundation, Inc.

This file is part of GNU Binutils.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */


/* Contributed by Steve Chamberlain
   		  sac@cygnus.com

This file looks after requests from arparse.y, to provide the MRI
style librarian command syntax + 1 word LIST

*/

#include "bfd.h"
#include "arsup.h"
#include "libiberty.h"
#include "bucomm.h"
#include "filenames.h"

static void map_over_list
  PARAMS ((bfd *, void (*function) (bfd *, bfd *), struct list *));
static void ar_directory_doer PARAMS ((bfd *, bfd *));
static void ar_addlib_doer PARAMS ((bfd *, bfd *));

extern int verbose;

static void
map_over_list (arch, function, list)
     bfd *arch;
     void (*function) PARAMS ((bfd *, bfd *));
     struct list *list;
{
  bfd *head;

  if (list == NULL)
    {
      bfd *next;

      head = arch->next;
      while (head != NULL)
	{
	  next = head->next;
	  function (head, (bfd *) NULL);
	  head = next;
	}
    }
  else
    {
      struct list *ptr;

      /* This may appear to be a baroque way of accomplishing what we
	 want.  however we have to iterate over the filenames in order
	 to notice where a filename is requested but does not exist in
	 the archive.  Ditto mapping over each file each time -- we
	 want to hack multiple references.  */
      for (ptr = list; ptr; ptr = ptr->next)
	{
	  boolean found = false;
	  bfd *prev = arch;

	  for (head = arch->next; head; head = head->next) 
	    {
	      if (head->filename != NULL
		  && FILENAME_CMP (ptr->name, head->filename) == 0)
		{
		  found = true;
		  function (head, prev);
		}
	      prev = head;
	    }
	  if (! found)
	    fprintf (stderr, _("No entry %s in archive.\n"), ptr->name);
	}
    }
}


FILE *outfile;

/*ARGSUSED*/
static void
ar_directory_doer (abfd, ignore)
     bfd *abfd;
     bfd *ignore ATTRIBUTE_UNUSED;
{
    print_arelt_descr(outfile, abfd, verbose);
}

void
ar_directory (ar_name, list, output)
     char *ar_name;
     struct list *list;
     char *output;
{
  bfd *arch;

  arch = open_inarch (ar_name, (char *) NULL);
  if (output)
    {
      outfile = fopen(output,"w");
      if (outfile == 0)
	{
	  outfile = stdout;
	  fprintf (stderr,_("Can't open file %s\n"), output);
	  output = 0;
	}
    }
  else 
    outfile = stdout;

  map_over_list (arch, ar_directory_doer, list);

  bfd_close (arch);

  if (output)
   fclose (outfile);
}

void
DEFUN_VOID(prompt)
{
  extern int interactive;
  if (interactive) 
  {
    printf("AR >");
    fflush(stdout); 
  }
}

void
maybequit ()
{
  if (! interactive) 
    xexit (9);
}


bfd *obfd;
char *real_name ; 
void
DEFUN(ar_open,(name, t),
      char *name AND
      int t)

{
  char *tname = (char *) xmalloc (strlen (name) + 10);
  real_name = name;
  /* Prepend tmp- to the beginning, to avoid file-name clashes after
     truncation on filesystems with limited namespaces (DOS).  */
  sprintf(tname, "tmp-%s", name);
  obfd = bfd_openw(tname, NULL);

  if (!obfd) {
    fprintf(stderr,_("%s: Can't open output archive %s\n"), program_name,
	    tname);

    maybequit();
  }
  else {
    if (!t) {
      bfd **ptr;
      bfd *element;
      bfd *ibfd;
      ibfd = bfd_openr(name, NULL);
      if (!ibfd) {
	fprintf(stderr,_("%s: Can't open input archive %s\n"),
		program_name, name);
	maybequit();
	return;
      }
      if (bfd_check_format(ibfd, bfd_archive) != true) {
	fprintf(stderr,_("%s: file %s is not an archive\n"), program_name,
		name);
	maybequit();
	return;
      }
      ptr = &(obfd->archive_head);
      element = bfd_openr_next_archived_file(ibfd, NULL);

      while (element) {
	*ptr = element;
	ptr = &element->next;
	element = bfd_openr_next_archived_file(ibfd, element);
      }
    }

    bfd_set_format(obfd, bfd_archive);

    obfd->has_armap = 1;
  }
}


static void
ar_addlib_doer (abfd, prev)
     bfd *abfd;
     bfd *prev;
{
  /* Add this module to the output bfd */
  if (prev != NULL)
    prev->next = abfd->next;
  abfd->next = obfd->archive_head;
  obfd->archive_head = abfd;
}

void
ar_addlib (name, list)
     char *name;
     struct list *list;
{
  if (obfd == NULL)
    {
      fprintf (stderr, _("%s: no output archive specified yet\n"), program_name);
      maybequit ();
    }
  else
    {
      bfd *arch;

      arch = open_inarch (name, (char *) NULL);
      if (arch != NULL)
	map_over_list (arch, ar_addlib_doer, list);

      /* Don't close the bfd, since it will make the elements disasppear */
    }
}

void
DEFUN(ar_addmod, (list),
      struct list *list)
{
  if (!obfd) {
    fprintf(stderr, _("%s: no open output archive\n"), program_name);
    maybequit();
  }
  else 
  {
    while (list) {
      bfd *abfd = bfd_openr(list->name, NULL);
      if (!abfd)  {
	fprintf(stderr,_("%s: can't open file %s\n"), program_name,
		list->name);
	maybequit();
      }
      else {
	abfd->next = obfd->archive_head;
	obfd->archive_head = abfd;
      }
      list = list->next;
    }
  }
}



void
DEFUN_VOID(ar_clear)
{
if (obfd) 
 obfd->archive_head = 0;
}

void
DEFUN(ar_delete, (list),
      struct list *list)
{
  if (!obfd) {
    fprintf(stderr, _("%s: no open output archive\n"), program_name);
    maybequit();
  }
  else 
  {
    while (list) {
      /* Find this name in the archive */
      bfd *member = obfd->archive_head;
      bfd **prev = &(obfd->archive_head);
      int found = 0;
      while (member) {
	if (FILENAME_CMP(member->filename, list->name) == 0) {
	  *prev = member->next;
	  found = 1;
	}
	else {
	  prev = &(member->next);
	}
	  member = member->next;
      }
      if (!found)  {
	fprintf(stderr,_("%s: can't find module file %s\n"), program_name,
		list->name);
	maybequit();
      }
      list = list->next;
    }
  }
}


void
DEFUN_VOID(ar_save)
{

  if (!obfd) {
    fprintf(stderr, _("%s: no open output archive\n"), program_name);
    maybequit();
  }
  else {
    char *ofilename = xstrdup (bfd_get_filename (obfd));
    bfd_close(obfd);
    
    rename (ofilename, real_name);
    obfd = 0;
    free(ofilename);
  }
}



void
DEFUN(ar_replace, (list),
      struct list *list)
{
  if (!obfd) {
    fprintf(stderr, _("%s: no open output archive\n"), program_name);
    maybequit();
  }
  else 
  {
    while (list) {
      /* Find this name in the archive */
      bfd *member = obfd->archive_head;
      bfd **prev = &(obfd->archive_head);
      int found = 0;
      while (member) 
      {
	if (FILENAME_CMP(member->filename, list->name) == 0) 
	{
	  /* Found the one to replace */
	  bfd *abfd = bfd_openr(list->name, 0);
	  if (!abfd) 
	  {
	    fprintf(stderr, _("%s: can't open file %s\n"), program_name, list->name);
	    maybequit();
	  }
	  else {
	    *prev = abfd;
	    abfd->next = member->next;
	    found = 1;
	  }
	}
	else {
	  prev = &(member->next);
	}
	member = member->next;
      }
      if (!found)  {
	bfd *abfd = bfd_openr(list->name, 0);
	fprintf(stderr,_("%s: can't find module file %s\n"), program_name,
		list->name);
	if (!abfd) 
	{
	  fprintf(stderr, _("%s: can't open file %s\n"), program_name, list->name);
	  maybequit();
	}
	else 
	{
	  *prev = abfd;
	}
      }

    list = list->next;
    }
  }
}

/* And I added this one */
void
DEFUN_VOID(ar_list)
{
  if (!obfd) 
  {
    fprintf(stderr, _("%s: no open output archive\n"), program_name);
    maybequit();
  }
  else {
    bfd *abfd;
    outfile = stdout;
    verbose =1 ;
    printf(_("Current open archive is %s\n"), bfd_get_filename (obfd));
    for (abfd = obfd->archive_head;
	 abfd != (bfd *)NULL;
	 abfd = abfd->next) 
    {
      ar_directory_doer (abfd, (bfd *) NULL);
    }
  }
}


void 
DEFUN_VOID(ar_end)
{
  if (obfd)
  {
    fclose((FILE *)(obfd->iostream));
    unlink(bfd_get_filename (obfd));
  }
}
void
DEFUN(ar_extract,(list),
      struct list *list)
{
  if (!obfd) 
  {

    fprintf(stderr, _("%s: no open  archive\n"), program_name);
    maybequit();
  }
  else 
  {
    while (list) {
      /* Find this name in the archive */
      bfd *member = obfd->archive_head;
      int found = 0;
      while (member && !found) 
      {
	if (FILENAME_CMP(member->filename, list->name) == 0) 
	{
	  extract_file(member);
	  found = 1;
	  }

	member = member->next;
      }
      if (!found)  {
	bfd_openr(list->name, 0);
	fprintf(stderr,_("%s: can't find module file %s\n"), program_name,
		list->name);

      }
      list = list->next;
    }
  }
}
