/* Network Installation Kit
 * program to manage version files
 *
 * $Source: /afs/net.mit.edu/project/netconf/sgi_52/7.7tom/src/util/RCS/version.c,v $
 * $Author: tom $
 *
 * Tom Coppeto
 * MIT Network Group
 * 10 October 1992
 * 
 * Last Modified 23 October 1995 by jyp
 *
 */

static char rcsid[] = "$";

#include <stdio.h>

char buf[BUFSIZ];

#define DB_FILE "/etc/packages"
#define DB_SAVEFILE "/etc/packages.previous"
#define DB_REF "/mit/netconf/versions"

#define READ 1
#define WRITE 2

#define SUCCESS    0
#define ERROR     -1
#define NOT_FOUND -2
#define CMD_ERROR -3

typedef struct
{
  char *refversion;
  int ackversion;
} Header;

typedef struct 
{
  char *tag;
  int  version;
  int  ackversion;
  int  time;
  char *comment;
} Entry;

FILE *open_db();
char *make_temp_file();


main(argc, argv)
     int argc; 
     char **argv;
{
  char *pname;

  pname = *argv;
  if(*++argv)
    {
      if(!strcmp(*argv, "add"))
	return(add_entry(++argv));
      if(!strcmp(*argv, "delete"))
	return(delete_entry(++argv));
      if(!strcmp(*argv, "list"))
	return(list_entries(++argv));
      if(!strcmp(*argv, "version"))
	return(show_version(++argv));
      if(!strcmp(*argv, "clear"))
	return(clear_entries(++argv));
      if(!strcmp(*argv, "restore"))
	return(restore_entries(++argv));

      fprintf(stderr, "unknown argument: %s\n", *argv);
    }

  fprintf(stderr, "usage: %s [list | add | delete | clear]\n", pname);
  exit(CMD_ERROR);  
}


add_entry(args)
     char **args;
{
  Entry e;
  FILE *fp;

  bzero(&e, sizeof(e));
  
  if(*args && *(args+1))
    {
      e.tag     = *args++;
      e.version = atoi(*args++);
      e.time    = time(0);
    }
  else
    {
      fprintf(stderr, "usage: add <tag> <version>\n");
      return(CMD_ERROR);
    }

  if(!(fp = open_db(WRITE)))
    return(ERROR);
  store(fp, &e);
  return(close_db(fp));
}


delete_entry(args)
     char **args;
{
  FILE *fp;

  if(!*args)
    {
      fprintf(stderr, "usage: delete\n");
      return(CMD_ERROR);
    }

  if(!(fp = open_db(WRITE)))
    return(ERROR);
  if(delete(fp, *args) != SUCCESS)
    {
      close_db(fp);
      return(ERROR);
    }
  return(close_db(fp));
}


list_entries(args)
     char **args;
{
  FILE *fp;
  Entry e;
  int i = 0;

  if(*args)
    {
      fprintf(stderr, "usage: list\n");
      return(CMD_ERROR);
    }
  
  if(!(fp = open_db(READ)))
    return(ERROR);
  printf("contents of %s\n----------------------------------------------------------------\n", 
	 DB_FILE);
  while(lookup(fp, "*", &e) == SUCCESS)
    printf("%3d. %-20s %10d \t%s", ++i, e.tag, e.version, ctime(&e.time));
  close_db(fp);
  return(SUCCESS);
}


int
show_version(args)
     char **args;
{
  FILE *fp;
  Entry e;

  if(!*args)
    {
      fprintf(stderr, "usage: version <tag>\n");
      return(CMD_ERROR);
    }
  
  if(!(fp = open_db(READ)))
    return(ERROR);
  if(lookup(fp, *args, &e) == SUCCESS)
    printf("%d\n", e.version);
  else
    return(NOT_FOUND);
  close_db(fp);
  return(SUCCESS);
}


int
clear_entries(args)
     char **args;
{
  if(*args)
    {
      fprintf(stderr, "usage: list\n");
      return(CMD_ERROR);
    }
    
  return(rename(DB_FILE, DB_SAVEFILE));
}


int 
restore_entries(args)
     char **args;
{

  if(*args)
    {
      fprintf(stderr, "usage: list\n");
      return(CMD_ERROR);
    }

  return(rename(DB_SAVEFILE, DB_FILE));
}
  

/*
 * Function:      int lookup(fp, tag, entry)
 *                      FILE  *fp;
 *                      char  tag;     entity to lookup in file
 *                      Entry *entry;  the entry for tag in file
 *
 * Description:   lookup returns the Entry for the associated tag in file.
 *                The first line matching tag is returned.
 *
 * Returns:       SUCCESS
 *                NOTFOUND
 * 
 * Notes:         file format:
 *                              tag(string)   version(string)   date(integer)
 */

int
lookup(fp, tag, entry)
     FILE *fp;
     char *tag;
     Entry *entry;
{
  char *c;
  char *d;
  static int index = 0;

  while(fgets(buf, sizeof(buf), fp))
    {      
      c = buf;
      while(isspace(*c))
	++c;
      if(!*c)
	continue;
      d = c;
      while(!isspace(*d))
	++d;
      *d++ = '\0';

      if((*tag != '*') && strcmp(c, tag))
	continue;

      entry->tag = c;

      c = d;
      while(isspace(*c))
	++c;
      if(!*c)
	continue;
      d = c;
      while(!isspace(*d))
	++d;
      *d++ = '\0';
      entry->version = atoi(c);

      c = d;
      while(isspace(*c))
	++c;
      if(!*c)
	continue;
      d = c;
      while(!isspace(*d))
	++d;
      *d++ = '\0';
      entry->time = atoi(c);

      return(SUCCESS);
    }

  index = 0;
  return(NOT_FOUND);
}


