/*_ filespec.c   Mon Jul  3 1989   Modified by: Walter Bright */
/* Copyright (C) 1986-1987 by Northwest Software        */
/* All Rights Reserved                                  */
/* Written by Walter Bright                             */

/* Package with which to manipulate filespecs   */

#include        <stdio.h>
#include        "filespec.h"

#ifndef MEM_H
#include        "mem.h"
#endif

#ifndef VAX11C
#include <string.h>
#endif

#if BSDUNIX
#include        <pwd.h>
#endif

#if MSDOS || __OS2__ || __NT__ || _WIN32
#include        <direct.h>
#include        <ctype.h>
#endif

#if M_UNIX || M_XENIX || __linux__ || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun
#include        <stdlib.h>
#include        <unistd.h>
#endif

#ifndef assert
#include        <assert.h>
#endif

/* Macro to determine if char is a path or drive delimiter      */
#if MSDOS || __OS2__ || __NT__ || _WIN32
#define ispathdelim(c)  ((c) == '\\' || (c) == ':' || (c) == '/')
#else
#ifdef VMS
#define ispathdelim(c)  ((c)==':' || (c)=='[' || (c)==']' )
#else
#ifdef MPW
#define ispathdelim(c)  ((c) == ':')
#else
#define ispathdelim(c)  ((c) == '/')
#endif /* MPW */
#endif /* VMS */
#endif

/**********************/

char * filespecaddpath(const char *path,const char *filename)
{   char *filespec;
    size_t pathlen;

    if (!path || (pathlen = strlen(path)) == 0)
        filespec = mem_strdup(filename);
    else
    {
        filespec = (char *) mem_malloc(pathlen + 1 + strlen(filename) + 1);
        if (filespec)
        {   strcpy(filespec,path);
#if MSDOS || __OS2__ || __NT__ || _WIN32
            if (!ispathdelim(filespec[pathlen - 1]))
                strcat(filespec,"\\");
#else
#if VMS
#else
#if MPW
            if (!ispathdelim(filespec[pathlen - 1]))
                strcat(filespec,":");
#else
            if (!ispathdelim(filespec[pathlen - 1]))
                strcat(filespec,"/");
#endif /* MPW   */
#endif /* VMS   */
#endif
            strcat(filespec,filename);
        }
    }
    return filespec;
}

