
/*
   This software is copyright 1989 by John Dunning.  See the file
   'COPYLEFT.JRD' for the full copyright notice.
*/


/* object library maintainer for atari 8-bit .obj files.
   later, maybe random other kinds of files too. */

#define VERSION "1.0"

#include <stdio.h>
#include <ctype.h>
#include <file.h>

#ifdef M6502
/* lots of things in here are defined to use 16-bit values */
#define USHORT int
/* other stuff might as well go here */
#define unlink delete
#define malloc xmalloc

#else
#define USHORT unsigned short
#endif

#include "symtab.h"
#include "obj.h"
#include "libr.h"

#ifdef M6502
/* do this part differently on 800 */
#else
#include <types.h>
#include <stat.h>		/* for _dta, for file size */
#endif

char * lfname;			/* name of the file we've got open */
int in_lfd;			/* fd of the file we've got open */
int out_lfd;			/* fd output, if writing */
int obj_lfd;			/* fd for .obj files */

struct librfile lfile;		/* header of the file we've got open */

int input_fd;			/* fd of obj files we read */

#ifdef __GNUC__
int _stksize = 16384;
#endif

/* defs for keeping track of what we're doing when maintaining a library */

/* we build a linked list of these when adding/deleting etc.  If we
   were writing this in a real language, we'd do this with included
   defstructs, but since we must use C, we make the first part of
   this look just like the def of a librmod, in libr.h.  */

struct entry
	{
	char e_name[12];	/* the file name of this entry */
	USHORT e_nbytes;	/* nbytes this entry */
	struct entry * e_next;	/* next entry */
	int new;		/* if this is a new entry */
	int skip;		/* this is an old entry being skipped */
	};

#define SKIP_REPLACE	1
#define SKIP_DELETE	2

char * skip_names[] =
	{
	"", "Replacing", "Deleting"
	};

struct entry * lfile_components;



#ifdef M6502
/* debug code */

char * xmalloc(n)
int n;
{
  char * foo;
  
  foo = pmalloc(n);
/*  printf("xmalloc(%d)->%x; sp %x\n", n, foo, &foo); */
  bzero(foo, n);
  return(foo);
}
#endif

/* util routines */

min(x, y)
int x,y;
{
  if (x < y) 
    return(x);
   else
    return(y);
}

#ifndef M6502
unsigned
#endif
char read8(input_fd)
int input_fd;
{
#ifdef M6502
  unsigned 
#endif
	char foo;
  read(input_fd, &foo, 1);
  return(foo);
}

USHORT read16(input_fd)
int input_fd;
{
  return(read8(input_fd) | (read8(input_fd) << 8));
}

write8(ch)
char ch;
{
  write(out_lfd, &ch, 1);
}

write16(sh)
USHORT sh;
{
  write8(sh & 0xFF);
  write8(sh >> 8);
}

int open_carefully(name, mode, must_exist)
char * name;
int mode;
int must_exist;
{
  int fd;
#ifdef M6502
  char buf[64];
/* prepend a D: if necessary */
  if (!strchr(name, ':'))
	{
	strcpy(buf, "D:");
	strcat(buf, name);
	name = buf;
	}
#endif
  printf("open('%s', %x)->", name, mode);
  fd = open(name, mode);
  if ((fd < 0) && must_exist)
  	barf("Can't open '%s'\n", name);
  printf("%d\n", fd);
  return(fd);
}

read_header(fd)
int fd;
{
  lfile.l_header = read16(fd);
  lfile.type = read16(fd);
  lfile.n_modules = read16(fd);
  lfile.flags = read16(fd);
/*
#ifdef M6502
  printf("read_header: %x %x %x %x\n", lfile.l_header, 
	lfile.type, lfile.n_modules, lfile.flags);
#endif
*/
}

read_mdesc(fd, mod)
int fd;
struct librmod * mod;
{
  read(fd, mod->m_name, 12);
  mod->m_nbytes = read16(fd);
}

/* build the module list for an existing file.  assume the next byte 
   to be read is the first one of the module structs.  assume lfile
   is valid */
build_modlist(fd)
int fd;
{
  int i;
  struct entry * elt;
  struct entry * prev_elt;

  lfile_components = elt = 
#ifdef M6502
				0;
#else
				(struct entry * )NULL;
#endif
  for (i = 0 ; i < lfile.n_modules ; i++)
	{
	prev_elt = elt;
	elt = 
#ifdef M6502
		malloc(sizeof(struct entry));
#else
		(struct entry *)malloc(sizeof(struct entry));
#endif
	read(fd, elt->e_name, 12);
	elt->e_nbytes = read16(fd);
/*
#ifdef M6502
  printf("build_modlist: got name '%s' nbytes %d\n", 
	elt->e_name, elt->e_nbytes);
#endif
*/
	elt->new = 0;
	elt->skip = 0;
	if (prev_elt)
		prev_elt->e_next = elt;
	    else
		lfile_components = elt;
	}	
}

string_upcase(str)
char * str;
{
  for ( ; *str ; str++)
	if (islower(*str))
		*str = toupper(*str);
}

