/*
**  DosFCheck - check file names for DOS consistency
**
**  Distribute freely, it only encourages DOS compatibility!
**  - DJ Delorie
*/

/* This file is not part of GCC.  */

#include <stdio.h>
#ifdef __MSDOS__
#include <alloc.h>
#else
#include <malloc.h>
#endif
#include <ctype.h>
#include <string.h>

typedef struct ENT
{
  struct ENT *next;
  char *dos_name;
  char *full_name;
  char *path;
  int tagged;
} ENT;

ENT *eroot = 0;

int first_inv = 1;
int first_msg = 1;

/****************************************************************\
 *  Utility routines						*
\****************************************************************/

void
invalid_msg ()
{
  if (first_inv)
    {
      if (first_msg)
	first_msg = 0;
      else
	putchar ('\n');
      printf ("The following files are not valid DOS file names:\n");
      first_inv = 0;
    }
}

ENT *
alloc_ent ()
{
  ENT *rv = (ENT *)malloc (sizeof (ENT));
  if (rv == 0)
    {
      fprintf (stderr, "Unable to allocate memory for an ENT\n");
      exit (1);
    }
  memset (rv, 0, sizeof (ENT));
  return rv;
}

void
fill_ent (ent, path)
ENT *ent;
char *path;
{
  char *first = path;
  char *null = path+strlen (path);
  char *last_slash = strrchr (path, '/');
  char *cp, *dp;
  int dots_seen, chars_seen;

  if (last_slash+1 == null)
    {
      * --null = '\0';
      last_slash = strrchr (path, '/');
    }

  if (!last_slash)
    {
      last_slash = first-1;
    }

  if (null-last_slash < 13)
    ent->dos_name = (char *)malloc (null-last_slash);
  else
    ent->dos_name = (char *)malloc (13);
  ent->full_name = (char *)malloc (null-last_slash);
  ent->path = (char *)malloc (last_slash-first+1);

  strcpy (ent->full_name, last_slash+1);
  if (last_slash > first)
    {
      strncpy (ent->path, first, last_slash-first);
      ent->path[last_slash-first] = '\0';
    }
  else
    *ent->path = '\0';

  cp = last_slash+1;
  dp = ent->dos_name;
  dots_seen = 0;
  chars_seen = 0;
  while (1)
    {
      if (! *cp)
	break;
      switch (*cp)
	{
	case '.':
	  if (cp == last_slash+1 && strcmp (last_slash+1, "."))
	    {
	      invalid_msg ();
	      printf ("%s - file name cannot start with dot\n", path);
	      *dp = 0;
	      break;
	    }
	  if (dots_seen == 1)
	    {
	      invalid_msg ();
	      printf ("%s - too many dots\n", path);
	      *dp = '\0';
	      break;
	    }
	  *dp++ = '.';
	  chars_seen = 0;
	  dots_seen++;
	  break;
	case '"':
	case '*':
	case '+':
	case ',':
	case ';':
	case '<':
	case '=':
	case '>':
	case '?':
	case '[':
	case '\\':
	case ']':
	case '|':
	  invalid_msg ();
	  printf ("%s - invalid character `%c'\n", path, *cp);
	  *dp++ = '?';
	  chars_seen++;
	  break;
	default:
	  if (dots_seen)
	    {
	      if (chars_seen >= 3)
		break;
	    }
	  else
	    if (chars_seen >= 8)
	      break;
	  if ((*cp <= ' ') || (*cp >= 0x7f))
	    {
	      invalid_msg ();
	      printf ("%s - invalid character `%c'\n", path, *cp);
	      *dp++ = '?';
	      chars_seen++;
	      break;
	    }
	  if (islower (*cp))
	    *dp++ = toupper (*cp);
	  else
	    *dp++ = *cp;
	  chars_seen++;
	  break;
	}
      cp++;
    }
  *dp++ = '\0';
}

int
compare_ent_dosname (e1, e2)
ENT **e1;
ENT **e2;
{
  int r = strcmp ((*e1)->dos_name, (*e2)->dos_name);
  if (r == 0)
    r = strcmp ((*e1)->path, (*e2)->path);
  if (r == 0)
    r = strcmp ((*e1)->full_name, (*e2)->full_name);
  return r;
}

