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


/*
 * uucheck.c
 *
 * Various checking and processing of one input part
 **/


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

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

#include "uudeview.h"
#include "uuerror.h"
#include "uuutil.h"
#include "toolbox.h"


static int  lastvalid = 0;
static char lastname[255];
static int  lastpart  = 0;

static char *nofname = "UNKNOWN";
static int   nofnum  = 0;

/*
 * special characters we allow a filename to have
 */

static char *fnchars = "._-~!";

/*
 * Extract a filename from the subject line. We need anything to identify
 * the name of the program for sorting. If a nice filename cannot be found, 
 * the subject line itself is used
 * ptonum is, if not NULL, a pointer to the part number in the subject line,
 * so that it won't be used as filename.
 **/

char *GetFileName (char *subject, char *ptonum, char *ptonend)
{
  char *ptr = subject, *iter, *result, *part, save;
  int count, length=0, alflag=0;

/*
 * If this file has no subject line, assume it is the next part of the
 * previous file (this is done in PreProcessPart)
 **/

  if (subject == NULL)
    return NULL;

/*
 * If the subject starts with 'Re', it is ignored
 * REPosts or RETries are not ignored!
 **/

  if (ignreply &&
      (subject[0] == 'R' || subject[0] == 'r') &&
      (subject[1] == 'E' || subject[1] == 'e') &&
      (subject[2] == ':' || subject[2] == ' ')) {
    uuerror = IGNORED;
    return NULL;
  }

/*
 * Ignore a "Repost" prefix of the subject line. We don't want to get
 * a file named "Repost" :-)
 **/

  if (_FP_strnicmp (subject, "repost", 6) == 0) {
    subject += 6;
    while (*subject == ' ' || *subject == ':')
      subject++;
  }
  while (*subject == ' ') subject++;

  part = _FP_stristr (subject, "part");
  if (part == subject) {
    subject += 4;
    while (*subject == ' ') subject++;
  }

  /*
   * If the file was encoded by uuenview, then the filename is enclosed
   * in [brackets]. But check what's inside these bracket's, try not to
   * fall for something other than a filename
   */

  ptr = subject;
  while ((iter = strchr (ptr, '[')) != NULL) {
    if (strchr (iter, ']') == NULL) {
      ptr = iter + 1;
      continue;
    }
    iter++;
    while (isspace (*iter))
      iter++;
    count = length = alflag = 0;
    while (iter[count] && 
	   (isalnum (iter[count]) || strchr (fnchars, iter[count])!=NULL)) {
      if (isalpha (iter[count]))
	alflag++;
      count++;
    }
    if (count<4 || alflag==0) {
      ptr = iter + 1;
      continue;
    }
    length = count;
    while (isspace (iter[count]))
      count++;
    if (iter[count] == ']') {
      ptr = iter;
      break;
    }
    length = 0;
    ptr = iter + 1;
  }

  /*
   * new filename detection routine, fists mostly for files by ftp-by-email
   * servers that create subject lines with ftp.host.address:/full/path/file
   * on them. We look for slashes and take the filename from after the last
   * one ... or at least we try to.
   */

  if (length == 0) {
    ptr = subject;
    while ((iter = strchr (ptr, '/')) != NULL) {
      if (iter >= ptonum && iter <= ptonend) {
	ptr = iter + 1;
	continue;
      }
      count = length = 0;
      iter++;
      while (iter[count] &&
	     (isalnum(iter[count])||strchr(fnchars, iter[count])!=NULL))
	count++;
      if (iter[count] == ' ' && length > 4) {
	length = count;
	break;
      }
      ptr = iter + ((count)?count:1);
    }
  }

  /*
   * Look for two alphanumeric strings separated by a '.'
   * (That's most likely a filename)
   **/

  if (length == 0) {
    ptr = subject;
    while (*ptr && *ptr != 0x0a && *ptr != 0x0d && ptr != part) {
      iter  = ptr;
      count = length = alflag = 0;
      
      if (_FP_strnicmp (ptr, "ftp", 3) == 0) {
	/* hey, that's an ftp address */
	while (isalpha (*ptr) || isdigit (*ptr) || *ptr == '.')
	  ptr++;
	continue;
      }
      
      while ((isalnum(*iter)||strchr(fnchars, *iter)!=NULL||
	      *iter=='/') && *iter && iter != ptonum && *iter != '.') {
	if (isalpha (*iter))
	  alflag = 1;
	
	count++; iter++;
      }
      if (*iter == '\0' || iter == ptonum) {
	if (iter == ptonum)
	  ptr  = ptonend;
	else
	  ptr  = iter;

	length = 0;
	continue;
      }
      if (*iter++ != '.' || count > 32 || alflag == 0) {
	ptr    = iter;
	length = 0;
	continue;
      }
      if (_FP_strnicmp (iter, "edu", 3) == 0 || 
	  _FP_strnicmp (iter, "gov", 3) == 0) {
	/* hey, that's an ftp address */
	while (isalpha (*iter) || isdigit (*iter) || *iter == '.')
	  iter++;
	ptr    = iter;
	length = 0;
	continue;
      }
      
      length += count + 1;
      count   = 0;
      
      while ((isalnum(iter[count])||strchr(fnchars, iter[count])!=NULL||
	      iter[count]=='/') && iter[count] && iter[count] != '.')
	count++;
      
      if (iter[count]==':' && iter[count+1]=='/') {
	/* looks like stuff from a mail server */
	ptr = iter + 1;
	length = 0;
	continue;
      }
      
      if (count > 8 || iter == ptonum) {
	ptr    = iter;
	length = 0;
	continue;
      }

      if (iter[count] != '.') {
	length += count;
	break;
      }
      
      while (iter[count] &&
	     (isalnum(iter[count])||strchr(fnchars, iter[count])!=NULL||
	      iter[count]=='/'))
	count++;
      
      if (iter[count]==':' && iter[count+1]=='/') {
	/* looks like stuff from a mail server */
	ptr = iter + 1;
	length = 0;
	continue;
      }
      
      if (count < 12 && iter != ptonum) {
	length += count;
	break;
      }

      ptr    = iter;
      length = 0;
    }
  }

  if (length == 0) { /* No filename found, use subject line for ident */
    ptr = subject;

    while (*ptr && !isalpha (*ptr))
      ptr++;

    while ((isalnum(ptr[length])||strchr(fnchars,ptr[length])!=NULL||
	    ptr[length] == '/') && 
	   ptr[length] && ptr+length!=part && ptr+length!=ptonum)
      length++;

    if (length) {
      if (ptr[length] == '\0' || ptr[length] == 0x0a || ptr[length] == 0x0d) {
        length--;

        while ((ptr[length] == ' ' || isdigit (ptr[length])) && length > 0)
          length--;
      }
      else {
        length--;

        while (ptr[length] == ' ' && length > 0)
          length--;
      }
      length++;
    }
  }

  if (length == 0) { /* Still found nothing? We need *something*! */
    ptr    = nofname;
    length = strlen (nofname);
  }

  if ((result = (char *) malloc (length + 1)) == NULL) {
    uuerror = NOMEM;
    return NULL;
  }
    
  memcpy (result, ptr, length);
  result[length] = '\0';
    
  return result;
}

