#include <unistd.h>		/* readlink */
#include <limits.h>		/* PATH_MAX */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>		/* strcmp, memset */
#include <dirent.h>		/* opendir, etc. */
#include <sys/types.h>		/* lstat */
#include <sys/stat.h>		/* lstat */
#include <errno.h>

char *progname;

typedef struct _filename {
  struct _filename *next;
  char *name;
} filename;

typedef struct {
  filename *first, *current, *last;
} filelist;

void makefilelist(list)
     filelist *list;
{
  list->first = NULL;
  list->current = NULL;
  list->last = NULL;

  return;
}

void freefilelist(list)
     filelist *list;
{
  filename *i, *j = NULL;

  i = list->first;
  while (i != NULL)
    {
      j = i;
      i = i->next;
      free(j->name);
      free(j);
    }
}

void savefile(list, name)
     filelist *list;
     char *name;
{
  filename *new;

  new = malloc(sizeof(filename));
  if (new == NULL)
    {
      fprintf(stderr, "%s: error in savefile during malloc()\n", progname);
      exit(1);
    }

  new->name = malloc(strlen(name) + 1);
  if (new->name == NULL)
    {
      fprintf(stderr, "%s: error in savefile during malloc()\n", progname);
      exit(1);
    }

  strcpy(new->name, name);

  new->next = NULL;
  if (list->first == NULL)
    list->first = new;
  if (list->current == NULL)
    list->current = new;
  if (list->last != NULL)
    list->last->next = new;
  list->last = new;
}

char *getfile(list)
     filelist *list;
{
  char *ptr;

  if (list->current)
    {
      ptr = list->current->name;
      list->current = list->current->next;
      return ptr;
    }
  else
    return NULL;
}

void process(absolute, relative, linkname, dest, fangs)
     char *absolute, *relative;
     char *linkname, *dest;
     int fangs;
{
  FILE *linkfile = NULL;
  DIR *dir;
  char abs[PATH_MAX], rel[PATH_MAX];
  char *directoryname;
  char nextdest[PATH_MAX];
  char rcslink[PATH_MAX], linkval[PATH_MAX];
  struct dirent *curent;
  struct stat info;
  filelist dirlist;
  int length;

  fprintf(stdout, "Processing %s\n", relative);

  if (chdir(absolute))
    {
      fprintf(stderr, "%s: error %d from chdir()\n", progname, errno);
      exit(1);
    }

  if (!fangs)
    fprintf(stdout, "cd %s\n", absolute);

  makefilelist(&dirlist);

  dir = opendir(".");
  if (dir == NULL)
    {
      fprintf(stderr, "%s: could not open directory %s\n",
	      progname, relative);
      exit(1);
    }

  while (curent = readdir(dir))
    {
      if (strcmp(curent->d_name, ".") && strcmp(curent->d_name, ".."))
	{
	  if (lstat(curent->d_name, &info))
	    {
	      fprintf(stderr, "%s: error %d from lstat(%s)", progname,
		      errno, curent->d_name);
	      exit(1);
	    }

	  if ((info.st_mode & S_IFMT) == S_IFDIR)
	    {
	      savefile(&dirlist, curent->d_name);
	    }
	}
    }

  closedir(dir);

  sprintf(rcslink, "%s%s", dest, relative+1);
  length = readlink(linkname, linkval, sizeof(linkval));

  if (length == -1 && errno != ENOENT)
    {
      fprintf(stderr, "%s: error %d from readlink\n", progname, errno);
      exit(1);
    }

  if (length != -1)
    linkval[length] = '\0'; /* bug? */

  if (length == -1 || strcmp(rcslink, linkval))
    {
      if (length != -1)
	{
	  if (!fangs)
	    fprintf(stdout, "rm %s; ", linkname);
	  else
	    if (unlink(linkname))
	      {
		fprintf(stderr, "%s: error %d from unlink\n",
			progname, errno);
		exit(1);
	      }
	}

      if (!fangs)
	fprintf(stdout, "ln -s %s %s\n", rcslink, linkname);
      else
	if (symlink(rcslink, linkname))
	  {
	    fprintf(stderr, "%s: symlink returned error %d\n",
		    progname, errno);
	    exit(1);
	  }
    }

  while (directoryname = getfile(&dirlist))
    {
      if (dest[0] == '.')
	sprintf(nextdest, "../%s", dest);
      else
	strcpy(nextdest, dest);

      sprintf(abs, "%s/%s", absolute, directoryname);
      sprintf(rel, "%s/%s", relative, directoryname);
      process(abs, rel, linkname, nextdest, fangs);
    }

  freefilelist(&dirlist);
}

void usage()
{
  fprintf(stderr,
	  "usage: cd top; rcslink [-n] destination linkname\n");
  exit(1);
}

int main(argc, argv)
     int argc;
     char **argv;
{
  char curdir[PATH_MAX];
  char *partial = ".";
  char *linkname, *dest;
  int doit = 1;

  progname = argv[0];

  if (getcwd(curdir, sizeof(curdir)) == NULL)
    {
      fprintf(stderr, "%s: getcwd() failed with error %d\n", progname, errno);
      exit(1);
    }

  if (argc != 3 && argc != 4)
    usage();

  if (argc == 4)
    {
      if (strcmp(argv[1], "-n"))
	usage();

      doit = 0;
      linkname = argv[3];
      dest = argv[2];
    }
  else
    {
      if (!strcmp(argv[1], "-n"))
	usage();

      linkname = argv[2];
      dest = argv[1];
    }

  process(curdir, partial, linkname, dest, doit);
  exit(0);
}
