/*
 * This file is part of uudeview, the simple and friendly multi-part multi-
 * file uudecoder  program  (c)  1994 by Frank Pilhofer. The author may be
 * contacted by his email address,          fp@informatik.uni-frankfurt.de
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */


#include <stdio.h>
#include <ctype.h>
#include <fcntl.h>
#include <signal.h>

#ifndef NO_POSIX
#ifndef _DOS
#include <unistd.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <memory.h>
#endif

#include "uudeview.h"
#include "uucheck.h"
#include "uufree.h"
#include "uunconc.h"
#include "uuerror.h"
#include "uudecomp.h"
#include "uuutil.h"
#include "uutar.h"
#include "uuio.h"
#include "toolbox.h"

/* For the sake of getcwd() */
#ifdef _DOS
#include <direct.h>
#endif

/*
 * Operational Flags
 */

int incext = 0, stdinput = 0, interact = 1;
int decomp = 0, detar    = 0, ignreply = 1;
int quiet  = 0, fast     = 0;
int desperate = 0;
int overwrite = 0;

/*
 * Global Variables
 */

uulist  *GlobalFileList = NULL;
infile  *ftoread        = NULL;
extlist *extensions     = NULL;
static char savepath[256];
char *stdfile;

static char *codenames[4] = {
  "", " UUdata", " Base64", " XXdata"
};

/*
 * Parts with different known extensions will not be merged by SPMS.
 * if first character is '@', it is synonym to the previous one.
 */
char *knownexts[] = {
  "mpg", "@mpeg", "avi", "mov",
  "gif", "jpg", "@jpeg", "tif",
  "voc", "wav", "@wave", "au",
  "zip", "arj", "tar",
  NULL
};

/*
 * Header lines starting with these texts will be stripped
 */
char *hateheaders[] = {
  "Path:", "Newsgroups:", "Organization:", "Lines:",
  "Message-ID:", "NNTP-Posting-Host:", "Xref:",
  "References:", "X-Newsreader:", "Distribution",
  "Sender:", "Nntp-Posting-Host:", "Reply-To:",
  "Approved:", "Mime-Version:", "Content-Type:",
  "Content-Transfer-Encoding:", "X-Posting-Software:",
  NULL
};

/*
 * In DOS, we must open the file binary, O_BINARY is defined there
 */

#ifndef _DOS
#ifndef O_BINARY
#define O_BINARY 	0
#endif
#endif

int IsHeaderLine (char *text)
{
  char **hiter = hateheaders;

  if (text == NULL || hiter == NULL)
    return 0;

  while (*hiter) {
    if (strncmp (text, *hiter, strlen (*hiter)) == 0)
      return 1;
    hiter++;
  }
  return 0;
}

int IsKnownExtension (char *filename)
{
  char **eiter = knownexts, *ptr=strrchr(filename, '.');
  int count=0, where=0;

  if (ptr == NULL)
    return -1;
  ptr++;

  while (*eiter) {
    if (_FP_stricmp (ptr, (**eiter=='@')?*eiter+1:*eiter) == 0)
      return where;
    else
      eiter++;

    if (*eiter == NULL)
      break;

    if (**eiter=='@')
      count++;
    else
      where = ++count;
  }
  return -1;
}

int addftoread (char *file)
{
  infile *inew, *iter;

  if ((inew = (infile *) malloc (sizeof (infile))) == NULL) {
    uuerror = NOMEM;
    return 0;
  }
  inew->delflag = (file[0]=='|') ? 1 : 0;
  strcpy (inew->name, (file[0]=='|') ? file+1 : file);
  inew->NEXT = NULL;

  if (ftoread == NULL)
    ftoread = inew;
  else {
    iter = ftoread;
    while (iter->NEXT != NULL)
      iter = iter->NEXT;

    iter->NEXT = inew;
  }

  return 1;
}