#ifndef MPW
/**********************/
char * filespecrootpath(char *filespec)
{
#if SUN || M_UNIX || M_XENIX || __linux__ || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun
#define DIRCHAR '/'
#endif
#if MSDOS || __OS2__ || __NT__ || _WIN32
#define DIRCHAR '\\'
#endif
#ifdef MPW
#define DIRCHAR ':'
#endif

    char *cwd, *cwd_t;
    char *p, *p2;

    if (!filespec)
        return filespec;
#if MSDOS || __OS2__ || __NT__ || _WIN32
    /* if already absolute (with \ or drive:) ... */
    if (*filespec == DIRCHAR || (isalpha((unsigned char)*filespec) && *(filespec+1) == ':'))
        return filespec;        /*      ... return input string */
#else
    if (*filespec == DIRCHAR)   /* already absolute ... */
        return filespec;        /*      ... return input string */
#endif

    /* get current working directory path */
#if SUN || M_UNIX || M_XENIX || __linux__ || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun
    cwd_t = (char *)getcwd(NULL, 256);
#endif
#if MSDOS || __OS2__ || __NT__ || _WIN32
    char cwd_d[132];
    if (getcwd(cwd_d, sizeof(cwd_d)))
       cwd_t = cwd_d;
    else
       cwd_t = NULL;
#endif

    if (cwd_t == NULL)
    {
        mem_free(filespec);
        return NULL;    /* error - path too long (more than 256 chars !)*/
    }
    cwd = mem_strdup(cwd_t);    /* convert cwd to mem package */
#if MSDOS
    assert(strlen(cwd) > 0);
    if (cwd[strlen(cwd) - 1] == DIRCHAR)
        cwd[strlen(cwd) - 1] = '\0';
#endif
#if SUN || M_UNIX || M_XENIX || __linux__ || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun
    free(cwd_t);
#endif
    p = filespec;
    while (p != NULL)
    {
        p2 = (char *)strchr(p, DIRCHAR);
        if (p2 != NULL)
        {
            *p2 = '\0';
            if (strcmp(p, "..") == 0)   /* move up cwd */
                /* remove last directory from cwd */
                *((char *)strrchr(cwd, DIRCHAR)) = '\0';
            else if (strcmp(p, ".") != 0) /* not current directory */
            {
                cwd_t = cwd;
                cwd = (char *)mem_calloc(strlen(cwd_t) + 1 + strlen(p) + 1);
#if SUN || M_UNIX || M_XENIX || __linux__ || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun
                sprintf(cwd, "%s/%s", cwd_t, p);  /* add relative directory */
#endif
#if MSDOS || __OS2__ || __NT__ || _WIN32
                sprintf(cwd, "%s\\%s", cwd_t, p);  /* add relative directory */
#endif
                mem_free(cwd_t);
            }
            /* else if ".", then ignore - it means current directory */
            *p2 = DIRCHAR;
            p2++;
        }
        else if (strcmp(p,"..") == 0)   /* move up cwd */
        {
            /* remove last directory from cwd */
            *((char *)strrchr(cwd, DIRCHAR)) = '\0';
        }
        else if (strcmp(p,".") != 0) /* no more subdirectories ... */
        {   /* ... save remaining string */
            cwd_t = cwd;
            cwd = (char *)mem_calloc(strlen(cwd_t) + 1 + strlen(p) + 1);
#if SUN || M_UNIX || M_XENIX || __linux__ || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun
            sprintf(cwd, "%s/%s", cwd_t, p);  /* add relative directory */
#endif
#if MSDOS || __OS2__ || __NT__ || _WIN32
            sprintf(cwd, "%s\\%s", cwd_t, p);  /* add relative directory */
#endif
            mem_free(cwd_t);
        }
        p = p2;
    }
    mem_free(filespec);

    return cwd;
#ifdef VMS
    assert(0);
#endif
}
#endif

/**********************/

char * filespecdotext(const char *filespec)
{   const char *p;
    size_t len;

    if ((p = filespec) != NULL)
    {   p += (len = strlen(p));
        while (1)
        {       if (*p == '.')
                    break;
                if (p <= filespec || ispathdelim(*p))
                {   p = filespec + len;
                    break;
                }
                p--;
        }
    }
    return (char *)p;
}

/**********************/

char * filespecname(const char *filespec)
{   register const char *p;

    /* Start at end of string and back up till we find the beginning    */
    /* of the filename or a path.                                       */
    for (p = filespec + strlen(filespec);
         p != filespec && !ispathdelim(*(p - 1));
         p--
        )
            ;
    return (char *)p;
}

/***********************/

char * filespecgetroot(const char *name)
{       char *root,*p,c;

        p = filespecdotext(name);
        c = *p;
        *p = 0;
        root = mem_strdup(name);
        *p = c;
        return root;
}

/*****************************/

char * filespecforceext(const char *filespec,const char *ext)
{   register char *p;
    register const char *pext;

    if (ext && *ext == '.')
        ext++;
    if ((p = (char *)filespec) != NULL)
    {   pext = filespecdotext(filespec);
        if (ext)
        {   size_t n = pext - filespec;
            p = (char *) mem_malloc(n + 1 + strlen(ext) + 1);
            if (p)
            {   memcpy(p,filespec,n);
                p[n] = '.';
                strcpy(&p[n + 1],ext);
            }
        }
        else
            p = mem_strdup(filespec);
    }
    return p;
}

/*****************************/

