The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
/*
 * $Id: Catalog.xs,v 0.70 2005/08/09 15:47:00 dankogai Exp $
 */

#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

static int
not_here(char *s)
{
    croak("%s not implemented on this architecture", s);
    return -1;
}

#include "common/util.c"
#include "common/macdate.c"
#include <Finder.h>

static SV *
xs_getcatalog(char *path){
    FSRef  Ref;
    FSCatalogInfo Catalog;
    FInfo  *fip = (FInfo *)&Catalog.finderInfo;
    FXInfo *fxp = (FXInfo *)&Catalog.extFinderInfo;
    SV*   ret[21];
    SV*   permissions[4];
    SV*   finderInfo[5];
    SV*   fdLocation[2];
    SV*   extFinderInfo[5];
    OSErr err;
    int i;

    if (err = FSPathMakeRef(path, &Ref, NULL)){
	seterr(err) ; return &PL_sv_undef;
    }

    if (err = FSGetCatalogInfo(&Ref,
			       kFSCatInfoGettableInfo,
			       &Catalog,
			       NULL, NULL, NULL))
    
    {
	seterr(err) ; return &PL_sv_undef;
    }

    ret[0] = sv_2mortal(newSVpv((char *)&Ref, sizeof(Ref)));
    ret[1] = sv_2mortal(newSVuv(Catalog.nodeFlags));
    ret[2] = sv_2mortal(newSViv(Catalog.volume));
    ret[3] = sv_2mortal(newSVuv(Catalog.parentDirID));
    ret[4] = sv_2mortal(newSVuv(Catalog.nodeID));
    ret[5] = sv_2mortal(newSVuv(Catalog.sharingFlags));
    ret[6] = sv_2mortal(newSVuv(Catalog.userPrivileges));

    /*
    ret[] = sv_2mortal(newSViv(Catalog.reserved1));
    ret[] = sv_2mortal(newSViv(Catalog.reserved2));
    */

    /* dates are converted to double */

    ret[7]  = sv_2mortal(newSVnv(UDT2D(&Catalog.createDate)));
    ret[8]  = sv_2mortal(newSVnv(UDT2D(&Catalog.contentModDate)));
    ret[9]  = sv_2mortal(newSVnv(UDT2D(&Catalog.attributeModDate)));
    ret[10] = sv_2mortal(newSVnv(UDT2D(&Catalog.accessDate)));
    ret[11] = sv_2mortal(newSVnv(UDT2D(&Catalog.backupDate)));

    /* permission is stored as arrayref */
    for (i = 0; i < 4; i++){
	permissions[i] = sv_2mortal(newSVuv(Catalog.permissions[i]));
    }
    ret[12] = sv_2mortal(newRV_noinc((SV*)av_make(4, permissions)));

    /* finder info too, is stored as arrayref */

    finderInfo[0] = sv_2mortal(newSVpv((char *)&fip->fdType, 4));
    finderInfo[1] = sv_2mortal(newSVpv((char *)&fip->fdCreator, 4));
    finderInfo[2] = sv_2mortal(newSVuv(fip->fdFlags));

    fdLocation[0] = sv_2mortal(newSViv(fip->fdLocation.v));
    fdLocation[1] = sv_2mortal(newSViv(fip->fdLocation.h));
    finderInfo[3] = sv_2mortal(newRV_noinc((SV*)av_make(2, fdLocation)));

    finderInfo[4] = sv_2mortal(newSViv(fip->fdFldr));
    ret[13] = sv_2mortal(newRV_noinc((SV*)av_make(5,finderInfo)));

    /* extra finder info is stored as arraryref */

    extFinderInfo[0] = sv_2mortal(newSViv(fxp->fdIconID));
    extFinderInfo[1] = sv_2mortal(newSViv(fxp->fdScript));
    extFinderInfo[2] = sv_2mortal(newSViv(fxp->fdXFlags));
    extFinderInfo[3] = sv_2mortal(newSViv(fxp->fdComment));
    extFinderInfo[4] = sv_2mortal(newSViv(fxp->fdPutAway));
    ret[14] = sv_2mortal(newRV_noinc((SV*)av_make(5, extFinderInfo)));

    /* size of forks are stored in IV */
    /* to store 64bit value, we use NV instead of IV */

    ret[15] = sv_2mortal(newSVnv(Catalog.dataLogicalSize));
    ret[16] = sv_2mortal(newSVnv(Catalog.dataPhysicalSize));
    ret[17] = sv_2mortal(newSVnv(Catalog.rsrcLogicalSize));
    ret[18] = sv_2mortal(newSVnv(Catalog.rsrcPhysicalSize));
    
    /* these are UInt32 */
    ret[19] = sv_2mortal(newSVuv(Catalog.valence));
    ret[20] = sv_2mortal(newSVuv(Catalog.textEncodingHint));

    /* now return the result */
    return newRV_noinc((SV *)(av_make(21, ret)));
 }

