/*
*   $Id: mac.c 443 2006-05-30 04:37:13Z darren $
*
*   Copyright (c) 2001, Maarten L. Hekkelman
*
*   Author: Maarten L. Hekkelman <maarten@hekkelman.com>
*           http://www.hekkelman.com
*
*   This source code is released for free distribution under the terms of the
*   GNU General Public License. It is provided on an as-is basis and no
*   responsibility is accepted for its failure to perform as expected.
*
*   This module contains support functions for Exuberant Ctags on Macintosh.
*/

/*
*   INCLUDE FILES
*/
#include "general.h"

#include <Files.h>
#include <TextUtils.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

/*
*   FUNCTION DEFINITIONS
*/

static int get_path(const char* in_unix_path, unsigned char* out_mac_path)
{
    int l = strlen(in_unix_path);
    int result = 0;
    
    if (l > 254)
        result = -1;
    else
    {
        const char* s = in_unix_path;
        char *d = (char*)out_mac_path + 1;
        
        if (*s != '/')
            *d++ = ':';
        else
            ++s;
        
        while (*s)
        {
            if (s[0] == '.' && s[1] == '.' && s[2] == '/')
            {
                s += 3;
                *d++ = ':';
            }
            else if (s[0] == '.' && s[1] == '/')
                s += 2;
            else if (s[0] == '/')
            {
                *d++ = ':';
                
                ++s;
                while (*s == '/')
                    ++s;
            }
            else
                *d++ = *s++;
        }

        out_mac_path[0] = (d - (char*)out_mac_path) - 1;
    }
    
    return result;
}

DIR *opendir(const char *dirname)
{
    DIR* dirp = (DIR*)calloc(1, sizeof(DIR));

    if (dirp != NULL)
    {
        OSErr err;
        Str255 s;
        CInfoPBRec pb = { 0 };
        
        if (strcmp(dirname, "."))
        {
            get_path(dirname, s);
            pb.hFileInfo.ioNamePtr = s;
        }
        else
            pb.hFileInfo.ioNamePtr = NULL;
        
        err = PBGetCatInfoSync(&pb);
        if (err != noErr || (pb.hFileInfo.ioFlAttrib & ioDirMask) == 0)
        {
            free(dirp);
            dirp = NULL;
        }
        else
        {
            dirp->file.vRefNum = pb.hFileInfo.ioVRefNum;
            dirp->file.parID = pb.hFileInfo.ioDirID;
            dirp->file.name[0] = '\0';
            dirp->index = 1;
        }
    }
    
    return dirp;
}

struct dirent *readdir(DIR *dirp)
{
    if (dirp)
    {
        CInfoPBRec pb = { 0 };
        
        pb.hFileInfo.ioVRefNum = dirp->file.vRefNum;
        pb.hFileInfo.ioDirID = dirp->file.parID;
        pb.hFileInfo.ioFDirIndex = dirp->index++;
        pb.hFileInfo.ioNamePtr = dirp->file.name;
    
        if (PBGetCatInfoSync(&pb) != noErr)
            return NULL;
        
        memcpy(dirp->ent.d_name, dirp->file.name + 1, dirp->file.name[0]);
        dirp->ent.d_name[dirp->file.name[0]] = 0;
        return &dirp->ent;
    }
    return NULL;
}

int closedir(DIR *dirp)
{
    if (dirp)
        free(dirp);
    return 0;
}

void rewinddir(DIR *dirp)
{
    if (dirp)
        dirp->index = 1;
}

int mstat(const char* file, struct stat* st)
{
    CInfoPBRec      pb;
    unsigned char   path[256];
    int             result = 0;

    memset(&pb, 0, sizeof(CInfoPBRec));

    if (strcmp(file, ".") == 0)
    {
        memset(st, 0, sizeof(struct stat));
        st->st_mode = S_IFDIR;
        st->st_ino = -1;
    }
    else
    {
        result = get_path(file, path);
        
        if (result == 0)
        {
            pb.hFileInfo.ioNamePtr = path;
            
            if (PBGetCatInfoSync(&pb) != noErr)
                result = -1;
            else
            {
                memset(st, 0, sizeof(struct stat));
    
                if (pb.hFileInfo.ioFlAttrib & ioDirMask)
                    st->st_mode = S_IFDIR;
                else
                    st->st_mode = S_IFREG;

                st->st_ino = pb.hFileInfo.ioFlStBlk;
                st->st_dev = pb.hFileInfo.ioVRefNum;
                st->st_nlink = 1;
                st->st_size = pb.hFileInfo.ioFlLgLen;
                st->st_atime = pb.hFileInfo.ioFlMdDat;
                st->st_mtime = pb.hFileInfo.ioFlMdDat;
                st->st_ctime = pb.hFileInfo.ioFlCrDat;
            }
        }
    }

    return result;
}

#undef fopen

FILE* mfopen(const char* file, const char* mode)
{
    unsigned char path[256];
        
    if (get_path(file, path) == 0)
    {
        int l = path[0];
        memmove(path, path + 1, l);
        path[l] = 0;
        return fopen((char*)path, mode);
    }
    else
        return NULL;
}

char* getcwd(char* out_path, int out_path_len)
{
    OSErr       err = noErr;
    CInfoPBRec  pb;
    FSSpec      cwd;

    if (out_path == NULL)
    {
        if (out_path_len < PATH_MAX)
            out_path_len = PATH_MAX;
        out_path = (char*)malloc(out_path_len);
    }
    
    err = FSMakeFSSpec(0, 0, "\p:", &cwd);
    
    if (cwd.parID == fsRtParID)
    {
        *out_path = '/';
        memcpy(out_path + 1, cwd.name + 1, cwd.name[0]);
        out_path[1 + cwd.name[0]] = 0;
    }
    else
    {
        /* The object isn't a volume */
        
        /* Is the object a file or a directory? */
        
        char t[PATH_MAX];
        char* s;

        s = t + PATH_MAX - cwd.name[0] - 1;
        memcpy(s, cwd.name + 1, cwd.name[0]);
        s[cwd.name[0]] = 0;
        
        /* Get the ancestor directory names */
        pb.dirInfo.ioNamePtr = cwd.name;
        pb.dirInfo.ioVRefNum = cwd.vRefNum;
        pb.dirInfo.ioDrParID = cwd.parID;
        do  /* loop until we have an error or find the root directory */
        {
            pb.dirInfo.ioFDirIndex = -1;
            pb.dirInfo.ioDrDirID = pb.dirInfo.ioDrParID;
            err = PBGetCatInfoSync(&pb);
            if ( err == noErr )
            {
                *--s = '/';
                s -= cwd.name[0];
                memcpy(s, cwd.name + 1, cwd.name[0]);
            }
        }
        while (err == noErr && pb.dirInfo.ioDrDirID != fsRtDirID && s > t + 1);

        if (s > t + 1)
        {
            *--s = '/';
            strcpy(out_path, s);
        }
        else
            strcpy(out_path, ".");
    }

    return out_path;
}

/* vi:set tabstop=4 shiftwidth=4: */
