#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include "cmdline.h"
#include "umem.h"
#include "errors.h"
#include "libm.h"
#include "module.h"
#include "dict.h"
#include "lib.h"

#define VERSION 120

extern HASHREC **publichash;
extern int pagesize;
/* Command line parameters */
BOOL prm_case_sensitive = TRUE;		/* True if case sensitive */
uint prm_errcount = 25;

/* Global varieables */
LIST *objlist = 0;			/* List of object files */
LIST *attriblist = 0;
char *libfile = 0;			/* EXE file name */

char *usage_text = "[/u/pxx]  library [ [*] [+] [-] file] list [@filename]\n"
    "\t/u  - make non-case sensitive dictionary\n"
		"\t/pxx- set page size to xx (default 16)\n"
		"\t *  - extract a file\n"
		"\t +  - add a file\n"
		"\t -  - delete a file\n"
		"\t-+  - replace a file\n\n"
		"\t @filename reads paramaters from file\n\n"
		"\t XLIB will create an import section if a filename with\n"
		"\t    a .DEF extension is used\n" ;


void BoolSetup(char select, char *string);
void PageSetup(char select, char *string);

ARGLIST ArgList[] = {
  { 'u', ARG_BOOL, BoolSetup },
	{ 'p', ARG_CONCATSTRING, PageSetup },
  { 0, 0, 0 }
} ;
/*
 * Setup for boolean command line args
 */
static void BoolSetup(char select, char *string)
{
  switch(select) {
    case 'u':
			prm_case_sensitive = FALSE;
			break;
  }
}
void PageSetup(char select, char *string)
{
	int val = atoi(string);
	int i,j=0, mask = 1;
	if (val < 16)
		fatal("Page size too small");
	for (i=0; i < sizeof(short)*8; i++) {
		if (val & mask)
			j++;
		mask <<=1;
	}
	if (j != 1)
		fatal("Page size must be multiple of 2");
}
/*
 * Insert a file onto one of the lists.  .LIB files go on library list,
 *   anything else is assumed an .obj file regardless of extension
 */
static void InsertAnyFile(char *filename, int attribs)
{
  char *newbuffer, buffer[100];

  /* Allocate buffer and make .o if no extension */
	strcpy(buffer,filename);
  AddExt(buffer,".obj");
  newbuffer = (char *) AllocateMemory(strlen(buffer) + 1);
  strcpy(newbuffer,buffer);

  /* Insert file */
	AppendToList(&objlist,newbuffer);
	AppendToList(&attriblist, (void *)attribs);
}
/* 
 * Parse a line of commands and files
 */
static BOOL ReadArgs(int *argc, char **argv, int *mode, int *maxmode, BOOL uselib)
{
	BOOL rv;
	int i,st=1;
	/* minus signs will be eaten by parse_args but we want them to propogate
	 * through, so we change them to '#'
	 */
	for (i=1; i < *argc; i++) {
		char *t;
		while ((t = strchr(argv[i],'-')) != 0)
			*t = '#';
		while ((t = strchr(argv[i],'+')) != 0)
			*t = '%';
	}
  /* Scan command line for switches */
  rv = parse_args(argc,argv,TRUE);
	if (uselib) {
		char buffer[100];
		if (*argc < 2)
			return(FALSE);
		strcpy(buffer,argv[1]);
		AddExt(buffer,".LIB");
		libfile = AllocateMemory(strlen(buffer)+1);
		strcpy(libfile,buffer);
		st++;
	}

  /* Scan the command line for file names or response files */
  for (i=st; i < *argc; i++) {
    char *p = argv[i];
    while (*p)
      *p++ = (char)toupper(*p);
		p = argv[i];
		if (*p == '*' || *p == '#' || *p == '%') {
			*mode = 0;
			while (*p == '*' || *p == '#' || *p == '%') {
				if (*p++ == '*') {
					*mode |= MD_EXTRACT;
				}
				else
					if (*(p-1) == '#') {
						*mode |= MD_DELETE;
					}
					else {
						*mode |= MD_INSERT;
					}
			}
		}
    if (*p == '@') {
      rv &= ReadResponse(&argv[i][1],mode,maxmode);
    }
    else
			if (*p != '&' && *p > 31) {
		  	InsertAnyFile(p,*mode);
			}
		*maxmode |= *mode;
  }
	return(rv);
}
/*
 * Read a line of ascii text from a file
 *   Get rid of \n
 */
static void ReadLine(char *buffer, int count, FILE *file, char *name)
{
  char *pos;
  *buffer = 0;
  fgets(buffer,count,file);
  pos = buffer + strlen(buffer) -1;
  /* The test is needed because my editor doesn't put CR/LF at the end of file */
	if (*pos <32)
    *pos = 0;
}
/*
 * Read the response file
 */
static BOOL ReadResponse(char *filename, int *mode, int *maxmode)
{
  FILE *in;
	BOOL rv = TRUE;

  /* Open file */
  if ((in = fopen(filename,"r")) ==0)
    fatal("Missing or invalid response file %s", filename);

  /* Read EXE file name */
	while (!feof(in)) {
		int argc=1;
		int i = 0;
		char *argv[100];
  	char buffer[100];
  	ReadLine(buffer,100,in,filename);
		while (TRUE) {
			char ch;
			while ( buffer[i] == ' ')
				i++;
			if (buffer[i] < 32)
				break;
			argv[argc++] = &buffer[i];
			while (buffer[i] > 32)
				i++;
			ch = buffer[i];
			buffer[i++] = 0;
			if (ch < 32) {
				break;
			}
		}
		if (argc > 1)
			rv &= ReadArgs(&argc,argv,mode,maxmode,FALSE);
	}
  fclose(in);
	return(rv);
}
/*
 * Main routine
 *   Read command line
 *   Make EXE and MAP filenames if not already extant
 *   Pass 1 init
 *   Pass 1
 *   Pass 1 rundown
 *   pass 2 init
 *   Pass 2
 *   Pass 2 rundown
 */
int main(int argc, char *argv[])
{
	int mode=MD_INSERT, maxmode=0;

	banner(VMSG("XLIB"));

	if (!ReadArgs(&argc,argv,&mode,&maxmode,TRUE))
		usage(argv[0]);
	if (maxmode) {
		publichash = CreateHashTable(HASH_TABLE_SIZE);
		ReadLib(maxmode,libfile);
		Extract(maxmode,libfile);
		Delete(maxmode,libfile);
		Insert(maxmode,libfile);
		CalculateDictionary();
		OutputLibrary(libfile);
		RemoveHashTable(publichash);
	}
	else
		usage(argv[0]);
	return(0);
}