/***************************************************************************
* WFile.c : Work file - work (do things) on (ASCII-) files                 *
*									   *
*      - add or remove CRs (0x0d)                                          *
*      - expand tabs to multiple spaces or vice versa			   *
*      - change foreign symbols and special characters			   *
*									   *
* V1.00 - 12-Jan-91 Joerg Fenin / Metalworx				   *
***************************************************************************/

/* --------------------- source code revisions, tracked by RCS ---------- */

/* $Header: Hard0:C-Compiler/src/wfile/rcs/wfile.c,v 1.0 91/08/12 20:28:34 Mtwx Exp $ */
/* $Log:	wfile.c,v $
 * Revision 1.0  91/08/12  20:28:34  Mtwx
 * Initial revision
 *  */

/* -------------------------- Includes ---------------------------------- */

#include <stdio.h>
#ifdef AMIGA
#include <ctype.h>
#include <exec/types.h>
#endif

#include "wfile.h"

/* -------------------------- Defines ----------------------------------- */

#ifdef UNIX
#define TRUE 1
#define FALSE 0
typedef short BOOL;
#endif

#ifdef MSDOS
#define TRUE 1
#define FALSE 0
typedef short BOOL;
#endif

/* -------------------------- global variables -------------------------- */

unsigned char ch_src_sym[MAXCHANGE]; /* Change table for foreign symbols */
unsigned char ch_trg_sym[MAXCHANGE];
unsigned char final_chr;  /* takes desired final char if specified */
char *templ_argv[MAXOPTIONS];	/* Array of pointers for templated
				   argvs */
int change_count=0;	    /* counter for changes to be done */
#ifdef AMIGA
char color_on[]="\033[2m";
char color_off[]="\033[0m";
#else
char color_on[];
char color_off[];
#endif

/* -------------------------- forward references ------------------------ */

void usage(),parse_change();
#ifndef __SASC
char *strupr();
#endif
int workfile();

/* -------------------------- external references ----------------------- */

extern int Revision;
extern char Version[],Date[];

extern int parse_profile(),unix2msdos(),msdos2unix();
extern void whelp();

/* -------------------------- main control loop ------------------------- */