/*
 * Extract the Part Number from the subject line.
 * We look first for numbers in (#/#)'s, then for numbers in [#/#]'s
 * and then for digits that are not part of a string.
 * If we cannot find anything, assume it is the next part of the
 * previous file.
 * If we find a part number, we put a pointer to it in *where. This is
 * done so that the GetFileName function doesn't accidentally use the
 * part number as the file name. *whend points to the end of this part
 * number.
 **/

int GetPartNo (char *subject, char **where, char **whend)
{
  char *ptr = subject, *iter, *delim;
  int count, length=0;

  *where = NULL;
  *whend = NULL;

  if (subject == NULL) {
    uuerror = NOINFO;
    return -1;
  }

  if (ignreply &&
      (subject[0] == 'R' || subject[0] == 'r') && /* Ignore replies, but not */
      (subject[1] == 'E' || subject[1] == 'e') && /* reposts                 */
      (subject[2] == ':' || subject[2] == ' ')) {
    uuerror = IGNORED;
    return -2;
  }

  while ((iter = strchr (ptr, '(')) != NULL) {
    count = length = 0; iter++;

    while (*iter == ' ' || *iter == '#')
      iter++;

    if (!isdigit (*iter)) {
      ptr = iter;
      continue;
    }
    while (isdigit (iter[length = count]))
      count++;

    if (iter[count] == '\0' || iter[count+1] == '\0') {
      iter  += count;
      length = 0;
      break;
    }
    if (iter[count] == ')') {
      *where = iter;
      delim  = ")";
      break;
    }

    while (iter[count] == ' ' || iter[count] == '#' ||
           iter[count] == '/' || iter[count] == '\\')  count++;

    if (_FP_strnicmp (iter + count, "of", 2) == 0)
      count += 2;

    while (iter[count] == ' ')    count++;
    while (isdigit (iter[count])) count++;
    while (iter[count] == ' ')    count++;

    if (iter[count] == ')') {
      *where = iter;
      delim  = ")";
      break;
    }

    length = 0;
    ptr    = iter;
  }

  ptr = subject;

  if (length == 0) {
    while ((iter = strchr (ptr, '[')) != NULL) {
      count = length = 0; iter++;
      
      while (*iter == ' ' || *iter == '#')
	iter++;

      if (!isdigit (*iter)) {
	ptr = iter;
	continue;
      }
      while (isdigit (iter[length = count]))
	count++;

      if (iter[count] == '\0' || iter[count+1] == '\0') {
	iter  += count;
        length = 0;
	break;
      }
      if (iter[count] == ']') {
	*where = iter;
	delim  = "]";
	break;
      }

      while (iter[count] == ' ' || iter[count] == '#' ||
             iter[count] == '/' || iter[count] == '\\')  count++;

      if (_FP_strnicmp (iter + count, "of", 2) == 0)
	count += 2;

      while (iter[count] == ' ')    count++;
      while (isdigit (iter[count])) count++;
      while (iter[count] == ' ')    count++;

      if (iter[count] == ']') {
	*where = iter;
	delim  = "]";
	break;
      }

      length = 0;
      ptr    = iter;
    }
  }

  if (length == 0) {
    if ((iter = _FP_stristr (subject, "part")) != NULL) {
      iter += 4;

      while (isspace (*iter) || *iter == '.' || *iter == '-')
	iter++;

      while (isdigit (iter[length]))
        length++;

      if (length == 0) {
	if (_FP_strnicmp (iter, "one", 3) == 0)        length = 1;
	else if (_FP_strnicmp (iter, "two", 3) == 0)   length = 2;
	else if (_FP_strnicmp (iter, "three", 5) == 0) length = 3;
	else if (_FP_strnicmp (iter, "four",  4) == 0) length = 4;
	else if (_FP_strnicmp (iter, "five",  4) == 0) length = 5;
	else if (_FP_strnicmp (iter, "six",   3) == 0) length = 6;
	else if (_FP_strnicmp (iter, "seven", 5) == 0) length = 7;
	else if (_FP_strnicmp (iter, "eight", 5) == 0) length = 8;
	else if (_FP_strnicmp (iter, "nine",  4) == 0) length = 9;
	else if (_FP_strnicmp (iter, "ten",   3) == 0) length = 10;

	if (length && (*whend = strchr (iter, ' '))) {
	  *where = iter;
	  return length;
	}
	else
	  length = 0;
      }
      else {
	*where = iter;
	delim  = "of";
      }
    }
  }

  if (length == 0) {
    if ((iter = _FP_strirstr (subject, "of")) != NULL) {
      while (iter>subject && isspace (*(iter-1)))
	iter--;
      if (isdigit(*(iter-1))) {
	while (iter>subject && isdigit (*(iter-1)))
	  iter--;
	if (!isdigit (*iter) && !isalpha (*iter) && *iter != '.')
	  iter++;
	ptr = iter;

	while (isdigit (*ptr)) {
	  ptr++; length++;
	}
	*where = iter;
	delim  = "of";
      }
    }
  }

  if (length == 0) {
    count = strlen(subject) - 1;
    ptr   = subject;
 
    while (count > 0) {
      if (!isdigit(ptr[count])||isalpha(ptr[count+1])||ptr[count+1] == '.') {
	count--;
	continue;
      }
      length = 0;

      while (count >= 0 && isdigit (ptr[count])) {
	count--; length++;
      }
      if (count>=0 && ((isalpha (ptr[count]) && 
			(ptr[count] != 's' || ptr[count+1] != 't') &&
			(ptr[count] != 'n' || ptr[count+1] != 'd')) || 
		       ptr[count] == '/' || ptr[count] == '.' || 
		       ptr[count] == '-' || ptr[count] == '_')) {
        length = 0;
        continue;
      }
      count++;
      iter = ptr + count;

      if (length > 4) {
	length = 0;
	continue;
      }
      *where = iter;
      delim  = "of";
      break;
    }
  }

  if (length == 0) {
    /*
     * some people use the strangest things, including spelling mistakes :-)
     */
    if ((iter = _FP_stristr (subject, "first")) != NULL)        length = 1;
    else if ((iter = _FP_stristr (subject, "second")) != NULL)  length = 2;
    else if ((iter = _FP_stristr (subject, "third")) != NULL)   length = 3;
    else if ((iter = _FP_stristr (subject, "forth")) != NULL)   length = 4;
    else if ((iter = _FP_stristr (subject, "fourth")) != NULL)  length = 4;
    else if ((iter = _FP_stristr (subject, "fifth")) != NULL)   length = 5;
    else if ((iter = _FP_stristr (subject, "sixth")) != NULL)   length = 6;
    else if ((iter = _FP_stristr (subject, "seventh")) != NULL) length = 7;
    else if ((iter = _FP_stristr (subject, "eigth")) != NULL)   length = 8;
    else if ((iter = _FP_stristr (subject, "nineth")) != NULL)  length = 9;
    else if ((iter = _FP_stristr (subject, "ninth")) != NULL)   length = 9;
    else if ((iter = _FP_stristr (subject, "tenth")) != NULL)   length = 10;

    if (length && (*whend = strchr (iter, ' '))) {
      *where = iter;
      return length;
    }
    else
      length = 0;
  }

  if (length == 0) {
    uuerror = NOINFO;
    return -1;
  }

  *where = iter;

  if ((*whend = _FP_stristr (iter, delim)) != NULL && (*whend - *where) < 12) {
    ptr = (*whend += strlen (delim));

    while (*ptr == ' ')
      ptr++;

    if (isdigit (*ptr)) {
      *whend = ptr;
      while (isdigit (**whend))
	*whend += 1;
    }
  }
  else
    *whend = iter + length;

  return atoi (iter);
}

