
/* 
 * $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 */

/*	Copyright (c) 1984 AT&T	*/
/*	  All Rights Reserved  	*/

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/
#ident "$Header: xsar.c 1.1 90/01/21 $@(#)xsar.c	1.1 89/12/01"
/*
 * Messages files archiver
 *
 * Usage : xsar <action> <archive_name> [messages files]*
 * with action : a : append (or replace)
 *		 r : alias to a
 *               d : delete
 *               x : extract
 *               t : display contents
 */
 
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include "xsar.h"

char *gettxt();
char *basename();

typedef struct {  
  char *ar_arname;
  char *ar_name;
  char ar_action;
  long ar_sym_I;
  long ar_sym_E;
  long ar_msg_E;
  long ar_msg_I;
  long ar_msg_end;
} AR;

#define AR_NOPE		0
#define AR_APPEND	1
#define AR_REPLACE	2
#define AR_NEW		3
#define AR_DELETE	4
#define AR_EXTRACT	5
#define AR_DISPLAY	6

#define AR_INC		20

char action;
AR *ar_list;
int ar_free;
int ar_max;
int ar_old;

FILE *in, *out, *fd;
char buf[BUFSIZ];
long off_hdrI1, off_hdrE1, off_hdrI2, off_hdrE2, off_msg_end;

main(argc, argv)
  int argc;
  char *argv[];
{
  char c;
  register int i;
  int pos;
  long curoff;
  
  if (argc <= 2)
    usage();
    
  /*
   * Build the contents list if the archive already exists
   */
  if (access(argv[2], 4) == 0)
    build_list(argv[2]);
    
  /*
   * Determine the action
   */
  c = (argv[1][0] == '-') ? argv[1][1] : argv[1][0];
  switch(c){
  case 'a':
  case 'r':
    action = AR_APPEND;
    break;
  case 'd':
    action = AR_DELETE;
    break;
  case 'x':
    action = AR_EXTRACT;
    break;
  case 't':
    action = AR_DISPLAY;
    break;
  default:
    fprintf(stderr, gettxt("xsar:1","Unknown option '%c'\n"), c);
    exit(1);
  }

  /*
   * Archive must pre-exist if not appending
   * In that case, errno was kept since first call to access.
   */
  if (action != AR_APPEND && ar_list == 0){
    perror(argv[2]);
    exit(1);
  }
  
  /*
   * Check file list
   */
  if (argc == 3){
    if (action == AR_DISPLAY){
      display_list();
      exit(0);
    } else {
      /*
       * All other actions require file names
       */
      fprintf(stderr, gettxt("xsar:2","Missing file list\n"));
      exit(1);
    }
  }
  
  /*
   * Determine the actions for each file in the list, according
   * to the names given as arguments
   */
  for (i = 3 ; i < argc ; i++){
    switch(action){
    case AR_APPEND:
      /*
       * File must exist and be readable to be put in the archive
       */
      if (access(argv[i], 4) == -1){
        perror(argv[i]);
        continue;
      }
      if ((pos = isin_list(argv[i])) == -1){
        /*
         * File is not in the archive
         */
        pos = add_list(argv[i]);
        ar_list[pos].ar_action = AR_NEW;
      } else
        ar_list[pos].ar_action = AR_REPLACE;
      ar_list[pos].ar_name = argv[i];
      continue;
    case AR_DELETE:
      if ((pos = isin_list(argv[i])) == -1){
        /*
         * File is not in the archive
         */
        fprintf(stderr, gettxt("xsar:3","%s : not in the archive -- ignored\n"), argv[i]);
        continue;
      }
      ar_list[pos].ar_action = AR_DELETE;
      continue;
    case AR_EXTRACT:
      if ((pos = isin_list(argv[i])) == -1){
        /*
         * File is not in the archive
         */
        fprintf(stderr, gettxt("xsar:4","%s : not in the archive -- ignored\n"), argv[i]);
        continue;
      }
      ar_list[pos].ar_action = AR_EXTRACT;
      ar_list[pos].ar_name = argv[i];
      continue;
    case AR_DISPLAY:
      if ((pos = isin_list(argv[i])) == -1){
        /*
         * File is not in the archive
         */
        fprintf(stdout, gettxt("xsar:5","%s: not in the archive\n"), argv[i]);
        continue;
      }
      fprintf(stdout, "%s\n", ar_list[pos].ar_arname);
      continue;
    default:
      fprintf(stderr, gettxt("xsar:6","Internal error: unknown action\n"));
      exit(1);
    }
  }
  
  /*
   * Create output file (as a tempfile)
   */
  if ((out = tmpfile()) == NULL){
    perror("tmpfile");
    exit(1);
  }
  
  cp_hdrI1();
  
  /*
   * Handle messages
   */
  for (i = 0 ; i < ar_free ; i++){
    switch(ar_list[i].ar_action){
    case AR_REPLACE:
      cp_msg_I_E(i);
      continue;
    case AR_NEW:
      cp_msg_I_E(i);
      continue;
    case AR_DELETE:
      continue;
    case AR_EXTRACT:
      curoff = ftell(in);
      cp_msg_E_I(i);
      if (fseek(in, curoff, 0) != 0){
      	perror(gettxt("xsar:7","seek error 8"));
      	exit(1);
      }
      cp_msg_I_I(i);
      continue;
    case AR_NOPE:
      cp_msg_I_I(i);
      continue;
    default:
      fprintf(stderr, gettxt("xsar:8","Internal error: unknown action (2)\n"));
      exit(1);
    }
  }
   
  cp_hdrI2();
  /*
   * Handle symbol tables
   */
  sym_first(in);
  for (i = 0 ; i < ar_free ; i++){
    switch(ar_list[i].ar_action){
    case AR_REPLACE:
      sym_next(in);
      cp_sym_I_E(i);
      continue;
    case AR_NEW:
      cp_sym_I_E(i);
      continue;
    case AR_DELETE:
      sym_next(in);
      continue;
    case AR_EXTRACT:
      curoff = ftell(in);
      cp_sym_E_I(i);
      if (fseek(in, curoff, 0) != 0){
      	perror(gettxt("xsar:9","seek error 7"));
      	exit(1);
      }
      cp_sym_I_I(i);
      continue;
    case AR_NOPE:
      cp_sym_I_I(i);
      continue;
    default:
      fprintf(stderr, gettxt("xsar:10","Internal error: unknown action (3)\n"));
      exit(1);
    }
  }
  
  /*
   * End work
   */
  copy_arch(argv[2]);
  exit(0);
}

