/* * This module handles emulation of Macintosh FSSpecs by maintaining * a list of referenced volumes and directories to handle the * conversion of full paths to FSSpecs and vice versa. * * This implementation uses a simple linear counter to generate vRefNums * and dirIDs. The directory structure as referenced by the running * program is stored as a tree so that for any FSSpec, a trace back * through the parID fields will step up this tree to the root. * * As on the Mac, the root of a volume has parID==2, and the root of the * filesystem has parID==1. */ #include #include #include #include #include #include #include "OSLib.h" #include "StringUtils.h" //#include "MacSpecs.h" #undef DEBUG #if !defined(MAC_FS) #ifdef DEBUG static unsigned long memused; #define MEMUSED(x) memused+=x #else #define MEMUSED(x) #endif //static int MacEmulInitialized=0; struct DirNode { char *name; /* name of directory part */ unsigned dirID; /* of this directory, as seen by apps */ union { struct DirNode *parent; /* of this directory */ struct VolNode *volume; /* of this root directory [dirID==2] */ } p; struct DirNode *list; /* list of contained directories */ struct DirNode *next; /* next directory in parent's list */ }; struct VolNode { unsigned short vRefNum; /* number */ struct DirNode root; /* of volume */ struct VolNode *next; /* next volume in list */ }; typedef struct DirNode DirNode; typedef struct VolNode VolNode; static VolNode *vols; /* The dirID is allocated as-they-come, no matter which volume is being referenced. */ static unsigned lastDirID = 3; /* last directory ID; starts at 3 */ static unsigned lastVolRef = 2; /* last volume ref; starts at 2 */ /* Support for 65536 directories */ #define MAXDIRMAPROWS 256 #define DIRMAPROWSIZE 256 /* 256 entries allocated at a time */ static DirNode **dirIDMap[MAXDIRMAPROWS]; /* map of maps of dirIDs to DirNodes */ static unsigned dirMapRows; /* # of allocated rows */ /* Add a reference for a new dirID. */ static int AddDirMapEntry(DirNode *node) { unsigned row = node->dirID / DIRMAPROWSIZE; unsigned col = node->dirID % DIRMAPROWSIZE; while (row >= dirMapRows) { if (row >= 256) { fprintf(stderr, "Fatal error: too many directories referenced, out of memory\n"); return 0; } dirIDMap[row] = (DirNode **) calloc(sizeof(DirNode *), DIRMAPROWSIZE); if (dirIDMap[row] == NULL) return 0; MEMUSED(sizeof(DirNode *) * DIRMAPROWSIZE); dirMapRows++; } dirIDMap[row][col] = node; return 1; } /* Add a volume node in 'list' if necessary; return new reference or found reference. */ static VolNode *FindOrAddVolRef(VolNode **list, const char *name) { int sz; VolNode *vn; while (*list != NULL) { if (OS_EqualPath(name, (*list)->root.name)) return *list; else list = &((*list)->next); } /* Cannot support more */ if (lastVolRef >= 256) return NULL; #ifdef DEBUG printf("FindOrAddVolRef: adding ref for '%s'\n", name); #endif vn = (VolNode *) malloc(sizeof(VolNode)); if (vn==NULL) return NULL; MEMUSED(sizeof(VolNode)); vn->next = NULL; vn->vRefNum = lastVolRef++; /* Initialize root */ sz = strlen(name)+1; vn->root.name = (char *) malloc(sz); if (vn->root.name==NULL) return NULL; MEMUSED(sz); strcpy(vn->root.name, name); vn->root.dirID = 2; vn->root.p.volume = vn; vn->root.list = NULL; vn->root.next = NULL; *list = vn; /* add to parent's list */ return vn; } /* Look up the DirNode for a given dirID */ static DirNode *FindDirMapEntry(unsigned dirID) { unsigned row = dirID / DIRMAPROWSIZE; unsigned col = dirID % DIRMAPROWSIZE; assert(dirID != 2); if (row >= dirMapRows) return NULL; /* illegal */ else return dirIDMap[row][col]; } /* Add a directory node in 'list' if necessary; return new reference or found reference. For a volume, do the same, except use a different parID. */ static DirNode *FindOrAddDirRef(DirNode *parent, const char *name) { int sz; DirNode *dn,**list; list = &parent->list; while (*list != NULL) { if (OS_EqualPath(name, (*list)->name)) return *list; else list = &((*list)->next); } /* Cannot support more */ if (lastDirID >= 65536) return NULL; #ifdef DEBUG printf("FindOrAddDirRef: adding ref for '%s' [parent '%s']\n", name, parent==NULL ? "" : parent->name); #endif /* Make node */ sz = strlen(name)+1; dn = (DirNode *) malloc(sizeof(DirNode)); if (dn==NULL) return NULL; MEMUSED(sizeof(DirNode)); dn->name = (char *) malloc(sz); if (dn->name==NULL) return NULL; MEMUSED(sz); strcpy(dn->name, name); dn->list = NULL; dn->next = NULL; dn->dirID = lastDirID++; if (!AddDirMapEntry(dn)) return 0; dn->p.parent = parent; *list = dn; /* add to parent's list */ return dn; } static int FindOrAdd(const OSPathSpec *path, unsigned *vRefNum, unsigned *dirID) { VolNode *vol; DirNode *level; char pb[OS_PATHSIZE],voln[OS_VOLSIZE]; char pathbuf[OS_PATHSIZE]; char *ptr; #if defined(WIN32_FS) || defined(POSIX_FS) strncpy(pathbuf, path->s, OS_PATHSIZE-1); pathbuf[OS_PATHSIZE-1] = 0; #else if (OS_PathSpecToString(path, pathbuf, OS_PATHSIZE) == NULL) return 0; #endif ptr = (char *)OS_GetDirPtr(pathbuf); strncpy(voln, pathbuf, ptr-pathbuf); voln[ptr-pathbuf] = 0; strcpy(pb, ptr); vol = FindOrAddVolRef(&vols, voln); if (vol==NULL) return 0; *vRefNum = vol->vRefNum; level = &vol->root; /* We expect the path to be a full path, which on all current non-Mac systems start with OS_PATHSEP. We can't add a blank directory as the root, since, given a blank volume, we may have "" + OS_PATHSEP + "" + OS_PATHSEP + "dir" which would be a network reference on some systems. */ ptr=pb+1; assert(*pb == OS_PATHSEP); while (*ptr) { const char *st; st=ptr; while (*ptr && *ptr != OS_PATHSEP) ptr++; *ptr=0; level = FindOrAddDirRef(level, st); ptr++; if (level==NULL) return 0; } *dirID = level->dirID; return 1; } /* Look up a vRefNum/dirID pair. */ static void StepVolDir(unsigned *vRefNum, unsigned *dirID, DirNode **node) { if (*vRefNum == 1) { *node = NULL; } else { if (*dirID == 2) { VolNode *step; step = vols; while (step && step->vRefNum != *vRefNum) step = step->next; if (step != NULL) { *node = &step->root; *vRefNum = 1; *dirID = 1; } else *node = NULL; } else { *node = FindDirMapEntry(*dirID); if (*node) *dirID = (*node)->p.parent->dirID; } } } /* Resolve a path to a vRefNum/dirID. */ static int ResolvePath(const OSPathSpec *path, unsigned *vRefNum, unsigned *dirID) { // meaningless #if 0 OSSpec full; /* See if the path exists */ full.path = *path; OS_MakeNameSpec("", &full.name); if (OS_Status(&full) != OS_NOERR) return 0; #endif if (FindOrAdd(path, vRefNum, dirID)) { *vRefNum = - (*vRefNum); #ifdef DEBUG printf("ResolvePath: Found ref for '%s' (%d,%d)\n", OS_PathSpecToString(path,NULL), *vRefNum, *dirID); #endif return 1; } else { *vRefNum = 0; *dirID = 0; #ifdef DEBUG printf("ResolvePath: could not find '%s' or add it!\n", OS_PathSpecToString(path,NULL)); #endif return 0; } } /* Resolve vRefNum/dirID to a vol and dir string */ static int ResolveVolDir(unsigned vRefNum, unsigned dirID, char *vol, char *dir) { DirNode *nodes[256]; DirNode *cur; int idx; char *dptr; vRefNum = -vRefNum; idx=0; do { StepVolDir(&vRefNum, &dirID, &cur); if (cur!=NULL) nodes[idx++] = cur; } while (cur!=NULL); if (idx) strcpy(vol, nodes[--idx]->name); else { *vol = 0; // totally invalid! *dir = 0; return 0; } dptr=dir; *dptr++ = OS_PATHSEP; while (idx--) { strcpy(dptr, nodes[idx]->name); dptr+=strlen(dptr); *dptr++ = OS_PATHSEP; } *dptr=0; return 1; } /*********************************************************************/ #if 0 #pragma mark - #endif static char stvol[OS_VOLSIZE], stdir[OS_PATHSIZE]; static char stpath[OS_PATHSIZE]; static char stname[(OS_NAMESIZE < 256 ? 256 : OS_NAMESIZE)]; /* Translate an OSPathSpec into the volume and directory for an FSSpec. */ _DLL _PAS OSError OS_OSPathSpec_To_VolDir(const OSPathSpec *spec, short *vRefNum, long *dirID) { unsigned vol, dir; if (ResolvePath(spec, &vol, &dir)) { *vRefNum = vol; *dirID = dir; return OS_NOERR; } else { *vRefNum = 0; *dirID = 0; return OS_DNFERR; } } _DLL _PAS OSError OS_OSSpec_To_FSSpec(const OSSpec *spec, FSSpec *fss) { OSError err; short vRefNum; long dirID; int len; err = OS_OSPathSpec_To_VolDir(&spec->path, &vRefNum, &dirID); fss->vRefNum = vRefNum; fss->parID = dirID; if (err != OS_NOERR) return err; // optimize #if defined(POSIX_FS) || defined(WIN32_FS) len = strlen(spec->name.s); if (len >= sizeof(fss->name)) return OS_FNTLERR; c2pstrcpy(fss->name, spec->name.s); #else OS_NameSpecToString2(&spec->name, stname); /* Enforce Mac-style limits */ if (strlen(stname) > sizeof(fss->name)) return OS_FNTLERR; c2pstrcpy(fss->name, stname); #endif return OS_NOERR; } /* Translate the volume and directory from an FSSpec into an OSNameSpec. (same as OS_VolDir_To_OSPathSpec but only goes one level) */ _DLL _PAS OSError OS_VolDir_To_OSNameSpec(short vRefNum, long dirID, OSNameSpec *spec, long *parID) { unsigned int cv,cd; DirNode *node; cv = -vRefNum; cd = dirID; StepVolDir(&cv, &cd, &node); if (node == NULL) return OS_DNFERR; else { *parID = cd; #if defined(POSIX_FS) || defined(WIN32_FS) strcpy(spec->s, node->name); return OS_NOERR; #else return OS_MakeNameSpec(node->name, spec); #endif } } /* Translate the volume and directory from an FSSpec into an OSPathSpec. */ _DLL _PAS OSError OS_VolDir_To_OSPathSpec(short vRefNum, long dirID, OSPathSpec *spec) { OSError err; /* CWD-local path? */ if (vRefNum==0 || dirID==0) { if ((err = OS_GetCWD(spec)) != OS_NOERR) return err; } else { if (!ResolveVolDir(vRefNum, dirID, stvol, stdir)) return OS_DNFERR; #if defined(POSIX_FS) || defined(WIN32_FS) { int vlen = strlen(stvol); int dlen = strlen(stdir); if (vlen + dlen < OS_PATHSIZE) { memcpy(spec->s, stvol, vlen); memcpy(spec->s + vlen, stdir, dlen + 1); } } #else if ((err = OS_MakePathSpec(stvol, stdir, spec)) != OS_NOERR) return err; #endif } return OS_NOERR; } /* Translate an FSSpec into an OSSpec. */ _DLL _PAS OSError OS_FSSpec_To_OSSpec(const FSSpec *fss, OSSpec *spec) { OSError err; err = OS_VolDir_To_OSPathSpec(fss->vRefNum, fss->parID, &spec->path); if (err != OS_NOERR) return err; #if (defined(POSIX_FS) || defined(WIN32_FS)) && (OS_NAMESIZE >= 64) p2cstrcpy(spec->name.s, (ConstStr255Param)fss->name); return OS_NOERR; #else p2cstrcpy(stname, (ConstStr255Param)fss->name); return OS_MakeNameSpec(stname, &spec->name); #endif } #if defined(WIN32_FS) extern __stdcall __declspec(dllimport) unsigned char SetFileAttributesA(const char *, long); #define FILE_ATTRIBUTES_HIDDEN 2 #endif #if defined(WIN32_FS) #define FORK_DIR "RESOURCE.FRK" #else #warning "Unix still uses resource.frk, although Latitude uses AppleDouble" #define FORK_DIR "resource.frk" #endif _DLL _PAS OSError OS_GetRsrcOSSpec(const OSSpec *spec, OSSpec *rspec, bool create) { char dbuffer[OS_PATHSIZE]; OSError err; /* Make directory */ OS_PathSpecToString2(&spec->path, dbuffer); if ((err = OS_MakeSpec2(dbuffer, FORK_DIR, rspec)) != OS_NOERR) return err; if (OS_Status(rspec) != OS_NOERR) { /* not found: rspec->name is FORK_DIR */ if (create) { //DPRINT(("creating directory %s\n", OS_SpecToString1(rspec))); if ((err = OS_Mkdir(rspec)) != OS_NOERR) /* can't make dir */ return err; #if defined(WIN32_FS) SetFileAttributesA(OS_SpecToString1(rspec), FILE_ATTRIBUTE_HIDDEN); #endif } else return err; /* can't do anything else */ /* fixup path spec */ if ((err = OS_MakeSpec2(dbuffer, FORK_DIR, rspec)) != OS_NOERR) return err; } else { if (OS_IsFile(rspec)) /* resource.frk is a file */ return OS_FNIDERR; } if ((err = OS_MakeNameSpec(OS_NameSpecToString1(&spec->name), &rspec->name)) != OS_NOERR) return err; //DPRINT(("Filename is %s\n", OS_SpecToString1(rspec))); return OS_NOERR; } ////////////////////////// #else // defined(MAC_FS) ////////////////////////// #warning This module is redundant! //void OS_InitMacEmul(void) { } //void OS_StopMacEmul(void) { } _DLL _PAS OSError OS_OSPathSpec_To_VolDir(const OSPathSpec *spec, short *vRefNum, long *dirID) { *vRefNum = spec->vRefNum; *parID = spec->dirID; return OS_NOERR; } _DLL _PAS OSError OS_OSSpec_To_FSSpec(const OSSpec *spec, FSSpec *fss) { pstrcpy(fss->name,spec->name.name); return OS_NOERR; } _DLL _PAS OSError OS_VolDir_To_OSPathSpec(short vRefNum, long dirID, OSPathSpec *spec) { spec->vRefNum = vRefNum; spec->dirID = parID; return OS_NOERR; } _DLL _PAS OSError OS_VolDir_To_OSNameSpec(short vRefNum, long dirID, OSNameSpec *spec, long *parID) { #error } _DLL _PAS OSError OS_FSSpec_To_OSSpec(const FSSpec *fss, OSSpec *spec) { pstrcpy(spec->name.name, fss->name); return OS_NOERR; } _DLL _PAS OSError OS_GetRsrcOSSpec(OSSpec *spec, OSSpec *rspec, Boolean create) { *rspec = *spec; return OS_NOERR; } #endif // defined(MAC_FS) ///////////////////////////////////////