/*
 * Function:      int store(fp, entry)
 *                      FILE  *fp;
 *                      Entry *entry;  the entry for tag in file
 *
 * Description:   store() stores entry in file, fp.
 *                If the tag does not exist, the entry is appended.
 *                If it does exist, it is rewritten by copying the file to
 *                a temporary file first.
 *
 * Returns:       SUCCESS
 *                ERROR
 * 
 * Notes:         file format:
 *                              tag(string)   version(string)   date(integer)
 * 
 */

#define v1_store(fp, entry) \
  fprintf(fp, "%-20s %10d %d\n", \
	  (entry)->tag, (entry)->version, (entry)->time)


int      
store(fp, entry)
     FILE *fp;
     Entry *entry;
{
  FILE *ftemp;
  Entry e;
  char *tempfile;

  if(lookup(fp, entry->tag, &e) == NOT_FOUND)
    {
      fseek(fp, 0, 2);
      v1_store(fp, entry);
      return(SUCCESS);
    }
  
  rewind(fp);
  tempfile = make_temp_file("version");
  if(!(ftemp = fopen(tempfile, "w")))
    {
      fprintf(stderr, "unable to open temp file, %s, for writing: aborting.\n",
                       tempfile);
      return(ERROR);
    }

  while(lookup(fp, "*", &e) == SUCCESS)
    {
      if(strcmp(e.tag, entry->tag))
	v1_store(ftemp, &e);
      else
	v1_store(ftemp, entry);
    }
  
  if(fclose(ftemp) < 0)
    {
      perror("");
      fprintf(stderr, "unable to close temp file, aborting");
      return(ERROR);
    }

  if(!(ftemp = fopen(tempfile, "r")))
    {
      fprintf(stderr, "unable to open temp file, \"%s\", aborting.\n", 
	      tempfile);
      return(ERROR);
    }
  
  if(!(fp = freopen(DB_FILE, "w+", fp)))
    {
      perror("");
      return(ERROR);
    }
  copy_file(ftemp, fp);

  if(fclose(fp) < 0)
    {
      perror("");
      fprintf(stderr, "unable to close version file, aborting");
      return(ERROR);
    }

  fclose(ftemp);
  unlink(tempfile);
  return(SUCCESS);
}



int      
delete(fp, tag)
     FILE *fp;
     char *tag;
{
  FILE *ftemp;
  Entry e;
  char *tempfile;

  if(lookup(fp, tag, &e) == NOT_FOUND)
    return(SUCCESS);
  
  rewind(fp);
  tempfile = make_temp_file("version");
  if(!(ftemp = fopen(tempfile, "w")))
    {
      fprintf(stderr, "unable to open temp file, %s, for writing: aborting.\n",
                       tempfile);
      return(ERROR);
    }

  while(lookup(fp, "*", &e) == SUCCESS)
    {
      if(strcmp(e.tag, tag))
	v1_store(ftemp, &e);
    }
  
  if(fclose(ftemp) < 0)
    {
      perror("");
      fprintf(stderr, "unable to close temp file, aborting");
      return(ERROR);
    }

  if(!(ftemp = fopen(tempfile, "r")))
    {
      fprintf(stderr, "unable to open temp file, \"%s\", aborting.\n", 
	      tempfile);
      return(ERROR);
    }
  
  if(!(fp = freopen(DB_FILE, "w+", fp)))
    {
      perror("");
      return(ERROR);
    }

  copy_file(ftemp, fp);

  if(fclose(fp) < 0)
    {
      perror("");
      fprintf(stderr, "unable to close version file, aborting");
      return(ERROR);
    }

  fclose(ftemp);
  unlink(tempfile);
  return(SUCCESS);
}


int
copy_file(from, to)
     FILE *from;
     FILE *to;
{
  char buf[BUFSIZ];
  
  while(fgets(buf, sizeof(buf), from))
    fprintf(to, "%s", buf);
  return(SUCCESS);
}



FILE *
open_db(mode)
     int mode;
{
  FILE *fsave;
  FILE *fp = (FILE *) NULL;

  if(mode == WRITE)
    {
      rename(DB_FILE, DB_SAVEFILE);
      fsave = fopen(DB_SAVEFILE, "r");
      if(!(fp = fopen(DB_FILE, "a+")))
	{
	  perror("");
	  rename(DB_SAVEFILE, DB_FILE);
	  return(fp);
	}
      if(fsave)
	{
	  copy_file(fsave, fp);
	  fclose(fsave);	
	}
    }
  else
    if(!(fp = fopen(DB_FILE, "r")))
      return(fp);

  rewind(fp);
  return(fp);
}



close_db(fp)
     FILE *fp;
{
  return(fclose(fp));
}

	
  
char *
make_temp_file(label)
     char *label;
{
  static char buf[32];

  strcpy(buf, "/tmp/");
  strncat(buf, label, sizeof(buf)-12);
  sprintf(buf+strlen(buf), ".%d", getpid());
  return(buf);
}
  
  
      

