/*
 * 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>

#ifndef NO_POSIX
#include <malloc.h>
#include <string.h>
#endif

#ifndef _DOS
#ifndef NO_POSIX
#include <unistd.h>
#endif
#endif

#include "uudeview.h"
#include "uuerror.h"
#include "uucheck.h"
#include "uufree.h"
#include "toolbox.h"

/*
 * Simply copy a line up to a CR or the end of the string
 **/

char *_CopyLine (char *ptr, int maxlen)
{
  char *result;

  if (maxlen == -1) {
    while (*ptr == ' ') ptr++;
    for (maxlen=0; ptr[maxlen]&&ptr[maxlen]!=0x0a&&ptr[maxlen]!=0x0d; maxlen++);
    while (maxlen>0 && ptr[maxlen-1] == ' ')
      maxlen--;
  }

  if ((result = (char *) malloc (maxlen + 1)) == NULL) {
    uuerror = NOMEM;
    return NULL;
  }

  memcpy (result, ptr, maxlen);
  result[maxlen] = '\0';

  return result;
}

char *cutdir (char *filename)
{
  char *ptr;

  if (filename == NULL)
    return NULL;

  if ((ptr = strrchr (filename, '/')) == NULL)
    ptr = filename;
  else
    ptr++;

  return _CopyLine (ptr, -1);
}

char *dosfname (char *filename)
{
  char dosname[13], *ptr1, *ptr2;
  int count = 0;

  ptr1 = dosname;
  ptr2 = filename;

  while (*ptr2 && *ptr2 != '.' && count++ < 8) {
    if (*ptr2 == ' ') {
      *ptr1++ = '_';
      ptr2++;
    }
    else 
      *ptr1++ = *ptr2++;
  }

  *ptr1++ = '.';
  count = 0;

  if (*ptr2++ == '.') {
    while (*ptr2 && *ptr2 != '.' && count++ < 3) {
      if (*ptr2 == ' ') {
        *ptr1++ = '_';
        ptr2++;
      }
      else
        *ptr1++ = *ptr2++;
    }
  }
  *ptr1 = '\0';

  return _CopyLine (dosname, -1);
}

char *filemode (int mode)
{
  static char result[11];
  static char rwxkey[8][4] = 
    { "---", "--x", "-w-", "-wx", 
      "r--", "r-x", "rw-", "rwx" };

  result[0] = '-';
  result[1] = rwxkey[((mode >> 6) & 0x07)][0];
  result[2] = rwxkey[((mode >> 6) & 0x07)][1];
  result[3] = rwxkey[((mode >> 6) & 0x07)][2];
  result[4] = rwxkey[((mode >> 3) & 0x07)][0];
  result[5] = rwxkey[((mode >> 3) & 0x07)][1];
  result[6] = rwxkey[((mode >> 3) & 0x07)][2];
  result[7] = rwxkey[  mode       & 0x07 ][0];
  result[8] = rwxkey[  mode       & 0x07 ][1];
  result[9] = rwxkey[  mode       & 0x07 ][2];
  result[10]= '\0';

  return result;
}

/*
 * Workaround for Netscape
 */
/*
 * Determines whether Netscape may have broken up a data line (by
 * inserting a newline). This only seems to happen after <a in a
 * href statement
 */

int BrokenByNetscape (char *string)
{
  char *ptr;
  int len;

  if (string==NULL || (len=strlen(string))<3)
    return 0;

  if ((ptr = _FP_stristr (string, "<a href=")) != NULL) {
    if (_FP_stristr (string, "</a>") > ptr)
      return 2;
  }

  ptr = string + len;

  while (len && (*(ptr-1)=='\x0d' || *(ptr-1)=='\x0a')) {
    ptr--; len--;
  }
  if (len<3)         return 0;
  if (*--ptr == ' ') ptr--;
  ptr--;

  if (_FP_strnicmp (ptr, "<a", 2) == 0)
    return 1;

  return 0;
}

/*
 * Try to repair a Netscape-corrupted line of data.
 * This must only be called on corrupted lines, since non-Netscape
 * data may even _get_ corrupted by this procedure.
 * 
 * Some checks are included multiply to speed up the procedure. For
 * example: (*p1!='<' || strnicmp(p1,"</a>",4)). If the first expression
 * becomes true, the costly function isn't called :-)
 *
 * Since '<', '>', '&' might even be replaced by their html equivalents
 * in href strings, I'm now using two passes, the first one for &amp; + co,
 * the second one for hrefs.
 */

