/* $VER: ar ar.c V0.1 (31.01.98)
 *
 * This file is part of ar, a portable archive maintanance
 * utility for normal and BSD-style archives.
 * Copyright (c) 1999  Frank Wille
 *
 * ar is freeware and part of the portable and retargetable ANSI C
 * compiler vbcc, copyright (c) 1995-99 by Volker Barthelmann.
 * ar may be freely redistributed as long as no modifications are
 * made and nothing is charged for it. Non-commercial usage is allowed
 * without any restrictions.
 * EVERY PRODUCT OR PROGRAM DERIVED DIRECTLY FROM MY SOURCE MAY NOT BE
 * SOLD COMMERCIALLY WITHOUT PERMISSION FROM THE AUTHOR.
 *
 *
 * v0.1  (31.01.99) phx
 *       First working version, which only supports 'q' (quick append)
 *       and 't' (table of contents), reads and writes normals and
 *       BSD-style archives. Symbol table will not be created!
 * v0.0  (29.01.99) phx
 *       File created.
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "ar.h"
#include "errors.h"


extern void show_usage(void);
extern void ar_delete(struct Args *);
extern void ar_move(struct Args *);
extern void ar_print(struct Args *);
extern void ar_qappend(struct Args *);
extern void ar_replace(struct Args *);
extern void ar_contents(struct Args *);
extern void ar_extract(struct Args *);

/* local */
static uint32 getmodifiers(char,char *,uint32);
static uint32 chkmodifier(uint32,uint32,char,char);
static void argerror(void);



#ifdef AMIGA
extern char **expand_args(int *,int,char *[],int);


int main(int _argc,char *_argv[])
{
  int argc;
  char **argv = expand_args(&argc,_argc,_argv,2);

#else
int main(int argc,char *argv[])
{
#endif
  struct Args a;
  char *p,c;

  /* init */
  memset(&a,0,sizeof(struct Args));

  /* parse arguments */
  if (argc < 3) {
    if (argc == 2)
      error(EMISSARCHNAME);
    argerror();
  }
  p = argv[1];
  if (*p == '-')
    p++;

  /* get key argument */
  switch (c = *p++) {
    case 'd':                   /* delete */
      if (argc < 4)
        error(EMISSFILENAME);
      a.modifier = getmodifiers(c,p,AR_TRUNC|AR_BSD|AR_VERBOSE);
      a.arname = argv[2];
      a.files = &argv[3];
      a.filecnt = argc - 3;
      ar_delete(&a);
      break;

    case 'm':                   /* move */
      a.modifier = getmodifiers(c,p,AR_TRUNC|AR_BSD|AR_VERBOSE|AR_POS);
      if (a.modifier & AR_POS) {
        if (argc < 4)
          error(EMISSARCHNAME);
        if (argc < 5)
          error(EMISSFILENAME);
        a.position = argv[2];
        a.arname = argv[3];
        a.files = &argv[4];
        a.filecnt = argc - 4;
      }
      else {
        if (argc < 4)
          error(EMISSFILENAME);
        a.arname = argv[2];
        a.files = &argv[3];
        a.filecnt = argc - 3;
      }
      ar_move(&a);
      break;

    case 'p':                   /* print */
      a.modifier = getmodifiers(c,p,AR_TRUNC|AR_VERBOSE);
      a.arname = argv[2];
      if (argc >= 4) {
        a.files = &argv[3];
        a.filecnt = argc - 3;
      }
      ar_print(&a);
      break;

    case 'q':                   /* quick append */
      if (argc < 4)
        error(EMISSFILENAME);
      a.modifier = getmodifiers(c,p,AR_TRUNC|AR_BSD|AR_VERBOSE|AR_CREATIGN);
      a.arname = argv[2];
      a.files = &argv[3];
      a.filecnt = argc - 3;
      ar_qappend(&a);
      break;

    case 'r':                   /* replace */
      a.modifier = getmodifiers(c,p,AR_TRUNC|AR_BSD|AR_VERBOSE|
                                  AR_POS|AR_CREATIGN|AR_UPDATE);
      if (a.modifier & AR_POS) {
        if (argc < 4)
          error(EMISSARCHNAME);
        if (argc < 5)
          error(EMISSFILENAME);
        a.position = argv[2];
        a.arname = argv[3];
        a.files = &argv[4];
        a.filecnt = argc - 4;
      }
      else {
        if (argc < 4)
          error(EMISSFILENAME);
        a.arname = argv[2];
        a.files = &argv[3];
        a.filecnt = argc - 3;
      }
      ar_replace(&a);
      break;

    case 't':                   /* table of contents */
      a.modifier = getmodifiers(c,p,AR_TRUNC|AR_VERBOSE);
      a.arname = argv[2];
      if (argc >= 4) {
        a.files = &argv[3];
        a.filecnt = argc - 3;
      }
      ar_contents(&a);
      break;

    case 'x':                   /* extract */
      a.modifier = getmodifiers(c,p,AR_TRUNC|AR_VERBOSE|AR_PRESERVE|
                                  AR_UPDATE);
      a.arname = argv[2];
      if (argc >= 4) {
        a.files = &argv[3];
        a.filecnt = argc - 3;
      }
      ar_extract(&a);
      break;

    default:
      error(EUNKNOWNKEY,c);
      break;
  }

  return (0);
}


void cleanup()
{
}


static uint32 getmodifiers(char key,char *s,uint32 allowed)
{
  char c;
  uint32 m = 0;

  for (;;) {
    switch (c = *s++) {
      case '\0':
        break;
      case 'a':
        m |= chkmodifier(AR_AFTER,allowed,c,key);
        continue;
      case 'b':
        m |= chkmodifier(AR_BEFORE,allowed,c,key);
        continue;
      case 'i':
        m |= chkmodifier(AR_BEFORE,allowed,c,key);
        continue;
      case 'c':
        m |= chkmodifier(AR_CREATIGN,allowed,c,key);
        continue;
      case 'o':
        m |= chkmodifier(AR_PRESERVE,allowed,c,key);
        continue;
      case 'u':
        m |= chkmodifier(AR_UPDATE,allowed,c,key);
        continue;
      case 'v':
        m |= chkmodifier(AR_VERBOSE,allowed,c,key);
        continue;
      case 'T':
        m |= chkmodifier(AR_TRUNC,allowed,c,key);
        continue;
      case 'B':
        m |= chkmodifier(AR_BSD,allowed,c,key);
        continue;
      default:
        error(EMODNOTALLOWED,c,key);
        continue;
    }
    break;
  }
  return (m);
}


static uint32 chkmodifier(uint32 mod,uint32 allowed,char c,char key)
{
  if (mod & allowed)
    return (mod);
  error(EMODNOTALLOWED,c,key);
  return (0);
}


static void argerror(void)
{
  show_usage();
  exit(1);
}


size_t filesize(FILE *fh,char *name)
{
  long oldpos,size;

  if ((oldpos = ftell(fh)) >= 0)
    if (fseek(fh,0,SEEK_END) >= 0)
      if ((size = ftell(fh)) >= 0)
        if (fseek(fh,oldpos,SEEK_SET) >= 0)
          return ((size_t)size);
  fclose(fh);
  error(EREADERR,name);  /* doesn't return */
  return (0);
}
