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.