WWW/NextStep/Implementation/ 775 472 105 0 5324275371 11300 WWW/NextStep/Implementation/Makefile 664 472 105 2631 5304456753 13032 # # Generated by the NeXT Project Builder. # # NOTE: Do NOT change this file -- Project Builder maintains it. # # Put all of your customizations in files called Makefile.preamble # and Makefile.postamble (both optional), and Makefile will include them. # NAME = WorldWideWeb PROJECTVERSION = 1.1 LANGUAGE = English APPICON = worldbook2.tiff DOCICONS = docflash2.tiff GLOBAL_RESOURCES = WorldWideWeb.nib docflash.tiff docflash2.tiff \ icon1.tiff worldbook2.tiff CLASSES = Anchor.m FileAccess.m HTStyle.m HyperAccess.m HyperManager.m \ HyperText.m NewsAccess.m StyleToy.m TcpAccess.m TextToy.m \ WWWPageLayout.m HFILES = HTAccess.h HTFile.h HTFTP.h HTParse.h HTTCP.h HTTP.h HTUtils.h \ tcp.h WWW.h Anchor.h FileAccess.h HTStyle.h HyperAccess.h \ HyperManager.h HyperText.h NewsAccess.h StyleToy.h TcpAccess.h \ TextToy.h WWWPageLayout.h HTString.h MFILES = WorldWideWeb_main.m CFILES = HTAccess.c HTFile.c HTFTP.c HTParse.c HTString.c HTTCP.c HTTP.c MAKEFILEDIR = /NextDeveloper/Makefiles/app INSTALLDIR = $(HOME)/Apps INSTALLFLAGS = -c -s -m 755 SOURCEMODE = 444 ICONSECTIONS = -sectcreate __ICON app worldbook2.tiff \ -sectcreate __ICON docflash2 docflash2.tiff LIBS = -lMedia_s -lNeXT_s DEBUG_LIBS = $(LIBS) PROF_LIBS = $(LIBS) -include Makefile.preamble include $(MAKEFILEDIR)/app.make -include Makefile.postamble -include Makefile.dependencies 8,SourceFiles.html8 ,Bugs.htmlWWW/NextStep/Implementation/StyleToy.m 644 472 105 21777 5304717173 13372 /* Style Toy: Allows user manipulation of styles StyleToy.m ** --------- ---------------------------------- */ #import "StyleToy.h" #import "HTParse.h" #import "HTStyle.h" #import "HTUtils.h" #import "HyperText.h" #define THIS_TEXT (HyperText *)[[[NXApp mainWindow] contentView] docView] /* Field numbers in the parameter form: */ #define SGMLTAG_FIELD 4 #define FONT_NAME_FIELD 2 #define FONT_SIZE_FIELD 3 #define FIRST_INDENT_FIELD 0 #define SECOND_INDENT_FIELD 1 @implementation StyleToy extern char * appDirectory; /* Pointer to directory for application */ // Global styleSheet available to every one: HTStyleSheet * styleSheet = 0; static HTStyle * style; /* Current Style */ static OpenPanel * open_panel; /* Keep the open panel alive */ static SavePanel * save_panel; /* Keep a Save panel too */ // Create new one: + new { self = [super new]; [self loadDefaultStyleSheet]; return self; } // Set connections to other objects: - setTabForm:anObject { TabForm = anObject; return self; } - setParameterForm:anObject { ParameterForm = anObject; return self; } - setStylePanel:anObject { StylePanel = anObject; return self; } - setNameForm:anObject { NameForm = anObject; return self; } // ACTION METHODS // ============== // Display style in the panel - display_style { if(style->name) [NameForm setStringValue:style->name at:0]; else [NameForm setStringValue:"" at:0]; if(style->SGMLTag) [ParameterForm setStringValue:style->SGMLTag at:SGMLTAG_FIELD]; else [ParameterForm setStringValue:"" at:SGMLTAG_FIELD]; [ParameterForm setStringValue:[style->font name] at:FONT_NAME_FIELD]; [ParameterForm setFloatValue:style->fontSize at:FONT_SIZE_FIELD]; if(style->paragraph) { char tabstring[255]; int i; [ParameterForm setFloatValue:style->paragraph->indent1st at:FIRST_INDENT_FIELD]; [ParameterForm setFloatValue:style->paragraph->indent2nd at:SECOND_INDENT_FIELD]; tabstring[0]=0; for(i=0; i < style->paragraph->numTabs; i++) { sprintf(tabstring+strlen(tabstring), "%.0f ", style->paragraph->tabs[i].x); } [TabForm setStringValue:tabstring at:0]; } return self; } // Load style from Panel // // @@ Tabs not loaded -load_style { char * name = 0; char * stripped; style->fontSize=[ParameterForm floatValueAt:FONT_SIZE_FIELD]; StrAllocCopy(name, [NameForm stringValueAt:0]); stripped = HTStrip(name); if (*stripped) { id font; font = [Font newFont:stripped size:style->fontSize]; if (font) style->font = font; } free(name); name = 0; StrAllocCopy(name, [ParameterForm stringValueAt:SGMLTAG_FIELD]); stripped = HTStrip(name); if (*stripped) { StrAllocCopy(style->SGMLTag, stripped); } free(name); name = 0; if (!style->paragraph) style->paragraph = malloc(sizeof(*(style->paragraph))); style->paragraph->indent1st = [ParameterForm floatValueAt: FIRST_INDENT_FIELD]; style->paragraph->indent2nd = [ParameterForm floatValueAt: SECOND_INDENT_FIELD]; return self; } // Open a style sheet from a file // ------------------------------ // // We overlay any previously defined styles with new ones, but leave // old ones which are not redefined. - open:sender { NXStream * s; // The file stream const char * filename; // The name of the file char *typelist[2] = {"style",(char *)0}; // Extension must be ".style." if (!open_panel) { open_panel = [OpenPanel new]; [open_panel allowMultipleFiles:NO]; } if (![open_panel runModalForTypes:typelist]) { if (TRACE) printf("No file selected.\n"); return nil; } filename = [open_panel filename]; s = NXMapFile(filename, NX_READONLY); if (!s) { if(TRACE) printf("Styles: Can't open file %s\n", filename); return nil; } if (!styleSheet) styleSheet = HTStyleSheetNew(); StrAllocCopy(styleSheet->name, filename); if (TRACE) printf("Stylesheet: New one called %s.\n", styleSheet->name); (void)HTStyleSheetRead(styleSheet, s); NXCloseMemory(s, NX_FREEBUFFER); style = styleSheet->styles; [self display_style]; return self; } // Load default style sheet // ------------------------ // // We load EITHER the user's style sheet (if it exists) OR the system one. // This saves a bit of time on load. An alternative would be to load the // system style sheet and then overload the styles in the user's, so that // he could redefine a subset only of the styles. // If the HOME directory is defined, then it is always used a the // style sheet name, so that any changes will be saved in the user's // $(HOME)/WWW directory. - loadDefaultStyleSheet { NXStream * stream; if (!styleSheet) styleSheet = HTStyleSheetNew(); styleSheet->name = malloc(strlen(appDirectory)+13+1); strcpy(styleSheet->name, appDirectory); strcat(styleSheet->name, "default.style"); if (getenv("HOME")) { char name[256]; strcpy(name, getenv("HOME")); strcat(name, "/WWW/default.style"); StrAllocCopy(styleSheet->name, name); stream = NXMapFile(name, NX_READONLY); } else stream = 0; if (!stream) { char name[256]; strcpy(name, appDirectory); strcat(name, "default.style"); if (TRACE) printf("Couldn't open $(HOME)/WWW/default.style\n"); stream = NXMapFile(name, NX_READONLY); if (!stream) printf("Couldn't open %s, errno=%i\n", name, errno); } if (stream) { (void)HTStyleSheetRead(styleSheet, stream); NXCloseMemory(stream, NX_FREEBUFFER); style = styleSheet->styles; [self display_style]; } return self; } // Save style sheet to a file // -------------------------- - saveAs:sender { NXStream * s; // The file stream char * slash; int status; char * suggestion=0; // The name of the file to suggest const char * filename; // The name chosen if (!save_panel) { save_panel = [SavePanel new]; // Keep between invocations } StrAllocCopy(suggestion,styleSheet->name); slash = rindex(suggestion, '/'); // Point to last slash if (slash) { *slash++ = 0; /* Separate directory and filename */ status = [save_panel runModalForDirectory:suggestion file:slash]; } else { status = [save_panel runModalForDirectory:"." file:suggestion]; } free(suggestion); if (!status) { if (TRACE) printf("No file selected.\n"); return nil; } filename = [save_panel filename]; s = NXMapFile(filename, NX_WRITEONLY); if (!s) { if(TRACE) printf("Styles: Can't open file %s for write\n", filename); return nil; } StrAllocCopy(styleSheet->name, filename); if (TRACE) printf("StylestyleSheet: Saving as `%s'.\n", styleSheet->name); (void)HTStyleSheetWrite(styleSheet, s); NXSaveToFile(s, styleSheet->name); NXCloseMemory(s, NX_FREEBUFFER); style = styleSheet->styles; [self display_style]; return self; } // Move to next style // ------------------ - NextButton:sender { if (styleSheet->styles) if (styleSheet->styles->next) if (style->next) { style = style->next; } else { style = styleSheet->styles; } [self display_style]; return self; } - FindUnstyledButton:sender { [THIS_TEXT selectUnstyled: styleSheet]; return self; } // Apply current style to selection // -------------------------------- - ApplyButton:sender { [THIS_TEXT applyStyle:style]; return self; } - applyStyleNamed: (const char *)name { HTStyle * thisStyle = HTStyleNamed(styleSheet, name); if (!thisStyle) return nil; return [THIS_TEXT applyStyle:thisStyle]; } - heading1Button:sender { return [self applyStyleNamed:"Heading1" ]; } - heading2Button:sender { return [self applyStyleNamed:"Heading2" ]; } - heading3Button:sender { return [self applyStyleNamed:"Heading3" ]; } - heading4Button:sender { return [self applyStyleNamed:"Heading4" ]; } - heading5Button:sender { return [self applyStyleNamed:"Heading5" ]; } - heading6Button:sender { return [self applyStyleNamed:"Heading6" ]; } - normalButton:sender { return [self applyStyleNamed:"Normal" ]; } - addressButton:sender { return [self applyStyleNamed:"Address" ]; } - exampleButton:sender { return [self applyStyleNamed:"Example" ]; } - listButton:sender { return [self applyStyleNamed:"List" ]; } - glossaryButton:sender { return [self applyStyleNamed:"Glossary" ]; } // Move to previous style // ---------------------- - PreviousButton:sender { HTStyle * scan; for (scan = styleSheet->styles; scan; scan=scan->next) { if ((scan->next==style) || (scan->next==0)){ style = scan; break; } } [self display_style]; return self; } - SetButton:sender { [self load_style]; [THIS_TEXT updateStyle:style]; return self; } - PickButton:sender { HTStyle * st = [THIS_TEXT selectionStyle:styleSheet]; if (st) { style = st; [self display_style]; } return self; } - ApplyToSimilar:sender { return [THIS_TEXT applyToSimilar:style]; } @end "WWW/NextStep/Implementation/IB.proj 664 472 105 2201 5051712334 12536  typedstream@ StreamTable HashTableObjecti%%i![949c] typedstream@ProjectObject %%i%%@@@@@ WorldWideWeb@/private/Net/cernvax/userd/tbl/hypertext/WWW/NeXT/Implementation $(HOME)/Apps icon2.tiffStorage%ii*[3*] icon1.tiffhtml HashTablei%%i % @  HTParse.hWWW.hHTTP.h HTAccess.hHTFile.hHTFTP.hHTTCP.h HTUtils.htcp.hHTFTP.c HTParse.cHTTP.cHTTCP.cHTFile.c HTAccess.cWorldWideWeb_main.m TcpAccess.mHyperManager.m StyleToy.m HTStyle.mAnchor.m HyperAccess.mWWWPageLayout.m HyperText.m FileAccess.m NewsAccess.m TextToy.mWorldWideWeb.nib mainNibFile makeTarget make debug writeMakefile1 writeMainfile0 appClassName Application appWrapperNO[20c] typedstream@C[67c] typedstream@ HashTableObjecti%%ibj85 HTUtils.h86 HTString.h8< HTString.cWorldWideWeb.nib~8? PB.gdbinit89 Version.make8 docflash.tiff8GMakefile.preamble7 .dir3_0.wmd8=worldbook.tiff8>worldbook2.tiff87docflash2.tiff8,SourceFiles.html8 ,Bugs.htmlFile(filename, NX_WRITEONLY); if (!s) { if(TWWW/NextStep/Implementation/FileAccess.m 644 472 105 37367 5304662526 13602 // 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 #import #import #import // For fstat() #import // 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 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 5]; /* 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:filWWW/NextStep/Implementation/HyperText.m 644 472 105 133553 5304666746 13557 // HyperText Implementation HyperText.m // ------------------------ // // HyperText is like Text, but includes links to and from other hypertexts. // // Authors: // TBL Tim Berners-Lee CERN/CN // // See also: // The Anchor class, and the HyperAccess class. // // History: // 25 Sep 90 Written (TBL) with the help of the Interface builder. // 14 Mar 91 Page width is taken from application's page layout. // Notes. // // For all anchors to have addresses, the node must be made with // newAnchor:Server: . Do not use any other creation methods inherited. #import #import "HyperText.h" #import "HyperAccess.h" #import "HTUtils.h" #import "HTParse.h" #import "HTStyle.h" #import "WWW.h" @implementation HyperText #define ANCHOR_ID_PREFIX 'z' /* Auto anchors run z1, z2, ... 921122 */ extern HTStyleSheet * styleSheet; /* see StyleToy */ static int window_sequence = 0; /* For stacking windows neatly */ #define SLOTS 30 /* Number of position slots */ static HyperText * slot[SLOTS]; /* Ids of HT objects taking them */ #define NICE_HEIGHT 600.0 /* Allows a few windows to be stacked */ #define MAX_HEIGHT 720.0 /* Worth going bigger to get it all in */ #define MIN_HEIGHT 80.0 /* Mustn't lose the window! */ #define MIN_WIDTH 200.0 static HyperText * HT; /* Global pointer to self to allow C mixing */ +initialize { int i; for (i=0; isize.width - leftMargin - rightMargin); } // Class methods // ------------- // // Build a HyperText GIVEN its nodeAnchor. // -------------------------------------- + newAnchor:(Anchor *)anAnchor Server:(id)aServer { NXRect aFrame = {{0.0, 0.0}, {page_width(), NICE_HEIGHT}}; self = [super newFrame:&aFrame]; if (TRACE) printf("New node, server is %i\n", aServer); nextAnchorNumber = 0; protection = 0; // Can do anything format = WWW_HTML; // By default [self setMonoFont: NO]; // By default theRuns->chunk.growby = 16 * sizeof(theRuns->runs[0]); // efficiency server = (HyperAccess *)aServer; [self setDelegate:aServer]; /* For changes */ nodeAnchor= anAnchor; [nodeAnchor setNode:self]; return self; } // Instance Methods // ---------------- // Free the hypertext. - free { slot[slotNumber] = 0; // Allow slot to be reused [nodeAnchor setNode:nil]; // Invalidate the node return [super free]; } // Read and set format - (int) format {return format; } - setFormat:(int)f {format = f; return self; } // Useful diagnostic routine: Dump to standard output // --------------------------------------------------- // // This first lists the runs up to and including the current run, // then it lists the attributes of the current run. // - dump:sender { int pos; /* Start of run being scanned */ int sob=0; /* Start of text block being scanned */ NXRun * r = theRuns->runs; NXTextBlock * block = firstTextBlock; printf("Hypertext %i, selected(%i,%i)", self, sp0.cp, spN.cp); if (delegate) printf(", has delegate"); printf(".\n"); printf(" Frame is at (%f, %f, size is (%f, %f)\n", frame.origin.x, frame.origin.y, frame.size.width, frame.size.height); printf(" Text blocks and runs up to character %i:\n", sp0.cp); for (pos = 0; pos<=sp0.cp; pos = pos+((r++)->chars)) { while(sob <= pos) { printf("%5i: Block of %i/%i characters at 0x%x starts `%10.10s'\n", sob, block->chars, malloc_size(block->text), block->text, block->text); sob = sob + block->chars; block = block->next; } printf("%5i: %3i of fnt=%i p=%i gy=%3.2f RGB=%i i=%i fl=%x\n", pos, r->chars, (int)r->font, r->paraStyle, r->textGray,r->textRGBColor, (int)(r->info), *(int*)&(r->rFlags)); } r--; /* Point to run for start of selection */ printf("\n Current run:\n\tFont name:\t%s\n", [r->font name]); { NXTextStyle *p = (NXTextStyle *)r->paraStyle; if (!p) { printf("\tNo paragraph style!\n"); } else { int tab; printf("\tParagraph style %i\n", p); printf("\tIndents: first=%f, left=%f\n", p->indent1st, p->indent2nd); printf("\tAlignment type=%i, %i tabs:\n", p->alignment, p->numTabs); for (tab=0; tabnumTabs; tab++) { printf("\t Tab kind=%i at %f\n", p->tabs[tab].kind, p->tabs[tab].x); } } } printf("\n"); return self; } // Adjust Scrollers and Window size for current text size // ------------------------------------------------------ // //The scrollers are turned off if they possibly can be, to simplify the screen. // If the text is editable, they have to be left on, although formatted text is // allowed to wrap round, and so horizontal scroll bars are not necessary. // The window size is adjusted as a function of the text size and scrollers. // // @@ Bug: The resize bar should be removed if there are no scrollers. // This is difficult to do -- might have to make a new window. // - adjustWindow { #define MAX_WIDTH paperWidth NXRect scroll_frame; NXRect old_scroll_frame; NXSize size; BOOL scroll_X, scroll_Y; // Do we need scrollers? ScrollView* scrollview = [window contentView];// Pick up id of ScrollView float paperWidth = page_width(); // Get page layout width [window disableFlushWindow]; // Prevent flashes [self setVertResizable:YES]; // Can change size automatically [self setHorizResizable:tFlags.monoFont]; [self calcLine]; // Wrap text to current text size [self sizeToFit]; // Reduce size if possible. if (maxY > MAX_HEIGHT) { scroll_Y = YES; size.height = NICE_HEIGHT; } else { scroll_Y = [self isEditable]; size.height = maxY < MIN_HEIGHT ? MIN_HEIGHT : maxY; } if (tFlags.monoFont) { scroll_X = [self isEditable] || (maxX>MAX_WIDTH); [self setNoWrap]; } else { scroll_X = NO; [self setCharWrap:NO]; // Word wrap please } if (maxX > MAX_WIDTH) { size.width = MAX_WIDTH; } else { size.width = maxX < MIN_WIDTH ? MIN_WIDTH : maxX; } // maxX is the length of the longest line. // It only represnts the width of the page // needed if the line is quad left. If the longest line was // centered or flush right, it may be truncated unless we resize // it to fit. if (!scroll_X) { [self sizeTo:size.width:maxY]; [self calcLine]; [self sizeToFit]; // Algorithm found by trial and error. } // Set up the scroll view and window to match: [ScrollView getFrameSize:&scroll_frame.size forContentSize: &size horizScroller: scroll_X vertScroller: scroll_Y borderType: NX_LINE]; [scrollview setVertScrollerRequired:scroll_Y]; [scrollview setHorizScrollerRequired:scroll_X]; // Has the frame size changed? [scrollview getFrame:&old_scroll_frame]; if ( (old_scroll_frame.size.width != scroll_frame.size.width) ||(old_scroll_frame.size.height != scroll_frame.size.height)) { // Now we want to leave the top left corner of the window unmoved: #ifdef OLD_METHOD NXRect oldframe; [window getFrame:&oldframe]; [window sizeWindow:scroll_frame.size.width:scroll_frame.size.height]; [window moveTopLeftTo: oldframe.origin.x : oldframe.origin.y + oldframe.size.height]; #else NXRect newFrame; scroll_frame.origin.x = 150 + (slotNumber % 10) * 30 + ((slotNumber/10)%3)* 40; scroll_frame.origin.y = 185 + NICE_HEIGHT - scroll_frame.size.height - (slotNumber % 10) * 20 - ((slotNumber/10)%3)* 3; [Window getFrameRect:&newFrame forContentRect:&scroll_frame style:NX_TITLEDSTYLE]; // Doesn't allow space for resize bar newFrame.origin.y = newFrame.origin.y - 9.0; newFrame.size.height = newFrame.size.height + 9.0; // For resize bar [window placeWindow:&newFrame]; #endif } #ifdef VERSION_1_STRANGENESS // In version 2, the format of the last run is overwritten with the format // of the preceding run! { NXRect frm; /* Try this to get over "text strangeness" */ [self getFrame:&frm]; [self renewRuns:NULL text:NULL frame:&frm tag:0]; } #endif [window reenableFlushWindow]; [self calcLine]; /* Prevent messy screen */ [window display]; /* Ought to clean it up */ return self; } /* adjustWindow */ // Set up a window in the current application for this hypertext // ------------------------------------------------------------- - setupWindow { NXRect scroll_frame; // Calculated later NXSize min_size = {300.0,200.0}; // Minimum size of text NXSize max_size = {1.0e30,1.0e30}; // Maximum size of text ScrollView * scrollview; NXSize nice_size = { 0.0, NICE_HEIGHT }; // Guess height nice_size.width = page_width(); [ScrollView getFrameSize:&scroll_frame.size forContentSize: &nice_size horizScroller:NO vertScroller:YES borderType: NX_LINE]; { int i; /* Slot address */ for(i=0; (iruns; sor+s->chars<=sp0.cp; sor = sor+((s++)->chars)) ; for (e=s; sor+e->charschars); for(r=s; r<=e; r++) { if (a= (Anchor *)r->info) return a; } if (TRACE) printf("HyperText: No anchor selected.\n"); return nil; } // Public method: Generate an anchor for the selected text // -------------------------------------------------------- // // If the document is not editable, then nil is returned // (unless the user asks for an existing anchor). // - (Anchor *) referenceSelected { Anchor * a; HTStyle * style = HTStyleNew(); a = [self anchorSelected]; if (a) return a; /* User asked for existing one */ if ([self isEditable]) [window setDocEdited:YES]; else return nil; a = [self anchor]; style->anchor = a; [self applyStyle:style]; if (TRACE) printf("HyperText: New dest anchor %i from %i to %i.\n", a, sp0.cp, spN.cp); [delegate textDidChange:self]; return a; } // Generate a live anchor for the text, and link it to a given one // ---------------------------------------------------------------- - (Anchor *) linkSelTo:(Anchor *)anAnchor { Anchor * a; HTStyle * style = HTStyleNew(); if (!anAnchor) return nil; /* Anchor must exist */ if ([self isEditable]) [window setDocEdited:YES]; else return nil; a = [self anchorSelected]; if (!a) { a = [self anchor]; if (TRACE) printf("HyperText: New source anchor %i from %i to %i.\n", a, sp0.cp, spN.cp); } else { [a select]; if (TRACE) printf("HyperText: Existing source anchor %i selected.\n",a); } style->anchor = a; [a linkTo:anAnchor]; // Link it up [self applyStyle:style]; // Will highlight it because linked free(style); return a; } // Purge anchor from selected text // ------------------------------- // // The anchor is left becuase in general we don't delete anchors. // In any case, we would have to check whether all text referencing it // was deleted. // - unlinkSelection { HTStyle * style = HTStyleNew(); if ([self isEditable]) [window setDocEdited:YES]; else return nil; style->anchor = CLEAR_POINTER; [self applyStyle:style]; free(style); return self; } - (Anchor *) referenceAll { return nodeAnchor; // Just return the same one each time } // Select an anchor // ---------------- // // If there are any runs linked to this anchor, we select them. Otherwise, // we just bring the window to the front. - selectAnchor:(Anchor *)anchor { NXRun *s, *e; /* run for start, run for end */ int start, sor; NXRun * limit = (NXRun *)((char *)theRuns->runs + theRuns->chunk.used); for (sor=0, s=theRuns->runs; schars) { if (s->info == (void *)anchor){ start = sor; for (e=s; (e < limit) &&((e+1)->info == (void*)anchor); sor = sor+(e++)->chars); [window makeKeyAndOrderFront: self]; [self setSel:start:sor+e->chars]; return [self scrollSelToVisible]; } } if (TRACE) printf("HT: Anchor has no explicitly related text.\n"); return [window makeKeyAndOrderFront: self]; } // // Return selected link (if any) selectedLink: // ----------------------------- // // This implementation scans down the list of anchors to find the first one // on the list which is at least partially selected. // - selectedLink { int sor; /* Start of run */ NXRun *r, *s, *e; /* Scan, Start and end runs */ Anchor * a; int startPos, endPos; for (sor = 0, s=theRuns->runs; sor+s->chars<=sp0.cp; sor = sor+((s++)->chars)) ; startPos = sor; /* Start of s */ for (e=s; sor+e->charschars); for(r=s, a=nil; r<=e; startPos = startPos + (r++)->chars) { if (a= (Anchor *)r->info) { break; } } if (!a) { if (TRACE) printf("HyperText: No anchor selected.\n"); return nil; } // Extend/reduce selection to entire anchor { endPos = startPos + r->chars; for (s=r; (Anchor *)((s-1)->info) == a; s--) startPos = startPos-(s-1)->chars; for (e=r; (Anchor *)((e+1)->info) == a; e++) endPos = endPos+(e+1)->chars; [self setSel:startPos:endPos]; } return a; } /// Follow selected link (if any) followLink: // ----------------------------- // // Find selected link and follow it - followLink { Anchor * a = [self selectedLink]; if (!a) return a; // No link selected if ([a follow]) return a; // Try to follow link if (TRACE) printf("HyperText: Can't follow anchor.\n"); return a; // ... but we did highlight it. } - setTitle:(const char *)title { return [window setTitle:title]; } // STYLE METHODS // ============= // // Find Unstyled Text // ------------------ // // We have to check whether the paragraph style for each run is one // on the style sheet. // - selectUnstyled: (HTStyleSheet *)sheet { NXRun * r = theRuns->runs; int sor; for (sor=0; sorparaStyle)) { [self setSel:sor:sor+r->chars]; /* Select unstyled run */ return self; } sor = sor+r->chars; } return nil; } // Copy a style into a run // ----------------------- static void apply(HTStyle * style, NXRun * r) { if (style->font) { r->font = style->font; } if (style->paragraph) { r->paraStyle = style->paragraph; } if (style->anchor) { r->info = (style->anchor == CLEAR_POINTER) ? 0 : (style->anchor); } if (style->textGray>=0) r->textGray = style->textGray; r->rFlags.underline = NO; if (r->info) { // r->textGray = 0.166666666; /* Slightly grey - horrid */ if ([(Anchor *)(r->info) destination]) { // r->textGray = NX_DKGRAY; /* Anchor highlighting */ r->rFlags.underline = YES; } } r->rFlags.dummy = (r->info != 0); /* Keep track for typingRun */ if (style->textRGBColor>=0) r->textRGBColor = style->textRGBColor; } // Check whether copying a style into a run will change it // ------------------------------------------------------- static BOOL willChange(HTStyle * style, NXRun *r) { if (r->font != style->font) return YES; if (style->textRGBColor>=0) if (r->textRGBColor != style->textRGBColor) return YES; if (style->textGray>=0) if (r->textGray != style->textGray) return YES; if (style->paragraph) { if (r->paraStyle != style->paragraph) return YES; } if (style->anchor) { if (r->info != style->anchor) return YES; } return NO; } // Update a style // -------------- // // - updateStyle:(HTStyle *)style { NXRun * r = theRuns->runs; int sor; for (sor=0; sorparaStyle == style->paragraph) apply(style, r); sor = sor+r->chars; } [self calcLine]; [window display]; return nil; } // Delete an anchor from this node, without freeing it. // ---------------- // // - disconnectAnchor:(Anchor *)anchor { NXRun * r = theRuns->runs; int sor; for (sor=0; sorinfo == (void *)anchor) r->info = 0; r->textGray = NX_BLACK; sor = sor+r->chars; } [window display]; return nil; } // Find start of paragraph // ----------------------- // // Returns the position of the character after the newline, or 0. // - (int) startOfParagraph: (int) pos { NXTextBlock * block; int sob; unsigned char * p; for(block=firstTextBlock, sob=0; sob+block->chars <=pos; block=block->next) sob = sob + block->chars; for(p=block->text+(pos-sob)-1; p >= block->text; p--) if (*p == '\n') return sob + (p - block->text) +1 ;/* Position of newline */ while (block->prior) { block = block->prior; sob = sob - block->chars; for(p=block->text+(block->chars-1); p>=block->text; p--) if (*p == '\n') return sob + (p-block->text)+1;/* Position of newline */ } return 0; } // Find end of paragraph // ----------------------- // // Returns the position after the newline, or the length of the text. // Note that any number of trailing newline characters are included in // this paragraph .. basically because the text object does not support // the concept of space after or before paragraphs, so extra paragrpah // marks must be used. // - (int) endOfParagraph: (int) pos { NXTextBlock * block; int sob; unsigned char * p; BOOL found_newline = NO; if (pos>=textLength) return textLength; for(block=firstTextBlock, sob=0; sob+block->chars <=pos; block=block->next) sob = sob + block->chars; // Find text block for pos p = block->text+(pos-sob); // Start part way through this one while (block) { for(; p < block->text+block->chars; p++) { if (found_newline) { if (*p != '\n') return sob + (p-block->text); /* Position after newline */ } else { if (*p == '\n') { found_newline = YES; } } } sob = sob + block->chars; /* Move to next block */ block = block->next; if (block) p = block->text; } return textLength; } // Do two runs imply the same format? // ---------------------------------- BOOL run_match(NXRun* r1, NXRun *r2) { return (r1->font == r2->font) && (r1->paraStyle == r2->paraStyle) && (r1->textGray == r2->textGray) && (r1->textRGBColor == r2->textRGBColor) && (r1->superscript == r2->superscript) && (r1->subscript == r2->subscript) && (r1->info == r2->info) && (*(int *)&r1->rFlags == *(int*)&r2->rFlags); } // Check Consecutive runs and merge if necessary // --------------------------------------------- // // If the runs match in EVERY way, they are combined into one, and // all the other runs are shuffled down. // - (BOOL) mergeRun: (NXRun *) run { NXRun * r, *last; if (run_match(run, run+1) ) { if (TRACE) printf("HT: Merging run %i\n", run); run->chars = run->chars +(run+1)->chars; last = ((NXRun *)((char*)theRuns->runs + theRuns->chunk.used))-1; for(r=run+1; rchunk.used = theRuns->chunk.used - sizeof(*r); return YES; } return NO; } // Apply style to a given region // ----------------------------- // // Note that one should not have two consecutive runs of the same style, // nor any zero length runs. We have a little calculation, therefore, // in order to work out how many runs will eventually be needed: // this may be more or less than we started with. // Remember that appling a style to a run may or may not change it. // // PS: Actually, we notice that text insertion does leave two consecutive // runs the same in the Text object, but deletion cleans up. - applyStyle:(HTStyle *)style from:(int)start to:(int)end { int pos; /* Character position within text */ int increase; /* Number of runs to be split */ int new_used; /* New number of bytes in runs */ BOOL need_run_before, need_run_after;/* Sometimes we don't need them */ int run_before_start, run_after_end;/* Start of run_before etc */ NXRun * s, *e; /* Start and end run */ NXRun *p; /* Pointer to run being read */ NXRun *w; /* Pointer to run being written */ NXRun *r; /* Pointer to end of runs */ if (start == end) { apply(style, &typingRun); /* Will this work? */ if (TRACE) printf("Style applied to typing run\n"); return nil; /* Can't operate on nothing */ } // First we determine in which runs the first and last characters to // be changed lie. for (pos=0, s=theRuns->runs; pos+s->chars<=start; pos = pos+((s++)->chars)) /*loop*/; /* s points to run containing char after selection start */ run_before_start = pos; for (e=s; pos+e->chars < end; pos = pos+((e++)->chars)) ; /* Find end run */ /* e points to run containing character before selection end */ run_after_end = pos+e->chars; r = (NXRun *) (((char *)(theRuns->runs)) +theRuns->chunk.used); /* The end*/ if (TRACE) { printf("Runs: used=%i, elt. size=%i, %i elts, total=%i\n", theRuns->chunk.used, sizeof(*r), r - theRuns->runs, (r-theRuns->runs)*sizeof(*r) ); printf(" runs at %i, r=%i. textLength:%i, r ends at:%i\n", theRuns->runs, r, textLength, pos); } // Move up runs as necessary in order to make room for the splitting // of the start and end runs into two. We only do this if necessary. if (!willChange(style, s)) start = run_before_start; /* No run before is needed now */ need_run_before = (start>run_before_start); if (!willChange(style, e)) end = run_after_end; /* No run after is needed now */ need_run_after = (end < run_after_end); if (TRACE) printf( "Run s=%i, starts at %i; changing (%i,%i); Run e=%i ends at %i\n", s-theRuns->runs, run_before_start, start, end, e-theRuns->runs, run_after_end); increase = need_run_after + need_run_before; if (increase) { new_used = theRuns->chunk.used + increase*sizeof(*r); if (new_used> theRuns->chunk.allocated) { NXRun* old = theRuns->runs; theRuns = (NXRunArray*)NXChunkGrow(&theRuns->chunk, new_used); if (theRuns->runs !=old) { /* Move pointers */ if (TRACE) printf("HT:Apply style: moving runs!\n"); e = theRuns->runs + (e-old); r = theRuns->runs + (r-old); s = theRuns->runs + (s-old); } } for (p=r-1; p>=e; p--) p[increase] = p[0]; /* Move up the runs after */ r = r+increase; /* Point to after them 910212*/ /* p = e-1 */ if (need_run_after) { e = e + increase-1; /* Point last to be changed */ e[0] = e[1]; /* Copy the last run */ e[1].chars = run_after_end - end; e[0].chars = e[0].chars - e[1].chars; /* Split the run into two */ } if (need_run_before) { for(;p>=s; p--) p[1] = p[0]; /* Move runs up, copying 1st*/ s[0].chars = start - run_before_start; /* Split the run into two */ if (need_run_after && (s+1==e)) { /* If only one middle run */ s[1].chars = end-start; /* The run we need */ } else { s[1].chars = s[1].chars - s[0].chars; /* The remainder */ } s++; /* Move on to point to first run to be changed */ if (!need_run_after) e++; /* First to be changed */ } theRuns->chunk.used = new_used; } /* end if increase */ // We consider the bit of text which is to be styled, s thru e. // We scan through, first, applying the style, until we find two runs which // need to be merged. p=s; if (p==theRuns->runs) { apply(style, p++); /* Don't merge with run -1! */ } for(; p<=e; p++) { apply(style, p); if (run_match(p,p-1)) { break; } } // Once we have merged two runs, we have to copy the rest of them across, // merging others as necessary. w = p-1; /* w now points to last written run */ for(;p<=e; p++) { apply(style, p); if (run_match(p,w)) { w->chars = w->chars+p->chars; /* Combine with w */ } else { w++; /* or skip */ *w = *p; /* and keep a copy */ } } // Now, is any runs were merged, we have to copy the rest of the runs down // and decrease the size of the chunk. w++; /* Point to next to be written */ if (wchunk.used = (char*)w - (char*)theRuns->runs; } [self calcLine]; /* Update line breaks */ return [window display]; /* Update window */ } // Apply a style to the selection // ------------------------------ // // If the style is a paragraph style, the // selection is extended to encompass a number of paragraphs. // - applyStyle:(HTStyle *)style { int start, end; if (TRACE) printf("Applying style %i to (%i,%i)\n", style, sp0.cp, spN.cp); if (sp0.cp<0) { /* No selection */ return [self applyStyle:style from:0 to:0]; /* Apply to typing run */ } if (!style) return nil; if ([self isEditable]) [window setDocEdited:YES]; else return nil; start = sp0.cp; end = spN.cp; if (style->paragraph) { /* Extend to an integral number of paras. */ start = [self startOfParagraph:start]; end = [self endOfParagraph:end]; } return [self applyStyle:style from:start to:end]; } // Apply style to all similar text // ------------------------------- // // - applyToSimilar:(HTStyle*)style { NXRun * r = theRuns->runs; int sor; NXRun old_run; for (sor = 0; sor<=sp0.cp; sor = sor+((r++)->chars)) ;/* Find run after */ old_run = *(r-1); /* Point to run for start of selection */ if (TRACE) printf( "Applying style %i to unstyled text similar to (%i,%i)\n", style, sp0.cp, spN.cp); for(r=theRuns->runs; (char*)r-(char*)theRuns->runs < theRuns->chunk.used; r++) { if (r->paraStyle == old_run.paraStyle) { if(TRACE) printf(" Applying to run %i\n", r); apply(style,r); if (r!=theRuns->runs){ if ([self mergeRun:r-1]) r--; /* Do again if shuffled down */ } } } [self calcLine]; [window display]; return self; } // Pick up the style of the selection // ---------------------------------- - (HTStyle *) selectionStyle:(HTStyleSheet *) sheet { NXRun * r = theRuns->runs; int sor; for (sor = 0; sor<=sp0.cp; sor = sor+((r++)->chars)) ;/* Find run after */ r--; /* Run for start of selection */ return HTStyleForRun(sheet, r); /* for start of selection */ } // Another replaceSel method, this time using styles: // ------------------------------------------------- // // The style is as given, or where that is not defined, as the // current style of the selection. - replaceSel: (const char *)aString style:(HTStyle*)aStyle { NXRun * r = theRuns->runs; int sor; NXRunArray newRuns; for (sor = 0; sor<=sp0.cp; sor = sor+((r++)->chars)) ;/* Find run after */ r--; /* Run for start of selection */ newRuns.runs[0] = *r; /* Copy it */ newRuns.chunk.used = sizeof(*r); /* 1 run used */ apply(aStyle, newRuns.runs); /* change it */ newRuns.runs->chars = strlen(aString); /* Match the size to the string */ return [self replaceSel:aString length:newRuns.runs->chars runs:&newRuns]; } // Read in as Plain Text readText: // --------------------- // // This method overrides the method of Text, so as to force a plain text // hypertext to be monofont and fixed width. Also, the window is updated. // - readText: (NXStream *)stream { // [self setMonoFont:YES]; Seems to leave it in a strange state [self setHorizResizable:YES]; [self setNoWrap]; [self setFont:[Font newFont:"Ohlfs" size:10.0]]; // @@ Should be XMP [super readText:stream]; format = WWW_PLAINTEXT; // Remember #ifdef NOPE { NXRect frm; /* Try this to get over "text strangeness" */ [self getFrame:&frm]; /* on plain text only Aug 91 */ [self renewRuns:NULL text:NULL frame:&frm tag:0]; } #endif [self adjustWindow]; return self; } // Read in as Rich Text readRichText: // --------------------- // // This method overrides the method of Text, so as to force a plain text // hypertext to be monofont and fixed width. Also, the window is updated. // - readRichText: (NXStream *)stream { id status = [super readRichText:stream]; [self adjustWindow]; format = WWW_RICHTEXT; // Remember return status; } // Window Delegate Methods // ======================= // Prevent closure of edited window without save // - windowWillClose:sender { int choice; if (![window isDocEdited]) return self; choice = NXRunAlertPanel("Close", "Save changes to `%s'?", "Yes", "No", "Don't close", [window title]); if (choice == NX_ALERTALTERNATE) return self; if (choice == NX_ALERTOTHER) return nil; return [server saveNode:self]; } // Change configuration as window becomes key window // - windowDidBecomeMain:sender { return [delegate hyperTextDidBecomeMain:self]; } /* FORMAT CONVERSION FROM SGML ** =========================== ** ** As much as possible, this is written in C for portability. It is in a separate ** include file which could be used elsewhere. */ /* Input procedure for printing a trace as we go */ #define NEXT_CHAR NXGetc(sgmlStream) #define BACK_UP NXUngetc(sgmlStream) /* Globals for using many subroutines within a method */ static NXStream *sgmlStream; static HyperText * HT; /* Pointer to self for C */ // Inputting from the text object: // ------------------------------ static unsigned char * read_pointer; /* next character to be read */ static unsigned char * read_limit; static NXTextBlock * read_block; void start_input() { read_block = HT->firstTextBlock; read_pointer = read_block->text; /* next character to be read */ read_limit = read_pointer+read_block->chars; } unsigned char next_input_block() { char c = *read_pointer; read_block = read_block->next; if (!read_block) read_block = HT->firstTextBlock; /* @@@ FUDGE */ read_pointer = read_block->text; read_limit = read_pointer + read_block->chars; return c; } #define START_INPUT start_input() #define NEXT_TEXT_CHAR (read_pointer+1==read_limit? next_input_block():*read_pointer++) // Outputting to the text object // ============================= // // These macros are used by the parse routines // #define BLOCK_SIZE NX_TEXTPER /* Match what Text seems to use */ static NXTextBlock *write_block; /* Pointer to block being filled */ static unsigned char *write_pointer; /* Pointer to next characetr to be written */ static unsigned char *write_limit; /* Pointer to the end of the allocated area*/ static NXRun * lastRun; /* Pointer to the run being appended to */ static int original_length; /* of text */ #define OUTPUT(c) { *write_pointer++ = (c); \ if (write_pointer==write_limit) {end_output(); append_start_block(); }} #define OUTPUTS(string) {const char * p; for(p=(string);*p;p++) OUTPUT(*p);} #define START_OUTPUT append_begin() #define FINISH_OUTPUT finish_output() #define LOADPLAINTEXT loadPlainText() #define SET_STYLE(s) set_style(s) // Allocate a text block to accumulate text // // Bugs: // It might seem logical to set the "malloced" bit to 1, because the text block // has been allocted with malloc(). However, this crashes the program as at the // next edit of the text, the text object frees the block while still using it. // Chaos results, sometimes corrupting the stack and/or looping for ages. @@ // We therefore set it to zero! (This might have been something else -TBL) // void append_start_block() { NXTextBlock *previous_block=write_block; /* to previous write block */ if (TRACE)printf(" Starting to append new block.\n"); lastRun = ((NXRun*) ((char*)HT->theRuns->runs + HT->theRuns->chunk.used))-1; write_block = (NXTextBlock*)malloc(sizeof(*write_block)); write_block->tbFlags.malloced=0; /* See comment above */ write_block->text = (unsigned char *)malloc(BLOCK_SIZE); write_block->chars = 0; // For completeness: not used. write_pointer = write_block->text; write_limit = write_pointer + BLOCK_SIZE; // Add the block into the linked list after previous block: write_block->prior = previous_block; write_block->next = previous_block->next; if (write_block->next) write_block->next->prior = write_block; else HT->lastTextBlock = write_block; previous_block->next = write_block; } // Start the output process altogether // void append_begin() { if (TRACE)printf("Begin append to text.\n"); [HT setText:""]; // Delete everything there original_length = HT->textLength; if (TRACE) printf("Text now contains %i characters\n", original_length); lastRun = ((NXRun*) ((char*)HT->theRuns->runs + HT->theRuns->chunk.used))-1; // Use the last existing text block: write_block = HT->lastTextBlock; // It seems that the Text object doesn't like to be empty: it always wants to // have a newline in at leats. However, we need it seriously empty and so we // forcible empty it. CalcLine will crash if called with it in this state. if (original_length==1) { if (TRACE) printf("HT: Clearing out single character from Text.\n"); lastRun->chars = 0; /* Empty the run */ write_block->chars = 0; /* Empty the text block */ HT->textLength = 0; /* Empty the whole Text object */ original_length = 0; /* Note we have cleared it */ } write_pointer = write_block->text+write_block->chars; write_limit = write_pointer + BLOCK_SIZE; } // Set a style for new text // void set_style(HTStyle *style) { if (!style) { if (TRACE) printf("set_style: style is null!\n"); return; } if (TRACE) printf(" Changing to style `%s' -- %s change.\n", style->name, willChange(style, lastRun) ? "will" : "won't"); if (willChange(style, lastRun)) { int size = (write_pointer - write_block->text); lastRun->chars = lastRun->chars + size - write_block->chars; write_block->chars = size; if (lastRun->chars) { int new_used = (((char *)(lastRun+2)) - (char*)HT->theRuns->runs); if (new_used > HT->theRuns->chunk.allocated) { if (TRACE) printf(" HT: Extending runs.\n"); HT->theRuns = (NXRunArray*)NXChunkGrow( &HT->theRuns->chunk, new_used); lastRun = ((NXRun*) ((char*)HT->theRuns->runs + HT->theRuns->chunk.used))-1; } lastRun[1]=lastRun[0]; lastRun++; HT->theRuns->chunk.used = new_used; } apply(style, lastRun); lastRun->chars = 0; /* For now */ } } // Transfer text to date to the Text object // ---------------------------------------- void end_output() { int size = (write_pointer - write_block->text); if (TRACE)printf( " HT: Adding block of %i characters, starts: `%.20s...'\n", size, write_block->text); lastRun->chars = lastRun->chars + size - write_block->chars; write_block->chars = size; HT->textLength = HT->textLength + size; } // Finish altogether // ----------------- void finish_output() { int size = write_pointer - write_block->text; if (size==0) { HT->lastTextBlock = write_block->prior; /* Remove empty text block */ write_block->prior->next = 0; free(write_block->text); free(write_block); } else { end_output(); } // get rid of zero length run if any if (lastRun->chars==0) { /* Chop off last run */ HT->theRuns->chunk.used= (char*)lastRun - (char*)HT->theRuns; } // calcLine requires that the last character be a newline! { unsigned char * p = HT->lastTextBlock->text + HT->lastTextBlock->chars - 1; if (*p != '\n') { if (TRACE) printf( "HT: Warning: Last character was %i not newline: overwriting!\n", *p); *p = '\n'; } } [HT adjustWindow]; /* Adjustscrollers and window size */ } // Loading plain text void loadPlainText() { [HT setMonoFont:YES]; [HT setHorizResizable:YES]; [HT setNoWrap]; [HT readText:sgmlStream]; /* will read to end */ [HT adjustWindow]; /* Fix scrollers */ } // Methods enabling an external parser to add styled data // ------------------------------------------------------ // // These use the macros above in the same way as a built-in parser would. // - appendBegin { HT = self; START_OUTPUT; return self; } - appendStyle:(HTStyle *) style { SET_STYLE(style); return self; } - appendText: (const char *)text { OUTPUTS(text); return self; } - appendEnd { FINISH_OUTPUT; return self; } // Begin an anchor - (Anchor *)appendBeginAnchor: (const char *)name to:(const char *)reference { HTStyle * style = HTStyleNew(); char * parsed_address; Anchor * a = *name ? [Anchor newParent:nodeAnchor tag:name] : [self anchor]; style->anchor = a; [(Anchor *)style->anchor isLastChild]; /* Put in correct order */ if (*reference) { /* Link only if href */ parsed_address = HTParse(reference, [nodeAnchor address], PARSE_ALL); [(Anchor *)(style->anchor) linkTo: [Anchor newAddress:parsed_address]]; free(parsed_address); } SET_STYLE(style); /* Start anchor here */ free(style); return a; } - appendEndAnchor // End it { HTStyle * style = HTStyleNew(); style->anchor = CLEAR_POINTER; SET_STYLE(style); /* End anchor here */ free(style); return self; } // Reading from a NeXT stream // -------------------------- #define END_OF_FILE NXAtEOS(sgmlStream) #define NEXT_CHAR NXGetc(sgmlStream) #define BACK_UP NXUngetc(sgmlStream) #include "ParseHTML.h" // Methods overriding Text methods // =============================== // // Respond to mouse events // ----------------------- // // The first click will have set the selection point. On the second click, // we follow a link if possible, otherwise we allow Text to select a word as usual. // - mouseDown:(NXEvent*)theEvent { if (theEvent->data.mouse.click != 2) return [super mouseDown:theEvent]; if (![self followLink]) return [super mouseDown:theEvent]; return self; } // The following are necessary to undo damage done by the Text object // in version 2.0 of NeXTStep. For some reason, iff the "info" is // nonzero, text typed in is given // a different copy of the typingRun parastyle, and zero anchor info. // Otherwise, the current run is broken in two at the insertion point, // but no changes made to the run contents. // The problem with simply repairing is that many runs will be made inside // an anchor. // We have to use a "dummy" flag to mean "This has an anchor: be careful!" // This is horrible. - keyDown:(NXEvent*)theEvent #ifdef TRY1 { id result; NXTextStyle *typingPara = typingRun.paraStyle; int originalLength = textLength; int originalStart = sp0.cp; int originalEnd = spN.cp; result = [super keyDown:theEvent]; { int inserted = originalEnd-originalStart + textLength-originalLength; if (TRACE) printf( "KeyDown, size(sel) %i (%i-%i)before, %i (%i-%i)after.\n", originalLength, originalStart, originalEnd, textLength, sp0.cp, spN.cp); if (inserted>0) { NXRun * s; int pos; int start = sp0.cp-inserted; for (pos=0, s=theRuns->runs; pos+s->chars<=start; pos = pos+((s++)->chars)) /*loop*/; // s points to run containing first char of insertion if (pos!=start) printf( "HT: Strange: inserted %i at %i, start of run=%i !!\n", inserted, start, pos); if (s > theRuns->runs) { /* ie s-1 is valid */ s->paraStyle = typingPara; /* Repair damage to runs */ /* What about freeing the old paragraph style? @@ */ s->info = (s-1)->info; s->rFlags.dummy = 1; /* Pass on flag */ } } } return result; } #else // The typingRun field does not seem to reliably reflect the // format which would be appropriate if typing were to occur. // We have to use our own. { NXRun run; { NXRun * s; /* To point to run BEFORE selection */ int pos; /* If there is a nonzero selection, take the run containing the ** first character. If the selection is empty, take the run containing the ** character before the selection. */ if (sp0.cp == spN.cp) { for (pos=0, s=theRuns->runs; pos+s->charschars)) /*loop*/; } else { for (pos=0, s=theRuns->runs; pos+s->chars<=sp0.cp; /* First ch */ pos = pos+((s++)->chars)) /*loop*/; } /* Check our understanding */ if (typingRun.paraStyle != 0) { if (typingRun.paraStyle != s->paraStyle) printf("WWW: Strange: Typing run has bad style.\n"); if ((s->info != 0) && (typingRun.info != s->info)) printf( "WWW: Strange: Typing run has bad anchor info.\n"); } typingRun = *s; /* Copy run to be used for insertion */ run = *s; /* save a copy */ } if (!run.rFlags.dummy) return [super keyDown:theEvent]; // OK! { id result; int originalLength = textLength; int originalStart = sp0.cp; int originalEnd = spN.cp; result = [super keyDown:theEvent]; /* Does it really change? YES! */ if (TRACE) { if (typingRun.info != run.info) printf( "Typing run info was %p, now %p !!\n", run.info, typingRun.info); if (typingRun.paraStyle != run.paraStyle) printf( "Typing run paraStyle was %p, now %p !!\n", run.paraStyle, typingRun.paraStyle); } /* Patch the new run if necessary: */ { int inserted = originalEnd-originalStart + textLength-originalLength; if (TRACE) printf( "KeyDown, size(sel) %i (%i-%i)before, %i (%i-%i)after.\n", originalLength, originalStart, originalEnd, textLength, sp0.cp, spN.cp); if (inserted>0) { NXRun * s; int pos; int start = sp0.cp-inserted; for (pos=0, s=theRuns->runs; pos+s->chars<=start; pos = pos+((s++)->chars)) /*loop*/; // s points to run containing first char of insertion if (pos!=start) { /* insert in middle of run */ if (TRACE) printf( "HT: Inserted %i at %i, in run starting at=%i\n", inserted, start, pos); } else { /* inserted stuff starts run */ if (TRACE) printf ("Patching info from %d to %d\n", s->info, run.info); s->info = run.info; s->paraStyle = run.paraStyle; /* free old one? */ s->rFlags.dummy = 1; } } /* if inserted>0 */ } /* block */ return result; } } #endif // After paste, determine paragraph styles for pasted material: // ------------------------------------------------------------ - paste:sender; { id result; int originalLength = textLength; int originalStart = sp0.cp; int originalEnd = spN.cp; Anchor * typingInfo; result = [super paste:sender]; // Do the paste { int inserted = originalEnd-originalStart + textLength-originalLength; if (TRACE) printf( "Paste, size(sel) %i (%i-%i)before, %i (%i-%i)after.\n", originalLength, originalStart, originalEnd, textLength, sp0.cp, spN.cp); if (inserted>0) { NXRun *s, *r; int pos; int start = sp0.cp-inserted; for (pos=0, s=theRuns->runs; pos+s->chars<=start; pos = pos+((s++)->chars)) /*loop*/; // s points to run containing first char of insertion if (pos!=sp0.cp-inserted) printf("HT paste: Strange: insert@%i != run@%i !!\n", start, pos); if (s > theRuns->runs) typingInfo = (s-1)->info; else typingInfo = 0; for (r=s; pos+r->charschars) { r->paraStyle = HTStyleForRun(styleSheet, r)->paragraph; r->info = typingInfo; } } } return result; } @end Runs; } // calcLine requires that the last character be a newline! { unsigned char * p = HT->lastTextBlock->text + HT->laWWW/NextStep/Implementation/TextToy.h 644 472 105 1163 5062671204 13147 /* Generated by Interface Builder */ #import #import #import "Anchor.h" #import "HyperAccess.h" @interface TextToy:Object { id SearchWindow; Anchor *StartAnchor; Anchor *EndAnchor; List * accesses; } - setSearchWindow:anObject; #ifdef JUNK - Do_getStartLength:sender; - Do_getSel:sender; - Do_setBackgroundGray:sender; #endif /* Link handling: */ - linkToMark: sender; - linkToNew: sender; - unlink:sender; - markSelected: sender; - markAll: sender; - followLink: sender; - dump: sender; // Access manager function: - registerAccess:(HyperAccess *)access; @end tcp.h8WWWPageLayout.m8  TcpAccess.h8!HTFTP.c8" icon2.tiff7HyperManager.h8$WWWPageLayout.h8% Makefile.dependencies8& HTAccess.h8' HTParse.c8( HTParse.h8) HTStyle.h84 WorldWideWeb.iconheader8+HTFTP.h8, HTStyle.m8- NewsAccess.h8.WWW.h8/HTTP.c8  StyleToy.hWWW/NextStep/Implementation/TextToy.m 644 472 105 3371 5304636230 13156 // Text Management Module TextToy.m // ---------------------- // This file allows one to create links between Hypertexts. It selects the // current HyperText and then passes the buck to the HyperText class. #import "TextToy.h" #import #import "Anchor.h" #import "HyperText.h" #import #import "HTUtils.h" @implementation TextToy #define THIS_TEXT (HyperText *)[[[NXApp mainWindow] contentView] docView] Anchor * Mark; /* A marked Anchor */ - setSearchWindow:anObject { SearchWindow = anObject; return self; } /* Action Methods ** ============== */ /* Set up the start and end of a link */ - linkToMark:sender { return [THIS_TEXT linkSelTo:Mark]; } - linkToNew:sender { return nil; } - unlink:sender; { return [THIS_TEXT unlinkSelection]; } - markSelected:sender { Mark = [THIS_TEXT referenceSelected]; return Mark; } - markAll:sender { Mark = [THIS_TEXT referenceAll]; return Mark; } - followLink:sender { return [THIS_TEXT followLink]; // never mind whether there is a link } - dump : sender { return [THIS_TEXT dump:sender]; } // Window Delegate Functions // ------------------------- - windowDidBecomeKey:window { return self; } // When a document is selected, turn the index search on or off as // appropriate - windowDidBecomeMain:window { HyperText * HT = [[window contentView] docView]; if (!HT) return self; if ([HT isIndex]) { [SearchWindow makeKeyAndOrderFront:self]; } else { [SearchWindow orderOut:self]; } return self; } // Access Management functions // =========================== - registerAccess:(HyperAccess *)access { if (!accesses) accesses=[List new]; return [accesses addObject:access]; } @end ink:sender; { return [THIS_TEXT unlinkSelection]; } - markSelected:sender { Mark = [THIS_TEXT referenceSelected]; return Mark; } - markAll:sender { Mark = [THIS_TEXT referenceAll]; return Mark; } - followLink:sender { return [THIS_TEXT fWWW/NextStep/Implementation/icon1.tiff 644 472 105 3652 5304426473 13253 MM*   UUUUUUUU/j ꪪj *jjUU ꪪ ? />333}" "ꪪ ꪪ eY[ zꮮ목"je3z [庪ꪪiUjbVfjjUVjjUfUjjfjjUZjUjUjjjjUUUUUUUU    ?   ? ??333303   330  >7(Rxpp ' ') && (typingRun.info != s->info)) printf( "WWW: Strange: Typing ruWWW/NextStep/Implementation/WorldWideWeb.nib/ 755 472 105 0 5311410312 14361 WWW/NextStep/Implementation/WorldWideWeb.nib/data.classes 644 472 105 5040 5311407557 16756 RexecAccess = { SUPERCLASS = HyperAccess; }; TextToy = { ACTIONS = { followLink:; Do_setBackgroundGray:; markSelected:; linkToNew:; linkToMark:; Do_getSel:; unlink:; Do_getStartLength:; markAll:; dump:; }; OUTLETS = { SearchWindow; }; SUPERCLASS = Object; }; FirstResponder = { ACTIONS = { }; SUPERCLASS = Object; }; StyleToy = { ACTIONS = { heading5Button:; FindUnstyledButton:; listButton:; heading2Button:; ApplyButton:; NextButton:; PreviousButton:; saveAs:; SetButton:; heading4Button:; heading1Button:; heading3Button:; normalButton:; PickButton:; ApplyToSimilar:; glossaryButton:; addressButton:; exampleButton:; heading6Button:; open:; }; OUTLETS = { TabForm; ParameterForm; StylePanel; NameForm; }; SUPERCLASS = Object; }; FileAccess = { ACTIONS = { linkToNew:; saveAsPlainText:; saveAs:; makeNew:; saveAsRichText:; linkToFile:; }; SUPERCLASS = HyperAccess; }; NewsAccess = { SUPERCLASS = HyperAccess; }; WWWPageLayoutJunk = { SUPERCLASS = Object; }; TcpAccess = { SUPERCLASS = HyperAccess; }; HyperText = { ACTIONS = { followLink:; }; SUPERCLASS = Text; }; WWWPageLayout = { ACTIONS = { pickedUnits:; }; OUTLETS = { bottomMargin; leftMargin; sideForm; topMargin; rightMargin; topBotForm; }; SUPERCLASS = PageLayout; }; Anchor = { OUTLETS = { OutgoingReference; DestAnchor; Node; }; SUPERCLASS = Object; }; DrawPageLayout = { ACTIONS = { pickedUnits:; }; OUTLETS = { topMargin; leftMargin; bottomMargin; rightMargin; }; SUPERCLASS = PageLayout; }; HyperAccess = { ACTIONS = { search:; openRTF:; searchRTF:; openSGML:; searchSGML:; open:; hyperTextDidBecomeMain:; }; OUTLETS = { keywords; manager; contentSearch; openString; titleString; addressString; }; SUPERCLASS = Object; }; HyperManager = { ACTIONS = { print:; help:; next:; save:; traceOn:; previous:; goToBlank:; linkToString:; copyAddress:; inspectLink:; back:; traceOff:; setTitle:; runPagelayout:; goHome:; closeOthers:; saveAll:; windowDidBecomeKey:; }; OUTLETS = { }; SUPERCLASS = HyperAccess; }; Browser = { SUPERCLASS = Application; }; PageLayout = { ACTIONS = { pickedPaperSize:; pickedUnits:; pickedButton:; pickedLayout:; pickedOrientation:; }; OUTLETS = { ok; unitsList; paperView; paperSizeList; scale; orientation; cancel; height; accessoryView; layoutList; appIcon; width; }; SUPERCLASS = Panel; }; = run.paraStyle) printf( "Typing run paraStyle was %p, now %p !!\n", run.paraStyle, typingRun.paraStyle); } /* Patch the new run if necessary: */ { int inserted = originalEnd-originalStart + textLength-originalLength; if (TRACE) printf( "KeyDown, size(sel) %i (%i-%i)before, %i (%i-%i)after.\n", originalLength, originalStart, originalEnd, textLength, sp0.cp, spN.cp); if (inserted>0) { NXRun * s; int pos; int start = sp0.cp-insWWW/NextStep/Implementation/WorldWideWeb.nib/data.nib 644 472 105 53222 5311407557 16116  typedstream@ IBObjectDataObject@i CustomObject*@ Application@@ButtonControlView ResponderfffffفSفS@ss@Listi [13@]FormMatrix-- i@sFormCell ActionCellCell*@ssFont%fss Helvetica 0i:f@!Title:@:@iiii[1@]ff-ff@@#::s?*0!Field:cRR ButtonCellSetss NXImages*p NXreturnSign TextField?ʁ$$ TextFieldCell0?*::: Address:G  8: Address : ?*8:7 Other document Link selection to other document@f .______________________________________________́e))CopyŨ?}}Inspect selected linkŨBox< 99ڞ""ڒ@@sDiagnostics only݁LOpen showing sourceŨ@r`$`$OpenHelvetica-Bold MenuCellPaste vqȟqȒ@ [10@]窄Cutx窄c窄Paste As& MenuTemplate󒅴+*@*@ccc WorldWideWebuu 窄InfozՄׄG(G([2@]窄Panel...窄Help...G煅MenusubmenuAction:p NXmenuArrow窄Navigate~ՄބRdRd@[5@]窄Back^窄Next>窄Previous<窄HomeH窄R煅ۅܺ窄Documentz?Մ䄢ҁҁ𒅒 [12@]窄 Open file...o窄Open given document addressO窄 New file...n窄Saves窄Save all edited windowsS窄Save a copy inMՄ넢PP[4@]窄HyperText Markup Language...窄Rich Text Format ...窄 Plain Text...窄 PostScript...煅ۅܺ)窄 Inspect ...3窄 (Diagnostics)]Մ򄢒@[7@]窄Panel窄"Open HTML file showing SGML source窄Open HTML file showing RTF窄 Dump text format to standard out?窄Turn console trace on+窄Turn console trace off-窄Item煅ۅܺ:窄 Miniaturize窄Open master template document窄Close all other windowsW窄Closew 煅ۅܺ窄Editܺ窄FindzՄdd߄窄 Find Panel...&f窄 Find Next&g窄 Find Previous&d窄Enter Selection&e窄Jump to Selection&j煅ۅܺZ窄Links~Մrr@[8@]窄 Mark all A窄Mark selection M窄Link to markedL窄Link to file...窄 Link to NewN窄 Follow linkj窄UnlinkZ窄Helpr煅ۅܺk窄Stylez?Մ[[𒅒@ 窄 Copy style1窄 Apply style2窄Glossary窄窄Example窄Address窄Normal窄 Heading 1!窄 Heading 2@窄 Heading 3#窄 Heading 4$窄 [煅ۅܺ窄Print...p窄Page layout...窄WindowswՄ<<@[3@]窄Arrange in Front窄Miniaturize Windowm窄 Close Windoww煅ۅܺ窄ServicesXՄ"RR@R煅ۅܺ窄Hideh窄Quitq u煅ۅ~dՄۅՄӄRR@R煅ۅܺ窄Delete窄Undo&z窄]Մdd@߄窄&f窄&g窄&d窄&e窄Jump To Selection&j煅ۅܺ窄 Spelling...窄Check Spelling;窄 Select Alla q煅؁؁ޞzޒHyperMedia Browser/Editoŕޞ{]]ޒ Version 0.15 Beta only >ޞL]ޒby Tim Berners-Leeޞ H  ޒUCopyright 1990,91, CERN. Distribution restricted: ask for terms. TEST VERSION ONLY?*>ےޞXޒʄɨ݄ޞ$$ޒHyperText: Text which is not constrained to be linear. HyperMedia: Information which is not constrained linear... or to be text. ScrollViewޞ璁ޒ؄ClipView㒁TextJJ `J ciifffcfffsJ>UqIʃqIʴ[1161c]{\rtf0\ansi{\fonttbl\f0\fswiss Helvetica;} \margl40 \margr40 {\colortbl;\red0\green0\blue0;} \pard\tx533\tx1067\tx1601\tx2135\tx2668\tx3202\tx3736\tx4270\tx4803\tx5337\f0\b0\i0\ulnone\fs24\fc1\cf1 This version of the WWW application can pick up hypertext infomation from files in a number of formats, from local files, from remote files using NFS or anonymous FTP, from hypertext servers by name or text search, and from internet news. Hypertext files may be editied, and links made from hypertext files to other files or any other information.\ \ This was the original prototype for the World-Wide Web. Many browers for other platforms now exist. Read the web for details.\ \ You should configure the newsreader code in this application to klnow where your local news (NNTP) srever is. Type in a terminal window\ \ dwrite WorldWideWeb NewsHost xxxx\ \ where xxxx is replaced by the intrenet name of your news server.\ \ For more help, use "Help" from the menu. If that doesn't work, then your application has been incompletely installed.\ \ If you have any comments or have bugs, please mail www-bug@info.cern.ch. quoting the version number (above). } J@fsNXCursors@pNXibeamScroller㒁ff:s?0ӄ _doScroller:J@@@ffs yo$$ޒ/An excercise in global information availabilityޙwmےفف߁A   8:¨  ?*8:n`$`$ ̨ŨfffLink selection toŨ@l99ʄ˨"" Plain TextH8pNXradiopNXradioHے؁(ے)DD)+--+؄-]-]-- ؄0&Left:0&Right:]?*0&-`g-g-- ؄00Top:00Bottom:g?*00ʄMargins on Paper -@ VGG)'ʄM'99(؁&'nn' Save file as:́?*mO5O5'  HyperText<"$ Rich TextH8"$ VO?*RadioH8"$B3+ꙄےVV [11@]^%((^@Apply style to selectionMŨ^^@Find unstyled textŨ^^@Apply style to all similar textŨ^xVii^@style of selectionŨ^V88^@>>Ũ^8V88^@<<Ũ^v^ @0H Style name:s?*0Hے^ЁhPhP^|Z9Z9|ʄ Style sheet~^HH^@ͨŨ^HH^@ Save as...Ũ]@DџDђ^\ʄFormat\66]\;\ ߄0pFirst line indent:0pSuccessive indent:0pFont:0p Point Size:0p SGML tag:?*0p\1-1-\ 0$Tabs:1-?*0$\;HH\@' ]M/+d\=-<:ޙQ#^\ZXѝ\ ʁșΝV1/ޙ+)ꙁЁʙޙ?=i)'WindowTemplateN iiii***@s@ AccessoriesWindow)`x\ם%x]Using the navigation Commands􄛒xx [[ @The navigation buttons allow you to navigate through the Hypertext web as a tree. The "Backup" button takes you back the way you came, to the text which you selected to get to where you are. You can use it repeatedly to retrace your steps back to the first link you followed. Using the "Next" button is like using "BackUp" and then selecting the next refernece after the one you took. For example, if you selected one of a list of references, then using "Next" will take you to the next of those. The "Previous" button works in a similar way to "Next", but goes to the previous reference. The "Home" button reselects the original page which you get when you started the browser in the first place.?*@`x^R'-+VsmymStyleToyUU߄Ğ)G&G&Ē@ < PreviousŨĞ 88Ē@㨁ŨĞR)9&9&Ē@Back upŨÄĞ00Ē@Ũ@)8&8&Ē@Next >ŨęO NewsAccessڝ:8xx㒅֞3EE֒ @You can just create a new hypertext document with Command/n, but it's better to make a reference to it and link that reference to it as you make it, using Command/N. This ensures that you can always find the document again. Go to your personal notebook, for instance, and add some text describing the new thing you are going to create. Then select that text and use "Link to New" . Later on, you can add and delete links so that your new node is linked to other things to which it is relevant.?*֞] QQ֒@ Making linksŨ֞::֒ @ See also:?*@(x]Making a new Documentց`xkiꙁ'(  ~|F<`\f\ބQS؁]ׄށ`xr^D<^V] Style editor^`xᕙPP؁턙ek!k!Search @+ 0*words:􆅅?*0*ꙁށ֙&'ĄLU] Navigationā`x C;ف]Ä`x`^{mJ<́ʙҁʙ]^ޙԝ1+ WWWPageLayout؁֙\Zȁƙ  Som/- srumf^JSx]h􄛒xx WW @ZIf you want to link one peice of text to another, whether they are in the same hypertext document or not, you follow the same procedure. First, mark the thing which the link will lead to. You can mark a whole document with "Mark All", or you can mark just a selection with "Mark Selection". Once you have marked something, you can select another area and use "Link to marked". You can link many things to the same marked area without having to mark it again. It stays marked until you mark something else. From then on, whenever you double click on the linked text, you will jump across the link. ?* :: @i?* X  @Making a new documentŨ@`xɁęڙCjفS]Document Inspector`x><ہ֙c^b\V& }m\6镙 ꙽CԁP] Index Searchx홄TextToy!B<'ޙꙁ TcpAccess@<)^X&o^-+l^ޙmkC=   ρęԁʙ啙́ę+) HyperManager()8qmX|^ Ɓę5/ FileAccess΁ʙi^ FontManagerޙH<ʝ@*qyل)yyw y'y yVHycMys y Reference y Field2y#yVy/ySuccessive indentyStylery!y2yXIy+Fy-y%yτyField4y SearchPanelyy΄y-ySGML tagy1y ?yyyyyyۄhyyԄ News Accessery"y>yR2y MenuItem1y|Sy`LyIȳdy&yForm1yyOy$ FileAccesseryބField1yJyoy@yԄ'yIyyy yЄy„%yyys Style nameyFirst line indentyyÄeySywordsyHyۄ*y& Font Managery#yĄ&yHy"Access managery y1LeftyyrRyyfNyiy$yƄyFylyQyiOy3yy-y?TopyTabsyMainMenuyu yVȳyڄyq yyyy ScrollingTexty{y6Document details ...yForm2yɄyy넄FieldylPy TcpAccesseryVyyCBottomyׄ(yƄcy]UySubmenuy Cmd/O panelyTy}7(Rxpp ' 'j꾮목 je jjW庪 j jWWW/NextStep/Implementation/Anchor.m 664 472 105 20743 5304675163 13004 /* Hypertext "Anchor" Object Anchor.m ** ========================== ** ** An anchor represents a region of a hypertext node which is linked to ** another anchor in the same or a different node. */ #define ANCHOR_CURRENT_VERSION 0 #import #import #import #import #import "Anchor.h" #import "HTUtils.h" #import "HTParse.h" #import "HyperText.h" #import "HyperManager.h" @implementation Anchor:Object static HyperManager *manager; static List * orphans; // Grand list of all anchors with no parents List * HTHistory; // List of visited anchors + initialize { orphans = [List new]; HTHistory = [List new]; [Anchor setVersion:ANCHOR_CURRENT_VERSION]; return self; } + setManager:aManager { manager = aManager; return self; } // Creation Methods // ================ // // Do not use "new" by itself outside this module. In order to enforce // consistency, we insist that you furnish more information about the // anchor you are creating. // + new { Anchor * new_anchor; new_anchor = [super new]; new_anchor->DestAnchor = nil; new_anchor->Address = (char *)0; new_anchor->Sources = [List new]; new_anchor->children = [List new]; new_anchor->parent = 0; return new_anchor; } // Case insensitive string comparison // ---------------------------------- // On entry, // s Points to one string, null terminated // t points to the other. // On exit, // returns YES if the strings are equivalent ignoring case // NO if they differ in more than their case. // PRIVATE BOOL equivalent(const char * s, const char *t) { for(;*s && *t; s++, t++) { if (toupper(*s) != toupper(*t)) return NO; } return toupper(*s)==toupper(*t); } // Create new or find old sub-anchor // --------------------------------- // // This one is for a new anchor being edited into an existing // document. The parent anchor must already exist. + newParent:(Anchor *)anAnchor tag:(const char *)tag { List * kids = anAnchor->children; int n = [kids count]; int i; for(i=0; ichildren addObject:self]; StrAllocCopy(Address, tag); return self; } // Create new or find old named anchor // ----------------------------------- // // This one is for a reference which is found in a document, and might // not be already loaded. // Note: You are not guarranteed a new anchor -- you might get an old one, // like with fonts. + newAddress:(const char *)anAddress; { char * anc = HTParse(anAddress, "", PARSE_ANCHOR); // Anchor id specified? // If the node is a sub-anchor, we recursively load its parent. // Then we create a sub-anchor within than node. if (*anc) { char *nod = HTParse(anAddress, "", PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION); Anchor * foundParent = [Anchor newAddress:nod]; free(nod); self = [Anchor newParent:foundParent tag:anc]; free (anc); // If the node has no parent, we check in a list of such nodes to see // whether we have it. } else { /* Is not a sub anchor */ int i; int n=[orphans count]; free(anc); for(i=0; iparent){ List * kids = up->parent->children; unsigned i = [kids indexOf:up]; Anchor * nextOne =[kids objectAt:i+offset]; if (nextOne) { [HTHistory removeLastObject]; [nextOne follow]; } else { if (TRACE) printf("Anchor: No such logical step\n"); } } return self; } +next { return [self moveBy:+1]; } +previous { return [self moveBy:-1]; } // Reorder the children // -------------------- // // This is necessary to ensure that an anchor which might have existed already // in fact is put in the correct order as we load the node. // - isLastChild { if(parent) { List * siblings = parent->children; [siblings removeObject:self]; return [siblings addObject:self]; } return nil; } // // Free an anchor // -------------- - free { if (Address) free(Address); if (parent) [parent->children removeObject:self]; if (TRACE) printf("Anchor: free called! Not removed from Node!!!!!!!\n"); [Sources makeObjectsPerform:@selector(unload)]; if (!parent) [orphans removeObject:self]; return [super free]; } // Get list of sources - sources { return Sources; } // Return parent - parent { return parent; } // Remove the reference from this anchor to an other // - unlink { if (DestAnchor) { (void) [(HyperText*)Node disconnectAnchor:self]; /* select */ [[DestAnchor sources] removeObject:self]; DestAnchor = nil; } return self; } // For allowing dangling links, when things disappear - unload { DestAnchor = nil; /* invalidate the pointer */ return self; } // This removes the anchor from the structure entirely, and frees it. // - delete { if (DestAnchor) [self unlink]; // Remove outgoing link [Sources makeObjectsPerform:@selector(unlink)]; // Remove incomming links return [self free]; } // Set the region represented by the anchor // - (void) setNode: (id) node { Node = node; } /* Select the anchor select ** ----------------- ** ** This will load the node is necessary, if the anchor has only a network ** address. */ - selectDiagnostic: (int) diag { Anchor * nodeAnchor = parent ? parent : self; if (!nodeAnchor->Node) { /* If the node is not loaded, */ if (!nodeAnchor->Address) { if (TRACE) printf( "Anchor %p: node not loaded, no address!\n", nodeAnchor); return nil; } else { if (![manager loadAnchor:nodeAnchor Diagnostic:diag]) { if (TRACE) printf("Anchor %p: Couldn't load node `%s'!\n", nodeAnchor, nodeAnchor->Address); return nil; } } } if (!nodeAnchor) return nil; /* Failed */ if (!nodeAnchor->Node) return nodeAnchor; /* Ok, foreign */ return [nodeAnchor->Node selectAnchor:self]; /* Ok, text */ } /* Select the anchor select ** ----------------- ** ** This will load the node is necessary, if the anchor has only a network ** address. */ - select { return [self selectDiagnostic:0]; } // Set reference string - setAddress: (const char *) ref_string { if (TRACE) printf("Anchor %p has address `%s'\n", self, ref_string); StrAllocCopy(Address, ref_string); return self; } // Return the address of this anchor - (const char *)address { return Address; } // Generate a malloc'd string for the FULL anchor address // - (char *)fullAddress { char * result; if (parent) { result = (char *) malloc( strlen(Address) + 1 + strlen([parent address])+ 1); strcpy(result, [parent address]); strcat(result, "#"); strcat(result, Address); } else { /* no parent */ result = (char *) malloc(strlen(Address)+ 1); strcpy(result, Address); } return result; } // Link this Anchor to another given one // ------------------------------------- - (void) linkTo:(Anchor *)destination; { if (TRACE) printf( "Anchor: Linking anchor %p to anchor %p\n", self, destination); DestAnchor = destination; [destination->Sources addObject:self]; } // Follow a link to its destination // -------------------------------- - (BOOL) follow; { if (DestAnchor) if ([DestAnchor select]) { if (TRACE) printf("Anchor: followed link from %p to %p\n", self, DestAnchor); [HTHistory addObject:self]; return YES; } return NO; } // Figure out the node from that of the parent - node { return parent ? [parent node] : Node; } - destination { return DestAnchor; } @end s are equivalent ignoring casWWW/NextStep/Implementation/Anchor.h 664 472 105 3104 5304672063 12743 /* Hypertext "Anchor" Object Anchor.h ** ========================== ** ** An anchor represents a region of a hypertext node which is linked ** to another anchor in the same or a different node. */ #import #import #import // Main definition of anchor: // ========================== @interface Anchor:Object { id Node; // The node within which this is an anchor /* (HyperText *) */ // If not a subanchor Anchor * parent; // If this is a subanchor List * children; // If this has subanchors, these are they. // Information about this anchor: char * Address; // The address of this anchor // Generated locally, not archived: List * Sources; // A list of anchors pointing to this id DestAnchor; // The anchor, if loaded, to which this leads } + initialize; + setManager:aManager; // Set class variable + newAddress:(const char *)address; + newParent:(Anchor*)anAnchor tag:(const char *)tag; +back; +next; +previous; - (void)setNode:(id)node; - (const char *)address; - (char *)fullAddress; - setAddress: (const char *) ref_string; - select; // Load if nec, select and bring to front - selectDiagnostic:(int)diag; // Same with source display option - isLastChild; // Move it in the list of children - (BOOL)follow; // Follow link if we can, return "can we?" - (void) linkTo:(Anchor *)destination; - node; // Return the node in which the anchor sits - destination; // Return the desination anchor - parent; // Return the parent if any - unload; // Make link dangle @end uivalent(const char * s, const char *t) { for(;*s && *t; s++, t++) { if (toupper(*s) != toupper(*t)) return NO; } return toupper(*s)==toupper(*t); } // Create new or find old sub-anchor // --------------------------------- // // This one is for a new anchor being edited into an existing // document. The parent anchor must already exist. + newParent:(Anchor *)anAnchor tag:(const char *)tag { List * kids = anAnchor-WWW/NextStep/Implementation/HyperText.h 644 472 105 6263 5304656621 13476 // HyperText Class // #import #import #import "Anchor.h" #import "HTStyle.h" /* Bit fields describing the capabilities of a node: */ #define HT_READ 1 #define HT_WRITE 2 #define HT_LINK_TO_NODE 4 #define HT_LINK_TO_PART 8 #define HT_LINK_FROM_NODE 16 #define HT_LINK_FROM_PART 32 #define HT_DO_ANYTHING 63 extern void write_rtf_header(NXStream* rtfStream); @interface HyperText:Text { id server; // Responsible for maintaining this node // List * Anchors; // A list of the anchors Anchor * nodeAnchor; // An anchor representing the node // List * unAnchors; // List of unanchored links to other nodes int nextAnchorNumber; // The serial number of the next anchor int protection; // Server capability authorised BOOL isIndex; // Can accept a keyword search // List * alsoStore; // Store these nodes at the same time // HyperText * storeWith; // Store along with the given node please. int slotNumber; // Window display position int format; // See WWW.h for values } + newAnchor:(Anchor*)anAnchor Server:(id)server; - readSGML:(NXStream*)sgmlStream diagnostic:(int)diagnostic; - writeSGML:(NXStream*)sgmlStream relativeTo:(const char *)aName; - readText:(NXStream*)stream; // Overrides Text's method. - server; - (BOOL) isIndex; - setupWindow; - adjustWindow; // Adust scroll bars, sizeability, size, etc. - (int) format; - setFormat:(int)format; // Style handling: - applyToSimilar: (HTStyle *)style; // Apply this style to the selection - applyStyle: (HTStyle *)style; // Apply this style to the selection - selectUnstyled: (HTStyleSheet *)sheet;// Select the first unstyled run. - updateStyle: (HTStyle *)style; // Update all text with changed style. - (HTStyle *)selectionStyle:(HTStyleSheet*)sheet; // style if any of selection - replaceSel:(const char *)aString style:(HTStyle*)aStyle; // Paste in styled text // "Fast" Methods for external parsers: - appendBegin; // Start an append sequence - appendStyle:(HTStyle *) style; // Set the style for future text - appendText: (const char *)text; // Add a string - appendBeginAnchor: (const char *)name to:(const char *)reference; // Begin an anchor - appendEndAnchor; // End it - appendEnd; // Flush out all additions so far // Anchor handling: //- anchors; // Set of anchors - nodeAnchor; // Single anchor representing this node - selectedLink; // Return selected anchor if any - followLink; // (If selected) - unlinkSelection; // Remove anchor info from selection - (Anchor *) referenceSelected; // Generate anchor for this node - (Anchor *) referenceAll; - (Anchor *) linkSelTo: (Anchor*)anchor;// Link selected text to this anchor. - disconnectAnchor: (Anchor*)anchor; // Remove reference from this node. - selectAnchor: (Anchor*)anchor; // Bring to front and highlight it. - setTitle:(const char *)title; - dump: sender; // diagnostic output // Override methods of superclasses: - readText: (NXStream *)stream; // Also set format variable. - readRichText: (NXStream *)stream; // Also set format variable. - mouseDown:(NXEvent*)theEvent; // Double click become hyperjump - keyDown:(NXEvent*)theEvent; // - paste:sender; // // Window delegate methods: - windowDidBecomeMain:sender; @end rent:foundParent tag:anc]; free (anc); // If the node has no parent, we check in a list of such nodes to see // whether we have it. } else { /* Is not a sub anchor */ int i; int n=[orphans count]; free(anc); for(i=0; i #import // #import /* TBL */ #import "HTUtils.h" /* TBL */ #import /* TBL */ #import /* TBL */ #import /* TBL */ #import /* TBL */ extern char * appDirectory; /* Name of the directory containing the application */ /* Get Integer default value */ static int int_default(const char * param) { int result = 0; const char * string = NXGetDefaultValue("WorldWideWeb", param); if (string) sscanf(string, "%i", &result); return result; } void main(int argc, char *argv[]) { // NXArgc = argc; /* TBL */ // NXArgv = argv; /* TBL */ char *p; static NXDefaultsVector myDefaults = { { "PaperType", "Letter"}, // Non-USA users will have to override { "LeftMargin", "72"}, // (72) Space for ring binding { "RightMargin", "36"}, // (72) Note printers need some margin { "TopMargin", "36"}, // (108) All margins in points { "BottomMargin", "36"}, // (108) PrintInfo defaults in brackets { NULL, NULL} // Terminate list. }; appDirectory = malloc(strlen(argv[0])); strcpy(appDirectory, argv[0]); if (p = strrchr(appDirectory, '/')) p[1]=0; /* Chop home directory after slash */ if (TRACE) printf("WWW: Run from %s\n", appDirectory); NXApp = [Application new]; NXRegisterDefaults("WorldWideWeb", myDefaults); [NXApp loadNibSection:"WorldWideWeb.nib" owner:NXApp]; // The deafult Margins seem to be 72, 72, 108, 108 which is a lot. { int leftM = int_default("LeftMargin"); int rightM = int_default("RightMargin"); int topM = int_default("TopMargin"); int bottomM = int_default("BottomMargin"); PrintInfo * pi = [NXApp printInfo]; [pi setPaperType:NXGetDefaultValue("WorldWideWeb", "PaperType") andAdjust:YES]; [pi setVertCentered:NO]; [pi setMarginLeft:leftM right:rightM top:topM bottom:bottomM]; // Points. } [NXApp run]; [NXApp free]; exit(0); } / End it - appendEnd; // Flush out all additions so far // Anchor handling: //- anchors; // Set of anchors - nodeAnchor; // Single anchor representing this node - selectedLink; // Return selected anchor if any - followLink; // (If selected) - unlinkSelection; // Remove anchor info from selection - (Anchor *) referenceSelected; // Generate ancWWW/NextStep/Implementation/SourceFiles.html 664 472 105 6561 5304722773 14507
WorldWideWeb source files

