/* Copyright (C) 1993,1994 by the author(s).
 
 This software is published in the hope that it will be useful, but
 WITHOUT ANY WARRANTY for any part of this software to work correctly
 or as described in the manuals. See the ShapeTools Public License
 for details.

 Permission is granted to use, copy, modify, or distribute any part of
 this software but only under the conditions described in the ShapeTools 
 Public License. A copy of this license is supposed to have been given
 to you along with ShapeTools in a file named LICENSE. Among other
 things, this copyright notice and the Public License must be
 preserved on all copies.
 */
/*
 * AtFS -- Attribute Filesystem
 *
 * afnames.c -- deal with UNIX-Filesystem names
 *
 * Author: Andreas Lampen (Andreas.Lampen@cs.tu-berlin.de)
 *
 * $Header: afnames.c[7.0] Sun Jan 23 16:05:55 1994 andy@cs.tu-berlin.de frozen $
 */

#include "atfs.h"
#include "afarchive.h"

/*============================================
 *  af_uniqpath -- build canonical pathname
 *============================================*/

static char *givenPath[8] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
static char *uniqPath[8] =  {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};

EXPORT char *af_uniqpath (pathSym)
     char *pathSym;
{
  char curPath[PATH_MAX], tmpPath[PATH_MAX];
  int i=0;

  if (!pathSym || !(*pathSym) || !strcmp (pathSym, ".")) {
    getcwd (tmpPath, PATH_MAX);
    return (af_entersym (tmpPath));
  }

  /* check if "pathSym" is registered in cache */
  while (givenPath[i] && (i < 8)) {
    if (pathSym == givenPath[i])
      return (uniqPath[i]);
    i++;
  }

  /* add pathSym to cache */
  if (i==8)
    i=0;

  getcwd (curPath, PATH_MAX);
  if (chdir (pathSym) == -1) {
    /* char tmpPath[PATH_MAX+64];
     * sprintf (tmpPath, "cannot change to directory %s", pathSym);
     * af_wng ("uniqPath", tmpPath);
     */
    return (pathSym);
  }
  getcwd (tmpPath, PATH_MAX);
  givenPath[i] = pathSym;
  uniqPath[i] = af_entersym (tmpPath);
  chdir (curPath);

  return (uniqPath[i]);
}

/*================================================================
 *	af_setarchpath
 *================================================================*/

static char *archpath = NULL;

EXPORT char *af_setarchpath (pathname)
     char *pathname;
{
  register char *oldpath = archpath;

  if (pathname == NULL)
    archpath = NULL;
  else
    archpath = af_uniqpath (af_entersym(pathname));
  return (oldpath);
}

/*================================================================
 *	afArchiveOwner
 *================================================================*/

LOCAL char *curAtFSPath = NULL;
LOCAL uid_t curAtFSuid = -1;
LOCAL gid_t curAtFSgid = -1;
LOCAL bool  curAtFSWriteOk = FALSE;
LOCAL mode_t curAtFSMode = 0;

EXPORT Af_user *afArchiveOwner (archDirSym, writeok, gid)
     char    *archDirSym;
     bool    *writeok; /* out */
     gid_t   *gid; /* out */
{
  struct stat ibuf;

  *writeok = FALSE;
  *gid = -1;

  if (archDirSym == curAtFSPath) {
    *writeok = curAtFSWriteOk;
    *gid = curAtFSgid;
    return (af_afuser (curAtFSuid));
  }

  if (stat (archDirSym, &ibuf) == ERROR) {
    curAtFSPath = NULL;
    curAtFSuid = -1;
    curAtFSgid = -1;
    curAtFSWriteOk = FALSE;
    curAtFSMode = 0;
    return (NULL);
  }

  if (!access (archDirSym, W_OK))
    *writeok = TRUE;

  curAtFSPath = archDirSym;
  curAtFSuid = ibuf.st_uid;
  *gid = curAtFSgid = ibuf.st_gid;
  curAtFSWriteOk = *writeok;
  curAtFSMode = ibuf.st_mode;

  return (af_afuser (ibuf.st_uid));
} /* afArchiveOwner */ 

EXPORT mode_t afArchiveMode (archDirSym)
     char    *archDirSym;
{
  bool  writeOk;
  gid_t dummy;

  if (archDirSym == curAtFSPath) {
    return (curAtFSMode);
  }

  afArchiveOwner (archDirSym, &writeOk, &dummy);
    return (curAtFSMode); /* returns 0 if afArchiveOwner fails */
}


/*========================================================================
 * afArchivePath -- build pathname for directory containing archive files
 *                  (realize symbolic links for AtFS subdirectory)
 *========================================================================*/

static struct { char *dirname, *atfsname; } arDirList[AF_MAXLISTS];
static int arDirCount = 0;

