/* $Id: main.c,v 1.14 1998/10/17 14:51:28 fraserm Exp $
   $Log: main.c,v $
   Revision 1.14  1998/10/17 14:51:28  fraserm
   -l bug caused by splitting word option processing

   Revision 1.13  1998/10/17 14:34:45  fraserm
   type mismatches

   Revision 1.12  1998/10/17 13:24:50  fraserm
   ANSI-fied the code
   added "-" option to allow futrther options from standard input

   Revision 1.11  1998/03/08 22:44:13  fraserm
   screen refresh by interval timer for xagm
   doesn't sort dictionary unless it has to

   Revision 1.10  1998/03/07 16:29:53  fraserm
   in so I can get it out again

   Revision 1.9  1997/02/24 23:41:57  fraser
   minor updates gforbefore upload

   Revision 1.8  1996/10/25 19:11:37  fraser
   updates changing spinny line to 2 digit percentage

 * Revision 1.7  1996/10/23  15:55:34  fraser
 * missing ')' in usage text
 *
 * Revision 1.6  1996/10/23  15:42:33  fraser
 * bugfix: default average word length restriction still applied even
 * in # of words mode
 *
 * Revision 1.5  1996/10/04  17:40:08  fraser
 * changed time output from elapsed to CPU (user+system) and page/swap
 *
 * Revision 1.4  1996/09/30  21:01:22  fraser
 * bugfix: -c didn't reset -l speclen to zero
 *
 * Revision 1.3  1996/09/27  14:00:23  fraser
 * revised command-line switches; may now prefix each target
 * with -c, -l, -h, and -p
 *
 * Revision 1.2  1996/09/12  14:20:56  fraser
 * add module version printing
 *
 * Revision 1.1  1996/09/12  13:19:06  fraser
 * Initial revision
 *
*/
/* main.c: main module for agm
*/

char main_RCSid[] = "$Revision: 1.14 $";

/* You may distribute unmodified copies of this program freely and unrestricted
   save that it be without profit, provided this header is included.

   You may distribute modified copies of this program provided all
   copies are marked as such.

   You may not distribute this program for profit.

   */

/* Take words from the command line, build list of words internally from
   a central wordlist, then pick words from this which can be derived from
   command line word, and repeat until no more can be, or until anagram
   is fully complete */

#include "agm.h"
/* following just for getrusage () */
#include <unistd.h>

int listdict = FALSE; /* just output the final word dictionary */
int hardmin = FALSE; /* hard minimums on words */
int time_out = FALSE; /* show time statistics ? */
int read_default = TRUE; /* read the default wordfile */
char *prevs[MAXWORDS]; /* array pointing to the anagram currently under
			  consideration */
int prevcount = 0; /* and where we are in it */
int totlen = 0; /* and total length of it */
int dups_removed = 0; /* number of duplicate dictionary words removed by sorting */
int maxwords = -1; /* max number of words allowed */
int partial = FALSE; /* whether partial anagrams are allowed */
long perms, sucperms, rejected; /* for collecting statistics */
int screenwid = SCREENWID, spos; /* screen width and current output position */
int is_a_tty; /* set if outputting to terminal */
int force_percent = FALSE; /* force the program to output to the terminal;
			      used by xagm Tcl/Tk frontend */
int notquiet = TRUE;

unsigned int wordcount = 0, filecount = 0, must_sort = FALSE; /* guff to do
								 with array */

struct wnode *lstart = NULL, *lend = NULL; /* start and end of main wordlist */

int pct= 0, lastpct = -1; /* percentage complete counters */

unsigned int minlen, speclen = 0; /* set by command line options */