Source Files

The files mentioned below are those which form part of the WWW project. These are divided into common files, and files for each machine. See also:
  • Module dependency diagram (future)

Common files

These files contain machine-independent draft ANSI-C code, or conditional compilation instructions to select small pieces of code for special environments. The .h files should be included (or imported) by each file using the routines in the corresponding .c file.
HTAccess.c , .h
This module provides access to a hypertext node, whatever its source. It provides a device descriptor which can then be used to read the contents of the node.
HTParse.c , .h
Contains routines for parsing, stripping, and simplifying hypertext addresses. (See also: Hypertext Addressing ).
HTTP.c , .h
Client access code for HyperText Transfer protocol. (See also: HTTP protocol ).
tcp.h
A bunch of machine-dependent #include statements and macros for TCP socket code on any system we can compile on.
utilities.h
Macros of general use throughout the project.
WWW.h
WWW-specific definitions

On the NeXT machine

For the NeXT, the project is managed by the Interface Builder. The source files are listed, and the Makefile generated from, the interface builder project file:
WorldWideWeb.nib
Click on this file to launch the interface builder, if you are running on a NeXT with the directory mounted as a file system.
The following are currently NeXT-specific but could be made common:
ParseHTML.h
An HTML parser and outputter.
HTStyle.m, .h
Styles: Loading, saving, creating, searching for named.
The class files which follow are all accessible through the interface builder file. In brackets is the superclass of which each is a subclass.
Anchor.m, .h
A management of anchors (Object)
FileAccess.m ,h
Local and remote NFS mounted file handler (HyperAccess)
Hypermanager.m, .h
Protocol maanager, Application delegate. (HyperAccess)
HyperText.m, .h
Subclass of Text, with anchors and window management. (Text)
HyperAccess.m , .h
Dummy superclass of access object. (Object)
NewsAccess.m, .h
NNTP protocol handler (Object)
StyleToy.m, .h
Style manipulation. (Object)
TcpAccess.m, h
Protocol handler for HTTP (HyperAccess)
TextToy.m, .h
Text manipulation menu items. (Object)
The files which follow are generated by the interface builder.
WorldWideWeb_main.m
The main program. t n=[orphans count]; free(anc); for(i=0; i #import #import "Anchor.h" #import "HyperText.h" @interface HyperAccess:Object // Target variables for interface builder hookups: { id manager; // The object which manages different access mechanisms. id openString; id keywords; id titleString; id addressString; id contentSearch; } // Interface builder initialisation methods: - setManager:anObject; - setOpenString:anObject; - setKeywords:anObject; - setTitleString:anObject; - setAddressString:anObject; - setContentSearch:anObject; // Action methods for buttons etc: - search:sender; - searchRTF: sender; - searchSGML: sender; - open: sender; - openRTF:sender; - openSGML:sender; - saveNode:(HyperText *)aText; // Calls form other code: - manager; - (const char *)name; // Name for this access method - loadAnchor:(Anchor *)a; // Loads an anchor. - loadAnchor:(Anchor *)a Diagnostic:(int)level ;// Loads an anchor. // Text delegate methods: - textDidChange:textObject; - (BOOL)textWillChange:textObject; // HyperText delegate methods: - hyperTextDidBecomeMain:sender; @end make8 docflash.tiff8GMakefile.preamble7 .dir3_0.wmd8=worldbook.tiff8>worldbook2.tiff87docflash2.tiff8,SourceFiles.html8 ,Bugs.htmlfullAddress]; [openString setStringValue: destinatiWWW/NextStep/Implementation/HyperAccess.m 644 472 105 5725 5304416123 13751 // HyperAccess.m // A HyperAccess object provides access to hyperinformation, using // particular protocols and data format transformations. // This actual class will not work itself: it just contains common code. // History: // 26 Sep 90 Written TBL #include #include #import "HyperAccess.h" #import "HyperManager.h" #import "Anchor.h" #import "HTUtils.h" @implementation HyperAccess // Methods used by the Interface Builder code to connect up the application: - setTitleString:anObject { titleString = anObject; return self; } - setAddressString:anObject { addressString = anObject; return self; } - setOpenString:anObject { openString = anObject; return self; } - setKeywords:anObject { keywords = anObject; return self; } - setContentSearch:anObject { contentSearch = anObject; return self; } - setManager:anObject; { manager = anObject; [(HyperManager *)manager registerAccess:self]; return self; } // Methods to return the values of instance variables - manager { return manager; } // Return the name of this access method - (const char *)name { return "Generic"; } // Actions: // These are all dummies, because only subclasses of this class actually work. - search:sender { return nil; } - searchRTF:sender { return nil; } - searchSGML:sender { return nil; } // Direct open buttons: - open:sender { return nil; } - openRTF:sender { return nil; } - openSGML:sender { return nil; } - accessName:(const char *)name Diagnostic:(int)level { return nil; /* can't do that. */ } // This will load an anchor which has a name - loadAnchor: (Anchor *) anAnchor { return [self loadAnchor:anAnchor Diagnostic:0]; // If not otherwise implemented } - loadAnchor: (Anchor *)a Diagnostic:(int)diagnostic { return nil; } - saveNode:(HyperText *)aText { NXRunAlertPanel(NULL, "You cannot overwrite this original document. You can use `save a copy in...'", NULL,NULL,NULL); printf( "HyperAccess: You cannot save a hypertext document in this domain.\n"); return nil; } // Text Delegate methods // --------------------- // These default methods for an access allow editing, and change the cross // in the window close button to a broken one if the text changes. #ifdef TEXTISEMPTY // Called whenever the text is changed - text:thatText isEmpty:flag { if (TRACE) printf("Text %i changed, length=%i\n", thatText, [thatText textLength]); return self; } #endif - textDidChange:textObject { if (TRACE) printf("HM: text Did Change.\n"); [[textObject window] setDocEdited:YES]; /* Broken cross in close button */ return self; } - (BOOL)textWillChange:textObject { if (TRACE) printf("HM: text Will Change -- OK\n"); return NO; /* Ok - you may change (sic) */ } // These delegate methods are special to HyperText: - hyperTextDidBecomeMain: sender { return [manager hyperTextDidBecomeMain: sender]; /* Pass the buck */ } @end ; return self; } // Methods to return WWW/NextStep/Implementation/HTFile.c 664 472 105 21123 5041302757 12656 /* File Access HTFile.c ** =========== ** ** This is unix-specific code in general, with some VMS bits. ** These are routines for file access used by WWW browsers. ** ** History: ** Feb 91 Written Tim Berners-Lee CERN/CN ** Apr 91 vms-vms access included using DECnet syntax ** ** Bugs: ** Cannot access VMS files from a unix machine. How can we know that the ** target machine runs VMS? */ #ifdef EXPLICIT_INCLUDES #ifdef vms include #else #include #include #endif #endif #include "HTUtils.h" #include "WWW.h" #include "HTParse.h" #include "tcp.h" #include "HTTCP.h" #include "HTFTP.h" PRIVATE char *HTMountRoot = "/Net/"; /* Where to find mounts */ /* PRIVATE char *HTCacheRoot = "/tmp/Cache/";*/ /* Where to cache things */ /* PRIVATE char *HTSaveRoot = "$(HOME)/WWW/";*/ /* Where to save things */ /* Convert filenames between local and WWW formats ** ----------------------------------------------- ** Make up a suitable name for saving the node in ** ** E.g. $(HOME)/WWW/news/1234@cernvax.cern.ch ** $(HOME)/WWW/http/crnvmc/FIND/xx.xxx.xx ** ** On exit, ** returns a malloc'ed string which must be freed by the caller. */ #ifdef __STDC__ PUBLIC char * HTLocalName(const char * name) #else PUBLIC char * HTLocalName(name) char * name; #endif { char * access = HTParse(name, "", PARSE_ACCESS); char * host = HTParse(name, "", PARSE_HOST); char * path = HTParse(name, "", PARSE_PATH+PARSE_PUNCTUATION); if (0==strcmp(access, "file")) { free(access); if ((0==strcmp(host, HTHostName())) || !*host) { free(host); if (TRACE) printf("Node `%s' means path `%s'\n", name, path); return(path); } else { char * result = (char *)malloc( strlen("/Net/")+strlen(host)+strlen(path)+1); sprintf(result, "%s%s%s", "/Net/", host, path); free(host); free(path); if (TRACE) printf("Node `%s' means file `%s'\n", name, result); return result; } } else { /* other access */ char * result; CONST char * home = (CONST char*)getenv("HOME"); if (!home) home = "/tmp"; result = (char *)malloc( strlen(home)+strlen(access)+strlen(host)+strlen(path)+6+1); sprintf(result, "%s/WWW/%s/%s%s", home, access, host, path); free(path); free(access); free(host); return result; } } /* Make a WWW name from a full local path name ** ** Bugs: ** At present, only the names of two network root nodes are hand-coded ** in and valid for the NeXT only. This should be configurable in ** the general case. */ #ifdef __STDC__ PUBLIC char * WWW_nameOfFile(const char * name) #else PUBLIC char * WWW_nameOfFile(name) char * name; #endif { char * result; if (0==strncmp("/private/Net/", name, 13)) { result = (char *)malloc(7+strlen(name+13)+1); sprintf(result, "file://%s", name+13); } else if (0==strncmp(HTMountRoot, name, 5)) { result = (char *)malloc(7+strlen(name+5)+1); sprintf(result, "file://%s", name+5); } else { result = (char *)malloc(7+strlen(HTHostName())+strlen(name)+1); sprintf(result, "file://%s%s", HTHostName(), name); } if (TRACE) printf("File `%s'\n\tmeans node `%s'\n", name, result); return result; } /* Determine file format from file name ** ------------------------------------ ** ** */ #ifdef __STDC__ PUBLIC WWW_Format HTFileFormat(const char * filename) #else PUBLIC WWW_Format HTFileFormat(filename) char * filename; #endif { CONST char * extension; for (extension=filename+strlen(filename); (extension>filename) && (*extension != '.') && (*extension!='/'); extension--) /* search */ ; if (*extension == '.') { return 0==strcmp(".html", extension) ? WWW_HTML : 0==strcmp(".rtf", extension) ? WWW_RICHTEXT : 0==strcmp(".txt", extension) ? WWW_PLAINTEXT : WWW_INVALID; /* Unrecognised */ } else { return WWW_PLAINTEXT; } } /* Determine write access to a file // -------------------------------- // // On exit, // return value YES if file can be accessed and can be written to. // // Isn't there a quicker way? */ #ifdef __STDC__ PUBLIC BOOL HTEditable(const char * filename) #else PUBLIC BOOL HTEditable(filename) char * filename; #endif { #ifndef vms #ifndef NO_UNIX_IO int groups[NGROUPS]; uid_t myUid; int ngroups; /* The number of groups */ struct stat fileStatus; int i; if (stat(filename, &fileStatus)) /* Get details of filename */ return NO; /* Can't even access file! */ ngroups = getgroups(NGROUPS, groups); /* Groups to which I belong */ myUid = geteuid(); /* Get my user identifier */ if (TRACE) { int i; printf("File mode is 0%o, uid=%d, gid=%d. My uid=%d, %d groups (", fileStatus.st_mode, fileStatus.st_uid, fileStatus.st_gid, myUid, ngroups); for (i=0; i=0 Value of file descriptor or socket to be used ** to read data. ** *pFormat Set to the format of the file, if known. ** (See WWW.h) ** */ #ifdef __STDC__ int HTOpenFile(const char * addr, WWW_Format * pFormat) #else int HTOpenFile(addr, pFormat) char * addr; WWW_Format * pFormat; #endif { char * filename; int fd = -1; /* Unix file descriptor number = INVALID */ char * nodename = 0; char * newname=0; /* Simplified name of file */ /* Reduce the filename to a basic form (hopefully unique!) */ StrAllocCopy(newname, addr); HTSimplify(newname); filename=HTParse(newname, "", PARSE_PATH|PARSE_PUNCTUATION); nodename=HTParse(newname, "", PARSE_HOST); free(newname); *pFormat = HTFileFormat(filename); #ifdef vms /* Assume that the file is remote ultrix! @@ */ { /* We try converting the filename into Files-11 syntax. That is, we assume ** first that the file is, like us, on a VMS node. We try remote ** (or local) DECnet access. Files-11, VMS, VAX and DECnet ** are trademarks of Digital Equipment Corporation. */ char wholename[255]; /* VMS Fudge */ char vmsname[255]; char *second = strchr(filename+1, '/'); /* 2nd slash */ char *last = strrchr(filename, '/'); /* last slash */ if (!second) { /* Only one slash */ sprintf(vmsname, "%s::%s", nodename, filename); } else if(second==last) { /* Exactly two slashes */ *second = 0; /* Split filename from disk */ sprintf(vmsname, "%s::%s:%s", nodename, filename+1, second+1); *second = '/'; /* restore */ } else { /* More than two slashes */ char * p; *second = 0; /* Split disk from directories */ *last = 0; /* Split dir from filename */ sprintf(vmsname, "%s::%s:[%s]%s", nodename, filename+1, second+1, last+1); *second = *last = '/'; /* restore filename */ for (p=strchr(vmsname, '['); *p!=']'; p++) if (*p=='/') *p='.'; /* Convert dir sep. to dots */ } fd = open(vmsname, O_RDONLY, 0); /* If the file wasn't VMS syntax, then perhaps it is ultrix */ if (fd<0) { if (TRACE) printf("HTAccess: Can't open as %s\n", vmsname); sprintf(vmsname, "%s::\"%s\"", nodename, filename); fd = open(vmsname, O_RDONLY, 0); if (fd<0) { if (TRACE) printf("HTAccess: Can't open as %s\n", vmsname); } } } #else /* For unix, we try to translate the name into the name of a transparently ** mounted file. */ #ifndef NO_UNIX_IO { char * localname = HTLocalName(addr); fd = open(localname, O_RDONLY, 0); if(TRACE) printf ("HTAccess: Opening `%s' gives %d\n", localname, fd); free(localname); } #endif #endif /* Now, as transparently mounted access has failed, we try FTP. */ if (fd<0) if (strcmp(nodename, HTHostName())!=0) { free(filename); return HTFTP_open_file_read(addr); } /* All attempts have failed if fd<0. */ if (fd<0) printf("Can't open `%s', errno=%d\n", filename, errno); free(filename); return fd; } char *)malloc( strlen(home)+strlen(access)+strlen(host)+strlen(path)+6+1); sprintf(result, "%s/WWW/%s/%s%s", home, access, host, path); free(path); free(access); free(host); return result; } } /* Make a WWW name from a full local path name ** ** Bugs: ** At present, only the names of two network root nodes are hand-coded ** in and valid for the NeXT only. This should be configurable in ** the general case. */ #ifWWW/NextStep/Implementation/ParseHTML.h 664 472 105 105066 5311403740 13332 /* FORMAT CONVERSION FROM SGML ** =========================== ** ** ** 22 Nov 92 Fixed quoting of hrefs. ** CERN_WEIRDO ifdefed out -- proper SGML expected ** REMOVE_SCRIPT ifdefed out -- did ignore lines starting with "." */ #import "HTStyle.h" extern HTStyleSheet * styleSheet; #ifndef NEXT_CHAR static FILE * sgmlStream; #define END_OF_FILE NXAtEOS(sgmlStream) /* @@@@ */ #define NEXT_CHAR getc(sgmlStream) #define BACK_UP ungetc(sgmlStream) #endif #define upper(c) ( ((c>='a')&&(c<='z')) ? (char)((int)c-32) : c ) /* State machine states: */ enum state_enum {S_text, /* We are not in a tag */ #ifdef REMOVE_SCRIPT S_column_1, /* as Text but first character on input line */ S_dot, /* We have had dor in first column */ S_junk_script, /* Ignore everything until NL or ";" */ #else #define S_column_1 S_text #endif S_word, /* We have just had a non-white printable */ S_tag_start, /* We have just had "<" */ S_tag_h, S_tag_a, S_end_a, S_tag_d, S_end_d, S_tag_i, S_tag_l, S_tag_lis, S_tag_n, S_tag_o, S_end_o, S_tag_p, S_tag_u, S_end_u, S_tag_end, /* We have just had "" */ #ifdef CERN_WEIRDO S_junk_line, /* Ignore everything until "\n" */ #endif S_done}; typedef struct _SGML_style { char * start_tag; /* Tag to mark start of a style */ char * paragraph_tag; /* Tag to mark paragraph mark within style */ char * tab_tag; /* Tag to mark tab within style */ char * end_tag; /* Tag to mark end of style */ char * start_text; /* Text conventionally starting this style */ char * paragraph_text; /* Text used as a paragraph mark within style*/ char * end_text; /* Text used to end a style */ HTStyle * style; /* Paragraph style to be used */ int free_format; /* Flag: are line ends word breaks only? */ int litteral; /* Flag: end only at close tag (cheat) ? */ } SGML_style; /* Stack of previous styles: */ typedef struct _NestedStyle { struct _NestedStyle * next; /* previously nested style or 0 */ SGML_style * SGML; /* SGML style interrupted */ } NestedStyle; /* MODULE-WIDE DATA ** ** */ /* We delay changing style until necessary to avoid dummy style changes ** resulting in too many extra newlines. */ static SGML_style * current_style; /* The current output style */ static SGML_style * next_style; /* The next style to go into */ static NestedStyle * styleStack; static int output_in_word; /* Flag: Last character ouput was non-white */ /* Paragraph Styles used by the SGML parser: ** ---------------------------------------- */ static SGML_style Normal = { "", "

\n", "\t", "", "","", "", 0 ,1, 0}; static SGML_style Heading[6] = { { "\n

", "

\n

", "\t", "

", "", "", "", 0, 1, 0}, { "\n

", "

\n

", "\t", "

", "", "", "", 0, 1, 0}, { "\n

", "

\n

", "\t", "

", "", "", "", 0, 1, 0}, { "\n

", "

\n

", "\t", "

", "", "", "", 0, 1, 0}, { "\n
", "
\n
", "\t", "
", "", "", "", 0, 1, 0}, { "\n
", "
\n
", "\t", "
", "", "", "", 0, 1, 0} }; static SGML_style Glossary = /* Large hanging indent with tab */ { "\n
\n
", "\n
", "\n
", "\n
\n", "", "", "", 0, 1}; static SGML_style listStyle = /* Hanging indent with tab */ { "\n
    \n
  • ", "\n
  • ", "\t", "\n
", "\267\t", "\267\t", "", 0, 1, 0}; static SGML_style addressStyle = { "\n
", "

", "\t", "\n

", "", "", "", 0, 1, 0 }; /* Explicit format styles: */ static SGML_style Example = /* Fixed width font, at least 80 chars wide */ { "\n", "\n", "\t", "", "", "", "", 0 , 0, 1}; static SGML_style Preformatted = /* Fixed width font, at least 80 chars wide */ { "\n
", "\n", "\t", "
", "", "", "", 0 , 0, 0}; /* not litteral */ static SGML_style Fixed = /* Fixed width font, at least 80 chars wide */ { "\n", "

", "\t", "", "", "", "", 0 , 1, 0}; static SGML_style Listing = /* Fixed width font, at least 80 chars wide */ { "\n

", "\n", "\t", "", "", "", "", 0 , 0, 1}; /* Table of all possible SGML paragraph styles */ static SGML_style * styleTable[] = { &Normal, &Heading[0], &Heading[1], &Heading[2], &Heading[3], &Heading[4], &Heading[5], &Glossary, &listStyle, &addressStyle, &Preformatted, &Fixed, &Example, &Listing }; /* style table */ #define NUMBER_OF_STYLES (sizeof(styleTable)/sizeof(styleTable[0])) /* Highlighting styles */ static HTStyle * Highlighting[3]; /* F U N C T I O N S */ /* Get Styles from style sheet ** --------------------------- */ void get_styles() { Normal.style = HTStyleNamed(styleSheet, "Normal"); Heading[0].style = HTStyleNamed(styleSheet, "Heading1"); Heading[1].style = HTStyleNamed(styleSheet, "Heading2"); Heading[2].style = HTStyleNamed(styleSheet, "Heading3"); Heading[3].style = HTStyleNamed(styleSheet, "Heading4"); Heading[4].style = HTStyleNamed(styleSheet, "Heading5"); Heading[5].style = HTStyleNamed(styleSheet, "Heading6"); Glossary.style = HTStyleNamed(styleSheet, "Glossary"); listStyle.style = HTStyleNamed(styleSheet, "List"); addressStyle.style= HTStyleNamed(styleSheet, "Address"); Example.style = HTStyleNamed(styleSheet, "Example"); Preformatted.style = HTStyleNamed(styleSheet, "Example"); Listing.style = HTStyleNamed(styleSheet, "Listing"); Highlighting[0] = HTStyleNamed(styleSheet, "Italic"); Highlighting[1] = HTStyleNamed(styleSheet, "Bold"); Highlighting[2] = HTStyleNamed(styleSheet, "Bold-Italic"); } /* Output the code for styles ** -------------------------- */ void output_paragraph() { HTStyle * s = current_style->style; int newlines = ((s->spaceBefore+s->spaceAfter) / s->paragraph->lineHt) + 1; int i; for(i=0; iparagraph_text); output_in_word = 0; } /* Switch SGML paragraph style (finishing the old one) ** ** The "formatted" flag allows us to add a paragraph end at the end of a ** normal style (such as

etc) but suppresses this for litteral text ** styles such as and <LISTING which have explicit paragraph end. ** Thus, ALL text between <XMP> tags is litteral, and no newline results ** from going in and out of <XMP> sections. ** ** Now, we allow only the larger of the space before/space after ** requirements, as that is nearer what is meant. */ void update_style() { HTStyle * cur = current_style->style; HTStyle * next = next_style->style; OUTPUTS(current_style->end_text); if (current_style->free_format && cur && next) { /* generate new lines */ int i; float space = cur->spaceAfter > next->spaceBefore ? cur->spaceAfter : next->spaceBefore; /* max */ int newlines = (space/cur->paragraph->lineHt) + 1; output_in_word = 0; for(i=0; i<newlines; i++) OUTPUT('\n'); /* Rather approximate! */ } current_style = next_style; if (current_style->style) SET_STYLE(current_style->style); OUTPUTS(current_style->start_text); } #define UPDATE_STYLE {if (current_style!=next_style) update_style();} /* Rememember that we will be going into style s ** --------------------------------------------- */ void change_style(SGML_style * s) { next_style = s; } /* End an SGML style */ void end_style() { if (styleStack) { NestedStyle * N = styleStack; styleStack = N->next; free(N); if (styleStack) change_style(styleStack->SGML); else change_style(&Normal); } else { if (TRACE) printf("HTML: We have ended more styles than we have started!\n"); change_style(&Normal); /* Note there is no nesting! */ } } /* Start a nested SGML style */ void start_style(SGML_style * s) { NestedStyle * N = malloc(sizeof(*N)); N->next = styleStack; N->SGML = s; styleStack = N; change_style(s); } /* Start a highlighted area ** ------------------------ */ void start_highlighting(HTStyle * style) { /* SET_STYLE(style); @@@ to be fixed up */ } /* End a highlighted area ** ---------------------- */ void end_highlighting() { /* @@@@@@ Need set and unset style functions, traits and nesting */ } /* Check keyword syntax ** --------------------- ** ** This function is called when there is only one thing it can be. ** The check is case-insensitive. ** ** On entry, ** s Points to a template string in uppercase, with a space ** standing for any amount of space in the input stream. ** THE FIRST CHARACTER HAS ALREADY BEEN READ AND CHECKED. ** On exit, ** returns YES if matched, all maching characters gobbled up; ** NO if failure, only matching characters gobbled up. */ static BOOL check(char *s) { char * p = s+1; /* Pointer to template string */ char c; /* Character from stream */ for (; *p; p++) { if (*p == ' ') { for(c=NEXT_CHAR; WHITE(c) ;c=NEXT_CHAR) /*null*/ ; BACK_UP; /* Put non-blank back into stream */ } else { c = NEXT_CHAR; if (upper(c) != *p) { printf("SGML parse: `%c' found when `%c' in `%s' was expected.\n", c, *p, s); BACK_UP; /* Put eroneous character back on stream */ return NO; /* failed: syntax error */ } /* bad char */ } /* non-blank */ } /* for */ return YES; /* succeded: go to end of template string */ } /* Read example text ** ----------------- ** ** Returns when terminator or end-of-file found. ** ** As we are looking for a terminator, we have to buffer things which ** could be terminators so as to be able to replace thm into the output ** stream if we find they aren't. If there wasn't the ambiguity as to ** upper/lower case, we could of course just regurgitate the terminator ** itself. ** */ static int parse_example(SGML_style * style, char * terminator) { char * p = terminator; char buffer[20]; /* One longer than the terminator */ char * q = buffer; start_style(style); UPDATE_STYLE; for (;;){ if (END_OF_FILE) return S_text; /* return if end of stream */ *q = NEXT_CHAR; if (upper(*q)==*p) { p++; q++; if (!*p) { end_style(); return S_text; /* Return: terminator found */ } } else { if (q!=buffer) { /* Replace what could have been terminator */ for(p=buffer; p<q; p++) { OUTPUT(*p); } buffer[0] = *q; /* Put this char back at beginning of buffer */ p = terminator; /* point to start of terminator again */ q = buffer; } #ifdef JUNK if (*q !=10) { OUTPUT(*q); /* Most common 99% path */ } else { output_paragraph(); /* @@ gives space_before and after */ } #else OUTPUT(*q); /* Most common 99% path */ #endif } } } /* Read in an SGML Stream readSGML: ** ---------------------- */ /* State machine to perform lexical analysis of SGML ** ** This routine parses an SGML stream to produce styles, text and anchors. ** ** This machine does not do good error recovery. It ignores tags which it doesn't ** understand. It is a simple finite state machine with no push-down stack, and ** therefore cannot (yet) understand nested constructs. ** ** NON-REENTRANT. ** ** On entry, ** sgmlStream is open for read, and will provide the marked up data. ** diagnostic 0 => Read and interpret ** 1 => Dump RTF into buffer as text. ** On exit, ** return value is self. ** self has anchors added which came up. ** Is loaded if state returned is "done". */ /* When a state has been found, we break out of the switch with this macro. ** It is a macro to allow the code to be changed more easily (eg to return). ** As it breaks out of the inner switch only, we must remember breaks after ** that switch to get out of the next outer one, and so on. */ #ifdef NeXT - readSGML: (NXStream *)stream diagnostic:(int)diagnostic #else int readSGML(HyperText * self, FILE * stream, int diagnostic) #endif #define SETSTATE(x) {state=(x); break;} { enum state_enum state = S_column_1; /* Information to be accumulated: */ char title[256]; /* See <TITLE> tag. */ char reference[256]; /* See <A HREF=...> attribute */ char anchor_name[256]; /* See <A NAME=...> attribute */ int title_length = 0; int reference_length = 0; int anchor_name_length = 0; /* See <A NAME=...> attribte */ BOOL end_style_on_nl = NO; /* For styles which only last a line (ugh!) */ BOOL white_significant = NO; /* Not free format */ /* Set up global pointer for other routines */ output_in_word = 0; /* Flag: Last character output was non-white */ HT = self; sgmlStream = stream; /* Pick up the styles we want from a local style sheet */ get_styles(); styleStack = 0; current_style = &Normal; if (TRACE) printf("Parsing SGML stream %i\n", sgmlStream); START_OUTPUT; set_style(Normal.style); /* Was random! 910910 TBL */ while(!END_OF_FILE && (state!=S_done)) { char c = NEXT_CHAR; if (c == (char)-1) { if (TRACE) printf("*** HT: -1 found on input stream not at EOF!\n"); break; } #ifdef CHARACTER_TRACE if(TRACE) printf("<%c>", c); #endif switch (state) { #ifdef REMOVE_SCRIPT case S_column_1: if (c=='.') { SETSTATE(S_dot); } BACK_UP; SETSTATE(S_text); case S_dot: /* Dot in first column */ if (WHITE(c)) { OUTPUT('.'); BACK_UP; SETSTATE(S_text); /* OOPS: must have been real "." */ } else { SETSTATE(S_junk_script); /* Throw away SCRIPT commands */ } case S_junk_script: SETSTATE( (c=='\n')||(c==';') ? S_column_1 : S_junk_script); #endif case S_word: /* We have just had non-white characters */ if (c=='<') SETSTATE(S_tag_start); if (c=='&') goto rcdata; if (!WHITE(c)) { OUTPUT(c); break; } case S_text: /* We are not in a tag or a word */ switch(c) { case '<': SETSTATE(S_tag_start); /* Special code for CERN SGML double newline significance: ugh! :-( */ case '\n': if (white_significant) { output_paragraph(); output_in_word = 0; SETSTATE(S_text); } #ifdef CERN_WEIRDO /* Obsolete 921122 */ if (end_style_on_nl) { end_style(); end_style_on_nl = NO; } else { int newlines = 1; while( (c=NEXT_CHAR)==10) { newlines++; } if (newlines>1) { output_paragraph(); /* n newlines becomes a paragraph.*/ output_in_word=0; } BACK_UP; /* Go back and check c again */ SETSTATE(S_column_1); } #else { int newlines = 1; while( (c=NEXT_CHAR)==10) { newlines++; } BACK_UP; /* Go back and check c again */ SETSTATE(S_column_1); } #endif case '\t': UPDATE_STYLE; /* Must be in new style */ /* FALL THROUGH! */ case ' ': OUTPUT(c); output_in_word = 0; SETSTATE(S_text); default: /* New word */ /* The character is non-white. Print a space if necessary. */ UPDATE_STYLE; /* Must be in new style */ if (output_in_word) { OUTPUT(' '); } rcdata: if (c=='&') { /* Entities */ c = NEXT_CHAR; switch (c) { case 'a': if (check("AMP;")) { c = '&'; goto printable; }; break; case 'l': if (check("LT;")) { c = '<'; goto printable; }; break; case 'g': if (check("GT;")) { c = '>'; goto printable; }; break; case 'q': if (check("QUOT;")){ c = '"'; goto printable; }; break; default: break; } if (TRACE) fprintf(stderr, "HTML: Bad entity.\n"); SETSTATE(S_word); } printable: OUTPUT(c); /* First char of new word */ output_in_word = 1; SETSTATE(S_word); /* Now take rest of word faster */ } /* switch(c) */ break; case S_tag_start: switch (c) { case 'A': case 'a': SETSTATE(S_tag_a); case 'd': case 'D': SETSTATE(S_tag_d); case 'H': case 'h': SETSTATE(S_tag_h); case 'i': case 'I': SETSTATE(S_tag_i); case 'L': case 'l': SETSTATE(S_tag_l); case 'n': case 'N': SETSTATE(S_tag_n); case 'O': case 'o': SETSTATE(S_tag_o); case 'p': case 'P': SETSTATE(S_tag_p); case 'r': case 'R': SETSTATE(check("RESTOFFILE") ? S_restoffile:S_junk_tag) case 'T': case 't': SETSTATE(check("TITLE>") ? S_title : S_junk_tag); case 'U': case 'u': SETSTATE(S_tag_u); case 'X': case 'x': SETSTATE( check("XMP>") ? parse_example(&Example, "") : S_junk_tag); case '/': SETSTATE( S_tag_end); default: SETSTATE( S_junk_tag); } /* switch on character */ break; case S_tag_end: switch (c) { case 'A': case 'a': SETSTATE(S_end_a); case 'D': case 'd': SETSTATE(S_end_d); case 'H': case 'h': SETSTATE(S_end_h); case 'I': case 'i': if (check("ISINDEX")) isIndex = YES; SETSTATE(S_junk_tag); case 'n': case 'N': SETSTATE(check("NODE>") ? S_done : S_junk_tag) case 'O': case 'o': SETSTATE(S_end_o); case 'P': case 'p': if (check("PRE")) { end_style(); white_significant = NO; SETSTATE(S_junk_tag); } case 'U': case 'u': SETSTATE(S_end_u); default: SETSTATE(S_junk_tag); } /* switch on character */ break; case S_junk_tag: SETSTATE( (c=='>') ? S_text : S_junk_tag); #ifdef CERN_WEIRDO case S_junk_line: SETSTATE( (c=='\n') ? S_column_1 : S_junk_line); #endif case S_tag_i: switch(c) { #ifdef CERN_WEIRDO case '1': SETSTATE(S_junk_line); /* Junk I1 */ #endif case 's': case 'S': if (check("SINDEX")) isIndex = YES; SETSTATE(S_junk_tag); default: SETSTATE(S_junk_tag); } break; case S_tag_a: switch(c) { case 'd': case 'D': if (!check("DDRESS>")) { SETSTATE(S_junk_tag) }; start_style(&addressStyle); SETSTATE( S_text); case '\n': case ' ': case '>': reference_length = 0; anchor_name_length = 0; SETSTATE(S_anchor); } /* switch on character */ break; case S_tag_p: if ((c==' ') || (c=='>')) { output_paragraph(); SETSTATE( c=='>'? S_text : S_junk_tag); } if ((c=='R') || (c=='r')) { /*
 */
			    if (check("RE")) {
				start_style(&Preformatted);
				update_style();
			        white_significant = YES;
				SETSTATE( S_junk_tag);
			    }
			}
			if ((c=='L') || (c=='l')) {		/* OBSOLETE @@ */
			    if (check("LAINTEXT>")) {
				if (TRACE) printf("Loading as plain text\n");
				[self readText:sgmlStream];	/* will read to end */
				SETSTATE(S_done);		/* Inhibit RTF load */
                             }
			 }
			 SETSTATE(S_junk_tag);

	case S_tag_lis:
	    		SETSTATE( check("TING>") ? 
			  parse_example(&Listing, "")
			  : S_junk_tag);