int addexts (char *elist)
{
  extlist *enew;
  int iter;

  while (*elist) {
    if (*elist != '.') {
      elist++;
      continue;
    }
    if (*++elist == '\0')
      break;

    if ((enew = (extlist *) malloc (sizeof (infile))) == NULL) {
      uuerror = NOMEM;
      return 0;
    }
    iter = 0;

    while (*elist != '.' && *elist != '\0' && iter < 7)
      enew->extension[iter++] = *elist++;

    enew->extension[iter] = '\0';

    enew->NEXT = extensions;
    extensions = enew;
  }
  return 1;
}

int work_file (char *filename)
{
  extlist *iter = extensions;
  char *ptr;

  if ((ptr = strrchr (filename, '.')) == NULL)
    return 1;

  ptr++;

  while (iter) {
    if (_FP_stricmp (ptr, iter->extension) == 0)
      return incext ? 1 : 0;

    iter = iter->NEXT;
  }

  return incext ? 0 : 1;
}

int proc_stdin (void)
{
  static char buffer[1024];
  FILE *target;
  int bytes;

  if ((stdfile = tempnam (NULL, "uu")) == NULL) {
    uuerror = FILEERR;
    return 0;
  }

  if ((target = fopen (stdfile, "wb")) == NULL) {
    uuerror = FILEERR;
    free (stdfile);
    return 0;
  }

  while (!feof (stdin)) {
    bytes = fread (buffer, 1, 1024, stdin);

    if (bytes == 0 && !feof (stdin)) {
      uuerror = FILEERR;
      fclose (target);
      unlink (stdfile);
      free   (stdfile);
      return 0;
    }
    if (fwrite (buffer, 1, bytes, target) != bytes) {
      uuerror = FILEERR;
      fclose (target);
      unlink (stdfile);
      free   (stdfile);
      return 0;
    }
  }

  fclose (target);

  return 1;
}

void loadparfile (char *filename)
{
  char line[256], *argv[32], *p1, *p2, c;
  FILE *pfile;
  int argc;

  if ((pfile = fopen (filename+1, "r")) == NULL) {
    printf ("\nCouldn't load parameter file %s (ignored)", filename+1);
    return;
    }

  argv[0] = NULL;

  while (!feof (pfile)) {
    if (fgets (line, 256, pfile) == NULL)
        break;

    line[strlen(line)-1] = '\0';
    if (line[strlen(line)-1] == 0x0a || line[strlen(line)-1] == 0x0d)
        line[strlen(line)-1] = '\0';

    if (line[0] == '\0')
        continue;

    argc    = 2;
    argv[1] = p1 = line;

    while ((p2 = strchr (p1, ' ')) != NULL) {
        *p2++ = '\0';

        while (*p2 == ' ')
            p2++;
        if (*p2 == '\0')
            break;

        argv[argc++] = p1 = p2;
        }

    work_comline (argc, argv);

    c = fgetc (pfile);
    if (feof (pfile))
      break;
    else
      ungetc (c, pfile);
  }
  fclose (pfile);
  unlink (filename+1);
}

int work_comline (int argc, char *argv[])
{
  int number=1;

  for (number=1; number<argc; number++) {
    if (*argv[number] == '@')
      loadparfile (argv[number]);
    else if (*argv[number] != '-' && *argv[number] != '+')
      addftoread (argv[number]);
    else switch (*(argv[number] + 1)) {
    case '\0':
      if (!stdinput) {
	if (proc_stdin() == 0) {
	  printf ("Error reading standard input");
	  break;
	}
	addftoread (stdfile);
	stdinput = 1;
      }
      break;
    case 'e':
      if (number+1 < argc && *argv[number+1] == '.') {
        incext = (*argv[number] == '+') ? 1 : 0;
        addexts (argv[++number]);
      }
      break;
    case 'i':
      interact  = (*argv[number] == '+') ? 1 : 0;
      overwrite = (*argv[number] == '-') ? 1 : overwrite;
      break;
    case 'd':
      desperate= (*argv[number] == '+') ? 1 : 0;
      break;
    case 'u':
      decomp   = (*argv[number] == '+') ? 1 : 0;
      break;
    case 't':
      detar    = (*argv[number] == '+') ? 1 : 0;
      break;
    case 'r':
      ignreply = (*argv[number] == '+') ? 1 : 0;
      break;
    case 'p':
      if (number+1 < argc)
        strcpy (savepath, argv[++number]);
      break;
    case 'o':
      overwrite = (*argv[number] == '+') ? 1 : 0;
      break;
    case 'f':
      fast      = 1;
      break;
    case 'V':
      fprintf (stdout, "uudeview %spl%s compiled on %s\n",
	       VERSION, PATCH, __DATE__);
      exit (0);
      break;
    default:
      usage ();
      exit (1);
      break;
    }
  }
  if (stdinput)
    interact = 0;

  return 1;
}