int main (int argc, char **argv)
{
  int wordno; /* loops along argument list */
  void matchwords ();
#ifdef CPULIMIT
  struct rlimit rl;
#endif
#ifdef TIMER
  struct rusage usage;
#endif

  is_a_tty = isatty (1);
  if (argc < 2) { /* Bleugh */
    fprintf (stderr, "Anagram Search Release %s usage:\n%s [ global-options ] [ [ word-options ] word [ word ... ] ... ]\n",
	     RELEASE, argv[0]);
    fprintf (stderr, "\n\"global-options\" is one or more of:\n\t%s\tdump dictionary and exit\n\t%s\tquiet mode (output nothing but anagrams)\n\t%s<len>\tset screen width to <len> characters\n\t%s\toutput useless elapsed time statistics\n\t%s<sec>\tlimit CPU time used to <sec> seconds\n\t%cwordfile\n\t\tread wordfile into the dictionary\n\t%c%s\tdo not read the standard wordfile\n",
	     LISTOPT, QUIETOPT, SWOPT, TIMEOPT, CLOPT,
	     FILECHAR, FILECHAR, NOREADDEF);
    fprintf (stderr, "\n\"word-options\" is one or more of:\n\t%s\thard limits on word length\n\t%s<len>\tset minimum average (actual minimum with %s) word length\n\t%s<num>\tset maximum number of words\n\t%s\tallow partial anagrams\n",
	     HARDOPT, SPECOPT, HARDOPT, MAXWOPT, PARTOPT);
    exit (-1);
  }
  for (wordno = 1; /* who needs efficiency? */
       wordno < argc
	 && (*argv[wordno] == FILECHAR
	     || strcmp (argv[wordno], QUIETOPT) == 0
	     || strncmp (argv[wordno], SWOPT, strlen (SWOPT)) == 0
	     || strncmp (argv[wordno], TIMEOPT, strlen (TIMEOPT)) == 0
	     || strncmp (argv[wordno], FORCEOPT, strlen (FORCEOPT)) == 0
	     || strncmp (argv[wordno], VERSOPT, strlen (VERSOPT)) == 0
	     || strncmp (argv[wordno], LISTOPT, strlen (LISTOPT)) == 0
	     || strncmp (argv[wordno], CLOPT, strlen (CLOPT)) == 0);
       ++wordno) {
    if (*argv[wordno] == FILECHAR) {
      if (strcmp (argv[wordno] + 1, NOREADDEF) == 0) {
	read_default = FALSE;
      }
      else {
	gobble_file (argv[wordno] + 1, argv[0]);
      }
    }
    else if (strcmp (argv[wordno], LISTOPT) == 0) {
      listdict = TRUE;
    }
    else if (strcmp (argv[wordno], FORCEOPT) == 0) {
      force_percent = TRUE;
    }
    else if (strcmp (argv[wordno], VERSOPT) == 0) {
      printf ("%s: module version information:\n\t%s\tagm.h\n\t%s\tmain.c\n\t%s\tgobble.c\n\t%s\tlistfuncs.c\n\t%s\toutput.c\n\t%s\tprocess.c\n\t%s\tsort.c\n\t%s\twordfuncs.c\n\t%s\tprogress.c\n",
	      argv[0], HEADER_RCSID, main_RCSid, gobble_RCSid, listfuncs_RCSid, output_RCSid, process_RCSid, sort_RCSid, wordfuncs_RCSid, progress_RCSid);
      return 0;
    }
#ifdef TIMER
    else if (strcmp (argv[wordno], TIMEOPT) == 0) {
      time_out = TRUE;
    }
#endif
    else if (strcmp (argv[wordno], QUIETOPT) == 0) {
      notquiet = FALSE;
    }
    else if (strncmp (argv[wordno], SWOPT, strlen (SWOPT)) == 0) {
      if (sscanf (argv[wordno]+strlen(SWOPT), "%d", &screenwid) != 1) {
	fprintf (stderr, "%s: error setting output width to %s\n",
		 argv[0], argv[wordno]+strlen(SWOPT));
	exit (-1);
      }
    }
#ifdef CPULIMIT
    else if (strncmp (argv[wordno], CLOPT, strlen (CLOPT)) == 0) {
      if (getrlimit (RLIMIT_CPU, &rl) != 0) {
	fprintf (stderr, "%s: can't get current CPU limit\n", argv[0]);
	exit (-1);
      }
      if (sscanf (argv[wordno]+strlen(CLOPT), "%ld", &rl.rlim_cur) != 1) {
	fprintf (stderr, "%s: don't understand CPU limit %ld\n",
		 argv[0], rl.rlim_cur);
	exit (-1);
      }
      if (setrlimit (RLIMIT_CPU, &rl) != 0) {
	fprintf (stderr, "%s: can't set CPU limit to %ld\n",
		 argv[0], rl.rlim_cur);
      }
      if (notquiet) printf ("%s: CPU time limit set to %ld seconds\n", argv[0], rl.rlim_cur);
    }
#endif
  }
  if (read_default) {
    gobble_file (WORDFILE, argv[0]);
  }
  if (must_sort) {
    if (notquiet) printf ("%s: sorting dictionary (%d words)",
			  argv[0], wordcount);
    fflush (stdout);
    merge_sort (&lstart, wordcount);
    if (notquiet) printf ("\n%s: %d duplicate%s (removed): %d unique\n",
			  argv[0], dups_removed, dups_removed == 1 ? "" : "s",
			  wordcount - dups_removed);
  }
  else {
    if (notquiet) printf ("%s: dictionary already sorted with no duplicates\n",
			  argv[0]);
  }
  if (listdict) {
    list_dictionary (lstart);
    return 0;
  }
  if (strcmp(READSTDIN, argv[wordno]) == 0) { /* read standard input for rest
						 of options, ignore command
						 line */
    char readword[WORDLEN];
    if (is_a_tty) {
      printf("Enter command line options, or EOF to quit:\n");
    }
    while (scanf("%s", readword) == 1) {
      do_cl_word(argv, readword);
    }
  }
  else {
    while (wordno < argc) {
      do_cl_word(argv, argv[wordno++]);
    }
  }
#ifdef TIMER
  if (time_out) {
    if (getrusage (RUSAGE_SELF, &usage) != 0) {
      fprintf (stderr, "%s: unable to obtain usage information\n", argv[0]);
      return -1;
    }
    else {
      printf ("CPU user %20ld:%02ld.%02ld\nCPU system %18ld:%02ld.%02ld\nPage faults %23ld\nSwaps %29ld\n",
	      (long) usage.ru_utime.tv_sec / 60,
	      (long) usage.ru_utime.tv_sec % 60,
	      (long) usage.ru_utime.tv_usec / 10000,
	      (long) usage.ru_stime.tv_sec / 60,
	      (long) usage.ru_stime.tv_sec % 60,
	      (long) usage.ru_stime.tv_usec / 10000,
	      (long) usage.ru_majflt,
	      (long) usage.ru_nswap);
    }
  }
#endif
  return 0;
}