int main(argc,argv)
int argc;
char *argv[];
{
  int ExpFactor=2,i,j,num_options=0,Options=0,tabsize, t_argc;
  long len;
  char *rbuf,*wbuf,backup_filename[100], pro_filename[100],
       *argument[MAXOPTIONS];
  FILE *file;

  printf("\n%s %s/%d - %s  (C) 1991 Metalworx\n",argv[0],Version,Revision,
	 Date);

/* evaluate command line */
  if(argc < 2)
  {
    usage(argv[0]);
    exit(1);
  }

  if(strcmp(argv[1],"-?")==NULL)
  {
    whelp();
    exit(1);
  }

  if(argc>2)
  {
    for(i=2;i<argc;i++,num_options++)
    {
      argument[num_options]=(char *)malloc(strlen(argv[i])+1);
      strcpy(argument[num_options],argv[i]);
    }
    for(i=0;i<num_options;i++)
    {
      if(toupper(argument[i][1])=='F')  /* read commands from profile */
      {
	if(strlen(&argument[i][2]))             /* profile name given */
	  strcpy(pro_filename,&argument[i][2]);
	else
	  strcpy(pro_filename,"wfile.pro");     /* default profile name */
	t_argc=parse_profile(pro_filename);
	for(j=0;j<t_argc;j++)
	  argument[num_options++]=templ_argv[j];
	continue;
      }

      if(strcmp(strupr(&argument[i][1]),"AI")==NULL)
      {
	t_argc=amiga2msdos(argument[i]);
	for(j=0;j<t_argc;j++)
	  argument[num_options++]=templ_argv[j];
	continue;
      }

      if(strcmp(strupr(&argument[i][1]),"IA")==NULL)
      {
	t_argc=msdos2amiga(argument[i]);
	for(j=0;j<t_argc;j++)
	  argument[num_options++]=templ_argv[j];
	continue;
      }

      if(strcmp(strupr(&argument[i][1]),"AU")==NULL)
      {
	t_argc=amiga2unix(argument[i]);
	for(j=0;j<t_argc;j++)
	  argument[num_options++]=templ_argv[j];
	continue;
      }

      if(strcmp(strupr(&argument[i][1]),"UA")==NULL)
      {
	t_argc=unix2amiga(argument[i]);
	for(j=0;j<t_argc;j++)
	  argument[num_options++]=templ_argv[j];
	continue;
      }

      if(strcmp(strupr(&argument[i][1]),"UI")==NULL)
      {
	t_argc=unix2msdos(argument[i]);
	for(j=0;j<t_argc;j++)
	  argument[num_options++]=templ_argv[j];
	continue;
      }

      if(strcmp(strupr(&argument[i][1]),"IU")==NULL)
      {
	t_argc=msdos2unix(argument[i]);
	for(j=0;j<t_argc;j++)
	  argument[num_options++]=templ_argv[j];
	continue;
      }

/* add final character */
      if(argument[i][0]=='+' && isdigit(argument[i][1]))
      {
	Options|=ADD_FINAL_CHR;
	final_chr=atoi(&argument[i][1]);
      }

/* change (foreign) characters ? */
      if(strpos(argument[i],"=") > -1)
      {
	parse_change(argument[i],change_count);
	change_count++;
      }

      if(strcmp(strupr(&argument[i][1]),"CR")==NULL)
      {
	switch(argument[i][0])
	{
	  case '+':
	    Options|=ADDCR;
	    break;
	  case '-':
	    Options|=REMCR;
	    break;
	  default:
	    Options|=CRMODE;
	    printf("Unknown \"CR\" mode! %s assumed.\n",CRTEXT);
	    break;
	}
      }

      if(toupper(argument[i][1])=='T')
      {
	tabsize=atoi(&argument[i][2]);
	if(tabsize==0)
	{
	  tabsize=TABSIZE;
	  printf("Tabsize %d assumed.\n",tabsize);
	}
	if(tabsize==1)
	{
	  puts("Tabsize of 1 doesn't make sense! - Try something from 2 upwards.");
	  exit(1);
	}
	switch(argument[i][0])
	{
	  case '+':
	    Options|=SHRSPACE;
	    break;
	  case '-':
	    Options|=EXPTABS;
	    break;
	  default:
	    Options|=TABMODE;
	    printf("Unknown \"TAB\" mode! %s assumed.\n",TABTEXT);
	    break;
	}
      }

      if(strcmp(argument[i][1],"0")==NULL)
	Options|=REAL_NULL;   /* really replace something against a NULL
				 char */

      if(toupper(argument[i][1])=='B')
	Options|=NO_BACKUP;
    }
  }
  if(change_count)
    Options|=CHANGE_FOREIGN;

/* now check presence of file (argv[1]) and get length */

  if ((file = fopen(argv[1], "r")) == NULL)
  {
    perror(argv[1]);
    exit(1);
  }
  fseek(file, 0, 2); /* set to EOF - 0 Bytes */
  len = ftell(file);
  printf("%s: %6ld bytes read, ", argv[1],len);
  fflush(stdout);
  if(len <= 0)
  {
    puts("File read error!");
    exit(1);
  }

/* allocate memory buffer */

  if(Options & EXPTABS)
    ExpFactor ++;
  if(Options & ADDCR)
    ExpFactor ++;
  rbuf = (char *)malloc(len*ExpFactor);
  if(rbuf==NULL)
  {
    printf("Couldn't get %d bytes of memory!!\n",len*ExpFactor);
    exit(1);
  }
  printf("%7ld bytes allocated, ",len*ExpFactor);
  fflush(stdout);

/* read file into buffer */

  fseek(file,0,0);  /* set to start of file */
  if(fread(rbuf,len,1,file) != 1)
  {
    perror(argv[1]);
    puts("read failed!!");
    exit(1);
  }
  fclose(file);

/* create backup of original file */

  if(!(Options & NO_BACKUP))
  {
    strcpy(backup_filename,argv[1]);
#ifdef AMIGA
    strcat(backup_filename,".bak");
#endif
#ifdef UNIX
    strcat(backup_filename,".bak");
#endif
#ifdef MSDOS
    i=strpos(argv[1],".");
    if(i>-1)
      strcat(&backup_filename[i],".bak");
    else
      strcat(backup_filename,".bak");
#endif
    if((file=fopen(backup_filename,"w"))==NULL)
    {
      perror(argv[1]);
      puts("opening of backup file failed!");
      exit(1);
    }
    else
      if(fwrite(rbuf,len,1,file) !=1)
      {
	perror(argv[1]);
	puts("write to backup file failed!");
	exit(1);
      }
  }

/* start up user desired routines */

  wbuf=rbuf+len;	  /* save start address of write buffer */
  len=workfile(rbuf,len,Options,tabsize);

/* write back file with new contents */

  if ((file=fopen(argv[1],"w")) == NULL)
  {
    perror(argv[1]);
    puts("open to write failed!");
    exit(1);
  }
  fflush(stdout);
  if(fwrite(wbuf,len,1,file) != 1)
  {
    perror(argv[1]);
    puts("write failed!");
    exit(1);
  }
  printf("%6ld bytes written\n",len);
  fclose(file);

/* clean up and exit */

  free(rbuf);
  printf("\n");
  exit(0);
}

