├── .gitignore ├── LICENSE ├── ManOpen.xcodeproj └── project.pbxproj ├── ManOpen ├── AproposDocument.h ├── AproposDocument.m ├── Base.lproj │ ├── Apropos.xib │ ├── DocController.xib │ ├── ManOpen.xib │ ├── ManPage.xib │ └── PrefPanel.xib ├── CustomInfo.plist ├── FindPanelController.h ├── FindPanelController.m ├── Images.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ └── ManOpen-1 (dragged).png │ └── Contents.json ├── Info-ManOpen.plist ├── Makefile ├── Makefile.postamble ├── Makefile.preamble ├── ManDocument.h ├── ManDocument.m ├── ManDocumentController.h ├── ManDocumentController.m ├── ManOpen-nextstep.tiff ├── ManOpen.entitlements ├── ManOpen.icns ├── ManOpen.iconheader ├── ManOpen.scriptSuite ├── ManOpen.scriptTerminology ├── ManOpen.tiff ├── ManOpen_main.m ├── ManWindowController.h ├── ManWindowController.m ├── NSData+Utils.h ├── NSData+Utils.m ├── PB.project ├── PrefPanelController.h ├── PrefPanelController.m ├── cat2html.tproj │ ├── Makefile │ ├── Makefile.postamble │ ├── Makefile.preamble │ ├── PB.project │ └── cat2html.l ├── cat2rtf.tproj │ ├── Makefile │ ├── Makefile.postamble │ ├── Makefile.preamble │ ├── PB.project │ └── cat2rtf.l ├── en.lproj │ ├── Credits.rtf │ ├── InfoPlist.strings │ ├── Localizable.strings │ └── ServicesMenu.strings ├── h.template ├── helpQMark.tiff └── m.template ├── README.md └── openman.tproj ├── h.template ├── m.template ├── openman.1 └── openman.m /.gitignore: -------------------------------------------------------------------------------- 1 | ManOpen.xcodeproj/project.xcworkspace 2 | ManOpen.xcodeproj/xcuserdata 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2021, Nick Zitzmann All rights reserved. 4 | Copyright (c) 2000-2012, Carl Lindberg 5 | Copyright (c) 1993, Harald Schlangmann 6 | 7 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 12 | 13 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 16 | -------------------------------------------------------------------------------- /ManOpen/AproposDocument.h: -------------------------------------------------------------------------------- 1 | /* AproposDocument.h created by lindberg on Tue 10-Oct-2000 */ 2 | 3 | #import 4 | 5 | @class NSMutableArray; 6 | @class NSTableColumn, NSTableView; 7 | 8 | @interface AproposDocument : NSDocument 9 | @property(nonatomic,retain) IBOutlet NSTableView *tableView; 10 | @property(nonatomic,retain) IBOutlet NSTableColumn *titleColumn; 11 | 12 | - (id)initWithString:(NSString *)apropos manPath:(NSString *)manPath title:(NSString *)title; 13 | - (void)parseOutput:(NSString *)output; 14 | 15 | - (IBAction)saveCurrentWindowSize:(id)sender; 16 | - (IBAction)openManPages:(id)sender; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /ManOpen/AproposDocument.m: -------------------------------------------------------------------------------- 1 | /* AproposDocument.m created by lindberg on Tue 10-Oct-2000 */ 2 | 3 | #import "AproposDocument.h" 4 | #import 5 | #import "ManDocumentController.h" 6 | #import "PrefPanelController.h" 7 | 8 | @interface NSDocument (LionRestorationMethods) 9 | - (void)encodeRestorableStateWithCoder:(NSCoder *)coder; 10 | - (void)restoreStateWithCoder:(NSCoder *)coder; 11 | @end 12 | 13 | @interface AproposDocument () 14 | @property(nonatomic,retain) NSString *searchString; 15 | @property(nonatomic,retain) NSString *title; 16 | @property(nonatomic,retain) NSMutableOrderedSet *> *titlesAndDescriptions; // this is an ordered set, rather than an array, because ordered sets will automatically filter duplicate results 17 | @end 18 | 19 | @implementation AproposDocument 20 | 21 | + (BOOL)canConcurrentlyReadDocumentsOfType:(NSString *)typeName 22 | { 23 | return YES; 24 | } 25 | 26 | - (void)_loadWithString:(NSString *)apropos manPath:(NSString *)manPath title:(NSString *)aTitle 27 | { 28 | ManDocumentController *docController = [ManDocumentController sharedDocumentController]; 29 | NSMutableString *command = [docController manCommandWithManPath:manPath]; 30 | NSData *output; 31 | 32 | self.titlesAndDescriptions = [[NSMutableOrderedSet alloc] init]; 33 | self.title = aTitle; 34 | [self setFileType:@"apropos"]; 35 | 36 | /* Searching for a blank string doesn't work anymore... use a catchall regex */ 37 | if ([apropos length] == 0) 38 | apropos = @"."; 39 | self.searchString = apropos; 40 | 41 | /* 42 | * Starting on Tiger, man -k doesn't quite work the same as apropos directly. 43 | * Use apropos then, even on Panther. Panther/Tiger no longer accept the -M 44 | * argument, so don't try... we set the MANPATH environment variable, which 45 | * gives a warning on Panther (stderr; ignored) but not on Tiger. 46 | */ 47 | // [command appendString:@" -k"]; 48 | [command setString:@"/usr/bin/apropos"]; 49 | 50 | [command appendFormat:@" %@", EscapePath(apropos, YES)]; 51 | output = [docController dataByExecutingCommand:command manPath:manPath]; 52 | /* The whatis database appears to not be UTF8 -- at least, UTF8 can fail, even on 10.7 */ 53 | [self parseOutput:[[NSString alloc] initWithData:output encoding:NSASCIIStringEncoding]]; 54 | } 55 | 56 | - (instancetype)initWithString:(NSString *)apropos manPath:(NSString *)manPath title:(NSString *)aTitle 57 | { 58 | self = [super init]; 59 | if (self) 60 | { 61 | [self _loadWithString:apropos manPath:manPath title:aTitle]; 62 | 63 | if (self.titlesAndDescriptions.count == 0) 64 | { 65 | NSAlert *nothingFoundAlert = [[NSAlert alloc] init]; 66 | 67 | nothingFoundAlert.messageText = NSLocalizedString(@"Nothing found", @"Title of an unsuccessful apropos search"); 68 | nothingFoundAlert.informativeText = [NSString stringWithFormat:NSLocalizedString(@"No pages related to '%@' found", @"Body of an unsuccessful apropos search"), apropos]; 69 | (void)[nothingFoundAlert runModal]; 70 | return nil; 71 | } 72 | } 73 | return self; 74 | } 75 | 76 | 77 | - (NSString *)windowNibName 78 | { 79 | return @"Apropos"; 80 | } 81 | 82 | - (NSString *)displayName 83 | { 84 | return self.title; 85 | } 86 | 87 | - (void)windowControllerDidLoadNib:(NSWindowController *)windowController 88 | { 89 | NSString *sizeString = [[NSUserDefaults standardUserDefaults] stringForKey:@"AproposWindowSize"]; 90 | 91 | [super windowControllerDidLoadNib:windowController]; 92 | 93 | if (sizeString != nil) 94 | { 95 | NSSize windowSize = NSSizeFromString(sizeString); 96 | NSWindow *window = self.tableView.window; 97 | NSRect frame = [window frame]; 98 | 99 | if (windowSize.width > 30.0 && windowSize.height > 30.0) { 100 | frame.size = windowSize; 101 | [window setFrame:frame display:NO]; 102 | } 103 | } 104 | 105 | self.tableView.target = self; 106 | self.tableView.doubleAction = @selector(openManPages:); 107 | [self.tableView sizeLastColumnToFit]; 108 | } 109 | 110 | - (void)parseOutput:(NSString *)output 111 | { 112 | NSArray *lines = [output componentsSeparatedByString:@"\n"]; 113 | NSUInteger i, count = [lines count]; 114 | 115 | if ([output length] == 0) return; 116 | 117 | lines = [lines sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]; 118 | 119 | for (i=0; i= 0L) 155 | { 156 | NSDictionary *titleAndDescription = self.titlesAndDescriptions[clickedRow]; 157 | NSString *manPage = titleAndDescription[@"titles"]; 158 | 159 | [[ManDocumentController sharedDocumentController] openString:manPage oneWordOnly:YES]; 160 | } 161 | } 162 | } 163 | 164 | - (void)printDocumentWithSettings:(NSDictionary *)printSettings showPrintPanel:(BOOL)showPrintPanel delegate:(id)delegate didPrintSelector:(SEL)didPrintSelector contextInfo:(void *)contextInfo 165 | { 166 | NSPrintOperation *op = [NSPrintOperation printOperationWithView:self.tableView]; 167 | [op setShowsPrintPanel:showPrintPanel]; 168 | [op setShowsProgressPanel:showPrintPanel]; 169 | [op runOperationModalForWindow:self.tableView.window delegate:nil didRunSelector:NULL contextInfo:NULL]; 170 | } 171 | 172 | /* NSTableView dataSource */ 173 | - (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView 174 | { 175 | return self.titlesAndDescriptions.count; 176 | } 177 | 178 | - (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row 179 | { 180 | return self.titlesAndDescriptions[row][tableColumn.identifier]; 181 | } 182 | 183 | /* Document restoration */ 184 | #define RestoreSearchString @"SearchString" 185 | #define RestoreTitle @"Title" 186 | 187 | - (void)encodeRestorableStateWithCoder:(NSCoder *)coder 188 | { 189 | [super encodeRestorableStateWithCoder:coder]; 190 | [coder encodeObject:self.searchString forKey:RestoreSearchString]; 191 | [coder encodeObject:self.title forKey:RestoreTitle]; 192 | } 193 | 194 | - (void)restoreStateWithCoder:(NSCoder *)coder 195 | { 196 | [super restoreStateWithCoder:coder]; 197 | 198 | if (![coder containsValueForKey:RestoreSearchString]) 199 | return; 200 | 201 | NSString *search = [coder decodeObjectForKey:RestoreSearchString]; 202 | NSString *theTitle = [coder decodeObjectForKey:RestoreTitle]; 203 | NSString *manPath = [[NSUserDefaults standardUserDefaults] manPath]; 204 | 205 | [self _loadWithString:search manPath:manPath title:theTitle]; 206 | [[self windowControllers] makeObjectsPerformSelector:@selector(synchronizeWindowTitleWithDocumentName)]; 207 | [self.tableView reloadData]; 208 | } 209 | 210 | @end 211 | -------------------------------------------------------------------------------- /ManOpen/Base.lproj/Apropos.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /ManOpen/Base.lproj/ManOpen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | -------------------------------------------------------------------------------- /ManOpen/Base.lproj/ManPage.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /ManOpen/CustomInfo.plist: -------------------------------------------------------------------------------- 1 | { 2 | NSInfoPlistVersion = "5.0"; 3 | NSAppVersion = "Version 2.5.1"; 4 | NSHumanReadableShortName = "ManOpen"; 5 | NSHumanReadableCompleteName = "ManOpen"; 6 | NSHumanReadableCopyright = "Copyright (c) 1999-2005 Carl Lindberg"; 7 | NSTypes = ( 8 | { 9 | NSName = "man"; 10 | NSHumanReadableName = "Man page (nroff source)"; 11 | NSUnixExtensions = ("man"); 12 | NSDOSExtensions = ("man"); 13 | NSRole = Viewer; 14 | NSDocumentClass = ManDocument; 15 | CFBundleTypeIconFile = "ManOpen.icns"; 16 | }, 17 | { 18 | NSName = "mangz"; 19 | NSHumanReadableName = "Man page (nroff source), gzipped"; 20 | NSUnixExtensions = ("man.gz"); 21 | NSRole = Viewer; 22 | NSDocumentClass = ManDocument; 23 | CFBundleTypeIconFile = "ManOpen.icns"; 24 | }, 25 | { 26 | NSName = "cat"; 27 | NSHumanReadableName = "Cat file (plain text)"; 28 | NSUnixExtensions = ("cat"); 29 | NSDOSExtensions = ("cat"); 30 | NSRole = Viewer; 31 | NSDocumentClass = ManDocument; 32 | CFBundleTypeIconFile = "ManOpen.icns"; 33 | }, 34 | { 35 | NSName = "catgz"; 36 | NSHumanReadableName = "Cat file (plain text), gzipped"; 37 | NSUnixExtensions = ("cat.gz"); 38 | NSRole = Viewer; 39 | NSDocumentClass = ManDocument; 40 | CFBundleTypeIconFile = "ManOpen.icns"; 41 | } 42 | ); 43 | NSServices = ( 44 | { 45 | NSPortName = ManOpen; 46 | NSMessage = openFiles; 47 | NSSendTypes = (NSFilenamesPboardType); 48 | NSMenuItem = { 49 | default = "ManOpen/Open File"; 50 | }; 51 | NSExecutable = ManOpen; 52 | }, 53 | { 54 | NSPortName = ManOpen; 55 | NSMessage = openSelection; 56 | NSSendTypes = (NSStringPboardType); 57 | NSMenuItem = { 58 | default = "ManOpen/Open Selection"; 59 | }; 60 | NSKeyEquivalent = { 61 | default = M; 62 | }; 63 | NSExecutable = ManOpen; 64 | }, 65 | { 66 | NSPortName = ManOpen; 67 | NSMessage = openApropos; 68 | NSSendTypes = (NSStringPboardType); 69 | NSMenuItem = { 70 | default = "ManOpen/Apropos"; 71 | }; 72 | NSKeyEquivalent = { 73 | default = A; 74 | }; 75 | NSExecutable = ManOpen; 76 | } 77 | ); 78 | 79 | /* MacOS X Settings... force them for the time being */ 80 | 81 | CFBundleIconFile = "ManOpen.icns"; 82 | CFBundleShortVersionString = "2.5.1"; 83 | CFBundleGetInfoString = "ManOpen 2.5.1 (c) 2005 Carl Lindberg"; // GetInfo puts "Version: " in front 84 | CFBundleIdentifier = "org.clindberg.ManOpen"; 85 | 86 | /* Apple introduced an "x-man-page" URL scheme with Panther. */ 87 | NSAppleScriptEnabled = YES; 88 | CFBundleURLTypes = ( 89 | { 90 | CFBundleURLName = "Man Page URL"; 91 | CFBundleURLSchemes = ("x-man-page"); 92 | } 93 | ); 94 | 95 | } 96 | -------------------------------------------------------------------------------- /ManOpen/FindPanelController.h: -------------------------------------------------------------------------------- 1 | 2 | #import "SystemType.h" 3 | #ifdef OPENSTEP 4 | #import "NSWindowController.h" 5 | #else 6 | #import 7 | #endif 8 | 9 | @class NSText, NSTextField, NSButton; 10 | 11 | /* This class has been obsoleted by setUsesFindPanel: in NSTextView, so we no longer use it. */ 12 | @interface FindPanelController : NSWindowController 13 | { 14 | IBOutlet NSTextField *stringField; 15 | IBOutlet NSTextField *statusField; 16 | IBOutlet NSButton *nextButton; 17 | IBOutlet NSButton *previousButton; 18 | IBOutlet NSButton *ignoreCaseButton; 19 | } 20 | 21 | + (id)sharedInstance; 22 | 23 | - (IBAction)findNext:(id)sender; 24 | - (IBAction)findPrevious:(id)sender; 25 | - (IBAction)jumpToSelection:(id)sender; 26 | - (IBAction)enterSelection:(id)sender; 27 | 28 | - (NSText *)targetText; 29 | 30 | - (NSString *)searchString; 31 | - (NSString *)stringFromTargetText; 32 | - (NSString *)stringFromFindPasteboard; 33 | - (void)saveStringToFindPasteboard:(NSString *)aString; 34 | 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /ManOpen/FindPanelController.m: -------------------------------------------------------------------------------- 1 | 2 | #import "FindPanelController.h" 3 | #import 4 | #import 5 | #import 6 | #import 7 | #import 8 | #import 9 | #import 10 | #import 11 | #import 12 | #import 13 | #import 14 | #import 15 | #import "ManDocumentController.h" 16 | #import "ManDocument.h" 17 | 18 | @interface NSText (Search) 19 | - (BOOL)findString:(NSString *)aString 20 | ignoreCase:(BOOL)ignoreCase 21 | backwards:(BOOL)backwards 22 | wrap:(BOOL)wrap; 23 | @end 24 | 25 | @interface FindPanelController (Private) 26 | - (BOOL)searchGoingBackwards:(BOOL)backwards; 27 | - (void)_findNext:(id)sender; 28 | - (void)_findPrevious:(id)sender; 29 | - (void)_findEnter:(id)sender; 30 | @end 31 | 32 | @implementation FindPanelController 33 | 34 | + (id)allocWithZone:(NSZone *)aZone 35 | { 36 | return [self sharedInstance]; 37 | } 38 | 39 | + (id)sharedInstance 40 | { 41 | static id instance = nil; 42 | 43 | if (instance == nil) { 44 | instance = [[super allocWithZone:NULL] initWithWindowNibName:@"FindPanel"]; 45 | [instance setWindowFrameAutosaveName:@"Find Panel"]; 46 | [instance setShouldCascadeWindows:NO]; 47 | } 48 | 49 | return instance; 50 | } 51 | 52 | - (void)windowDidLoad 53 | { 54 | [super windowDidLoad]; 55 | [nextButton setTarget:self]; 56 | [nextButton setAction:@selector(_findNext:)]; 57 | [previousButton setTarget:self]; 58 | [previousButton setAction:@selector(_findPrevious:)]; 59 | [stringField setTarget:self]; 60 | [stringField setAction:@selector(_findEnter:)]; 61 | [statusField setTextColor:[NSColor controlShadowColor]]; 62 | [statusField setStringValue:@""]; 63 | [[self window] useOptimizedDrawing:YES]; 64 | } 65 | 66 | - (void)showWindow:(id)sender 67 | { 68 | [super showWindow:sender]; 69 | [stringField setStringValue:[self stringFromFindPasteboard]]; 70 | [stringField selectText:self]; 71 | } 72 | 73 | - (IBAction)findNext:(id)sender 74 | { 75 | if ([self isWindowLoaded] && [[self window] isVisible]) 76 | [nextButton performClick:sender]; 77 | else 78 | [self _findNext:sender]; 79 | } 80 | 81 | - (IBAction)findPrevious:(id)sender 82 | { 83 | if ([self isWindowLoaded] && [[self window] isVisible]) 84 | [previousButton performClick:sender]; 85 | else 86 | [self _findPrevious:sender]; 87 | } 88 | 89 | - (IBAction)jumpToSelection:(id)sender 90 | { 91 | NSText *theText = [self targetText]; 92 | [theText scrollRangeToVisible:[theText selectedRange]]; 93 | } 94 | 95 | - (IBAction)enterSelection:(id)sender 96 | { 97 | NSString *string = [self stringFromTargetText];; 98 | 99 | if ([self isWindowLoaded] && [[NSApplication sharedApplication] keyWindow] == [self window]) 100 | return; 101 | 102 | if ([string length] == 0) 103 | { 104 | NSBeep(); 105 | return; 106 | } 107 | 108 | [self saveStringToFindPasteboard:string]; 109 | [stringField setStringValue:string]; 110 | [stringField selectText:self]; 111 | } 112 | 113 | - (NSText *)targetText 114 | { 115 | id document = [[NSDocumentController sharedDocumentController] currentDocument]; 116 | if ([document respondsToSelector:@selector(textView)]) 117 | return [document textView]; 118 | return nil; 119 | } 120 | 121 | - (void)saveStringToFindPasteboard:(NSString *)aString 122 | { 123 | NSPasteboard *findPasteboard = [NSPasteboard pasteboardWithName:NSFindPboard]; 124 | 125 | if ([aString length] == 0) return; 126 | 127 | NS_DURING 128 | [findPasteboard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil]; 129 | [findPasteboard setString:aString forType:NSStringPboardType]; 130 | NS_HANDLER 131 | NS_ENDHANDLER; 132 | } 133 | 134 | - (NSString *)stringFromFindPasteboard 135 | { 136 | NSPasteboard *findPasteboard = [NSPasteboard pasteboardWithName:NSFindPboard]; 137 | NSString *string = @""; 138 | 139 | NS_DURING 140 | if ([[findPasteboard types] containsObject:NSStringPboardType]) 141 | string = [findPasteboard stringForType:NSStringPboardType]; 142 | NS_HANDLER 143 | string = @""; 144 | NS_ENDHANDLER; 145 | 146 | return string; 147 | } 148 | 149 | - (NSString *)stringFromTargetText 150 | { 151 | NSText *theText = [self targetText]; 152 | NSRange selectedRange = [theText selectedRange]; 153 | 154 | return (selectedRange.length == 0)? nil : [[theText string] substringWithRange:selectedRange]; 155 | } 156 | 157 | - (NSString *)searchString 158 | { 159 | if ([self isWindowLoaded] && [[self window] isVisible]) 160 | { 161 | NSString *string = [stringField stringValue]; 162 | if ([string length] > 0) 163 | return string; 164 | } 165 | 166 | return [self stringFromFindPasteboard]; 167 | } 168 | 169 | - (BOOL)searchGoingBackwards:(BOOL)backwards 170 | { 171 | NSString *string = [self searchString]; 172 | NSText *targetText = [self targetText]; 173 | 174 | if ([string length] == 0 || targetText == nil) 175 | { 176 | if (targetText == nil) 177 | [statusField setStringValue:@"Nothing to search"]; 178 | return NO; 179 | } 180 | 181 | [self saveStringToFindPasteboard:string]; 182 | [stringField selectText:self]; 183 | 184 | if ([targetText findString:string ignoreCase:[ignoreCaseButton state] 185 | backwards:backwards wrap:YES]) 186 | { 187 | [[targetText window] makeFirstResponder:targetText]; //make sure selection shows 188 | [statusField setStringValue:@""]; 189 | return YES; 190 | } 191 | else 192 | { 193 | NSBeep(); 194 | [statusField setStringValue:@"Not found"]; 195 | return NO; 196 | } 197 | } 198 | 199 | - (void)_findNext:(id)sender 200 | { 201 | [self searchGoingBackwards:NO]; 202 | } 203 | 204 | - (void)_findPrevious:(id)sender 205 | { 206 | [self searchGoingBackwards:YES]; 207 | } 208 | 209 | - (void)_findEnter:(id)sender 210 | { 211 | if ([self searchGoingBackwards:NO]) 212 | [[self window] orderOut:sender]; 213 | } 214 | 215 | - (BOOL)validateMenuItem:(NSMenuItem *)item 216 | { 217 | if ([item action] == @selector(enterSelection:) || 218 | [item action] == @selector(jumpToSelection:)) 219 | { 220 | NSText *targetText = [self targetText]; 221 | return targetText != nil && [targetText selectedRange].length > 0; 222 | } 223 | if ([item action] == @selector(findPrevious:) || 224 | [item action] == @selector(findNext:)) 225 | { 226 | return [[self searchString] length] > 0; 227 | } 228 | return YES; 229 | } 230 | 231 | @end 232 | 233 | /* 234 | * The obsolete NSCStringText has a similar method, which is very handy for 235 | * find panel usage, so we implement it on NSText. 236 | */ 237 | @implementation NSText (Search) 238 | 239 | - (BOOL)findString:(NSString *)aString 240 | ignoreCase:(BOOL)ignoreCase 241 | backwards:(BOOL)backwards 242 | wrap:(BOOL)wrap 243 | { 244 | NSString *searchText = [self string]; 245 | NSRange selectedRange = [self selectedRange]; 246 | NSRange beforeRange, afterRange, searchRange, foundRange; 247 | NSStringCompareOptions mask = 0; 248 | 249 | beforeRange = NSMakeRange(0, selectedRange.location); 250 | afterRange.location = NSMaxRange(selectedRange); 251 | afterRange.length = [searchText length] - afterRange.location; 252 | 253 | if (ignoreCase) mask |= NSCaseInsensitiveSearch; 254 | if (backwards) mask |= NSBackwardsSearch; 255 | 256 | searchRange = (backwards)? beforeRange : afterRange; 257 | foundRange = [searchText rangeOfString:aString options:mask range:searchRange]; 258 | 259 | if (foundRange.length == 0 && wrap) 260 | { 261 | searchRange = (backwards)? afterRange : beforeRange; 262 | foundRange = [searchText rangeOfString:aString options:mask range:searchRange]; 263 | } 264 | 265 | if (foundRange.length > 0) 266 | { 267 | [self setSelectedRange:foundRange]; 268 | [self scrollRangeToVisible:foundRange]; 269 | return YES; 270 | } 271 | 272 | return NO; 273 | } 274 | 275 | @end 276 | -------------------------------------------------------------------------------- /ManOpen/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "scale" : "1x", 6 | "size" : "16x16" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "scale" : "2x", 11 | "size" : "16x16" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "scale" : "1x", 16 | "size" : "32x32" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "scale" : "2x", 21 | "size" : "32x32" 22 | }, 23 | { 24 | "filename" : "ManOpen-1 (dragged).png", 25 | "idiom" : "mac", 26 | "scale" : "1x", 27 | "size" : "128x128" 28 | }, 29 | { 30 | "idiom" : "mac", 31 | "scale" : "2x", 32 | "size" : "128x128" 33 | }, 34 | { 35 | "idiom" : "mac", 36 | "scale" : "1x", 37 | "size" : "256x256" 38 | }, 39 | { 40 | "idiom" : "mac", 41 | "scale" : "2x", 42 | "size" : "256x256" 43 | }, 44 | { 45 | "idiom" : "mac", 46 | "scale" : "1x", 47 | "size" : "512x512" 48 | }, 49 | { 50 | "idiom" : "mac", 51 | "scale" : "2x", 52 | "size" : "512x512" 53 | } 54 | ], 55 | "info" : { 56 | "author" : "xcode", 57 | "version" : 1 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /ManOpen/Images.xcassets/AppIcon.appiconset/ManOpen-1 (dragged).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nickzman/ManOpen/12a2f59488753dd63376d7692eabda0397d60bc7/ManOpen/Images.xcassets/AppIcon.appiconset/ManOpen-1 (dragged).png -------------------------------------------------------------------------------- /ManOpen/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /ManOpen/Info-ManOpen.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleDocumentTypes 8 | 9 | 10 | CFBundleTypeName 11 | unknown 12 | CFBundleTypeRole 13 | Viewer 14 | LSItemContentTypes 15 | 16 | public.data 17 | 18 | LSTypeIsPackage 19 | 20 | NSDocumentClass 21 | ManDocument 22 | 23 | 24 | CFBundleTypeName 25 | apropos 26 | CFBundleTypeRole 27 | Viewer 28 | NSDocumentClass 29 | AproposDocument 30 | 31 | 32 | CFBundleTypeExtensions 33 | 34 | man 35 | 36 | CFBundleTypeIconFile 37 | ManOpen.icns 38 | CFBundleTypeName 39 | man 40 | CFBundleTypeRole 41 | Viewer 42 | NSDocumentClass 43 | ManDocument 44 | 45 | 46 | CFBundleTypeExtensions 47 | 48 | cat 49 | 50 | CFBundleTypeIconFile 51 | ManOpen.icns 52 | CFBundleTypeName 53 | cat 54 | CFBundleTypeRole 55 | Viewer 56 | NSDocumentClass 57 | ManDocument 58 | 59 | 60 | CFBundleTypeExtensions 61 | 62 | man.gz 63 | 64 | CFBundleTypeIconFile 65 | ManOpen.icns 66 | CFBundleTypeName 67 | mangz 68 | CFBundleTypeRole 69 | Viewer 70 | NSDocumentClass 71 | ManDocument 72 | 73 | 74 | CFBundleTypeExtensions 75 | 76 | cat.gz 77 | 78 | CFBundleTypeIconFile 79 | ManOpen.icns 80 | CFBundleTypeName 81 | catgz 82 | CFBundleTypeRole 83 | Viewer 84 | NSDocumentClass 85 | ManDocument 86 | 87 | 88 | CFBundleExecutable 89 | ManOpen 90 | CFBundleGetInfoString 91 | ManOpen 3.0 (c) 2021 Nick Zitzmann 92 | CFBundleIdentifier 93 | $(PRODUCT_BUNDLE_IDENTIFIER) 94 | CFBundleInfoDictionaryVersion 95 | 6.0 96 | CFBundleName 97 | ManOpen 98 | CFBundlePackageType 99 | APPL 100 | CFBundleShortVersionString 101 | 3.0 102 | CFBundleSignature 103 | ???? 104 | CFBundleURLTypes 105 | 106 | 107 | CFBundleURLName 108 | Man Page URL 109 | CFBundleURLSchemes 110 | 111 | x-man-page 112 | 113 | 114 | 115 | CFBundleVersion 116 | 3.0 117 | LSApplicationCategoryType 118 | public.app-category.developer-tools 119 | NSAppleScriptEnabled 120 | YES 121 | NSHumanReadableCompleteName 122 | ManOpen 123 | NSHumanReadableCopyright 124 | Copyright (c) 2021, Nick Zitzmann; Copyright (c) 1999-2012, Carl Lindberg; Copyright (c) 1993, Harald Schlangmann 125 | NSHumanReadableShortName 126 | ManOpen 127 | NSMainNibFile 128 | ManOpen 129 | NSPrincipalClass 130 | NSApplication 131 | NSServices 132 | 133 | 134 | NSExecutable 135 | ManOpen 136 | NSMenuItem 137 | 138 | default 139 | ManOpen/Open File in ManOpen 140 | 141 | NSMessage 142 | openFiles 143 | NSPortName 144 | ManOpen 145 | NSRequiredContext 146 | 147 | NSTextContext 148 | 149 | FilePath 150 | 151 | 152 | NSSendTypes 153 | 154 | NSURLPboardType 155 | public.url 156 | public.file-url 157 | 158 | 159 | 160 | NSExecutable 161 | ManOpen 162 | NSKeyEquivalent 163 | 164 | default 165 | M 166 | 167 | NSMenuItem 168 | 169 | default 170 | ManOpen/Open man Page in ManOpen 171 | 172 | NSMessage 173 | openSelection 174 | NSPortName 175 | ManOpen 176 | NSSendTypes 177 | 178 | NSStringPboardType 179 | public.plain-text 180 | 181 | 182 | 183 | NSExecutable 184 | ManOpen 185 | NSKeyEquivalent 186 | 187 | default 188 | A 189 | 190 | NSMenuItem 191 | 192 | default 193 | ManOpen/Search man Page Index in ManOpen 194 | 195 | NSMessage 196 | openApropos 197 | NSPortName 198 | ManOpen 199 | NSSendTypes 200 | 201 | NSStringPboardType 202 | public.plain-text 203 | 204 | 205 | 206 | NSSupportsAutomaticTermination 207 | 208 | NSSupportsSuddenTermination 209 | 210 | 211 | 212 | -------------------------------------------------------------------------------- /ManOpen/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Generated by the Apple Project Builder. 3 | # 4 | # NOTE: Do NOT change this file -- Project Builder maintains it. 5 | # 6 | # Put all of your customizations in files called Makefile.preamble 7 | # and Makefile.postamble (both optional), and Makefile will include them. 8 | # 9 | 10 | NAME = ManOpen 11 | 12 | PROJECTVERSION = 2.8 13 | PROJECT_TYPE = Application 14 | NEXTSTEP_APPICON = ManOpen.tiff 15 | NEXTSTEP_DOCICONS = ManOpen.tiff 16 | 17 | ICONSECTIONS = -sectcreate __ICON app ManOpen.tiff \ 18 | -sectcreate __ICON ManOpen ManOpen.tiff 19 | 20 | English_RESOURCES = ManOpen.nib PrefPanel.nib DocController.nib\ 21 | ManOpen-macintosh.nib Apropos.nib ManOpen-macos.nib\ 22 | ManPage.nib FindPanel.nib Credits.rtf InfoPlist.strings 23 | 24 | LANGUAGES = English 25 | 26 | GLOBAL_RESOURCES = LinkCursor.tiff helpQMark.tiff ManOpen-nextstep.tiff ManOpen.tiff\ 27 | ManOpen.icns ManOpen.scriptSuite\ 28 | ManOpen.scriptTerminology 29 | 30 | CLASSES = AproposDocument.m FindPanelController.m ManDocument.m\ 31 | ManDocumentController.m NSData+Utils.m PrefPanelController.m 32 | 33 | HFILES = AproposDocument.h FindPanelController.h ManDocument.h\ 34 | ManDocumentController.h ManOpenProtocol.h NSData+Utils.h\ 35 | PrefPanelController.h SystemType.h 36 | 37 | MFILES = ManOpen_main.m 38 | 39 | TOOLS = cat2html.tproj cat2rtf.tproj 40 | 41 | OTHERSRCS = CustomInfo.plist CustomInfoOSXOverrides.plist h.template \ 42 | m.template Makefile Makefile.postamble Makefile.preamble 43 | 44 | 45 | MAKEFILEDIR = $(NEXT_ROOT)/NextDeveloper/Makefiles/pb_makefiles 46 | CODE_GEN_STYLE = DYNAMIC 47 | MAKEFILE = app.make 48 | NEXTSTEP_INSTALLDIR = $(HOME)/Apps 49 | WINDOWS_INSTALLDIR = /MyApps 50 | LIBS = 51 | DEBUG_LIBS = $(LIBS) 52 | PROF_LIBS = $(LIBS) 53 | 54 | 55 | FRAMEWORKS = -framework AppKit -framework Foundation 56 | PROJECT_HEADERS = ManOpenProtocol.h SystemType.h 57 | 58 | 59 | 60 | NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc 61 | WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc 62 | PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc 63 | NEXTSTEP_JAVA_COMPILER = /usr/bin/javac 64 | WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe 65 | PDO_UNIX_JAVA_COMPILER = $(JDKBINDIR)/javac 66 | 67 | include $(MAKEFILEDIR)/platform.make 68 | 69 | -include Makefile.preamble 70 | 71 | include $(MAKEFILEDIR)/$(MAKEFILE) 72 | 73 | -include Makefile.postamble 74 | 75 | -include Makefile.dependencies 76 | -------------------------------------------------------------------------------- /ManOpen/Makefile.postamble: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Makefile.postamble 3 | # Copyright 1997, Apple Computer, Inc. 4 | # 5 | # Use this makefile, which is imported after all other makefiles, to 6 | # override attributes for a project's Makefile environment. This allows you 7 | # to take advantage of the environment set up by the other Makefiles. 8 | # You can also define custom rules at the end of this file. 9 | # 10 | ############################################################################### 11 | # 12 | # These variables are exported by the standard makefiles and can be 13 | # used in any customizations you make. They are *outputs* of 14 | # the Makefiles and should be used, not set. 15 | # 16 | # PRODUCTS: products to install. All of these products will be placed in 17 | # the directory $(DSTROOT)$(INSTALLDIR) 18 | # GLOBAL_RESOURCE_DIR: The directory to which resources are copied. 19 | # LOCAL_RESOURCE_DIR: The directory to which localized resources are copied. 20 | # OFILE_DIR: Directory into which .o object files are generated. 21 | # DERIVED_SRC_DIR: Directory used for all other derived files 22 | # 23 | # ALL_CFLAGS: flags to pass when compiling .c files 24 | # ALL_MFLAGS: flags to pass when compiling .m files 25 | # ALL_CCFLAGS: flags to pass when compiling .cc, .cxx, and .C files 26 | # ALL_MMFLAGS: flags to pass when compiling .mm, .mxx, and .M files 27 | # ALL_PRECOMPFLAGS: flags to pass when precompiling .h files 28 | # ALL_LDFLAGS: flags to pass when linking object files 29 | # ALL_LIBTOOL_FLAGS: flags to pass when libtooling object files 30 | # ALL_PSWFLAGS: flags to pass when processing .psw and .pswm (pswrap) files 31 | # ALL_RPCFLAGS: flags to pass when processing .rpc (rpcgen) files 32 | # ALL_YFLAGS: flags to pass when processing .y (yacc) files 33 | # ALL_LFLAGS: flags to pass when processing .l (lex) files 34 | # 35 | # NAME: name of application, bundle, subproject, palette, etc. 36 | # LANGUAGE: langage in which the project is written (default "English") 37 | # LOCAL_RESOURCES: localized resources (e.g. nib's, images) of project 38 | # GLOBAL_RESOURCES: non-localized resources of project 39 | # 40 | # SRCROOT: base directory in which to place the new source files 41 | # SRCPATH: relative path from SRCROOT to present subdirectory 42 | # 43 | # INSTALLDIR: Directory the product will be installed into by 'install' target 44 | # PUBLIC_HDR_INSTALLDIR: where to install public headers. Don't forget 45 | # to prefix this with DSTROOT when you use it. 46 | # PRIVATE_HDR_INSTALLDIR: where to install private headers. Don't forget 47 | # to prefix this with DSTROOT when you use it. 48 | # 49 | # EXECUTABLE_EXT: Executable extension for the platform (i.e. .exe on Windows) 50 | # 51 | ############################################################################### 52 | 53 | # Some compiler flags can be overridden here for certain build situations. 54 | # 55 | # WARNING_CFLAGS: flag used to set warning level (defaults to -Wmost) 56 | # DEBUG_SYMBOLS_CFLAGS: debug-symbol flag passed to all builds (defaults 57 | # to -g) 58 | # DEBUG_BUILD_CFLAGS: flags passed during debug builds (defaults to -DDEBUG) 59 | # OPTIMIZE_BUILD_CFLAGS: flags passed during optimized builds (defaults 60 | # to -O) 61 | # PROFILE_BUILD_CFLAGS: flags passed during profile builds (defaults 62 | # to -pg -DPROFILE) 63 | # LOCAL_DIR_INCLUDE_DIRECTIVE: flag used to add current directory to 64 | # the include path (defaults to -I.) 65 | # DEBUG_BUILD_LDFLAGS, OPTIMIZE_BUILD_LDFLAGS, PROFILE_BUILD_LDFLAGS: flags 66 | # passed to ld/libtool (defaults to nothing) 67 | 68 | 69 | # Library and Framework projects only: 70 | # INSTALL_NAME_DIRECTIVE: This directive ensures that executables linked 71 | # against the framework will run against the correct version even if 72 | # the current version of the framework changes. You may override this 73 | # to "" as an alternative to using the DYLD_LIBRARY_PATH during your 74 | # development cycle, but be sure to restore it before installing. 75 | 76 | 77 | # Ownership and permissions of files installed by 'install' target 78 | 79 | #INSTALL_AS_USER = root 80 | # User/group ownership 81 | #INSTALL_AS_GROUP = wheel 82 | # (probably want to set both of these) 83 | #INSTALL_PERMISSIONS = 84 | # If set, 'install' chmod's executable to this 85 | 86 | 87 | # Options to strip. Note: -S strips debugging symbols (executables can be stripped 88 | # down further with -x or, if they load no bundles, with no options at all). 89 | 90 | #STRIPFLAGS = -S 91 | 92 | 93 | ######################################################################### 94 | # Put rules to extend the behavior of the standard Makefiles here. Include them in 95 | # the dependency tree via cvariables like AFTER_INSTALL in the Makefile.preamble. 96 | # 97 | # You should avoid redefining things like "install" or "app", as they are 98 | # owned by the top-level Makefile API and no context has been set up for where 99 | # derived files should go. 100 | # 101 | 102 | # Override Apple's info.plist building to just copy in the XML version we have on disk. 103 | # Obviously, this will only work on OSX systems, but that is all we are supporting now. 104 | 105 | ifeq "macos" "$(PLATFORM_OS)" 106 | create-info-file: Info-ManOpen.plist 107 | @$(RM) -f $(INFO_FILE) 108 | $(CP) -p Info-ManOpen.plist $(GLOBAL_RESOURCE_DIR)/Info.plist 109 | endif 110 | -------------------------------------------------------------------------------- /ManOpen/Makefile.preamble: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Makefile.preamble 3 | # Copyright 1997, Apple Computer, Inc. 4 | # 5 | # Use this makefile for configuring the standard application makefiles 6 | # associated with ProjectBuilder. It is included before the main makefile. 7 | # In Makefile.preamble you set attributes for a project, so they are available 8 | # to the project's makefiles. In contrast, you typically write additional rules or 9 | # override built-in behavior in the Makefile.postamble. 10 | # 11 | # Each directory in a project tree (main project plus subprojects) should 12 | # have its own Makefile.preamble and Makefile.postamble. 13 | ############################################################################### 14 | # 15 | # Before the main makefile is included for this project, you may set: 16 | # 17 | # MAKEFILEDIR: Directory in which to find $(MAKEFILE) 18 | # MAKEFILE: Top level mechanism Makefile (e.g., app.make, bundle.make) 19 | 20 | # Compiler/linker flags added to the defaults: The OTHER_* variables will be 21 | # inherited by all nested sub-projects, but the LOCAL_ versions of the same 22 | # variables will not. Put your -I, -D, -U, and -L flags in ProjectBuilder's 23 | # Build Attributes inspector if at all possible. To override the default flags 24 | # that get passed to ${CC} (e.g. change -O to -O2), see Makefile.postamble. The 25 | # variables below are *inputs* to the build process and distinct from the override 26 | # settings done (less often) in the Makefile.postamble. 27 | # 28 | # OTHER_CFLAGS, LOCAL_CFLAGS: additional flags to pass to the compiler 29 | # Note that $(OTHER_CFLAGS) and $(LOCAL_CFLAGS) are used for .h, ...c, .m, 30 | # .cc, .cxx, .C, and .M files. There is no need to respecify the 31 | # flags in OTHER_MFLAGS, etc. 32 | # OTHER_MFLAGS, LOCAL_MFLAGS: additional flags for .m files 33 | # OTHER_CCFLAGS, LOCAL_CCFLAGS: additional flags for .cc, .cxx, and ...C files 34 | # OTHER_MMFLAGS, LOCAL_MMFLAGS: additional flags for .mm and .M files 35 | # OTHER_PRECOMPFLAGS, LOCAL_PRECOMPFLAGS: additional flags used when 36 | # precompiling header files 37 | # OTHER_LDFLAGS, LOCAL_LDFLAGS: additional flags passed to ld and libtool 38 | # OTHER_PSWFLAGS, LOCAL_PSWFLAGS: additional flags passed to pswrap 39 | # OTHER_RPCFLAGS, LOCAL_RPCFLAGS: additional flags passed to rpcgen 40 | # OTHER_YFLAGS, LOCAL_YFLAGS: additional flags passed to yacc 41 | # OTHER_LFLAGS, LOCAL_LFLAGS: additional flags passed to lex 42 | 43 | # These variables provide hooks enabling you to add behavior at almost every 44 | # stage of the make: 45 | # 46 | # BEFORE_PREBUILD: targets to build before installing headers for a subproject 47 | # AFTER_PREBUILD: targets to build after installing headers for a subproject 48 | # BEFORE_BUILD_RECURSION: targets to make before building subprojects 49 | # BEFORE_BUILD: targets to make before a build, but after subprojects 50 | # AFTER_BUILD: targets to make after a build 51 | # 52 | # BEFORE_INSTALL: targets to build before installing the product 53 | # AFTER_INSTALL: targets to build after installing the product 54 | # BEFORE_POSTINSTALL: targets to build before postinstalling every subproject 55 | # AFTER_POSTINSTALL: targts to build after postinstalling every subproject 56 | # 57 | # BEFORE_INSTALLHDRS: targets to build before installing headers for a 58 | # subproject 59 | # AFTER_INSTALLHDRS: targets to build after installing headers for a subproject 60 | # BEFORE_INSTALLSRC: targets to build before installing source for a subproject 61 | # AFTER_INSTALLSRC: targets to build after installing source for a subproject 62 | # 63 | # BEFORE_DEPEND: targets to build before building dependencies for a 64 | # subproject 65 | # AFTER_DEPEND: targets to build after building dependencies for a 66 | # subproject 67 | # 68 | # AUTOMATIC_DEPENDENCY_INFO: if YES, then the dependency file is 69 | # updated every time the project is built. If NO, the dependency 70 | # file is only built when the depend target is invoked. 71 | 72 | # Framework-related variables: 73 | # FRAMEWORK_DLL_INSTALLDIR: On Windows platforms, this variable indicates 74 | # where to put the framework's DLL. This variable defaults to 75 | # $(INSTALLDIR)/../Executables 76 | 77 | # Library-related variables: 78 | # PUBLIC_HEADER_DIR: Determines where public exported header files 79 | # should be installed. Do not include $(DSTROOT) in this value -- 80 | # it is prefixed automatically. 81 | # PRIVATE_HEADER_DIR: Determines where private exported header files 82 | # should be installed. Do not include $(DSTROOT) in this value -- 83 | # it is prefixed automatically. 84 | # LIBRARY_STYLE: This may be either STATIC or DYNAMIC, and determines 85 | # whether the libraries produced are statically linked when they 86 | # are used or if they are dynamically loadable. <> 87 | # LIBRARY_DLL_INSTALLDIR: On Windows platforms, this variable indicates 88 | # where to put the library's DLL. This variable defaults to 89 | # $(INSTALLDIR)/../Executables 90 | # 91 | # INSTALL_AS_USER: owner of the intalled products (default root) 92 | # INSTALL_AS_GROUP: group of the installed products (default wheel) 93 | # INSTALL_PERMISSION: permissions of the installed product (default o+rX) 94 | # 95 | # OTHER_RECURSIVE_VARIABLES: The names of variables which you want to be 96 | # passed on the command line to recursive invocations of make. Note that 97 | # the values in OTHER_*FLAGS are inherited by subprojects automatically -- 98 | # you do not have to (and shouldn't) add OTHER_*FLAGS to 99 | # OTHER_RECURSIVE_VARIABLES. 100 | 101 | # Additional headers to export beyond those in the PB.project: 102 | # OTHER_PUBLIC_HEADERS 103 | # OTHER_PROJECT_HEADERS 104 | # OTHER_PRIVATE_HEADERS 105 | 106 | # Additional files for the project's product: <> 107 | # OTHER_RESOURCES: (non-localized) resources for this project 108 | # OTHER_OFILES: relocatables to be linked into this project 109 | # OTHER_LIBS: more libraries to link against 110 | # OTHER_PRODUCT_DEPENDS: other dependencies of this project 111 | # OTHER_SOURCEFILES: other source files maintained by .pre/postamble 112 | # OTHER_GARBAGE: additional files to be removed by `make clean' 113 | 114 | # Set this to YES if you don't want a final libtool call for a library/framework. 115 | # BUILD_OFILES_LIST_ONLY 116 | 117 | # To include a version string, project source must exist in a directory named 118 | # $(NAME).%d[.%d][.%d] and the following line must be uncommented. 119 | # OTHER_GENERATED_OFILES = $(VERS_OFILE) 120 | 121 | # This definition will suppress stripping of debug symbols when an executable 122 | # is installed. By default it is YES. 123 | # STRIP_ON_INSTALL = NO 124 | 125 | 126 | # To support OPENSTEP, add NSDocument and info panel clones... 127 | ifeq "$(wildcard /usr/bin/uname)" "" 128 | SUBPROJECTS += OpenStep.subproj 129 | endif 130 | 131 | # For building on MacOS X, BUNDLE_STYLE needs to be defined to 132 | # generate the correct app layout during "make install". 133 | # Permissions must be set to allow user write, or else the 134 | # conversion fails since the default permissions have no 135 | # write access (this is an Apple Makefile bug). 136 | BUNDLE_STYLE = MACOSX 137 | INSTALL_PERMISSIONS = u+w,a+rX 138 | 139 | # If we're running on OPENSTEP, but are using Makefiles saved with 140 | # a later version of ProjectBuilder, reset the local resource variables 141 | # back (which is the main difference). GLOBAL_RESOURCES does not need 142 | # changing. 143 | ifneq "" "$(wildcard /bin/mkdirs)" 144 | ifneq "" "$(English_RESOURCES)" 145 | LANGUAGE = English 146 | LOCAL_RESOURCES += $(English_RESOURCES) 147 | endif 148 | endif 149 | 150 | # On MacOS X, we want to declare more extensions. 151 | # Actually, they still do cause occasional problems form 152 | # single-binary CFM apps that have version numbers (i.e 153 | # end in .1 or the like). 154 | #ifeq "macos" "$(PLATFORM_OS)" 155 | # OTHER_INFO_FILES += CustomInfoOSXOverrides.plist 156 | #endif 157 | -------------------------------------------------------------------------------- /ManOpen/ManDocument.h: -------------------------------------------------------------------------------- 1 | 2 | #import 3 | 4 | @interface ManDocument : NSDocument 5 | 6 | - (instancetype)initWithName:(NSString *)name section:(NSString *)section manPath:(NSString *)manPath title:(NSString *)title; 7 | 8 | @property(nonatomic,retain) NSString *shortTitle; 9 | @property(nonatomic,retain) NSData *taskData; 10 | @property(nonatomic,assign) BOOL hasLoaded; 11 | @property(nonatomic,retain) NSURL *xManPageURL; 12 | @property(nonatomic,retain) NSMutableArray *sections; 13 | @property(nonatomic,retain) NSMutableArray *sectionRanges; 14 | @property(nonatomic,retain) NSMutableDictionary *restoreData; 15 | 16 | - (void)loadCommand:(NSString *)command; 17 | 18 | - (void)showData; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /ManOpen/ManDocument.m: -------------------------------------------------------------------------------- 1 | 2 | #import "ManDocument.h" 3 | #import 4 | #import "ManDocumentController.h" 5 | #import "PrefPanelController.h" 6 | #import "NSData+Utils.h" 7 | #import "ManWindowController.h" 8 | 9 | #define RestoreWindowDict @"RestoreWindowInfo" 10 | #define RestoreSection @"Section" 11 | #define RestoreTitle @"Title" 12 | #define RestoreName @"Name" 13 | #define RestoreFileURL @"URL" 14 | #define RestoreFileType @"DocType" 15 | @interface NSDocument (LionRestorationMethods) 16 | - (void)encodeRestorableStateWithCoder:(NSCoder *)coder; 17 | - (void)restoreStateWithCoder:(NSCoder *)coder; 18 | @end 19 | 20 | 21 | @implementation ManDocument 22 | 23 | + (BOOL)canConcurrentlyReadDocumentsOfType:(NSString *)typeName 24 | { 25 | return YES; 26 | } 27 | 28 | - (void)_loadDocumentWithName:(NSString *)name 29 | section:(NSString *)section 30 | manPath:(NSString *)manPath 31 | title:(NSString *)title 32 | { 33 | ManDocumentController *docController = [ManDocumentController sharedDocumentController]; 34 | NSMutableString *command = [docController manCommandWithManPath:manPath]; 35 | 36 | [self setFileType:@"man"]; 37 | [self setShortTitle:title]; 38 | 39 | if (section && [section length] > 0) 40 | { 41 | [command appendFormat:@" %@", [section lowercaseString]]; 42 | self.xManPageURL = [[NSURL alloc] initWithString:[NSString stringWithFormat:@"x-man-page://%@/%@", section, title]]; 43 | } 44 | else 45 | { 46 | self.xManPageURL = [[NSURL alloc] initWithString:[NSString stringWithFormat:@"x-man-page://%@", title]]; 47 | } 48 | 49 | self.restoreData = [[NSMutableDictionary alloc] initWithObjectsAndKeys: 50 | name, RestoreName, 51 | title, RestoreTitle, 52 | section, RestoreSection, 53 | nil]; 54 | 55 | [command appendFormat:@" %@", name]; 56 | 57 | [self loadCommand:command]; 58 | } 59 | 60 | - (instancetype)initWithName:(NSString *)name 61 | section:(NSString *)section 62 | manPath:(NSString *)manPath 63 | title:(NSString *)title 64 | { 65 | self = [super init]; 66 | if (self) 67 | [self _loadDocumentWithName:name section:section manPath:manPath title:title]; 68 | return self; 69 | } 70 | 71 | 72 | - (void)makeWindowControllers 73 | { 74 | ManWindowController *controller = [[ManWindowController alloc] initWithWindowNibName:@"ManPage"]; 75 | 76 | [self addWindowController:controller]; 77 | } 78 | 79 | /* 80 | * Standard NSDocument method. We only want to override if we aren't 81 | * representing an actual file. 82 | */ 83 | - (NSString *)displayName 84 | { 85 | return ([self fileURL] != nil)? [super displayName] : [self shortTitle]; 86 | } 87 | 88 | - (void)addSectionHeader:(NSString *)header range:(NSRange)range 89 | { 90 | /* Make sure it is a header -- error text sometimes is not Courier, so it gets passed in here. */ 91 | if ([header rangeOfCharacterFromSet:[NSCharacterSet uppercaseLetterCharacterSet]].length > 0 && 92 | [header rangeOfCharacterFromSet:[NSCharacterSet lowercaseLetterCharacterSet]].length == 0) 93 | { 94 | NSString *label = header; 95 | int count = 1; 96 | 97 | /* Check for dups (e.g. lesskey(1) ) */ 98 | while ([self.sections containsObject:label]) { 99 | count++; 100 | label = [NSString stringWithFormat:@"%@ [%d]", header, count]; 101 | } 102 | 103 | [self.sections addObject:label]; 104 | [self.sectionRanges addObject:[NSValue valueWithRange:range]]; 105 | } 106 | } 107 | 108 | - (void)showData 109 | { 110 | @autoreleasepool 111 | { 112 | NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 113 | NSTextStorage *storage = nil; 114 | NSFont *manFont = [defaults manFont]; 115 | NSColor *linkColor = [defaults manLinkColor]; 116 | NSColor *textColor = [defaults manTextColor]; 117 | NSColor *backgroundColor = [defaults manBackgroundColor]; 118 | ManWindowController *manWC = self.windowControllers.firstObject; 119 | NSMutableDictionary *linkTextAttribtues = manWC.textView.linkTextAttributes.mutableCopy; 120 | 121 | if (manWC.textView == nil || self.hasLoaded) return; 122 | 123 | if ([self.taskData isRTFData]) 124 | { 125 | storage = [[NSTextStorage alloc] initWithRTF:self.taskData documentAttributes:NULL]; 126 | } 127 | else if (self.taskData != nil) 128 | { 129 | storage = [[NSTextStorage alloc] initWithHTML:self.taskData documentAttributes:NULL]; 130 | } 131 | 132 | if (storage == nil) 133 | storage = [[NSTextStorage alloc] init]; 134 | 135 | if ([[storage string] rangeOfCharacterFromSet:[NSCharacterSet letterCharacterSet]].length == 0) 136 | { 137 | [[storage mutableString] setString:@"\nNo manual entry."]; 138 | } 139 | 140 | if (self.sections == nil) { 141 | self.sections = [[NSMutableArray alloc] init]; 142 | self.sectionRanges = [[NSMutableArray alloc] init]; 143 | } 144 | [self.sections removeAllObjects]; 145 | [self.sectionRanges removeAllObjects]; 146 | 147 | /* Convert the attributed string to use the user's chosen font and text color */ 148 | if (storage != nil) 149 | { 150 | NSFontManager *manager = [NSFontManager sharedFontManager]; 151 | NSString *family = [manFont familyName]; 152 | CGFloat size = [manFont pointSize]; 153 | NSUInteger currIndex = 0; 154 | 155 | @try 156 | { 157 | [storage beginEditing]; 158 | 159 | while (currIndex < [storage length]) 160 | { 161 | NSRange currRange; 162 | NSDictionary *attribs = [storage attributesAtIndex:currIndex effectiveRange:&currRange]; 163 | NSFont *font = [attribs objectForKey:NSFontAttributeName]; 164 | 165 | /* We mark "sections" with Helvetica fonts */ 166 | if (font != nil && ![[font familyName] isEqualToString:@"Courier"]) { 167 | [self addSectionHeader:[[storage string] substringWithRange:currRange] range:currRange]; 168 | } 169 | 170 | if (font != nil && ![[font familyName] isEqualToString:family]) 171 | font = [manager convertFont:font toFamily:family]; 172 | if (font != nil && [font pointSize] != size) 173 | font = [manager convertFont:font toSize:size]; 174 | if (font != nil) 175 | [storage addAttribute:NSFontAttributeName value:font range:currRange]; 176 | 177 | [storage addAttribute:NSForegroundColorAttributeName value:textColor range:currRange]; 178 | 179 | currIndex = NSMaxRange(currRange); 180 | } 181 | 182 | [storage endEditing]; 183 | } 184 | @catch (NSException *localException) 185 | { 186 | NSLog(@"Exception during formatting: %@", localException); 187 | } 188 | 189 | [manWC.textView.layoutManager replaceTextStorage:storage]; 190 | [manWC.textView.window invalidateCursorRectsForView:manWC.textView]; 191 | } 192 | 193 | [linkTextAttribtues setValue:linkColor forKey:NSForegroundColorAttributeName]; 194 | manWC.textView.linkTextAttributes = linkTextAttribtues; 195 | [manWC.textView setBackgroundColor:backgroundColor]; 196 | [manWC.textView.window.toolbar validateVisibleItems]; 197 | 198 | /* 199 | * The 10.7 document reloading stuff can cause the loading methods to be invoked more than 200 | * once, and the second time through we have thrown away our raw data. Probably indicates 201 | * some overkill code elsewhere on my part, but putting in the hadLoaded guard to only 202 | * avoid doing anything after we have loaded real data seems to help. 203 | */ 204 | if (self.taskData != nil) 205 | self.hasLoaded = YES; 206 | 207 | // no need to keep around rtf data 208 | self.taskData = nil; 209 | } 210 | } 211 | 212 | - (NSString *)filterCommand 213 | { 214 | NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 215 | /* HTML parser in tiger got slow... RTF is faster, and is usable now that it supports hyperlinks */ 216 | // NSString *tool = @"cat2html"; 217 | NSString *tool = @"cat2rtf"; 218 | NSString *command = [[NSBundle mainBundle] pathForResource:tool ofType:nil]; 219 | 220 | command = EscapePath(command, YES); 221 | command = [command stringByAppendingString:@" -lH"]; // generate links, mark headers 222 | if ([defaults boolForKey:@"UseItalics"]) 223 | command = [command stringByAppendingString:@" -i"]; 224 | if (![defaults boolForKey:@"UseBold"]) 225 | command = [command stringByAppendingString:@" -g"]; 226 | 227 | return command; 228 | } 229 | 230 | - (void)loadCommand:(NSString *)command 231 | { 232 | ManDocumentController *docController = [ManDocumentController sharedDocumentController]; 233 | NSString *fullCommand = [NSString stringWithFormat:@"%@ | %@", command, [self filterCommand]]; 234 | 235 | self.taskData = nil; 236 | self.taskData = [docController dataByExecutingCommand:fullCommand]; 237 | 238 | [self showData]; 239 | } 240 | 241 | - (void)loadManFile:(NSString *)filename isGzip:(BOOL)isGzip 242 | { 243 | NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 244 | NSString *nroffFormat = [defaults stringForKey:@"NroffCommand"]; 245 | NSString *nroffCommand; 246 | BOOL hasQuote = ([nroffFormat rangeOfString:@"'%@'"].length > 0); 247 | 248 | /* If Gzip, change the command into a filter of the output of gzcat. I'm 249 | getting the feeling that the customizable nroff command is more trouble 250 | than it's worth, especially now that OSX uses the good version of gnroff */ 251 | if (isGzip) 252 | { 253 | NSString *repl = hasQuote? @"'%@'" : @"%@"; 254 | NSRange replRange = [nroffFormat rangeOfString:repl]; 255 | if (replRange.length > 0) { 256 | NSMutableString *formatCopy = nroffFormat.mutableCopy; 257 | [formatCopy replaceCharactersInRange:replRange withString:@""]; 258 | nroffFormat = [NSString stringWithFormat:@"/usr/bin/gzip -dc %@ | %@", repl, formatCopy]; 259 | } 260 | } 261 | 262 | nroffCommand = [NSString stringWithFormat:nroffFormat, EscapePath(filename, !hasQuote)]; 263 | [self loadCommand:nroffCommand]; 264 | } 265 | 266 | - (void)loadCatFile:(NSString *)filename isGzip:(BOOL)isGzip 267 | { 268 | NSString *binary = isGzip? @"/usr/bin/gzip -dc" : @"/bin/cat"; 269 | [self loadCommand:[NSString stringWithFormat:@"%@ '%@'", binary, EscapePath(filename, NO)]]; 270 | } 271 | 272 | - (BOOL)readFromURL:(NSURL *)url ofType:(NSString *)type error:(NSError **)error 273 | { 274 | if ([type isEqual:@"man"]) 275 | [self loadManFile:[url path] isGzip:NO]; 276 | else if ([type isEqual:@"mangz"]) 277 | [self loadManFile:[url path] isGzip:YES]; 278 | else if ([type isEqual:@"cat"]) 279 | [self loadCatFile:[url path] isGzip:NO]; 280 | else if ([type isEqual:@"catgz"]) 281 | [self loadCatFile:[url path] isGzip:YES]; 282 | else { 283 | NSDictionary *errorDetail = [NSDictionary dictionaryWithObject:@"Invalid document type" forKey:NSLocalizedDescriptionKey]; 284 | if (error != NULL) 285 | *error = [NSError errorWithDomain:@"ManOpen" code:0 userInfo:errorDetail]; 286 | return NO; 287 | } 288 | 289 | // strip extension twice in case it is a e.g. "1.gz" filename 290 | [self setShortTitle:[[[[url path] lastPathComponent] stringByDeletingPathExtension] stringByDeletingPathExtension]]; 291 | self.xManPageURL = url; 292 | 293 | self.restoreData = [[NSMutableDictionary alloc] initWithObjectsAndKeys: 294 | url, RestoreFileURL, 295 | type, RestoreFileType, 296 | nil]; 297 | 298 | if (self.taskData == nil) 299 | { 300 | NSDictionary *errorDetail = [NSDictionary dictionaryWithObject:@"Could not read manual data" forKey:NSLocalizedDescriptionKey]; 301 | if (error != NULL) 302 | *error = [NSError errorWithDomain:@"ManOpen" code:0 userInfo:errorDetail]; 303 | return NO; 304 | } 305 | 306 | return YES; 307 | } 308 | 309 | /* Always use global page layout */ 310 | - (IBAction)runPageLayout:(id)sender 311 | { 312 | [[NSApplication sharedApplication] runPageLayout:sender]; 313 | } 314 | 315 | - (void)printDocumentWithSettings:(NSDictionary *)printSettings showPrintPanel:(BOOL)showPrintPanel delegate:(id)delegate didPrintSelector:(SEL)didPrintSelector contextInfo:(void *)contextInfo 316 | { 317 | ManWindowController *manWC = self.windowControllers.firstObject; 318 | NSPrintOperation *operation = [NSPrintOperation printOperationWithView:manWC.textView]; 319 | NSPrintInfo *printInfo = [operation printInfo]; 320 | 321 | [printInfo setVerticallyCentered:NO]; 322 | [printInfo setHorizontallyCentered:YES]; 323 | [printInfo setHorizontalPagination:NSPrintingPaginationModeFit]; 324 | [operation setShowsPrintPanel:showPrintPanel]; 325 | [operation setShowsProgressPanel:showPrintPanel]; 326 | 327 | [operation runOperationModalForWindow:manWC.window delegate:nil didRunSelector:NULL contextInfo:NULL]; 328 | } 329 | 330 | 331 | - (void)encodeRestorableStateWithCoder:(NSCoder *)coder 332 | { 333 | [super encodeRestorableStateWithCoder:coder]; 334 | [coder encodeObject:self.restoreData forKey:RestoreWindowDict]; 335 | } 336 | 337 | - (void)restoreStateWithCoder:(NSCoder *)coder 338 | { 339 | [super restoreStateWithCoder:coder]; 340 | 341 | if (![coder containsValueForKey:RestoreWindowDict]) 342 | return; 343 | 344 | NSDictionary *restoreInfo = [coder decodeObjectForKey:RestoreWindowDict]; 345 | if ([restoreInfo objectForKey:RestoreName] != nil) 346 | { 347 | NSString *name = [restoreInfo objectForKey:RestoreName]; 348 | NSString *section = [restoreInfo objectForKey:RestoreSection]; 349 | NSString *title = [restoreInfo objectForKey:RestoreTitle]; 350 | NSString *manPath = [[NSUserDefaults standardUserDefaults] manPath]; 351 | 352 | [self _loadDocumentWithName:name section:section manPath:manPath title:title]; 353 | } 354 | /* Usually, URL-backed documents have been automatically restored already 355 | (the copyURL would be set), but just in case... */ 356 | else if ([restoreInfo objectForKey:RestoreFileURL] != nil && self.xManPageURL == nil) 357 | { 358 | NSURL *url = [restoreInfo objectForKey:RestoreFileURL]; 359 | NSString *type = [restoreInfo objectForKey:RestoreFileType]; 360 | [self readFromURL:url ofType:type error:NULL]; 361 | } 362 | 363 | [[self windowControllers] makeObjectsPerformSelector:@selector(synchronizeWindowTitleWithDocumentName)]; 364 | } 365 | 366 | @end 367 | -------------------------------------------------------------------------------- /ManOpen/ManDocumentController.h: -------------------------------------------------------------------------------- 1 | 2 | #import 3 | 4 | 5 | @class NSPanel, NSTextField, NSPopUpButton, NSFont; 6 | @class NSData, NSMutableString; 7 | 8 | extern NSString *EscapePath(NSString *path, BOOL addSurroundingQuotes); 9 | 10 | @interface ManDocumentController : NSDocumentController 11 | { 12 | IBOutlet NSPanel *openTextPanel; 13 | IBOutlet NSPanel *aproposPanel; 14 | IBOutlet NSPanel *helpPanel; 15 | IBOutlet NSTextField *aproposField; 16 | IBOutlet NSTextField *openTextField; 17 | IBOutlet NSPopUpButton *openSectionPopup; 18 | BOOL startedUp; 19 | } 20 | 21 | - (id)openWord:(NSString *)word; 22 | 23 | - (oneway void)openFile:(NSString *)filename; 24 | - (oneway void)openString:(NSString *)string; 25 | - (oneway void)openString:(NSString *)string oneWordOnly:(BOOL)oneOnly; 26 | - (oneway void)openName:(NSString *)name; 27 | - (oneway void)openName:(NSString *)name section:(NSString *)section; 28 | - (oneway void)openName:(NSString *)name section:(NSString *)section manPath:(NSString *)manPath; 29 | - (oneway void)openApropos:(NSString *)apropos; 30 | - (oneway void)openApropos:(NSString *)apropos manPath:(NSString *)manPath; 31 | 32 | - (IBAction)openSection:(id)sender; 33 | - (IBAction)openTextPanel:(id)sender; 34 | - (IBAction)openAproposPanel:(id)sender; 35 | - (IBAction)okApropos:(id)sender; 36 | - (IBAction)okText:(id)sender; 37 | - (IBAction)cancelText:(id)sender; 38 | 39 | - (IBAction)orderFrontHelpPanel:(id)sender; 40 | - (IBAction)orderFrontPreferencesPanel:(id)sender; 41 | 42 | // Helper methods for document classes 43 | - (NSMutableString *)manCommandWithManPath:(NSString *)manPath; 44 | - (NSData *)dataByExecutingCommand:(NSString *)command; 45 | - (NSData *)dataByExecutingCommand:(NSString *)command manPath:(NSString *)manPath; 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /ManOpen/ManOpen-nextstep.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nickzman/ManOpen/12a2f59488753dd63376d7692eabda0397d60bc7/ManOpen/ManOpen-nextstep.tiff -------------------------------------------------------------------------------- /ManOpen/ManOpen.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.application-groups 6 | 7 | 8D98N325TG.org.clindberg.ManOpen 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ManOpen/ManOpen.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nickzman/ManOpen/12a2f59488753dd63376d7692eabda0397d60bc7/ManOpen/ManOpen.icns -------------------------------------------------------------------------------- /ManOpen/ManOpen.iconheader: -------------------------------------------------------------------------------- 1 | F ManOpen.app ManOpen app 2 | F ManOpen ManOpen app 3 | S man ManOpen ManOpen 4 | -------------------------------------------------------------------------------- /ManOpen/ManOpen.scriptSuite: -------------------------------------------------------------------------------- 1 | { 2 | Name = ManOpen; 3 | AppleEventCode = "Mopn"; 4 | 5 | Commands = { 6 | "GetURL" = { 7 | CommandClass = ManOpenURLHandlerCommand; 8 | AppleEventCode = GURL; 9 | AppleEventClassCode = GURL; 10 | }; 11 | }; 12 | } -------------------------------------------------------------------------------- /ManOpen/ManOpen.scriptTerminology: -------------------------------------------------------------------------------- 1 | { 2 | Name = "ManOpen commands"; 3 | Description = "Commands to handle a URL"; 4 | 5 | Commands = { 6 | "GetURL" = { 7 | "Name" = "GetURL"; 8 | "Description" = "Opens an x-man-page: URL"; 9 | }; 10 | }; 11 | } 12 | -------------------------------------------------------------------------------- /ManOpen/ManOpen.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nickzman/ManOpen/12a2f59488753dd63376d7692eabda0397d60bc7/ManOpen/ManOpen.tiff -------------------------------------------------------------------------------- /ManOpen/ManOpen_main.m: -------------------------------------------------------------------------------- 1 | 2 | #import 3 | 4 | int main(int argc, const char *argv[]) { 5 | return NSApplicationMain(argc, argv); 6 | } 7 | -------------------------------------------------------------------------------- /ManOpen/ManWindowController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ManWindowController.h 3 | // ManOpen (Application) 4 | // 5 | // Created by Nick Zitzmann on 2/23/21. 6 | // 7 | 8 | #import 9 | 10 | @interface ManTextView : NSTextView 11 | - (void)scrollRangeToTop:(NSRange)charRange; 12 | @end 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | @interface ManWindowController : NSWindowController 17 | @property(nonatomic,retain) IBOutlet ManTextView *textView; 18 | 19 | - (IBAction)saveCurrentWindowSize:(id)sender; 20 | - (IBAction)openSelection:(id)sender; 21 | - (IBAction)displaySection:(id)sender; 22 | - (IBAction)copyURL:(id)sender; 23 | @end 24 | 25 | NS_ASSUME_NONNULL_END 26 | -------------------------------------------------------------------------------- /ManOpen/ManWindowController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ManWindowController.m 3 | // ManOpen (Application) 4 | // 5 | // Created by Nick Zitzmann on 2/23/21. 6 | // 7 | 8 | #import "ManWindowController.h" 9 | #import "PrefPanelController.h" 10 | #import "ManDocument.h" 11 | #import "ManDocumentController.h" 12 | #import "NSData+Utils.h" 13 | 14 | @interface ManWindowController () 15 | 16 | @end 17 | 18 | @implementation ManWindowController 19 | 20 | - (void)windowDidLoad 21 | { 22 | NSString *sizeString = [[NSUserDefaults standardUserDefaults] stringForKey:@"ManWindowSize"]; 23 | NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 24 | 25 | [super windowDidLoad]; 26 | 27 | self.textView.editable = NO; 28 | self.textView.selectable = YES; 29 | self.textView.importsGraphics = NO; 30 | self.textView.richText = YES; 31 | self.textView.usesFindBar = YES; 32 | self.textView.incrementalSearchingEnabled = YES; 33 | 34 | if (sizeString != nil) 35 | { 36 | NSSize windowSize = NSSizeFromString(sizeString); 37 | NSWindow *window = self.window; 38 | NSRect frame = [window frame]; 39 | 40 | if (windowSize.width > 30.0 && windowSize.height > 30.0) { 41 | frame.size = windowSize; 42 | [window setFrame:frame display:NO]; 43 | } 44 | } 45 | 46 | self.textView.textStorage.mutableString.string = NSLocalizedString(@"Loading…", @"Displayed in the text view while the text is loading"); 47 | self.textView.backgroundColor = defaults.manBackgroundColor; 48 | self.textView.textColor = defaults.manTextColor; 49 | [self.document performSelector:@selector(showData) withObject:nil afterDelay:0.0]; 50 | 51 | [self.window makeFirstResponder:self.textView]; 52 | [self.window setDelegate:self]; 53 | } 54 | 55 | 56 | #pragma mark - 57 | 58 | 59 | - (IBAction)openSelection:(id)sender 60 | { 61 | NSRange selectedRange = self.textView.selectedRange; 62 | 63 | if (selectedRange.length > 0) 64 | { 65 | NSString *selectedString = [self.textView.string substringWithRange:selectedRange]; 66 | [[ManDocumentController sharedDocumentController] openString:selectedString]; 67 | } 68 | [self.window makeFirstResponder:self.textView]; 69 | } 70 | 71 | - (IBAction)displaySection:(id)sender 72 | { 73 | ManDocument *document = self.document; 74 | NSUInteger section = [document.sections indexOfObject:[sender title]]; 75 | 76 | if (section != NSNotFound && section < document.sectionRanges.count) 77 | { 78 | NSRange range = document.sectionRanges[section].rangeValue; 79 | 80 | [self.textView scrollRangeToTop:range]; 81 | } 82 | } 83 | 84 | - (IBAction)copyURL:(id)sender 85 | { 86 | ManDocument *document = self.document; 87 | 88 | if (document.xManPageURL != nil) 89 | { 90 | NSPasteboard *pb = [NSPasteboard generalPasteboard]; 91 | 92 | [pb clearContents]; 93 | [pb setData:[document.xManPageURL.absoluteString dataUsingEncoding:NSUTF8StringEncoding] forType:document.xManPageURL.fileURL ? (NSString *)kUTTypeFileURL : (NSString *)kUTTypeURL]; 94 | [pb setData:[[NSString stringWithFormat:@"<%@>", [document.xManPageURL absoluteString]] dataUsingEncoding:NSUTF8StringEncoding] forType:(NSString *)kUTTypeUTF8PlainText]; 95 | } 96 | } 97 | 98 | - (IBAction)saveCurrentWindowSize:(id)sender 99 | { 100 | CGSize size = self.window.frame.size; 101 | 102 | [[NSUserDefaults standardUserDefaults] setObject:NSStringFromSize(size) forKey:@"ManWindowSize"]; 103 | } 104 | 105 | #pragma mark - 106 | 107 | - (BOOL)validateMenuItem:(NSMenuItem *)item 108 | { 109 | if ([item action] == @selector(copyURL:)) 110 | return ((ManDocument *)self.document).xManPageURL != nil; 111 | return YES; 112 | } 113 | 114 | 115 | - (BOOL)validateToolbarItem:(NSToolbarItem *)item 116 | { 117 | if ([item.itemIdentifier isEqualToString:@"MDSectionIdentifier"]) 118 | { 119 | return ((ManDocument *)self.document).sections.count > 0UL; 120 | } 121 | else if ([item.itemIdentifier isEqualToString:@"MDOpenSelectionIdentifier"]) 122 | { 123 | return self.textView.selectedRange.length > 0UL; 124 | } 125 | return YES; 126 | } 127 | 128 | #pragma mark - 129 | 130 | - (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar *)toolbar 131 | { 132 | // MDSectionIdentifier is an NSMenuToolbarItem. As of Xcode 12.4, these items cannot be created in xibs, and if we try to coerce an NSToolbarItem into an NSMenuToolbarItem, then the AppKit throws an exception decoding the object. So we'll have to construct it manually... 133 | return @[@"MDSectionIdentifier"]; 134 | } 135 | 136 | 137 | - (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar *)toolbar 138 | { 139 | return @[NSToolbarFlexibleSpaceItemIdentifier, @"MDSectionIdentifier"]; 140 | } 141 | 142 | 143 | - (NSToolbarItem *)toolbar:(NSToolbar *)toolbar 144 | itemForItemIdentifier:(NSToolbarItemIdentifier)itemIdentifier 145 | willBeInsertedIntoToolbar:(BOOL)flag 146 | { 147 | if ([itemIdentifier isEqualToString:@"MDSectionIdentifier"]) 148 | { 149 | if (@available(macOS 10.15, *)) 150 | { 151 | NSMenuToolbarItem *menuItem = [[NSMenuToolbarItem alloc] initWithItemIdentifier:itemIdentifier]; 152 | 153 | menuItem.title = NSLocalizedString(@"Section", @"Section"); 154 | menuItem.label = menuItem.title; 155 | menuItem.menu = [[NSMenu alloc] initWithTitle:menuItem.title]; 156 | menuItem.menu.delegate = self; 157 | return menuItem; 158 | } 159 | else 160 | { 161 | NSToolbarItem *menuItem = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier]; 162 | NSPopUpButton *pullDown = [[NSPopUpButton alloc] initWithFrame:CGRectZero pullsDown:YES]; 163 | 164 | menuItem.label = NSLocalizedString(@"Section", @"Section"); 165 | menuItem.view = pullDown; 166 | [pullDown insertItemWithTitle:menuItem.label atIndex:0L]; 167 | pullDown.menu.delegate = self; 168 | return menuItem; 169 | } 170 | } 171 | return nil; 172 | } 173 | 174 | #pragma mark - 175 | 176 | - (void)menuNeedsUpdate:(NSMenu *)menu 177 | { 178 | // Here we assume this menu is the section pull-down menu. 179 | [menu removeAllItems]; 180 | // In an NSMenuToolbarItem, the first item in a pull-down is never actually displayed. In an NSPopUpButton, the first item in a pull-down is the pull-down's title. Either way, we need a first menu item that doesn't do anything. 181 | [menu addItemWithTitle:NSLocalizedString(@"Section", @"Section") action:nil keyEquivalent:@""]; 182 | // Add an item for each section. 183 | [((ManDocument *)self.document).sections enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 184 | [menu addItemWithTitle:obj action:@selector(displaySection:) keyEquivalent:@""]; 185 | }]; 186 | // For some reason, we lose the title if this gets called again. But since the sections are static, we have served our purpose. 187 | menu.delegate = nil; 188 | } 189 | 190 | #pragma mark - 191 | 192 | - (BOOL)textView:(NSTextView *)aTextView clickedOnLink:(id)link atIndex:(NSUInteger)charIndex 193 | { 194 | NSString *page = nil; 195 | 196 | /* On Tiger, NSURL, Panther and before, NSString */ 197 | if ([link isKindOfClass:[NSString class]] && [link hasPrefix:@"manpage:"]) 198 | page = [link substringFromIndex:8]; 199 | if ([link isKindOfClass:[NSURL class]]) 200 | page = [link resourceSpecifier]; 201 | 202 | if (page == nil) 203 | return NO; 204 | [[ManDocumentController sharedDocumentController] openString:page]; 205 | return YES; 206 | } 207 | 208 | - (void)textView:(NSTextView *)textView clickedOnCell:(id )cell inRect:(NSRect)cellFrame atIndex:(NSUInteger)charIndex 209 | { 210 | NSString *filename = nil; 211 | 212 | #pragma clang diagnostic push 213 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 214 | /* NSHelpAttachment stores the string in the fileName variable */ 215 | if ([[cell attachment] respondsToSelector:@selector(fileName)]) 216 | filename = [(id)[cell attachment] fileName]; 217 | #pragma clang diagnostic pop 218 | 219 | if ([filename hasPrefix:@"manpage:"]) { 220 | filename = [filename substringFromIndex:8]; 221 | [[ManDocumentController sharedDocumentController] openString:filename]; 222 | } 223 | } 224 | 225 | #pragma mark - 226 | 227 | - (void)windowDidUpdate:(NSNotification *)notification 228 | { 229 | /* Disable the Open Selection button if there's no selection to work on */ 230 | [self.window.toolbar validateVisibleItems]; 231 | } 232 | 233 | - (BOOL)windowShouldZoom:(NSWindow *)window toFrame:(NSRect)newFrame 234 | { 235 | return YES; 236 | } 237 | 238 | - (NSRect)windowWillUseStandardFrame:(NSWindow *)window defaultFrame:(NSRect)newFrame 239 | { 240 | NSScrollView *scrollView = [self.textView enclosingScrollView]; 241 | NSRect currentFrame = [window frame]; 242 | NSRect desiredFrame; 243 | NSSize textSize; 244 | NSRect scrollRect; 245 | NSRect contentRect; 246 | 247 | /* Get the text's natural size */ 248 | textSize = self.textView.textStorage.size; 249 | textSize.width += (self.textView.textContainerInset.width * 2) + 10; //add a little extra padding 250 | [self.textView sizeToFit]; 251 | textSize.height = NSHeight(self.textView.frame); //this seems to be more accurate 252 | 253 | /* Get the size the scrollView should be based on that */ 254 | scrollRect.origin = NSZeroPoint; 255 | scrollRect.size = [NSScrollView frameSizeForContentSize:textSize horizontalScrollerClass:scrollView.horizontalScroller.class verticalScrollerClass:scrollView.verticalScroller.class borderType:scrollView.borderType controlSize:scrollView.verticalScroller.controlSize scrollerStyle:scrollView.verticalScroller.scrollerStyle]; 256 | 257 | /* Get the window's content size -- basically the scrollView size plus our title area */ 258 | contentRect = scrollRect; 259 | contentRect.size.height += NSHeight([[window contentView] frame]) - NSHeight([scrollView frame]); 260 | 261 | /* Get the desired window frame size */ 262 | desiredFrame = [NSWindow frameRectForContentRect:contentRect styleMask:[window styleMask]]; 263 | 264 | /* Set the origin based on window's current location */ 265 | desiredFrame.origin.x = currentFrame.origin.x; 266 | desiredFrame.origin.y = NSMaxY(currentFrame) - NSHeight(desiredFrame); 267 | 268 | /* NSWindow will clip this rect to the actual available screen area */ 269 | return desiredFrame; 270 | } 271 | 272 | @end 273 | 274 | @implementation ManTextView 275 | 276 | - (void)scrollRangeToTop:(NSRange)charRange 277 | { 278 | NSLayoutManager *layout = [self layoutManager]; 279 | NSRange glyphRange = [layout glyphRangeForCharacterRange:charRange actualCharacterRange:NULL]; 280 | NSRect rect = [layout boundingRectForGlyphRange:glyphRange inTextContainer:[self textContainer]]; 281 | CGFloat height = NSHeight([self visibleRect]); 282 | 283 | if (height > 0) 284 | rect.size.height = height; 285 | 286 | [self scrollRectToVisible:rect]; 287 | } 288 | 289 | /* Make space page down (and shift/alt-space page up) */ 290 | - (void)keyDown:(NSEvent *)event 291 | { 292 | if ([[event charactersIgnoringModifiers] isEqual:@" "]) 293 | { 294 | if ([event modifierFlags] & (NSEventModifierFlagShift|NSEventModifierFlagOption)) 295 | [self pageUp:self]; 296 | else 297 | [self pageDown:self]; 298 | } 299 | else 300 | { 301 | [super keyDown:event]; 302 | } 303 | } 304 | 305 | /* 306 | * Draw page numbers when printing. Under early versions of MacOS X... the normal 307 | * NSString drawing methods don't work in the context of this method. So, I fell back on 308 | * CoreGraphics primitives, which did. However, I'm now just supporting Tiger (10.4) and up, 309 | * and it looks like the bugs have been fixed, so we can just use the higher-level 310 | * NSStringDrawing now, thankfully. 311 | */ 312 | - (void)drawPageBorderWithSize:(NSSize)size 313 | { 314 | NSFont *font = [[NSUserDefaults standardUserDefaults] manFont]; 315 | NSInteger currPage = [[NSPrintOperation currentOperation] currentPage]; 316 | NSString *pageString = [NSString stringWithFormat:@"%d", (int)currPage]; 317 | NSMutableParagraphStyle *style = [[NSMutableParagraphStyle defaultParagraphStyle] mutableCopy]; 318 | NSMutableDictionary *drawAttribs = [NSMutableDictionary dictionary]; 319 | NSRect drawRect = NSMakeRect(0.0f, 0.0f, size.width, 20.0f + [font ascender]); 320 | 321 | [style setAlignment:NSTextAlignmentCenter]; 322 | [drawAttribs setObject:style forKey:NSParagraphStyleAttributeName]; 323 | [drawAttribs setObject:font forKey:NSFontAttributeName]; 324 | 325 | [pageString drawInRect:drawRect withAttributes:drawAttribs]; 326 | 327 | // CGFloat strWidth = [str sizeWithAttributes:attribs].width; 328 | // NSPoint point = NSMakePoint(size.width/2 - strWidth/2, 20.0f); 329 | // CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort]; 330 | // 331 | // CGContextSaveGState(context); 332 | // CGContextSetTextMatrix(context, CGAffineTransformIdentity); 333 | // CGContextSetTextDrawingMode(context, kCGTextFill); //needed? 334 | // CGContextSetGrayFillColor(context, 0.0f, 1.0f); 335 | // CGContextSelectFont(context, [[font fontName] cStringUsingEncoding:NSMacOSRomanStringEncoding], [font pointSize], kCGEncodingMacRoman); 336 | // CGContextShowTextAtPoint(context, point.x, point.y, [str cStringUsingEncoding:NSMacOSRomanStringEncoding], [str lengthOfBytesUsingEncoding:NSMacOSRomanStringEncoding]); 337 | // CGContextRestoreGState(context); 338 | } 339 | 340 | @end 341 | -------------------------------------------------------------------------------- /ManOpen/NSData+Utils.h: -------------------------------------------------------------------------------- 1 | 2 | #import 3 | 4 | @interface NSData (Utils) 5 | 6 | @property(nonatomic,readonly,getter=isNroffData) BOOL nroffData; 7 | @property(nonatomic,readonly,getter=isRTFData) BOOL RTFData; 8 | @property(nonatomic,readonly,getter=isGzipData) BOOL gzipData; 9 | @property(nonatomic,readonly,getter=isBinaryData) BOOL binaryData; 10 | 11 | @end 12 | 13 | #import 14 | 15 | @interface NSFileHandle (Utils) 16 | 17 | - (NSData *)readDataToEndOfFileIgnoreInterrupt; 18 | 19 | @end 20 | 21 | -------------------------------------------------------------------------------- /ManOpen/NSData+Utils.m: -------------------------------------------------------------------------------- 1 | 2 | #import "NSData+Utils.h" 3 | #import 4 | #import 5 | #import 6 | 7 | @implementation NSData (Utils) 8 | 9 | /* 10 | * Checks the data to see if it looks like the start of an nroff file. 11 | * Derived from logic in FreeBSD's file(1) command. 12 | */ 13 | - (BOOL)isNroffData 14 | { 15 | const char *bytes = [self bytes]; 16 | const char *ptr = bytes; 17 | 18 | #define MATCH(str) (strncmp(ptr, str, strlen(str)) == 0) 19 | 20 | while (isspace(*ptr)) ptr++; 21 | 22 | /* Some X11R6 pages have a weird #pragma line at the start */ 23 | if (MATCH("#pragma")) 24 | { 25 | const char *nextline = strchr(ptr, '\n'); 26 | if (nextline != NULL) { 27 | ptr = nextline; 28 | while (isspace(*ptr)) ptr++; 29 | } 30 | } 31 | 32 | /* If not at the beginning of a line, bail. */ 33 | if (!(ptr == bytes || *(ptr-1) == '\n' || *(ptr-1) == '\r')) return NO; 34 | 35 | 36 | /* Try for some common prefixes: .\", '\", '.\", \", and .\ */ 37 | if (MATCH(".\\\"")) return YES; 38 | if (MATCH("'\\\"")) return YES; 39 | if (MATCH("'.\\\"")) return YES; 40 | if (MATCH("\\\"")) return YES; 41 | if (MATCH(".\\ ")) return YES; 42 | if (MATCH("\\.\"")) return YES; // found this on a joke man page 43 | 44 | /* 45 | * Now check for .[letter][letter], and .\" again. In either case, 46 | * allow spaces after the '.' 47 | */ 48 | if (*ptr == '.') 49 | { 50 | /* skip over '.' and whitespace */ 51 | ptr++; 52 | while (isspace(*ptr)) ptr++; 53 | 54 | if (isalnum(ptr[0]) && isalnum(ptr[1])) return YES; 55 | if (ptr[0] == '\\' && ptr[1] == '"') return YES; 56 | } 57 | 58 | return NO; 59 | } 60 | 61 | - (BOOL)hasPrefixBytes:(void *)bytes length:(NSUInteger)len 62 | { 63 | if ([self length] < len) return NO; 64 | return (memcmp([self bytes], bytes, (size_t)len) == 0); 65 | } 66 | 67 | - (BOOL)isRTFData 68 | { 69 | char *header = "{\\rtf"; 70 | return [self hasPrefixBytes:header length:strlen(header)]; 71 | } 72 | 73 | - (BOOL)isGzipData 74 | { 75 | return ([self hasPrefixBytes:"\037\235" length:2] || // compress(1) header 76 | [self hasPrefixBytes:"\037\213" length:2]); // gzip(1) header 77 | } 78 | 79 | /* Very rough check -- see if more than a third of the first 100 bytes have the high bit set */ 80 | - (BOOL)isBinaryData 81 | { 82 | NSUInteger checklen = MIN((NSUInteger)100, [self length]); 83 | NSUInteger i; 84 | NSUInteger badByteCount = 0; 85 | unsigned const char *bytes = [self bytes]; 86 | 87 | if (checklen == 0) return NO; 88 | for (i=0; i 0) && (checklen / badByteCount) <= 2; 92 | } 93 | 94 | @end 95 | 96 | #import 97 | #import 98 | #import 99 | 100 | @implementation NSFileHandle (Utils) 101 | 102 | /* 103 | * The NSData -readDataToEndOfFile method does not deal with EINTR errors, which in most 104 | * cases is fine, but sometimes not when running under a debugger. So... this is more to help 105 | * folks working on the code, rather the users ;-) 106 | */ 107 | - (NSData *)readDataToEndOfFileIgnoreInterrupt 108 | { 109 | int fd = [self fileDescriptor]; 110 | size_t offset = 0; 111 | size_t allocated = 16384; 112 | unsigned char *bytes = malloc(allocated); 113 | ssize_t bytesRead; 114 | 115 | do { 116 | if (offset >= allocated) { 117 | allocated *= 2; 118 | bytes = reallocf(bytes, allocated); 119 | } 120 | assert(bytes != NULL); 121 | 122 | do { 123 | bytesRead = read(fd, bytes + offset, allocated - offset); 124 | } while (bytesRead < 0 && ((errno == EINTR) || (errno == EAGAIN) || (errno == EWOULDBLOCK))); 125 | 126 | if (bytesRead > 0) { 127 | offset += bytesRead; 128 | } 129 | 130 | } while (bytesRead > 0); 131 | 132 | if (bytesRead < 0) { 133 | free(bytes); 134 | [NSException raise:NSFileHandleOperationException format:@"%s: %s", __FUNCTION__, strerror(errno)]; 135 | } 136 | 137 | bytes = reallocf(bytes, offset); 138 | return [NSData dataWithBytesNoCopy:bytes length:(NSUInteger)offset]; 139 | } 140 | 141 | @end 142 | -------------------------------------------------------------------------------- /ManOpen/PB.project: -------------------------------------------------------------------------------- 1 | { 2 | APPCLASS = NSApplication; 3 | "DYNAMIC_CODE_GEN" = YES; 4 | "English_RESOURCES" = {}; 5 | FILESTABLE = { 6 | CLASSES = ( 7 | "AproposDocument.m", 8 | "FindPanelController.m", 9 | "ManDocument.m", 10 | "ManDocumentController.m", 11 | "NSData+Utils.m", 12 | "PrefPanelController.m" 13 | ); 14 | "English_INTERFACES" = ( 15 | "ManOpen.nib", 16 | "PrefPanel.nib", 17 | "DocController.nib", 18 | "ManOpen-macintosh.nib", 19 | "Apropos.nib", 20 | "ManOpen-macos.nib", 21 | "ManPage.nib", 22 | "FindPanel.nib" 23 | ); 24 | "English_OTHER_RESOURCES" = ("Credits.rtf", "InfoPlist.strings"); 25 | FRAMEWORKS = ("AppKit.framework", "Foundation.framework"); 26 | FRAMEWORKSEARCH = (); 27 | "H_FILES" = ( 28 | "AproposDocument.h", 29 | "FindPanelController.h", 30 | "ManDocument.h", 31 | "ManDocumentController.h", 32 | "ManOpenProtocol.h", 33 | "NSData+Utils.h", 34 | "PrefPanelController.h", 35 | "SystemType.h" 36 | ); 37 | IMAGES = ("LinkCursor.tiff", "helpQMark.tiff", "ManOpen-nextstep.tiff", "ManOpen.tiff"); 38 | INTERFACES = (); 39 | "OTHER_LINKED" = ("ManOpen_main.m"); 40 | "OTHER_RESOURCES" = ("ManOpen.icns", "ManOpen.scriptSuite", "ManOpen.scriptTerminology"); 41 | "OTHER_SOURCES" = ( 42 | "CustomInfo.plist", 43 | "h.template", 44 | "m.template", 45 | Makefile, 46 | "Makefile.postamble", 47 | "Makefile.preamble" 48 | ); 49 | "PRECOMPILED_HEADERS" = (); 50 | "PROJECT_HEADERS" = ("ManOpenProtocol.h", "SystemType.h"); 51 | "PUBLIC_HEADERS" = (); 52 | SUBPROJECTS = ("cat2rtf.tproj", "cat2html.tproj"); 53 | }; 54 | LANGUAGE = English; 55 | MAKEFILEDIR = "$(NEXT_ROOT)/NextDeveloper/Makefiles/pb_makefiles"; 56 | "NEXTSTEP_APPICON" = "ManOpen.tiff"; 57 | "NEXTSTEP_BUILDTOOL" = "/usr/bin/gnumake"; 58 | "NEXTSTEP_DOCUMENTEXTENSIONS" = ({Extension = man; Image = "ManOpen.tiff"; }); 59 | "NEXTSTEP_INSTALLDIR" = "$(HOME)/Apps"; 60 | "NEXTSTEP_JAVA_COMPILER" = "/usr/bin/javac"; 61 | "NEXTSTEP_MAINNIB" = "ManOpen.nib"; 62 | "NEXTSTEP_OBJCPLUS_COMPILER" = "/usr/bin/cc"; 63 | "PDO_UNIX_BUILDTOOL" = "$NEXT_ROOT/NextDeveloper/bin/make"; 64 | "PDO_UNIX_JAVA_COMPILER" = "$(JDKBINDIR)/javac"; 65 | "PDO_UNIX_OBJCPLUS_COMPILER" = "$(NEXTDEV_BIN)/gcc"; 66 | PROJECTNAME = ManOpen; 67 | PROJECTTYPE = Application; 68 | PROJECTVERSION = "2.8"; 69 | SYSTEMEXTENSIONS = (); 70 | "WINDOWS_BUILDTOOL" = "$NEXT_ROOT/NextDeveloper/Executables/make"; 71 | "WINDOWS_INSTALLDIR" = "/MyApps"; 72 | "WINDOWS_JAVA_COMPILER" = "$(JDKBINDIR)/javac.exe"; 73 | "WINDOWS_MAINNIB" = "ManOpen-windows.nib"; 74 | "WINDOWS_OBJCPLUS_COMPILER" = "$(DEVDIR)/gcc"; 75 | } 76 | -------------------------------------------------------------------------------- /ManOpen/PrefPanelController.h: -------------------------------------------------------------------------------- 1 | /* PrefPanelController.h created by lindberg on Fri 08-Oct-1999 */ 2 | 3 | #import 4 | 5 | @interface PrefPanelController : NSWindowController 6 | { 7 | NSMutableArray *manPathArray; 8 | IBOutlet NSArrayController *manPathController; 9 | IBOutlet NSTableView *manPathTableView; 10 | IBOutlet NSTextField *fontField; 11 | IBOutlet NSMatrix *generalSwitchMatrix; 12 | IBOutlet NSPopUpButton *appPopup; 13 | } 14 | 15 | + (id)sharedInstance; 16 | + (void)registerManDefaults; 17 | 18 | - (IBAction)openFontPanel:(id)sender; 19 | 20 | @end 21 | 22 | @interface PrefPanelController (ManPath) 23 | - (IBAction)addPathFromPanel:(id)sender; 24 | @end 25 | @interface PrefPanelController (DefaultManApp) 26 | - (IBAction)chooseNewApp:(id)sender; 27 | @end 28 | 29 | @interface NSUserDefaults (ManOpenPreferences) 30 | - (NSFont *)manFont; 31 | - (NSString *)manPath; 32 | - (NSColor *)manTextColor; 33 | - (NSColor *)manLinkColor; 34 | - (NSColor *)manBackgroundColor; 35 | @end 36 | 37 | // This needs to be in the header so IB can find it 38 | @interface DisplayPathFormatter : NSFormatter 39 | @end 40 | 41 | @interface ManOpenColorDataTransformer : NSValueTransformer 42 | @end 43 | -------------------------------------------------------------------------------- /ManOpen/cat2html.tproj/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Generated by the Apple Project Builder. 3 | # 4 | # NOTE: Do NOT change this file -- Project Builder maintains it. 5 | # 6 | # Put all of your customizations in files called Makefile.preamble 7 | # and Makefile.postamble (both optional), and Makefile will include them. 8 | # 9 | 10 | NAME = cat2html 11 | 12 | PROJECTVERSION = 2.8 13 | PROJECT_TYPE = Tool 14 | 15 | CFILES = cat2html.c 16 | 17 | OTHERSRCS = Makefile.preamble Makefile Makefile.postamble cat2html.l 18 | 19 | 20 | MAKEFILEDIR = $(NEXT_ROOT)/NextDeveloper/Makefiles/pb_makefiles 21 | CODE_GEN_STYLE = DYNAMIC 22 | MAKEFILE = tool.make 23 | LIBS = 24 | DEBUG_LIBS = $(LIBS) 25 | PROF_LIBS = $(LIBS) 26 | 27 | 28 | FRAMEWORKS = -framework Foundation 29 | 30 | 31 | NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc 32 | WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc 33 | PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc 34 | NEXTSTEP_JAVA_COMPILER = /usr/bin/javac 35 | WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe 36 | PDO_UNIX_JAVA_COMPILER = $(JDKBINDIR)/javac 37 | 38 | include $(MAKEFILEDIR)/platform.make 39 | 40 | -include Makefile.preamble 41 | 42 | include $(MAKEFILEDIR)/$(MAKEFILE) 43 | 44 | -include Makefile.postamble 45 | 46 | -include Makefile.dependencies 47 | -------------------------------------------------------------------------------- /ManOpen/cat2html.tproj/Makefile.postamble: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Makefile.postamble 3 | # Copyright 1997, Apple Computer, Inc. 4 | # 5 | # Use this makefile, which is imported after all other makefiles, to 6 | # override attributes for a project's Makefile environment. This allows you 7 | # to take advantage of the environment set up by the other Makefiles. 8 | # You can also define custom rules at the end of this file. 9 | # 10 | ############################################################################### 11 | # 12 | # These variables are exported by the standard makefiles and can be 13 | # used in any customizations you make. They are *outputs* of 14 | # the Makefiles and should be used, not set. 15 | # 16 | # PRODUCTS: products to install. All of these products will be placed in 17 | # the directory $(DSTROOT)$(INSTALLDIR) 18 | # GLOBAL_RESOURCE_DIR: The directory to which resources are copied. 19 | # LOCAL_RESOURCE_DIR: The directory to which localized resources are copied. 20 | # OFILE_DIR: Directory into which .o object files are generated. 21 | # DERIVED_SRC_DIR: Directory used for all other derived files 22 | # 23 | # ALL_CFLAGS: flags to pass when compiling .c files 24 | # ALL_MFLAGS: flags to pass when compiling .m files 25 | # ALL_CCFLAGS: flags to pass when compiling .cc, .cxx, and .C files 26 | # ALL_MMFLAGS: flags to pass when compiling .mm, .mxx, and .M files 27 | # ALL_PRECOMPFLAGS: flags to pass when precompiling .h files 28 | # ALL_LDFLAGS: flags to pass when linking object files 29 | # ALL_LIBTOOL_FLAGS: flags to pass when libtooling object files 30 | # ALL_PSWFLAGS: flags to pass when processing .psw and .pswm (pswrap) files 31 | # ALL_RPCFLAGS: flags to pass when processing .rpc (rpcgen) files 32 | # ALL_YFLAGS: flags to pass when processing .y (yacc) files 33 | # ALL_LFLAGS: flags to pass when processing .l (lex) files 34 | # 35 | # NAME: name of application, bundle, subproject, palette, etc. 36 | # LANGUAGE: langage in which the project is written (default "English") 37 | # LOCAL_RESOURCES: localized resources (e.g. nib's, images) of project 38 | # GLOBAL_RESOURCES: non-localized resources of project 39 | # 40 | # SRCROOT: base directory in which to place the new source files 41 | # SRCPATH: relative path from SRCROOT to present subdirectory 42 | # 43 | # INSTALLDIR: Directory the product will be installed into by 'install' target 44 | # PUBLIC_HDR_INSTALLDIR: where to install public headers. Don't forget 45 | # to prefix this with DSTROOT when you use it. 46 | # PRIVATE_HDR_INSTALLDIR: where to install private headers. Don't forget 47 | # to prefix this with DSTROOT when you use it. 48 | # 49 | # EXECUTABLE_EXT: Executable extension for the platform (i.e. .exe on Windows) 50 | # 51 | ############################################################################### 52 | 53 | # Some compiler flags can be overridden here for certain build situations. 54 | # 55 | # WARNING_CFLAGS: flag used to set warning level (defaults to -Wmost) 56 | # DEBUG_SYMBOLS_CFLAGS: debug-symbol flag passed to all builds (defaults 57 | # to -g) 58 | # DEBUG_BUILD_CFLAGS: flags passed during debug builds (defaults to -DDEBUG) 59 | # OPTIMIZE_BUILD_CFLAGS: flags passed during optimized builds (defaults 60 | # to -O) 61 | # PROFILE_BUILD_CFLAGS: flags passed during profile builds (defaults 62 | # to -pg -DPROFILE) 63 | # LOCAL_DIR_INCLUDE_DIRECTIVE: flag used to add current directory to 64 | # the include path (defaults to -I.) 65 | # DEBUG_BUILD_LDFLAGS, OPTIMIZE_BUILD_LDFLAGS, PROFILE_BUILD_LDFLAGS: flags 66 | # passed to ld/libtool (defaults to nothing) 67 | 68 | 69 | # Library and Framework projects only: 70 | # INSTALL_NAME_DIRECTIVE: This directive ensures that executables linked 71 | # against the framework will run against the correct version even if 72 | # the current version of the framework changes. You may override this 73 | # to "" as an alternative to using the DYLD_LIBRARY_PATH during your 74 | # development cycle, but be sure to restore it before installing. 75 | 76 | 77 | # Ownership and permissions of files installed by 'install' target 78 | 79 | #INSTALL_AS_USER = root 80 | # User/group ownership 81 | #INSTALL_AS_GROUP = wheel 82 | # (probably want to set both of these) 83 | #INSTALL_PERMISSIONS = 84 | # If set, 'install' chmod's executable to this 85 | 86 | 87 | # Options to strip. Note: -S strips debugging symbols (executables can be stripped 88 | # down further with -x or, if they load no bundles, with no options at all). 89 | 90 | #STRIPFLAGS = -S 91 | 92 | 93 | ######################################################################### 94 | # Put rules to extend the behavior of the standard Makefiles here. Include them in 95 | # the dependency tree via cvariables like AFTER_INSTALL in the Makefile.preamble. 96 | # 97 | # You should avoid redefining things like "install" or "app", as they are 98 | # owned by the top-level Makefile API and no context has been set up for where 99 | # derived files should go. 100 | # 101 | 102 | #LEX = /Net/tori/Users/lindberg/Unix/bin-nextstep/flex -8 103 | LEX = flex -8 104 | 105 | .l.c: 106 | @(echo "(If you don't have Flex 2.5.2 (2.5.1?) or later this won't work)"; \ 107 | cmd="$(LEX) $(ALL_LFLAGS) -P$* -o$*.c $*.l" ; echo $$cmd; $$cmd ) 108 | 109 | 110 | reallyclean: 111 | $(RM) -f cat2html.c 112 | -------------------------------------------------------------------------------- /ManOpen/cat2html.tproj/Makefile.preamble: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Makefile.preamble 3 | # Copyright 1997, Apple Computer, Inc. 4 | # 5 | # Use this makefile for configuring the standard application makefiles 6 | # associated with ProjectBuilder. It is included before the main makefile. 7 | # In Makefile.preamble you set attributes for a project, so they are available 8 | # to the project's makefiles. In contrast, you typically write additional rules or 9 | # override built-in behavior in the Makefile.postamble. 10 | # 11 | # Each directory in a project tree (main project plus subprojects) should 12 | # have its own Makefile.preamble and Makefile.postamble. 13 | ############################################################################### 14 | # 15 | # Before the main makefile is included for this project, you may set: 16 | # 17 | # MAKEFILEDIR: Directory in which to find $(MAKEFILE) 18 | # MAKEFILE: Top level mechanism Makefile (e.g., app.make, bundle.make) 19 | 20 | # Compiler/linker flags added to the defaults: The OTHER_* variables will be 21 | # inherited by all nested sub-projects, but the LOCAL_ versions of the same 22 | # variables will not. Put your -I, -D, -U, and -L flags in ProjectBuilder's 23 | # Build Attributes inspector if at all possible. To override the default flags 24 | # that get passed to ${CC} (e.g. change -O to -O2), see Makefile.postamble. The 25 | # variables below are *inputs* to the build process and distinct from the override 26 | # settings done (less often) in the Makefile.postamble. 27 | # 28 | # OTHER_CFLAGS, LOCAL_CFLAGS: additional flags to pass to the compiler 29 | # Note that $(OTHER_CFLAGS) and $(LOCAL_CFLAGS) are used for .h, ...c, .m, 30 | # .cc, .cxx, .C, and .M files. There is no need to respecify the 31 | # flags in OTHER_MFLAGS, etc. 32 | # OTHER_MFLAGS, LOCAL_MFLAGS: additional flags for .m files 33 | # OTHER_CCFLAGS, LOCAL_CCFLAGS: additional flags for .cc, .cxx, and ...C files 34 | # OTHER_MMFLAGS, LOCAL_MMFLAGS: additional flags for .mm and .M files 35 | # OTHER_PRECOMPFLAGS, LOCAL_PRECOMPFLAGS: additional flags used when 36 | # precompiling header files 37 | # OTHER_LDFLAGS, LOCAL_LDFLAGS: additional flags passed to ld and libtool 38 | # OTHER_PSWFLAGS, LOCAL_PSWFLAGS: additional flags passed to pswrap 39 | # OTHER_RPCFLAGS, LOCAL_RPCFLAGS: additional flags passed to rpcgen 40 | # OTHER_YFLAGS, LOCAL_YFLAGS: additional flags passed to yacc 41 | # OTHER_LFLAGS, LOCAL_LFLAGS: additional flags passed to lex 42 | 43 | # These variables provide hooks enabling you to add behavior at almost every 44 | # stage of the make: 45 | # 46 | # BEFORE_PREBUILD: targets to build before installing headers for a subproject 47 | # AFTER_PREBUILD: targets to build after installing headers for a subproject 48 | # BEFORE_BUILD_RECURSION: targets to make before building subprojects 49 | # BEFORE_BUILD: targets to make before a build, but after subprojects 50 | # AFTER_BUILD: targets to make after a build 51 | # 52 | # BEFORE_INSTALL: targets to build before installing the product 53 | # AFTER_INSTALL: targets to build after installing the product 54 | # BEFORE_POSTINSTALL: targets to build before postinstalling every subproject 55 | # AFTER_POSTINSTALL: targts to build after postinstalling every subproject 56 | # 57 | # BEFORE_INSTALLHDRS: targets to build before installing headers for a 58 | # subproject 59 | # AFTER_INSTALLHDRS: targets to build after installing headers for a subproject 60 | # BEFORE_INSTALLSRC: targets to build before installing source for a subproject 61 | # AFTER_INSTALLSRC: targets to build after installing source for a subproject 62 | # 63 | # BEFORE_DEPEND: targets to build before building dependencies for a 64 | # subproject 65 | # AFTER_DEPEND: targets to build after building dependencies for a 66 | # subproject 67 | # 68 | # AUTOMATIC_DEPENDENCY_INFO: if YES, then the dependency file is 69 | # updated every time the project is built. If NO, the dependency 70 | # file is only built when the depend target is invoked. 71 | 72 | # Framework-related variables: 73 | # FRAMEWORK_DLL_INSTALLDIR: On Windows platforms, this variable indicates 74 | # where to put the framework's DLL. This variable defaults to 75 | # $(INSTALLDIR)/../Executables 76 | 77 | # Library-related variables: 78 | # PUBLIC_HEADER_DIR: Determines where public exported header files 79 | # should be installed. Do not include $(DSTROOT) in this value -- 80 | # it is prefixed automatically. 81 | # PRIVATE_HEADER_DIR: Determines where private exported header files 82 | # should be installed. Do not include $(DSTROOT) in this value -- 83 | # it is prefixed automatically. 84 | # LIBRARY_STYLE: This may be either STATIC or DYNAMIC, and determines 85 | # whether the libraries produced are statically linked when they 86 | # are used or if they are dynamically loadable. <> 87 | # LIBRARY_DLL_INSTALLDIR: On Windows platforms, this variable indicates 88 | # where to put the library's DLL. This variable defaults to 89 | # $(INSTALLDIR)/../Executables 90 | # 91 | # INSTALL_AS_USER: owner of the intalled products (default root) 92 | # INSTALL_AS_GROUP: group of the installed products (default wheel) 93 | # INSTALL_PERMISSION: permissions of the installed product (default o+rX) 94 | # 95 | # OTHER_RECURSIVE_VARIABLES: The names of variables which you want to be 96 | # passed on the command line to recursive invocations of make. Note that 97 | # the values in OTHER_*FLAGS are inherited by subprojects automatically -- 98 | # you do not have to (and shouldn't) add OTHER_*FLAGS to 99 | # OTHER_RECURSIVE_VARIABLES. 100 | 101 | # Additional headers to export beyond those in the PB.project: 102 | # OTHER_PUBLIC_HEADERS 103 | # OTHER_PROJECT_HEADERS 104 | # OTHER_PRIVATE_HEADERS 105 | 106 | # Additional files for the project's product: <> 107 | # OTHER_RESOURCES: (non-localized) resources for this project 108 | # OTHER_OFILES: relocatables to be linked into this project 109 | # OTHER_LIBS: more libraries to link against 110 | # OTHER_PRODUCT_DEPENDS: other dependencies of this project 111 | # OTHER_SOURCEFILES: other source files maintained by .pre/postamble 112 | # OTHER_GARBAGE: additional files to be removed by `make clean' 113 | 114 | # Set this to YES if you don't want a final libtool call for a library/framework. 115 | # BUILD_OFILES_LIST_ONLY 116 | 117 | # To include a version string, project source must exist in a directory named 118 | # $(NAME).%d[.%d][.%d] and the following line must be uncommented. 119 | # OTHER_GENERATED_OFILES = $(VERS_OFILE) 120 | 121 | # This definition will suppress stripping of debug symbols when an executable 122 | # is installed. By default it is YES. 123 | # STRIP_ON_INSTALL = NO 124 | -------------------------------------------------------------------------------- /ManOpen/cat2html.tproj/PB.project: -------------------------------------------------------------------------------- 1 | { 2 | "DYNAMIC_CODE_GEN" = YES; 3 | FILESTABLE = { 4 | CLASSES = (); 5 | FRAMEWORKS = ("Foundation.framework"); 6 | "H_FILES" = (); 7 | "OTHER_LINKED" = ("cat2html.c"); 8 | "OTHER_SOURCES" = ("Makefile.preamble", Makefile, "Makefile.postamble", "cat2html.l"); 9 | SUBPROJECTS = (); 10 | }; 11 | LANGUAGE = English; 12 | "LOCALIZABLE_FILES" = {}; 13 | MAKEFILEDIR = "$(NEXT_ROOT)/NextDeveloper/Makefiles/pb_makefiles"; 14 | "NEXTSTEP_BUILDTOOL" = "/bin/gnumake"; 15 | "NEXTSTEP_JAVA_COMPILER" = "/usr/bin/javac"; 16 | "NEXTSTEP_OBJCPLUS_COMPILER" = "/usr/bin/cc"; 17 | "PDO_UNIX_BUILDTOOL" = "$NEXT_ROOT/NextDeveloper/bin/make"; 18 | "PDO_UNIX_JAVA_COMPILER" = "$(JDKBINDIR)/javac"; 19 | "PDO_UNIX_OBJCPLUS_COMPILER" = "$(NEXTDEV_BIN)/gcc"; 20 | PROJECTNAME = cat2html; 21 | PROJECTTYPE = Tool; 22 | PROJECTVERSION = "2.8"; 23 | "WINDOWS_BUILDTOOL" = "$NEXT_ROOT/NextDeveloper/Executables/make"; 24 | "WINDOWS_JAVA_COMPILER" = "$(JDKBINDIR)/javac.exe"; 25 | "WINDOWS_OBJCPLUS_COMPILER" = "$(DEVDIR)/gcc"; 26 | } 27 | -------------------------------------------------------------------------------- /ManOpen/cat2html.tproj/cat2html.l: -------------------------------------------------------------------------------- 1 | /* 2 | cat2html.l: cat to HTML converter specification 3 | (c) 1993 by Harald Schlangmann 4 | Permission is granted to use this code. Send additions 5 | and bug reports to my address below. 6 | 7 | v1.0 Harald Schlangmann, July 20 1993 8 | schlangm@informatik.uni-muenchen.de 9 | v1.1 Bold style x^H{x^H}* implemented. 10 | 11 | v2.0 Carl Lindberg lindberg@mac.com 12 | Added blank line suppressing. 13 | v2.1 New cat2html to spit out HTML with links -CEL 14 | 15 | */ 16 | 17 | #include 18 | 19 | #define BOLDFLAG 1 20 | #define ULINEFLAG 2 21 | 22 | int flags = 0, neededflags = 0; 23 | 24 | #define SETB neededflags |= BOLDFLAG 25 | #define UNSETB neededflags &= ~BOLDFLAG 26 | #define SETU neededflags |= ULINEFLAG 27 | #define UNSETU neededflags &= ~ULINEFLAG 28 | 29 | /* 30 | * Default settings, may be changed using options... 31 | */ 32 | 33 | static char *startBold = ""; 34 | static char *stopBold = ""; 35 | static char *startULine = ""; 36 | static char *stopULine = ""; 37 | static char *startHeader = ""; 38 | static char *stopHeader = ""; 39 | static int addLinks = 0; 40 | static int markHeaders = 0; 41 | static int lineCount = 0; 42 | static int maxLineCount = 3; 43 | 44 | 45 | /* Decode a UTF8 sequence to return the unicode number */ 46 | static unsigned decodeUTF8(unsigned char *buf, yy_size_t len) 47 | { 48 | int n = buf[0] & (0x7f >> len); 49 | yy_size_t i; 50 | 51 | for (i=1; i
",stdout);
 65 |     }
 66 |     
 67 |     static void emitPostamble(void)
 68 |     {
 69 |         fputs("
\n",stdout); 70 | } 71 | 72 | #define adjust() if( neededflags!=flags ) _adjust() 73 | 74 | static void _adjust(void) { 75 | 76 | if( (flags^neededflags)&ULINEFLAG ) 77 | fputs(neededflags&ULINEFLAG? 78 | startULine:stopULine,stdout); 79 | if( (flags^neededflags)&BOLDFLAG ) 80 | fputs(neededflags&BOLDFLAG? 81 | startBold:stopBold,stdout); 82 | flags = neededflags; 83 | } 84 | 85 | static void emitChar(int ch) { 86 | adjust(); 87 | 88 | if (ch=='\n') { 89 | if (lineCount > maxLineCount) return; 90 | lineCount++; 91 | } 92 | else lineCount = 0; 93 | 94 | switch(ch) { 95 | case '"': fputs(""",stdout); break; 96 | case '<': fputs("<" ,stdout); break; 97 | case '>': fputs(">" ,stdout); break; 98 | case '&': fputs("&" ,stdout); break; 99 | default: fputc(ch,stdout); 100 | } 101 | } 102 | 103 | static void emitString(char *string) 104 | { 105 | size_t i, len=strlen(string); 106 | for (i=0;i= 4 && charblock[3] == '\010') 121 | return; 122 | 123 | /* If the characters are equal, they are printed on top of each other, so make it bold */ 124 | if( charblock[0] == charblock[2] ) { 125 | if (doBold) SETB; 126 | emitChar(charblock[0]); 127 | if (doBold) UNSETB; 128 | } 129 | /* Otherwise, just emit the second character. */ 130 | else { 131 | #ifdef DEBUGBACKSPACE 132 | fprintf(stderr, "Unknown backspace pair %c and %c\n", charblock[0], charblock[2]); 133 | #endif 134 | emitChar(charblock[2]); 135 | } 136 | } 137 | 138 | static void emitBackspacedText(char *text, yy_size_t length) 139 | { 140 | yy_size_t i=0; 141 | while (i < length) { 142 | if ((i < (length-1)) && text[i+1] == '\010') { 143 | emitBackspacedLetters(&text[i], 3, 0); 144 | i+=3; 145 | } 146 | else { 147 | emitChar(text[i]); 148 | i++; 149 | } 150 | } 151 | } 152 | 153 | /* Use hexidecimal entities */ 154 | static void emitUnicode(unsigned charNum) 155 | { 156 | char entityBuf[20]; 157 | sprintf(entityBuf, "&#x%x;", charNum); 158 | emitRaw(entityBuf); 159 | } 160 | 161 | 162 | ALLBUTUL [^\n_\010] 163 | NEEDQUOTE [<>&"] 164 | VERBATIM [^_\n\010\x1B<>&( \t"\xC2-\xF4] 165 | UPPER [A-Z] 166 | UPPERCONT [A-Z0-9 \t()] 167 | UPPERBS {UPPER}\010{UPPER} 168 | UPPERBSCONT ({UPPERBS}|[ \t()]) 169 | UTF8START [\xC2-\xF4] 170 | UTF8CONT [\x80-\xBF] 171 | UTF8SEQ {UTF8START}({UTF8CONT}{1,3}) 172 | SGRSTART \x1B\[ 173 | 174 | %option debug 175 | %option noyywrap 176 | %option noinput 177 | %option 8bit 178 | %option prefix="cat2html" 179 | 180 | %x FIRSTLINE 181 | 182 | %% 183 | 184 | /* 185 | * Start state FIRSTLINE is used to treat the first non-empty 186 | * line special. (First line contains header). 187 | */ 188 | 189 | /* Some X.org X11 pages have a weird #pragma at the start; strip it out. */ 190 | "#pragma".*\n {} 191 | 192 | . { SETB; emitChar(yytext[0]); } 193 | 194 | .\n { 195 | SETB; 196 | emitChar(yytext[0]); 197 | UNSETB; 198 | emitChar('\n'); 199 | BEGIN(INITIAL); 200 | } 201 | 202 | \n { UNSETB; emitChar('\n'); } 203 | 204 | /* Part of the X11 thing gets put on a separate line by nroff, sigh. */ 205 | ^"vate/var/tmp/X11".*\n {} 206 | 207 | /* 208 | * Put a special HREF around likely looking links to other man pages 209 | */ 210 | [_a-zA-Z][-a-zA-Z0-9._]*(-[ \t\n]+[-a-zA-Z0-9._]+)?[ \t\n]*"("[1-9n][a-zA-Z]?")" { 211 | 212 | if (!addLinks) 213 | { 214 | emitString(yytext); 215 | } 216 | else 217 | { 218 | int i; 219 | char href[yyleng+1]; 220 | 221 | /* Change newlines to spaces in the href portion */ 222 | strcpy(href, yytext); 223 | for(i=0; i"); 229 | emitString(yytext); 230 | emitRaw(""); 231 | } 232 | } 233 | 234 | 235 | /* 236 | * Non-empty, all-uppercase lines are treated as headers 237 | */ 238 | ^{UPPER}{UPPERCONT}*$ { 239 | SETB; 240 | if (markHeaders) emitRaw(startHeader); 241 | emitString(yytext); 242 | if (markHeaders) emitRaw(stopHeader); 243 | UNSETB; 244 | emitChar('\n'); 245 | } 246 | 247 | /* Similar for all-uppercase lines that use backspace for bolding */ 248 | ^{UPPERBS}{UPPERBSCONT}*$ { 249 | SETB; 250 | if (markHeaders) emitRaw(startHeader); 251 | emitBackspacedText(yytext, yyleng); 252 | if (markHeaders) emitRaw(stopHeader); 253 | UNSETB; 254 | emitChar('\n'); 255 | } 256 | 257 | /* 258 | * nroff +- 259 | */ 260 | 261 | "+"\010_ emitRaw("±"); 262 | 263 | /* 264 | * underline (part 1) 265 | */ 266 | 267 | {ALLBUTUL}\010_ { 268 | SETU; 269 | emitChar(yytext[0]); 270 | UNSETU; 271 | } 272 | 273 | /* 274 | * nroff bullets 275 | */ 276 | o\010"+" emitRaw("·"); // "•" does not work 277 | "+"\010o emitRaw("·"); 278 | o\010o\010"+"\010"+" emitRaw("·"); 279 | "+"\010"+\010"o\010o emitRaw("·"); 280 | 281 | /* 282 | * underline (part 2) 283 | */ 284 | 285 | _\010{ALLBUTUL} { 286 | SETU; 287 | emitChar(yytext[2]); 288 | UNSETU; 289 | } 290 | 291 | /* 292 | * handle further BS combinations 293 | */ 294 | 295 | .\010.\010? { 296 | emitBackspacedLetters(yytext, yyleng, 1); 297 | } 298 | 299 | /* Same idea but with UTF-8 characters */ 300 | {UTF8SEQ}\010{UTF8SEQ}\010? { 301 | if (yytext[yyleng-1] != '\010') { 302 | char *backspace = index(yytext, '\010'); 303 | if (backspace != NULL) { 304 | emitUnicode(decodeUTF8((unsigned char *)backspace+1, (yyleng - (backspace-yytext) - 1))); 305 | } 306 | } 307 | } 308 | 309 | /* If we find a UTF8 sequence, decode it */ 310 | {UTF8SEQ} { 311 | emitUnicode(decodeUTF8((unsigned char *)yytext, yyleng)); 312 | } 313 | 314 | /* Some versions of nroff/grotty use SGR escape sequences instead of the backspace hacks */ 315 | {SGRSTART}0?m UNSETU;UNSETB; 316 | {SGRSTART}1m SETB; 317 | {SGRSTART}[347]m SETU; 318 | {SGRSTART}(21|22)m UNSETB; 319 | {SGRSTART}(23|24|27)m UNSETU; 320 | {SGRSTART}[0-9;]+m {/*ignore any other codes*/} 321 | 322 | /* 323 | group characters in VERBATIM to make this 324 | filter faster... 325 | */ 326 | [ \t\n]+ emitString(yytext); 327 | {VERBATIM}+/[^\010] emitString(yytext); 328 | 329 | /* 330 | remaining specials 331 | */ 332 | 333 | /* \n emitChar('\n'); */ /*Matched by above whitespace matching rule */ 334 | . emitChar(yytext[0]); 335 | 336 | %% 337 | 338 | static void usage() { 339 | 340 | fprintf(stderr,"Usage: cat2html [-il] []\n" 341 | "\tTranslate output of (g)nroff to HTML. If no\n" 342 | "\t is given, cat2html reads stdin.\n" 343 | "\toption -i uses italic characters for underlining.\n" 344 | "\toption -l adds 'manpage:' HREF links to other man pages.\n" 345 | "\tHTML output is sent to stdout.\n"); 346 | exit(1); 347 | } 348 | 349 | int main(int argc, char *argv[]) 350 | { 351 | int c; 352 | 353 | yy_flex_debug = 0; 354 | 355 | /* Keep the same args as cat2rtf, even though -s does not really make much difference */ 356 | while ((c = getopt(argc, argv, "dgGiISs:lH")) != EOF) 357 | { 358 | switch( c ) { 359 | case 'd': 360 | yy_flex_debug = 1; 361 | break; 362 | case 'g': 363 | case 'G': 364 | startBold = ""; // ""; 365 | stopBold = ""; // ""; 366 | break; 367 | case 'i': 368 | case 'I': 369 | startULine = ""; 370 | stopULine = ""; 371 | break; 372 | case 's': 373 | maxLineCount = atoi(optarg); 374 | break; 375 | case 'S': 376 | maxLineCount = -1; 377 | break; 378 | case 'l': 379 | addLinks = 1; 380 | break; 381 | case 'H': 382 | markHeaders = 1; 383 | break; 384 | case '?': 385 | default: 386 | usage(); 387 | } 388 | } 389 | 390 | if( optind < argc ) 391 | yyin = fopen(argv[optind], "r"); 392 | else 393 | yyin = stdin; 394 | 395 | emitPreamble(); 396 | BEGIN(FIRSTLINE); 397 | yylex(); 398 | emitPostamble(); 399 | 400 | /* Shuts up a compiler warning */ 401 | if (0) unput('h'); 402 | 403 | return 0; 404 | } 405 | -------------------------------------------------------------------------------- /ManOpen/cat2rtf.tproj/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Generated by the Apple Project Builder. 3 | # 4 | # NOTE: Do NOT change this file -- Project Builder maintains it. 5 | # 6 | # Put all of your customizations in files called Makefile.preamble 7 | # and Makefile.postamble (both optional), and Makefile will include them. 8 | # 9 | 10 | NAME = cat2rtf 11 | 12 | PROJECTVERSION = 2.8 13 | PROJECT_TYPE = Tool 14 | 15 | CFILES = cat2rtf.c 16 | 17 | OTHERSRCS = Makefile.preamble Makefile Makefile.postamble cat2rtf.l 18 | 19 | 20 | MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles 21 | CODE_GEN_STYLE = DYNAMIC 22 | MAKEFILE = tool.make 23 | LIBS = 24 | DEBUG_LIBS = $(LIBS) 25 | PROF_LIBS = $(LIBS) 26 | 27 | 28 | 29 | 30 | NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc 31 | WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc 32 | PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc 33 | NEXTSTEP_JAVA_COMPILER = /usr/bin/javac 34 | WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe 35 | PDO_UNIX_JAVA_COMPILER = $(JDKBINDIR)/javac 36 | 37 | include $(MAKEFILEDIR)/platform.make 38 | 39 | -include Makefile.preamble 40 | 41 | include $(MAKEFILEDIR)/$(MAKEFILE) 42 | 43 | -include Makefile.postamble 44 | 45 | -include Makefile.dependencies 46 | -------------------------------------------------------------------------------- /ManOpen/cat2rtf.tproj/Makefile.postamble: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Makefile.postamble 3 | # Copyright 1997, Apple Computer, Inc. 4 | # 5 | # Use this makefile, which is imported after all other makefiles, to 6 | # override attributes for a project's Makefile environment. This allows you 7 | # to take advantage of the environment set up by the other Makefiles. 8 | # You can also define custom rules at the end of this file. 9 | # 10 | ############################################################################### 11 | # 12 | # These variables are exported by the standard makefiles and can be 13 | # used in any customizations you make. They are *outputs* of 14 | # the Makefiles and should be used, not set. 15 | # 16 | # PRODUCTS: products to install. All of these products will be placed in 17 | # the directory $(DSTROOT)$(INSTALLDIR) 18 | # GLOBAL_RESOURCE_DIR: The directory to which resources are copied. 19 | # LOCAL_RESOURCE_DIR: The directory to which localized resources are copied. 20 | # OFILE_DIR: Directory into which .o object files are generated. 21 | # DERIVED_SRC_DIR: Directory used for all other derived files 22 | # 23 | # ALL_CFLAGS: flags to pass when compiling .c files 24 | # ALL_MFLAGS: flags to pass when compiling .m files 25 | # ALL_CCFLAGS: flags to pass when compiling .cc, .cxx, and .C files 26 | # ALL_MMFLAGS: flags to pass when compiling .mm, .mxx, and .M files 27 | # ALL_PRECOMPFLAGS: flags to pass when precompiling .h files 28 | # ALL_LDFLAGS: flags to pass when linking object files 29 | # ALL_LIBTOOL_FLAGS: flags to pass when libtooling object files 30 | # ALL_PSWFLAGS: flags to pass when processing .psw and .pswm (pswrap) files 31 | # ALL_RPCFLAGS: flags to pass when processing .rpc (rpcgen) files 32 | # ALL_YFLAGS: flags to pass when processing .y (yacc) files 33 | # ALL_LFLAGS: flags to pass when processing .l (lex) files 34 | # 35 | # NAME: name of application, bundle, subproject, palette, etc. 36 | # LANGUAGE: langage in which the project is written (default "English") 37 | # LOCAL_RESOURCES: localized resources (e.g. nib's, images) of project 38 | # GLOBAL_RESOURCES: non-localized resources of project 39 | # 40 | # SRCROOT: base directory in which to place the new source files 41 | # SRCPATH: relative path from SRCROOT to present subdirectory 42 | # 43 | # INSTALLDIR: Directory the product will be installed into by 'install' target 44 | # PUBLIC_HDR_INSTALLDIR: where to install public headers. Don't forget 45 | # to prefix this with DSTROOT when you use it. 46 | # PRIVATE_HDR_INSTALLDIR: where to install private headers. Don't forget 47 | # to prefix this with DSTROOT when you use it. 48 | # 49 | # EXECUTABLE_EXT: Executable extension for the platform (i.e. .exe on Windows) 50 | # 51 | ############################################################################### 52 | 53 | # Some compiler flags can be overridden here for certain build situations. 54 | # 55 | # WARNING_CFLAGS: flag used to set warning level (defaults to -Wmost) 56 | # DEBUG_SYMBOLS_CFLAGS: debug-symbol flag passed to all builds (defaults 57 | # to -g) 58 | # DEBUG_BUILD_CFLAGS: flags passed during debug builds (defaults to -DDEBUG) 59 | # OPTIMIZE_BUILD_CFLAGS: flags passed during optimized builds (defaults 60 | # to -O) 61 | # PROFILE_BUILD_CFLAGS: flags passed during profile builds (defaults 62 | # to -pg -DPROFILE) 63 | # LOCAL_DIR_INCLUDE_DIRECTIVE: flag used to add current directory to 64 | # the include path (defaults to -I.) 65 | # DEBUG_BUILD_LDFLAGS, OPTIMIZE_BUILD_LDFLAGS, PROFILE_BUILD_LDFLAGS: flags 66 | # passed to ld/libtool (defaults to nothing) 67 | 68 | 69 | # Library and Framework projects only: 70 | # INSTALL_NAME_DIRECTIVE: This directive ensures that executables linked 71 | # against the framework will run against the correct version even if 72 | # the current version of the framework changes. You may override this 73 | # to "" as an alternative to using the DYLD_LIBRARY_PATH during your 74 | # development cycle, but be sure to restore it before installing. 75 | 76 | 77 | # Ownership and permissions of files installed by 'install' target 78 | 79 | #INSTALL_AS_USER = root 80 | # User/group ownership 81 | #INSTALL_AS_GROUP = wheel 82 | # (probably want to set both of these) 83 | #INSTALL_PERMISSIONS = 84 | # If set, 'install' chmod's executable to this 85 | 86 | 87 | # Options to strip. Note: -S strips debugging symbols (executables can be stripped 88 | # down further with -x or, if they load no bundles, with no options at all). 89 | 90 | #STRIPFLAGS = -S 91 | 92 | 93 | ######################################################################### 94 | # Put rules to extend the behavior of the standard Makefiles here. Include them in 95 | # the dependency tree via cvariables like AFTER_INSTALL in the Makefile.preamble. 96 | # 97 | # You should avoid redefining things like "install" or "app", as they are 98 | # owned by the top-level Makefile API and no context has been set up for where 99 | # derived files should go. 100 | # 101 | 102 | #LEX = /Net/tori/Users/lindberg/Unix/bin-nextstep/flex -8 103 | LEX = flex -8 104 | 105 | .l.c: 106 | @(echo "(If you don't have Flex 2.5.2 (2.5.1?) or later this won't work)"; \ 107 | cmd="$(LEX) $(ALL_LFLAGS) -P$* -o$*.c $*.l" ; echo $$cmd; $$cmd ) 108 | 109 | 110 | reallyclean: 111 | $(RM) -f cat2rtf.c 112 | -------------------------------------------------------------------------------- /ManOpen/cat2rtf.tproj/Makefile.preamble: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Makefile.preamble 3 | # Copyright 1997, Apple Computer, Inc. 4 | # 5 | # Use this makefile for configuring the standard application makefiles 6 | # associated with ProjectBuilder. It is included before the main makefile. 7 | # In Makefile.preamble you set attributes for a project, so they are available 8 | # to the project's makefiles. In contrast, you typically write additional rules or 9 | # override built-in behavior in the Makefile.postamble. 10 | # 11 | # Each directory in a project tree (main project plus subprojects) should 12 | # have its own Makefile.preamble and Makefile.postamble. 13 | ############################################################################### 14 | # 15 | # Before the main makefile is included for this project, you may set: 16 | # 17 | # MAKEFILEDIR: Directory in which to find $(MAKEFILE) 18 | # MAKEFILE: Top level mechanism Makefile (e.g., app.make, bundle.make) 19 | 20 | # Compiler/linker flags added to the defaults: The OTHER_* variables will be 21 | # inherited by all nested sub-projects, but the LOCAL_ versions of the same 22 | # variables will not. Put your -I, -D, -U, and -L flags in ProjectBuilder's 23 | # Build Attributes inspector if at all possible. To override the default flags 24 | # that get passed to ${CC} (e.g. change -O to -O2), see Makefile.postamble. The 25 | # variables below are *inputs* to the build process and distinct from the override 26 | # settings done (less often) in the Makefile.postamble. 27 | # 28 | # OTHER_CFLAGS, LOCAL_CFLAGS: additional flags to pass to the compiler 29 | # Note that $(OTHER_CFLAGS) and $(LOCAL_CFLAGS) are used for .h, ...c, .m, 30 | # .cc, .cxx, .C, and .M files. There is no need to respecify the 31 | # flags in OTHER_MFLAGS, etc. 32 | # OTHER_MFLAGS, LOCAL_MFLAGS: additional flags for .m files 33 | # OTHER_CCFLAGS, LOCAL_CCFLAGS: additional flags for .cc, .cxx, and ...C files 34 | # OTHER_MMFLAGS, LOCAL_MMFLAGS: additional flags for .mm and .M files 35 | # OTHER_PRECOMPFLAGS, LOCAL_PRECOMPFLAGS: additional flags used when 36 | # precompiling header files 37 | # OTHER_LDFLAGS, LOCAL_LDFLAGS: additional flags passed to ld and libtool 38 | # OTHER_PSWFLAGS, LOCAL_PSWFLAGS: additional flags passed to pswrap 39 | # OTHER_RPCFLAGS, LOCAL_RPCFLAGS: additional flags passed to rpcgen 40 | # OTHER_YFLAGS, LOCAL_YFLAGS: additional flags passed to yacc 41 | # OTHER_LFLAGS, LOCAL_LFLAGS: additional flags passed to lex 42 | 43 | # These variables provide hooks enabling you to add behavior at almost every 44 | # stage of the make: 45 | # 46 | # BEFORE_PREBUILD: targets to build before installing headers for a subproject 47 | # AFTER_PREBUILD: targets to build after installing headers for a subproject 48 | # BEFORE_BUILD_RECURSION: targets to make before building subprojects 49 | # BEFORE_BUILD: targets to make before a build, but after subprojects 50 | # AFTER_BUILD: targets to make after a build 51 | # 52 | # BEFORE_INSTALL: targets to build before installing the product 53 | # AFTER_INSTALL: targets to build after installing the product 54 | # BEFORE_POSTINSTALL: targets to build before postinstalling every subproject 55 | # AFTER_POSTINSTALL: targts to build after postinstalling every subproject 56 | # 57 | # BEFORE_INSTALLHDRS: targets to build before installing headers for a 58 | # subproject 59 | # AFTER_INSTALLHDRS: targets to build after installing headers for a subproject 60 | # BEFORE_INSTALLSRC: targets to build before installing source for a subproject 61 | # AFTER_INSTALLSRC: targets to build after installing source for a subproject 62 | # 63 | # BEFORE_DEPEND: targets to build before building dependencies for a 64 | # subproject 65 | # AFTER_DEPEND: targets to build after building dependencies for a 66 | # subproject 67 | # 68 | # AUTOMATIC_DEPENDENCY_INFO: if YES, then the dependency file is 69 | # updated every time the project is built. If NO, the dependency 70 | # file is only built when the depend target is invoked. 71 | 72 | # Framework-related variables: 73 | # FRAMEWORK_DLL_INSTALLDIR: On Windows platforms, this variable indicates 74 | # where to put the framework's DLL. This variable defaults to 75 | # $(INSTALLDIR)/../Executables 76 | 77 | # Library-related variables: 78 | # PUBLIC_HEADER_DIR: Determines where public exported header files 79 | # should be installed. Do not include $(DSTROOT) in this value -- 80 | # it is prefixed automatically. 81 | # PRIVATE_HEADER_DIR: Determines where private exported header files 82 | # should be installed. Do not include $(DSTROOT) in this value -- 83 | # it is prefixed automatically. 84 | # LIBRARY_STYLE: This may be either STATIC or DYNAMIC, and determines 85 | # whether the libraries produced are statically linked when they 86 | # are used or if they are dynamically loadable. <> 87 | # LIBRARY_DLL_INSTALLDIR: On Windows platforms, this variable indicates 88 | # where to put the library's DLL. This variable defaults to 89 | # $(INSTALLDIR)/../Executables 90 | # 91 | # INSTALL_AS_USER: owner of the intalled products (default root) 92 | # INSTALL_AS_GROUP: group of the installed products (default wheel) 93 | # INSTALL_PERMISSION: permissions of the installed product (default o+rX) 94 | # 95 | # OTHER_RECURSIVE_VARIABLES: The names of variables which you want to be 96 | # passed on the command line to recursive invocations of make. Note that 97 | # the values in OTHER_*FLAGS are inherited by subprojects automatically -- 98 | # you do not have to (and shouldn't) add OTHER_*FLAGS to 99 | # OTHER_RECURSIVE_VARIABLES. 100 | 101 | # Additional headers to export beyond those in the PB.project: 102 | # OTHER_PUBLIC_HEADERS 103 | # OTHER_PROJECT_HEADERS 104 | # OTHER_PRIVATE_HEADERS 105 | 106 | # Additional files for the project's product: <> 107 | # OTHER_RESOURCES: (non-localized) resources for this project 108 | # OTHER_OFILES: relocatables to be linked into this project 109 | # OTHER_LIBS: more libraries to link against 110 | # OTHER_PRODUCT_DEPENDS: other dependencies of this project 111 | # OTHER_SOURCEFILES: other source files maintained by .pre/postamble 112 | # OTHER_GARBAGE: additional files to be removed by `make clean' 113 | 114 | # Set this to YES if you don't want a final libtool call for a library/framework. 115 | # BUILD_OFILES_LIST_ONLY 116 | 117 | # To include a version string, project source must exist in a directory named 118 | # $(NAME).%d[.%d][.%d] and the following line must be uncommented. 119 | # OTHER_GENERATED_OFILES = $(VERS_OFILE) 120 | 121 | # This definition will suppress stripping of debug symbols when an executable 122 | # is installed. By default it is YES. 123 | # STRIP_ON_INSTALL = NO 124 | -------------------------------------------------------------------------------- /ManOpen/cat2rtf.tproj/PB.project: -------------------------------------------------------------------------------- 1 | { 2 | FILESTABLE = { 3 | CLASSES = (); 4 | "H_FILES" = (); 5 | "OTHER_LINKED" = ("cat2rtf.c"); 6 | "OTHER_SOURCES" = ("Makefile.preamble", Makefile, "Makefile.postamble", "cat2rtf.l"); 7 | SUBPROJECTS = (); 8 | }; 9 | GENERATEMAIN = YES; 10 | LANGUAGE = English; 11 | "LOCALIZABLE_FILES" = {}; 12 | "NEXTSTEP_JAVA_COMPILER" = "/usr/bin/javac"; 13 | "NEXTSTEP_OBJCPLUS_COMPILER" = "/usr/bin/cc"; 14 | "PDO_UNIX_JAVA_COMPILER" = "$(JDKBINDIR)/javac"; 15 | "PDO_UNIX_OBJCPLUS_COMPILER" = "$(NEXTDEV_BIN)/gcc"; 16 | PROJECTNAME = cat2rtf; 17 | PROJECTTYPE = Tool; 18 | PROJECTVERSION = "2.8"; 19 | "WINDOWS_JAVA_COMPILER" = "$(JDKBINDIR)/javac.exe"; 20 | "WINDOWS_OBJCPLUS_COMPILER" = "$(DEVDIR)/gcc"; 21 | } 22 | -------------------------------------------------------------------------------- /ManOpen/cat2rtf.tproj/cat2rtf.l: -------------------------------------------------------------------------------- 1 | /* 2 | cat2rtf.l: cat to RTF converter specification 3 | (c) 1993 by Harald Schlangmann 4 | Permission is granted to use this code. Send additions 5 | and bug reports to my address below. 6 | 7 | v1.0 Harald Schlangmann, July 20 1993 8 | schlangm@informatik.uni-muenchen.de 9 | v1.1 Bold style x^H{x^H}* implemented. 10 | 11 | v2.0 Carl Lindberg lindberg@mac.com 12 | Added blank line suppressing. 13 | v2.1 Added links. 14 | v2.2 Added RTF hyperlinks 15 | */ 16 | 17 | #include 18 | 19 | #if defined(__APPLE__) && !defined(NeXT) 20 | #define RTF_HYPERLINKS 21 | #endif 22 | 23 | #define BOLDFLAG 1 24 | #define ULINEFLAG 2 25 | 26 | int flags = 0, neededflags = 0; 27 | 28 | #define SETB neededflags |= BOLDFLAG 29 | #define UNSETB neededflags &= ~BOLDFLAG 30 | #define SETU neededflags |= ULINEFLAG 31 | #define UNSETU neededflags &= ~ULINEFLAG 32 | 33 | /* 34 | * Default settings, may be changed using options... 35 | */ 36 | 37 | static char *startBold = "\n\\b "; 38 | static char *stopBold = "\n\\b0 "; 39 | static char *startULine = "\n\\ul "; 40 | static char *stopULine = "\n\\ulnone "; 41 | static char *startHeader = "\n\\f1 "; 42 | static char *stopHeader = "\n\\f0 "; 43 | static int addLinks = 0; 44 | static int markHeaders = 0; /* Mark headers with Helvetica */ 45 | /* 46 | * 'lineCount' is here to squeeze multiple-blank lines (like 'more -s' 47 | * does with the traditional nroff output). By default, a max of 3 48 | * consecutive blank lines are allowed. A value of -1 means to not 49 | * squeeze lines at all. -CEL 50 | */ 51 | static int lineCount = 0; 52 | static int maxLineCount = 3; 53 | 54 | /* Decode a UTF8 sequence to return the unicode number */ 55 | static unsigned decodeUTF8(unsigned char *buf, yy_size_t len) 56 | { 57 | int n = buf[0] & (0x7f >> len); 58 | yy_size_t i; 59 | 60 | for (i=1; i 0 && lineCount > maxLineCount) 105 | { 106 | // Squeeze the blank line -- don't output it. 107 | return; 108 | } 109 | lineCount++; 110 | } 111 | else lineCount = 0; 112 | 113 | if( ch=='\n'||ch=='{'||ch=='}'||ch=='\\' ) 114 | fputc('\\',stdout); 115 | fputc(ch,stdout); 116 | } 117 | 118 | static void emitChars(char *string, yy_size_t len) 119 | { 120 | yy_size_t i; 121 | 122 | for (i=0; i= 4 && charblock[3] == '\010') 139 | return; 140 | 141 | /* If the characters are equal, they are printed on top of each other, so make it bold */ 142 | if( charblock[0] == charblock[2] ) { 143 | if (doBold) SETB; 144 | emitChar(charblock[0]); 145 | if (doBold) UNSETB; 146 | } 147 | /* Otherwise, just emit the second character. */ 148 | else { 149 | #ifdef DEBUGBACKSPACE 150 | fprintf(stderr, "Unknown backspace pair %c and %c\n", charblock[0], charblock[2]); 151 | #endif 152 | emitChar(charblock[2]); 153 | } 154 | } 155 | 156 | static void emitBackspacedText(char *text, yy_size_t length) 157 | { 158 | yy_size_t i=0; 159 | while (i < length) { 160 | if ((i < (length-1)) && text[i+1] == '\010') { 161 | emitBackspacedLetters(&text[i], 3, 0); 162 | i+=3; 163 | } 164 | else { 165 | emitChar(text[i]); 166 | i++; 167 | } 168 | } 169 | } 170 | 171 | /* 172 | * RTF has a "u" directive, which is followed by the unicode number 173 | * (as a signed 16-bit value). For compatibility with readers that 174 | * do not support this, a "uc" directive is supported, where the 175 | * argument specifies the number of characters after the "u" to 176 | * ignore. The idea is to put it alternate text, so that readers 177 | * which do not know "uc" or "u" will show the other (ASCII) text 178 | * instead. We just show a '?'. Put both inside a {...} section 179 | * so that the scope of the "uc" setting is limited. 180 | */ 181 | static void emitUnicode(unsigned charNum) 182 | { 183 | short rtfVal = charNum; 184 | char rtfBuf[20]; 185 | sprintf(rtfBuf, "{\\uc1\\u%hd?}", rtfVal); 186 | emitString(rtfBuf); 187 | } 188 | 189 | ALLBUTUL [^\n_\010] 190 | NEEDQUOTE [\\{}] 191 | VERBATIM [^\n_\010\x1B( \t\\{}\xC2-\xF4] 192 | UPPER [A-Z] 193 | UPPERCONT [-/A-Z0-9 \t()] 194 | UPPERBS {UPPER}\010{UPPER} 195 | UPPERBSCONT ({UPPERBS}|[ \t()]) 196 | UTF8START [\xC2-\xF4] 197 | UTF8CONT [\x80-\xBF] 198 | UTF8SEQ {UTF8START}({UTF8CONT}{1,3}) 199 | SGRSTART \x1B\[ 200 | 201 | %option 8bit 202 | %option debug 203 | %option noyywrap 204 | %option noinput 205 | %option prefix="cat2rtf" 206 | 207 | %x FIRSTLINE 208 | 209 | %% 210 | 211 | /* 212 | * Start state FIRSTLINE is used to treat the first non-empty 213 | * line special. (First line contains header). 214 | */ 215 | 216 | /* Some X.org X11 pages have a weird #pragma at the start; strip it out. */ 217 | "#pragma".*\n {} 218 | 219 | . SETB; emitChar(yytext[0]); 220 | 221 | .\n { 222 | SETB; 223 | emitChar(yytext[0]); 224 | emitChar('\n'); 225 | BEGIN(INITIAL); 226 | UNSETB; 227 | } 228 | 229 | \n UNSETB; emitChar('\n'); 230 | 231 | /* Part of the X11 thing gets put on a separate line by nroff, sigh. */ 232 | ^"vate/var/tmp/X11".*\n {} 233 | 234 | /* 235 | * Put a NeXTHelpLink next to likely looking links to other man pages if desired 236 | */ 237 | [_a-zA-Z][-a-zA-Z0-9._]*(-[ \t\n]+[-a-zA-Z0-9._]+)?[ \t\n]*"("[1-9n][a-zA-Z]?")" { 238 | 239 | if (addLinks) 240 | { 241 | char namebuf[yyleng+1]; 242 | int i; 243 | 244 | strcpy(namebuf, yytext); 245 | for (i=0; i| -S] []\n" 382 | "\tTranslate output of (g)nroff to RTF. If no\n" 383 | "\t is given, cat2rtf reads stdin.\n" 384 | "\tOption -g uses gray for bold characters,\n" 385 | "\toption -i uses italic characters for underlining.\n" 386 | "\toption -l will add NeXT help link buttons.\n" 387 | "\toption -s will allow only consecutive blank lines,\n" 388 | "\toption -S will not do any squeezing of blank lines.\n" 389 | "\tRTF output is sent to stdout.\n"); 390 | exit(1); 391 | } 392 | 393 | int main(int argc, char *argv[]) 394 | { 395 | int c; 396 | 397 | yy_flex_debug = 0; 398 | 399 | while ((c = getopt(argc, argv, "dgGiISs:lH")) != EOF) 400 | { 401 | switch( c ) { 402 | case 'd': 403 | yy_flex_debug = 1; 404 | break; 405 | case 'g': 406 | case 'G': 407 | startBold = "\n\\gray333 "; 408 | stopBold = "\n\\gray0 "; 409 | break; 410 | case 'i': 411 | case 'I': 412 | startULine = "\n\\i "; 413 | stopULine = "\n\\i0 "; 414 | break; 415 | case 's': 416 | maxLineCount = atoi(optarg); 417 | break; 418 | case 'S': 419 | maxLineCount = -1; 420 | break; 421 | case 'l': 422 | addLinks = 1; 423 | break; 424 | case 'H': 425 | markHeaders = 1; 426 | break; 427 | case '?': 428 | default: 429 | usage(); 430 | } 431 | } 432 | 433 | if( optind < argc ) 434 | yyin = fopen(argv[optind], "r"); 435 | else 436 | yyin = stdin; 437 | 438 | emitPreamble(); 439 | BEGIN(FIRSTLINE); 440 | yylex(); 441 | emitPostamble(); 442 | 443 | /* Shuts up a compiler warning */ 444 | if (0) unput('h'); 445 | 446 | return 0; 447 | } 448 | -------------------------------------------------------------------------------- /ManOpen/en.lproj/Credits.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\mac\ansicpg10000\cocoartf102 2 | {\fonttbl\f0\fswiss\fcharset77 Helvetica;} 3 | {\colortbl;\red255\green255\blue255;} 4 | \margl120\margr120\vieww11520\viewh8400\viewkind0 5 | \pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\qc 6 | 7 | \f0\fs24 \cf0 \ 8 | \ 9 | A graphical viewer for Unix manual pages\ 10 | \ 11 | Carl Lindberg lindberg@clindberg.org\ 12 | \ 13 | http://www.clindberg.org\ 14 | } -------------------------------------------------------------------------------- /ManOpen/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localizations for the type names so that RCDefaultApp shows them well */ 2 | man = "Man Page"; 3 | cat = "Preformatted Man Page"; 4 | mangz = "Compressed Man Page"; 5 | catgz = "Compressed Preformatted Man Page"; 6 | 7 | -------------------------------------------------------------------------------- /ManOpen/en.lproj/Localizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nickzman/ManOpen/12a2f59488753dd63376d7692eabda0397d60bc7/ManOpen/en.lproj/Localizable.strings -------------------------------------------------------------------------------- /ManOpen/en.lproj/ServicesMenu.strings: -------------------------------------------------------------------------------- 1 | /* 2 | ServicesMenu.strings 3 | ManOpen 4 | 5 | Created by Nick Zitzmann on 2/28/21. 6 | 7 | */ 8 | 9 | "Open File in ManOpen" = "Open File in ManOpen"; 10 | "Open man Page in ManOpen" = "Open man Page in ManOpen"; 11 | "Search man Page Index in ManOpen" = "Search man Page Index in ManOpen"; 12 | -------------------------------------------------------------------------------- /ManOpen/h.template: -------------------------------------------------------------------------------- 1 | $$ 2 | /* $FILENAME$ created by $USERNAME$ on $DATE$ */ 3 | 4 | #import 5 | 6 | @interface $FILENAMESANSEXTENSION$ : NSObject 7 | { 8 | 9 | } 10 | 11 | @end 12 | -------------------------------------------------------------------------------- /ManOpen/helpQMark.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nickzman/ManOpen/12a2f59488753dd63376d7692eabda0397d60bc7/ManOpen/helpQMark.tiff -------------------------------------------------------------------------------- /ManOpen/m.template: -------------------------------------------------------------------------------- 1 | $$ Lines starting with $$ are not inserted into newly created files 2 | $$ The following substitutions are made: 3 | $$ 4 | $$ $FILENAME$ e.g. foo.m 5 | $$ $FILENAMESANSEXTENSION$ e.g. foo 6 | $$ $DIRECTORY$ e.g. /tmp/MyNewApp 7 | $$ $PROJECTNAME$ e.g. MyNewApp 8 | $$ $SUBPROJECTNAME$ e.g. TheGoodPart.subproj 9 | $$ $USERNAME$ e.g. mwagner 10 | $$ $DATE$ e.g. Jan-1-1994 11 | $$ 12 | /* $FILENAME$ created by $USERNAME$ on $DATE$ */ 13 | 14 | #import "$FILENAMESANSEXTENSION$.h" 15 | 16 | @implementation $FILENAMESANSEXTENSION$ 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ManOpen 2 | 3 | ManOpen provides a graphical interface for viewing Unix man pages, which are the standard documentation format on macOS for command line programs, programmer libraries, and other system processes. 4 | 5 | ManOpen also supports Apropos, which performs a keyword search on the man pages installed on the computer. All Apropos searches are performed locally on your computer; ManOpen will not connect to the Internet for any reason whatsoever. 6 | 7 | ManOpen can be useful for opening a man page without using the command line, browsing and searching through man pages, or for printing them out. Customization options for power users are provided. 8 | 9 | ManOpen 3.x is based on ManOpen 2.x by Carl Lindberg, and ManOpen 1.x by Harald Schlangmann. This version has been greatly enhanced, with a new UI for macOS 11 ("Big Sur"), support for Dark Mode, native support for Apple Silicon Macs, plus several internal refactors and rewrites so the app will run on modern macOS releases. ManOpen requires macOS 10.9 ("Mavericks") or later. 10 | 11 | ManOpen is free software, licensed under the 3-clause (modified) BSD license. This program is provided "as is" and without any warranty. Source code is available on [GitHub](https://github.com/nickzman/ManOpen). 12 | 13 | ### Installation 14 | 15 | Simply copy ManOpen.app to your computer's Applications folder, or wherever you want to put it. 16 | 17 | ManOpen adds three services to your computer: 18 | 19 | - **Open File in ManOpen** will attempt to open the selected file(s) in ManOpen, or fail silently if any can't be opened. 20 | - **Open man Page in ManOpen** will take the selected text, and attempt to open a man page for each selected word. 21 | - **Search man Page Index in ManOpen** will take the selected text, and search for man pages for each selected word. 22 | 23 | As of macOS 11.0, like other macOS services, you can enable and disable these services in System Preferences, in the Keyboard preference pane, under the Shortcuts tab, under the Services table item. You may need to log out and log back in again after installing the app before the services will appear. Also, do note that some third party apps, particularly antivirus apps, are known to interfere with services. 24 | 25 | ### Searching for man Pages 26 | 27 | By default, ManOpen will look for man pages in the following locations: 28 | 29 | 1. `/usr/share/man` (where built-in macOS command line tool man pages are stored) 30 | 2. `/usr/local/man` (where third-party macOS command line tool man pages are stored) 31 | 3. Several additional paths used by commonly used first- and third-party developer tools and package managers, if they are installed: 32 | 1. `/opt/X11/share/man` (XQuartz) 33 | 2. `/sw/share/man` and `/opt/sw/share/man` (Fink) 34 | 3. `/opt/local/share/man` (MacPorts) 35 | 4. `/opt/homebrew/share/man` (Homebrew) 36 | 5. `/Applications/Xcode.app/Contents/Developer/usr/share/man` (Xcode, used for its developer tools) 37 | 6. `/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/share/man` (Xcode, used for the standard C library, and other macOS APIs) 38 | 7. `/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/share/man` (Xcode, used for its compilers) 39 | 8. `/Applications/CMake.app/Contents/man` (CMake GUI) 40 | 41 | You can add additional paths as necessary in the Preferences window, under the Advanced tab. You can add a path by clicking the + button. If the path is inside a hidden folder or an application bundle, you can navigate into them by pressing Shift-Command-G to navigate to any arbitrary path. 42 | 43 | ### Version History 44 | 45 | Version 3.0: 46 | 47 | - ManOpen now runs natively on Apple Silicon Macs, and requires macOS 10.9 or later. Support for older Macs has been discontinued. 48 | - The UI has been updated to look like a modern macOS app as of macOS 11 ("Big Sur"). 49 | - The app has been internally rewritten in places to use modern memory management, auto-layout, the hardened runtime, and new APIs. 50 | - Searching man pages is now done through a search bar instead of a find panel. 51 | - The app now supports auto-termination, so it will disappear from the Dock when all its windows have been closed, and it'll automatically quit if it hasn't been used in a while. You don't have to manually quit the app anymore. 52 | - The services have been renamed to make their function more clear. 53 | - Fixed a bug where the "Copy URL" menu item used the incorrect URL scheme. -------------------------------------------------------------------------------- /openman.tproj/h.template: -------------------------------------------------------------------------------- 1 | $$ 2 | /* $FILENAME$ created by $USERNAME$ on $DATE$ */ 3 | 4 | #import 5 | 6 | @interface $FILENAMESANSEXTENSION$ : NSObject 7 | { 8 | 9 | } 10 | 11 | @end 12 | -------------------------------------------------------------------------------- /openman.tproj/m.template: -------------------------------------------------------------------------------- 1 | $$ Lines starting with $$ are not inserted into newly created files 2 | $$ The following substitutions are made: 3 | $$ 4 | $$ $FILENAME$ e.g. foo.m 5 | $$ $FILENAMESANSEXTENSION$ e.g. foo 6 | $$ $DIRECTORY$ e.g. /tmp/MyNewApp 7 | $$ $PROJECTNAME$ e.g. MyNewApp 8 | $$ $SUBPROJECTNAME$ e.g. TheGoodPart.subproj 9 | $$ $USERNAME$ e.g. mwagner 10 | $$ $DATE$ e.g. Jan-1-1994 11 | $$ 12 | /* $FILENAME$ created by $USERNAME$ on $DATE$ */ 13 | 14 | #import "$FILENAMESANSEXTENSION$.h" 15 | 16 | @implementation $FILENAMESANSEXTENSION$ 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /openman.tproj/openman.1: -------------------------------------------------------------------------------- 1 | .TH OPENMAN 1 2 | .SH NAME 3 | openman \- display manual pages in ManOpen.app 4 | .SH SYNOPSIS 5 | .B openman 6 | [ 7 | .BI \-kb 8 | ] 9 | [ 10 | .BI \-M 11 | .I path 12 | ] 13 | [ 14 | .BI \-m 15 | .I path 16 | ] 17 | [ 18 | .BI \-f 19 | .I file 20 | ] 21 | [ 22 | .I section 23 | ] 24 | [ 25 | .I name ... 26 | ] 27 | 28 | .SH DESCRIPTION 29 | .I Openman 30 | is a command-line utility to open Unix man pages in ManOpen.app. The 31 | syntax is generally the same as the man(1) command, with an additional 32 | option to directly open files. 33 | .PP 34 | .I Openman 35 | will open a page in ManOpen.app for every title given. If a section 36 | specifier is given, 37 | .I openman 38 | looks in that section of the manual for the given 39 | .I titles. 40 | .I Section 41 | is typically an Arabic section number (``1'' for user commands, 42 | ``8'' for administrative commands, etc), but it can be a named 43 | section as well. If 44 | .I section 45 | is omitted, 46 | .I openman 47 | searches all sections of the manual, giving preference to commands 48 | over subroutines in system libraries, and printing the first section 49 | it finds, if any. 50 | .PP 51 | If the 52 | .B \-k 53 | flag is specified, then apropos(1) mode is used, with each given title 54 | used as an Apropos lookup in ManOpen.app instead of being opened as 55 | an individual page. 56 | .PP 57 | Normally, ManOpen.app is brought forward to be the active application 58 | when messaged by openman (meaning Terminal.app will no longer be the 59 | active application). If the 60 | .B \-b 61 | flag is specfied, ManOpen.app not be forcibly be made active (i.e. 62 | will stay in the background). 63 | .PP 64 | The man search path can be specified with the 65 | .B \-M 66 | or 67 | .B \-m 68 | flag. The search path is a colon (`:') separated list of directories 69 | in which manual subdirectories may be found; e.g. ``/usr/local/man:/usr/share/man''. 70 | .hw MANPATH 71 | If a search path is not supplied, the value of the environment variable 72 | `MANPATH' is used for the search path. If that isn't set, ManOpen's 73 | man path (as specified in the application's preferences) is used. 74 | .PP 75 | A file can be directly specified with the 76 | .B \-f 77 | flag. If it's an nroff source file, ManOpen's "Nroff command" (as set in 78 | the applications's preferences) will be used to process the file, 79 | otherwise ManOpen will open it directly. The argument can be a full or 80 | relative path. 81 | .PP 82 | If you use the tcsh shell, you can set openman's completion settings to 83 | be similar to man(1)'s, which causes it to complete using command names. 84 | Add the following to your ~/.cshrc or ~/.tchrc: 85 | .IP 86 | complete openman 'n/-M/d/' 'p/*/c/' 87 | .PP 88 | .SH "SEE ALSO" 89 | ManOpen.app, man(1), apropos(1), whereis(1) 90 | .SH AUTHOR 91 | Carl Lindberg 92 | .PP 93 | Please send any bug reports/suggestions. 94 | -------------------------------------------------------------------------------- /openman.tproj/openman.m: -------------------------------------------------------------------------------- 1 | 2 | #import 3 | #import 4 | #import // for getopt() 5 | #import // for isdigit() 6 | 7 | 8 | static NSString *MakeNSStringFromPath(const char *filename) 9 | { 10 | NSFileManager *manager = [NSFileManager defaultManager]; 11 | return [manager stringWithFileSystemRepresentation:filename length:strlen(filename)]; 12 | } 13 | 14 | static NSString *MakeAbsolutePath(const char *filename) 15 | { 16 | NSString *currFile = MakeNSStringFromPath(filename); 17 | 18 | if (![currFile isAbsolutePath]) 19 | { 20 | currFile = [[[NSFileManager defaultManager] currentDirectoryPath] 21 | stringByAppendingPathComponent:currFile]; 22 | } 23 | 24 | return currFile; 25 | } 26 | 27 | static void usage(const char *progname) 28 | { 29 | fprintf(stderr,"%s: [-bk] [-M path] [-f file] [section] [name ...]\n", progname); 30 | } 31 | 32 | int main (int argc, char * const *argv) 33 | { 34 | @autoreleasepool 35 | { 36 | NSString *manPath = nil; 37 | NSString *section = nil; 38 | NSMutableArray *files = [NSMutableArray array]; 39 | BOOL aproposMode = NO; 40 | BOOL forceToFront = YES; 41 | int argIndex; 42 | char c; 43 | int maxConnectTries = 8; 44 | int connectCount = 0; 45 | CFErrorRef lsError = NULL; 46 | NSArray *manOpenURLs = CFBridgingRelease(LSCopyApplicationURLsForBundleIdentifier(CFSTR("org.clindberg.ManOpen"), &lsError)); 47 | NSMutableDictionary *distributedDictionary = [[NSMutableDictionary alloc] init]; 48 | NSMutableArray *namesAndSections = [[NSMutableArray alloc] init]; 49 | CFMessagePortRef remotePort; 50 | SInt32 status; 51 | 52 | while ((c = getopt(argc,argv,"hbm:M:f:kaCcw")) != EOF) 53 | { 54 | switch(c) 55 | { 56 | case 'm': 57 | case 'M': 58 | manPath = MakeNSStringFromPath(optarg); 59 | break; 60 | case 'f': 61 | [files addObject:MakeAbsolutePath(optarg)]; 62 | break; 63 | case 'b': 64 | forceToFront = NO; 65 | break; 66 | case 'k': 67 | aproposMode = YES; 68 | break; 69 | case 'a': 70 | case 'C': 71 | case 'c': 72 | case 'w': 73 | // MacOS X man(1) options; no-op here. 74 | break; 75 | case 'h': 76 | case '?': 77 | default: 78 | usage(argv[0]); 79 | exit(0); 80 | } 81 | } 82 | 83 | if (optind >= argc && [files count] <= 0) 84 | { 85 | usage(argv[0]); 86 | exit(0); 87 | } 88 | 89 | if (optind < argc && !aproposMode) 90 | { 91 | NSString *tmp = MakeNSStringFromPath(argv[optind]); 92 | 93 | if (isdigit(argv[optind][0]) || 94 | /* These are configurable in /etc/man.conf; these are just the default strings. Hm, they are invalid as of Panther. */ 95 | [tmp isEqualToString:@"system"] || 96 | [tmp isEqualToString:@"commands"] || 97 | [tmp isEqualToString:@"syscalls"] || 98 | [tmp isEqualToString:@"libc"] || 99 | [tmp isEqualToString:@"special"] || 100 | [tmp isEqualToString:@"files"] || 101 | [tmp isEqualToString:@"games"] || 102 | [tmp isEqualToString:@"miscellaneous"] || 103 | [tmp isEqualToString:@"misc"] || 104 | [tmp isEqualToString:@"admin"] || 105 | [tmp isEqualToString:@"n"] || // Tcl pages on >= Panther 106 | [tmp isEqualToString:@"local"]) 107 | { 108 | section = tmp; 109 | optind++; 110 | } 111 | } 112 | 113 | if (optind >= argc) 114 | { 115 | if ([section length] > 0) 116 | { 117 | /* MacOS X assumes it's a man page name */ 118 | section = nil; 119 | optind--; 120 | } 121 | 122 | if (optind >= argc && [files count] <= 0) 123 | { 124 | exit(0); 125 | } 126 | } 127 | 128 | // Use Launch Services to find ManOpen. 129 | if (!manOpenURLs) 130 | { 131 | fprintf(stderr, "Cannot locate ManOpen.\n"); 132 | exit(1); 133 | } 134 | else if (![manOpenURLs.firstObject isKindOfClass:[NSURL class]]) 135 | { 136 | fprintf(stderr, "Received an unknown object type from Launch Services.\n"); 137 | exit(1); 138 | } 139 | 140 | // Use NSWorkspace to launch ManOpen. 141 | if (@available(macOS 10.15, *)) 142 | { 143 | NSWorkspaceOpenConfiguration *configuration = [[NSWorkspaceOpenConfiguration alloc] init]; 144 | dispatch_semaphore_t launchLock = dispatch_semaphore_create(0); 145 | 146 | configuration.activates = forceToFront; 147 | [[NSWorkspace sharedWorkspace] openApplicationAtURL:manOpenURLs.firstObject configuration:configuration completionHandler:^(NSRunningApplication * _Nullable app, NSError * _Nullable error) { 148 | if (!app) 149 | { 150 | fprintf(stderr, "Could not launch ManOpen\n"); 151 | exit(1); 152 | } 153 | dispatch_semaphore_signal(launchLock); 154 | }]; 155 | dispatch_semaphore_wait(launchLock, dispatch_time(DISPATCH_TIME_NOW, 10000000000)); // wait until the open event has gone through 156 | } 157 | else 158 | { 159 | NSError *launchErr = nil; 160 | 161 | #pragma clang diagnostic push 162 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 163 | if (![[NSWorkspace sharedWorkspace] launchApplicationAtURL:manOpenURLs.firstObject options:forceToFront ? 0UL : NSWorkspaceLaunchWithoutActivation configuration:@{} error:&launchErr]) 164 | { 165 | fprintf(stderr, "Could not launch ManOpen\n"); 166 | exit(1); 167 | } 168 | #pragma clang diagnostic pop 169 | } 170 | 171 | // Use a Mach port to open a connection; keep trying until one connects. 172 | do 173 | { 174 | remotePort = CFMessagePortCreateRemote(NULL, CFSTR("8D98N325TG.org.clindberg.ManOpen.MachIPC")); 175 | if (!remotePort) 176 | sleep(1); 177 | } while (remotePort == nil && connectCount++ < maxConnectTries); 178 | 179 | if (remotePort == nil) 180 | { 181 | fprintf(stderr,"Could not open connection to ManOpen\n"); 182 | exit(1); 183 | } 184 | 185 | if (files.count) 186 | distributedDictionary[@"Files"] = files; 187 | 188 | if (manPath == nil && getenv("MANPATH") != NULL) 189 | manPath = MakeNSStringFromPath(getenv("MANPATH")); 190 | 191 | for (argIndex = optind; argIndex < argc; argIndex++) 192 | { 193 | NSString *currFile = MakeNSStringFromPath(argv[argIndex]); 194 | NSDictionary *nameAndSection; 195 | 196 | if (section) 197 | nameAndSection = @{@"Name": currFile, @"Section": section}; 198 | else 199 | nameAndSection = @{@"Name": currFile}; 200 | [namesAndSections addObject:nameAndSection]; 201 | } 202 | if (aproposMode) 203 | distributedDictionary[@"Apropos"] = @YES; 204 | if (manPath) 205 | distributedDictionary[@"ManPath"] = manPath; 206 | distributedDictionary[@"NamesAndSections"] = namesAndSections; 207 | 208 | // Once we've got all our data together, send the message to the app. The message ID below is intentional, because I feel like I went mental writing this (it was my third attempt at replacing the original DO code). 209 | if (@available(macOS 10.13, *)) 210 | { 211 | status = CFMessagePortSendRequest(remotePort, 5150, (CFDataRef)[NSKeyedArchiver archivedDataWithRootObject:distributedDictionary requiringSecureCoding:YES error:NULL], 10.0, 10.0, NULL, NULL); 212 | } 213 | else 214 | { 215 | #pragma clang diagnostic push 216 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 217 | status = CFMessagePortSendRequest(remotePort, 5150, (CFDataRef)[NSKeyedArchiver archivedDataWithRootObject:distributedDictionary], 10.0, 10.0, NULL, NULL); 218 | #pragma clang diagnostic pop 219 | } 220 | } 221 | exit(0); // insure the process exit status is 0 222 | return 0; // ...and make main fit the ANSI spec. 223 | } 224 | --------------------------------------------------------------------------------