EXPORT char *afArchivePath (pathname)
     char *pathname;
{
  register int  i = 0, len;
  char          tmpname[PATH_MAX];
  struct stat   ibuf;
  register FILE *infile;

  /* see if there is an explicit pathname where archives shall be stored */
  if (archpath)
    return (af_entersym (archpath));

  while (arDirList[i].dirname != pathname) {
    if (i == arDirCount) {
      arDirList[i].dirname = pathname;

      ibuf.st_mode = 0;
      if (!strcmp (pathname, "/"))
	sprintf (tmpname, "/%s", AF_SUBDIR);
      else
	sprintf (tmpname, "%s/%s", pathname, AF_SUBDIR);
      stat (tmpname, &ibuf);
      if (S_ISREG(ibuf.st_mode)) {
	/* AF_SUBDIR is a regular file -- maybe it contains
	 * a reference to another AtFS directory.
	 * -> open file and read contents
	 */
	if ((infile = fopen (tmpname, "r"))) {
	  fgets (tmpname, PATH_MAX, infile);
	  fclose (infile);
	}
	/* remove trailing newline */
	len = strlen(tmpname);
	if (tmpname[len-1] == '\n')
	  tmpname[len-1] = '\0';
	arDirList[i].atfsname = af_uniqpath (af_entersym (tmpname));
	arDirCount++;
	break;
      }
      arDirList[i].atfsname = af_entersym (tmpname);
      arDirCount++;
      break;
    }
    else
      i++;
  }
  return (arDirList[i].atfsname);
}

/*================================================================
 * Get Archive File Name
 *================================================================*/

EXPORT char *afArchiveName (archivePath, class, name, type, mode)
     char *archivePath, *class, *name, *type;
     int  mode;
{
  static char arName[PATH_MAX];
  char *namePtr = arName, *tmpPtr;
  mode_t  oldUmask;

  /* build only pathname first */
  tmpPtr = archivePath;
  while ((*namePtr = *tmpPtr++)) namePtr++;
  *namePtr++ = '/';
  if (class && class[0]) {
    tmpPtr = class;
    while ((*namePtr = *tmpPtr++)) namePtr++;
  }
  *namePtr = '\0';

  oldUmask = umask (000);

  if (mode == AF_WRITE) {
    bool  writeOk;
    gid_t dummy;
    struct stat dirIbuf;

    /* check existence of subdirectories */
    if (afArchiveOwner (archivePath, &writeOk, &dummy)) {
      /* AtFS directory exists, look for subdirectory */
      if (writeOk == TRUE) {
	if (stat (arName, &dirIbuf) == ERROR) {
	  /* subdirectory does not exist, create it with same mode
	     and group as AtFS dir */
	  mkdir (arName, curAtFSMode);
	  if (chown (arName, curAtFSuid, curAtFSgid) == -1)
	    chown (arName, -1, curAtFSgid);
	}
	else {
	  /* subdirectory exists, check permission */
	  if (access (arName, W_OK)) {
	    /* Hmm, no permision ? Try to change mode */
	    chmod (arName, curAtFSMode);
	    if (chown (arName, curAtFSuid, curAtFSgid) == -1)
	      chown (arName, -1, curAtFSgid);
	  }
	}
      }
      /* else AtFS directory not writable */
    }
    else {
      /* no AtFS directory -- try to create it (plus subdirectories) */
      char subdirName[PATH_MAX];
      mkdir (archivePath, 0755);
      sprintf (subdirName, "%s/%s", archivePath, AF_ATTRDIR);
      mkdir (subdirName, 0755);
      sprintf (subdirName, "%s/%s", archivePath, AF_DATADIR);
      mkdir (subdirName, 0755);
    }
  }

  umask (oldUmask);

  /* add file name */
  if (name && name[0]) {
    *namePtr++ = '/';
    tmpPtr = name;
    while ((*namePtr = *tmpPtr++)) namePtr++;
  }
  if (type && type[0]) {
    *namePtr++ = '.';
    tmpPtr = type;
    while ((*namePtr = *tmpPtr++)) namePtr++;
  }
  *namePtr++ = '\0';

  return (arName);
}

/*================================================================
 * Build Object Cache File Name
 *================================================================*/

EXPORT char *afCacheFileName (archivePath, name)
     char *archivePath, *name;
{
  static char cName[PATH_MAX];
  char *namePtr = cName, *tmpPtr;

  tmpPtr = archivePath;
  while ((*namePtr = *tmpPtr++)) namePtr++;
  *namePtr++ = '/';
  tmpPtr = name;
  while ((*namePtr = *tmpPtr++)) namePtr++;
  *namePtr++ = '\0';

  return (cName);
}

/*====================================================================
 * afCacheUniqueName -- get unique filename for file in object cache
 *====================================================================*/

#ifdef __STDC__
EXPORT char *afCacheUniqueName (char *unixName, char uniqChar)
#else
EXPORT char *afCacheUniqueName (unixName, uniqChar)
     char *unixName, uniqChar;
#endif
{
  char uniqName[NAME_MAX+1];

#if (NAME_MAX < 128)
  char nameFragment[NAME_MAX-4];
  int nameLen = strlen (unixName);
  
  if (nameLen > NAME_MAX-5) {
    strcpy (nameFragment, &unixName[nameLen-(NAME_MAX-5)]);
    sprintf (uniqName, "%s%s%c", AF_CACHEFILEID, nameFragment, uniqChar);
  }
  else
    sprintf (uniqName, "%s%s%c", AF_CACHEFILEID, unixName, uniqChar);
#else
  sprintf (uniqName, "%s%s%c", AF_CACHEFILEID, unixName, uniqChar);
#endif

  return (af_entersym (uniqName));
}