int NetscapeCollapse (char *string)
{
  char *p1=string, *p2=string;

  if (string==NULL)
    return 0;

  /*
   * First pass
   */
  while (*p1) {
    if (*p1 == '&') {
      if      (_FP_strnicmp (p1, "&amp;", 5) == 0) { p1+=5; *p2++='&'; }
      else if (_FP_strnicmp (p1, "&lt;",  4) == 0) { p1+=4; *p2++='<'; }
      else if (_FP_strnicmp (p1, "&gt;",  4) == 0) { p1+=4; *p2++='>'; }
      else *p2++ = *p1++;
    }
    else *p2++ = *p1++;
  }
  *p2 = '\0';
  /*
   * Second pass
   */
  p1 = p2 = string;

  while (*p1) {
    if (*p1 == '<') {
      if ((_FP_strnicmp (p1, "<ahref=\"", 8) == 0 ||
	   _FP_strnicmp (p1, "<a href=\"",9) == 0) && 
	  (strstr (p1, "</a>") != 0 || strstr (p1, "</A>") != 0)) {
	while (*p1 && *p1!='>')        p1++;
	if (*p1=='\0' || *(p1+1)!='<') return 0;
	p1++;
	while (*p1 && (*p1!='<' || _FP_strnicmp(p1,"</a>",4)!=0)) {
	  *p2++ = *p1++;
	}
	if (_FP_strnicmp(p1,"</a>",4) != 0)
	  return 0;
	p1+=4;
      }
      else
	*p2++ = *p1++;
    }
    else
      *p2++ = *p1++;
  }
  *p2 = '\0';

  return 1;
}

/*
 * Insert one part of a file into the global list
 **/

int InsertPartToList (uufile *data)
{
  uulist *iter = GlobalFileList, *unew;
  uufile *fiter, *last;

  while (iter != NULL) {
    if ((_FP_stricmp (data->subfname, iter->subfname) == 0 ||
         (data->mimeid && iter->mimeid &&
          strcmp (data->mimeid, iter->mimeid) == 0)) &&
	!(iter->begin && data->data->begin) &&
	!(iter->end   && data->data->end)) {

      /*
       * if we already have this part, don't try to insert it
       */

      for (fiter=iter->thisfile;
	   fiter && (data->partno>fiter->partno) && !fiter->data->end;
	   fiter=fiter->NEXT);
      if (fiter && 
	  (data->partno==fiter->partno || 
	   (data->partno > fiter->partno && fiter->data->end)))
	goto goahead;

      if (iter->filename == NULL && data->filename != NULL) {
        if ((iter->filename = (char *) malloc (strlen (data->filename) + 1)) == NULL) {
	  uuerror = NOMEM;
          return -1;
        }
	strcpy (iter->filename, data->filename);
      }
      if (iter->mode == 0 && data->data->mode != 0)
        iter->mode = data->data->mode;
      if (data->data->uudet)
        iter->uudet = data->data->uudet;
      if (data->data->begin)
        iter->begin = 1;
      if (data->data->end)
        iter->end = 1;       

      if (data->partno != -1 && data->partno < iter->thisfile->partno) {
	data->NEXT     = iter->thisfile;
	iter->thisfile = data;
	
	return 1;
      }
      fiter = iter->thisfile;
      last  = NULL;

      while (fiter) {
	if (data->partno == fiter->partno) {
          if (fiter->data->subject == NULL)
            return 0;
	  else if (_FP_stristr (fiter->data->subject, "repost") != NULL &&
		   _FP_stristr (data->data->subject,  "repost") == NULL)
	    return 0;
          else if (fiter->data->uudet && !data->data->uudet)
            return 0;
          else {
            data->NEXT  = fiter->NEXT;
            fiter->NEXT = NULL;
            killfile (fiter);

            if (last == NULL)
              iter->thisfile = data;
            else
              last->NEXT     = data;

            return 1;
          }
        }

	if (fiter->NEXT == NULL || 
	    (data->partno != -1 && data->partno < fiter->NEXT->partno)) {
	  data->NEXT  = fiter->NEXT;
	  fiter->NEXT = data;

	  if (data->partno == -1)
	    data->partno = fiter->partno + 1;

	  return 1;
	}
        last  = fiter;
	fiter = fiter->NEXT;
      }
      
      return -1; /* Shouldn't get here */
    }
  goahead:
    if (iter->NEXT == NULL) 
      break;

    iter = iter->NEXT;
  }
  /*
   * handle new entry
   */

  if (data->partno == -1) {
    /*
     * if it's got no part no, and it's MIME mail, then assume this is
     * part no. 1. If it's not MIME, then we can't handle it; if it
     * had a 'begin', it'd have got a part number assigned by
     * PreProcessPart().
     */
    if (data->data->uudet == B64ENCODED)
      data->partno = 1;
    else
      return -1;
  }

  if ((unew = (uulist *) malloc (sizeof (uulist))) == NULL) {
    uuerror = NOMEM;
    return -1;
  }

  if ((unew->subfname = _FP_strdup (data->subfname)) == NULL) {
    uuerror = NOMEM;
    free (unew);
    return -1;
  }
  if (data->filename != NULL) {
    if ((unew->filename = _FP_strdup (data->filename)) == NULL) {
      uuerror = NOMEM;
      free (unew->subfname);
      free (unew);
      return -1;
    }
  }
  else
    unew->filename = NULL;

  if (data->mimeid != NULL) {
    if ((unew->mimeid = _FP_strdup (data->mimeid)) == NULL) {
      uuerror = NOMEM;
      free (unew->subfname);
      free (unew->filename);
      free (unew);
      return -1;
    }
  }
  else
    unew->mimeid = NULL;

  unew->state    = ST_READ;
  unew->binfile  = NULL;
  unew->tarblock = -1;
  unew->thisfile = data;
  unew->mode     = data->data->mode;
  unew->uudet    = data->data->uudet;
  unew->begin    = data->data->begin;
  unew->end      = data->data->end;
  unew->misparts = NULL;
  unew->NEXT     = NULL;
  unew->branch   = NULL;

  if (iter == NULL)
    GlobalFileList = unew;
  else
    iter->NEXT = unew;

  return 1;
}

