This is ILocalFileD.m in view mode; [Download] [Up]
/*$Copyright:
* Copyright (C) 1992.5.22. Recruit Co.,Ltd.
* Institute for Supercomputing Research
* All rights reserved.
* NewsBase by ISR, Kazuto MIYAI, Gary ARAKAKI, Katsunori SUZUKI, Kok-meng Lue
*
* You may freely copy, distribute and reuse the code in this program under
* following conditions.
* - to include this notice in the source code, if it is to be distributed
* with source code.
* - to add the file named "COPYING" within the code, which shall include
* GNU GENERAL PUBLIC LICENSE(*).
* - to display an acknowledgement in binary code as follows: "This product
* includes software developed by Recruit Co.,Ltd., ISR."
* - to display a notice which shall state that the users may freely copy,
* distribute and reuse the code in this program under GNU GENERAL PUBLIC
* LICENSE(*)
* - to indicate the way to access the copy of GNU GENERAL PUBLIC LICENSE(*)
*
* (*)GNU GENERAL PUBLIC LICENSE is stored in the file named COPYING
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
$*/
// Provides support routines for external local disk files. This exists
// as a Class only there are no instances. Just a convienient way to have
// an object-oriented interface to local disk access.
// Eventually all input/ouput to the local disk should be handled by this
// Class. Now, some local disk I/O is embedded in some objects.
#import "ILocalFileD.h"
#import <c.h>
#import <libc.h>
#import <sys/types.h>
#import <sys/stat.h>
#import <sys/dir.h>
#import <ctype.h>
#import "IArticleD.h"
#import "InfoD.h"
#import "ITextD.h"
#import "IMediaTable.h"
#import "IMediaD.h"
#import "IBinaryD.h"
#import "data_types.h"
#import <mach.h>
#import <appkit/NXImage.h>
#import <appkit/Pasteboard.h>
#import <appkit/OpenPanel.h>
#import <appkit/SavePanel.h>
#import "IConvertMIME.h"
#import "Localization.h"
#import "errdebug.h"
@implementation ILocalFileD
static NXAtom domain;
static NXAtom fileExtension;
static const char *types[] = {NEWS_FILE_EXTENSION, MM_FILE_EXTENSION,
MIME_FILE_EXTENSION, NULL};
static char previousDirectory[MAXPATHLEN] = "";
+ initialize
{
domain = NXUniqueStringNoCopy("local-file");
fileExtension = NXUniqueStringNoCopy("local-file");
return(self);
}
+ (NXAtom)domain
{
return(domain);
}
+ (NXAtom)fileExtension
{
return(fileExtension);
}
+ (NXAtom)pasteboardType
{
return(NULL);
}
+ (BOOL)open
{
const char *rootDirectoryName;
OpenPanel *openPanel;
if ((rootDirectoryName = NXGetDefaultValue(OWNER, SAVEDIRECTORY)) == 0) {
rootDirectoryName = getenv("HOME");
}
openPanel = [OpenPanel new];
[openPanel setTitle:"Open NewsBase Article"];
switch([openPanel runModalForDirectory:rootDirectoryName file:NULL
types:types]) {
case 1:
[self openFromName:[openPanel filename]];
return(YES);
case 0:
return(NO);
}
return(YES);
}
+ (BOOL)openFromName:(const char *)filename
{
IExternalD *external;
DBG(1, ;);
external = [[IExternalD
allocFromZone:NXCreateZone(vm_page_size, vm_page_size, YES)]
initWithDomain:domain andPath:filename];
[external performDoubleClickAction2:self];
return(YES); // maybe a false positive return but can't be helped!
}
+ (IArticleD *)loadFromName:(const char *)path inZone:(NXZone *)zone
{
const char *extension;
InfoD *header;
ITextD *text;
DIR *dirp;
struct direct *dp;
IArticleD *article;
Class MediaClass;
IMediaD *mediaObject;
NXStream *stream;
IConvertMIME *conv;
char workingDirectoryName[MAXPATHLEN];
if ((extension = rindex(path, '.')) == 0) {
NXRunAlertPanel(LoStr("NewsBase"), LoStr("file has no extension: %s"),
NULL,NULL,NULL, path);
return(nil);
}
++extension;
DBG(1, fprintf(stderr, "%s", path);)
if (strcmp(extension, NEWS_FILE_EXTENSION) == 0) {
// plain text
if ((stream = NXMapFile(path, NX_READONLY)) == NULL) {
NXRunAlertPanel(LoStr("NewsBase"),
LoStr("file cannot be opened: %s"), NULL, NULL, NULL, path);
return(nil);
}
article = [[IArticleD allocFromZone:zone] init];
header = [[InfoD allocFromZone:zone] initWithKey:HEADER_INFO];
[header readFromStream:stream];
text = [[ITextD allocFromZone:zone] initWithKey:PLAINTEXT];
[text readFromStream:stream];
[article insertKeyedObject:(IKeyedObject *)header];
[article insertKeyedObject:text];
NXCloseMemory(stream, NX_FREEBUFFER);
return(article);
} else if (strcmp(extension, MM_FILE_EXTENSION) == 0) {
// multimedia
article = [[IArticleD allocFromZone:zone] init];
getwd(workingDirectoryName);
if (chdir(path) == -1) {
NXRunAlertPanel(LoStr("NewsBase"),"%s: %s",NULL,NULL,NULL
,strerror(errno), path);
return(nil);
}
if ((dirp = opendir(path)) == NULL) {
NXRunAlertPanel(LoStr("NewsBase"),"%s: %s",NULL,NULL,NULL
,strerror(errno), path);
return(nil);
}
for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
if (dp->d_name[0] == '.') {
continue;
}
if ((extension = rindex(dp->d_name, '.')) != 0) {
++extension;
}
// There are really two kinds of .rtf objects: index.rtf and
// regular .rtf objects. The media table gives the object for
// index.rtf. Regular .rtf objects can be handled as binary
// objects.
if (extension != 0 && (strcmp(extension, RTF_FILE_EXTENSION)
!= 0 || strcmp(dp->d_name, INDEX_RTF) == 0) &&
(MediaClass = [IMediaTable mediaClassForFileExtension:
extension]) != Nil) {
mediaObject = [[(id)MediaClass allocFromZone:zone]
initWithKey:dp->d_name];
} else {
// Unknown file type use generic binary object
mediaObject = [[IBinaryD allocFromZone:zone]
initWithKey:dp->d_name];
}
if ([mediaObject readFromFile:dp->d_name] == YES) {
[article insertKeyedObject:mediaObject];
} else {
[mediaObject free];
NXRunAlertPanel(LoStr("NewsBase"),
LoStr("file cannot be opened %s/%s"),
NULL,NULL,NULL, path, dp->d_name);
}
}
chdir(workingDirectoryName);
return(article);
} else if (strcmp(extension, MIME_FILE_EXTENSION) == 0) {
// MIME
if ((stream = NXMapFile(path, NX_READONLY)) == NULL) {
NXRunAlertPanel(LoStr("NewsBase"),
LoStr("file cannot be opened: %s"), NULL, NULL, NULL, path);
return(nil);
}
conv = [[IConvertMIME allocFromZone:zone] init];
article = [conv convertToItem:stream];
[conv free];
NXCloseMemory(stream, NX_FREEBUFFER);
return(article);
} else {
// unknown
NXRunAlertPanel(LoStr("NewsBase"),
LoStr("file has unknown extension: %s"), NULL, NULL, NULL, path);
return(nil);
}
return(nil);
}
+ setSaveDomainAndPath:(IArticleD *)article
{
const char *directory;
const char *filename;
SavePanel *savePanel;
[self directory:&directory andFilename:&filename forArticle:article];
savePanel = [SavePanel new];
[savePanel setTitle:"Save NewsBase Article"];
if ([article objectWithKey:INDEX_RTF] != nil) {
[savePanel setRequiredFileType:MM_FILE_EXTENSION];
} else {
[savePanel setRequiredFileType:NEWS_FILE_EXTENSION];
}
if (([savePanel runModalForDirectory:directory file:filename]) == 1) {
[[article external] setDomain:domain andPath:[savePanel filename]];
strncpy(previousDirectory, [savePanel filename],
sizeof(previousDirectory) - 1);
*rindex(previousDirectory, '/') = '\0';
return(self);
} else {
return(nil);
}
}
+ (BOOL)save:anObject toName:(const char *)path
{
char component[256];
char *ptr;
const char *objectKey;
NXStream *stream;
int n;
id mediaObject;
char workingDirectoryName[MAXPATHLEN];
char path2[MAXPATHLEN];
const char *thePath;
if ([anObject class] != [IArticleD class]) {
return(NO);
}
if ([anObject objectWithKey:INDEX_RTF] == nil) {
stream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
[[anObject objectWithKey:HEADER_INFO] writeToStream:stream];
NXWrite(stream, "\r\n", 2);
[[anObject objectWithKey:PLAINTEXT] writeToStream:stream];
NXSaveToFile(stream, path);
NXCloseMemory(stream, NX_FREEBUFFER);
// update path for this domain
[[anObject external] setDomain:domain andPath:path];
strncpy(previousDirectory, path, sizeof(previousDirectory) - 1);
*rindex(previousDirectory, '/') = '\0';
} else {
// add mm extension if neccessary
if ((ptr = rindex(path, '/'), ptr = rindex(ptr, '.')) == 0 ||
strcmp(ptr + 1, MM_FILE_EXTENSION) != 0) {
sprintf(path2, "%s.%s", path, MM_FILE_EXTENSION);
STRDBG(path2);
thePath = path2;
} else {
thePath = path;
}
// this is a multimedia article so make multimedia directory
mkdir(thePath, 0755);
getwd(workingDirectoryName);
chdir(thePath);
for (n = 0; (mediaObject = [anObject objectAt:n]) != nil;++n) {
// skip external objects
if ([mediaObject respondsTo:@selector(writeToStream:)] == NO) {
continue;
}
// make sure the key can be used as a filename
/*
if ((ptr = rindex(objectKey = [mediaObject key], '/')) == 0) {
strncpy(component, objectKey, sizeof(component));
} else {
strncpy(component, ptr + 1, sizeof(component));
}
component[sizeof(component) - 1] = '\0';
for (ptr = component; *ptr != '\0'; ++ptr) {
if (isalnum(*ptr) == 0 && *ptr != '.') {
*ptr = '_';
}
}
*/
stream = NXOpenMemory(NULL, 0, NX_READWRITE);
[mediaObject writeToStream:stream];
NXSaveToFile(stream, [mediaObject key]);
NXCloseMemory(stream, NX_FREEBUFFER);
}
chdir(workingDirectoryName);
// update path for this domain
[[anObject external] setDomain:domain andPath:thePath];
strncpy(previousDirectory, path, sizeof(previousDirectory) - 1);
*rindex(previousDirectory, '/') = '\0';
}
return(YES);
}
+ (BOOL)saveWithoutPrompt:(IArticleD *)saveArticle
{
const char *externalPath;
BOOL flag;
char buffer[2 * MAXPATHLEN];
// use the name for this domain stored in the external object
if ((externalPath = [[saveArticle external] keyForDomain:domain
isDirty:&flag]) != NULL) {
strncpy(buffer, externalPath, sizeof(buffer));
// strip external type extension
*rindex(buffer, '.') = '\0';
return([ILocalFileD save:saveArticle toName:buffer]);
}
return(NO);
}
+ (BOOL)save:(IArticleD *)saveArticle
{
const char *directory;
const char *filename;
SavePanel *savePanel;
[self directory:&directory andFilename:&filename forArticle:saveArticle];
savePanel = [SavePanel new];
[savePanel setTitle:"Save NewsBase Article"];
if ([saveArticle objectWithKey:INDEX_RTF] != nil) {
[savePanel setRequiredFileType:MM_FILE_EXTENSION];
} else {
[savePanel setRequiredFileType:NEWS_FILE_EXTENSION];
}
if (([savePanel runModalForDirectory:directory file:filename]) == 0) {
NXRunAlertPanel(LoStr("NewsBase"),LoStr("%s will not be saved."),
NULL,NULL,NULL, filename);
return(NO);
}
filename = [savePanel filename];
[ILocalFileD save:saveArticle toName:filename];
return(YES);
}
+ (BOOL)saveAsMIME:(IArticleD *)saveArticle
{
IConvertMIME *conv;
NXStream *articleStream;
const char *directory;
const char *filename;
SavePanel *savePanel;
conv = [[IConvertMIME alloc] init];
articleStream = NXOpenMemory(NULL, 0, NX_READWRITE);
[conv convertToMIME:saveArticle stream:articleStream];
[conv free];
[self directory:&directory andFilename:&filename forArticle:saveArticle];
savePanel = [SavePanel new];
[savePanel setTitle:"Save NewsBase Article"];
[savePanel setRequiredFileType:MIME_FILE_EXTENSION];
if (([savePanel runModalForDirectory:directory file:filename]) == 0) {
NXRunAlertPanel(LoStr("NewsBase"),LoStr("%s will not be saved."),
NULL,NULL,NULL, filename);
return(NO);
}
NXSaveToFile(articleStream, [savePanel filename]);
NXCloseMemory(articleStream, NX_FREEBUFFER);
return(YES);
}
// construct path for disk file
+ directory:(const char **)directory andFilename:(const char **)filename
forArticle:saveArticle
{
const char *externalPath;
const char *rootDirectory;
const char *newsgroup, *subject;
int articleNum;
char number[16];
char *ptr, *commaPtr;
static char buffer[MAXPATHLEN], *bufferEnd;
InfoD *header;
BOOL flag;
bufferEnd = &buffer[sizeof(buffer) - 1];
*bufferEnd = '\0';
// if it already has a name for a disk file then use that name
if ((externalPath = [[saveArticle external] keyForDomain:domain
isDirty:&flag]) != NULL) {
strncpy(buffer, externalPath, sizeof(buffer));
*directory = buffer;
ptr = rindex(buffer, '/');
*ptr = '\0';
*filename = ++ptr;
*rindex(ptr, '.') = '\0';
*rindex(ptr, '.') = '\0';
return(self);
}
*directory = buffer;
header = [saveArticle objectWithKey:HEADER_INFO];
// use previous directory if one exists
if (previousDirectory[0] != '\0') {
strncpy(buffer, previousDirectory, sizeof(buffer) - 1);
ptr = index(buffer, '\0');
} else {
// construct it from defaults and newsgroup name
if ((rootDirectory = NXGetDefaultValue(OWNER, SAVEDIRECTORY)) == 0) {
rootDirectory = getenv("HOME");
}
strncpy(buffer, rootDirectory, sizeof(buffer) - 1);
// add newsgroup name to path
ptr = index(buffer, '\0');
if ((newsgroup = (const char *)[header infoForKey:GROUPNAME]) != NULL
&& newsgroup[0] != '\0') {
if (bufferEnd - ptr) {
*ptr++ = '/';
strncpy(ptr, newsgroup, bufferEnd - ptr);
// use first newsgroup name
if ((commaPtr = index(ptr, ',')) != 0) {
*commaPtr = '\0';
}
// change '.' to '/'
for (; *ptr != '\0'; ++ptr) {
if (*ptr == '.') {
*ptr = '/';
}
}
}
}
}
STRDBG(buffer);
// now construct filename
*filename = ++ptr;
if ((subject = (const char *)[header infoForKey:SUBJECT]) != NULL &&
subject[0] != '\0') {
if (bufferEnd - ptr > 0) {
strncpy(ptr, subject, bufferEnd - ptr);
// replace non-alphanumerics with '_'
for (; *ptr != '\0'; ++ptr) {
if (isalnum(*ptr) == 0) {
*ptr = '_';
}
}
}
}
if ((articleNum = (int)[header infoForKey:ARTICLE_NUM]) != 0) {
sprintf(number, "-%d", articleNum);
strncpy(ptr, number, bufferEnd - ptr);
}
return(self);
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.