/************************************************************************ 
*                                                                       * 
*                                                                       * 
*                      Filename:        uniq.c                          *      
*									*
*  uniq - compact repeated lines		Author: John Woods      *
*									*
*  	( looks like UNIX version )					*
*									*
* Written 02/08/86 by John Woods, placed into public domain.  Enjoy.    *
*									*
* Amiga port by Gary Duncan , Canberra Australia                        * 
*                                                                       * 
*                                                                       * 
*-----------------------------------------------------------------------* 
*
*
* 
* Modification record: 
*~~~~~~~~~~~~~~~~~~~~
* 
* Date         By whom             Change 
* ----         -------             ------ 
* 
*		JW		   Original 
* 04Jan90 	GMD		   Amiga port : reformatted, headers 
* 						added , usage() added.	
*						+/-n option bug fixed
*
* Function    Description
* ~~~~~~~~    ~~~~~~~~~~~ 
*+ main  
*+ skip 
*+ equal
*+ show
*+ uniq  
*+ usage
*+ getline
*+ xfopen 
*+  
*
*
*******************************************************************************/

#include "stdio.h"
#include "ctype.h"

/*
 *	give a version # ( GMD )
 */
#define	VERS	"1.1"

/* If the symbol WRITE_ERROR is defined, uniq will exit(1) if it gets a
 * write error on the output.  This is not (of course) how V7 uniq does it,
 * so undefine the symbol if you want to lose your output to a full disk
 */

#define WRITE_ERROR 1
#define GBUF 1024 

extern char *MakeDate[] ;

FILE *fopen();
FILE *xfopen();

char xbuffer [BUFSIZ];
int xuflag = 1;			/* default is union of -d and -u outputs */
int xdflag = 1;			/* flags are mutually exclusive */
int xcflag = 0;
int xfields = 0;
int xchars = 0;


char *xcurline ;
char *xprevline ;
char xbuf1 [GBUF]  ;
char xbuf2 [GBUF]  ;

/*************************************************************************** 
 
 
  Name :      	main

  Purpose:	..every C prog deserves one..


  Entry    :           
 
 
  Returns  :           
                               
 
 
****************************************************************************/ 


int
main(argc,argv)
char *argv[];
{
  char *p;
  int inf = -1, outf;


  if (argc == 1 )
  {
	usage() ;
	exit (1) ;
  }	

  setbuf(stdout, xbuffer);
  
  for (--argc, ++argv; (argc > 0) && (**argv == '-' || **argv == '+');
       --argc, ++argv) 
  {
  	if (**argv == '+')
  		xchars = atoi(*argv + 1);

  	else if (isdigit(argv[0][1]))
  		xfields = atoi(*argv + 1);

  	else if (argv[0][1] == '\0')
  		inf = 0;		/* - is stdin */

  	else
	  {
  		for (p = *argv+1; *p; p++) {
  			switch(*p) {
  			case 'd':	
				xdflag = 1; 
				xuflag = 0; 	
				break;
  			case 'u':	
				xuflag = 1; 
				xdflag = 0; 
				break;
  			case 'c':	
				xcflag = 1; 
				break;
  			default:	
				usage();
				exit(1) ;
  		}
	     }
  	}
  }
  /* input file */
  if (argc == 0)
	inf = 0;
  else
	if (inf == -1) {		/* if - was not given */
		fclose(stdin);
		xfopen(*argv++, "r");
		argc--;
  	}

  if (argc == 0) 
	outf = 1;
  else {
  	fclose(stdout);
 	xfopen(*argv++, "w");
 	argc--;
  }

  uniq();
  fflush(stdout);
  exit(0);
}

/*************************************************************************** 
 
 
  Name :      	skip

  Purpose:
		+- n options ; skip fields / charas in fields

  Entry    :           
 
 
  Returns  :           
		ptr to (+nth) chara  in (-nth)field
		or NULL if not found                                
 
 
****************************************************************************/ 



char *skip(s)

char *s;
{
int n;
  
  /* skip xfields */

  for (n = xfields; n > 0 ; --n) 
  {
							/* skip blanks */
	while (*s && (*s == ' ' || *s == '\t')) 
		s++;

	if (!*s)
		 return s;

	while (*s && (*s != ' ' && *s != '\t')) 
		s++;

	if (!*s) 
		return s;
  }
  /* skip characters */
  
  for (n = xchars; n > 0; --n) {
	if (!*s) 
		return s;
	s++;
  }
  return s;
}