/*
 * Obtain and process some information about the data.
 **/

uufile *PreProcessPart (fileread *data)
{
  char *where, *whend, temp[80];
  uufile *result;

  if ((result = (uufile *) malloc (sizeof (uufile))) == NULL) {
    uuerror = NOMEM;
    return NULL;
  }
  if (data->partno) {
    where          = NULL;
    result->partno = data->partno;
  }
  else if ((result->partno = GetPartNo (data->subject,&where,&whend)) == -2) {
    free (result);
    uuerror = IGNORED;
    return NULL;
  }

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

  result->subfname = GetFileName (data->subject, where, whend);

  if (data->mimeid != NULL)
    result->mimeid = _FP_strdup (data->mimeid);
  else
    result->mimeid = NULL;

  /*
   * If it's the first part of something and has some valid data, but
   * no subject or anything, initialize lastvalid
   */
  if (result->subfname == NULL && (data->begin || result->partno == 1) &&
      data->uudet) {
    /*
     * in this case, it really _should_ have a filename somewhere
     */
    if (result->filename != NULL)
      result->subfname = _FP_strdup (result->filename);
    else { /* if not, escape to UNKNOWN. We need to fill subfname */
      sprintf (temp, "%s.%03d", nofname, ++nofnum);
      result->subfname = _FP_strdup (temp);
    }
    /*
     * in case the strdup failed
     */
    if (result->subfname == NULL) {
      uuerror = NOMEM;
      free (result->filename);
      free (result);
      return NULL;
    }
    /*
     * if it's also got an 'end', or is the last part in a MIME-Mail,
     * then don't set lastvalid
     */
    if (!data->end && (!data->partno || data->partno != data->maxpno)) {
      /*
       * initialize lastvalid
       */
      lastvalid = 1;
      lastpart  = result->partno = 1;
      strcpy (lastname, result->subfname);
    }
    else
      result->partno = 1;
  }
  else if (result->subfname == NULL && data->uudet) {
    /*
     * if we have lastvalid, use it
     */
    if (lastvalid) {
      result->subfname = _FP_strdup (lastname);
      result->partno   = ++lastpart;

      /*
       * if it's the last part, invalidate lastvalid
       */
      if (data->end || (data->partno && data->partno == data->maxpno))
	lastvalid = 0;
    }
    else if (data->uudet == B64ENCODED) {
      /*
       * exception for MIME-encoded files. Assume it's the first
       * part. I wonder why it's got no part number?
       */
      if (result->filename != NULL)
        result->subfname = _FP_strdup (result->filename);
      else { /* if not, escape to UNKNOWN. We need to fill subfname */
        sprintf (temp, "%s.%03d", nofname, ++nofnum);
        result->subfname = _FP_strdup (temp);
      }
      if (result->subfname == NULL) {
        uuerror = NOMEM;
        free (result->filename);
        free (result);
        return NULL;
      }
      lastvalid = 1;
      lastpart  = result->partno = 1;
      strcpy (lastname, result->subfname);
    }
    else { 
      /* 
       * it's got no info, it's got no begin, and we don't know anything
       * about this part. Let's forget all about it.
       */
      free (result->filename);
      free (result);
      return NULL;
    }
  }
  else if (result->subfname == NULL && result->partno == -1) {
    /*
     * This, too, is a part without any useful information that we
     * should forget about.
     */
    free (result->filename);
    free (result);
    return NULL;
  }
  else if (result->subfname == NULL) {
    /*
     * This is a part without useful subject name, a valid part number
     * but no encoded data. It *could* be the zeroeth part of something,
     * but we don't care here. Just forget it.
     */
    free (result->filename);
    free (result);
    return NULL;
  }

  /*
   * now, handle some cases where we have a useful subject but no
   * useful part number
   */

  if (result->partno == -1 && data->begin) {
    /*
     * hmm, this is reason enough to initialize lastvalid, at least 
     * if we have no end
     */
    if (!data->end) {
      strcpy (lastname, result->subfname);
      result->partno = lastpart = 1;
      lastvalid = 1;
    }
    else
      result->partno = 1;
  }
  else if (result->partno == -1 && data->uudet) {
    if (lastvalid) {
      /*
       * if the subject filename is the same as last time, use part no
       * of lastvalid. If at end, invalidate lastvalid
       */
      if (_FP_stricmp (lastname, result->subfname) == 0)
	result->partno = ++lastpart;

      if (data->end)
	lastvalid = 0;
    }
    else {
      /*
       * data but no part no. It's something InsertPartToList() should
       * handle
       */
      goto skipcheck;
    }
  }
  else if (result->partno == -1) {
    /*
     * it's got no data, so why should we need this one anyway?
     */
    free (result->subfname);
    free (result->filename);
    free (result);
    return NULL;
  }

  /*
   * at this point, the part should have a valid subfname and a valid
   * part number. If it doesn't, then fail.
   */
  if (result->subfname == NULL || result->partno == -1) {
    free (result->subfname);
    free (result->filename);
    free (result);
    return NULL;
  }

 skipcheck:
  result->data = data;
  result->NEXT = NULL;
  uuerror      = OK;

  return result;
}