void load_files (void)
{
  infile *fiter = ftoread;
  fileread *loaded;
  uufile *fload;
  FILE *datei;
  char c;

  while (fiter) {
    if ((datei = fopen (fiter->name, "rb")) == NULL) {
      printf ("ERROR: could not open %s\n", fiter->name);
      fiter = fiter->NEXT;
      continue;
    }
    while (!feof (datei)) {
      if (uuerror) {
        if (faterr[uuerror])
          printf ("ERROR: %s while reading %s\n", uuemsg[uuerror], fiter->name);

        uuerror = 0;
      }
      /* 
       * Peek file. A Linux system was reported not to detect EOF
       */
      c = fgetc (datei);
      if (feof (datei))
        break;
      else
        ungetc (c, datei);

      if ((loaded = ScanPart (datei, fiter->name)) == NULL) 
        continue;

      if ((loaded->subject == NULL || *(loaded->subject) == '\0') &&
          (loaded->mimeid  == NULL || *(loaded->mimeid)  == '\0') &&
          (loaded->filename== NULL || *(loaded->filename)== '\0') &&
          (loaded->uudet   == 0)) {
        killfread (loaded);
	if (fast) break;
        continue;
      }
      
      if ((fload = PreProcessPart (loaded)) == NULL) {
        killfread (loaded);
	if (fast) break;
        continue;
      }
      if (!quiet && !stdinput &&
          ((loaded->subject && *(loaded->subject)) ||
           (loaded->mimeid  && *(loaded->mimeid))  ||
           (loaded->filename&& *(loaded->filename))||
           (loaded->uudet)))
        printf ("Loaded '%s' %s%s%s%s %d '%s'\n",
		(loaded->subject)  ? loaded->subject  : "",
		(loaded->filename) ? loaded->filename : "",
                (loaded->begin)?" begin":"", (loaded->end)?" end":"",
		codenames[loaded->uudet], fload->partno,
		fload->subfname);
    
      if (InsertPartToList (fload) != 1) {
        killfile (fload);
	if (fast) break;
        continue;
      }
      /*
       * if in fast mode, we don't look any further, because we're told
       * that each source file holds at most one encoded part
       */
      if (fast)
	break;

    }
    fiter = fiter->NEXT;
    fclose (datei);
  }
  uuerror = 0;
}

char getyn (char def) 
{
  char line[20];

  if (interact && !feof (stdin)) {
    fflush (stdout);
    if (fgets (line, 19, stdin) == NULL)
      return def;
    if (line[0] == '\n')
      return def;
  }
  else {
    printf ("%c\n", def);
    return def;
  }
  return tolower(line[0]);
}