int string_equal(str1, str2)
char * str1, * str2;
{
  for ( ; *str1 || *str2 ; str1++, str2++)
	if (*str1 != *str2)
		return(0);
	return(1);
}
/* not needed?
char * string_copy(str)
char * str;
{
  char * t = (char * )malloc(strlen(str) + 1);
  strcpy(t, str);
  return(t);
}
*/

string_pad(target, source, nbytes)
char * target;
char * source;
int nbytes;
{
  int i;
  
  for (i = nbytes ; (i > 0) && (*source) ; i--)
	*target++ = *source++;
  for ( ; i > 0 ; i--)
	*target++ = ' ';
}

string_trim(target, source, nbytes)
char * target;
char * source;
int nbytes;
{
  for ( ; ((nbytes > 0) && (*source != ' ')) ; nbytes--)
	*target++ = *source++;
  *target = '\0';
}

/* maintaining entry list */

struct entry * last_entry()
{
  struct entry * elt;
  
  for (elt = lfile_components ; elt && elt->e_next ; elt = elt->e_next)
	;
  return(elt);
}

/* delete an entry.  return 1 if found it */

int del_entry(name, why)
char * name;
int why;
{
  struct entry * elt;
  char buf[80];
  int found;

  found = 0;
  string_upcase(name);
  for (elt = lfile_components ; elt ; )
	{
	string_trim(buf, elt->e_name, 12);
	if (string_equal(name, buf))
		{
		/* set skip bit in this one; we always add new 
		   entries at the end */
		elt->skip = why;
		found++;
		}
	if (elt)
		elt = elt->e_next;
	}
  return(found);
}

/* return 1 if replaced old entry */
int add_entry(name, nbytes)
char * name;
int nbytes;
{
  struct entry * elt;
  struct entry * prev_elt;
  int replaced;
  char buf[14];

  prev_elt = 
#ifdef M6502
		NULL;
#else
		(struct entry *)NULL;
#endif
  replaced = del_entry(name, SKIP_REPLACE);
  prev_elt = last_entry();

  /* now make the new one */
  elt = 
#ifdef M6502
	malloc(sizeof(struct entry));
#else
	(struct entry *)malloc(sizeof(struct entry));
#endif

#ifdef M6502
/* dump drivespec if any */
  {
    char * p;
    
    if (p = strchr(name, ':'))
    	name = p + 1;
  }
#endif

/*  elt->e_name = string_copy(name); */
  string_pad(elt->e_name, name, 12);
  elt->new = 1;				/* get this from file, not libr */
  elt->e_nbytes = nbytes;
  if (prev_elt)
	prev_elt->e_next = elt;
    else
	lfile_components = elt;
  return(replaced);
}

/* on 800, this will have to read and count the whole file,
   unless we're using Spartados */
int file_nbytes(name)
char * name;
{
#ifdef M6502
  char buf[64];
  int fd;
  int siz, total;
  
  fd = open_carefully(name, O_RDONLY, 1);
  for (total = 0 ; ((siz = read(fd, buf, 64)) > 0) ; )
	total += siz;
  printf("close->%x\n", close(fd));
  printf("%s is %d bytes long\n", name, total);
  return(total);
#else
  struct stat st;
  
  if (stat(name, &st) < 0)
	return(-1);
    else
	return(st.st_size);
#endif
}

/* zzz */

#ifdef M6502
barf()
{
  int * arg;
  
  arg = &arg;		/* point at self */
  arg += arg[1] + 1;	/* add next word; arg count, plus 1 */
  fprintf(stderr, *arg, *--arg, *--arg, *--arg);
  fprintf(stderr, "\n");
  exit(1);
}

#else
barf(msg, arg1, arg2, arg3)
{
  fprintf(stderr, msg, arg1, arg2, arg3);
  fprintf(stderr, "\n");
  exit(1);
}
#endif

/* open libr and generate the initial components list */
read_library(must_exist)
int must_exist;
{
  int i;

  lfile_components = 
#ifdef M6502
			NULL;
#else
			(struct entry * )NULL;
#endif
  in_lfd = open_carefully(lfname, O_RDONLY, must_exist);
/*
#ifdef M6502
  printf("read_lib: fd %x\n", fd);
#endif
*/
  if (in_lfd > 0)			/* open it ok? */
	{
	read_header(in_lfd);		/* read header */
	build_modlist(in_lfd);
	return;
	}
    else
	{
	if (must_exist)
		barf("Can't open %s", lfname);
	    else
		{
		fprintf(stderr, "Creating library %s\n", lfname);
		in_lfd = 0;
		lfile_components = (struct entry * )NULL;
		}
	}
}

/* using the components_list, write a new library */