/*
 * At this point, all files are read in and stored in the
 * "GlobalFileList". Do some checking. All parts there?
 **/

uulist *CheckGlobalList (void)
{
  char misparts[99]; int miscount=0, count;
  uulist *liter = GlobalFileList;
  short flag, part, pflag;
  uufile *fiter, *tmp;

  while (liter != NULL) {
    miscount = 0;

    if (liter->state >= ST_OK);
    else if ((fiter = liter->thisfile) == NULL)
      liter->state = ST_MISPART;
    else {
      flag = pflag = 0;

      if ((part = fiter->partno) > 1) {
        tmp = fiter;
        while (tmp) {
          if (tmp->data->begin || tmp->data->uudet == B64ENCODED)
            break;
          tmp = tmp->NEXT;
        }
        if (!tmp) {
          for (count=1; count<part && miscount < 98; count++)
            misparts[miscount++] = count;
        }
      }
      if (miscount == 98) {
        liter = liter->NEXT;
        continue;
      }

      if (fiter->NEXT != NULL || fiter->data->begin ||
	  fiter->data->end    || fiter->data->uudet)  {
	pflag = 1;
	
	if (!quiet)
	  printf ("Found File '%s' Part NOs %d ", 
		  (liter->filename && liter->filename[0]) ? 
                  liter->filename : ((liter->subfname) ? liter->subfname :
				                         nofname),
		  fiter->partno);
      }

      if (liter->uudet == B64ENCODED)
	flag |= 3; /* Don't need begin or end with B64data */

      if (fiter->data->begin) {flag |= 1; if (pflag&0x3f&&!quiet) printf ("begin ");}
      if (fiter->data->end)   {flag |= 2; if (pflag&0x3f&&!quiet) printf ("end ");}
      if (fiter->data->uudet)  flag |= 4; 
      
      fiter = fiter->NEXT;

      while (fiter != NULL) {
	if (!quiet)
	  printf ("%d ", fiter->partno);

	if (fiter->partno != part + 1) {
          for (count=part+1; count < fiter->partno && miscount < 98; count++) 
            misparts[miscount++] = count;
        }
        if (miscount == 98)
	  break;

        part = fiter->partno;

	if (fiter->data->begin) {flag |= 1; if (!quiet) printf ("begin ");}
	if (fiter->data->end)   {flag |= 2; if (!quiet) printf ("end ");}
	if (fiter->data->uudet)  flag |= 4;
	
	fiter = fiter->NEXT;
      }

      /*
       * if in fast mode, we don't notice an 'end'. So if its uu or xx
       * encoded and looks ok otherwise, we hope it's there
       */

      if (fast && 
	  (liter->uudet == UU_ENCODED || liter->uudet == XX_ENCODED) &&
	  (flag & 0x01) && (flag & 0x04))
	flag |= 2;

      if (miscount) {
        free (liter->misparts);
        if ((liter->misparts = (char *) malloc (miscount + 1)) != NULL) {
          memcpy (liter->misparts, misparts, miscount);
          liter->misparts[miscount] = 0;
        }
        liter->state = ST_MISPART;
      }
      else {
	if ((flag & 4) == 0)
	  liter->state = ST_NODATA;
	else if ((flag & 1) == 0)
	  liter->state = ST_NOBEGIN;
	else if ((flag & 2) == 0)
	  liter->state = ST_NOEND;
	else {
	  if (!quiet)
	    printf ("OK");
	  liter->state = ST_OK;
	}
      }
    }
    if (pflag&0x3f) { 
      pflag = 0x40;
      if (!quiet)
	printf ("\n");
    }
    if (liter->state==ST_OK && 
        (liter->filename==NULL || liter->filename[0]=='\0')) {
      if (liter->filename)
        free (liter->filename);
      if (liter->subfname && liter->subfname[0] &&
          strpbrk(liter->subfname, "()[];: ")==NULL)
        liter->filename = _FP_strdup (liter->subfname);
      else {
        sprintf (misparts, "%s.%03d", nofname, ++nofnum);
        liter->filename = _FP_strdup (misparts);
      }
    }
    liter = liter->NEXT;
  }
  if (pflag && !quiet)
    printf ("\n");

  return GlobalFileList;
}