long exists (char *filename)
{
  FILE *datei;
  long pos;

  if (filename == NULL || *filename == '\0')
    return 0;

  if ((datei = fopen (filename, "rb")) == NULL)
    return 0;

  fseek (datei, 0, SEEK_END);
  pos = ftell (datei);
  
  fclose (datei);
  return pos;
}

int iscomp (uulist *data)
{
  char *ptr;

  if ((ptr = strrchr (data->filename, '.')) == NULL)
    return 0;

  if (strcmp (ptr, ".Z"  ) == 0 || strcmp (ptr, ".tz" ) == 0 ||
      strcmp (ptr, ".z"  ) == 0 || strcmp (ptr, ".gz" ) == 0 ||
      strcmp (ptr, ".tgz") == 0 || strcmp (ptr, ".taz") == 0)
    return 1;

  return 0;
}

long getokt (char *ptr)
{
  long result = 0;

  while (*ptr && isdigit(*ptr))
    result = result * 8 + *ptr++ - '0';

  return result;
}

int istarfile (uulist *uthis)
{
  char *ptr;

  if (uthis == NULL)
    return 0;

  if ((ptr = strrchr (uthis->filename, '.')) == NULL)
    return 0;

  if (strcmp (ptr, ".tar") == 0)
    return 1;
  else
    return 0;
}

int findpart (uulist *uthis, short partno)
{
  uufile *iter = uthis->thisfile;

  while (iter) {
    if (iter->partno == partno)
      return 1;
    iter = iter->NEXT;
  }
  return 0;
}