void more (uulist *uin, FILE *out, int type)
{
  FILE *inpfile = NULL;
  int aline=0, lines=0, cols=0, header=0;
  char text[256], *p;

  if (out == NULL || uin == NULL || uin->thisfile == NULL) {
    printf ("\tError -- (?)\n");
    return;
  }
  if (type == SHOW_INFO) {
    if ((inpfile = fopen (uin->thisfile->data->sfname, "r")) == NULL) {
      printf ("\tError -- could not open file (?)\n");
      return;
    }
    fseek (inpfile, uin->thisfile->data->startpos, SEEK_SET);
  }
  else if (type == SHOW_FILE) {
    if ((inpfile = fopen (uin->binfile, "r")) == NULL) {
      printf ("\tError -- could not open file (?)\n");
      return;
    }
  }
  if ((p = getenv ("LINES")) != NULL) {
    if ((lines = atoi(p)) < 5)
      lines = 25;
  }
  else
    lines = 25;

  if ((p = getenv ("COLUMNS")) != NULL) {
    if ((cols = atoi(p)) < 30)
      cols = 80;
  }
  else
    cols = 80;

  while (!feof (inpfile) &&
	 (type != SHOW_INFO || 
	  (ftell (inpfile) < (uin->thisfile->data->startpos +
			      uin->thisfile->data->length)))) {
    if ((p = fgets (text, cols, inpfile)) == NULL)
      break;
    if (uin->uudet == B64ENCODED && valid_data (text))
      break;
    if (strncmp (text, "begin ", 6) == 0)
      break;
    if (aline+2 == lines) {
      aline = 0;
      if (out == stdout) {
	fprintf (out, "<<MORE -- (q) to quit>>");
	if (getyn ('y') == 'q')
	  break;
      }
    }
    while (*p && *p!='\x0d' && *p!='\x0a')
      p++;
    /*
     * Headers are annoying. Get rid of them.
     */
    if (IsHeaderLine (text))
      header = 1;

    if ((*p == '\x0d' || *p == '\x0a') && header) {
      header = 0;
      continue;
    }

    if (header)
      continue;

    *p = '\0'; aline++;
    fprintf (out, "%s\n", text);
  }
  if (out == stdout && aline+2 == lines) {
    fprintf (out, "<<END -- hit return>>");
    getyn ('?');
  }
  else
    fprintf (out, "<<END>>");

  fclose (inpfile);
}

char *indent (int depth)
{
  static char spaces[80];

  memset (spaces, ' ', depth*2);
  spaces[depth*2] = '\0';

  return spaces;
}