uulist *checkagain (uulist *uthis)
{
  char misparts[99]; int miscount=0, count;
  short flag=0, part;
  uufile *fiter;

  free (uthis->misparts);
  uthis->misparts = NULL;

  if ((fiter = uthis->thisfile) == NULL) {
    uthis->state = ST_MISPART;
    return uthis;
  }

  if ((part = fiter->partno) > 1) {
    for (count=1; count<part && miscount < 98; count++)
      misparts[miscount++] = count;
  }

  if (!quiet)
    printf ("%d ", fiter->partno);

  if (fiter->data->uudet == B64ENCODED)
    flag |= 3;

  if (fiter->data->begin) {flag |= 1; if (!quiet) printf ("begin ");}
  if (fiter->data->end)   {flag |= 2; if (!quiet) printf ("end ");}
  if (fiter->data->uudet)  flag |= 4; 
      
  fiter = fiter->NEXT;

  while (fiter != NULL) {
    if (!quiet)
      printf ("%d ", fiter->partno);

    if (fiter->partno != part + 1) {
      for (count = part+1; count < fiter->partno && miscount < 98; count++) 
	misparts[miscount++] = count;
    }
    if (miscount == 98)
      return uthis;

    part = fiter->partno;

    if (fiter->data->uudet == B64ENCODED)
      flag |= 3;

    if (fiter->data->begin) {flag |= 1; if (!quiet) printf ("begin ");}
    if (fiter->data->end)   {flag |= 2; if (!quiet) printf ("end ");}
    if (fiter->data->uudet)  flag |= 4;
    
    fiter = fiter->NEXT;
  }
  if (miscount) {
    if ((uthis->misparts = (char *) malloc (miscount + 1)) != NULL) {
      memcpy (uthis->misparts, misparts, miscount);
      uthis->misparts[miscount] = 0;
    }
    uthis->state = ST_MISPART;
  }
  else {
    if ((flag & 4) == 0)
      uthis->state = ST_NODATA;
    else if ((flag & 1) == 0)
      uthis->state = ST_NOBEGIN;
    else if ((flag & 2) == 0)
      uthis->state = ST_NOEND;
    else
      uthis->state = ST_OK;
  }
  return uthis;
}


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