/*
 * Build the contents list
 */
build_list(name)
  char *name;
{
  long curoff, lastoff;
  int pos;
  
  if ((in = fopen(name, "r")) == NULL){
    perror(name);
    exit(1);
  }

  skip_hdrI1(in);
  lastoff = curoff = ftell(in);
  
  while(fgets(buf, BUFSIZ, in) != NULL){
    if (is_hdrI2()){
      /*
       * Found the symbol tables
       */
      off_msg_end = lastoff;
      off_hdrI2 = curoff;
      off_hdrE2 = ftell(in);
      return;
    }
    if (is_msgI1()){
      /*
       * Found a module name
       */
      buf[strlen(buf) - 1] = '\0';
      pos = add_list(buf + len_msgI1);
      ar_old++;
      ar_list[pos].ar_msg_I = curoff;
      ar_list[pos].ar_msg_E = curoff = ftell(in);
      ar_list[pos].ar_msg_end = lastoff;
    } else if (is_msgI2()){
        /*
         * Found the end of previous entry
         */
    	lastoff = curoff;
    	curoff = ftell(in);
    } else
      curoff = ftell(in);
  }
}

/*
 * Add an entry in the table
 */
add_list(name)
  char *name;
{
  /*
   * Keep only basename
   */
  name = basename(name);
  
  if (ar_max == ar_free){
    /*
     * No space left in the table
     */
    ar_max += AR_INC;
    if (ar_list == 0){
      if ((ar_list = (AR *)malloc(sizeof(AR) * AR_INC)) == 0){
        fprintf(stderr, gettxt("xsar:11","Out of memory\n"));
        exit(1);
      }
    } else {
      if ((ar_list = (AR *)realloc(ar_list, sizeof(AR) * ar_max)) == 0){
        fprintf(stderr, gettxt("xsar:12","Out of memory\n"));
        exit(1);
      }
    }
  }
  if ((ar_list[ar_free].ar_arname = malloc(strlen(name) + 1)) == 0){
    fprintf(stderr, gettxt("xsar:13","Out of memory\n"));
    exit(1);
  }
  strcpy(ar_list[ar_free].ar_arname, name);
  ar_list[ar_free].ar_action = AR_NOPE;
  return ar_free++;
}