int
compare_ent_fullname (e1, e2)
ENT **e1;
ENT **e2;
{
  int r = strncmp ((*e1)->full_name, (*e2)->full_name, 14);
  if (r == 0)
    r = strcmp ((*e1)->path, (*e2)->path);
  if (r == 0)
    r = strcmp ((*e1)->full_name, (*e2)->full_name);
  return r;
}

char *
mpath (ent)
ENT *ent;
{
  static char buf[500];
  if (ent->path && ent->path[0])
    sprintf (buf, "%s/%s", ent->path, ent->full_name);
  else
    return ent->full_name;
  return buf;
}

/****************************************************************\
 *  List handling routines					*
\****************************************************************/

void
add_ent (ent)
ENT *ent;
{
  ent->next = eroot;
  eroot = ent;
}

void
handle_input (line)
char *line;
{
  ENT *ent = alloc_ent ();
  fill_ent (ent, line);
  add_ent (ent);
}

void
display_problems ()
{
  ENT **elist, *ent;
  int ecount, i, first, first_err;

  for (ecount=0, ent=eroot; ent; ent=ent->next, ecount++);
  elist = (ENT **)malloc (sizeof (ENT *) * ecount);
  for (ecount=0, ent=eroot; ent; ent=ent->next, ecount++)
    elist[ecount] = ent;

  qsort (elist, ecount, sizeof (ENT *), compare_ent_dosname);

  first = 1;
  first_err = 1;
  for (i=0; i<ecount-1; i++)
    {
      if ((strcmp (elist[i]->dos_name, elist[i+1]->dos_name) == 0)
	  && (strcmp (elist[i]->path, elist[i+1]->path) == 0))
	{
	  if (first_err)
	    {
	      if (first_msg)
		first_msg = 0;
	      else
		putchar ('\n');
	      printf ("The following resolve to the same DOS file names:\n");
	      first_err = 0;
	    }
	  if (first)
	    {
	      printf ("%14s : %s\n", elist[i]->dos_name, mpath (elist[i]));
	      first = 0;
	    }
	  printf ("\t\t %s\n", mpath (elist[i+1]));
	}
      else
	first = 1;
    }

  qsort (elist, ecount, sizeof (ENT *), compare_ent_fullname);

  first = 1;
  first_err = 1;
  for (i=0; i<ecount-1; i++)
    {
      if ((strncmp (elist[i]->full_name, elist[i+1]->full_name, 14) == 0)
	  && (strcmp (elist[i]->path, elist[i+1]->path) == 0))
	{
	  if (first_err)
	    {
	      if (first_msg)
		first_msg = 0;
	      else
		putchar ('\n');
	      printf ("The following resolve to the same SysV file names:\n");
	      first_err = 0;
	    }
	  if (first)
	    {
	      printf ("%.14s : %s\n", elist[i]->full_name, mpath (elist[i]));
	      first = 0;
	      elist[i]->tagged = 1;
	    }
	  printf ("\t\t %s\n", mpath (elist[i+1]));
	  elist[i+1]->tagged = 1;
	}
      else
	first = 1;
    }

  first_err = 1;
  for (i=0; i<ecount; i++)
    {
      if ((strlen (elist[i]->full_name) > 14) && !elist[i]->tagged)
	{
	  if (first_err)
	    {
	      if (first_msg)
		first_msg = 0;
	      else
		putchar ('\n');
	      printf ("The following file names are too long for SysV:\n");
	      first_err = 0;
	    }
	  printf ("%.14s : %s\n", elist[i]->full_name, mpath (elist[i]));
	}
    }
}

/****************************************************************\
 *  Main entry point						*
\****************************************************************/

main (argc, argv)
int argc;
char **argv;
{
  FILE *input = stdin;
  if (argc > 1)
    {
      input = fopen (argv[1], "r");
      if (!input)
	{
	  perror (argv[1]);
	  exit (1);
	}
    }
  while (1)
    {
      char line[500];
      char *lp;
      fgets (line, 500, input);
      if (feof (input))
	break;
      lp = line+strlen (line);
      while ((lp != line) && (*lp <= ' '))
	lp--;
      lp[1] = 0;
      handle_input (line);
    }
  display_problems ();
}