/* Subnodes are delimited by .... They have the same address as the
** node, but the anchor IDs must be different. This is not thought out.	@@
** Perhaps a hierarchical anchor ID format ....
*/
	case S_tag_n:
	    switch(c) {
	    case 'o':
	    case 'O':	if (check("ODE>")) {	/* Load a subnode */
			    if(TRACE)  printf("Loading subnode...NOT IMPLEMENTED\n");
#ifdef NOT_DEFINED
			    Anchor * a = [Anchor new];
			    HyperText * SN;
			    [a setAddress:[nodeAnchor address]];
			    SN = [HyperText newAnchor:a Server:server];
			    [alsoStore addObject:SN];
			    SN->storeWith = self;
			    [SN readSGML:sgmlStream diagnostic:diagnostic];
			    /* But leave it hidden from view for now. */
#endif
	    		}
	    		SETSTATE(S_text);
			
	    case 'E':					/* '9')) {
			    nextAnchorNumber = value;
			    if (TRACE) fprintf(stderr, "Next anchor number: %i\n", value);
			    BACK_UP;
			    SETSTATE(S_junk_tag);
			    break;
			}
			value = value*10 + (c-'0');
		    }
		}
		SETSTATE(S_junk_tag);
		
	    } /* switch */
	    break;
	    
/*	Parse anchor tag:
**	----------------
*/
	case S_anchor:
	    if (c==' ') SETSTATE( S_anchor);			/* Ignore spaces */
	    if ((c=='H')||(c=='h')) {
		if (check("HREF = ")) {
		    SETSTATE( S_href);
		}
	    }
	    if ((c=='N')||(c=='n')) {
		if (check("NAME = ")) {
		    SETSTATE( S_aname);
		}
	    }
	    
	    if (c=='>') {			/* Anchor tag is over */
		/* Should use appendStartAnchor! @@@ */
		HTStyle * style = HTStyleNew();
	    	char * parsed_address;
		int anchorNumber;
		
	        reference[reference_length]=0;		/* Terminate it */
	        anchor_name[anchor_name_length]=0;	/* Terminate it */

		style->anchor =
		  *anchor_name ? [Anchor newParent:nodeAnchor tag:anchor_name]
			      : [self anchor];
			      
		/* If next anchor number not specified, ensure it is safe */
		
		if ((anchor_name[0] == ANCHOR_ID_PREFIX)
		&&  (sscanf(anchor_name+1, "%i", &anchorNumber) > 0))	/* numeric? */
		 if (anchorNumber >= nextAnchorNumber)
		  nextAnchorNumber = anchorNumber+1;		/* Prevent reuse */
		
		
		[(Anchor *)style->anchor isLastChild];	/* Put in correct order */	      
		if (*reference) {			/* Link only if href */
		    parsed_address = HTParse(reference, [nodeAnchor address],
		    	 PARSE_ALL);
		    [(Anchor *)(style->anchor) linkTo:
		    			[Anchor newAddress:parsed_address]];
		    free(parsed_address);
		}
		
		UPDATE_STYLE;
		SET_STYLE(style);		/* Start anchor here */
		free(style);
		SETSTATE(S_text);
	    }
	    printf("SGML: Bad attribute in anchor.\n");
	    SETSTATE( S_junk_tag);
    
	case S_href:
	    if (c=='"') SETSTATE (S_href_quoted);
	case S_href_unquoted:
	    if ((c==' ') || (c=='\n')) SETSTATE( S_anchor);
	    if (c=='>'){
	        BACK_UP;
	    	SETSTATE( S_anchor);
	    }
	    if (reference_length<255) {
		reference[reference_length++] = c;
	    }
	    SETSTATE(S_href_unquoted);

	case S_href_quoted:
	    if (c=='"') SETSTATE( S_anchor);
	    if (reference_length<255) {
		reference[reference_length++] = c;
	    }
	    SETSTATE( state);

	case S_aname:
	    if ((c==' ') || (c=='\n')) SETSTATE( S_anchor);
	    if (c=='>'){
	        BACK_UP;
	    	SETSTATE( S_anchor);
	    }
	    if (anchor_name_length<255) {
		anchor_name[anchor_name_length++] = c;
	    }
	    SETSTATE( state);
    
	case S_end_a:
	    switch(c) {
	    case 'd':					/* End address */
	    case 'D':
	    	if (!check("DDRESS >")) SETSTATE(S_junk_tag);
		end_style();
		SETSTATE(S_text);

	    case '>':					/* End anchor */
		{
		    [HT appendEndAnchor];
		    SETSTATE(S_text);
		}
		
	    default: SETSTATE(S_junk_tag);
	    } /* switch c */
	    break;

/*	Parse glossary tags
**	-------------------
**
**	We allow 
but we do not allow
(text)
... */ case S_tag_d: switch(c) { case 'L': case 'l': /* Start Definition list
*/ (void) check("L> <"); /* Ignore first DT */ c = NEXT_CHAR; if (c=='/') { check("/DL>"); } else { (void) check("DT>"); start_style(&Glossary); } SETSTATE(S_text); case 'T': case 't': /* Definition term
*/ output_paragraph(); SETSTATE(S_junk_tag); case 'D': case 'd': /* Definition definition
*/ OUTPUT('\t'); SETSTATE(S_junk_tag); } /*switch c */ break; case S_end_d: /* end definition list
*/ if ((c != 'l')&&(c!='L')) SETSTATE(S_junk_tag); end_style(); SETSTATE(S_junk_tag); /* Parse highlighting and headers ** ------------------------------ ** @ All these formats should be nested, and should be defined by a style sheet. */ case S_tag_h: switch (c) { case '1': case '2': case '3': case '4': case '5': case '6': start_style(&Heading[c-'1']); update_style(); #ifdef CERN_WEIRDO end_style_on_nl = YES; /* Style can end at line end */ #endif SETSTATE( S_junk_tag); case 'P': case 'p': switch (c=NEXT_CHAR) { case '1': case '2': case '3': start_highlighting(Highlighting[c-'1']); SETSTATE( S_junk_tag); default: SETSTATE( S_junk_tag); } break; default: SETSTATE( S_junk_tag); } /* switch c */ break; case S_end_h: switch (c) { case '1': case '2': case '3': case '4': case '5': case '6': #ifdef CERN_WEIRDO end_style_on_nl = NO; /* That's over. */ #endif end_style(); SETSTATE( S_junk_tag); case 'P': case 'p': switch (NEXT_CHAR) { case '1': case '2': case '3': end_highlighting(); SETSTATE( S_junk_tag); default: SETSTATE( S_junk_tag); } /* switch */ break; default: SETSTATE( S_junk_tag); } /* switch c */ break; /* Parse Lists, ordered and unordered ** ---------------------------------- ** ** This only affects the horizontal line format, not the font. */ case S_tag_o: case S_tag_u: if ((c == 'l') || (c=='L')) { (void) check("L>
  • "); /* Ignore first LI after UL */ start_style(&listStyle); } SETSTATE(S_text); case S_tag_l: switch(c) { case 'I': case 'i': /* List element
  • */ c = NEXT_CHAR; if (c=='S') { SETSTATE(S_tag_lis); } output_paragraph(); SETSTATE(S_text); default: SETSTATE(S_junk_tag); } /*switch c */ break; case S_end_o: /* end n list */ case S_end_u: /* end n list */ if ((c != 'l')&&(c!='L')) SETSTATE(S_junk_tag); end_style(); SETSTATE(S_junk_tag); /* Parse rest of file on another format */ case S_restoffile: switch (c) { case ' ': case '\n': case '\t': break; case 'p': case 'P': if (check("PLAINTEXT>")) { if (TRACE) printf("Loading as plain text\n"); start_style(&Example); LOADPLAINTEXT; SETSTATE(S_done); /* ... */ } case 'R': case 'r': if (check("RTF>")) { if (TRACE) printf("Loading as RTF\n"); [self readRichText:sgmlStream]; /* will read to end */ [self adjustWindow]; /* Fix scrollers */ SETSTATE(S_done); /* Inhibit RTF load */ } } break; /* Parse .. */ case S_title: if (c=='<') { if (check("")) { title[title_length]=0; /* Add a terminator */ if (TRACE)printf("\nTitle:\t`%s'\n", title); [[self window] setTitle:title]; SETSTATE( S_text); } else SETSTATE( S_junk_tag); /* @@@ forgets < in titles! */ } else { if (title_length < 255) title[title_length++] = c; SETSTATE( state); } /* if */ case S_done: break; /* Should never happen */ } /* switch state */ } /* for loop */ if ((state!=S_text) && (state != S_done)) if(TRACE) printf("*** Unfinished SGML file: Left in state %i\n", state); if (state != S_done) { OUTPUT('\n'); /* Ensure that the text always ends in \n for ScanALine */ FINISH_OUTPUT; } /* Clean up any styles left nested */ while (styleStack) { NestedStyle * N = styleStack; styleStack = N->next; if (TRACE) printf("HT: Left in style at end of document!\n"); free(N); } [window setDocEdited:NO]; tFlags.changeState = 0; /* Please notify delegate if changed */ return self; } /* readSGML:diagnostic: */ /* Write SGML File back OUT ** ------------------------ ** ** This is currently quite NeXT-specific. ** ** We run through te runs. When a characteristic of a run changes, we ** output the approporiate SGML code. When several characteristics change at ** the same place, we output the code in an order such that the resulting ** structures wil be nested. This means first unwrapping the old ones, and ** then entering the new ones. For example, it is better to produce ** **

    ...

    ... ** than ** **

    ...

    ... ** ** The special treatment of newlines is because we want to strip extra newlines ** out. We ignore newlines at the beginning and end of the para style, ** and we treat multiple newlines as a single paragraph mark. ** ** Bugs: @@@ Highlighting is ignored. ** @@@ end text is ignored. */ #define LINE_WRAP 64 /* Start thinking about line wrap here */ static int SGML_gen_newlines; /* Number of newlines pending during SGML generation */ static SGML_gen_errors; /* Number of unrcognizable runs */ static SGML_style * currentSGML; static const char * saveName; /* pointer to name node is being saved under */ static char * prefix; /* Pointer to prefix string to be junked */ static int lineLength; /* Number of characters on a line so far */ /* This function, for any paragraph style, finds the SGML style, if any */ SGML_style * findSGML(void *para) { int i; if (!para) return &Normal; /* Totally unstyled becomes Normal */ for (i=0; istyle; if(style) { if (style->paragraph == para) return S; } } } if (TRACE) printf("HT: Can't find SGML style!\n"); SGML_gen_errors++; return &Normal; } /* This function generates the code for one run, given the previous run. ** */ void change_run(NXRun *last, NXRun *r) { int chars_left = r->chars; if (r->info != last->info) { /* End anchor */ if (last->info) NXPrintf(sgmlStream, ""); } if (r->paraStyle != last->paraStyle) if (last->paraStyle) { /* End paragraph */ if (currentSGML) NXPrintf(sgmlStream, "%s", currentSGML->end_tag); else NXPrintf(sgmlStream,"

    \n"); lineLength = 0; /* At column 1 */ } if (r->paraStyle != last->paraStyle) { /* Start paragraph */ currentSGML = findSGML(r->paraStyle); if (currentSGML) { if (currentSGML->free_format) while(chars_left && WHITE(*read_pointer)) {/* Strip leading */ (chars_left)--; /* white space */ (void) NEXT_TEXT_CHAR; } NXPrintf(sgmlStream, "%s", currentSGML->start_tag); prefix = currentSGML->start_text; } SGML_gen_newlines=0; /* Cancel */ } if (r->info != last->info) { /* Start anchor */ if (SGML_gen_newlines) { /* Got anchor, need paragraph separator */ NXPrintf(sgmlStream, "%s", currentSGML->paragraph_tag); SGML_gen_newlines=0; /* paragraph flushed. */ } if (r->info) { Anchor * a = (Anchor *) r->info; Anchor * d = [a destination]; NXPrintf(sgmlStream, ""); } } /* Now output the textual part of the run ** ** Within the prefix region (prefix!=0), we discard white space and ** characters matching *prefix++. Note the prefix string may contain white space. ** ** The SGML_gen_newlines flag means that newlines have been found. They are ** not actually implemented unless some more non-white text is found, so that ** trailing newlines on the end of paragraphs are stripped. ** ** The line wrapping is primitive in the extreme, as only text characters are ** counted. In practise it limits the length of any line to a reasonable amount, ** though this is not guarranteed. */ { while (chars_left) { char c = NEXT_TEXT_CHAR; chars_left--; if (prefix) { if (*prefix) { if (c==*prefix) { ++prefix; continue; /* Strip prefix characters */ } if (WHITE(c)) continue; /* Strip white space */ if (TRACE) printf( "HTML: WARNING: Paragraph prefix incomplete: %i found where %i expected.\n", c, *prefix); } prefix=0; /* Prefix is over */ } if (c=='\n') { /* Paragraph Marks: */ if (currentSGML->free_format) { SGML_gen_newlines++; /* Just flag it */ prefix = currentSGML->paragraph_text; } else { NXPrintf(sgmlStream, "%s", currentSGML->paragraph_tag); } lineLength = 0; /* At column 1 */ } else { /* Not newline */ if (SGML_gen_newlines) {/* Got text, need paragraph separator */ NXPrintf(sgmlStream, "%s", currentSGML->paragraph_tag); SGML_gen_newlines=0; /* paragraph flushed. */ lineLength = 0; /* At column 1 */ } if (c=='\t') { if (currentSGML) NXPrintf(sgmlStream, "%s", currentSGML->tab_tag); else NXPrintf(sgmlStream, "\t"); } else { /* Not tab or newline */ lineLength ++; /* @@bug doesn't count entity names */ if ((currentSGML->free_format) && (lineLength++ > LINE_WRAP) /* Wrap lines if we can */ && (c==' ')) { c = '\n'; lineLength = 0; } if (currentSGML->litteral) { NXPrintf(sgmlStream, "%c", c); } else { switch(c) { case '<': NXPrintf(sgmlStream, "<"); break; case '&': NXPrintf(sgmlStream, "&"); break; default: NXPrintf(sgmlStream, "%c", c); break; } /* switch */ } /* not litteral */ } } } } } /* change_run */ /* This is the body of the SGML output method. */ - writeSGML:(NXStream *) stream relativeTo:(const char *)aName { NXRun * r = theRuns->runs; int sor; /* Character position of start of run */ NXRun dummy; dummy.paraStyle = 0; dummy.info = 0; dummy.chars = 0; SGML_gen_newlines=0; /* Number of newlines read but not inserted */ HT = self; saveName = aName; sgmlStream = stream; SGML_gen_errors = 0; currentSGML = 0; prefix = 0; /* No prefix to junk */ START_INPUT; lineLength = 0; /* Starting in column 1 */ NXPrintf(stream, "

    \n"); NXPrintf(stream, "%s", [window title]); if (nextAnchorNumber) NXPrintf(stream, "\n\n", nextAnchorNumber); NXPrintf(stream, "
    \n"); NXPrintf(stream, ""); /* Change style tags etc */ change_run(&dummy, r); /* Start first run */ for (sor=r++->chars; sorchars) { if (TRACE) printf("%4i: %i chars in run %3i.\n", sor, r->chars, r-theRuns->runs); change_run(r-1, r); /* Runs 2 to N */ } change_run(r, &dummy); /* Close last run */ tFlags.changeState = 0; /* Please notify delegate if changed */ NXPrintf(stream, "\n"); return (SGML_gen_errors) ? nil : self; } /* switch state */ } /* for loop */ if ((state!=S_text) && (state != S_done)) if(TRACE) printf("*** Unfinished SGML file: Left in state %i\n", state); if (state != S_done) { OUTPUT('\n'); /* Ensure that the text always ends in \n for ScanALine */ FINISH_OUTPUT; } /* Clean up any styles left nested */ while (styleStack) { NestedStyle * N = styleStack; styleStack = N->next; if (TRACE) printf("HT: Left in sWWW/NextStep/Implementation/FileAccess.h 644 472 105 511 5014162131 13473 /* Generated by Interface Builder */ #import "HyperAccess.h" @interface FileAccess:HyperAccess { } + initialize; - saveAs: sender; - saveAsRichText: sender; - saveAsPlainText: sender; - makeNew:sender; - linkToNew:sender; - linkToFile:sender; - openMy:(const char *)filename diagnostic:(int)diagnostic; - goHome:sender; @end .m80SourceFiles.html8 HyperManager.m8 HyperAccess.h8 HyperAccess.m8HTFile.c8F ParseHTML.h8 FileAccess.h8; Bugs.htmWWW/NextStep/Implementation/Bugs.html 664 472 105 10074 5310621670 13165
    Bug list for the WorldWideWeb application on the NeXT

    Bug List - Wish List

    The following are noted problems with the WWW app on the NeXT . See also the list of bug fixes and features added. These are the serious bugs first:
    • Spurious error message: DPS client library error: PostScript program error, DPSContext 160128 %%[ Error: invalidid; OffendingCommand: orderwindow ]%% is produced when an index window is closed or miniaturized but is not the key window. [This may be due to an earlier orderFront: being sent to the search panel during the index window's delegate's windowDidBecomeMain: and this leaving the window list or something in an impropper state].
    • No Alert Panel when Cmd/O with bad access code.
    • Inserting at beginning of paragraph get style of previous paragraph!
    • Saving a reference to a news article in an html file screws up the address (adds a directory to it!)
    • Crash is news host not set up still?!
    • If no tags, then random format used for HTML.
    • Highlighting is not implemented. Any character highlighting is removed from a file when it is saved (except that of the paragraph styles).
    • Quit doesn't check for unsaved files!

    Here are the less serious ones:

    • When you do a CMD-O [or Cmd/F] it probably should position the text cursor in the field where you type the reference (Ira H. Fuchs )
    • Supress anchors in printed document using stylesheet
    • Home page algoithm should match other systems.
    • The password given for anonymous FTP should have the actual user name, not "user".
    • If (save) fails, then try (save as) to get, amongst other things, path creation for the profile files.
    • Style editor does not allow styles to be modified properly.
    • Cutting and pasting anchors leaves them grey but strips them of their anchor status. (This is because the "info" inormation is not copied in to rtf. Can we extend rtf?).
    • Missing window resize bar with NeXTStep 3.0
    • HyperText is not automatically resized when one resizes the window with the mouse.

    News:

    • Reply 430 "article not available(?)" converted to "cannot contact server". Must be more intelligent about different replies.
    • Should try loading saved file instead of unavailable article.

    Improvements desirable:

    • Dispense with the SGML tag in stylesheets, or make stylesheet completely SGML-oriented.
    • rtf or SGML for style sheet format.
    • Remote files should be copied locally, especially if they require opening in the workspace. (Ira H. Fuchs )
    • File access: open directories as README plus file list - or open them in the workspace?
    • Perhaps have selection of various blanks which one could use, for annotations, text sections, footnotes, etc.
    • A style menu with each style as an item
    • Automatically insert paragraph prefix when RETURN pushed in list, delete when paragraph deleted.

    Not included yet but won't be missed yet:

    • Link Typing. Link inspector in general: see destination of link to see why it doesn't work. Document inspector: allow editing of the document title!
    • "Seen flag" - hash table of all seen nodes. Link with .newsrc? Server-specific?
    • Annotate: Make unique message-id for file name, save locally if poss else in user area. Use different template (note.html)
    • Format negociation.
    • Concept of a 32-bit pointer to a name in a hash table for such things as style names, Format names, etc. Associated with a list of things with that name. Is this a NXAtom?
    • Web traversal - Serialisation: Select by link type, depth, breadth/depth-first order. Need this tracing to: Print, search by content, generate index, generate SGML book, etc.
    • Create the $(HOME)/WWW subdirectory if necessary.
    • Default styles held in program in case system styles don't exist.
    • Web checking: finding dangling links or links to things which have disappeared. This is part of serialisation, I guess.
    • Journaling, Graphical overview ab_tag); else NXPrintf(sgmlStream, "\t"); } else { /* Not tab or newline */ lineLength ++; /* @@bug doesn't count entity names */ if ((currentSGML->free_format) && (lineLength++ > LINE_WRAP) /* Wrap lines if we can */ && (c==' ')) { c = '\n'; lineLength = 0; } if (currentSGML->litteral) { NXPrintf(sgmlStream, "%c", c); } else { switch(c) { case '<': NXPWWW/NextStep/Implementation/TcpAccess.m 644 472 105 4377 5304414571 13417 // TcpAccess.m // A HyperAccess object provides access to hyperinformation, using particular // protocols and data format transformations. This one provides access to // the CERNVM FIND system using HTTP/TCP. // History: // 26 Sep 90 Written TBL #import "TcpAccess.h" #import "Anchor.h" #import "HTUtils.h" #import "HTTP.h" /* Module parameters: ** ----------------- ** ** These may be undefined and redefined by syspec.h */ @implementation TcpAccess // Return the name of the access // ----------------------------- - (const char *)name { return "http"; } // Open or search by name // ----------------------- - accessName:(const char *)arg anchor:(Anchor *)anAnchor diagnostic:(int)diagnostic { HyperText * HT; // the new hypertext NXStream * sgmlStream; // Input stream for marked up hypertext int s; // Socket number for returned data /* Get node name: */ // Make a hypertext object with an anchor list. HT = [HyperText newAnchor:anAnchor Server:self]; [HT setupWindow]; [[HT window]setTitle:"Connecting..."]; /* Tell user something's happening */ [HT setEditable:NO]; /* This is read-only data */ // Now, let's get a stream setup up from the server for the sgml data: s = HTTP_Get(arg); if (s<0) return nil; /* Failed .. error will be reported by HTTP_get */ sgmlStream = NXOpenFile(s, NX_READONLY); if (diagnostic == 2) { /* Can read the SGML straight */ [HT readText:sgmlStream]; return HT; } // Now we parse the SGML [[HT window]setTitle:"Loading..."]; /* Tell user something's happening */ [HT readSGML:sgmlStream diagnostic:diagnostic]; // Clean up now it's on the screen: if (TRACE) printf("Closing streams\n"); NXClose(sgmlStream); close(s); return HT; } // Actions: // This will load an anchor which has a name // // On entry, // Anchor's address is valid. // On exit: // If there is no success, nil is returned. // Otherwise, the anchor is returned. // - loadAnchor: (Anchor *) a Diagnostic:(int)diagnostic { HyperText * HT; if ([a node]) { return a; /* Already loaded */ } else { HT = [self accessName:[a address] anchor:a diagnostic:diagnostic]; if (!HT) return nil; return a; } } @end selection of various blanks which one could use, for annotations, text sections, footnotes, etc.
    • A style menu with each style as an item
    • Automatically insert paragraph prefix when RETURN pushed in list, delete when paragraph deleted.

    Not WWW/NextStep/Implementation/HTAccess.c 664 472 105 3230 5032335333 13153 /* Access Manager HTAccess.c ** ============== */ #include "HTParse.h" #include "HTUtils.h" #include "WWW.h" #include "HTFTP.h" #include "HTTP.h" #include "HTFile.h" #include #include #ifdef EXPLICIT_INCLUDES #ifndef vms #include #include #else /* VMS */ #include #include file #include unixio #endif /* VMS */ #else /* not explicit includes */ #include "tcp.h" #endif /* Open a file descriptor for a document ** ------------------------------------- ** ** On entry, ** addr must point to the fully qualified hypertext reference. ** ** On exit, ** returns <0 Error has occured. ** >=0 Value of file descriptor or socket to be used ** to read data. ** *pFormat Set to the format of the file, if known. ** (See WWW.h) ** */ #ifdef __STDC__ int HTOpen(const char * addr, WWW_Format * pFormat) #else int HTOpen(addr, pFormat) char * addr; WWW_Format * pFormat; #endif { char * access=0; /* Name of access method */ access = HTParse(addr, "file:", PARSE_ACCESS); if (0==strcmp(access, "file")) { return HTOpenFile(addr, pFormat); } else if (0==strcmp(access, "http")) { free(access); *pFormat = WWW_HTML; return HTTP_Get(addr); } else if (0==strcmp(access, "news")) { printf("HTAccess: Sorry, Internet news not integrated yet.\n"); } printf("HTAccess: Unknown access `%s'\n", access); free(access); return -1; } /* Close socket opened for reading a file ** -------------------------------------- ** */ #ifdef __STDC__ PUBLIC int HTClose(int soc) #else PUBLIC int HTClose(soc) int soc; #endif { return HTFTP_close_file(soc); } 86 HTString.h8< HTString.cWorldWideWeb.nib~8? PB.gdbinit89 Version.make8 docflash.tiff8GMakefile.preamble7 .dir3_0.wmd8=worldbook.tiff8>worldbook2.tiff87docflash2.tiff8,SourceFiles.html8 ,Bugs.htmlText:sgmlStream]; return HT; } // Now we parse theWWW/NextStep/Implementation/HTFile.h 664 472 105 3077 5013465011 12643 /* File Access HTFile.h ** =========== ** ** These are routines for file access used by WWW browsers. ** */ /* Convert filenames between local and WWW formats ** ----------------------------------------------- ** Make up a suitable name for saving the node in ** ** E.g. $(HOME)/WWW/news/1234@cernvax.cern.ch ** $(HOME)/WWW/http/crnvmc/FIND/xx.xxx.xx */ #ifdef __STDC__ extern char * HTLocalName(const char * name); #else extern char * HTLocalName(); #endif /* Make a WWW name from a full local path name ** */ #ifdef __STDC__ extern char * WWW_nameOfFile(const char * name); #else extern char * WWW_nameOfFile(); #endif /* Determine file format from file name ** ------------------------------------ */ #ifdef __STDC__ extern int HTFileFormat(const char * filename); #else extern int HTFileFormat(); #endif /* Determine write access to a file // -------------------------------- // // On exit, // return value YES if file can be accessed and can be written to. // // Isn't there a quicker way? */ #ifdef __STDC__ extern BOOL HTEditable(const char * filename); #else extern BOOL HTEditable(); #endif /* Open a file descriptor for a document ** ------------------------------------- ** ** On entry, ** addr must point to the fully qualified hypertext reference. ** ** On exit, ** returns <0 Error has occured. ** >=0 Value of file descriptor or socket to be used ** to read data. ** *pFormat Set to the format of the file, if known. ** (See WWW.h) ** */ #ifdef __STDC__ extern int HTOpenFile(const char * addr, WWW_Format* format); #else extern int HTOpenFile(); #endif ething's happening */ [HT readSGML:sgmlStream diagnostic:diagnostic]; // Clean up now it's on the screen: if (TRACE) printf("Closing streams\n"); NXClose(sgmlStream); close(s); return HT; } // Actions: // This will load an anchor which has a name // // On entry, // Anchor's address is valid. // On exit: // If there is no success, nil is returned. // Otherwise, the anchor is returned. // - loadAnchor: (AncWWW/NextStep/Implementation/NewsAccess.m 644 472 105 60272 5304414746 13625 // NewsAccess.m // A HyperAccess object provides access to hyperinformation, using particular // protocols and data format transformations. This one provides access to // the Internet/Usenet News system using NNTP/TCP(). // History: // 26 Sep 90 Written TBL #define NEWS_PORT 119 /* See rfc977 */ #define APPEND /* Use append methods */ #define MAX_CHUNK 40 /* Largest number of articles in one window */ #define CHUNK_SIZE 20 /* Optimum number of articles for quick display */ #import "NewsAccess.h" #import #import "Anchor.h" #import "HTParse.h" #import "HTStyle.h" #import extern HTStyleSheet * styleSheet; #define NEXT_CHAR next_char() #define LINE_LENGTH 512 /* Maximum length of line of ARTICLE etc */ #define GROUP_NAME_LENGTH 256 /* Maximum length of group name */ /* Module parameters: ** ----------------- ** ** These may be undefined and redefined by syspec.h */ #define NETCLOSE close /* Routine to close a TCP-IP socket */ #define NETREAD read /* Routine to read from a TCP-IP socket */ #define NETWRITE write /* Routine to write to a TCP-IP socket */ #ifdef NeXT #import /* NeXT has all this packaged up */ #define ntohs(x) (x) #define htons(x) (x) #else #include /* For bzero etc */ #include /* TCP-specific types */ #include #include #include /* independent */ #include /* independent */ extern char *malloc(); extern void free(); extern char *strncpy(); #endif #include #include /* Must be after netinet/in.h */ #include #import #import "HTUtils.h" /* Coding convention macros */ @implementation NewsAccess // Module-wide variables static const char * NewsHost; static struct sockaddr_in soc_address; /* Binary network address */ static int s; /* Socket for conn. to NewsHost */ static char response_text[LINE_LENGTH+1]; /* Last response from NewsHost */ static HyperText * HT; /* the new hypertext */ static int diagnostic; /* level: 0=none 1=rtf 2=source */ static HTStyle *addressStyle; /* For heading, from address etc */ static HTStyle *textStyle; /* Text style */ #define INPUT_BUFFER_SIZE 4096 static char input_buffer[INPUT_BUFFER_SIZE]; /* Input buffer */ static char * input_read_pointer; static char * input_write_pointer; /* Procedure: Read a character from the input stream ** ------------------------------------------------- */ PRIVATE char next_char(void) { int status; if (input_read_pointer >= input_write_pointer) { status = read(s, input_buffer, INPUT_BUFFER_SIZE); /* Get some more data */ if (status <= 0) return (char)-1; input_write_pointer = input_buffer + status; input_read_pointer = input_buffer; } return *input_read_pointer++; } // Initialisaion for this class // ---------------------------- // // We pick up the NewsHost name from, in order: // // 1. WorldWideWeb // 2. Global // 3. News // 4. Defualt to cernvax.cern.ch (!!!) + initialize { const struct hostent *phost; /* Pointer to host - See netdb.h */ struct sockaddr_in* sin = &soc_address; /* Set up defaults: */ sin->sin_family = AF_INET; /* Family = internet, host order */ sin->sin_port = NEWS_PORT; /* Default: new port, */ /* Get name of Host */ if ((NewsHost = NXGetDefaultValue("WorldWideWeb","NewsHost"))==0) if ((NewsHost = NXGetDefaultValue("News","NewsHost")) == 0) NewsHost = "cernvax.cern.ch"; if (*NewsHost>='0' && *NewsHost<='9') { /* Numeric node address: */ sin->sin_addr.s_addr = inet_addr((char *)NewsHost); /* See arpa/inet.h */ } else { /* Alphanumeric node name: */ phost=gethostbyname((char*)NewsHost); /* See netdb.h */ if (!phost) { NXRunAlertPanel(NULL, "Can't find internet node name `%s'.", NULL,NULL,NULL, NewsHost); CTRACE(tfp, "NewsAccess: Can't find internet node name `%s'.\n",NewsHost); return nil; /* Fail */ } memcpy(&sin->sin_addr, phost->h_addr, phost->h_length); } if (TRACE) printf( "NewsAccess: Parsed address as port %4x, inet %d.%d.%d.%d\n", (unsigned int)ntohs(sin->sin_port), (int)*((unsigned char *)(&sin->sin_addr)+0), (int)*((unsigned char *)(&sin->sin_addr)+1), (int)*((unsigned char *)(&sin->sin_addr)+2), (int)*((unsigned char *)(&sin->sin_addr)+3)); s=-1; /* Disconnected */ return self; } // Return the name of the access // ----------------------------- - (const char *)name { return "news"; } // Get Styles from stylesheet // static void get_styles() { if (!addressStyle) addressStyle = HTStyleNamed(styleSheet, "Address"); if (!textStyle) textStyle = HTStyleNamed(styleSheet, "Example"); } /* Send NNTP Command line to remote host & Check Response ** ------------------------------------------------------ ** ** On entry, ** command points to the command to be sent, including CRLF, or is null ** pointer if no command to be sent. ** On exit, ** Negative status indicates transmission error, socket closed. ** Positive status is an NNTP status. */ static int response(const char * command) { int result; char * p = response_text; if (command) { int status = write(s, command, strlen(command)); if (status<0){ if (TRACE) printf( "NewsAccess: Unable to send comand. Disconnecting.\n"); close(s); s = -1; return status; } /* if bad status */ if (TRACE) printf("NNTP command sent: %s", command); } /* if command to be sent */ for(;;) { if (((*p++=NEXT_CHAR) == '\n') || (p == &response_text[LINE_LENGTH])) { *p++=0; /* Terminate the string */ if (TRACE) printf("NNTP Response: %s\n", response_text); sscanf(response_text, "%i", &result); return result; } /* if end of line */ if (*(p-1) < 0) return -1; /* End of file on response */ } /* Loop over characters */ } // Case insensitive string comparisons // ----------------------------------- // // On entry, // template must be already un upper case. // unknown may be in upper or lower or mixed case to match. // static BOOL match(const char * unknown, const char * template) { const char * u = unknown; const char * t = template; for (;*u && *t && (toupper(*u)==*t); u++, t++) /* Find mismatch or end */ ; return (BOOL)(*t==0); /* OK if end of template */ } // Find Author's name in mail address // ---------------------------------- // // On exit, // THE EMAIL ADDRESS IS CORRUPTED // // For example, returns "Tim Berners-Lee" if given any of // " Tim Berners-Lee " // or " tim@online.cern.ch ( Tim Berners-Lee ) " // static char * author_name(char * email) { char *s, *e; if ((s=index(email,'(')) && (e=index(email, ')'))) if (e>s) { *e=0; /* Chop off everything after the ')' */ return HTStrip(s+1); /* Remove leading and trailing spaces */ } if ((s=index(email,'<')) && (e=index(email, '>'))) if (e>s) { strcpy(s, e+1); /* Remove <...> */ return HTStrip(email); /* Remove leading and trailing spaces */ } return HTStrip(email); /* Default to the whole thing */ } /* Paste in an Anchor ** ------------------ ** ** ** On entry, ** HT has a selection of zero length at the end. ** text points to the text to be put into the file, 0 terminated. ** addr points to the hypertext refernce address, ** terminated by white space, comma, NULL or '>' */ static void write_anchor(const char * text, const char * addr) { char href[LINE_LENGTH+1]; { const char * p; strcpy(href,"news:"); for(p=addr; *p && (*p!='>') && !WHITE(*p) && (*p!=','); p++); strncat(href, addr, p-addr); /* Make complete hypertext reference */ } [HT appendBeginAnchor:"" to:href]; [HT appendText:text]; [HT appendEndAnchor]; } /* Write list of anchors ** --------------------- ** ** We take a pointer to a list of objects, and write out each, ** generating an anchor for each. ** ** On entry, ** HT has a selection of zero length at the end. ** text points to a comma or space separated list of addresses. ** On exit, ** *text is NOT any more chopped up into substrings. */ static void write_anchors(char * text) { char * start = text; char * end; char c; for (;;) { for(;*start && (WHITE(*start)); start++); /* Find start */ if (!*start) return; /* (Done) */ for(end=start; *end && (*end!=' ') && (*end!=','); end++); /* Find end */ if (*end) end++; /* Include comma or space but not NULL */ c = *end; *end = 0; write_anchor(start, start); *end = c; start = end; /* Point to next one */ } } /* Read in an Article ** ------------------ */ // // Note the termination condition of a single dot on a line by itself. // RFC 977 specifies that the line "folding" of RFC850 is not used, so we // do not handle it here. static void read_article() { char line[LINE_LENGTH+1]; char *references=NULL; /* Hrefs for other articles */ char *newsgroups=NULL; /* Newsgroups list */ char *p = line; BOOL done = NO; /* Read in the HEADer of the article: ** ** The header fields are either ignored, or formatted and put into the ** Text. */ if (diagnostic!=2) #ifdef APPEND [HT appendStyle:addressStyle]; #else [HT applyStyle:addressStyle]; #endif while(!done){ if (((*p++=NEXT_CHAR) == '\n') || (p == &line[LINE_LENGTH])) { *--p=0; /* Terminate the string */ if (TRACE) printf("H %s\n", line); if (line[0]=='.') { if (line[1]<' ') { /* End of article? */ done = YES; break; } } else if (line[0]<' ') { break; /* End of Header? */ } else if (match(line, "SUBJECT:")) { [HT setTitle:line+8]; } else if (match(line, "DATE:") || match(line, "FROM:") || match(line, "ORGANIZATION:")) { strcat(line, "\n"); #ifdef APPEND [HT appendText:index(line,':')+1]; #else [HT replaceSel:index(line,':')+1 style:addressStyle]; #endif } else if (match(line, "NEWSGROUPS:")) { StrAllocCopy(newsgroups, HTStrip(index(line,':')+1)); } else if (match(line, "REFERENCES:")) { StrAllocCopy(references, HTStrip(index(line,':')+1)); } /* end if match */ p = line; /* Restart at beginning */ } /* if end of line */ } /* Loop over characters */ #ifdef APPEND [HT appendText:"\n"]; [HT appendStyle:textStyle]; #else [HT replaceSel:"\n" style:addressStyle]; #endif if (newsgroups) { [HT appendText: "\nNewsgroups: "]; write_anchors(newsgroups); free(newsgroups); } if (references) { [HT appendText: "\nReferences: "]; write_anchors(references); free(references); } [HT appendText: "\n\n\n"]; // Read in the BODY of the Article: // p = line; while(!done){ if (((*p++=NEXT_CHAR) == '\n') || (p == &line[LINE_LENGTH])) { *p++=0; /* Terminate the string */ if (TRACE) printf("B %s", line); if (line[0]=='.') { if (line[1]<' ') { /* End of article? */ done = YES; break; } else { /* Line starts with dot */ [HT appendText: &line[1]]; /* Ignore first dot */ } } else { /* Normal lines are scanned for buried references to other articles. ** Unfortunately, it will pick up mail addresses as well! */ char *l = line; char * p; while (p=index(l, '<')) { char *q=index(l,'>'); if (q>p && index(p,'@')) { char c = q[1]; q[1] = 0; /* chop up */ *p = 0; [HT appendText:l]; *p = '<'; /* again */ *q = 0; [HT appendBeginAnchor:"" to:p+1]; *q = '>'; /* again */ [HT appendText:p]; [HT appendEndAnchor]; q[1] = c; /* again */ l=q+1; } else break; /* line has unmatched <> */ } [HT appendText: l]; /* Last bit of the line */ } /* if not dot */ p = line; /* Restart at beginning */ } /* if end of line */ } /* Loop over characters */ } /* Read in a List of Newsgroups ** ---------------------------- */ // // Note the termination condition of a single dot on a line by itself. // RFC 977 specifies that the line "folding" of RFC850 is not used, so we // do not handle it here. static void read_list() { char line[LINE_LENGTH+1]; char *p; BOOL done = NO; /* Read in the HEADer of the article: ** ** The header fields are either ignored, or formatted and put into the ** Text. */ #ifdef APPEND [HT appendText: "\nNewsgroups:\n\n"]; /* Should be haeding style */ #else [HT replaceSel:"\nNewsgroups:\n\n" style:textStyle]; /* Should be heading */ #endif p = line; while(!done){ if (((*p++=NEXT_CHAR) == '\n') || (p == &line[LINE_LENGTH])) { *p++=0; /* Terminate the string */ if (TRACE) printf("B %s", line); if (line[0]=='.') { if (line[1]<' ') { /* End of article? */ done = YES; break; } else { /* Line starts with dot */ #ifdef APPEND [HT appendText: &line[1]]; #else [HT replaceSel:&line[1] style:textStyle]; /* Ignore first dot */ #endif } } else { /* Normal lines are scanned for references to newsgroups. */ char group[LINE_LENGTH]; int first, last; char postable; if (sscanf(line, "%s %i %i %c", group, &first, &last, &postable)==4) write_anchor(line, group); else #ifdef APPEND [HT appendText:line]; #else [HT replaceSel:line style:textStyle]; #endif } /* if not dot */ p = line; /* Restart at beginning */ } /* if end of line */ } /* Loop over characters */ } /* Read in a Newsgroup ** ------------------- ** Unfortunately, we have to ask for each article one by one if we want more ** than one field. ** */ void read_group(const char * groupName, int first_required, int last_required) { char line[LINE_LENGTH+1]; char author[LINE_LENGTH+1]; char subject[LINE_LENGTH+1]; char *p; BOOL done; char buffer[LINE_LENGTH]; char *reference=0; /* Href for article */ int art; /* Article number WITHIN GROUP */ int status, count, first, last; /* Response fields */ /* count is only an upper limit */ sscanf(response_text, " %i %i %i %i", &status, &count, &first, &last); if(TRACE) printf("Newsgroup status=%i, count=%i, (%i-%i) required:(%i-%i)\n", status, count, first, last, first_required, last_required); if (last==0) { [HT appendText: "\nNo articles in this group.\n"]; return; } #define FAST_THRESHOLD 100 /* Above this, read IDs fast */ #define CHOP_THRESHOLD 50 /* Above this, chop off the rest */ if (first_required last)) last_required = last; if (last_required<=first_required) { [HT appendText: "\nNo articles in this range.\n"]; return; } if (last_required-first_required+1 > MAX_CHUNK) { /* Trim this block */ first_required = last_required-CHUNK_SIZE+1; } if (TRACE) printf (" Chunk will be (%i-%i)\n", first_required, last_required); /* Link to earlier articles */ if (first_required>first) { int before; /* Start of one before */ if (first_required-MAX_CHUNK <= first) before = first; else before = first_required-CHUNK_SIZE; sprintf(buffer, "%s/%i-%i", groupName, before, first_required-1); if (TRACE) printf(" Block before is %s\n", buffer); [HT appendBeginAnchor:"" to:buffer]; [HT appendText: " (Earlier articles...)\n\n"]; [HT appendEndAnchor]; } done = NO; /*#define USE_XHDR*/ #ifdef USE_XHDR if (count>FAST_THRESHOLD) { sprintf(buffer, "\nThere are about %i articles currently available in %s, IDs as follows:\n\n", count, groupName); [HT appendText:buffer]; anchor_start = [HT textLength]; sprintf(buffer, "XHDR Message-ID %i-%i\n", first, last); status = response(buffer); if (status==221) { p = line; while(!done){ if (((*p++=NEXT_CHAR) == '\n') || (p == &line[LINE_LENGTH])) { *p++=0; /* Terminate the string */ if (TRACE) printf("X %s", line); if (line[0]=='.') { if (line[1]<' ') { /* End of article? */ done = YES; break; } else { /* Line starts with dot */ /* Ignore strange line */ } } else { /* Normal lines are scanned for references to articles. */ char * space = strchr(line, ' '); if (space++) write_anchor(space, space); } /* if not dot */ p = line; /* Restart at beginning */ } /* if end of line */ } /* Loop over characters */ /* leaving loop with "done" set */ } /* Good status */ }; #endif /* Read newsgroup using individual fields: */ if (!done) { if (first==first_required && last==last_required) [HT appendText:"\nAll available articles:\n\n"]; else [HT appendText: "\nArticles:\n\n"]; for(art=first_required; art<=last_required; art++) { /*#define OVERLAP*/ #ifdef OVERLAP /* With this code we try to keep the server running flat out by queuing just ** one extra command ahead of time. We assume (1) that the server won't abort if ** it get input during output, and (2) that TCP buffering is enough for the ** two commands. Both these assumptions seem very reasonable. However, we HAVE had ** a hangup with a loaded server. */ if (art==first_required) { if (art==last_required) { sprintf(buffer, "HEAD %i\n", art); /* Only one */ status = response(buffer); } else { /* First of many */ sprintf(buffer, "HEAD %i\nHEAD %i\n", art, art+1); status = response(buffer); } } else if (art==last_required) { /* Last of many */ status = response(NULL); } else { /* Middle of many */ sprintf(buffer, "HEAD %i\n", art+1); status = response(buffer); } #else sprintf(buffer, "HEAD %i\n", art); status = response(buffer); #endif if (status == 221) { /* Head follows - parse it:*/ p = line; /* Write pointer */ done = NO; while(!done){ if ( ((*p++=NEXT_CHAR) == '\n') || (p == &line[LINE_LENGTH]) ) { *--p=0; /* Terminate & chop LF*/ p = line; /* Restart at beginning */ if (TRACE) printf("G %s\n", line); switch(line[0]) { case '.': done = (line[1]<' '); /* End of article? */ break; case 'S': case 's': if (match(line, "SUBJECT:")) strcpy(subject, line+8); /* Save author */ break; case 'M': case 'm': if (match(line, "MESSAGE-ID:")) { char * addr = HTStrip(line+11) +1; /* Chop < */ addr[strlen(addr)-1]=0; /* Chop > */ StrAllocCopy(reference, addr); } break; case 'f': case 'F': if (match(line, "FROM:")) strcpy(author, author_name(index(line,':')+1)); break; } /* end switch on first character */ } /* if end of line */ } /* Loop over characters */ sprintf(buffer, "\"%s\" - %s\n", subject, author); if (reference) { write_anchor(buffer, reference); free(reference); reference=0; } else { [HT appendText:buffer]; } /* Change the title bar to indicate progress! */ if (art%10 == 0) { sprintf(buffer, "Reading newsgroup %s, Article %i (of %i-%i) ...", groupName, art, first, last); [HT setTitle:buffer]; } } /* If good response */ } /* Loop over article */ } /* If read headers */ /* Link to later articles */ if (last_required Same article ** xxxxx News group (no "@") */ group_wanted = (index(arg, '@')==0) && (index(arg, '*')==0); list_wanted = (index(arg, '@')==0) && (index(arg, '*')!=0); p1 = HTParse(arg, "", PARSE_PATH|PARSE_PUNCTUATION); if (list_wanted) { strcpy(command, "LIST "); } else if (group_wanted) { char * slash = strchr(p1, '/'); strcpy(command, "GROUP "); first = 0; last = 0; if (slash) { *slash = 0; strcpy(groupName, p1); *slash = '/'; (void) sscanf(slash+1, "%i-%i", &first, &last); } else { strcpy(groupName, p1); } strcat(command, groupName); } else { strcpy(command, "ARTICLE "); if (index(p1, '<')==0) strcat(command,"<"); strcat(command, p1); if (index(p1, '>')==0) strcat(command,">"); } free(p1); strcat(command, "\r\n"); /* CR LF, as in rfc 977 */ } /* scope of p1 */ if (!*arg) return nil; // Ignore if no name // Make a hypertext object with an anchor list. HT = [HyperText newAnchor:anAnchor Server:self]; [HT setupWindow]; [HT selectText:self]; /* Replace everything with what's to come */ // Now, let's get a stream setup up from the NewsHost: for(retries=0;retries<2; retries++){ if (s<0) { [[HT window]setTitle:"Connecting to NewsHost ..."]; /* Tell user */ s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); status = connect(s, (struct sockaddr*)&soc_address, sizeof(soc_address)); if (status<0){ char message[256]; close(s); s=-1; if (TRACE) printf("NewsAccess: Unable to connect to news host.\n"); /* if (retries<=1) continue; WHY TRY AGAIN ? */ NXRunAlertPanel(NULL, "Could not access newshost %s.", NULL,NULL,NULL, NewsHost); sprintf(message, "\nCould not access %s.\n\n (Check default WorldWideWeb NewsHost ?)\n", NewsHost); [HT setText:message]; return HT; } else { if (TRACE) printf("NewsAccess: Connected to news host %s.\n", NewsHost); if ((response(NULL) / 100) !=2) { close(s); s=-1; NXRunAlertPanel("News access", "Could not retrieve information:\n %s.", NULL,NULL,NULL, response_text); [[HT window]setTitle: "News host response"]; [HT setText:response_text]; return HT; } } } /* If needed opening */ [[HT window]setTitle:arg]; /* Tell user something's happening */ status = response(command); if (status<0) break; if ((status/ 100) !=2) { NXRunAlertPanel("News access", response_text, NULL,NULL,NULL); [HT setText:response_text]; close(s); s=-1; // return HT; -- no:the message might be "Timeout-disconnected" left over continue; // Try again } // Load a group, article, etc // [HT appendBegin]; if (list_wanted) read_list(); else if (group_wanted) read_group(groupName, first, last); else read_article(); [HT appendEnd]; [HT setEditable:NO]; /* This is read-only data */ [HT adjustWindow]; return HT; } /* Retry loop */ [HT setText:"Sorry, could not load requested news.\n"]; /* NXRunAlertPanel(NULL, "Sorry, could not load `%s'.", NULL,NULL,NULL, arg); No -- message earlier wil have covered it */ return HT; } // Actions: // ======= // This will load an anchor which has a name // ----------------------------------------- // // On entry, // Anchor's address is valid. // On exit: // If there is no success, nil is returned. // Otherwise, the anchor is returned. // - loadAnchor: (Anchor *) a Diagnostic:(int)diagnostic { HyperText * HT; if (![a node]) { HT = [self accessName:[a address] anchor:a diagnostic:diagnostic]; if (!HT) return nil; [[HT window] setDocEdited:NO]; } return a; } @end f line */ } /* Loop over characters */ /* leaving loop with "done" set */ } /* Good status */ }; #endif /* Read newsgroup using individual fields: */ if (!done) { if (first==first_required && last==last_required) [HT appendText:"\nAll available articles:\n\n"]; else [HT appendText: "\nAWWW/NextStep/Implementation/Features.html 664 472 105 14154 5063612365 14054 Features & Bug Fixes for WorldWideWeb on the NeXT

    Features and Bug Fixes

    The following are features added to the WWW app on the NeXT with time, in reverse chronological order. See also the outstanding bug list . Credits in brackets are to those who mailed me with the bug reports, to whom thanks.

    To be released: version 0.13

    • The Search panel appears automatically when an index is the main window and disappears otherwise. The panel appears in the top right hand corner to be out of the way of document windows. The Cmd-F key and menu item are unnecessary.
    • The "Open from hypertext reference" text is automatically loaded with the address of the main window as a default. This can be picked up and pasted into mail, etc.
    • Bug fix: Plain text windows don't get distortions in the vertical positioning of scan lines when you scroll them any more.
    • Bug fix: Inability to access cs.orst.edu. WWW used the FTP "PASV" command when picking up remote files - some servers (eg cs.orst.edu) don't support this. Now it doesn't - it uses "PORT" instead, which is mandatory for servers. (Ira Fuchs)
    • Bug fix: Failed attempts to load remote files now only produce one Alert panel instead of two one after the other. (Ira H Fuchs)
    • Bug fix: Loading a newsgroup with no articles in crashed the application. Now it doesn't. Also, title bar "Connecting..." no longer left up once the connection has succeeded. (Ira H. Fuchs )
    • Info->Panel is now not editable. (Michael D Mellinger )

    19 Aug 91 version 0.12

    • Bug fix: .rtf files were loaded as plain text. ( burchard@horizon.math.utah.edu (Paul Burchard) )
    • Default.html file shipped with the .app now has absolute references, and so will work on a remote site. (many people!)

    15 Mar 91 version 0.11

    • Feature: If a file is not available locally, or mounted under /Net/hostname/..., the WWW will attempt to pick it up by anonymous FTP giving user@host as password. (Currently the user name given is always "user"!)
    • Bug fix: If one didn't have a personal home file, two error messages would be given on startup. Now the system default is loaded without complaint. (Bug introduced at version 0.9 )

    4 April 91 version 0.10

    • Bug fix: A dummy window created when following a link to a different application would cause crashes if it was resized, or if the link was followed a second time..

    3 April 91 version 0.9

    • Bug fix: Closing a window which has been edited now prompts for a save as it should. Alert panels have been introduced for some file system, communication and server errors.
    • Bug fix: One can now edit files to which one has group access.

    26 March 91 version 0.8

    • Changed anchor highlighting from dark grey to underline, now underline is available. (I may make underline &/or colour a user preference or style later.)
    • Modified paragraph spacing so that dummy spaces are not introduced between two paragraphs neither or which are "Normal" style. Also, now, the space allowed is the larger of the "space before" the next paragraph and the "space after" the preceding paragraph, rather than the sum. Text is generally more compact.
    • Bug fix: A few memory leaks removed with the aid of the excellent MallocDebug utility.
    • Feature: When a new file is made, it is given a default title generated from the filename, irrespective of the title of the template.
    • Fixed version 2 bug: The last run of a bit of text would have its formating information overwritten by the previous run's format. Bug was in v2 [Text rebewRuns:text:frame:tag] not called any more.
    • Fixed version 2 bug: Typing within anchor would produce non-anchor text, breaking the paragraph up when the docuement was saved and reloaded. (Not 100% tested)
    • Version 2 enhancements came for free: Spell checking, "Windows" menu, "Services" menu. "File" (was "Window") menu is now renamed "Document". Also, random crashes seem to have disappeared since V2 (touch wood).
    • Bug fix: Newly created windows can be edited the moment you have given a filename for them to be stored in: the bug which made them uneditable has been removed.
    • Bug fix: No longer crshes when paragraph formatting a selection including the last character.
    • Bug fix: The restyling of text can only occur if document is editable. It sets the document "edited".

    19 March 91 Version 0.7

    • The annoying vertical fidgeting on loading a new window has been removed.
    • " Print " and " Page layout " menu items have been added. Page layout maintains the "PageType" default in the NXDefaults database, so it is persisent between invokations.
    • " Help " and " Open master template " menu items added.
    • "Window" menu renamed "File" to match v2.0 standards.
    • " Link to File " menu item allows direct link to files of this or any other application. (Not directories).
    • Opening remote mounted hypertext files from the workspace now gives them the correct hypertext address, so that links made to them will be correct.

    1 Feb 91 Version 0.5

    • "New file" and "Link to New" and "Unlink" implemented.
    • "Address" style implemented in HTML "ADDRESS" tags.
    • Bug fix: Long relative pathnames now treated properly.

    30 Jan 91 Version 0.4