write_library()
{
  struct entry * elt;
  int copy_lfd;
  char buf[64];
  int size;
      
  out_lfd = open_carefully("libr65.tmp", O_WRONLY | O_CREAT | O_TRUNC, 0);

  lfile.l_header = LIBR_HEADER;
  lfile.type = LT_OLB;		/* fixed, for now */
  lfile.flags = 0;
/* refresh module count */
  lfile.n_modules = 0;
  for (elt = lfile_components ; elt ; elt = elt->e_next)
	if (!elt->skip)			/* not skipping this one? */
	  	lfile.n_modules++;	/* then count it */

/* write the header */
  write16(lfile.l_header);
  write16(lfile.type);
  write16(lfile.n_modules);
  write16(lfile.flags);

/* write the dictionary */
  for (elt = lfile_components ; elt ; elt = elt->e_next)
	if (!elt->skip)
		{
		write(out_lfd, elt->e_name, 12);
		write16(elt->e_nbytes);
		}

/* write the modules themselves */
  for (elt = lfile_components ; elt ; elt = elt->e_next)
	{
	string_trim(buf, elt->e_name, 12);
	if (elt->skip)
		{
		/* skip this entry in the original library */
		fprintf(stderr, "%s %s\n", skip_names[elt->skip], buf);
		size = elt->e_nbytes;
		for ( ; size > 0 ; size -= 64)
			read(in_lfd, buf, min(size, 64));
		}
	    else
		{
		if (elt->new)
			{
			/* new file.  open the obj file */
			fprintf(stderr, "Adding %s\n", buf);
			obj_lfd = open_carefully(buf, O_RDONLY, 1);
			copy_lfd = obj_lfd;
			}
		    else
			{
			fprintf(stderr, "Copying %s\n", buf);
			copy_lfd = in_lfd;
			}
		/* run the copy loop */
		for (size = elt->e_nbytes ; size > 0 ; size -= 64)
			{
			read(copy_lfd, buf, min(size, 64));
			write(out_lfd, buf, min(size, 64));
			}
		/* if have an obj file open, close it */
		if (obj_lfd > 0)
			{
			close(obj_lfd);
			obj_lfd = 0;
			}
		}
	}
  /* copy writing.  close libr file */
  close(out_lfd);
  printf("closing in_lfd %d\n", in_lfd);
  if (in_lfd > 0)
  	close(in_lfd);
  printf("del '%s'->%x\n", lfname, unlink(lfname));
  printf("ren '%s','%s'->%x\n", "libr65.tmp", lfname, 
	rename("libr65.tmp", lfname));
  out_lfd = 0;
  printf("New library written\n");
}
	
/* routines to really do things */

add_files(argc, argv)
int argc;
char ** argv;
{
  int i, nbytes, found;
  
  if (argc <= 3)
	barf("Add what files?");
  found = 0;
  read_library(0);		/* read the library */
  for (i = 3 ; i < argc ; i++)
	{
	nbytes = file_nbytes(argv[i]);
	if (nbytes < 0)
		fprintf(stderr, "%s not found", argv[i]);
	    else
		{
		found++;
		add_entry(argv[i], nbytes);
		}
	}
  if (found = 0)
	fprintf(stderr, "No files added");
    else
	write_library();
  printf("Finished adding files\n");
}

/* delete some library members */
del_files(argc, argv)
int argc;
char ** argv;
{
  struct entry * elt;
  int i, deleted;
  
  if (argc <= 3)
	barf("Delete what files?");
  read_library(1);
  for (i = 3, deleted = 0 ; i < argc ; i++)
	if (del_entry(argv[i], SKIP_DELETE))
		deleted++;
	    else
		fprintf(stderr, "Library contains no member %s\n", argv[i]);
  if (deleted)
	write_library();
    else
	fprintf(stderr, "No members matched");
   close(in_lfd);
}

list_library()
{
  struct entry * elt;
  char buf[80];
  int i;
  
  read_library(1);
  close(in_lfd);
  strcpy(buf, lfname);
  string_upcase(buf);
  printf("  Library %s\n", buf);
  printf("  ------------  -----\n");
  for (elt = lfile_components ; elt ; elt = elt->e_next)
	{
	buf[12] = '\0';
	for (i = 0 ; i < 12 ; i++)
		{
		buf[i] = elt->e_name[i];
/*
printf("idx %d chr %c buf '%s'\n", i, elt->e_name[i], buf);
*/
		}
	printf("  %12s  %5d\n", buf, elt->e_nbytes);
	}
}

print_version()
{
  printf("Libr65 v %s\n", VERSION);
}

/* main body */

#ifdef M6502
char cmdline[80];
#endif

main(argc, argv)
int argc;
char ** argv;
{
  char op;			/* what we're doing to a file */

#ifdef M6502
/* might not be able to get cmd line from os, so prompt for it */
  if (argc == 0)
	argc = readargs("LIBR65>", cmdline, argv + 1) + 1;
#endif

  if (argc < 3)
	barf("Try LIBR65 <option> <file>");

  op = *argv[1];
  lfname = argv[2];

  switch (op)
	{
	case 'a': case 'A':
		add_files(argc, argv);
		break;
	case 'd': case 'D':
		del_files(argc, argv);
		break;
	case 'l': case 'L':
		list_library();
		break;
	case 'v': case 'V':
		print_version();
		break;
	default:
		barf("I don't understand option '%c'", op);
	}
  exit(0);
}