/*================================================================
 *	af_gtmpname
 *================================================================*/

static int count=0;

EXPORT char *af_gtmpname (pathname)
     char *pathname;
{
  char tmpname[PATH_MAX];

  if (pathname && pathname[0])
    sprintf (tmpname, "%s/tmp%d%d", pathname, getpid(), count++);
  else
    sprintf (tmpname, "%s/atfs%d%d", AF_TMPDIR, getpid(), count++);
  return (af_entersym (tmpname));
} /* af_gtmpname */


/*================================================================
 *	af_gbusname
 *================================================================*/

EXPORT char *af_gbusname (pathname, name, type)
     char *pathname;
     char *name, *type;
{
  char busyname[PATH_MAX];

  if (!pathname || !(*pathname))
    strcpy (busyname, name);
  else if (!strcmp (pathname, "/"))
    sprintf (busyname, "/%s", name);
  else
    sprintf (busyname, "%s/%s", pathname, name);
  if (type && type[0]) {
    strcat (busyname, ".");
    strcat (busyname, type);
  }
  return (af_entersym (busyname));
} /* af_gbusname */ 


/*================================================================
 *	af_afpath
 *================================================================*/

EXPORT char *af_afpath (unixName)
     char *unixName;
{
  register char *namePtr, *tmpPtr;
  static   char afPath[PATH_MAX];

  if (unixName)
    strcpy (afPath, unixName);
  else {
    afPath[0] = '\0';
    return (afPath);
  }

  /* cut off version binding */
  if (afPath[strlen(afPath)-1] == ']')
    if ((tmpPtr = strrchr (afPath, '[')))
      *tmpPtr = '\0';

  /* cut name */
  if ((namePtr = strrchr (afPath, '/'))) {
    if (namePtr == afPath) /* e.g. "/tmp" */
      afPath[1] = '\0';
    else
      namePtr[0] = '\0';
  }
  else {
    afPath[0] = '\0';
    return (afPath);
  }

  /* cut AtFS subdirectory name if present (eg. "AtFS" or "AtFS/Attr"*/
  if ((namePtr = strrchr (afPath, '/'))) {
    if (!strcmp (AF_SUBDIR, namePtr+1))
      namePtr[0] = '\0';
    else {
      if ((namePtr > afPath) && (tmpPtr = strrchr (namePtr-1, '/'))) {
	if (!strcmp (AF_SUBDIR, tmpPtr+1) && (!strcmp (AF_ATTRDIR, namePtr+1)
	    || !strcmp (AF_DATADIR, namePtr+1)))
	  tmpPtr[0] = '\0';
      }
    }
  }
  else
    if (!strcmp (AF_SUBDIR, afPath))
      afPath[0] = '\0';

  return (afPath);
}

/*================================================================
 *	af_afname
 *================================================================*/

EXPORT char *af_afname (unixName)
     char *unixName;
{
  register char *typeptr, *namePtr;
  static   char afname[PATH_MAX];

  if (!unixName) {
    afname[0] = '\0';
    return (afname);
  }

  /* set namePtr to beginning of name */
  if ((namePtr = strrchr (unixName, '/')) == NULL)
    namePtr = unixName;
  else
    namePtr++;
  strcpy (afname, namePtr);

  /* special handling for "." and ".." */
  if (!strcmp (afname, ".") || !strcmp (afname, ".."))
    return (afname);

  /* if a UNIX type-extension is given -- cut it, except the dot is */
  /*                                      at position 0 (e.g. .cshrc) */
  if ((typeptr = strrchr (afname, '.')))
    if ((typeptr != afname) && typeptr[1])
      typeptr[0] = '\0';

  return (afname);
}

/*================================================================
 *	af_aftype
 *================================================================*/

EXPORT char *af_aftype (unixName)
     char *unixName;
{
  register char *typeptr, *namePtr;
  static   char aftype[TYPE_MAX];
  register bool isarch = FALSE;

  if (!unixName) {
    aftype[0] = '\0';
    return (aftype);
  }

  /* set namePtr to beginning of name */
  if ((namePtr = strrchr (unixName, '/')) == NULL)
    namePtr = unixName;
  else
    namePtr++;

  /* if there is no UNIX type-extension */
  if ((typeptr = strrchr (namePtr, '.')) == NULL)
    aftype[0] = '\0';
  else {
    /* if the found dot indicates a "hidden file" (eg. .cshrc) */
    if (typeptr == namePtr)
      aftype[0] = '\0';
    else {
      strcpy (aftype, typeptr + sizeof(char));
      /* if the named file is an archive, cut the name-extension */
      if (isarch)
	aftype [strlen (aftype) - sizeof (char)] = '\0';
    }
  }
  return (aftype);
}