int process_one (uulist *iter, int depth)
{
  int ftar=istarfile(iter), fcomp=iscomp(iter);
  char line[128], command[128], tmp, *ptr1, *ptr2;
  int res, escflag; char *name = NULL;

  do {
    free (name);

    if ((name = cutdir (iter->filename)) == NULL) {
      res  = 0;
      break;
    }

    if (!quiet) {
      printf ("%s%s %-15s is %s   [d] (?=help) ",
	      indent(depth), filemode((int)iter->mode), name,
	      (iter->state >= ST_OK) ? "OK." : "in error (DESPERATE MODE)");
    }
    
    if (fcomp && decomp) {
      if (!quiet)
	printf ("%c\n", 'd');
      tmp = 'd';
    }
    else if (ftar && detar) {
      if (!quiet)
	printf ("%c\n", 't');
      tmp = 't';
    }
    else {
      if (!quiet)
	tmp = getyn ('d');
      else
	tmp = 'd';
    }

    if (tmp == 'n') {
      if (iter->NEXT == NULL)
        res = 3;
      else
        res = 1;

      break;
    }
    else if (tmp == 'c') {
      printf ("\n\n-------------------------------------------------------------------------------\n\n");
      printf ("\tYou are now using UUDEVIEW, the uudecoder by Frank Pilhofer\n\n");
      printf ("\tThe  program  is  distibuted  under  the  terms  of the GNU\n");
      printf ("\tGeneral Public License.  You should have received a copy of\n");
      printf ("\tthe GPL with the uudeview program. In particular this means\n");
      printf ("\tthat the program is distributed without any warranty.\n\n");
      printf ("\tIf you like uudeview, you are encouraged to send the author\n");
      printf ("\ta postcard (find  the address  in the  README  file), or at\n");
      printf ("\tleast an email.\n\n");
      printf ("\tCheck out uudeview's WWW home page:\n");
      printf ("\t\thttp://www.uni-frankfurt.de/~fp/uudeview/\n\n");
      printf ("\tYou can always find  the most recent  version via anonymous\n");
      printf ("\tftp on\n");
      printf ("\t\tftp.uni-frankfurt.de:/pub/dist/frank\n\n");
      printf ("\t            Frank Pilhofer (fp@informatik.uni-frankfurt.de)\n\n");
      printf ("-------------------------------------------------------------------------------\n\n");
      continue;
    }
    else if (tmp == 'q') {
      res = 3;
      break;
    }
    else if (tmp == '?') {
      printf ("\n\tYou can ...\n");
      printf ("\t  - (d)ecode the file\n");
      printf ("\t  - (y)es - same as (d)\n");
      printf ("\t  - go to (n)ext file\n");
      printf ("\t  - show file (i)nfo\n");
      /*
       * Can't execute anything in QuickWin
       */
#ifndef _WINDOWS       
      printf ("\t  - (e)xecute a command\n");
#endif      
      printf ("\t  - (l)ist textfile\n");
      printf ("\t  - (r)ename file\n");
      printf ("\t  - set decode (p)ath: %s\n", savepath);
      /*
       * Can't do (u)ncompress in QuickWin because of system()
       */
#ifndef _WINDOWS       
      if (fcomp)
	printf ("\t  - (u)ncompress the file\n");
#endif	
      if (ftar)
	printf ("\t  - enter the (t)ar archive\n");
      if (depth>1)
	printf ("\t  - (q)uit archive processing\n");
      else
	printf ("\t  - (q)uit program\n");
      printf ("\t  - display (c)opyright\n\n");
      continue;
    }
    else if (tmp == 'r') {
      printf ("\nEnter new filename: "); fflush (stdout);
      if (fgets (line, 250, stdin) == NULL)
	printf ("\n\n ERROR: Could not get filename\n");
      else if (strlen (line) > 1) {
	line[strlen(line)-1] = '\0';
	free (iter->filename);
	iter->filename = _CopyLine (line, -1);
      }
      printf ("\n");
      continue;
    }
    else if (tmp == 'p') {
      printf ("\nEnter new path: "); fflush (stdout);
      if (fgets (line, 250, stdin) == NULL)
        printf ("\n\n ERROR: Could not get path\n");
      else if (strlen (line) > 1) {
	line[strlen(line)-1] = '\0';
	strcpy (savepath, line);
	if (savepath[strlen(savepath)-1] == '/' || savepath[strlen(savepath)-1] == '\\')
	  savepath[strlen(savepath)-1] = '\0';
      }
      printf ("\n");
      continue;
    }
    else if (tmp == 'i') {
      printf ("\nFile info ...\n\n");
      printf ("-------------------------------------------------------------------------------\n");
      more (iter, stdout, SHOW_INFO);
      printf ("\n-------------------------------------------------------------------------------\n\n");
      continue;
    }
    if (!(iter->state & ST_DECODED)) {
      if (uudecode (iter) != 1) {
	printf ("\n%s ERROR while decoding %s (%s): %s\n\n",
		indent (depth), name,
		(iter->subfname) ? iter->subfname : "",
		uuemsg[uuerror]);
	uuerror = OK;
	res     = 0;
	break;
      }
    }
    if (tmp == 'd' || tmp == 'y' || tmp == '\0' || tmp == '\n') {
      if (!write_file (iter, savepath)) {
	printf ("\n%s  Error writing file\n\n", indent(depth));
	uuerror = OK;
	res     = 0;
      }
      else {
	if (!quiet)
	  printf ("%s  File successfully written\n", indent(depth));
	res = 1;
      }

      break;
    }
    if (iter->tarblock != -1)
      if (tar2bin (iter) == NULL) {
	printf ("\n%s ERROR while untarring %s (%s): %s\n\n",
		indent (depth), name,
		(iter->subfname) ? iter->subfname : "",
		uuemsg[uuerror]);
	uuerror = OK;
	continue;
      }
	/*
	 * Can't do in QuickWin
	 */
#ifndef _WINDOWS	     
    if (tmp == 'u' && fcomp) {
      if (uncompress (iter) == NULL) {
	printf ("\n%s ERROR while decompressing %s (%s): %s\n\n",
		indent (depth), name,
		(iter->subfname) ? iter->subfname : "",
		uuemsg[uuerror]);
	uuerror = OK;
      }
      else {
	res = 2;
	break;
      }
    }
#else
    if (0);
#endif	    
    else if (tmp == 't' && ftar) {
      if (tarscan (iter) == NULL) {
	printf ("\n%s ERROR while detarring %s (%s): %s\n\n",
		indent (depth), name,
		(iter->subfname) ? iter->subfname : "",
		uuemsg[uuerror]);
	uuerror = OK;
      }
      else {
	res = 2;
	break;
      }
    }
    else if (tmp == 'l') {
      printf ("\nContents of file ...\n\n");
      printf ("-------------------------------------------------------------------------------\n");
      more (iter, stdout, SHOW_FILE);
      printf ("\n-------------------------------------------------------------------------------\n\n");
    }
      /*
       * Can't do that in QuickWin, since we'd need system()
       */
#ifndef _WINDOWS           
    else if (tmp == 'e') {
      printf ("\nEnter command line ($) for file: "); fflush (stdout);
      if (fgets (line, 79, stdin) == NULL)
	printf ("\n\n ERROR: Could not get Command line\n\n");
      else if (strlen (line) > 1) {
	ptr1 = line; ptr2 = command;

	while (*ptr1 && *ptr1 != '\n') {
	  if (!escflag || *ptr1 != '\\') {
	    if (*ptr1 == '$') {
	      strcpy (ptr2, iter->binfile);
	      ptr2  += strlen (iter->binfile);
	      ptr1++;
	    }
	    else
	      *ptr2++ = *ptr1++;
	  }
	  else if (escflag) {
	    *ptr2++ = *ptr1++;
	    escflag = 0;
	  }
	  else
	    escflag = 1;
	}
	*ptr2 = '\0';

	printf ("-------------------------------------------------------------------------------\n");
	system (command);
	printf ("-------------------------------------------------------------------------------\n\n");
      }
    }
#endif	
    else {
      printf ("\n%s ERROR: unknown action '%c'. Enter ? for list of options.\n\n", 
	      indent (depth), tmp);
    }
  }
  while (1);

  free (name);

  return res;
}
    