/*************************************************************************** 
 
 
  Name :      	equal

  Purpose:
		tests 2 strings for equality

  Entry    :           
 
 
  Returns  :           
                               
 
 
****************************************************************************/ 



int equal(s1, s2) 

char *s1, *s2;
{
  return !strcmp( skip(s1), skip(s2));
}

/*************************************************************************** 
 
 
  Name :      	show

  Purpose:


  Entry    :           
 
 
  Returns  :           
                               
 
 
****************************************************************************/ 



void
show(line,count) 

int  count ;
char *line; 
{
  if (xcflag) 
	printf("%4d %s", count,line);
  else {
	if ((xuflag && count == 1) || (xdflag && count != 1))
		printf("%s", line);
  }
}

/*************************************************************************** 
 
 
  Name :      	uniq

  Purpose:


  Entry    :           
 
 
  Returns  :           
                               
 
 
****************************************************************************/ 




int
uniq()
{
  char *ptr;
  int seen = 1 ;

  /* initialise "prev" line ( setup ?? ) */

  xprevline = xbuf1;
  if (getline(xprevline, GBUF) < 0) 
	return(0);

  xcurline = xbuf2;

  /* get xcurline and compare 
   * if not equal, dump xprevline and swap pointers 
   * else continue, bumping seen count
   */
  while (getline(xcurline, GBUF) > 0) {
	if (!equal(xprevline, xcurline)) {
  		show(xprevline, seen);
	  	seen = 1;
  		ptr = xcurline;
	  	xcurline = xprevline;
  		xprevline = ptr;
	} 
	else
  		seen += 1;
  }
  show(xprevline, seen);
  return 0;
}

/*************************************************************************** 
 
 
  Name :      	getline

  Purpose:


  Entry    :           
 
 
  Returns  :           
			-1 ; EOF                               
			# of charas read 
 
****************************************************************************/ 




int getline(buf,count)

char *buf;
int count;
{
char c;
char * cats = buf ;
int ct = 0;

    while (ct++ < count) 
    {
  	c = getc(stdin);
  	if (c <= 0) 
		return(-1);
  	*cats++ = c;
  	if (c == '\n') 
        {
	   *cats++ = 0;
	   /*
	    *	read another line if no 'n' compliance
	    */
	    if ( xchars || xfields )
	    {
		if ( *skip(buf) == '\0' )
		{
		   ct = 0 ;
		   cats = buf ;
		}
		else		/* exit */
		   return (ct) ;
	    }
	    else
		return (ct) ;  
	}
     }
  return(ct);

}
  

/*************************************************************************** 
 
 
  Name :      	xfopen

  Purpose:


  Entry    :           
 
 
  Returns  :           
                               
 
 
v****************************************************************************/ 



FILE *xfopen(fn, mode)

char *fn, *mode; 
{
  FILE *p;
  extern int errno;
  extern char *sys_errlist[];

  if ((p = fopen(fn,mode))  == NULL) {
  	perror("uniq");
	fflush(stdout);
	exit(1);
  }
  return (p);
}

/*************************************************************************** 
 
 
  Name :      	usage

  Purpose:


  Entry    :           
 
 
  Returns  :           
                               
 
 
****************************************************************************/ 


int
usage()

{
static char *rats[] = {
  "      - Written by John Woods (Amiga port by Gary Duncan)\n",
  "usage:	uniq [-udc] [+n] [-n] [input [output]]" ,
  "             -c ; print lines , count repeated ones",
  "             -d ; print first occurrence of replicated lines only (default)",
  "             -u ; print only unique lines",
  "             +n ; skip n charas from start of field",
  "             -n ; skip n fields from start of line",
  ""
	} ;

int	j = 0 ;
char * ptr  ;


	fprintf(stderr, "uniq: Version %s [ %s ]\n" , VERS , MakeDate[0] ) ;
	
  	while ( *(ptr=rats[j++]) )
  		fprintf(stderr,"%s\n" , ptr );
}