/* -------------------------- referenced functions ---------------------- */

void parse_change(param,num)
char *param;
int num;
{
  int i;

  if(param[1]=='\'' && param[3]=='\'')    /* quoted character */
    ch_src_sym[num]=param[2];
  else					  /* decimal value */
    ch_src_sym[num]=atoi(&param[1]);
  i=strpos(param,"=");
  switch(param[i+1])
  {
    case '\'':
      ch_trg_sym[num]=param[i+2];
      break;
    default:
      ch_trg_sym[num]=atoi(&param[i+1]);
      break;
  }
}

void usage(name)
char *name;
{
  printf("Usage: %s < file| -? >  [<+|->CR] [<+|->T[size]] [-%sddd%s=%sddd%s [...] [-0]]\n",
	 name,color_on,color_off,color_on,color_off);
  printf("%*s [-b] [-f[file]] [-AI] [-IA] [-AU] [-UA] [-UI] [-IU] [+%sddd%s]\n",
	 strlen(name)+7," ",color_on,color_off);
  printf("\nValues in %sthis color%s must be entered as (decimal) ASCII values.\n",
	 color_on,color_off);
}

#ifndef __SASC
char *strupr(s)
char *s;
{
  register int i;
  char *us;

  if(strlen(s))
  {
    us=(char *)malloc(strlen(s)+1);
    if(us)
      for(i=0;i<=strlen(s);i++)
	us[i]=toupper(s[i]);
    return us;
  }
  else
    return NULL;
}
#endif

int workfile(buf,len,Options,tabsize)
register char *buf;
register long len;
int Options;
int tabsize;
{
  register char *ptr = buf + len;
  register int i,j,k,l;
  int nl_pos_i=-1,nl_pos_j=-1;
			  /*  holds buffer position of last newline */
  BOOL shr_possible,quoted=FALSE;

  for(i=j=0;i<len;i++)
  {
    switch(buf[i])
    {
      case 0x0d:
	if (Options & REMCR)
	  continue;
	else
	  ptr[j++]=0x0d;
	break;
      case 0x0a:
	if (Options & ADDCR)
	{
	  ptr[j++]=0x0d;
	  ptr[j++]=0x0a;
	}
	else
	  ptr[j++]=0x0a;
	nl_pos_i=i;	/* save pos of newline in read buffer  */
	nl_pos_j=j-1;	/* save pos of newline in write buffer */
	break;
      case '\t':
	if ((Options & EXPTABS) && (quoted==FALSE))
	{
	  l=j;		/* use origin position of write buffer */
	  for(k=l-nl_pos_j-1;
	      k < (((l-nl_pos_j-1)/tabsize+1) * tabsize);
	      k++)
	    ptr[j++]=' ';
	}
	else
	  ptr[j++]='\t';
	break;
      case ' ':
	if ((Options & SHRSPACE) && (quoted==FALSE))
	{
	  shr_possible=TRUE;
	  for(k=i-nl_pos_i-1,l=0;
	      k < (((i-nl_pos_i-1)/tabsize+1) * tabsize);
	      k++,l++)
	  {
	    if(buf[i+l] != ' ')
	      shr_possible=FALSE;
	  }
	  if(l==1)
	    shr_possible=FALSE; /* doesn't make sense to shrink one
				   SPACE into one TAB */
	  if(shr_possible==TRUE)
	  {
	    ptr[j++]='\t';
	    i+=l-1;
	  }
	  else
	    ptr[j++]=' ';
	}
	else
	  ptr[j++]=' ';
	break;
      case '\"':
	if(buf[i-1]!='\\')  /* not quoted itself */
	  quoted=1-quoted;  /* negate quotation status */
	ptr[j++]='\"';
	break;
      default:
	if(Options & CHANGE_FOREIGN)
	{
	  l=FALSE;		/* set found indicator to NOT FOUND */
	  for(k=0;k<change_count;k++)
	    if(buf[i]==ch_src_sym[k])
	    {
	      l=TRUE;		/* set found indicator to FOUND */
	      if(ch_trg_sym[k]!='\0') /* anything else than a NULL char ? */
		ptr[j++]=ch_trg_sym[k];
	      else if(Options & REAL_NULL) /* should the NULL really be
					      inserted ? */
		ptr[j++]=ch_trg_sym[k];
	    }
	  if(l==FALSE)          /* not a symbol that is to be changed */
	    ptr[j++]=buf[i];
	}
	else
	  ptr[j++]=buf[i];
	break;
    }
  }
  if(Options & ADD_FINAL_CHR)
    ptr[j++]=final_chr;
  return j;
}