int process_files (uulist *iter, int depth)
{
  char *ptr, *name;
  int res;

  while (iter != NULL) {
    res = 0;

    if ((name = cutdir (iter->filename)) == NULL) {
      iter = iter->NEXT;
      continue;
    }

    if (!(work_file (name))) {
      if (interact && !quiet)
        printf ("%s%s %-15s ignored.\n", indent (depth),
                filemode((int)iter->mode), name);
      
      iter = iter->NEXT;
      free (name);
      continue;
    }
    switch (iter->state) {
      case ST_MISPART: ptr = "Missing Part(s)";break;
      case ST_NOBEGIN: ptr = "No Begin found"; break;
      case ST_NOEND:   ptr = "No End found";   break;
      case ST_NODATA:  ptr = NULL;             break;
      case ST_OK:      ptr = "State is OK";    break;
      default:         ptr = "Unknown State";  break;
    }
    if (iter->state >= ST_OK ||
	(desperate && (iter->state == ST_MISPART ||
		       iter->state == ST_NOEND))) {
      switch (process_one (iter, depth)) {
      case 2:
	res = process_files (iter->branch, depth+1);
	break;
      case 3:
	return 1;
	break;
      }
    }
    else {
      if (ptr && iter->filename)
        printf ("\n%s ERROR: File %s (%s): %s\n\n", 
                indent (depth), name,
                (iter->subfname) ? iter->subfname : "", ptr);
      res  = 0;
    }
    free (name);
    
    if (!res)
       iter = iter->NEXT;
  }
  return 0;
}

void sighandler (int signal)
{
  printf ("\n\nReceived Signal (%d), cleaning up temp files.\n\n", signal);

  killlist (GlobalFileList);
  exit (signal);
}

