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.