This is FileAccess.m in view mode; [Download] [Up]
// File Access FileAccess.m // =========== // // A HyperAccess object provides access to hyperinformation, using particular // protocols and data format transformations. This one provides access to // files in mounted filesystems. #define takeBackup YES // Take backup when saving existing file Cmd/S #import "FileAccess.h" #import <appkit/appkit.h> #import <stdio.h> #import <sys/types.h> #import <sys/stat.h> // For fstat() #import <libc.h> // For getgroups() #import "HTUtils.h" #import "WWW.h" // For WWW_ constants etc #import "HTParse.h" #import "HTTCP.h" // TCP utilities, like getting host name #import "HTFTP.h" // FTP protocol routines #import "HTFile.h" // File access routines #define THIS_TEXT (HyperText *)[[[NXApp mainWindow] contentView] docView] @implementation FileAccess : HyperAccess #define LENGTH 256 extern char * appDirectory; /* Pointer to directory for application */ // Initialize this class // + initialize { return [super initialize]; } // Overlay method returning the name of the access. - (const char *)name { return "file"; } ////////////////////////////////////////////////////////////////////////////////////// // S A V I N G // Save as an HTML file of given name (any format) // ---------------------------------- // - save: (HyperText *)HT inFile:(const char *)filename format:(int)format { NXStream * s; // The file stream s = NXOpenMemory(NULL, 0, NX_WRITEONLY); if (format==WWW_HTML) { char *saveName = WWW_nameOfFile(filename); // The WWW address [HT writeSGML:s relativeTo:saveName]; free(saveName); } else if (format==WWW_RICHTEXT) [HT writeRichText:s]; else if (format==WWW_PLAINTEXT || format==WWW_SOURCE) [HT writeText:s]; else fprintf(stderr, "HT/File: Unknown format!\n"); if (TRACE) printf("HT file: %li bytes in file `%s' format %i.\n", NXTell(s), filename, format); NXFlush(s); /* Try to get over missing end */ NXSaveToFile(s, filename); NXCloseMemory(s, NX_FREEBUFFER); return self; } // Get Filename near a "hint" node // ------------------------------- // // On entry, // hint is a hypertext whose name is to be used as a hint for // the user when selecting the name. // On exit, // returns 0 if no file selected // pointer to static string with filename if ok // const char * ask_name(HyperText * hint, int format) { char * suggestion; char * slash; int status; static SavePanel * save_panel; /* Keep a Save panel alive */ if (!hint) return 0; if (!save_panel) { save_panel = [SavePanel new]; // Keep between invocations } if (format==WWW_HTML) [save_panel setRequiredFileType: "html"]; else if (format==WWW_RICHTEXT) [save_panel setRequiredFileType: "rtf"]; else if (format==WWW_PLAINTEXT) [save_panel setRequiredFileType: "txt"]; else if (format==WWW_POSTSCRIPT) [save_panel setRequiredFileType: "ps"]; else [save_panel setRequiredFileType: ""]; suggestion = HTLocalName([[hint nodeAnchor]address]); slash = strrchr(suggestion, '/'); // Point to last slash if (slash) { *slash++ = 0; /* Separate directory and filename */ status = [save_panel runModalForDirectory:suggestion file:slash]; } else { if (TRACE) printf ("No slash in directory!!\n"); status = [save_panel runModalForDirectory:"." file:suggestion]; } free(suggestion); if (!status) { if (TRACE) printf("Save cancelled.\n"); return 0; } return [save_panel filename]; } // Save as a file using save panel (any format) // -------------------------------- // On entry, // format Gives the file format to be used (see WWW.h) // hint is a node to be used for a suggested name. // - saveIn:(int)format like:(HyperText *)hint { const char * filename; // The name of the file selected HyperText * HT; HT = THIS_TEXT; if (!HT) return HT; // No main window! filename = ask_name(HT, format); if (!filename) return nil; // Save clancelled. return [self save:HT inFile:filename format:format]; } // Save as an HTML file using save panel // -------------------- // - saveAs:sender { return [self saveIn:WWW_HTML like:THIS_TEXT]; } // Save as an RTF file using save panel // -------------------- // - saveAsRichText:sender { return [self saveIn:WWW_RICHTEXT like:THIS_TEXT]; } // Save as a plain text file using save panel // ------------------------- // - saveAsPlainText:sender { return [self saveIn:WWW_PLAINTEXT like:THIS_TEXT]; } // Save as an HTML file of same name // --------------------------------- // // We don't use a suffix for the backup filename, because some file systems // (which may be NFS mounted) truncate filenames and can chop the suffix off! // - saveNode:(HyperText *) HT { char * filename; // The name chosen id status; FILE * fp; const char * addr = [[HT nodeAnchor]address]; filename = HTLocalName(addr); /* Take a backup before we do anything silly */ if (takeBackup) { char * backup_filename = 0; char * p = backup_filename; backup_filename = malloc(strlen(filename)+2); strcpy(backup_filename, filename); for(p=backup_filename+strlen(backup_filename);; p--) {// Start at null if ((*p=='/') || (p<backup_filename)) { p[1]=','; /* Insert comma after slash */ break; } p[1] = p[0]; /* Move up everything to the right of it */ } if (fp=fopen(filename, "r")) { /* File exists */ fclose(fp); if (TRACE) printf("File `%s' exists\n", filename); if (remove(backup_filename)) { if (TRACE) printf("Backup file `%s' removed\n", backup_filename); } if (rename(filename, backup_filename)) { /* != 0 => Failure */ if (TRACE) printf("Rename `%s' to `%s' FAILED!\n", filename, backup_filename); } else { /* Success */ if (TRACE) printf("Renamed `%s' to `%s'\n", filename, backup_filename); } } free(backup_filename); } /* if take backup */ status = [self save:HT inFile:filename format:[HT format]]; if (status) [[HT window] setDocEdited: NO]; free(filename); return status; } ////////////////////////////////////////////////////////////////////////////////// // // O P E N I N G // LOAD ANCHOR // // Returns If successful, the anchor of the node loaded or found. // If failed, nil. - loadAnchor: (Anchor *) anAnchor Diagnostic:(int)diagnostic { const char * addr = [anAnchor address]; NXStream * s; // The file stream HyperText * HT; // The new node char * filename; // The name of the file char * newname = 0; // The simplified name int format; // The file format int file_number = -1; // File number if FTP // First, we TRY to reduce the name to a unique one. if (!addr) return nil; StrAllocCopy(newname, addr); HTSimplify(newname); [anAnchor setAddress:newname]; filename = HTLocalName(newname); addr = [anAnchor address]; if ([anAnchor node]) { if (TRACE) printf("Anchor %p already has a node\n", anAnchor); } else { s = NXMapFile(filename, NX_READONLY); // Map file into memory if (!s) { if (TRACE) printf("Could not open file `%s', errno=%i.\n", filename, errno); file_number = HTFTP_open_file_read(newname); // Try FTP if (file_number>=0) { s = NXOpenFile(file_number, NX_READONLY); } } if (!s) { if (TRACE) printf("Could not open `%s' with FTP either.\n", newname); NXRunAlertPanel(NULL, "Could not open `%s'\n", NULL,NULL,NULL, newname /*, strerror(errno) */ ); free(filename); free(newname); return nil; } free(newname); // For unrecognised types, open in the Workspace: format = HTFileFormat(filename); if (format == WWW_INVALID) { char command[255]; /* Open in the workspace */ sprintf(command, "open %s", filename); system(command); } else { /* Known format */ // Make the new node: HT = [HyperText newAnchor:anAnchor Server:self]; [HT setupWindow]; [[HT window]setTitle:filename]; // Show something's happening if (file_number<0) [HT setEditable:HTEditable(filename)]; // This is editable? else [HT setEditable: NO]; // AFTP data. switch(format) { case WWW_HTML: if (diagnostic==2) { [HT readText:s]; [HT setFormat: WWW_SOURCE]; } else { [HT readSGML:s diagnostic:diagnostic]; } break; case WWW_RICHTEXT: if (diagnostic > 0) { [HT readText:s]; [HT setFormat: WWW_SOURCE]; } else { [HT readRichText:s]; } break; case WWW_PLAINTEXT: [HT readText:s]; break; } } // Clean up now it's on the screen: if (TRACE) printf("Closing file stream\n"); if (file_number>=0) { NXClose(s); HTFTP_close_file(file_number); } else { NXCloseMemory(s, NX_FREEBUFFER); } } /* If anAnchor not loaded before */ free(filename); return anAnchor; } // Open a file of a given name // --------------------------- // // // On exit, // Returns If successful, the anchor of the node loaded or found. // If failed, nil. - openFile:(const char *)filename diagnostic:(BOOL)diagnostic { Anchor * a; char * node_address = WWW_nameOfFile(filename); a = [self loadAnchor:[Anchor newAddress:node_address] Diagnostic:diagnostic]; free(node_address); return a; } // Ask the User for the name of a file // ----------------------------------- // const char * existing_filename() { HyperText * HT; // The new node char * suggestion = 0; char * slash; int status; char * typelist = 0; // Any extension. static OpenPanel * openPanel=0; // Get The Filename from the User if (!openPanel) { openPanel = [OpenPanel new]; [openPanel allowMultipleFiles:NO]; } HT = THIS_TEXT; // Hypertext in main window, if selected. if (HT) { suggestion = HTLocalName([[HT nodeAnchor]address]); slash = strrchr(suggestion, '/'); // Point to last slash if (slash) { *slash++ = 0; /* Separate directory and filename */ status = [openPanel runModalForDirectory:suggestion file:"" types:&typelist]; // (was: file:slash but this is silly as that is already open.) } else { if (TRACE) printf ("No slash in directory!!\n"); status = [openPanel runModalForDirectory:"." file:"" types:&typelist]; // (was: file:suggestion but this is silly as above) } free(suggestion); } else { status = [openPanel runModalForTypes:&typelist]; } if (!status) { if (TRACE) printf("Open cancelled\n"); return NULL; } return [openPanel filename]; } // Link to a given file // -------------------- - linkToFile:sender { const char * filename = existing_filename(); // Ask for filename if (filename) { char * node_address = WWW_nameOfFile(filename); Anchor * a = [Anchor newAddress:node_address]; free(node_address); return [THIS_TEXT linkSelTo:a]; } return nil; } // Open A File using the panel openDiagnostic: // --------------------------- // // On exit, // Returns If successful, the anchor of the node loaded or found. // If failed, nil. - openDiagnostic:(int)diagnostic { const char * filename = existing_filename(); if (!filename) { if (TRACE) printf("Open cancelled\n"); return nil; } return [self openFile:filename diagnostic:diagnostic]; } // Load a personal or system-wide version of a file // ------------------------------------------------ // // This accesses the personal copy for the user or, failing that, // for the system. It is important that this name is fully qualified // as other names will be generated relative to it. // We check whether the local version is there before loading it // in order to prevent error messages being given to the user if // it is not. // // On exit, // Returns If successful, the anchor of the node loaded or found. // If failed, nil. - openMy:(const char *)filename diagnostic:(int)diagnostic { Anchor * a; char name[256]; char template[256]; if (getenv("HOME")) { int status; struct stat buf; /* status buffer */ strcpy(name, getenv("HOME")); strcat(name, "/WWW/"); strcat(name, filename); status = stat(name, &buf); /* Does file exist? */ if (status>=0) { /* If it does, load it */ if (TRACE) printf("File: stat() returned %d\n", status); a = [self openFile:name diagnostic:diagnostic]; if (a) { if ([a node]) [a select]; return a; /* Found local copy */ } } } strcpy(template, appDirectory); strcat(template, filename); a = [self openFile:template diagnostic:diagnostic]; if (!a) return nil; [a setAddress:name]; /* For later save back */ [[a node] setEditable:YES]; /* This is editable data? */ if ([a node]) [a select]; /* Bring to front */ return a; } // Go Home // ------- // // This accesses the default page of text for the user or, failing that, // for the system. // - goHome:sender { return [self openMy:"default.html" diagnostic:0]; } // Make a new blank node named like the current node // ------------------------------------------------- // // On exit, // Returns If successful, the anchor of the node loaded or found. // If failed, nil. - makeNew:sender { id status; HyperText * hint = THIS_TEXT; // The old hypertext char * node_address; // of the new hypertext Anchor * a; // The new anchor const char * filename; // The name of the file selected HyperText * HT; // The new hypertext if (!hint) return nil; // No main window! a = [self openMy:"blank.html" diagnostic:0]; if (!a) return nil; // Couldn't find blank node [a select]; HT = [a node]; if (!HT) return HT; // No node?! filename = ask_name(hint, WWW_HTML); if (!filename) return nil; // Save cancelled. node_address = WWW_nameOfFile(filename); [a setAddress:node_address]; // Adopt new address as node name /* Make a default title for the document from the file name: ** ** On entry, ** node_address must be valid and have a colon in it ** On exit, ** Title is set ** This is destructive of the node_address string. */ { char * pDir = 0; /* Last Directory name */ char * pTitle = strrchr(node_address, '/'); /* File name */ if (pTitle) { *pTitle++ = 0; /* Chop in two */ pDir = strrchr(node_address, '/'); if (!pDir) pDir = strchr(node_address, ':'); pDir++; /* After delimiter 921122 */ } else { pTitle = strrchr(node_address, ':')+1; pDir = ""; } if (strrchr(pTitle, '.')) *strrchr(pTitle, '.') = 0;// Kill filetype */ if (pDir) { char title[255]; sprintf(title, "%s -- %s", pTitle, pDir); [HT setTitle:title]; /* Default title is filename */ } else { [HT setTitle:pTitle]; } } free(node_address); status = [self save:HT inFile:filename format:WWW_HTML]; if (!status) return nil; // Save failed! [[a node] setEditable:YES]; // This is editable data. return a; } // Make a new blank node named like the current node and link to it // ---------------------------------------------------------------- // - linkToNew:sender { HyperText * HT = THIS_TEXT; Anchor * a; if (![HT isEditable]) return nil; /* Won't be able to link to it */ a = [self makeNew:sender]; /* Make the new node */ if (!a) return nil; return [HT linkSelTo:a]; /* Link the selection to it */ } // Actions: // - search:sender { return nil; } - searchRTF:sender { return nil; } - searchSGML:sender { return nil; } // Direct open buttons: - open:sender { return [self openDiagnostic:0]; } - openRTF:sender { return [self openDiagnostic:1]; } - openSGML:sender { return [self openDiagnostic:2]; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.