/*
 * Copy messages from external file to archive
 */
cp_msg_I_E(pos)
  int pos;
{
  FILE *fd;
  if ((fd = fopen(ar_list[pos].ar_name, "r")) == NULL){
    perror(ar_list[pos].ar_name);
    return;
  }
  skip_hdrE1(fd);
  cp_msgI1(pos);
  cp_to_symE(fd, pos);
  cp_msgI2(pos);
  fclose(fd);
}

/*
 * Copy messages from archive to external file
 */
cp_msg_E_I(pos)
  int pos;
{
  FILE *fd;
  int len;
  
  if ((fd = fopen(ar_list[pos].ar_name, "w")) == NULL){
    perror(ar_list[pos].ar_name);
    return;
  }
  if (fseek(in, ar_list[pos].ar_msg_E, 0) != 0){
    perror(gettxt("xsar:14","seek error"));
    exit(1);
  }
  
  cp_startE(fd);
  cp_hdrE1(fd);
  if (pos == ar_old - 1)
    len = off_msg_end - ar_list[pos].ar_msg_E;
  else
    len = ar_list[pos + 1].ar_msg_end - ar_list[pos].ar_msg_E;
  copy_len(fd, in, len);
  fclose(fd);
}

/*
 * Copy messages from archive to archive
 */
cp_msg_I_I(pos)
  int pos;
{
  int len;
  
  if (fseek(in, ar_list[pos].ar_msg_I, 0) != 0){
    perror(gettxt("xsar:15","seek error 2"));
    exit(1);
  }
  if (pos == ar_old - 1)
    len = off_hdrI2 - ar_list[pos].ar_msg_I;
  else
    len = ar_list[pos + 1].ar_msg_I - ar_list[pos].ar_msg_I;

  copy_len(out, in, len);
}

/*
 * Copy a symtab from external file to archive
 */
cp_sym_I_E(pos)
  int pos;
{
  FILE *fd;
  int len;
  
  if ((fd = fopen(ar_list[pos].ar_name, "r")) == NULL){
    perror(ar_list[pos].ar_name);
    return;
  }
  if (fseek(fd, ar_list[pos].ar_sym_E, 0) != 0){
    perror(gettxt("xsar:16","seek error"));
    exit(1);
  }

  cp_symI1(pos);
  
  copy_eof(out, fd);
  cp_symI2(pos);
  fclose(fd);
}

/*
 * Copy a symtab from archive to external file
 */
cp_sym_E_I(pos)
  int pos;
{
  FILE *fd;
  int len;
  
  if ((fd = fopen(ar_list[pos].ar_name, "a")) == NULL){
    perror(ar_list[pos].ar_name);
    return;
  }
  cp_hdrE2(fd);
  fgets(buf, BUFSIZ, in);
  cp_to_symI2(fd);
  fclose(fd);
}

/*
 * Copy a symtab from archive to archive
 */
cp_sym_I_I(pos)
  int pos;
{
  cp_to_symI1();
}

/*
 * Print usage and exit
 */
usage()
{
  fprintf(stderr, gettxt("xsar:17","Usage: xsar action archive [files]*\n"));
  exit(1);
}

/*
 * Display name of each entry
 */
display_list()
{
  register int i;
  
  for (i = 0 ; i < ar_free ; i++)
    fprintf(stdout, "%s\n", ar_list[i].ar_arname);
}

/*
 * Check if a name is in the archive.
 * return position
 */
isin_list(name)
  char *name;
{
  register int i;
  name = basename(name);
  
  for (i = 0 ; i < ar_free ; i++)
    if (strcmp(name, ar_list[i].ar_arname) == 0)
      return i;
  return -1;
}

/*
 * Seek first symtab in archive
 */
sym_first(fd)
  FILE *fd;
{
  if (fd == NULL)
    return;
  if (fseek(fd, off_hdrE2, 0) != 0){
    perror(gettxt("xsar:18","seek error 3"));
    exit(1);
  }
  sym_next1(fd);
}

/*
 * Copy new archive into old one
 */
