This is WAISControl.m in view mode; [Download] [Up]
// WAISControl.m // // Free software created 1 Feb 1992 // by Paul Burchard <burchard@math.utah.edu>. #import "WAISControl.h" #import "Wais.h" #import "QuestionDoc.h" #import "SourceDoc.h" #import "BrowserPane.h" #import "IconWellControl.h" #import "MailSpeaker.h" #import <appkit/appkit.h> #import <sys/file.h> @implementation WAISControl // ----------------------- PREFERENCES ------------------------ // static NXDefaultsVector WAIStationDefaults = { { "SystemFolder", "/LocalLibrary/WAIS" }, { "SystemSourcesFolder", "/LocalLibrary/WAIS/sources" }, { "UserFolder", "~/Library/WAIS" }, { "UserSourcesFolder", "~/Library/WAIS/sources" }, { "UserDocumentsFolder", "~/Library/WAIS/documents" }, { "UserQuestionsFolder", "~/Library/WAIS/questions" }, { "OpenOnRetrieval", "YES" }, { "DocumentTypes", "" }, { "MaxSearchResults", "30" }, { "MailListAddress", "listserv@think.com" }, { "MailListMessage", "ADD wais-talk\nADD wais-interest\nADD wais-discussion\n" }, { "BugReportAddress", "burchard@math.utah.edu" }, { "BugReportCC", "bug-wais@think.com" }, { "BugReportSubject", "Comments on WAIStation.app " WAISTATION_VERSION " for NeXT" }, { "BugReportMessage", "Please help us in making the WAIS software better. Please answer candidly. How was the installation on the NeXT? How is the documentation? Comments on the server code: Comments on the NeXT interface: Comments on the existing WAIS sources on the net: What WAIS sources would you like to see: Would you like to help? In what way? General comments: " }, { NULL } }; + initialize { NXRegisterDefaults("WAIStation", WAIStationDefaults); return self; } // Returns NXAtom-ized version of full folder name. // Adds this name to the search path for the WaisClass (if not already there). // Name will be inserted at beginning if where<0, at end if where>0. // Note: subname may be NULL. - (NXAtom)addToPath:WaisClass inPos:(int)where subdir:(const char *)subname ofFolder:(const char *)name { char *buf; const char *home; NXAtom rtn; int i, len, cnt; id flist; // Valid folder? if(!name) return 0; if(!(name[0]=='/' || (name[0]=='~' && name[1]=='/'))) return 0; // Create buffer... home = NXHomeDirectory(); len = 1 + strlen(name) + strlen("/"); if(home) len += strlen(home); if(subname) len += strlen(subname); buf = s_malloc(len); if(!buf) return 0; // Replace initial '~' with home directory. if(name[0] == '~') { if(home) strcpy(buf, home); strcat(buf, name+1); } else strcpy(buf, name); len = strlen(buf); // Append subfolder if any (fix trailing '/'). if(subname) { if(buf[len-1] != '/') strcat(buf, "/"); strcat(buf, subname); } else if(buf[len-1] == '/') buf[len-1] = 0; // Append atomized version to WaisClass's path and return it. rtn = NXUniqueString(buf); s_free(buf); flist = [WaisClass folderList]; cnt = [flist count]; for(i=0; i<cnt; i++) if(rtn == *((NXAtom *)[flist elementAt:i])) break; if(i >= cnt) { if(where > 0) [flist addElement:(void *)&rtn]; else [flist insert:(void *)&rtn at:0]; } return rtn; } - createFolder:(const char *)name onError:(const char *)alertMsg { char cmd[2*MAXPATHLEN]; [Wais lockFileIO]; if(!name) { if(stringTable) NXRunAlertPanel( [stringTable valueForStringKey:"WAIStation"], [stringTable valueForStringKey:alertMsg], [stringTable valueForStringKey:"OK"], NULL, NULL); else { fprintf(stderr, "%s\n", alertMsg); fflush(stderr); } [Wais unlockFileIO]; return nil; } if(0 == access(name, R_OK|W_OK|X_OK)) { [Wais unlockFileIO]; return self; } sprintf(cmd, "mkdirs \'%s\'", name); system(cmd); if(0 == access(name, R_OK|W_OK|X_OK)) { [Wais unlockFileIO]; return self; } if(stringTable) NXRunAlertPanel( [stringTable valueForStringKey:"WAIStation"], [stringTable valueForStringKey:alertMsg], [stringTable valueForStringKey:"OK"], NULL, NULL); else { fprintf(stderr, "%s\n", alertMsg); fflush(stderr); } [Wais unlockFileIO]; return nil; } - usePrefs { const char *baseFolder, *thisFolder, *number; const char *docTypes, *typeStart, *typeExten, *typeEnd, *typesDone; char thisType[512], thisExten[512]; NXAtom folder; unsigned int rank; int limit; // Set flag to open or not open docs as they are retrieved. if(!NXGetDefaultValue("WAIStation", "OpenOnRetrieval") || 'Y'==*NXGetDefaultValue("WAIStation", "OpenOnRetrieval") || 'y'==*NXGetDefaultValue("WAIStation", "OpenOnRetrieval")) isOpenOnRetrieval = YES; else isOpenOnRetrieval = NO; // Set class-wide search limit for questions. if((number=NXGetDefaultValue("WAIStation", "MaxSearchResults")) && 1==sscanf(number, " %d ", &limit)) [WaisQuestion setSearchLimit:limit]; // Use document type ranking and file extension info. if(docTypes = NXGetDefaultValue("WAIStation", "DocumentTypes")) for(rank=1, typeStart=docTypes, typeExten=strchr(typeStart,'='), typesDone=docTypes+strlen(docTypes), typeEnd=(strchr(typeStart,',') ? strchr(typeStart,','):typesDone); typeStart && typeStart<typesDone; rank++, typeStart=typeEnd+(typeEnd[0]==',' ? 1:0), typeExten=strchr(typeStart,'='), typeEnd=(strchr(typeStart,',') ? strchr(typeStart,','):typesDone)) { if(typeExten && typeExten<typeEnd) { strncpy(thisType, typeStart, typeExten-typeStart); thisType[typeExten-typeStart] = 0; strncpy(thisExten, typeExten+1, typeEnd-typeExten-1); thisExten[typeEnd-typeExten-1] = 0; [WaisDocument registerExtension:thisExten forType:thisType]; } else { strncpy(thisType, typeStart, typeEnd-typeStart); thisType[typeEnd-typeStart] = 0; } [WaisQuestion preferDocumentType:thisType withRank:rank]; } // Create user directories if necessary and prepend to appropriate paths. if(!(baseFolder = NXGetDefaultValue("WAIStation", "UserFolder"))) baseFolder = "~/Library/WAIS"; if(thisFolder = NXGetDefaultValue("WAIStation", "UserSourcesFolder")) folder = [self addToPath:[WaisSource class] inPos:(-1) subdir:NULL ofFolder:thisFolder]; else folder = [self addToPath:[WaisSource class] inPos:(-1) subdir:"sources" ofFolder:baseFolder]; [self createFolder:folder onError:"Can't create folder for WAIS sources"]; if(thisFolder = NXGetDefaultValue("WAIStation", "UserDocumentsFolder")) folder = [self addToPath:[WaisDocument class] inPos:(-1) subdir:NULL ofFolder:thisFolder]; else folder = [self addToPath:[WaisDocument class] inPos:(-1) subdir:"documents" ofFolder:baseFolder]; [self createFolder:folder onError:"Can't create folder for WAIS documents"]; if(thisFolder = NXGetDefaultValue("WAIStation", "UserQuestionsFolder")) folder = [self addToPath:[WaisQuestion class] inPos:(-1) subdir:NULL ofFolder:thisFolder]; else folder = [self addToPath:[WaisQuestion class] inPos:(-1) subdir:"questions" ofFolder:baseFolder]; [self createFolder:folder onError:"Can't create folder for WAIS questions"]; // Append system directories (if any) to appropriate paths. if(thisFolder = NXGetDefaultValue("WAIStation", "SystemSourcesFolder")) [self addToPath:[WaisSource class] inPos:1 subdir:NULL ofFolder:thisFolder]; else if(baseFolder = NXGetDefaultValue("WAIStation", "SystemFolder")) [self addToPath:[WaisSource class] inPos:1 subdir:"sources" ofFolder:baseFolder]; return self; } - updatePrefs:sender { char number[128]; // Get new prefs from panel and save in database. if([prefsIsOpenOnRetrieval state] != 0) NXWriteDefault("WAIStation", "OpenOnRetrieval", "YES"); else NXWriteDefault("WAIStation", "OpenOnRetrieval", "NO"); sprintf(number, "%d", [prefsSearchLimit intValue]); NXWriteDefault("WAIStation", "MaxSearchResults", number); if([prefsDocumentTypes stringValue]) NXWriteDefault("WAIStation", "DocumentTypes", [prefsDocumentTypes stringValue]); if([prefsSystemFolder stringValue]) NXWriteDefault("WAIStation", "SystemFolder", [prefsSystemFolder stringValue]); if([prefsSystemSourcesFolder stringValue]) NXWriteDefault("WAIStation", "SystemSourcesFolder", [prefsSystemSourcesFolder stringValue]); if([prefsUserSourcesFolder stringValue]) NXWriteDefault("WAIStation", "UserSourcesFolder", [prefsUserSourcesFolder stringValue]); if([prefsUserDocumentsFolder stringValue]) NXWriteDefault("WAIStation", "UserDocumentsFolder", [prefsUserDocumentsFolder stringValue]); if([prefsUserQuestionsFolder stringValue]) NXWriteDefault("WAIStation", "UserQuestionsFolder", [prefsUserQuestionsFolder stringValue]); [self usePrefs]; return self; } - prefs:sender { const char *number; int limit; if(!prefsPanel) [NXApp loadNibSection:"Preferences.nib" owner:self]; if(!NXGetDefaultValue("WAIStation", "OpenOnRetrieval") || 'Y'==*NXGetDefaultValue("WAIStation", "OpenOnRetrieval") || 'y'==*NXGetDefaultValue("WAIStation", "OpenOnRetrieval")) [prefsIsOpenOnRetrieval setState:1]; else [prefsIsOpenOnRetrieval setState:0]; if((number=NXGetDefaultValue("WAIStation", "MaxSearchResults")) && 1==sscanf(number, " %d ", &limit)) [prefsSearchLimit setIntValue:limit]; [prefsSearchLimit sendAction:[prefsSearchLimit action] to:[prefsSearchLimit target]]; [prefsDocumentTypes setStringValue:NXGetDefaultValue("WAIStation", "DocumentTypes")]; [prefsSystemFolder setStringValue:NXGetDefaultValue("WAIStation", "SystemFolder")]; [prefsSystemSourcesFolder setStringValue:NXGetDefaultValue("WAIStation", "SystemSourcesFolder")]; [prefsUserSourcesFolder setStringValue:NXGetDefaultValue("WAIStation", "UserSourcesFolder")]; [prefsUserDocumentsFolder setStringValue:NXGetDefaultValue("WAIStation", "UserDocumentsFolder")]; [prefsUserQuestionsFolder setStringValue:NXGetDefaultValue("WAIStation", "UserQuestionsFolder")]; [prefsPanel update]; [prefsPanel makeKeyAndOrderFront:self]; return self; } + (const char *)defaultFolder { if(NXGetDefaultValue("WAIStation", "UserFolder")) return NXGetDefaultValue("WAIStation", "UserFolder"); else if(NXGetDefaultValue("WAIStation", "UserSourcesFolder")) return NXGetDefaultValue("WAIStation", "UserSourcesFolder"); else return [[Wais folderList] elementAt:0]; } // ----------------------- RETRIEVAL ------------------------ // // Callback from retrieval thread to main thread. - retrievalDone:data { int docAt, ok; id doc, check_rtn; if(!data || ![data isKindOf:[List class]] || ![[data objectAt:0] isKindOf:[Wais class]]) return nil; doc = [data objectAt:0]; check_rtn = [data objectAt:1]; [data free]; // If successfully retrieved and user desires docs to be opened on // retrieval, pop open doc. if(check_rtn && isOpenOnRetrieval) [self openFile:[doc key] ok:&ok]; // Enable browser entry for doc if check_rtn non-nil, else remove it. if(documentPaletteBrowser && (docAt=[documentPaletteBrowser indexOfEntry:[doc key]])>=0) { if(check_rtn) [documentPaletteBrowser setEntryEnabled:YES at:docAt]; else [documentPaletteBrowser removeEntryAt:docAt]; } else if(sourcePaletteBrowser && (docAt=[sourcePaletteBrowser indexOfEntry:[doc key]])>=0) { if(check_rtn) [sourcePaletteBrowser setEntryEnabled:YES at:docAt]; else [sourcePaletteBrowser removeEntryAt:docAt]; } return self; } any_t retrieval_thread(any_t rawArgs) { retrieval_args args = (retrieval_args)rawArgs; id doc, src, check_rtn, data; while(YES) { // Wait for retrieval requests to show up in list. mutex_lock(args->requestMutex); while([args->docList count] <= 0) condition_wait(args->requestCondition, args->requestMutex); doc = [args->docList objectAt:0]; mutex_unlock(args->requestMutex); if(!doc) continue; // Only one retrieval at a time, since same port may be used. // If doc file describes a source, load it into Wais system. // Else write auxiliary WAIS file to preserve access info. check_rtn = [doc retrieve]; if(check_rtn) { if([doc valueForStringKey:":type"] && 0==strcmp([doc valueForStringKey:":type"], "WSRC")) { src = [[WaisSource alloc] initKey:[doc key]]; [src readWaisFile]; } else [doc writeWaisFile]; } // Report back: // 1. Pop open retrieved doc if necessary. // 2. Enable browser entry if successful, else remove it. // (Use callback to main thread since AppKit is not thread-proof. // Callback will free data list.) // (Note: [NXApp delegate] = us.) data = [[List alloc] init]; [data addObject:doc]; [data addObject:check_rtn]; [Wais callback:[NXApp delegate] perform:@selector(retrievalDone:) with:data]; // Remove entry from request list. mutex_lock(args->requestMutex); [args->docList removeObjectAt:0]; mutex_unlock(args->requestMutex); } // Never reaches this. return 0; } - restartThread { // Already running? if(retrievalThread) return nil; // Create and detach thread. retrievalArgs.docList = retrievalList; retrievalArgs.requestMutex = requestMutex = mutex_alloc(); retrievalArgs.requestCondition = requestCondition = condition_alloc(); retrievalThread = cthread_fork(retrieval_thread, (any_t)&retrievalArgs); if(!retrievalThread) return nil; cthread_detach(retrievalThread); return self; } - terminateThread { int i; // Try to abort the retrieval thread. //!!! Zapping threads may screw up the mutex locking, so we don't //!!! try to recycle old mutexes and conditions. Even if old thread stays //!!! around, it's probably not doing much, so its freedom from the //!!! new mutexes shouldn't be a problem. if(!retrievalThread) return self; thread_suspend(cthread_thread(retrievalThread)); cthread_abort(retrievalThread); retrievalThread = 0; requestMutex = 0; requestCondition = 0; [Wais waisNewLocks]; // Clear retrieval request list. // Update palettes (removing disabled entries). [retrievalList empty]; for(i=[documentPaletteBrowser count]-1; i>=0; i--) if(![documentPaletteBrowser isEntryEnabledAt:i]) [documentPaletteBrowser removeEntryAt:i]; [documentPaletteBrowser update]; for(i=[sourcePaletteBrowser count]-1; i>=0; i--) if(![sourcePaletteBrowser isEntryEnabledAt:i]) [sourcePaletteBrowser removeEntryAt:i]; [sourcePaletteBrowser update]; return self; } - cancelRetrievals:sender { [self terminateThread]; [self restartThread]; return self; } //!!! This method should be called before doing any operation that might //!!! access a WaisDocument. If it is being retrieved, you should //!!! not access it as that might collide with operations in the //!!! retrieval thread. - (BOOL)isDocumentBeingRetrieved:waisDoc { int docAt; if(requestMutex) mutex_lock(requestMutex); docAt = [retrievalList indexOf:waisDoc]; if(requestMutex) mutex_unlock(requestMutex); if(docAt!=NX_NOT_IN_LIST && docAt>=0) return YES; return NO; } - retrieveDocuments:(const char *)keyList { int docAt; char *docKeys, *thisKey, *nextKey; id doc, inBrowser; BOOL isSource; // Assumes keyList is TAB-separated list of keys. if(!keyList || !retrievalThread) return nil; if(!(docKeys = s_malloc(strlen(keyList) + 1))) return nil; strcpy(docKeys, keyList); for(thisKey=docKeys, nextKey=strchr(thisKey, '\t'); thisKey; thisKey=nextKey, nextKey=strchr(thisKey?thisKey:"", '\t')) { if(nextKey) *nextKey++ = 0; // Check if doc is aleady retrieved or being retrieved (then ignore). if(!(doc = [WaisDocument objectForKey:thisKey])) continue; if([self isDocumentBeingRetrieved:doc] || [doc isRetrieved]) continue; // Check if it is really a source. isSource = NO; inBrowser = documentPaletteBrowser; if([doc valueForStringKey:":type"] && 0==strcmp([doc valueForStringKey:":type"], "WSRC")) isSource = YES; if(isSource) inBrowser = sourcePaletteBrowser; // Enter into correct palette browser, but disabled. if(inBrowser) { docAt = [inBrowser indexAddEntry:[doc key]]; [inBrowser setEntryEnabled:NO at:docAt]; [inBrowser update]; } // Enter doc into queue and notify thead of retrieval request. if(requestMutex) mutex_lock(requestMutex); [retrievalList addObjectIfAbsent:doc]; if(requestMutex) mutex_unlock(requestMutex); if(requestCondition) condition_signal(requestCondition); } s_free(docKeys); return self; } - retrieveDocumentsFrom:sender { return [self retrieveDocuments:[sender stringValue]]; } // ----------------------- PANEL SETUP ----------------------- // - init { id Handlers; [super init]; // Classify file types. Handlers = [[List alloc] initCount:0]; [Handlers addObject:[QuestionDoc class]]; [Handlers addObject:[SourceDoc class]]; [super setDocHandlers:Handlers]; launchWithCreateDoc = YES; // Keep track of retrieval threads. retrievalList = [[List alloc] initCount:0]; retrievalThread = 0; return self; } - free { if(retrievalThread) [self terminateThread]; [retrievalList free]; if(requestMutex) mutex_free(requestMutex); if(requestCondition) condition_free(requestCondition); return [super free]; } - appDidInit:sender { char userInfo[1024]; char hostname[512]; [super appDidInit:sender]; // Enable Alert Panels for Wais classes. [Wais setStringTable:stringTable]; // Set up according to preferences. [self usePrefs]; // Set user registration string. gethostname(hostname, 512); hostname[512-1] = 0; sprintf(userInfo, "WAIStation.app %s, from host: %s, user: %s", WAISTATION_VERSION, hostname, WAIS_USER); [WaisSource registerUser:userInfo]; // Pop up source and doc palettes. [self sourcePalette:self]; [self documentPalette:self]; [sourcePalettePanel makeKeyAndOrderFront:self]; // Start retrieval thread. // Must be done after doc and source palette created if we want updates! [self restartThread]; return self; } - help:sender { if(!helpPanel) [NXApp loadNibSection:"Help.nib" owner:self]; [helpPanel makeKeyAndOrderFront:self]; return self; } - info:sender { if(!infoPanel) [NXApp loadNibSection:"Info.nib" owner:self]; [infoPanel makeKeyAndOrderFront:self]; return self; } - bugReport:sender { //!!! Note the "MailSendDemo" port is undocumented. id mailSpeaker = [[MailSpeaker alloc] init]; port_t mailPort = NXPortFromName("MailSendDemo", NULL); if(!mailSpeaker || mailPort==PORT_NULL) { [mailSpeaker free]; return nil; } [mailSpeaker setSendPort:mailPort]; [mailSpeaker openSend]; if(NXGetDefaultValue("WAIStation", "BugReportAddress")) [mailSpeaker setTo:NXGetDefaultValue("WAIStation", "BugReportAddress")]; if(NXGetDefaultValue("WAIStation", "BugReportCC")) [mailSpeaker setCc:NXGetDefaultValue("WAIStation", "BugReportCC")]; if(NXGetDefaultValue("WAIStation", "BugReportSubject")) [mailSpeaker setSubject:NXGetDefaultValue("WAIStation", "BugReportSubject")]; if(NXGetDefaultValue("WAIStation", "BugReportMessage")) [mailSpeaker setBody:NXGetDefaultValue("WAIStation", "BugReportMessage")]; [mailSpeaker free]; port_deallocate(task_self(), mailPort); return self; } - signMeUp:sender { //!!! Note the "MailSendDemo" port is undocumented. id mailSpeaker = [[MailSpeaker alloc] init]; port_t mailPort = NXPortFromName("MailSendDemo", NULL); if(!mailSpeaker || mailPort==PORT_NULL) { [mailSpeaker free]; return nil; } [mailSpeaker setSendPort:mailPort]; [mailSpeaker openSend]; if(NXGetDefaultValue("WAIStation", "MailListAddress")) [mailSpeaker setTo:NXGetDefaultValue("WAIStation", "MailListAddress")]; [mailSpeaker setSubject:"SUBSCRIBE"]; if(NXGetDefaultValue("WAIStation", "MailListMessage")) [mailSpeaker setBody:NXGetDefaultValue("WAIStation", "MailListMessage")]; [mailSpeaker free]; port_deallocate(task_self(), mailPort); return self; } - sourcePalette:sender { int i, j, n, m; BOOL quiet; id srcList, srcPath; const char **srcFolder; if(!(srcPath = [WaisSource folderList])) return nil; if(!sourcePalettePanel) { [NXApp loadNibSection:"SourcePalette.nib" owner:self]; [[sourcePaletteBrowser setAlphabetized:YES] setAbbreviated:YES]; [sourcePaletteBrowser setEditable:YES]; } // Load all directories in source path (ignore missing ones). [sourcePaletteBrowser clear]; quiet = [Wais isQuiet]; [Wais setQuiet:YES]; m = [srcPath count]; for(j=0; j<m; j++) if((srcFolder=(const char **)[srcPath elementAt:j]) && (*srcFolder) && (srcList=[WaisSource loadFolder:*srcFolder])) { n = [srcList count]; for(i=0; i<n; i++) [sourcePaletteBrowser addEntry:[[srcList objectAt:i] key]]; [srcList free]; } [Wais setQuiet:quiet]; [sourcePaletteBrowser update]; [sourcePalettePanel orderFront:self]; if(!sourcePaletteIWC) sourcePaletteIWC = [[IconWellControl alloc] initWindow:sourcePalettePanel]; return self; } - documentPalette:sender { int i, j, n, m; BOOL quiet; id docList, docPath; const char **docFolder; if(!(docPath = [WaisDocument folderList])) return nil; if(!documentPalettePanel) { [NXApp loadNibSection:"DocumentPalette.nib" owner:self]; [[documentPaletteBrowser setAlphabetized:YES] setAbbreviated:YES]; [documentPaletteBrowser setEditable:YES]; } // Load all directories in document path (ignore missing ones). [documentPaletteBrowser clear]; quiet = [Wais isQuiet]; [Wais setQuiet:YES]; m = [docPath count]; for(j=0; j<m; j++) if((docFolder=(const char **)[docPath elementAt:j]) && (*docFolder) && (docList=[WaisDocument loadFolder:*docFolder])) { n = [docList count]; for(i=0; i<n; i++) { if([self isDocumentBeingRetrieved:[docList objectAt:i]]) continue; else if([[docList objectAt:i] isRetrieved]) [documentPaletteBrowser addEntry:[[docList objectAt:i] key]]; } [docList free]; } [Wais setQuiet:quiet]; [documentPaletteBrowser update]; [documentPalettePanel orderFront:self]; if(!documentPaletteIWC) documentPaletteIWC = [[IconWellControl alloc] initWindow:documentPalettePanel]; return self; } // ----------------------- DELEGATED FILE OPS ------------------------ // - (int)openFile:(const char *)fileName ok:(int *)flag { int rtn; // Try to open in this application first, then in Workspace. if([super handlerForFile:fileName]) { if([super openForHandlerAt:(-1) name:fileName]) *flag = YES; else *flag = NO; rtn = 0; } else { [Wais lockTransaction]; [[NXApp appSpeaker] setSendPort:NXPortFromName(NX_WORKSPACEREQUEST, NULL)]; rtn = [[NXApp appSpeaker] openFile:fileName ok:flag]; [Wais unlockTransaction]; } return rtn; } - (int)removeFile:(const char *)fileName ok:(int *)flag { int rtn = 0; char *auxFile = 0; id doc = nil; // If a WaisDocument, set it as unretrieved // and delete the associated ".wais" file too. if(!fileName) return (-1); *flag = NO; if(doc = [WaisDocument objectForKey:fileName]) { auxFile = s_malloc(1+strlen(fileName)+strlen(".wais")); if(!auxFile) return (-1); strcpy(auxFile, fileName); strcat(auxFile, ".wais"); //!!! Potential thread conflict if doc is being retrieved. // (But shouldn't be a problem since such entries are disabled.) [doc setUnretrieved]; } [Wais lockFileIO]; if(doc) rtn = unlink(auxFile); if(rtn == 0) rtn = unlink(fileName); else unlink(fileName); [Wais unlockFileIO]; if(auxFile) s_free(auxFile); if(rtn != 0) return rtn; *flag = YES; return 0; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.