void do_cl_word(char *argv[], char *clword)
{
  char thisword[WORDLEN];

  if (strcmp (clword, HARDOPT) == 0) {
    hardmin = !hardmin;
    maxwords = -1; /* unset this */
  }
  else if (strncmp (clword, MAXWOPT, strlen (MAXWOPT)) == 0) {
    if (sscanf (clword+strlen(MAXWOPT), "%d", &maxwords) != 1) {
      fprintf (stderr, "%s: error setting maximum # of words to %s\n",
	       argv[0], clword+strlen(MAXWOPT));
      exit (-1);
    }
    else {
      speclen = 0;
    }
  }
  else if (strcmp (clword, PARTOPT) == 0) {
    partial = !partial;
  }
  else if (strncmp (clword, SPECOPT, strlen (SPECOPT)) == 0) {
    if (sscanf (clword+strlen(SPECOPT), "%d", &speclen) != 1) {
      fprintf (stderr, "%s: error setting minimum word length to %s\n",
	       argv[0], clword+strlen(SPECOPT));
      exit (-1);
    }
    else {
      maxwords = -1; /* unset this */
    }
  }
  else {
    copysmall (thisword, clword);
    minus_process (thisword, argv[0]);
    if (speclen == 0 && maxwords == -1) {
      minlen = strlen (thisword) / WORDFACT;
      while (minlen < 2) ++minlen;
      if (minlen > MAXMIN) minlen = MAXMIN;
    }
    else {
      minlen = speclen;
    }
    if (maxwords >= 0) {
      if (notquiet) printf ("\n%s: %sanagrams of %s, number of words <= %d\n",
			    argv[0], partial ? "partial " : "", thisword, maxwords);
    }
    else {
      if (notquiet) printf ("\n%s: anagrams of %s, %sword length >= %d\n",
			    argv[0], thisword, hardmin ? "" : "average ", minlen);
    }
    fflush (stdout);
    spos = 0;
    perms = rejected = sucperms = 0;
    if (is_a_tty || force_percent) {
      arm_timer ();
    }
    process (thisword, lstart, lend, minlen, maxwords, 1);
    if (is_a_tty || force_percent) {
      disarm_timer ();
    }
    if (is_a_tty) {
      fputs ("   \b\b\b", stdout);
    }
    if (spos > 0) putchar ('\n');
    if (notquiet) printf ("%s: (%s) permutations=%ld rejections=%ld ok=%ld (%.2f%%)\n",
			  argv[0], thisword, perms, rejected, sucperms,
			  100 * (double) sucperms / (double) perms);
  }
}