uulist *_smparts_r (uulist *addit, int pass)
{
  uulist *iter = GlobalFileList;
  uufile *fiter, *dest, *temp;
  int count, flag, a, b;

  while (iter) {
    if (iter->state >= ST_OK || iter->uudet == 0) {
      iter = iter->NEXT;
      continue;
    }
    if (iter == addit) {
      iter = iter->NEXT;
      continue;
    }
    if ((iter->begin && addit->begin) || (iter->end && addit->end) ||
	(iter->uudet != addit->uudet)) {
      iter = iter->NEXT;
      continue;
    }
    if ((a = IsKnownExtension (addit->subfname)) != -1 &&
        (b = IsKnownExtension (iter->subfname))  != -1)
      if (a != b) {
        iter = iter->NEXT;
        continue;
      }

    flag  = count = 0;
    fiter = iter->thisfile;
    temp  = addit->thisfile;
    dest  = NULL;

    while (temp) {
      if (!(temp->data->uudet)) {
	temp = temp->NEXT;
	continue;
      }

      while (fiter && fiter->partno < temp->partno) {
        dest  = fiter;
        fiter = fiter->NEXT;
      }
      if (fiter && fiter->partno == temp->partno) {
        flag = 0;
        break;
      }
      else {
	flag   = 1;
        count += ((dest)  ? temp->partno - dest->partno - 1 : 0) +
                 ((fiter) ? fiter->partno - temp->partno - 1 : 0);
      }

      temp = temp->NEXT;
    }
    if (flag == 0 ||
        (pass == 0 && count > 0) ||
        (pass == 1 && count > 5)) {
      iter = iter->NEXT;
      continue;
    }

    dest  = iter->thisfile;
    fiter = addit->thisfile;

    if (iter->filename == NULL && addit->filename != NULL) {
      if ((iter->filename = (char *) malloc (strlen (addit->filename) + 1)) == NULL) {
        uuerror = NOMEM;
        return 0;
      }
      strcpy (iter->filename, addit->filename);
    }

    if (addit->begin) iter->begin = 1;
    if (addit->end)   iter->end   = 1;

    if (addit->mode != 0 && iter->mode == 0)
      iter->mode = addit->mode;

    while (fiter) {
      flag = 0;

      if (fiter->partno == iter->thisfile->partno ||
	  (dest->NEXT != NULL && fiter->partno == dest->NEXT->partno)) {
	temp           = fiter->NEXT;
	fiter->NEXT    = NULL;

	killfile (fiter);

	addit->thisfile= temp;
	fiter          = temp;
	continue;
      }
      if (fiter->partno < iter->thisfile->partno) {
	temp           = fiter->NEXT;
	fiter->NEXT    = iter->thisfile;
	iter->thisfile = fiter;
	dest           = fiter;
	addit->thisfile= temp;
	fiter          = temp;
      }
      else if (dest->NEXT == NULL || fiter->partno < dest->NEXT->partno) {
	temp           = fiter->NEXT;
	fiter->NEXT    = dest->NEXT;
	dest->NEXT     = fiter;
	addit->thisfile= temp;
	fiter          = temp;
      }
      else {
	dest = dest->NEXT;
      }
    }
    break;
  }
  return iter;
}

uulist *smergeparts (int pass)
{
  uulist *iter = GlobalFileList, *last=NULL, *res, *temp;
  int pflag=0;

  while (iter) {
    if (iter->state >= ST_OK || iter->uudet == 0) {
      last = iter;
      iter = iter->NEXT;
      continue;
    }
    if ((res = _smparts_r (iter, pass)) != NULL) {
      if (pflag++ == 0)
	if (!quiet)
	  printf ("\n");

      if (!quiet)
	printf ("Parts of '%s' merged with parts of '%s' (%d). Check ",
		(iter->subfname) ? iter->subfname : "",
		(res->subfname)  ? res->subfname  : "", pass);
 
      temp       = iter->NEXT;
      iter->NEXT = NULL;
      killlist (iter);

      /* Recheck _res_, maybe it's OK now */

      if (checkagain (res)->state == ST_OK && !quiet)
	printf ("OK");

      if (!quiet)
	printf ("\n");

      if (last == NULL) {
	GlobalFileList = temp;
	iter           = temp;
      }
      else {
	last->NEXT     = temp;
	iter           = temp;
      }

      continue;
    }
    last = iter;
    iter = iter->NEXT;
  }
  if (pflag && !quiet)
    printf ("\n");

  return GlobalFileList;
}

/*****************************************************************************
 + Frank Pilhofer                             fp@informatik.uni-frankfurt.de +
 +---------------------------------------------------------------------------+
 | Department of Computer Sciences * University of Frankfurt / Main, Germany |
 *****************************************************************************/