char * filespecdefaultext(const char *filespec,const char *ext)
{       register char *p;
        register const char *pext;

        pext = filespecdotext(filespec);
        if (*pext == '.')               /* if already got an extension  */
        {
            p = mem_strdup(filespec);
        }
        else
        {   size_t n = pext - filespec;
            p = (char *) mem_malloc(n + 1 + strlen(ext) + 1);
            if (p)
            {
                memcpy(p,filespec,n);
                p[n] = '.';
                strcpy(&p[n + 1],ext);
            }
        }
        return p;
}

/************************************/

#if !(MSDOS || __OS2__ || __NT__ || _WIN32)

char *filespectilde(char *filespec)
{
#if BSDUNIX
    struct passwd *passwdPtr;
    register char *f;

    if (filespec && *filespec == '~')
    {
        passwdPtr = NULL;
        if (filespec[1] == '/' || filespec[1] == 0)     /* if ~/... or ~ */
        {   f = filespec + 1;
            passwdPtr = getpwuid(getuid());
        }
        else                            /* else ~name   */
        {
            char c;

            f = strpbrk(filespec," /");
            if (!f)
                f = filespec + strlen(filespec); /* point at trailing 0 */
            c = *f;
            *f = 0;
            passwdPtr = getpwnam(filespec + 1);
            *f = c;
        }
        if (passwdPtr)
        {   char *p;

            p = (char *) mem_malloc(strlen(passwdPtr->pw_dir) + strlen(f) + 1);
            if (p)
            {
                strcpy(p,passwdPtr->pw_dir);
                strcat(p,f);
                mem_free(filespec);
                filespec = p;
            }
        }
    }
#endif
#if MSDOS || __OS2__ || __NT__ || _WIN32
#endif
#if VMS
    assert(0);
#endif
    return filespec;
}

/************************************/

char *filespecmultitilde(char *string)
{
    register char *p, *p2, *p3, *p4;

    string = filespectilde(string);     /* expand if first character is a '~' */

    if (string)
    {
       for (p = string; *p != '\0'; p++)
       {
           if (*p == '~')
           {
              *p = '\0';        /* terminate sub string */
              p2 = mem_strdup(string); /* get new sub string from old string */
              *p = '~'; /* reset ~ character */
              for (p3 = p + 1; *p3 != ' ' && *p3 != '\0'; p3++)
                  ; /* scan to next name, or end of the string */
              p4 = NULL;
              if (*p3 == ' ')
              {
                 p4 = mem_strdup(p3);  /* save reminder of the string */
                 *p3 = '\0';
              }
              p = mem_strdup(p);    /* get tilde string */
              mem_free(string);
              p = filespectilde(p);

              /* reconstruct the string from pieces */
              if (p4)
              {
                 string = (char *)
                       mem_calloc(strlen(p2) + strlen(p) + strlen(p4) + 1);
                 sprintf(string, "%s%s%s", p2, p, p4);
              }
              else
              {
                 string = (char *)
                       mem_calloc(strlen(p2) + strlen(p) + 1);
                 sprintf(string, "%s%s", p2, p);
              }
              mem_free(p);
              p = string + strlen(p2) + 2;
              mem_free(p2);
              if (p4)
                 mem_free(p4);
           }
       }
    }
    return string;
}

#endif

#ifndef MPW
/************************************/

char * filespecbackup(const char *filespec)
{
#if MSDOS || __OS2__ || __NT__ || _WIN32
    return filespecforceext(filespec,"BAK");
#endif
#if BSDUNIX || __linux__ || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun
    char *p,*f;

    /* Prepend .B to file name, if it isn't already there       */
    if (!filespec)
        return (char *)filespec;
    p = filespecname(filespec);
    if (p[0] == '.' && p[1] == 'B')
        return mem_strdup(filespec);
    f = (char *) mem_malloc(strlen(filespec) + 2 + 1);
    if (f)
    {   strcpy(f,filespec);
        strcpy(&f[p - filespec],".B");
        strcat(f,p);
    }
    return f;
#endif
}
#endif
