// -*- mode: cpp; mode: fold -*-
// Description								/*{{{*/
/* ######################################################################

   AR File - Handle an 'AR' archive
   
   AR Archives have plain text headers at the start of each file
   section. The headers are aligned on a 2 byte boundary.
   
   Information about the structure of AR files can be found in ar(5)
   on a BSD system, or in the binutils source.

   ##################################################################### */
									/*}}}*/
// Include Files							/*{{{*/
#include <config.h>

#include <apt-pkg/arfile.h>
#include <apt-pkg/error.h>
#include <apt-pkg/fileutl.h>
#include <apt-pkg/strutl.h>

#include <cstring>
#include <string>
#include <sys/types.h>

#include <apti18n.h>
									/*}}}*/

struct ARArchive::MemberHeader
{
   char Name[16];
   char MTime[12];
   char UID[6];
   char GID[6];
   char Mode[8];
   char Size[10];
   char Magic[2];
};

// ARArchive::ARArchive - Constructor					/*{{{*/
// ---------------------------------------------------------------------
/* */
ARArchive::ARArchive(FileFd &File) : List(0), File(File)
{
   LoadHeaders();
}
									/*}}}*/
// ARArchive::~ARArchive - Destructor					/*{{{*/
// ---------------------------------------------------------------------
/* */
ARArchive::~ARArchive()
{
   while (List != 0)
   {
      Member *Tmp = List;
      List = List->Next;
      delete Tmp;
   }   
}
									/*}}}*/
// ARArchive::LoadHeaders - Load the headers from each file		/*{{{*/
// ---------------------------------------------------------------------
/* AR files are structured with a 8 byte magic string followed by a 60
   byte plain text header then the file data, another header, data, etc */
bool ARArchive::LoadHeaders()
{
   off_t Left = File.Size();
   
   // Check the magic byte
   char Magic[8];
   if (File.Read(Magic,sizeof(Magic)) == false)
      return false;
   if (memcmp(Magic,"!<arch>\012",sizeof(Magic)) != 0)
      return _error->Error(_("Invalid archive signature"));
   Left -= sizeof(Magic);
   
   // Read the member list
   while (Left > 0)
   {
      MemberHeader Head;
      if (File.Read(&Head,sizeof(Head)) == false)
	 return _error->Error(_("Error reading archive member header"));
      Left -= sizeof(Head);

      // Convert all of the integer members
      Member *Memb = new Member();
      if (StrToNum(Head.MTime,Memb->MTime,sizeof(Head.MTime)) == false ||
	  StrToNum(Head.UID,Memb->UID,sizeof(Head.UID)) == false ||
	  StrToNum(Head.GID,Memb->GID,sizeof(Head.GID)) == false ||
	  StrToNum(Head.Mode,Memb->Mode,sizeof(Head.Mode),8) == false ||
	  StrToNum(Head.Size,Memb->Size,sizeof(Head.Size)) == false)
      {
	 delete Memb;
	 return _error->Error(_("Invalid archive member header"));
      }

      if (Left < 0 || Memb->Size > static_cast<unsigned long long>(Left))
      {
	 delete Memb;
	 return _error->Error(_("Invalid archive member header"));
      }
      // Check for an extra long name string
      if (memcmp(Head.Name,"#1/",3) == 0)
      {
	 char S[300];
	 unsigned long Len;
	 if (StrToNum(Head.Name+3,Len,sizeof(Head.Size)-3) == false ||
	     Len >= sizeof(S))
	 {
	    delete Memb;
	    return _error->Error(_("Invalid archive member header"));
	 }

	 if (Len > Memb->Size)
	 {
	    delete Memb;
	    return _error->Error(_("Invalid archive member header"));
	 }

	 if (File.Read(S,Len) == false)
	 {
	    delete Memb;
	    return false;
	 }
	 S[Len] = 0;
	 Memb->Name = S;
	 Memb->Size -= Len;
	 Left -= Len;
      }
      else
      {
	 unsigned int I = sizeof(Head.Name) - 1;
	 for (; Head.Name[I] == ' ' || Head.Name[I] == '/'; I--)
	 {
	    if (I == 0)
	    {
	       delete Memb;
	       return _error->Error(_("Invalid archive member header"));
	    }
	 }
	 Memb->Name = std::string(Head.Name,I+1);
      }

      // Account for the AR header alignment 
      off_t Skip = Memb->Size % 2;
      
      // Add it to the list
      Memb->Next = List;
      List = Memb;
      Memb->Start = File.Tell();
      if (File.Skip(Memb->Size + Skip) == false)
	 return false;
      if (Left < (off_t)(Memb->Size + Skip))
	 return _error->Error(_("Archive is too short"));
      Left -= Memb->Size + Skip;
   }   
   if (Left != 0)
      return _error->Error(_("Failed to read the archive headers"));
   
   return true;
}
									/*}}}*/
// ARArchive::FindMember - Find a name in the member list		/*{{{*/
// ---------------------------------------------------------------------
/* Find a member with the given name */
const ARArchive::Member *ARArchive::FindMember(const char *Name) const
{
   const Member *Res = List;
   while (Res != 0)
   {
      if (Res->Name == Name)
	 return Res;
      Res = Res->Next;
   }
   
   return 0;
}
									/*}}}*/