xs_setcatalog(SV* self, char *path){
    SV**   svh;
    FSRef  Ref, *rp;
    FSSpec Spec;
    FSCatalogInfo Catalog;
    FInfo  *fip = (FInfo *)&Catalog.finderInfo;
    FXInfo *fxp = (FXInfo *)&Catalog.extFinderInfo;
    AV*    arg = (AV *)SvRV(self);
    AV*    permissions;
    AV*    finderInfo;
    AV*    fdLocation;
    AV*    extFinderInfo;
    OSErr err;
    int i;

    if (path != NULL && strlen(path) != 0){
	if (err = FSPathMakeRef(path, &Ref, NULL)){
	    return seterr(err);
	}else{
	    rp = &Ref;
	}
    }else{
	if (svh = av_fetch(arg, 0, 0)) rp = (FSRef *)SvPV_nolen(*svh);
    }

    /* prefetch destination catalog; may be used for file locks */
    if (err = FSGetCatalogInfo(rp,
			       kFSCatInfoSettableInfo|kFSCatInfoNodeFlags,
			       &Catalog,
			       NULL, NULL, NULL))
    {
	return seterr(err);
    }

    /* 
     * unlock the file first.
     * Note FSp(Rst|Set)FLock dies with segfault when applied to
     * directories! 
     */

    FSRef2FSSpec(rp, &Spec);

    if (!(Catalog.nodeFlags & kFSNodeIsDirectoryMask)){
	if (err = FSpRstFLock(&Spec)){
	    return seterr(err);
	}
    }

    /* Now Let's set Catalog one by one */

    if (svh = av_fetch(arg, 1, 0)) Catalog.nodeFlags = SvUV(*svh);

    /* dates */
    if (svh = av_fetch(arg, 7, 0))
	D2UDT(SvNV(*svh), &Catalog.createDate);
    if (svh = av_fetch(arg, 8, 0))
	D2UDT(SvNV(*svh), &Catalog.contentModDate);
    if (svh = av_fetch(arg, 9, 0))
	D2UDT(SvNV(*svh),&Catalog.attributeModDate);
    if (svh = av_fetch(arg, 10, 0))
	D2UDT(SvNV(*svh), &Catalog.accessDate);
    if (svh = av_fetch(arg, 11, 0))
	D2UDT(SvNV(*svh), &Catalog.backupDate);

    /* permissions is stored as arrayref */
    if (svh = av_fetch(arg, 12, 0)){
	permissions = (AV *)SvRV(*svh);
	for (i = 0; i < 4; i++){
	    if (svh = av_fetch(permissions, i, 0))
		Catalog.permissions[i] = SvUV(*svh);
	}
    }

    /* finder info too, is stored as arrayref */

    if (svh = av_fetch(arg, 13, 0)){
	finderInfo = (AV *)SvRV(*svh);
	if (svh = av_fetch(finderInfo, 0, 0)) 
	    fip->fdType = char2OSType(SvPVX(*svh));
	if (svh = av_fetch(finderInfo, 1, 0))
	    fip->fdCreator = char2OSType(SvPVX(*svh));
	if (svh = av_fetch(finderInfo, 2, 0)) 
	    fip->fdFlags = SvUV(*svh);
	/* fdLocation */
	if (svh = av_fetch(finderInfo, 3, 0)){
	    fdLocation = (AV *)SvRV(*svh); 
	    if (svh = av_fetch(fdLocation, 0, 0))
		fip->fdLocation.v = SvIV(*svh);
	    if (svh = av_fetch(fdLocation, 1, 0))
		fip->fdLocation.h = SvIV(*svh);
	}
	/* and fdFldr */
	if (svh = av_fetch(finderInfo, 4, 0)) fip->fdFldr = SvIV(*svh);
    }

    /* extra finder info is stored as arraryref */
    if (svh = av_fetch(arg, 14, 0)){
	extFinderInfo = (AV *)SvRV(*svh);
	if (svh = av_fetch(extFinderInfo, 0, 0)) fxp->fdIconID = SvIV(*svh);
	if (svh = av_fetch(extFinderInfo, 1, 0)) fxp->fdScript = SvIV(*svh);
	if (svh = av_fetch(extFinderInfo, 2, 0)) fxp->fdXFlags = SvIV(*svh);
	if (svh = av_fetch(extFinderInfo, 3, 0)) fxp->fdComment = SvIV(*svh);
    }

    /* And at last, textEncodingHint */
    if (svh = av_fetch(arg, 20, 0)) Catalog.textEncodingHint = SvUV(*svh);

    /* now set it! */
    if (err = FSSetCatalogInfo(rp,
			       kFSCatInfoSettableInfo,
			       &Catalog))
    {
	return seterr(err);
    }
    
    /* Lock the File if neccesary */

    if (!(Catalog.nodeFlags & kFSNodeIsDirectoryMask)){
	if (Catalog.nodeFlags & kFSNodeLockedMask){
	    err = FSpSetFLock(&Spec);
	}
    }

    return seterr(err);
}

static char *
xs_fsref2path(SV *svref){
    static char path[1024];
    FSRef *rp = (FSRef *)SvPV_nolen(svref);
    FSRefMakePath(rp, path, 1024);
    return path;
}

MODULE = MacOSX::File::Catalog		PACKAGE = MacOSX::File::Catalog	

PROTOTYPES: ENABLE

SV *
xs_getcatalog(path)
    char *path;
    CODE:
        RETVAL = xs_getcatalog(path);
    OUTPUT:
	RETVAL

SV *
xs_setcatalog(self, path)
    SV*   self;
    char* path;
    CODE:
        RETVAL = xs_setcatalog(self, path);
    OUTPUT:
	RETVAL

char *
xs_fsref2path(svref)
    SV *svref;
    CODE:
	RETVAL = xs_fsref2path(svref);
    OUTPUT:
	RETVAL