void usage (void)
{
  printf ("\n\
  UUDEVIEW %s%s%s - the nice and friendly decoder - (w) 1994 Frank Pilhofer\n",
	  VERSION, (PATCH[0]>'0')?"pl":"", (PATCH[0]>'0')?PATCH:"");
  printf ("  usage:\n");
  printf ("    uudeview [{+-}e .ext1.ext2] [{+-}i] [{+-}r] [file ...]\n\n");
  printf ("\t+e/-e\tInclude or exclude extensions exclusively\n");
  printf ("\t-i\tDisable interactivity (do not ask, decode everything)\n");
  printf ("\t-r\tDo not ignore RE:plies (useful for decoding mails)\n");
  printf ("\t+u\tAutomatically uncompress / gunzip files\n");
  printf ("\t+d\tSets 'desperate' mode (process incomplete files)\n");
  printf ("\t-p <path>\tSets path where to save decoded binaries to\n\n");
  printf ("  Example:\n");
  printf ("    uudeview +e .jpg.gif -i newsfile\n");
  printf ("\tThis decodes all .jpg or .gif files encountered in <newsfile>\n");
  printf ("\twithout asking.\n\n");
}

int main (int argc, char *argv[])
{
  uulist   *llist;
  int       res;
#ifdef _WINDOWS
  struct _wsizeinfo ws;
#endif    

  /*
   * No Signal handler in QuickWin
   */
#ifndef _WINDOWS
  signal (SIGINT, sighandler);
#endif
  /*
   * In QuickWin, set the about() Box, and give more space to scroll
   */
#ifdef _WINDOWS
  ws._version = _QWINVER;
  ws._type    = _WINSIZEMAX;
  (void) _wabout        ("UUdeview for Windows\n(c) 1995 Frank Pilhofer\nfp@informatik.uni-frankfurt.de");
  (void) _wsetscreenbuf (fileno(stdout), 16384);
  (void) _wsetscreenbuf (fileno(stderr), 16384);
  (void) _wsetsize      (fileno(stdout), &ws);
#endif  

  /*
   * Check where we are and set the save directory
   */
  if (getcwd (savepath, 255) == NULL)
    strcpy (savepath, ".");

  /*
   * If we were called as uudecode, be quiet and don't interact
   */
  if (strstr (argv[0], "uudecode") != NULL) {
    interact = 0;
    quiet    = 1;
    ignreply = 0;
    overwrite= 1;

    if (argc < 2) {
      if (proc_stdin() == 0) {
	printf ("Error reading standard input");
	exit (1);
      }
      addftoread (stdfile);
      stdinput = 1;
    }
  }
  else if (argc < 2) {
    usage ();
    return 1;
  }

  init_conc    ();
  work_comline (argc, argv);
  load_files   ();

  if (savepath[strlen(savepath)-1] == '/' || savepath[strlen(savepath)-1] == '\\')
    savepath[strlen(savepath)-1] = '\0';

  if (!stdinput && GlobalFileList != NULL && !quiet)
    printf ("\n");

  llist = CheckGlobalList();

  if (!stdinput && GlobalFileList != NULL && !quiet)
    printf ("\n");

  /*
   * try merging thrice with increased tolerance
   */
          smergeparts (0);
          smergeparts (1);
  llist = smergeparts (99);

  res = process_files (llist, 1);

  if (!stdinput && GlobalFileList != NULL && !quiet)
    printf ("\n");
  if (stdinput) {
    unlink (stdfile);
    free   (stdfile);
  }
#ifndef _WINDOWS
  signal   (SIGINT, SIG_DFL);
#endif
  killlist (GlobalFileList);
  killext  (extensions);
  killif   (ftoread);

  /*
   * Without user interaction, or if the user has quit
   * the proggy, kill myself in QuickWin
   */
#ifdef _WINDOWS
  if (!interact || res==1)
    _wsetexit (_WINEXITNOPERSIST);
  else {
    printf ("\nNo more Programs to decode.\nSelect File-Exit to close window\n\n");
  }
#endif  

  return 0;
}