copy_arch(name)
  char *name;
{
  int n;
  if (in != NULL)
    fclose(in);
    
  if ((in = fopen(name, "w")) == NULL){
    perror(name);
    exit(1);
  }
  if (fseek(out, 0L, 0) != 0){
    perror(gettxt("xsar:19","seek error 6"));
    exit(1);
  }
  
  while ((n = fread(buf, 1, BUFSIZ, out)) > 0){
    if (fwrite(buf, 1, n, in) != n){
      perror(name);
      exit(1);
    }
  }
  fclose(in);
  fclose(out);
}

/*
 * Position file after hdrI2
 */
skip_hdrI2()
{
  if (in == NULL)
    return;
  while (fgets(buf, BUFSIZ, in) != NULL){
    if (is_hdrI2())
      return;
  }
}

/*
 * Return a file name with leading path
 */
char *
basename(name)
  char *name;
{
  char *ptr = strrchr(name, '/');
  return (ptr == 0) ? name : ptr + 1;
}

/*
 * Copy msgI1 to output file
 */
cp_msgI1(pos)
  int pos;
{
  fprintf(out, "%s%s\n", msg_msgI1, ar_list[pos].ar_arname);
}

/*
 * Copy msgI2 to output file
 */
cp_msgI2(pos)
  int pos;
{
  fprintf(out, "%s\n", msg_msgI2);
}

/*
 * Copy symI1 to output file
 */
cp_symI1(pos)
  int pos;
{
  fprintf(out, "%s%s\n", msg_symI1, ar_list[pos].ar_arname);
}

/*
 * Copy symI2 to output file
 */
cp_symI2(pos)
  int pos;
{
  fprintf(out, "%s\n", msg_symI2);
}

/*
 * Copy hdrE1 to file
 */
cp_hdrE1(fd)
  FILE *fd;
{
  fprintf(fd, "%s\n", msg_hdrE1);
}

/*
 * Copy hdrE2 to file
 */
cp_hdrE2(fd)
  FILE *fd;
{
  fprintf(fd, "%s\n", msg_hdrE2);
}

/*
 * Copy hdrI1 to file
 */
cp_hdrI1()
{
  fprintf(out, "%s\n", msg_hdrI1);
}

/*
 * Copy hdrI2 to file
 */
cp_hdrI2()
{
  fprintf(out, "%s\n", msg_hdrI2);
}

/*
 * Copy start message to file
 */
cp_startE(fd)
  FILE *fd;
{
  fprintf(fd, "%s\n", msg_startE);
}

/*
 * Copy from fd to archive until symtab
 * Keep symtab offset in ar_list
 */
cp_to_symE(fd, pos)
  FILE *fd;
  int pos;
{
  while (fgets(buf, BUFSIZ, fd) != NULL){
    if (is_hdrE2()){
      ar_list[pos].ar_sym_E = ftell(fd);
      return;
    }
    if (fputs(buf, out) == EOF){
      perror(ar_list[pos].ar_name);
      exit(1);
    }
  }
  ar_list[pos].ar_sym_E = ftell(fd);
}

/*
 * Copy src to dst until eof
 */
copy_eof(dst, src)
  FILE *dst, *src;
{
  int n;
  while ((n = fread(buf, 1, BUFSIZ, src)) > 0){
    if (fwrite(buf, 1, n, dst) != n){
      perror(gettxt("xsar:20","write error"));
      exit(1);
    }
  }
}

/*
 * Copy until next symbol table
 */
cp_to_symI1()
{
  int first = 1;
  
  long curoff = ftell(in);
  while (fgets(buf, BUFSIZ, in) != NULL){
    if (is_symI1()){
      if (first)
        first = 0;
      else {
        if (fseek(in, curoff, 0) != 0){
          perror(gettxt("xsar:21","seek error 5"));
          exit(1);
        }
        return;
      }
    }
    if (fputs(buf, out) == EOF){
      perror(gettxt("xsar:22","write error"));
      exit(1);
    }
    curoff = ftell(in);
  }
}

/*
 * Copy until next symbol table
 */
cp_to_symI2(fd)
  FILE *fd;
{
  while (fgets(buf, BUFSIZ, in) != NULL){
    if (is_symI2())
      return;
    if (fputs(buf, fd) == EOF){
      perror(gettxt("xsar:23","write error"));
      exit(1);
    }
  }
}
