/*  Copyright 1981 Gary Perlman */

#include "stat.h"
#include <math.h>

PGM(perm,Permute Line Order,5.5,6/12/86)

#define MAXCHARS BUFSIZ           /* maximum number of chars in lines */
#define MAXCHARS 25000           /* maximum number of chars in lines */
unsigned	Maxlines = 25000;

int 	Seed;        /* random seed that might be set on command line */
Boole	Alphasort;   /* sort lines alphabetically */
Boole	Intsort;     /* sort lines as integers */
Boole	Expsort;     /* sort lines as real numbers */
Boole	Numsort;     /* sort as xxx.yyy numbers */
Boole	Sort;        /* true if any sorting is to be done */
Boole	Reverse;     /* multiplying factor to reverse the order of any sort */
Boole	Caseignore;  /* ignore case when making alpha comparisons */
Boole	InfoVersion;          /* print version information */
Boole	InfoLimits;           /* print program limits */
Boole	InfoOptions;          /* print usage information */


#define sortwith(x) qsort ((char *) lptr, nlines, sizeof (*lptr), x)
#define	compare(name,fun,arg1,arg2) \
	int name (sp1, sp2) char **sp1, **sp2; { return (fun (*arg1, *arg2)); }

compare (alcmp,  strcmp, sp1, sp2)
compare (ralcmp, strcmp, sp2, sp1)

compare (cialcmp,  cistrcmp, sp1, sp2) /* case insensitive alpha compare */
compare (ciralcmp, cistrcmp, sp2, sp1) /* case insensitive reverse alpha */

compare (ncmp,  numcmp, sp1, sp2)
compare (rncmp, numcmp, sp2, sp1)

#define	diffint(a,b) (atoi(a) - atoi(b))
compare (intcmp,  diffint, sp1, sp2)
compare (rintcmp, diffint, sp2, sp1)

#define diffloat(a,b) ((atof (a) - atof (b)) < 0 ? -1 : 1)
compare (fltcmp,  diffloat, sp1, sp2)
compare (rfltcmp, diffloat, sp2, sp1)

main (argc, argv) char **argv;
	{
	char	**lptr;
	int 	nlines;
	int 	lineno;
#ifdef NEVER
	int 	fltcmp (), alcmp (), intcmp ();
	int 	rfltcmp (), ralcmp (), rintcmp ();
#endif

	ARGV0;
	initial (argc, argv);
	checkstdin ();
	nlines = readlines (&lptr, Maxlines, stdin);
	if (nlines > Maxlines)
		ERRMANY (lines, Maxlines)
	if (nlines < 0)
		ERRSPACE (lines)
	if (Sort)
		{
		if (Alphasort)
			{
			if (Caseignore)
				if (Reverse) sortwith (ciralcmp);
				else         sortwith (cialcmp);
			else
				if (Reverse) sortwith (ralcmp);
				else         sortwith (alcmp);
			}
		else if (Intsort)
			if (Reverse) sortwith (rintcmp);
			else         sortwith (intcmp);
		else if (Numsort)
			if (Reverse) sortwith (rncmp);
			else         sortwith (ncmp);
		else if (Expsort)
			if (Reverse) sortwith (rfltcmp);
			else         sortwith (fltcmp);
		}
    else
		strpermut (lptr, nlines);
	for (lineno = 0; lineno < nlines; lineno++)
		puts (lptr[lineno]);
	exit (0);
	}

/*FUNCTION initial: returns local version of optind, index to first operand */
int
initial (argc, argv) char **argv;
	{
	extern	char *optarg;    /* option value accessed through this by getopt */
	extern	int  optind;     /* will be index to first operand */
	int 	opterr = 0;      /* count of number of errors */
	int 	flag;            /* option flag characters read in here */

	while ((flag = getopt (argc, argv, "aceil:nrs:LOV")) != EOF)
		switch (flag)
			{
			default:
				opterr++;
				break;
			/* put option cases here */
			case 'O': InfoOptions = TRUE; break;
			case 'V': InfoVersion = TRUE; break;
			case 'L': InfoLimits = TRUE; break;
			case 'c':
				Caseignore = TRUE;
				/* FALLTHROUGH */
			case 'a':
				Alphasort = TRUE;
				Numsort = Intsort = Expsort = FALSE;
				break;
			case 'e':
				Expsort = TRUE;
				Numsort = Alphasort = Intsort = FALSE;
				break;
			case 'i':
				Intsort = TRUE;
				Numsort = Alphasort = Expsort = FALSE;
				break;
			case 'l':
				if (setint (Argv0, 'l', optarg, &Maxlines, 1, MAXINT))
					opterr++;
				break;
			case 'n':
				Numsort = TRUE;
				Intsort = Alphasort = Expsort = FALSE;
				break;
			case 'r': Reverse = TRUE; break;
			case 's':
				if (setint (argv[0], flag, optarg, &Seed, 1, MAXINT))
					opterr++;
				break;
			}

	if (opterr) /* print usage message and bail out */
		USAGE ("[-a | -c | -e | -i | -n] [-r] [-l lines] [-s seed]")

	Sort = Alphasort || Intsort || Numsort || Expsort;
	if (Reverse && !Sort)
		WARNING (reversal option has no effect without sorting option)
	if (Sort && Seed != 0)
		WARNING (setting random seed has no effect when sorting)

	usinfo ();

	ERROPT (optind)

	return (optind);
	}


strpermut (array, size) char *array[];
	{
	int 	i, irand;
	char	*temp;
	double  Maxrand = 2147483647;
	int 	randval;

	srandom (Seed);
	for (i = 0; i < size; i++)
		{
		while ((randval = random ()) < 0);
		irand = (int) (randval / Maxrand * (size-i)) + i;
		temp = array[i];
		array[i] = array[irand];
		array[irand] = temp;
		}
	}


usinfo ()
	{
	if (InfoVersion)
		pver (Version);
	if (InfoLimits)
		{
		plim (Argv0);
		const (MAXCHARS, "maximum number of characters in lines");
		const (Maxlines, "maximum number of input lines");
		}
	if (InfoOptions)
		{
		ppgm (Argv0, Purpose);
		lopt ('a', "order lines alphabetically", Alphasort);
		lopt ('c', "case insensitive alphabetic ordering", Caseignore);
		lopt ('e', "order lines as xxx.yyyEzzz numbers", Expsort);
		lopt ('i', "order lines as integers", Intsort);
		iopt ('l', "lines", "maximum number of input lines", Maxlines);
		lopt ('n', "order lines as xxx.yyy numbers", Numsort);
		lopt ('r', "reverse sorting order of lines", Reverse);
		iopt ('s', "seed", "integer random number generator seed", Seed);
		}
	if (InfoVersion || InfoLimits || InfoOptions)
		exit (0);
	}
