This is FaceFileCache.m in view mode; [Download] [Up]
#import "FaceFileCache.h" #import <sys/param.h> #import <sys/dir.h> #import <stdlib.h> #import <stdio.h> #import <string.h> #import <streams/streams.h> #import <appkit/NXCType.h> #import <appkit/Application.h> @interface FaceFileCache (Private) - (void)_freeDirectory; - (void)_freeHashTable; - (const char *)_entryForLogin:(const char *)login; - (BOOL)_loadDirectory; - (BOOL)_loadAliases; @end @implementation FaceFileCache - initWithDirectory:(const char *)aDirectory { if ((self = [super init])) { [self setDirectory:aDirectory]; } return self; } - free { [self _freeDirectory]; [self _freeHashTable]; return [super free]; } - (const char *)directory { return directory; } - (BOOL)setDirectory:(const char *)aDirectory { [self _freeDirectory]; [self _freeHashTable]; if (aDirectory) { if (aDirectory[0] == '~' && aDirectory[1] == '/') { const char *home = NXHomeDirectory(); directory = NXZoneMalloc([self zone], strlen(home)+strlen(aDirectory)-1+1); strcpy(directory, home); strcat(directory, aDirectory+1); } else { directory = NXCopyStringBufferFromZone(aDirectory, [self zone]); } } return [self shouldLoad]; } - (const char *)filenameForLogin:(const char *)login { const char *entry; if ([self shouldLoad]) [self load]; /* first try full login name; if that fails, try `@domain' part only */ if (login && [hashTable count] > 0 && ((entry = [self _entryForLogin:login]) || (entry = [self _entryForLogin:strchr(login, '@')]))) { char buf[MAXPATHLEN+1]; sprintf(buf, "%s/%s", directory, entry); return NXCopyStringBuffer(buf); } return NULL; } #define STAT_EQ(s1, s2) ((s1)->st_ino == (s2)->st_ino && \ (s1)->st_dev == (s2)->st_dev && \ (s1)->st_mtime == (s2)->st_mtime && \ (s1)->st_size == (s2)->st_size) - (BOOL)shouldLoad { struct stat st; if (directory == NULL) return NO; if (stat(directory, &st) < 0) return NO; if (hashTable == nil) return YES; if (STAT_EQ(&st, &dirstat)) return NO; memcpy(&dirstat, &st, sizeof(dirstat)); return YES; } - (BOOL)load { BOOL result; [self _freeHashTable]; if (directory == NULL) return NO; hashTable = [[HashTable allocFromZone:[self zone]] initKeyDesc:"*" valueDesc:"*"]; result = [self _loadDirectory]; result |= [self _loadAliases]; return result; } @end // FaceFileCache @implementation FaceFileCache (Private) - (void)_freeDirectory { if (directory) NXZoneFree([self zone], directory), directory = NULL; memset(&dirstat, 0, sizeof(dirstat)); } - (void)_freeHashTable { [hashTable freeKeys:free values:free]; hashTable = [hashTable free]; } - (const char *)_entryForLogin:(const char *)login { // Lookup login name in hash table. const char *result = NULL; if (login && (result = [hashTable valueForKey:login]) == NULL) { /* shorten login name by repeatedly removing first element of domain name, and try again... */ char buf[MAXPATHLEN+1]; char *at, *dot; strcpy(buf, login); if ((at = strchr(buf, '@')) != NULL) { while ((dot = strchr(at + 1, '.')) != NULL) { strcpy(at + 1, dot + 1); result = [hashTable valueForKey:buf]; if (result != NULL) break; } } } return result; } - (void)_loginFromFilename:(char *)buf { char *s = buf; // convert to lower case. while (*s) { *s = NXToLower(*s); ++s; } // Backward compatibility: convert aap.noot.mies.org -> aap@noot.mies.org if (strchr(buf, '@') == NULL) { char *dot; // Heuristic: skip W.E. in W.E.Coyote.death.valley.org while ((dot = strchr(buf, '.')) != NULL && dot <= buf + 1) { buf = dot + 1; } if (dot) *dot = '@'; } } static const char _tiff[] = ".tiff"; - (BOOL)_loadDirectory { DIR *dir = opendir(directory); struct direct *d; if (dir == NULL) return NO; while ((d = readdir(dir)) != NULL) { char *key, *value; if (d->d_namlen <= sizeof(_tiff) -1 || strcmp(d->d_name + d->d_namlen - (sizeof(_tiff) -1), _tiff) != 0) continue; value = NXCopyStringBufferFromZone(d->d_name, [self zone]); key = NXZoneMalloc([self zone], d->d_namlen - (sizeof(_tiff) -1) + 1); memcpy(key, d->d_name, d->d_namlen - (sizeof(_tiff) -1)); key[d->d_namlen - (sizeof(_tiff) -1)] = '\0'; [self _loginFromFilename:key]; /* do not overwrite existing entry. (alternative: do overwrite but that is somewhat harder to implement without memory leaks so I am taking the easy way out:-) */ if ([hashTable isKey:key]) { // Oh brother this is painful... NXZoneFree([self zone], key); NXZoneFree([self zone], value); continue; } [hashTable insertKey:key value:value]; } closedir(dir); return YES; } - (BOOL)_loadAliases { char filename[MAXPATHLEN+1]; NXStream *aliases; char *ap, *end, *nl, *colon; char *key, *value; int len, maxlen; sprintf(filename, "%s/%s", directory, "aliases"); aliases = NXMapFile(filename, NX_READONLY); if (aliases == NULL) return NO; NXGetMemoryBuffer(aliases, &ap, &len, &maxlen); for (end = ap + len; (ap < end); ap = nl + 1) { nl = strchr(ap, '\n'); if (nl == NULL) nl = end; if (*ap == '#') continue; colon = strchr(ap, ':'); if (colon == NULL) continue; key = NXZoneMalloc([self zone], colon - ap + 1); memcpy(key, ap, colon - ap); key[colon - ap] = '\0'; // Skip spaces beyond ':'. while (*++colon == ' ') ; if (colon == nl) { NXZoneFree([self zone], key); continue; } value = NXZoneMalloc([self zone], nl - colon + sizeof(_tiff)); memcpy(value, colon, nl - colon); strcpy(&value[nl - colon], _tiff); if ([hashTable isKey:key]) { NXZoneFree([self zone], key); NXZoneFree([self zone], value); continue; } [hashTable insertKey:key value:value]; } NXCloseMemory(aliases, NX_FREEBUFFER); return YES; } @end // FaceFileCache (Private) #ifdef TESTING int main() { FaceFileCache *cache = [[FaceFileCache alloc] initWithDirectory:"/LocalLibrary/Images/People"]; const char *result = [cache filenameForLogin:"tom@basil.icce.rug.nl"]; printf("%s\n", result); return 0; } #endif
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.