├── .gitignore ├── CHANGES.txt ├── Documentation ├── Documentation.html └── Documentation.md ├── License.txt ├── README.md ├── Shared ├── Alerts.h ├── Alerts.m ├── Common.c ├── Common.h ├── NSWorkspace+Additions.h ├── NSWorkspace+Additions.m ├── PathParser.h ├── PathParser.m ├── SnapFileManager.h ├── SnapFileManager.m └── lz4 │ ├── NSData+LZ4.h │ ├── NSData+LZ4.m │ ├── lz4.c │ ├── lz4.h │ ├── lz4hc.c │ └── lz4hc.h ├── SnapApp ├── Base.lproj │ └── MainMenu.xib ├── CmdWindow.xib ├── CmdWindowController.h ├── CmdWindowController.m ├── Defaults.plist ├── Images.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── icon_128x128.png │ │ ├── icon_128x128@2x.png │ │ ├── icon_16x16.png │ │ ├── icon_16x16@2x.png │ │ ├── icon_256x256.png │ │ ├── icon_256x256@2x.png │ │ ├── icon_32x32.png │ │ ├── icon_32x32@2x.png │ │ ├── icon_512x512.png │ │ └── icon_512x512@2x.png │ ├── Contents.json │ ├── Delete.imageset │ │ ├── Contents.json │ │ └── delete.png │ ├── Status Menu Icon.imageset │ │ ├── Contents.json │ │ ├── status-menu-silhouette@1x.png │ │ ├── status-menu-silhouette@2x.png │ │ └── status-menu-silhouette@3x.png │ └── Trash.imageset │ │ ├── Contents.json │ │ └── trash2.png ├── PrefsWindowController.h ├── PrefsWindowController.m ├── Snap-Info.plist ├── SnapAppDelegate.h ├── SnapAppDelegate.m ├── SnapDocumentIcon.icns ├── SnapItem.h ├── SnapItem.m ├── SnapWindow.xib ├── SnapWindowController.h ├── SnapWindowController.m ├── en.lproj │ └── Credits.rtf └── main.m ├── SnapTools.xcodeproj └── project.pbxproj ├── Tools ├── CLI.h ├── CLI.m ├── comment │ ├── comment.1 │ └── comment.m ├── copy │ ├── copy.1 │ └── copy.m ├── getinfo │ ├── getinfo.1 │ └── getinfo.m ├── label │ ├── label.1 │ └── label.m ├── man │ ├── .gitignore │ ├── cat2html │ │ ├── build_cat2html.sh │ │ ├── cat2html │ │ └── cat2html.l │ ├── make_man_html.sh │ └── update_website_docs.sh ├── move │ ├── move.1 │ └── move.m ├── paths │ ├── paths.1 │ └── paths.m ├── show │ ├── show.1 │ └── show.m ├── snap │ ├── snap.1 │ └── snap.m └── trash │ ├── trash.1 │ └── trash.m └── icon.png /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | DerivedData 3 | 4 | ## Various settings 5 | *.pbxuser 6 | !default.pbxuser 7 | *.mode1v3 8 | !default.mode1v3 9 | *.mode2v3 10 | !default.mode2v3 11 | *.perspectivev3 12 | !default.perspectivev3 13 | xcuserdata 14 | 15 | ## Other 16 | *.xccheckout 17 | *.moved-aside 18 | *.xcuserstate 19 | *.xcworkspace 20 | 21 | dsa_priv.pem 22 | 23 | Podfile.lock 24 | pods 25 | -------------------------------------------------------------------------------- /CHANGES.txt: -------------------------------------------------------------------------------- 1 | CHANGES.txt 2 | 3 | DONE * Create trash tool 4 | DONE * Create tool installer/uninstaller 5 | DONE * Enable contextual menu for Snap windows 6 | 7 | https://github.com/OlehKulykov/LzmaSDKObjC 8 | * Trash should use Finder to trash files/folders, so undo-redo works 9 | * Version breakdown in Open With menu, just like Finder 10 | * GUI snap app should prompt (with remembrance) when asked to open many files (>50) at once 11 | * Prompt to install command line tools on launch 12 | * Custom tool install location via Cmd-Click 13 | * Ignore hidden files vs. not ignore flags 14 | * Sorting and unique options for parse results, which should deliver nsarray not set 15 | * Print label key value dict option in label tool 16 | * Rethink the label tool, should be tags now, use existing open source code in Tag project 17 | * snap -l : Load previous snap 18 | * Ability to copy files and file paths in Snap app 19 | * Change handling of AEOpenFiles in app delegate 20 | * Prefs should have its own nib, no point in loading on every launch 21 | * Tools Table View should be drag source and have contextual menu 22 | -------------------------------------------------------------------------------- /Documentation/Documentation.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sveinbjornt/SnapTools/4e8552449247285e04df54b726c74e65143683d6/Documentation/Documentation.html -------------------------------------------------------------------------------- /Documentation/Documentation.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sveinbjornt/SnapTools/4e8552449247285e04df54b726c74e65143683d6/Documentation/Documentation.md -------------------------------------------------------------------------------- /License.txt: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2012-2017, Sveinbjorn Thordarson 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Snap Application Icon 2 | 3 | # SnapTools 4 | 5 | **SnapTools is currently a work in progress** 6 | 7 | SnapTools is a collection of tools that integrate aspects of the macOS GUI 8 | with the UNIX command line interface. 9 | 10 | 11 | 12 | SnapTools contains the following programs: 13 | 14 | * **Snap.app** - shows window with a list of files fed in from command line 15 | * **snap** - open a window in Snap.app with file(s) 16 | * **comment** - set or show file's Finder/Spotlight comment 17 | * **copy** - copy file representation(s) to the clipboard 18 | * **getinfo** - show Finder's "Get Info" window for files(s) 19 | * **label** - set or print file labels 20 | * **move** - tell Finder to move file(s) 21 | * **paths** - show all paths in input 22 | * **show** - show file(s) in the Finder 23 | * **trash** - move file(s) to the Trash 24 | 25 | Together, they make interaction between the window environment and command line 26 | a great deal easier. 27 | 28 | 32 | 33 | ## BSD License 34 | 35 | Copyright (c) 2012-2018 Sveinbjorn Thordarson <sveinbjorn@sveinbjorn.org> 36 | 37 | Redistribution and use in source and binary forms, with or without modification, 38 | are permitted provided that the following conditions are met: 39 | 40 | 1. Redistributions of source code must retain the above copyright notice, this 41 | list of conditions and the following disclaimer. 42 | 43 | 2. Redistributions in binary form must reproduce the above copyright notice, this 44 | list of conditions and the following disclaimer in the documentation and/or other 45 | materials provided with the distribution. 46 | 47 | 3. Neither the name of the copyright holder nor the names of its contributors may 48 | be used to endorse or promote products derived from this software without specific 49 | prior written permission. 50 | 51 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 52 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 53 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 54 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 55 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 56 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 57 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 58 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 59 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 60 | POSSIBILITY OF SUCH DAMAGE. 61 | -------------------------------------------------------------------------------- /Shared/Alerts.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2003-2017, Sveinbjorn Thordarson 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | list of conditions and the following disclaimer in the documentation and/or other 13 | materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its contributors may 16 | be used to endorse or promote products derived from this software without specific 17 | prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #import 32 | #import 33 | 34 | @interface Alerts : NSObject 35 | 36 | + (void)alert:(NSString *)message subText:(NSString *)subtext style:(NSAlertStyle)style; 37 | + (void)alert:(NSString *)message subText:(NSString *)subtext; 38 | + (void)alert:(NSString *)message subTextFormat:(NSString *)formatString, ...; 39 | 40 | + (void)fatalAlert:(NSString *)message subText:(NSString *)subtext; 41 | + (void)fatalAlert:(NSString *)message subTextFormat:(NSString *)formatString, ...; 42 | 43 | + (void)sheetAlert:(NSString *)message forWindow:(NSWindow *)window subTextFormat:(NSString *)formatString, ...; 44 | + (void)sheetAlert:(NSString *)message subText:(NSString *)subtext forWindow:(NSWindow *)window; 45 | + (void)sheetAlert:(NSString *)message subText:(NSString *)subtext style:(NSAlertStyle)style forWindow:(NSWindow *)window; 46 | 47 | + (BOOL)proceedAlert:(NSString *)message subText:(NSString *)subtext withActionNamed:(NSString *)action; 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /Shared/Alerts.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2003-2017, Sveinbjorn Thordarson 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | list of conditions and the following disclaimer in the documentation and/or other 13 | materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its contributors may 16 | be used to endorse or promote products derived from this software without specific 17 | prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #import "Alerts.h" 32 | 33 | @implementation Alerts 34 | 35 | #pragma mark - 36 | 37 | + (void)alert:(NSString *)message subText:(NSString *)subtext style:(NSAlertStyle)style { 38 | NSAlert *alert = [[NSAlert alloc] init]; 39 | [alert addButtonWithTitle:@"OK"]; 40 | [alert setMessageText:message]; 41 | [alert setInformativeText:subtext]; 42 | [alert setAlertStyle:style]; 43 | [[alert window] setPreventsApplicationTerminationWhenModal:YES]; 44 | [alert runModal]; 45 | } 46 | 47 | + (void)alert:(NSString *)message subTextFormat:(NSString *)formatString, ... 48 | { 49 | va_list args; 50 | va_start(args, formatString); 51 | NSString *formattedString = [[NSString alloc] initWithFormat:formatString arguments:args]; 52 | va_end(args); 53 | [self alert:message subText:formattedString]; 54 | } 55 | 56 | + (void)alert:(NSString *)message subText:(NSString *)subtext { 57 | [self alert:message subText:subtext style:NSWarningAlertStyle]; 58 | } 59 | 60 | #pragma mark - 61 | 62 | + (void)fatalAlert:(NSString *)message subText:(NSString *)subtext { 63 | [self alert:message subText:subtext style:NSCriticalAlertStyle]; 64 | [[NSApplication sharedApplication] terminate:self]; 65 | } 66 | 67 | + (void)fatalAlert:(NSString *)message subTextFormat:(NSString *)formatString, ... { 68 | va_list args; 69 | va_start(args, formatString); 70 | NSString *formattedString = [[NSString alloc] initWithFormat:formatString arguments:args]; 71 | va_end(args); 72 | [self fatalAlert:message subText:formattedString]; 73 | } 74 | 75 | #pragma mark - 76 | 77 | + (void)sheetAlert:(NSString *)message forWindow:(NSWindow *)window subTextFormat:(NSString *)formatString, ... { 78 | va_list args; 79 | va_start(args, formatString); 80 | NSString *formattedString = [[NSString alloc] initWithFormat:formatString arguments:args]; 81 | va_end(args); 82 | [self sheetAlert:message subText:formattedString forWindow:window]; 83 | } 84 | 85 | + (void)sheetAlert:(NSString *)message subText:(NSString *)subtext forWindow:(NSWindow *)window { 86 | [self sheetAlert:message subText:subtext style:NSCriticalAlertStyle forWindow:window]; 87 | } 88 | 89 | + (void)sheetAlert:(NSString *)message subText:(NSString *)subtext style:(NSAlertStyle)style forWindow:(NSWindow *)window { 90 | NSAlert *alert = [[NSAlert alloc] init]; 91 | [alert addButtonWithTitle:@"OK"]; 92 | [alert setMessageText:message]; 93 | [alert setInformativeText:subtext]; 94 | [alert setAlertStyle:style]; 95 | [alert beginSheetModalForWindow:window modalDelegate:self didEndSelector:nil contextInfo:nil]; 96 | } 97 | 98 | #pragma mark - 99 | 100 | + (BOOL)proceedAlert:(NSString *)message subText:(NSString *)subtext withActionNamed:(NSString *)actionName { 101 | NSAlert *alert = [[NSAlert alloc] init]; 102 | [alert addButtonWithTitle:actionName ? actionName : @"Proceed"]; 103 | [alert addButtonWithTitle:@"Cancel"]; 104 | [alert setMessageText:message]; 105 | [alert setInformativeText:subtext]; 106 | [alert setAlertStyle:NSWarningAlertStyle]; 107 | return ([alert runModal] == NSAlertFirstButtonReturn); 108 | } 109 | 110 | @end 111 | -------------------------------------------------------------------------------- /Shared/Common.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2017, Sveinbjorn Thordarson 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | list of conditions and the following disclaimer in the documentation and/or other 13 | materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its contributors may 16 | be used to endorse or promote products derived from this software without specific 17 | prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | -------------------------------------------------------------------------------- /Shared/Common.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2017, Sveinbjorn Thordarson 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | list of conditions and the following disclaimer in the documentation and/or other 13 | materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its contributors may 16 | be used to endorse or promote products derived from this software without specific 17 | prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #define PROGRAM_NAME @"SnapTools" 32 | #define PROGRAM_VERSION @"1.0" 33 | #define PROGRAM_CREATOR_STAMP [NSString stringWithFormat:@"%@-%@", PROGRAM_NAME, PROGRAM_VERSION] 34 | #define PROGRAM_MIN_SYS_VERSION @"10.8.0" 35 | #define PROGRAM_BUNDLE_IDENTIFIER @"org.sveinbjorn.Snap" 36 | #define PROGRAM_AUTHOR @"Sveinbjorn Thordarson" 37 | #define PROGRAM_WEBSITE @"http://sveinbjorn.org/snaptools" 38 | #define PROGRAM_GITHUB_WEBSITE @"http://github.com/sveinbjornt/SnapTools" 39 | #define PROGRAM_DONATIONS @"http://sveinbjorn.org/donations" 40 | 41 | #define PROGRAM_FILENAME_SUFFIX @".snap" 42 | 43 | #define PROGRAM_LICENSE_FILE @"License.html" 44 | #define PROGRAM_DOCUMENTATION @"Documentation.html" 45 | 46 | #define PROGRAM_MANPAGE_URL @"http://sveinbjorn.org/files/manpages/snap.man.html" 47 | #define PROGRAM_DOCUMENTATION_URL @"http://sveinbjorn.org/files/manpages/SnapDocumentation.html" 48 | 49 | #define PROGRAM_APP_SUPPORT_PATH [[NSString stringWithFormat:@"~/Library/Application Support/%@", PROGRAM_NAME] stringByExpandingTildeInPath] 50 | #define PROGRAM_HISTORY_PATH [NSString stringWithFormat:@"%@/History", PROGRAM_APP_SUPPORT_PATH] 51 | #define PROGRAM_BOOKMARKS_PATH [NSString stringWithFormat:@"%@/Bookmarks", PROGRAM_APP_SUPPORT_PATH] 52 | #define PROGRAM_REQUIRED_DIRS @[PROGRAM_APP_SUPPORT_PATH, PROGRAM_HISTORY_PATH, PROGRAM_BOOKMARKS_PATH] 53 | 54 | #define COLUMNS @[@"Icon", @"Path", @"File Size", @"Kind", @"Date Modified", @"Date Created", @"Date Accessed", @"User:Group", @"Permission", @"UTI"] 55 | 56 | #define GENERIC_EXEC_ICON_PATH @"/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/ExecutableBinaryIcon.icns" 57 | 58 | #define DEFAULT_TOOL_INSTALL_DIR @"/usr/local/bin" 59 | 60 | // abbreviations 61 | #define DEFAULTS [NSUserDefaults standardUserDefaults] 62 | #define FILEMGR [NSFileManager defaultManager] 63 | #define WORKSPACE [NSWorkspace sharedWorkspace] 64 | #define VALUES_KEYPATH(X) [NSString stringWithFormat:@"values.%@", (X)] 65 | 66 | 67 | -------------------------------------------------------------------------------- /Shared/NSWorkspace+Additions.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2003-2017, Sveinbjorn Thordarson 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | list of conditions and the following disclaimer in the documentation and/or other 13 | materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its contributors may 16 | be used to endorse or promote products derived from this software without specific 17 | prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #import 32 | 33 | @interface NSWorkspace (Additions) 34 | 35 | - (NSArray *)applicationsForFile:(NSString *)filePath; 36 | - (NSString *)defaultApplicationForFile:(NSString *)filePath; 37 | 38 | - (NSDictionary *)labelDictionary; 39 | - (BOOL)setLabelNamed:(NSString *)labelStr forFile:(NSString *)filePath; 40 | - (BOOL)setLabelNumber:(NSUInteger)label forFile:(NSString *)filePath; 41 | - (int)labelNumberForFile:(NSString *)path; 42 | - (NSString *)labelNameForFile:(NSString *)path; 43 | - (NSColor *)labelColorForFile:(NSString *)path; 44 | 45 | - (unsigned long long)nrCalculateFolderSize:(NSString *)folderPath; 46 | - (UInt64)fileOrFolderSize:(NSString *)path; 47 | - (NSString *)fileSizeAsHumanReadableString:(UInt64)size; 48 | - (NSString *)fileOrFolderSizeAsHumanReadable:(NSString *)path; 49 | - (NSString *)createTempFileNamed:(NSString *)fileName withContents:(NSString *)str encoding:(NSStringEncoding)encoding; 50 | - (NSString *)createTempFileNamed:(NSString *)fileName withContents:(NSString *)str; 51 | - (NSString *)createTempFileWithContents:(NSString *)contentStr; 52 | - (NSString *)createTempFileWithContents:(NSString *)contentStr encoding:(NSStringEncoding)textEncoding; 53 | 54 | - (BOOL)moveFileToTrash:(NSString *)path; 55 | - (BOOL)showFinderGetInfoForFile:(NSString *)path; 56 | - (BOOL)setFinderComment:(NSString *)comment forFile:(NSString *)filePath; 57 | - (NSString *)finderCommentForFile:(NSString *)path; 58 | - (void)notifyFinderFileChangedAtPath:(NSString *)path; 59 | - (NSString *)kindStringForFile:(NSString *)path; 60 | 61 | - (void)flushServices; 62 | - (BOOL)openPathInDefaultBrowser:(NSString *)path; 63 | - (BOOL)runCommandInTerminal:(NSString *)cmd; 64 | - (BOOL)isFinderRunning; 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /Shared/PathParser.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2017, Sveinbjorn Thordarson 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | list of conditions and the following disclaimer in the documentation and/or other 13 | materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its contributors may 16 | be used to endorse or promote products derived from this software without specific 17 | prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #import 32 | 33 | @interface PathParser : NSObject 34 | 35 | + (NSString *)makeAbsolutePath:(NSString *)path; 36 | + (NSString *)makeAbsolutePath:(NSString *)path relativeToDirectory:(NSString *)dirPath; 37 | 38 | + (NSMutableSet *)parse:(NSString *)str; 39 | + (NSMutableSet *)parse:(NSString *)str absoluteOnly:(BOOL)absOnly; 40 | 41 | + (BOOL)isHiddenFileAtPath:(NSString *)path; 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /Shared/PathParser.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2017, Sveinbjorn Thordarson 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | list of conditions and the following disclaimer in the documentation and/or other 13 | materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its contributors may 16 | be used to endorse or promote products derived from this software without specific 17 | prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #import "PathParser.h" 32 | 33 | @implementation PathParser 34 | 35 | + (NSString *)makeAbsolutePath:(NSString *)path { 36 | NSString *currDir = [[NSFileManager defaultManager] currentDirectoryPath]; 37 | return [self makeAbsolutePath:path relativeToDirectory:currDir]; 38 | } 39 | 40 | + (NSString *)makeAbsolutePath:(NSString *)path relativeToDirectory:(NSString *)dirPath { 41 | NSString *absPath = [path stringByExpandingTildeInPath]; 42 | if ([absPath isAbsolutePath] == NO) { 43 | absPath = [dirPath stringByAppendingPathComponent:path]; 44 | } 45 | return [absPath stringByStandardizingPath]; 46 | } 47 | 48 | + (NSString *)trim:(NSString *)str { 49 | return [str stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; 50 | } 51 | 52 | + (BOOL)isHiddenFileAtPath:(NSString *)path { 53 | return [[path lastPathComponent] hasPrefix:@"."]; 54 | } 55 | 56 | + (NSMutableSet *)parse:(NSString *)str { 57 | return [PathParser parse:str absoluteOnly:NO]; 58 | } 59 | 60 | + (NSMutableSet *)parse:(NSString *)str absoluteOnly:(BOOL)absOnly { 61 | // empty string? 62 | str = [self trim:str]; 63 | if (![str length]) { 64 | return [NSMutableSet set]; 65 | } 66 | 67 | NSMutableSet *potentialPaths = [NSMutableSet set]; 68 | 69 | // Separate each line of input, parse it for potential paths 70 | NSArray *lines = [str componentsSeparatedByString:@"\n"]; 71 | NSLog(@"Found %d lines", [lines count]); 72 | for (NSString *l in lines) { 73 | NSString *line = [self trim:l]; 74 | 75 | // is the full line a valid path? 76 | NSString *abs = absOnly ? line : [self makeAbsolutePath:line]; 77 | if ([[NSFileManager defaultManager] fileExistsAtPath:abs]) { 78 | [potentialPaths addObject:line]; 79 | continue; 80 | } 81 | 82 | // otherwise, try to find a path within the string 83 | NSUInteger len = [line length]; 84 | for (int i = 0; i < len; i++) { 85 | if ([line characterAtIndex:i] == ' ') { 86 | NSString *preStr = [line substringToIndex:i]; 87 | NSString *postStr = [line substringFromIndex:i]; 88 | [potentialPaths addObject:[self trim:preStr]]; 89 | [potentialPaths addObject:[self trim:postStr]]; 90 | } 91 | } 92 | } 93 | 94 | // Standardise paths and filter out invalid ones 95 | NSMutableSet *paths = [NSMutableSet set]; 96 | for (NSString *p in potentialPaths) { 97 | 98 | NSString *absPath = absOnly ? p : [self makeAbsolutePath:p]; 99 | 100 | if (![absPath hasPrefix:@"/"]) { 101 | continue; 102 | } 103 | 104 | if ([[NSFileManager defaultManager] fileExistsAtPath:absPath]) { 105 | [paths addObject:absPath]; 106 | } 107 | } 108 | return paths; 109 | } 110 | 111 | @end 112 | -------------------------------------------------------------------------------- /Shared/SnapFileManager.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2017, Sveinbjorn Thordarson 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | list of conditions and the following disclaimer in the documentation and/or other 13 | materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its contributors may 16 | be used to endorse or promote products derived from this software without specific 17 | prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #import 32 | 33 | @interface SnapFileManager : NSObject 34 | 35 | + (NSArray *)readHistory; 36 | + (NSArray *)readBookmarks; 37 | 38 | + (NSArray *)readSnapFileAtPath:(NSString *)path; 39 | + (BOOL)writeSnap:(NSArray *)items toPath:(NSString *)path; 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /Shared/SnapFileManager.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2017, Sveinbjorn Thordarson 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | list of conditions and the following disclaimer in the documentation and/or other 13 | materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its contributors may 16 | be used to endorse or promote products derived from this software without specific 17 | prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #import "SnapFileManager.h" 32 | #import "Common.h" 33 | #import "NSData+LZ4.h" 34 | 35 | @implementation SnapFileManager 36 | 37 | + (NSArray *)readHistory { 38 | NSArray *files = [SnapFileManager snapFilesInDirectory:PROGRAM_HISTORY_PATH]; 39 | return files; 40 | } 41 | 42 | + (NSArray *)readBookmarks { 43 | NSArray *files = [SnapFileManager snapFilesInDirectory:PROGRAM_BOOKMARKS_PATH]; 44 | return files; 45 | } 46 | 47 | + (NSArray *)snapFilesInDirectory:(NSString *)path { 48 | // TODO: Add caching 49 | NSMutableArray *arr = [NSMutableArray array]; 50 | NSDirectoryEnumerator *dirEnumerator = [FILEMGR enumeratorAtPath:path]; 51 | NSString *filename; 52 | while ((filename = [dirEnumerator nextObject]) != nil) { 53 | if ([filename hasSuffix:PROGRAM_FILENAME_SUFFIX]) { 54 | [arr addObject:filename]; 55 | } 56 | } 57 | return arr; 58 | } 59 | 60 | #pragma mark - 61 | 62 | + (NSArray *)readSnapFileAtPath:(NSString *)path { 63 | 64 | NSData *fileData = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:path] 65 | options:NSDataReadingMappedAlways 66 | error:nil]; 67 | if (!fileData) { 68 | NSLog(@"Unable to read file %@", path); 69 | return nil; 70 | } 71 | 72 | if ([SnapFileManager isCompressedData:fileData]) { 73 | fileData = [fileData decompressLZ4]; 74 | if (!fileData) { 75 | NSLog(@"Unable to decompress data in file %@", path); 76 | return nil; 77 | } 78 | } 79 | 80 | NSString *str = [[NSString alloc] initWithData:fileData encoding:NSUTF8StringEncoding]; 81 | if (!str) { 82 | NSLog(@"Unable to create string from data"); 83 | return nil; 84 | } 85 | 86 | NSArray *paths = [str componentsSeparatedByString:@"\n"]; 87 | 88 | // NSString *lz4path = [path stringByAppendingString:@".lz4"]; 89 | // [SnapFileManager writeSnap:paths toPath:path]; 90 | 91 | return paths; 92 | } 93 | 94 | + (BOOL)writeSnap:(NSArray *)items toPath:(NSString *)path { 95 | NSString *str = [items componentsJoinedByString:@"\n"]; 96 | 97 | NSData *data = [str dataUsingEncoding:NSUTF8StringEncoding]; 98 | if (!data) { 99 | NSLog(@"Error encoding string data"); 100 | return NO; 101 | } 102 | data = [data compressLZ4WithLevel:kLZ4HighCompression]; 103 | if (!data) { 104 | NSLog(@"Failed to LZ4 compress data"); 105 | return NO; 106 | } 107 | 108 | // write 4 byte header 109 | /* Magic Number */ 110 | // LZ4F_writeLE32(dstPtr, LZ4F_MAGICNUMBER); 111 | // dstPtr += 4; 112 | // headerStart = dstPtr; 113 | // 114 | // /* FLG Byte */ 115 | // *dstPtr++ = (uint8_t)(((1 & 0x03) << 6) /* Version('01') */ 116 | // + ((cctxPtr->prefs.frameInfo.blockMode & 0x01 ) << 5) /* Block mode */ 117 | // + ((cctxPtr->prefs.frameInfo.contentChecksumFlag & 0x01 ) << 2) /* Frame checksum */ 118 | // + ((cctxPtr->prefs.frameInfo.contentSize > 0) << 3)); /* Frame content size */ 119 | // /* BD Byte */ 120 | // *dstPtr++ = (BYTE)((cctxPtr->prefs.frameInfo.blockSizeID & 0x07) << 4); 121 | // /* Optional Frame content size field */ 122 | // if (cctxPtr->prefs.frameInfo.contentSize) { 123 | // LZ4F_writeLE64(dstPtr, cctxPtr->prefs.frameInfo.contentSize); 124 | // dstPtr += 8; 125 | // cctxPtr->totalInSize = 0; 126 | // } 127 | // /* CRC Byte */ 128 | // *dstPtr = LZ4F_headerChecksum(headerStart, dstPtr - headerStart); 129 | // dstPtr++; 130 | 131 | 132 | 133 | return [data writeToFile:path atomically:YES]; 134 | } 135 | 136 | + (BOOL)isCompressedData:(NSData *)data { 137 | // if ([data length] < 4) { 138 | // return NO; 139 | // } 140 | // 141 | // unsigned long headerLong = 0; 142 | // [data getBytes:&headerLong length:4]; 143 | // return (headerLong == LZ4_MAGIC_HEADER); 144 | 145 | if ([data length] == 0) { 146 | return NO; 147 | } 148 | 149 | unsigned char byte = NULL; 150 | [data getBytes:&byte length:1]; 151 | return (byte != '/'); 152 | 153 | } 154 | 155 | @end 156 | -------------------------------------------------------------------------------- /Shared/lz4/NSData+LZ4.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSData+LZ4.h 3 | // lz4-objc 4 | // 5 | // Created by Josh Chung on 2/18/13. 6 | // Copyright (c) 2013 Josh Chung. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef enum { 12 | kLZ4FastCompression = 0, 13 | kLZ4HighCompression 14 | } LZ4CompressionLevel; 15 | 16 | #define LZ4_MAGIC_HEADER 0x184D2204 17 | 18 | @interface NSData (LZ4) 19 | 20 | - (NSData *)compressLZ4; 21 | - (NSData *)compressLZ4WithLevel:(LZ4CompressionLevel)level; 22 | - (NSData *)decompressLZ4; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /Shared/lz4/NSData+LZ4.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSData+LZ4.m 3 | // lz4-objc 4 | // 5 | // Created by Josh Chung on 2/18/13. 6 | // Copyright (c) 2013 Josh Chung. All rights reserved. 7 | // 8 | 9 | #import "NSData+LZ4.h" 10 | 11 | #import "lz4.h" 12 | #import "lz4hc.h" 13 | 14 | static const int lz4_header_size = sizeof(uint32_t); 15 | 16 | @implementation NSData (LZ4) 17 | 18 | - (NSData *)compressLZ4 { 19 | return [self compressLZ4WithLevel:kLZ4FastCompression]; 20 | } 21 | 22 | - (NSData *)compressLZ4WithLevel:(LZ4CompressionLevel)level { 23 | int (*compressFunction)(const char *, char *, int); 24 | switch (level) { 25 | case kLZ4FastCompression: 26 | compressFunction = LZ4_compress; 27 | break; 28 | case kLZ4HighCompression: 29 | compressFunction = LZ4_compressHC; 30 | break; 31 | default: 32 | NSAssert(NO, @"Invalid Compression Level"); 33 | return nil; 34 | } 35 | 36 | uint32_t original_size = (uint32_t)self.length; 37 | char *out_buff = (char *)malloc(lz4_header_size + LZ4_compressBound(original_size)); 38 | memcpy(out_buff, &original_size, lz4_header_size); 39 | int out_size = compressFunction(self.bytes, out_buff + lz4_header_size, (int)self.length); 40 | if (out_size < 0) { 41 | free(out_buff); 42 | return nil; 43 | } 44 | return [[NSData alloc] initWithBytesNoCopy:out_buff length:lz4_header_size + out_size]; 45 | } 46 | 47 | - (NSData *)decompressLZ4 { 48 | uint32_t original_size; 49 | memcpy(&original_size, self.bytes, lz4_header_size); 50 | 51 | char *out_buff = (char *)malloc(original_size); 52 | if (LZ4_uncompress(self.bytes + lz4_header_size, out_buff, original_size) < 0) { 53 | free(out_buff); 54 | return nil; 55 | } 56 | return [[NSData alloc] initWithBytesNoCopy:out_buff length:original_size]; 57 | } 58 | 59 | @end 60 | -------------------------------------------------------------------------------- /Shared/lz4/lz4.h: -------------------------------------------------------------------------------- 1 | /* 2 | LZ4 - Fast LZ compression algorithm 3 | Header File 4 | Copyright (C) 2011-2012, Yann Collet. 5 | BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are 9 | met: 10 | 11 | * Redistributions of source code must retain the above copyright 12 | notice, this list of conditions and the following disclaimer. 13 | * Redistributions in binary form must reproduce the above 14 | copyright notice, this list of conditions and the following disclaimer 15 | in the documentation and/or other materials provided with the 16 | distribution. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | You can contact the author at : 31 | - LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html 32 | - LZ4 source repository : http://code.google.com/p/lz4/ 33 | */ 34 | #pragma once 35 | 36 | #if defined (__cplusplus) 37 | extern "C" { 38 | #endif 39 | 40 | 41 | //************************************** 42 | // Compiler Options 43 | //************************************** 44 | #ifdef _MSC_VER // Visual Studio 45 | # define inline __inline // Visual is not C99, but supports some kind of inline 46 | #endif 47 | 48 | 49 | //**************************** 50 | // Simple Functions 51 | //**************************** 52 | 53 | int LZ4_compress (const char* source, char* dest, int isize); 54 | int LZ4_uncompress (const char* source, char* dest, int osize); 55 | 56 | /* 57 | LZ4_compress() : 58 | Compresses 'isize' bytes from 'source' into 'dest'. 59 | Destination buffer must be already allocated, 60 | and must be sized to handle worst cases situations (input data not compressible) 61 | Worst case size evaluation is provided by function LZ4_compressBound() 62 | 63 | isize : is the input size. Max supported value is ~1.9GB 64 | return : the number of bytes written in buffer dest 65 | 66 | 67 | LZ4_uncompress() : 68 | osize : is the output size, therefore the original size 69 | return : the number of bytes read in the source buffer 70 | If the source stream is malformed, the function will stop decoding and return a negative result, indicating the byte position of the faulty instruction 71 | This function never writes outside of provided buffers, and never modifies input buffer. 72 | note : destination buffer must be already allocated. 73 | its size must be a minimum of 'osize' bytes. 74 | */ 75 | 76 | 77 | //**************************** 78 | // Advanced Functions 79 | //**************************** 80 | 81 | static inline int LZ4_compressBound(int isize) { return ((isize) + ((isize)/255) + 16); } 82 | #define LZ4_COMPRESSBOUND( isize) ((isize) + ((isize)/255) + 16) 83 | 84 | /* 85 | LZ4_compressBound() : 86 | Provides the maximum size that LZ4 may output in a "worst case" scenario (input data not compressible) 87 | primarily useful for memory allocation of output buffer. 88 | inline function is recommended for the general case, 89 | but macro is also provided when results need to be evaluated at compile time (such as table size allocation). 90 | 91 | isize : is the input size. Max supported value is ~1.9GB 92 | return : maximum output size in a "worst case" scenario 93 | note : this function is limited by "int" range (2^31-1) 94 | */ 95 | 96 | 97 | int LZ4_compress_limitedOutput (const char* source, char* dest, int isize, int maxOutputSize); 98 | 99 | /* 100 | LZ4_compress_limitedOutput() : 101 | Compress 'isize' bytes from 'source' into an output buffer 'dest' of maximum size 'maxOutputSize'. 102 | If it cannot achieve it, compression will stop, and result of the function will be zero. 103 | This function never writes outside of provided output buffer. 104 | 105 | isize : is the input size. Max supported value is ~1.9GB 106 | maxOutputSize : is the size of the destination buffer (which must be already allocated) 107 | return : the number of bytes written in buffer 'dest' 108 | or 0 if the compression fails 109 | */ 110 | 111 | 112 | int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize); 113 | 114 | /* 115 | LZ4_uncompress_unknownOutputSize() : 116 | isize : is the input size, therefore the compressed size 117 | maxOutputSize : is the size of the destination buffer (which must be already allocated) 118 | return : the number of bytes decoded in the destination buffer (necessarily <= maxOutputSize) 119 | If the source stream is malformed, the function will stop decoding and return a negative result, indicating the byte position of the faulty instruction 120 | This function never writes beyond dest + maxOutputSize, and is therefore protected against malicious data packets 121 | note : Destination buffer must be already allocated. 122 | This version is slightly slower than LZ4_uncompress() 123 | */ 124 | 125 | 126 | #if defined (__cplusplus) 127 | } 128 | #endif 129 | -------------------------------------------------------------------------------- /Shared/lz4/lz4hc.h: -------------------------------------------------------------------------------- 1 | /* 2 | LZ4 HC - High Compression Mode of LZ4 3 | Header File 4 | Copyright (C) 2011-2012, Yann Collet. 5 | BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are 9 | met: 10 | 11 | * Redistributions of source code must retain the above copyright 12 | notice, this list of conditions and the following disclaimer. 13 | * Redistributions in binary form must reproduce the above 14 | copyright notice, this list of conditions and the following disclaimer 15 | in the documentation and/or other materials provided with the 16 | distribution. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | You can contact the author at : 31 | - LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html 32 | - LZ4 source repository : http://code.google.com/p/lz4/ 33 | */ 34 | #pragma once 35 | 36 | 37 | #if defined (__cplusplus) 38 | extern "C" { 39 | #endif 40 | 41 | 42 | int LZ4_compressHC (const char* source, char* dest, int isize); 43 | 44 | /* 45 | LZ4_compressHC : 46 | return : the number of bytes in compressed buffer dest 47 | note : destination buffer must be already allocated. 48 | To avoid any problem, size it to handle worst cases situations (input data not compressible) 49 | Worst case size evaluation is provided by function LZ4_compressBound() (see "lz4.h") 50 | */ 51 | 52 | 53 | /* Note : 54 | Decompression functions are provided within regular LZ4 source code (see "lz4.h") (BSD license) 55 | */ 56 | 57 | 58 | #if defined (__cplusplus) 59 | } 60 | #endif 61 | -------------------------------------------------------------------------------- /SnapApp/CmdWindow.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 | NSUnarchiveFromData 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /SnapApp/CmdWindowController.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2017, Sveinbjorn Thordarson 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | list of conditions and the following disclaimer in the documentation and/or other 13 | materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its contributors may 16 | be used to endorse or promote products derived from this software without specific 17 | prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #import 32 | 33 | @interface CmdWindowController : NSWindowController 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /SnapApp/CmdWindowController.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2017, Sveinbjorn Thordarson 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | list of conditions and the following disclaimer in the documentation and/or other 13 | materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its contributors may 16 | be used to endorse or promote products derived from this software without specific 17 | prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #import "CmdWindowController.h" 32 | #import "Common.h" 33 | #import "SnapAppDelegate.h" 34 | #import "PathParser.h" 35 | 36 | @interface CmdWindowController () 37 | { 38 | IBOutlet NSTextField *cmdTextField; 39 | IBOutlet NSButton *runButton; 40 | IBOutlet NSTextField *itemCountTextField; 41 | IBOutlet NSProgressIndicator *progressIndicator; 42 | 43 | NSTask *task; 44 | NSTimer *checkStatusTimer; 45 | NSPipe *outputPipe; 46 | NSFileHandle *readHandle; 47 | BOOL isTaskRunning; 48 | BOOL outputEmpty; 49 | NSMutableString *outputString; 50 | } 51 | @end 52 | 53 | @implementation CmdWindowController 54 | 55 | - (void)windowDidLoad { 56 | [super windowDidLoad]; 57 | 58 | NSString *notificationName = NSTaskDidTerminateNotification; 59 | // if (execStyle == PlatypusExecStyle_Authenticated) { 60 | // notificationName = STPrivilegedTaskDidTerminateNotification; 61 | // } 62 | [[NSNotificationCenter defaultCenter] addObserver:self 63 | selector:@selector(taskFinished:) 64 | name:notificationName 65 | object:nil]; 66 | 67 | } 68 | 69 | - (IBAction)runButtonPressed:(id)sender { 70 | [self runCommand:[cmdTextField stringValue]]; 71 | } 72 | 73 | #pragma mark - Task 74 | 75 | - (void)runCommand:(NSString *)cmd { 76 | 77 | outputString = [NSMutableString string]; 78 | 79 | //initalize task 80 | task = [[NSTask alloc] init]; 81 | 82 | //apply settings for task 83 | [task setLaunchPath:@"/bin/bash"]; 84 | 85 | // construct arguments list 86 | NSMutableArray *args = [[NSMutableArray alloc] initWithCapacity:10]; 87 | [args addObject:@"-l"]; 88 | [args addObject:@"-c"]; 89 | [args addObject:cmd]; 90 | [task setArguments:args]; 91 | 92 | [task setCurrentDirectoryPath:[@"~/" stringByExpandingTildeInPath]]; 93 | 94 | // set output to file handle and start monitoring it if script provides feedback 95 | outputPipe = [NSPipe pipe]; 96 | [task setStandardOutput:outputPipe]; 97 | readHandle = [outputPipe fileHandleForReading]; 98 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(getOutputData:) name:NSFileHandleReadCompletionNotification object:readHandle]; 99 | [readHandle readInBackgroundAndNotify]; 100 | 101 | //set it off 102 | isTaskRunning = YES; 103 | NSLog(@"Executing %@", [task description]); 104 | [task launch]; 105 | 106 | [progressIndicator setUsesThreadedAnimation:YES]; 107 | [progressIndicator startAnimation:self]; 108 | } 109 | 110 | // read from the file handle and append it to the text window 111 | - (void)getOutputData:(NSNotification *)aNotification { 112 | //get the data from notification 113 | NSData *data = [aNotification userInfo][NSFileHandleNotificationDataItem]; 114 | 115 | //make sure there's actual data 116 | if ([data length]) { 117 | NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 118 | if (str) { 119 | // append 120 | [outputString appendString:str]; 121 | } 122 | // schedule to go and read more data in the background again. 123 | [[aNotification object] readInBackgroundAndNotify]; 124 | } 125 | } 126 | 127 | // OK, called when we receive notification that task is finished 128 | // Some cleaning up to do, controls need to be adjusted, etc. 129 | - (void)taskFinished:(NSNotification *)aNotification { 130 | 131 | // stop and dispose of task 132 | if (task != nil) { 133 | task = nil; 134 | } 135 | isTaskRunning = NO; 136 | 137 | // clear filehandle of any remaining data 138 | NSData *data; 139 | while ((data = [readHandle availableData]) && [data length]) { 140 | [outputString appendString:[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]]; 141 | } 142 | 143 | [progressIndicator stopAnimation:self]; 144 | 145 | NSMutableSet *paths = [PathParser parse:outputString]; 146 | 147 | SnapAppDelegate *del = (SnapAppDelegate *)[[NSApplication sharedApplication] delegate]; 148 | 149 | NSLog(@"Output: %@", outputString); 150 | NSLog(@"%@", [paths description]); 151 | 152 | if ([paths count]) { 153 | [del newSnapWindowWithPaths:[paths allObjects]]; 154 | } else { 155 | NSBeep(); 156 | } 157 | } 158 | 159 | @end 160 | -------------------------------------------------------------------------------- /SnapApp/Defaults.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sveinbjornt/SnapTools/4e8552449247285e04df54b726c74e65143683d6/SnapApp/Defaults.plist -------------------------------------------------------------------------------- /SnapApp/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "icon_16x16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "icon_16x16@2x.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "icon_32x32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "icon_32x32@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "icon_128x128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "icon_128x128@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "icon_256x256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "icon_256x256@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "icon_512x512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "icon_512x512@2x.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /SnapApp/Images.xcassets/AppIcon.appiconset/icon_128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sveinbjornt/SnapTools/4e8552449247285e04df54b726c74e65143683d6/SnapApp/Images.xcassets/AppIcon.appiconset/icon_128x128.png -------------------------------------------------------------------------------- /SnapApp/Images.xcassets/AppIcon.appiconset/icon_128x128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sveinbjornt/SnapTools/4e8552449247285e04df54b726c74e65143683d6/SnapApp/Images.xcassets/AppIcon.appiconset/icon_128x128@2x.png -------------------------------------------------------------------------------- /SnapApp/Images.xcassets/AppIcon.appiconset/icon_16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sveinbjornt/SnapTools/4e8552449247285e04df54b726c74e65143683d6/SnapApp/Images.xcassets/AppIcon.appiconset/icon_16x16.png -------------------------------------------------------------------------------- /SnapApp/Images.xcassets/AppIcon.appiconset/icon_16x16@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sveinbjornt/SnapTools/4e8552449247285e04df54b726c74e65143683d6/SnapApp/Images.xcassets/AppIcon.appiconset/icon_16x16@2x.png -------------------------------------------------------------------------------- /SnapApp/Images.xcassets/AppIcon.appiconset/icon_256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sveinbjornt/SnapTools/4e8552449247285e04df54b726c74e65143683d6/SnapApp/Images.xcassets/AppIcon.appiconset/icon_256x256.png -------------------------------------------------------------------------------- /SnapApp/Images.xcassets/AppIcon.appiconset/icon_256x256@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sveinbjornt/SnapTools/4e8552449247285e04df54b726c74e65143683d6/SnapApp/Images.xcassets/AppIcon.appiconset/icon_256x256@2x.png -------------------------------------------------------------------------------- /SnapApp/Images.xcassets/AppIcon.appiconset/icon_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sveinbjornt/SnapTools/4e8552449247285e04df54b726c74e65143683d6/SnapApp/Images.xcassets/AppIcon.appiconset/icon_32x32.png -------------------------------------------------------------------------------- /SnapApp/Images.xcassets/AppIcon.appiconset/icon_32x32@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sveinbjornt/SnapTools/4e8552449247285e04df54b726c74e65143683d6/SnapApp/Images.xcassets/AppIcon.appiconset/icon_32x32@2x.png -------------------------------------------------------------------------------- /SnapApp/Images.xcassets/AppIcon.appiconset/icon_512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sveinbjornt/SnapTools/4e8552449247285e04df54b726c74e65143683d6/SnapApp/Images.xcassets/AppIcon.appiconset/icon_512x512.png -------------------------------------------------------------------------------- /SnapApp/Images.xcassets/AppIcon.appiconset/icon_512x512@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sveinbjornt/SnapTools/4e8552449247285e04df54b726c74e65143683d6/SnapApp/Images.xcassets/AppIcon.appiconset/icon_512x512@2x.png -------------------------------------------------------------------------------- /SnapApp/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /SnapApp/Images.xcassets/Delete.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "delete.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /SnapApp/Images.xcassets/Delete.imageset/delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sveinbjornt/SnapTools/4e8552449247285e04df54b726c74e65143683d6/SnapApp/Images.xcassets/Delete.imageset/delete.png -------------------------------------------------------------------------------- /SnapApp/Images.xcassets/Status Menu Icon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "status-menu-silhouette@1x.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "status-menu-silhouette@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "status-menu-silhouette@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /SnapApp/Images.xcassets/Status Menu Icon.imageset/status-menu-silhouette@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sveinbjornt/SnapTools/4e8552449247285e04df54b726c74e65143683d6/SnapApp/Images.xcassets/Status Menu Icon.imageset/status-menu-silhouette@1x.png -------------------------------------------------------------------------------- /SnapApp/Images.xcassets/Status Menu Icon.imageset/status-menu-silhouette@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sveinbjornt/SnapTools/4e8552449247285e04df54b726c74e65143683d6/SnapApp/Images.xcassets/Status Menu Icon.imageset/status-menu-silhouette@2x.png -------------------------------------------------------------------------------- /SnapApp/Images.xcassets/Status Menu Icon.imageset/status-menu-silhouette@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sveinbjornt/SnapTools/4e8552449247285e04df54b726c74e65143683d6/SnapApp/Images.xcassets/Status Menu Icon.imageset/status-menu-silhouette@3x.png -------------------------------------------------------------------------------- /SnapApp/Images.xcassets/Trash.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "trash2.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /SnapApp/Images.xcassets/Trash.imageset/trash2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sveinbjornt/SnapTools/4e8552449247285e04df54b726c74e65143683d6/SnapApp/Images.xcassets/Trash.imageset/trash2.png -------------------------------------------------------------------------------- /SnapApp/PrefsWindowController.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2017, Sveinbjorn Thordarson 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | list of conditions and the following disclaimer in the documentation and/or other 13 | materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its contributors may 16 | be used to endorse or promote products derived from this software without specific 17 | prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #import 32 | 33 | @interface PrefsWindowController : NSWindowController 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /SnapApp/PrefsWindowController.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2017, Sveinbjorn Thordarson 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | list of conditions and the following disclaimer in the documentation and/or other 13 | materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its contributors may 16 | be used to endorse or promote products derived from this software without specific 17 | prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #import "PrefsWindowController.h" 32 | #import "NSWorkspace+Additions.h" 33 | #import "Common.h" 34 | 35 | @interface PrefsWindowController () 36 | { 37 | IBOutlet NSTableView *tableView; 38 | IBOutlet NSButton *installButton; 39 | IBOutlet NSButton *installAllButton; 40 | IBOutlet NSPopUpButton *terminalClientPopupButton; 41 | 42 | NSDictionary *statusDisplayStrings; 43 | 44 | NSArray *toolItems; 45 | } 46 | @end 47 | 48 | @implementation PrefsWindowController 49 | 50 | - (instancetype)init { 51 | if ((self = [super init])) { 52 | toolItems = @[]; 53 | 54 | NSAttributedString *instString = [[NSAttributedString alloc] initWithString:@"Installed" 55 | attributes:@{NSForegroundColorAttributeName: [NSColor grayColor]}]; 56 | NSAttributedString *notInstStr = [[NSAttributedString alloc] initWithString:@"Not Installed" 57 | attributes:@{NSForegroundColorAttributeName: [NSColor redColor]}]; 58 | NSAttributedString *oldStr = [[NSAttributedString alloc] initWithString:@"Old Version" 59 | attributes:@{NSForegroundColorAttributeName: [NSColor orangeColor]}]; 60 | statusDisplayStrings = @{ @"installed": instString, 61 | @"not_installed": notInstStr, 62 | @"old": oldStr 63 | }; 64 | [self updateToolTable]; 65 | } 66 | return self; 67 | } 68 | 69 | - (void)windowDidLoad { 70 | [super windowDidLoad]; 71 | } 72 | 73 | - (void)awakeFromNib { 74 | [self setIconsForTerminalMenu]; 75 | } 76 | 77 | - (void)setIconsForTerminalMenu { 78 | for (int i = 0; i < [terminalClientPopupButton numberOfItems]; i++) { 79 | [self setIconForTerminalMenuItemAtIndex:i]; 80 | } 81 | } 82 | 83 | - (void)setIconForTerminalMenuItemAtIndex:(NSInteger)index { 84 | NSMenuItem *menuItem = [terminalClientPopupButton itemAtIndex:index]; 85 | if ([menuItem image] != nil) { 86 | return; // already has an icon 87 | } 88 | NSSize smallIconSize = { 16, 16 }; 89 | 90 | if ([[menuItem title] isEqualToString:@"Select..."] == FALSE) { 91 | NSImage *icon; 92 | NSString *appPath = [WORKSPACE fullPathForApplication:[menuItem title]]; 93 | if (appPath) { 94 | icon = [WORKSPACE iconForFile:appPath]; 95 | } else { 96 | icon = [NSImage imageNamed:@"NSDefaultApplicationIcon"]; 97 | } 98 | [icon setSize:smallIconSize]; 99 | [menuItem setImage:icon]; 100 | } 101 | } 102 | 103 | - (IBAction)apply:(id)sender { 104 | [[NSUserDefaults standardUserDefaults] synchronize]; 105 | [self.window performClose:self]; 106 | } 107 | 108 | - (IBAction)revertToDefaults:(id)sender { 109 | // load Defaults.plist and apply 110 | NSString *defaultsPath = [[NSBundle mainBundle] pathForResource:@"Defaults" ofType:@"plist"]; 111 | NSDictionary *plist = [NSDictionary dictionaryWithContentsOfFile:defaultsPath]; 112 | for (NSString *key in plist) { 113 | [[NSUserDefaults standardUserDefaults] setObject:plist[key] forKey:key]; 114 | } 115 | [[NSUserDefaults standardUserDefaults] synchronize]; 116 | } 117 | 118 | - (IBAction)showHelp:(id)sender { 119 | [WORKSPACE openPathInDefaultBrowser:[[NSBundle mainBundle] pathForResource:PROGRAM_DOCUMENTATION ofType:nil]]; 120 | } 121 | 122 | #pragma mark - Tools 123 | 124 | - (IBAction)installSelectedItem:(id)sender { 125 | NSDictionary *selectedTool = toolItems[[tableView selectedRow]]; 126 | if ([[sender title] isEqualToString:@"Install"]) { 127 | [self installTool:selectedTool]; 128 | } else { 129 | [self uninstallTool:selectedTool]; 130 | } 131 | [self updateToolTable]; 132 | } 133 | 134 | - (BOOL)installTool:(NSDictionary *)tool { 135 | return [FILEMGR copyItemAtPath:tool[@"path"] toPath:tool[@"install_path"] error:nil]; 136 | } 137 | 138 | - (BOOL)uninstallTool:(NSDictionary *)tool { 139 | return [FILEMGR removeItemAtPath:tool[@"install_path"] error:nil]; 140 | } 141 | 142 | - (IBAction)installAll:(id)sender { 143 | if ([[sender title] isEqualToString:@"Install All"]) { 144 | [self installAllTools]; 145 | } else { 146 | [self uninstallAllTools]; 147 | } 148 | [self updateToolTable]; 149 | } 150 | 151 | - (BOOL)installAllTools { 152 | NSInteger failures = 0; 153 | for (NSDictionary *tool in toolItems) { 154 | if ([tool[@"status"] isEqualToString:@"installed"] == YES) { 155 | continue; 156 | } 157 | if (![self installTool:tool]) { 158 | failures += 1; 159 | } 160 | } 161 | return (failures == 0); 162 | } 163 | 164 | - (BOOL)uninstallAllTools { 165 | NSInteger failures = 0; 166 | for (NSDictionary *tool in toolItems) { 167 | if (![self uninstallTool:tool]) { 168 | failures += 1; 169 | } 170 | } 171 | return (failures == 0); 172 | } 173 | 174 | - (void)updateToolTable { 175 | toolItems = [self toolStatus]; 176 | 177 | BOOL allInstalled = YES; 178 | for (NSDictionary *tool in toolItems) { 179 | if (![FILEMGR fileExistsAtPath:tool[@"install_path"]]) { 180 | allInstalled = NO; 181 | break; 182 | } 183 | } 184 | 185 | NSString *title = allInstalled ? @"Uninstall All" : @"Install All"; 186 | [installAllButton setTitle:title]; 187 | 188 | [self updateSelection]; 189 | [tableView reloadData]; 190 | } 191 | 192 | - (NSArray *)toolStatus { 193 | NSString *resourceDir = [[NSBundle mainBundle] resourcePath]; 194 | NSDirectoryEnumerator *dirEnum = [[NSFileManager defaultManager] enumeratorAtPath:resourceDir]; 195 | 196 | NSMutableArray *tools = [NSMutableArray array]; 197 | NSImage *icon = [[NSImage alloc] initWithContentsOfFile:GENERIC_EXEC_ICON_PATH]; 198 | 199 | NSString *filename; 200 | while ((filename = [dirEnum nextObject])) { 201 | NSString *path = [NSString stringWithFormat:@"%@/%@", resourceDir, filename]; 202 | NSString *kind = [[NSWorkspace sharedWorkspace] kindStringForFile:path]; 203 | if ([kind isEqualToString:@"Unix executable"]) { 204 | 205 | NSString *installPath = [NSString stringWithFormat:@"%@/%@", DEFAULT_TOOL_INSTALL_DIR, filename]; 206 | NSString *status = @"not_installed"; 207 | if ([[NSFileManager defaultManager] fileExistsAtPath:installPath]) { 208 | status = @"installed"; 209 | } 210 | 211 | NSDictionary *t = @{ @"name": [path lastPathComponent], 212 | @"path": path, 213 | @"install_path": installPath, 214 | @"icon": icon, 215 | @"status": status }; 216 | [tools addObject:t]; 217 | } 218 | } 219 | 220 | return tools; 221 | } 222 | 223 | #pragma mark - Table View 224 | 225 | - (void)tableViewSelectionDidChange:(NSNotification *)aNotification { 226 | [self updateSelection]; 227 | } 228 | 229 | - (void)updateSelection { 230 | NSInteger selected = [tableView selectedRow]; 231 | if (selected == -1) { 232 | [installButton setEnabled:NO]; 233 | return; 234 | } 235 | 236 | NSDictionary *item = toolItems[selected]; 237 | NSString *title = [item[@"status"] isEqualToString:@"installed"] ? @"Uninstall" : @"Install"; 238 | [installButton setTitle:title]; 239 | [installButton setEnabled:YES]; 240 | } 241 | 242 | - (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView { 243 | return [toolItems count]; 244 | } 245 | 246 | - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex { 247 | NSString *identifier = [aTableColumn identifier]; 248 | if ([identifier isEqualToString:@"status"]) { 249 | return statusDisplayStrings[toolItems[rowIndex][identifier]]; 250 | } 251 | 252 | return toolItems[rowIndex][identifier]; 253 | } 254 | 255 | - (CGFloat)tableView:(NSTableView *)theTableView heightOfRow:(NSInteger)row { 256 | return 20; 257 | } 258 | 259 | @end 260 | -------------------------------------------------------------------------------- /SnapApp/Snap-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDocumentTypes 8 | 9 | 10 | CFBundleTypeExtensions 11 | 12 | snap 13 | 14 | CFBundleTypeIconFile 15 | SnapDocumentIcon 16 | CFBundleTypeName 17 | Snap Document 18 | CFBundleTypeRole 19 | Editor 20 | LSHandlerRank 21 | Owner 22 | LSItemContentTypes 23 | 24 | org.sveinbjorn.snap-document 25 | 26 | NSExportableTypes 27 | 28 | public.text 29 | 30 | 31 | 32 | CFBundleExecutable 33 | ${EXECUTABLE_NAME} 34 | CFBundleIdentifier 35 | $(PRODUCT_BUNDLE_IDENTIFIER) 36 | CFBundleInfoDictionaryVersion 37 | 6.0 38 | CFBundleName 39 | ${PRODUCT_NAME} 40 | CFBundlePackageType 41 | APPL 42 | CFBundleShortVersionString 43 | 1.0 44 | CFBundleSignature 45 | ???? 46 | CFBundleVersion 47 | 1 48 | LSApplicationCategoryType 49 | public.app-category.productivity 50 | LSMinimumSystemVersion 51 | ${MACOSX_DEPLOYMENT_TARGET} 52 | NSHumanReadableCopyright 53 | Copyright © 2012-2017 Sveinbjorn Thordarson. All rights reserved. 54 | NSMainNibFile 55 | MainMenu 56 | NSPrincipalClass 57 | NSApplication 58 | UTExportedTypeDeclarations 59 | 60 | 61 | UTTypeConformsTo 62 | 63 | public.data 64 | 65 | UTTypeDescription 66 | Snap Document 67 | UTTypeIconFile 68 | 69 | SnapDocumentIcon.icns 70 | 71 | UTTypeIdentifier 72 | org.sveinbjorn.snap-document 73 | UTTypeTagSpecification 74 | 75 | public.filename-extension 76 | 77 | snap 78 | 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /SnapApp/SnapAppDelegate.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2017, Sveinbjorn Thordarson 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | list of conditions and the following disclaimer in the documentation and/or other 13 | materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its contributors may 16 | be used to endorse or promote products derived from this software without specific 17 | prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #import 32 | 33 | @interface SnapAppDelegate : NSObject 34 | 35 | @property (assign, nonatomic) IBOutlet NSMenu *actionMenu; 36 | 37 | - (void)newSnapWindowWithPaths:(NSArray *)paths; 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /SnapApp/SnapAppDelegate.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2017, Sveinbjorn Thordarson 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | list of conditions and the following disclaimer in the documentation and/or other 13 | materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its contributors may 16 | be used to endorse or promote products derived from this software without specific 17 | prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #import "Common.h" 32 | #import "SnapAppDelegate.h" 33 | #import "SnapWindowController.h" 34 | #import "CmdWindowController.h" 35 | #import "NSWorkspace+Additions.h" 36 | #import "SnapFileManager.h" 37 | #import "Alerts.h" 38 | 39 | @interface SnapAppDelegate () 40 | { 41 | IBOutlet NSMenu *mainMenu; 42 | IBOutlet NSMenu *historyMenu; 43 | IBOutlet NSMenu *bookmarksMenu; 44 | 45 | NSStatusItem *statusItem; 46 | NSMutableArray *filesInOpenEvent; 47 | NSTimer *openFilesTimer; 48 | 49 | CmdWindowController *cmdController; 50 | NSMutableArray *controllers; 51 | } 52 | @end 53 | 54 | @implementation SnapAppDelegate 55 | 56 | - (void)awakeFromNib { 57 | controllers = [NSMutableArray array]; 58 | [self createStatusMenuItem]; 59 | } 60 | 61 | + (void)initialize { 62 | NSString *path = [[NSBundle mainBundle] pathForResource:@"Defaults" ofType:@"plist"]; 63 | NSDictionary *defaults = [NSDictionary dictionaryWithContentsOfFile:path]; 64 | [DEFAULTS registerDefaults:defaults]; 65 | } 66 | 67 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { 68 | // created required App Support folders 69 | BOOL isDir; 70 | NSError *err; 71 | for (NSString *dirPath in PROGRAM_REQUIRED_DIRS) { 72 | BOOL existingDir = ([FILEMGR fileExistsAtPath:dirPath isDirectory:&isDir] && isDir); 73 | if (!existingDir && ![FILEMGR createDirectoryAtPath:dirPath withIntermediateDirectories:YES attributes:nil error:&err]) { 74 | [Alerts alert:@"Error" subTextFormat:@"Could not create directory '%@', %@", 75 | dirPath, [err localizedDescription]]; 76 | } 77 | } 78 | } 79 | 80 | -(BOOL)application:(NSApplication *)sender openFile:(NSString *)filename{ 81 | return YES; 82 | } 83 | 84 | - (void)application:(NSApplication *)theApplication openFiles:(NSArray *)filenames { 85 | // This is really hackish but Apple Events are stupid. A single open event 86 | // can result in multiple invocations of this method for the files, even 87 | // though it was all part of the same user interface action. We need to collect 88 | // the files from each event and then process them after a small delay [!]. 89 | 90 | if (!filesInOpenEvent) { 91 | filesInOpenEvent = [NSMutableArray array]; 92 | } 93 | 94 | if (openFilesTimer) { 95 | [openFilesTimer invalidate]; 96 | } 97 | 98 | [filesInOpenEvent addObjectsFromArray:filenames]; 99 | openFilesTimer = [NSTimer scheduledTimerWithTimeInterval:0.25f 100 | target:self 101 | selector:@selector(processOpenFiles) 102 | userInfo:nil 103 | repeats:NO]; 104 | 105 | [theApplication replyToOpenOrPrint:NSApplicationDelegateReplySuccess]; 106 | } 107 | 108 | - (void)processOpenFiles { 109 | NSArray *filesToOpen = [filesInOpenEvent copy]; 110 | [openFilesTimer invalidate]; 111 | openFilesTimer = nil; 112 | [filesInOpenEvent removeAllObjects]; 113 | 114 | filesToOpen = [filesToOpen sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]; 115 | 116 | if ([filesToOpen[0] hasSuffix:@".snap"]) { 117 | filesToOpen = [SnapFileManager readSnapFileAtPath:filesToOpen[0]]; 118 | } 119 | 120 | if ([filesToOpen count]) { 121 | NSLog(@"Opening %lu files", (unsigned long)[filesToOpen count]); 122 | [self newSnapWindowWithPaths:filesToOpen]; 123 | } 124 | } 125 | 126 | - (void)newSnapWindowWithPaths:(NSArray *)paths { 127 | SnapWindowController *c = [[SnapWindowController alloc] initWithWindowNibName:@"SnapWindow"]; 128 | [[c window] makeKeyWindow]; 129 | [controllers addObject:c]; 130 | [c addPaths:paths]; 131 | } 132 | 133 | #pragma mark - 134 | 135 | //- (void)newSnapWithPaths:(NSArray *)paths { 136 | // 137 | //} 138 | 139 | #pragma mark - 140 | 141 | - (void)createStatusMenuItem { 142 | statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength]; 143 | [statusItem setHighlightMode:YES]; 144 | 145 | NSMenu *menu = [[NSMenu alloc] initWithTitle:@"Menu"]; 146 | for (NSMenuItem *item in [mainMenu itemArray]) { 147 | [menu addItem:[item copy]]; 148 | } 149 | 150 | [menu addItem:[NSMenuItem separatorItem]]; 151 | 152 | NSMenuItem *quitItem = [[NSMenuItem alloc] init]; 153 | [quitItem setTitle:[NSString stringWithFormat:@"Quit %@", PROGRAM_NAME]]; 154 | [quitItem setTarget:[NSApplication sharedApplication]]; 155 | [quitItem setAction:@selector(terminate:)]; 156 | [menu addItem:quitItem]; 157 | 158 | [menu setDelegate:self]; 159 | 160 | [statusItem setMenu:menu]; 161 | [statusItem setImage:[NSImage imageNamed:@"Status Menu Icon"]]; 162 | 163 | [[statusItem image] setTemplate:YES]; 164 | [statusItem setEnabled:YES]; 165 | } 166 | 167 | #pragma mark - Command Window 168 | 169 | - (IBAction)showCmdWindow:(id)sender { 170 | if (cmdController == nil) { 171 | cmdController = [[CmdWindowController alloc] initWithWindowNibName:@"CmdWindow"]; 172 | } 173 | [[cmdController window] center]; 174 | [[cmdController window] makeKeyAndOrderFront:self]; 175 | } 176 | 177 | #pragma mark - History & Bookmarks 178 | 179 | - (void)menuDidClose:(NSMenu *)menu { 180 | //[self constructMenus:self]; 181 | 182 | // if (menu == mainMenu) { 183 | [[NSApplication sharedApplication] activateIgnoringOtherApps:YES]; 184 | ProcessSerialNumber process; 185 | GetCurrentProcess(&process); 186 | SetFrontProcess(&process); 187 | 188 | return; 189 | // } 190 | 191 | NSArray *historyItems = [SnapFileManager readHistory]; 192 | 193 | // Create icon 194 | NSImage *icon = [NSImage imageNamed:@"SnapDocumentIcon"]; 195 | [icon setSize:NSMakeSize(16, 16)]; 196 | 197 | 198 | [historyMenu removeAllItems]; 199 | 200 | if ([historyItems count] > 0) { 201 | for (NSString *name in historyItems) { 202 | NSMenuItem *menuItem = [historyMenu addItemWithTitle:[name stringByDeletingPathExtension] 203 | action:@selector(historyItemSelected:) 204 | keyEquivalent:@""]; 205 | [menuItem setTarget:self]; 206 | [menuItem setEnabled:YES]; 207 | [menuItem setImage:icon]; 208 | } 209 | [historyMenu addItem:[NSMenuItem separatorItem]]; 210 | [historyMenu addItemWithTitle:@"Clear History" action:@selector(clearHistory:) keyEquivalent:@""]; 211 | 212 | } else { 213 | [historyMenu addItemWithTitle:@"Empty" action:nil keyEquivalent:@""]; 214 | } 215 | } 216 | 217 | - (IBAction)constructMenus:(id)sender { 218 | 219 | 220 | 221 | // NSArray *profiles = [self readProfilesList]; 222 | // NSArray *examples = [self readExamplesList]; 223 | // 224 | // // Create icon 225 | // NSImage *icon = [NSImage imageNamed:@"PlatypusProfile"]; 226 | // [icon setSize:NSMakeSize(16, 16)]; 227 | // 228 | // // Create Examples menu 229 | // NSMenu *examplesMenu = [[NSMenu alloc] init]; 230 | // 231 | // for (NSString *exampleName in examples) { 232 | // NSMenuItem *menuItem = [examplesMenu addItemWithTitle:[exampleName stringByDeletingPathExtension] 233 | // action:@selector(profileMenuItemSelected:) 234 | // keyEquivalent:@""]; 235 | // [menuItem setTarget:self]; 236 | // [menuItem setEnabled:YES]; 237 | // [menuItem setImage:icon]; 238 | // } 239 | // [examplesMenu addItem:[NSMenuItem separatorItem]]; 240 | // 241 | // NSMenuItem *examplesFolderItem = [examplesMenu addItemWithTitle:@"Open Examples Folder" 242 | // action:@selector(openExamplesFolder) 243 | // keyEquivalent:@""]; 244 | // [examplesFolderItem setTarget:self]; 245 | // [examplesFolderItem setEnabled:YES]; 246 | // 247 | // [examplesMenu addItem:[NSMenuItem separatorItem]]; 248 | // 249 | // NSMenuItem *createExamplesMenu = [examplesMenu addItemWithTitle:@"Build All Examples" 250 | // action:@selector(buildAllExamples) 251 | // keyEquivalent:@""]; 252 | // [createExamplesMenu setTarget:self]; 253 | // [createExamplesMenu setEnabled:YES]; 254 | // 255 | // [examplesMenuItem setSubmenu:examplesMenu]; 256 | // 257 | // //clear out all menu items 258 | // while ([profilesMenu numberOfItems] > numNonDynamicMenuitems) { 259 | // [profilesMenu removeItemAtIndex:numNonDynamicMenuitems]; 260 | // } 261 | // 262 | // if ([profiles count] > 0) { 263 | // for (NSString *profileName in profiles) { 264 | // NSMenuItem *menuItem = [profilesMenu addItemWithTitle:[profileName stringByDeletingPathExtension] 265 | // action:@selector(profileMenuItemSelected:) 266 | // keyEquivalent:@""]; 267 | // [menuItem setTarget:self]; 268 | // [menuItem setEnabled:YES]; 269 | // [menuItem setImage:icon]; 270 | // } 271 | // 272 | // [profilesMenu addItem:[NSMenuItem separatorItem]]; 273 | // 274 | // NSMenuItem *menuItem = [profilesMenu addItemWithTitle:@"Open Profiles Folder" 275 | // action:@selector(openProfilesFolder) 276 | // keyEquivalent:@""]; 277 | // [menuItem setTarget:self]; 278 | // [menuItem setEnabled:YES]; 279 | // 280 | // } else { 281 | // [profilesMenu addItemWithTitle:@"Empty" action:nil keyEquivalent:@""]; 282 | // } 283 | } 284 | 285 | - (IBAction)historyItemSelected:(id)sender { 286 | BOOL isBookmark = ([sender tag] == 10); 287 | NSString *folder = isBookmark ? PROGRAM_BOOKMARKS_PATH : PROGRAM_HISTORY_PATH; 288 | NSString *fullPath = [NSString stringWithFormat:@"%@/%@%@", folder, [sender title], PROGRAM_FILENAME_SUFFIX]; 289 | 290 | // if command key is down, we reveal in finder 291 | BOOL commandKeyDown = (([[NSApp currentEvent] modifierFlags] & NSCommandKeyMask) == NSCommandKeyMask); 292 | if (commandKeyDown) { 293 | [WORKSPACE selectFile:fullPath inFileViewerRootedAtPath:fullPath]; 294 | } else { 295 | [self application:[NSApplication sharedApplication] openFiles:@[fullPath]]; 296 | } 297 | 298 | } 299 | 300 | - (IBAction)clearHistory:(id)sender { 301 | 302 | } 303 | 304 | - (IBAction)bookmarkItemSelected:(id)sender { 305 | 306 | } 307 | 308 | - (IBAction)clearBookmarks:(id)sender { 309 | 310 | } 311 | 312 | #pragma mark - Uninstall 313 | 314 | - (IBAction)uninstallProgram:(id)sender { 315 | 316 | } 317 | 318 | #pragma mark - Help/Documentation/Website 319 | 320 | // Open Documentation.html file within app bundle 321 | - (IBAction)showHelp:(id)sender { 322 | [WORKSPACE openPathInDefaultBrowser:[[NSBundle mainBundle] pathForResource:PROGRAM_DOCUMENTATION ofType:nil]]; 323 | } 324 | 325 | // Open HTML version of snap command line tool's man page 326 | - (IBAction)showManPage:(id)sender { 327 | // [WORKSPACE openPathInDefaultBrowser:[[NSBundle mainBundle] pathForResource:PROGRAM_MANPAGE ofType:nil]]; 328 | NSBeep(); 329 | } 330 | 331 | // Open program website 332 | - (IBAction)openWebsite:(id)sender { 333 | [WORKSPACE openURL:[NSURL URLWithString:PROGRAM_WEBSITE]]; 334 | } 335 | 336 | // Open program GitHub website 337 | - (IBAction)openGitHubWebsite:(id)sender { 338 | [WORKSPACE openURL:[NSURL URLWithString:PROGRAM_GITHUB_WEBSITE]]; 339 | } 340 | 341 | // Open License HTML file 342 | - (IBAction)openLicense:(id)sender { 343 | [WORKSPACE openPathInDefaultBrowser:[[NSBundle mainBundle] pathForResource:PROGRAM_LICENSE_FILE ofType:nil]]; 344 | } 345 | 346 | // Open donations website 347 | - (IBAction)openDonations:(id)sender { 348 | [WORKSPACE openURL:[NSURL URLWithString:PROGRAM_DONATIONS]]; 349 | } 350 | 351 | @end 352 | -------------------------------------------------------------------------------- /SnapApp/SnapDocumentIcon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sveinbjornt/SnapTools/4e8552449247285e04df54b726c74e65143683d6/SnapApp/SnapDocumentIcon.icns -------------------------------------------------------------------------------- /SnapApp/SnapItem.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2017, Sveinbjorn Thordarson 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | list of conditions and the following disclaimer in the documentation and/or other 13 | materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its contributors may 16 | be used to endorse or promote products derived from this software without specific 17 | prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #import 32 | 33 | @interface SnapItem : NSObject 34 | 35 | @property (nonatomic, readonly, copy) NSString *path; 36 | @property (readonly) BOOL isDirectory; 37 | 38 | - (instancetype)initWithPath:(NSString *)path; 39 | + (instancetype)itemWithPath:(NSString *)path; 40 | 41 | - (id)attr:(NSString *)key; 42 | 43 | - (void)openInFinder; 44 | - (void)openWithApplication:(NSString *)appName; 45 | - (void)showInFinder; 46 | - (void)openContainingFolder; 47 | - (void)getInfo; 48 | - (void)quickLook; 49 | - (void)labelSelected:(id)sender; 50 | - (void)moveToTrash; 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /SnapApp/SnapItem.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2017, Sveinbjorn Thordarson 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | list of conditions and the following disclaimer in the documentation and/or other 13 | materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its contributors may 16 | be used to endorse or promote products derived from this software without specific 17 | prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #import 32 | #include 33 | #include 34 | 35 | #import "SnapItem.h" 36 | 37 | #import "Common.h" 38 | #import "NSWorkspace+Additions.h" 39 | 40 | @interface SnapItem() 41 | { 42 | BOOL statLoaded; 43 | struct stat statInfo; 44 | 45 | NSMutableDictionary *attr; 46 | } 47 | @end 48 | 49 | @implementation SnapItem 50 | 51 | #pragma mark - Initialization 52 | 53 | - (instancetype)init { 54 | if ((self = [super init])) { 55 | attr = [NSMutableDictionary dictionary]; 56 | statLoaded = FALSE; 57 | } 58 | return self; 59 | } 60 | 61 | - (instancetype)initWithPath:(NSString *)path { 62 | if ((self = [self init])) { 63 | [self setAttr:path forKey:@"Path"]; 64 | [FILEMGR fileExistsAtPath:path isDirectory:&_isDirectory]; 65 | } 66 | return self; 67 | } 68 | 69 | + (instancetype)itemWithPath:(NSString *)path { 70 | return [[SnapItem alloc] initWithPath:path]; 71 | } 72 | 73 | #pragma mark - Attributes 74 | 75 | - (void)setAttr:(id)obj forKey:(NSString *)key { 76 | attr[key] = obj; 77 | } 78 | 79 | - (BOOL)hasAttr:(NSString *)key { 80 | return (attr[key] != nil); 81 | } 82 | 83 | - (id)attr:(NSString *)key { 84 | if ([self hasAttr:key] == NO) { 85 | [self calcAttr:key]; 86 | } 87 | return attr[key]; 88 | } 89 | 90 | - (NSString *)path { 91 | return attr[@"Path"]; 92 | } 93 | 94 | - (UInt64)size { 95 | [self _stat]; 96 | return statInfo.st_size; 97 | } 98 | 99 | - (id)calcAttr:(NSString *)theAttribute { 100 | // icon 101 | if ([theAttribute isEqualToString:@"Icon"]) { 102 | NSImage *icon = [[NSWorkspace sharedWorkspace] iconForFile:[self path]]; 103 | if (icon) { 104 | [icon setSize:NSMakeSize(16,16)]; 105 | [self setAttr:icon forKey:theAttribute]; 106 | } 107 | } 108 | // size 109 | else if ([theAttribute isEqualToString:@"Size"]) { 110 | 111 | if (self.isDirectory) { 112 | [self setAttr:@"-" forKey:theAttribute]; 113 | } else { 114 | [self _stat]; 115 | 116 | NSString *sizeStr; 117 | if ([[DEFAULTS objectForKey:@"UseHumanReadableSizes"] boolValue]) { 118 | sizeStr = [WORKSPACE fileSizeAsHumanReadableString:statInfo.st_size]; 119 | } else { 120 | sizeStr = [NSString stringWithFormat:@"%lld", statInfo.st_size, nil]; 121 | } 122 | [self setAttr:sizeStr forKey:theAttribute]; 123 | } 124 | } 125 | // created 126 | else if ([theAttribute isEqualToString:@"Date Created"]) { 127 | [self _stat]; 128 | NSDate *date = [NSDate dateWithTimeIntervalSince1970:statInfo.st_birthtime]; 129 | [self setAttr:[date description] forKey:theAttribute]; 130 | } 131 | // accessed 132 | else if ([theAttribute isEqualToString:@"Date Accessed"]) { 133 | [self _stat]; 134 | NSDate *date = [NSDate dateWithTimeIntervalSince1970:statInfo.st_atime]; 135 | [self setAttr:[date description] forKey:theAttribute]; 136 | } 137 | // modified 138 | else if ([theAttribute isEqualToString:@"Date Modified"]) { 139 | [self _stat]; 140 | NSDate *date = [NSDate dateWithTimeIntervalSince1970:statInfo.st_mtime]; 141 | [self setAttr:[date description] forKey:theAttribute]; 142 | } 143 | // kind 144 | else if ([theAttribute isEqualToString:@"Kind"]) 145 | { 146 | NSString *kindStr = [[NSWorkspace sharedWorkspace] kindStringForFile:[self path]]; 147 | [self setAttr:kindStr forKey:theAttribute]; 148 | } 149 | // permissions 150 | else if ([theAttribute isEqualToString:@"Permissions"]) { 151 | [self _stat]; 152 | char buf[20]; 153 | strmode(statInfo.st_mode, (char *)&buf); 154 | [self setAttr:@((char *)&buf) forKey:theAttribute]; 155 | } 156 | // owner:group 157 | else if ([theAttribute isEqualToString:@"User:Group"]) { 158 | [self _stat]; 159 | const char *u, *g; 160 | u = user_from_uid(statInfo.st_uid, 0); 161 | g = group_from_gid(statInfo.st_gid, 0); 162 | NSString *user = @(u); 163 | NSString *group = @(g); 164 | NSString *ugStr = [NSString stringWithFormat:@"%@:%@", user, group, nil]; 165 | [self setAttr:ugStr forKey:theAttribute]; 166 | } 167 | // uniform type identifier 168 | else if ([theAttribute isEqualToString:@"UTI"]) { 169 | NSString *type = [[NSWorkspace sharedWorkspace] typeOfFile:[self path] error:nil]; 170 | NSString *uti = (type == nil) ? @"" : type; 171 | [self setAttr:uti forKey:theAttribute]; 172 | } 173 | // handler apps 174 | else if ([theAttribute isEqualToString:@"HandlerApps"]) { 175 | NSMutableArray *apps = [NSMutableArray array]; 176 | 177 | // first, get default app 178 | NSString *defaultApp = [[NSWorkspace sharedWorkspace] defaultApplicationForFile:[self path]]; 179 | if (defaultApp) 180 | [apps addObject:defaultApp]; 181 | 182 | // then, add other apps, excluding any duplicates of the default app, which is first 183 | NSArray *appsForFile = [[NSWorkspace sharedWorkspace] applicationsForFile:[self path]]; 184 | for (NSString *app in appsForFile) { 185 | if (!(defaultApp && [app isEqualToString:defaultApp])) { 186 | [apps addObject:app]; 187 | } 188 | } 189 | 190 | [self setAttr:apps forKey:theAttribute]; 191 | } 192 | 193 | return attr[theAttribute]; 194 | } 195 | 196 | #pragma mark - Actions 197 | 198 | - (void)openInFinder { 199 | [[NSWorkspace sharedWorkspace] openFile:[self path]]; 200 | } 201 | 202 | - (void)openWithApplication:(NSString *)appName { 203 | [[NSWorkspace sharedWorkspace] openFile:[self path] withApplication:appName]; 204 | } 205 | 206 | - (void)showInFinder { 207 | [[NSWorkspace sharedWorkspace] selectFile:[self path] inFileViewerRootedAtPath:[self path]]; 208 | } 209 | 210 | - (void)openContainingFolder { 211 | //[[self path] parent 212 | } 213 | 214 | - (void)getInfo { 215 | [WORKSPACE showFinderGetInfoForFile:[self path]]; 216 | } 217 | 218 | - (void)quickLook { 219 | NSTask *theTask = [[NSTask alloc] init]; 220 | [theTask setLaunchPath:@"/usr/bin/qlmanage"]; 221 | [theTask setArguments:@[@"-p", [self path]]]; 222 | [theTask launch]; 223 | } 224 | 225 | - (void)labelSelected:(id)sender { 226 | //[[NSWorkspace sharedWorkspace] setLabel:[sender tag] forFile:[self path]]; 227 | } 228 | 229 | - (void)moveToTrash { 230 | if ([WORKSPACE moveFileToTrash:[self path]] == NO) { 231 | NSBeep(); 232 | } 233 | // [self setAttr:trashPath forKey:@"Path"]; 234 | } 235 | 236 | #pragma mark - Stat 237 | 238 | - (void)_stat { 239 | if (statLoaded) { 240 | return; 241 | } 242 | stat([[self path] fileSystemRepresentation], &statInfo); 243 | statLoaded = TRUE; 244 | } 245 | 246 | #pragma mark - Debug 247 | 248 | - (NSString *)description { 249 | return [attr description]; 250 | } 251 | 252 | @end 253 | -------------------------------------------------------------------------------- /SnapApp/SnapWindow.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 | 80 | 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 | -------------------------------------------------------------------------------- /SnapApp/SnapWindowController.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2017, Sveinbjorn Thordarson 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | list of conditions and the following disclaimer in the documentation and/or other 13 | materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its contributors may 16 | be used to endorse or promote products derived from this software without specific 17 | prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #import 32 | 33 | @interface SnapWindowController : NSWindowController 34 | 35 | - (void)addPath:(NSString *)path; 36 | - (void)addPaths:(NSArray *)paths; 37 | - (void)clear; 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /SnapApp/SnapWindowController.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2017, Sveinbjorn Thordarson 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | list of conditions and the following disclaimer in the documentation and/or other 13 | materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its contributors may 16 | be used to endorse or promote products derived from this software without specific 17 | prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #import 32 | #import 33 | #import 34 | 35 | #import "SnapWindowController.h" 36 | #import "SnapAppDelegate.h" 37 | 38 | #import "Common.h" 39 | #import "SnapItem.h" 40 | #import "NSWorkspace+Additions.h" 41 | 42 | @interface SnapWindowController () 43 | { 44 | NSMutableArray *results; 45 | 46 | IBOutlet NSTableView *resultsTableView; 47 | IBOutlet NSProgressIndicator *progressIndicator; 48 | IBOutlet NSSearchField *filterTextField; 49 | IBOutlet NSTextField *numResultsTextField; 50 | IBOutlet NSMenu *tableColumnContextualMenu; 51 | IBOutlet NSView *statusBarView; 52 | IBOutlet NSScrollView *scrollView; 53 | } 54 | 55 | - (IBAction)interfaceSizeSelected:(id)sender; 56 | - (IBAction)columnChanged:(id)sender; 57 | - (IBAction)open:(id)sender; 58 | - (IBAction)showInFinder:(id)sender; 59 | - (IBAction)getInfo:(id)sender; 60 | - (IBAction)copyFile:(id)sender; 61 | - (IBAction)quickLook:(id)sender; 62 | - (IBAction)setLabel:(id)sender; 63 | - (IBAction)deleteFile:(id)sender; 64 | - (IBAction)moveToTrash:(id)sender; 65 | - (IBAction)openDirectoryInTerminal:(id)sender; 66 | - (IBAction)runInTerminal:(id)sender; 67 | - (void)updateNumFiles; 68 | 69 | @end 70 | 71 | @implementation SnapWindowController 72 | 73 | - (BOOL)window:(NSWindow *)window shouldPopUpDocumentPathMenu:(NSMenu *)menu { 74 | // prevent popup menu when window icon/title is cmd-clicked 75 | return NO; 76 | } 77 | 78 | - (BOOL)window:(NSWindow *)window shouldDragDocumentWithEvent:(NSEvent *)event from:(NSPoint)dragImageLocation withPasteboard:(NSPasteboard *)pasteboard { 79 | // prevent dragging of title bar icon 80 | return NO; 81 | } 82 | 83 | - (void)windowDidLoad { 84 | [super windowDidLoad]; 85 | 86 | results = [NSMutableArray array]; 87 | 88 | // put application icon in window title bar 89 | [[self window] setRepresentedURL:[NSURL URLWithString:PROGRAM_WEBSITE]]; 90 | NSButton *button = [[self window] standardWindowButton:NSWindowDocumentIconButton]; 91 | [button setImage:[NSApp applicationIconImage]]; 92 | 93 | // table view 94 | [resultsTableView setTarget:self]; 95 | [resultsTableView setDoubleAction:@selector(open:)]; 96 | [resultsTableView setDraggingSourceOperationMask:NSDragOperationCopy|NSDragOperationMove forLocal:NO]; 97 | [self updateColumns]; 98 | 99 | SnapAppDelegate *appDelegate = (SnapAppDelegate *)[[NSApplication sharedApplication] delegate]; 100 | NSMenu *menu = [appDelegate actionMenu]; 101 | [resultsTableView setMenu:menu]; 102 | 103 | [[[menu itemAtIndex:1] submenu] setDelegate:self]; 104 | // [menu setDelegate:self]; 105 | 106 | // status bar 107 | BOOL showStatusBar = [[NSUserDefaults standardUserDefaults] boolForKey:@"ShowStatusBar"]; 108 | [self setStatusBarHidden:!showStatusBar]; 109 | 110 | [self setObserveDefaults:YES]; 111 | } 112 | 113 | - (void)updateColumns { 114 | for (NSString *attr in COLUMNS) { 115 | if ([[DEFAULTS objectForKey:attr] boolValue] && [resultsTableView tableColumnWithIdentifier:attr] == nil) { 116 | NSTableColumn *col = [[NSTableColumn alloc] initWithIdentifier:attr]; 117 | [col setHeaderCell:[[NSTableHeaderCell alloc] initTextCell:attr]]; 118 | [resultsTableView addTableColumn:col]; 119 | } 120 | } 121 | } 122 | 123 | - (void)menuDidClose:(NSMenu *)menu { 124 | // UGH! 125 | // if (![[DEFAULTS objectforKey:@"ShowFileSize"] boolValue]) 126 | // [resultsTableView removeTableColumn:[resultsTableView tableColumnWithIdentifier:@"Size"]]; 127 | // if (![[DEFAULTS objectforKey:@"ShowKind"] boolValue]) 128 | // [resultsTableView removeTableColumn:[resultsTableView tableColumnWithIdentifier:@"Kind"]]; 129 | // if (![[DEFAULTS objectforKey:@"ShowUTI"] boolValue]) 130 | // [resultsTableView removeTableColumn:[resultsTableView tableColumnWithIdentifier:@"UTI"]]; 131 | // if (![[DEFAULTS objectforKey:@"ShowDateCreated"] boolValue]) 132 | // [resultsTableView removeTableColumn:[resultsTableView tableColumnWithIdentifier:@"CreatedDate"]]; 133 | // if (![[DEFAULTS objectforKey:@"ShowDateAccessed"] boolValue]) 134 | // [resultsTableView removeTableColumn:[resultsTableView tableColumnWithIdentifier:@"AccessedDate"]]; 135 | // if (![[DEFAULTS objectforKey:@"ShowDateModified"] boolValue]) 136 | // [resultsTableView removeTableColumn:[resultsTableView tableColumnWithIdentifier:@"ModifiedDate"]]; 137 | // if (![[DEFAULTS objectforKey:@"ShowFilePermissions"] boolValue]) 138 | // [resultsTableView removeTableColumn:[resultsTableView tableColumnWithIdentifier:@"Permissions"]]; 139 | // if (![[DEFAULTS objectforKey:@"ShowUserGroup"] boolValue]) 140 | // [resultsTableView removeTableColumn:[resultsTableView tableColumnWithIdentifier:@"User:Group"]]; 141 | // [self updateColumns]; 142 | } 143 | 144 | - (IBAction)columnChanged:(id)sender { 145 | NSString *attr = [sender title]; 146 | 147 | if (![sender state]) { 148 | [self addColumnForAttr:attr]; 149 | } else { 150 | [self removeColumnForAttr:attr]; 151 | } 152 | } 153 | 154 | - (void)addColumnForAttr:(NSString *)attr { 155 | if ([resultsTableView tableColumnWithIdentifier:attr] != nil) { 156 | return; 157 | } 158 | NSLog(@"Adding column %@", attr); 159 | NSTableColumn *col = [[NSTableColumn alloc] initWithIdentifier:attr]; 160 | [col setHeaderCell:[[NSTableHeaderCell alloc] initTextCell:attr]]; 161 | [resultsTableView addTableColumn:col]; 162 | } 163 | 164 | - (void)removeColumnForAttr:(NSString *)attr { 165 | NSLog(@"Removing column %@", attr); 166 | [resultsTableView removeTableColumn:[resultsTableView tableColumnWithIdentifier:attr]]; 167 | } 168 | 169 | - (IBAction)interfaceSizeSelected:(id)sender { 170 | 171 | } 172 | 173 | - (IBAction)filterFind:(id)sender { 174 | [DEFAULTS setBool:YES forKey:@"ShowStatusBar"]; 175 | [self.window makeFirstResponder:filterTextField]; 176 | } 177 | 178 | #pragma mark - Results 179 | 180 | - (void)addPaths:(NSArray *)paths { 181 | for (NSString *p in paths) { 182 | SnapItem *item = [SnapItem itemWithPath:p]; 183 | [results addObject:item]; 184 | } 185 | [self updateNumFiles]; 186 | [resultsTableView noteNumberOfRowsChanged]; 187 | } 188 | 189 | - (void)addPath:(NSString *)path { 190 | SnapItem *item = [SnapItem itemWithPath:path]; 191 | 192 | // // file size 193 | // [dict setObject:[NSNumber numberWithUnsignedLongLong:statInfo.st_size] forKey:@"Size"]; 194 | // 195 | // // access date 196 | // [dict setObject:[NSNumber numberWithUnsignedLongLong:statInfo.st_atime] forKey:@"AccessDate"]; 197 | // 198 | // // create date 199 | // [dict setObject:[NSNumber numberWithUnsignedLongLong:statInfo.st_birthtime] forKey:@"CreatedDate"]; 200 | // 201 | // // modified date 202 | // [dict setObject:[NSNumber numberWithUnsignedLongLong:statInfo.st_mtime] forKey:@"ModifiedDate"]; 203 | 204 | [results addObject:item]; 205 | 206 | [self updateNumFiles]; 207 | [resultsTableView noteNumberOfRowsChanged]; 208 | } 209 | 210 | - (void)updateNumFiles { 211 | NSString *label = [NSString stringWithFormat:@"%lu items", (unsigned long)[results count]]; 212 | [numResultsTextField setStringValue:label]; 213 | } 214 | 215 | - (void)clear { 216 | [results removeAllObjects]; 217 | [resultsTableView reloadData]; 218 | } 219 | 220 | - (void)menuWillOpen:(NSMenu *)menu { 221 | // we do this lazily 222 | // NSIndexSet *indexSet = [self selectedItems]; 223 | // NSString *path = [[results objectAtIndex:[indexSet firstIndex]] path]; 224 | NSArray *items = [results objectsAtIndexes:[self selectedItems]]; 225 | 226 | 227 | NSMenu *submenu = menu; 228 | [submenu removeAllItems]; 229 | 230 | if ([items count] == 0) { 231 | [submenu addItemWithTitle:@"None" action:nil keyEquivalent:@""]; 232 | [submenu addItem:[NSMenuItem separatorItem]]; 233 | [submenu addItemWithTitle:@"Select…" action:nil keyEquivalent:@""]; 234 | return; 235 | } 236 | 237 | //get default app for first file, and then separator 238 | NSArray *firstFileApps = [items[0] attr:@"HandlerApps"]; 239 | if (!firstFileApps || ![firstFileApps count]) { 240 | [submenu addItemWithTitle:@"None" action:nil keyEquivalent:@""]; 241 | [submenu addItem:[NSMenuItem separatorItem]]; 242 | [submenu addItemWithTitle:@"Select…" action:@selector(selectAppOpenWith:) keyEquivalent:@""]; 243 | return; 244 | } 245 | 246 | NSString *defaultAppPath = ([items count] > 1) ? nil : firstFileApps[0]; 247 | if (defaultAppPath) { 248 | [submenu addItem:[self menuItemForApp:defaultAppPath default:YES]]; 249 | [submenu addItem:[NSMenuItem separatorItem]]; 250 | } 251 | 252 | // build up array of items 253 | NSMutableArray *apps = [NSMutableArray array]; 254 | for (int i = 0; i < [items count]; i++) { 255 | //get all apps that open this file and append to apps 256 | NSArray *appsForFile = [items[i] attr:@"HandlerApps"]; 257 | for (NSString *app in appsForFile) { 258 | if (![apps containsObject:app] && !(defaultAppPath && [app isEqualToString:defaultAppPath])) { 259 | [apps addObject:app]; 260 | } 261 | } 262 | } 263 | 264 | if ([apps count] == 0) { 265 | return; 266 | } 267 | 268 | NSArray *sortedApps = [apps sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) { 269 | return [[obj1 lastPathComponent] caseInsensitiveCompare:[obj2 lastPathComponent]]; 270 | }]; 271 | for (NSString *appPath in sortedApps) { 272 | [submenu addItem:[self menuItemForApp:appPath default:NO]]; 273 | } 274 | 275 | [submenu addItem:[NSMenuItem separatorItem]]; 276 | [submenu addItemWithTitle:@"Select…" action:@selector(selectAppOpenWith:) keyEquivalent:@""]; 277 | } 278 | 279 | - (NSMenuItem *)menuItemForApp:(NSString *)appPath default:(BOOL)isDefault { 280 | NSString *name = [[NSFileManager defaultManager] displayNameAtPath:appPath]; 281 | NSString *title = isDefault ? [NSString stringWithFormat:@"%@ (default)", name] : name; 282 | NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:name 283 | action:@selector(openWithSender:) 284 | keyEquivalent:@""]; 285 | NSImage *icon = [[NSWorkspace sharedWorkspace] iconForFile:appPath]; 286 | [icon setSize:NSMakeSize(16,16)]; 287 | [item setImage:icon]; 288 | [item setTarget:self]; 289 | [item setToolTip:appPath]; 290 | 291 | return item; 292 | } 293 | 294 | #pragma mark - File functions 295 | 296 | - (NSIndexSet *)selectedItems { 297 | NSIndexSet *sel = [resultsTableView selectedRowIndexes]; 298 | if ([sel containsIndex:[resultsTableView clickedRow]]) { 299 | return sel; 300 | } else { 301 | return [NSIndexSet indexSetWithIndex:[resultsTableView clickedRow]]; 302 | } 303 | } 304 | 305 | - (void)performSelector:(SEL)selector onIndexes:(NSIndexSet *)indexSet { 306 | [[resultsTableView selectedRowIndexes] enumerateIndexesUsingBlock:^(NSUInteger row, BOOL *stop){ 307 | [results[row] performSelector:selector]; 308 | }]; 309 | } 310 | 311 | - (void)performSelector:(SEL)selector onIndexes:(NSIndexSet *)indexSet withObject:(id)obj { 312 | [[resultsTableView selectedRowIndexes] enumerateIndexesUsingBlock:^(NSUInteger row, BOOL *stop){ 313 | [results[row] performSelector:selector withObject:obj]; 314 | }]; 315 | } 316 | 317 | - (void)open:(id)sender { 318 | BOOL commandKeyDown = (([[NSApp currentEvent] modifierFlags] & NSCommandKeyMask) == NSCommandKeyMask); 319 | if (commandKeyDown) { 320 | [self performSelector:@selector(showInFinder) onIndexes:[self selectedItems]]; 321 | } else { 322 | [self performSelector:@selector(openInFinder) onIndexes:[self selectedItems]]; 323 | } 324 | } 325 | 326 | -(IBAction)selectAppOpenWith:(id)sender { 327 | 328 | } 329 | 330 | - (IBAction)openWithSender:(id)sender { 331 | [self performSelector:@selector(openWithApplication:) onIndexes:[self selectedItems] withObject:[sender toolTip]]; 332 | } 333 | 334 | - (IBAction)showInFinder:(id)sender { 335 | [self performSelector:@selector(showInFinder) onIndexes:[self selectedItems]]; 336 | } 337 | 338 | - (IBAction)getInfo:(id)sender { 339 | [self performSelector:@selector(getInfo) onIndexes:[self selectedItems]]; 340 | } 341 | 342 | - (IBAction)copyFile:(id)sender { 343 | NSIndexSet *rowIndexes = [self selectedItems]; 344 | NSMutableArray *filenames = [NSMutableArray arrayWithCapacity:[rowIndexes count]]; 345 | NSString *str = @""; 346 | 347 | NSInteger index = [rowIndexes firstIndex]; 348 | while (NSNotFound != index) { 349 | NSString *path = [results[index] path]; 350 | [filenames addObject:path]; 351 | str = [str stringByAppendingFormat:@"\'%@\' ", path, nil]; 352 | index = [rowIndexes indexGreaterThanIndex:index]; 353 | } 354 | 355 | [[NSPasteboard generalPasteboard] declareTypes:@[NSFilenamesPboardType, NSStringPboardType] owner:self]; 356 | [[NSPasteboard generalPasteboard] setPropertyList:filenames forType:NSFilenamesPboardType]; 357 | [[NSPasteboard generalPasteboard] setString:str forType:NSStringPboardType]; 358 | } 359 | 360 | - (IBAction)quickLook:(id)sender { 361 | [self performSelector:@selector(quickLook) onIndexes:[self selectedItems]]; 362 | } 363 | 364 | - (IBAction)setLabel:(id)sender { 365 | 366 | } 367 | 368 | - (IBAction)deleteFile:(id)sender { 369 | NSIndexSet *indexSet = [self selectedItems]; 370 | for (NSUInteger i = [results count] -1; i > 0; i--) { 371 | if ([indexSet containsIndex:i]) { 372 | [results removeObjectAtIndex:i]; 373 | } 374 | } 375 | [resultsTableView reloadData]; 376 | } 377 | 378 | - (IBAction)moveToTrash:(id)sender { 379 | [self performSelector:@selector(moveToTrash) onIndexes:[self selectedItems]]; 380 | } 381 | 382 | - (IBAction)openDirectoryInTerminal:(id)sender{ 383 | 384 | } 385 | 386 | - (IBAction)runInTerminal:(id)sender{ 387 | 388 | } 389 | 390 | #pragma mark - Key/value observation 391 | 392 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 393 | 394 | if ([keyPath hasSuffix:@"ShowStatusBar"]) { 395 | [self setStatusBarHidden:![[NSUserDefaults standardUserDefaults] boolForKey:@"ShowStatusBar"]]; 396 | } 397 | } 398 | 399 | - (void)setStatusBarHidden:(BOOL)hidden { 400 | if (hidden) { 401 | [statusBarView setHidden:YES]; 402 | NSRect windowRect = [[self.window contentView] bounds]; 403 | [scrollView setFrame:windowRect]; 404 | } else { 405 | NSRect windowRect = [[self.window contentView] bounds]; 406 | windowRect.origin.y = statusBarView.bounds.size.height; 407 | windowRect.size.height = windowRect.size.height - statusBarView.bounds.size.height; 408 | [scrollView setFrame:windowRect]; 409 | [statusBarView setHidden:NO]; 410 | 411 | } 412 | } 413 | 414 | 415 | - (void)setObserveDefaults:(BOOL)observeDefaults { 416 | NSArray *defaults = @[@"ShowStatusBar"]; 417 | 418 | for (NSString *key in defaults) { 419 | if (observeDefaults) { 420 | [[NSUserDefaultsController sharedUserDefaultsController] addObserver:self 421 | forKeyPath:VALUES_KEYPATH(key) 422 | options:NSKeyValueObservingOptionNew 423 | context:NULL]; 424 | } else { 425 | [[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:VALUES_KEYPATH(key)]; 426 | } 427 | } 428 | } 429 | 430 | #pragma mark - Table View 431 | 432 | - (void)tableViewSelectionDidChange:(NSNotification *)aNotification { 433 | } 434 | 435 | - (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView { 436 | return [results count]; 437 | } 438 | 439 | - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex { 440 | return [results[rowIndex] attr:[aTableColumn identifier]]; 441 | } 442 | 443 | - (BOOL)tableView:(NSTableView *)tv writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard *)pboard { 444 | NSMutableArray *filenames = [NSMutableArray arrayWithCapacity:[rowIndexes count]]; 445 | NSInteger index = [rowIndexes firstIndex]; 446 | 447 | while (NSNotFound != index) { 448 | [filenames addObject:[results[index] attr:@"Path"]]; 449 | index = [rowIndexes indexGreaterThanIndex:index]; 450 | } 451 | 452 | [pboard declareTypes:@[NSFilenamesPboardType] owner:self]; 453 | [pboard setPropertyList:filenames forType:NSFilenamesPboardType]; 454 | 455 | return YES; 456 | } 457 | 458 | - (CGFloat)tableView:(NSTableView *)theTableView heightOfRow:(NSInteger)row { 459 | return 20; 460 | } 461 | 462 | @end 463 | -------------------------------------------------------------------------------- /SnapApp/en.lproj/Credits.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\ansi\ansicpg1252\cocoartf1561\cocoasubrtf200 2 | {\fonttbl\f0\fswiss\fcharset0 Helvetica;} 3 | {\colortbl;\red255\green255\blue255;} 4 | {\*\expandedcolortbl;;} 5 | \paperw11900\paperh16840\vieww9600\viewh8400\viewkind0 6 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\partightenfactor0 7 | 8 | \f0\b\fs24 \cf0 Created by: 9 | \b0 \ 10 | Sveinbjorn Thordarson\ 11 | {\field{\*\fldinst{HYPERLINK "mailto:sveinbjornt@gmail.com"}}{\fldrslt }}\ 12 | {\field{\*\fldinst{HYPERLINK "http://sveinbjorn.org/snap"}}{\fldrslt http://sveinbjorn.org/snaptools}}\ 13 | \ 14 | SnapTools is free, open source software. If it\ 15 | makes your life easier, please {\field{\*\fldinst{HYPERLINK "http://sveinbjorn.org/donations"}}{\fldrslt make a donation.}}} -------------------------------------------------------------------------------- /SnapApp/main.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012-2017, Sveinbjorn Thordarson 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | list of conditions and the following disclaimer in the documentation and/or other 13 | materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its contributors may 16 | be used to endorse or promote products derived from this software without specific 17 | prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #import 32 | 33 | int main(int argc, char *argv[]) { 34 | return NSApplicationMain(argc, (const char **)argv); 35 | } 36 | -------------------------------------------------------------------------------- /Tools/CLI.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2017, Sveinbjorn Thordarson 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | list of conditions and the following disclaimer in the documentation and/or other 13 | materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its contributors may 16 | be used to endorse or promote products derived from this software without specific 17 | prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #import 32 | 33 | #import 34 | #import 35 | #import 36 | 37 | #import "Common.h" 38 | #import "PathParser.h" 39 | 40 | #define DANGEROUS_FILE_LIMIT 50 41 | 42 | NSString *ReadStandardInput(void); 43 | 44 | void NSPrintErr(NSString *format, ...); 45 | void NSPrint(NSString *format, ...); 46 | 47 | NSMutableArray *ReadRemainingArgs(int argc, const char **argv); 48 | NSMutableArray *ReadPathsFromStandardInput(BOOL absOnly); 49 | NSMutableArray *ValidPathsInArguments(NSArray *args); 50 | 51 | void PrintProgramVersion(void); 52 | 53 | 54 | -------------------------------------------------------------------------------- /Tools/CLI.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2017, Sveinbjorn Thordarson 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | list of conditions and the following disclaimer in the documentation and/or other 13 | materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its contributors may 16 | be used to endorse or promote products derived from this software without specific 17 | prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #import "CLI.h" 32 | 33 | NSString *ReadStandardInput(void) { 34 | NSData *inData = [[NSFileHandle fileHandleWithStandardInput] readDataToEndOfFile]; 35 | if (!inData || ![inData length]) { 36 | return nil; 37 | } 38 | return [[NSString alloc] initWithData:inData encoding:NSUTF8StringEncoding]; 39 | } 40 | 41 | NSMutableArray *ReadRemainingArgs(int argc, const char **argv) { 42 | NSMutableArray *remainingArgs = [NSMutableArray array]; 43 | while (optind < argc) { 44 | NSString *argStr = @(argv[optind]); 45 | optind += 1; 46 | [remainingArgs addObject:argStr]; 47 | } 48 | return remainingArgs; 49 | } 50 | 51 | #pragma mark - 52 | 53 | NSMutableArray *ReadPathsFromStandardInput(BOOL absOnly) { 54 | NSString *input = ReadStandardInput(); 55 | NSMutableSet *set = [PathParser parse:input absoluteOnly:absOnly]; 56 | return [NSMutableArray arrayWithArray:[set allObjects]]; 57 | } 58 | 59 | NSMutableArray *ValidPathsInArguments(NSArray *args) { 60 | NSMutableArray *paths = [NSMutableArray array]; 61 | 62 | for (NSString *a in args) { 63 | NSString *absPath = [PathParser makeAbsolutePath:a]; 64 | 65 | if ([[NSFileManager defaultManager] fileExistsAtPath:absPath] == NO) { 66 | NSPrintErr(@"no such file, skipping: %@", a); 67 | continue; 68 | } 69 | 70 | [paths addObject:absPath]; 71 | } 72 | return paths; 73 | } 74 | 75 | #pragma mark - 76 | 77 | void PrintProgramVersion(void) { 78 | NSPrint(@"%@ version %@ (%@)", 79 | [[NSProcessInfo processInfo] processName], 80 | PROGRAM_VERSION, 81 | PROGRAM_NAME); 82 | exit(EX_OK); 83 | } 84 | 85 | #pragma mark - 86 | 87 | // print NSString to stdout 88 | void NSPrint(NSString *format, ...) { 89 | va_list args; 90 | 91 | va_start(args, format); 92 | NSString *string = [[NSString alloc] initWithFormat:format arguments:args]; 93 | va_end(args); 94 | 95 | fprintf(stdout, "%s\n", [string UTF8String]); 96 | } 97 | 98 | // print NSString to stderr 99 | void NSPrintErr(NSString *format, ...) { 100 | va_list args; 101 | 102 | va_start(args, format); 103 | NSString *string = [[NSString alloc] initWithFormat:format arguments:args]; 104 | va_end(args); 105 | 106 | fprintf(stderr, "%s\n", [string UTF8String]); 107 | } 108 | -------------------------------------------------------------------------------- /Tools/comment/comment.1: -------------------------------------------------------------------------------- 1 | .Dd Jun 5, 2017 2 | .Dt COMMENT 1 3 | .Os Darwin 4 | .Sh NAME 5 | .Nm comment 6 | .Nd Add a Finder comment to one or more files 7 | .Sh SYNOPSIS 8 | .Nm 9 | .Op Fl vhf 10 | .Op Ar 11 | .Sh DESCRIPTION 12 | .Nm 13 | is a command line tool which sets the macOS Finder comment for 14 | one or more files. The Finder must be running when the tool is run. 15 | .Pp 16 | The following flags are supported: 17 | .Bl -tag -width -indent 18 | Ignore file limit. This flag is required to run the tool on 19 | a large number of files. 20 | .It Fl v, -version 21 | Print program version and exit 22 | .It Fl h, -help 23 | Print help and exit 24 | .El 25 | .Sh SEE ALSO 26 | .Xr copy 1 , 27 | .Xr getinfo 1 , 28 | .Xr open 1 , 29 | .Xr paths 1 , 30 | .Xr show 1 , 31 | .Xr snap 1 , 32 | .Xr trash 1 33 | .Sh AUTHORS 34 | .An Sveinbjorn Thordarson 35 | -------------------------------------------------------------------------------- /Tools/comment/comment.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2017, Sveinbjorn Thordarson 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | list of conditions and the following disclaimer in the documentation and/or other 13 | materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its contributors may 16 | be used to endorse or promote products derived from this software without specific 17 | prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #import "CLI.h" 32 | #import "NSWorkspace+Additions.h" 33 | 34 | static void PrintHelp(void); 35 | 36 | static const char optstring[] = "vhfp"; 37 | 38 | static struct option long_options[] = { 39 | {"version", no_argument, 0, 'v'}, 40 | {"help", no_argument, 0, 'h'}, 41 | {"force", no_argument, 0, 'f'}, 42 | {"print", no_argument, 0, 'p'}, 43 | {0, 0, 0, 0} 44 | }; 45 | 46 | int main(int argc, const char * argv[]) { @autoreleasepool { 47 | int optch; 48 | int long_index = 0; 49 | 50 | BOOL force = NO; 51 | BOOL printOnly = NO; 52 | 53 | // parse getopt 54 | while ((optch = getopt_long(argc, (char *const *)argv, optstring, long_options, &long_index)) != -1) { 55 | switch (optch) { 56 | 57 | // print version 58 | case 'v': 59 | PrintProgramVersion(); 60 | break; 61 | 62 | // ignore file limit 63 | case 'f': 64 | force = YES; 65 | break; 66 | 67 | case 'p': 68 | printOnly = YES; 69 | break; 70 | 71 | // print help with list of options 72 | case 'h': 73 | default: 74 | { 75 | PrintHelp(); 76 | exit(EX_OK); 77 | } 78 | break; 79 | } 80 | } 81 | 82 | NSMutableArray *args = ReadRemainingArgs(argc, argv); 83 | NSString *commentArg; 84 | 85 | if (!printOnly) { 86 | if (![args count]) { 87 | PrintHelp(); 88 | exit(EX_USAGE); 89 | } 90 | 91 | // First arg is label identifier 92 | commentArg = args[0]; 93 | [args removeObjectAtIndex:0]; 94 | } 95 | 96 | BOOL readStdin = ([args count] == 0); 97 | 98 | NSMutableArray *filePaths = [NSMutableArray array]; 99 | 100 | if (readStdin) { 101 | filePaths = ReadPathsFromStandardInput(NO); 102 | } else { 103 | filePaths = ValidPathsInArguments(args); 104 | if ([filePaths count] < 1) { 105 | PrintHelp(); 106 | exit(EX_USAGE); 107 | } 108 | } 109 | 110 | // Make sure Finder is running 111 | if ([[NSWorkspace sharedWorkspace] isFinderRunning] == NO) { 112 | NSPrintErr(@"Unable to set comment because Finder is not running."); 113 | exit(EX_UNAVAILABLE); 114 | } 115 | 116 | // Check if number of files exceeds limit 117 | if (([filePaths count] > DANGEROUS_FILE_LIMIT) && !force && !printOnly) { 118 | NSPrintErr(@"File count exceeds safety limit of %d. Use -f flag to override.", 119 | DANGEROUS_FILE_LIMIT); 120 | exit(EX_USAGE); 121 | } 122 | 123 | // Set comment for files 124 | unsigned long count = 0; 125 | for (NSString *path in filePaths) { 126 | 127 | if (printOnly) { 128 | NSString *comment = [[NSWorkspace sharedWorkspace] finderCommentForFile:path]; 129 | if (!comment) { 130 | comment = @""; 131 | } 132 | NSPrint(@"%@:\n%@\n", path, comment); 133 | } 134 | else { 135 | 136 | BOOL succ = [[NSWorkspace sharedWorkspace] setFinderComment:commentArg forFile:path]; 137 | if (!succ) { 138 | NSPrintErr(@"Failed to set comment of '%@'", commentArg); 139 | } else { 140 | count += 1; 141 | } 142 | } 143 | } 144 | 145 | if (count && !printOnly) { 146 | NSPrint(@"Comment set on %d file%@", count, count > 1 ? @"s" : @""); 147 | } 148 | 149 | return EX_OK; 150 | }} 151 | 152 | #pragma mark - 153 | 154 | static void PrintHelp(void) { 155 | NSPrint(@"usage: %@ [comment|-p] [file1 file2 ...]", [[NSProcessInfo processInfo] processName]); 156 | } 157 | -------------------------------------------------------------------------------- /Tools/copy/copy.1: -------------------------------------------------------------------------------- 1 | .Dd May 14, 2017 2 | .Dt COPY 1 3 | .Os Darwin 4 | .Sh NAME 5 | .Nm copy 6 | .Nd Copy one or more files to the Mac OS X Pasteboard 7 | .Sh SYNOPSIS 8 | .Nm 9 | .Op Fl vh 10 | .Op Ar 11 | .Sh DESCRIPTION 12 | .Nm 13 | is a command line tool which copies any file arguments to the Mac OS X 14 | Pasteboard. Files copied in this way can subsequently be pasted or moved 15 | using the Finder. 16 | .Pp 17 | The following flags are supported: 18 | .Bl -tag -width -indent 19 | .It Fl f, -force 20 | Ignore file limit. This flag is required to run the tool on 21 | a large number of files. 22 | .It Fl v, -version 23 | Print program version and exit 24 | .It Fl h, -help 25 | Print help and exit 26 | .El 27 | .Sh SEE ALSO 28 | .Xr getinfo 1 , 29 | .Xr open 1 , 30 | .Xr paths 1 , 31 | .Xr pbcopy 1 , 32 | .Xr pbpaste 1 , 33 | .Xr show 1 , 34 | .Xr snap 1 , 35 | .Xr trash 1 36 | .Sh AUTHORS 37 | .An Sveinbjorn Thordarson 38 | -------------------------------------------------------------------------------- /Tools/copy/copy.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2017, Sveinbjorn Thordarson 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | list of conditions and the following disclaimer in the documentation and/or other 13 | materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its contributors may 16 | be used to endorse or promote products derived from this software without specific 17 | prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #import "CLI.h" 32 | 33 | static void PrintHelp(void); 34 | 35 | static const char optstring[] = "fvh"; 36 | 37 | static struct option long_options[] = { 38 | {"force", no_argument, 0, 'f'}, 39 | {"version", no_argument, 0, 'v'}, 40 | {"help", no_argument, 0, 'h'}, 41 | {0, 0, 0, 0} 42 | }; 43 | 44 | int main(int argc, const char * argv[]) { @autoreleasepool { 45 | int optch; 46 | int long_index = 0; 47 | 48 | BOOL force = NO; 49 | 50 | // parse getopt 51 | while ((optch = getopt_long(argc, (char *const *)argv, optstring, long_options, &long_index)) != -1) { 52 | switch (optch) { 53 | 54 | case 'f': 55 | force = YES; 56 | break; 57 | 58 | // print version 59 | case 'v': 60 | PrintProgramVersion(); 61 | break; 62 | 63 | // print help with list of options 64 | case 'h': 65 | default: 66 | { 67 | PrintHelp(); 68 | exit(EX_OK); 69 | } 70 | break; 71 | } 72 | } 73 | 74 | NSMutableArray *args = ReadRemainingArgs(argc, argv); 75 | BOOL readStdin = ([args count] == 0); 76 | 77 | NSMutableArray *filePaths = [NSMutableArray array]; 78 | 79 | if (readStdin) { 80 | filePaths = ReadPathsFromStandardInput(NO); 81 | } else { 82 | filePaths = ValidPathsInArguments(args); 83 | if ([filePaths count] < 1) { 84 | PrintHelp(); 85 | exit(EX_USAGE); 86 | } 87 | } 88 | 89 | NSPasteboard *pboard = [NSPasteboard generalPasteboard]; 90 | if (pboard == nil) { 91 | NSPrintErr(@"Unable to connect to pasteboard"); 92 | exit(EX_UNAVAILABLE); 93 | } 94 | 95 | // Check if number of files exceeds limit 96 | if (([filePaths count] > DANGEROUS_FILE_LIMIT) && !force) { 97 | NSPrintErr(@"File count exceeds safety limit of %d. Use -f flag to override.", 98 | DANGEROUS_FILE_LIMIT); 99 | exit(EX_USAGE); 100 | } 101 | 102 | // Write to pasteboard 103 | [pboard declareTypes:[NSArray arrayWithObject:NSFilenamesPboardType] owner:nil]; 104 | [pboard setPropertyList:filePaths forType:NSFilenamesPboardType]; 105 | [pboard setString:[filePaths componentsJoinedByString:@"\n"] forType:NSStringPboardType]; 106 | 107 | NSPrint(@"%d file%@ copied to pasteboard", [filePaths count], [filePaths count] > 1 ? @"s" : @""); 108 | 109 | return EX_OK; 110 | }} 111 | 112 | #pragma mark - 113 | 114 | static void PrintHelp(void) { 115 | NSPrint(@"usage: %@ [file1 file2 ...]", [[NSProcessInfo processInfo] processName]); 116 | } 117 | -------------------------------------------------------------------------------- /Tools/getinfo/getinfo.1: -------------------------------------------------------------------------------- 1 | .Dd May 14, 2017 2 | .Dt GETINFO 1 3 | .Os Darwin 4 | .Sh NAME 5 | .Nm getinfo 6 | .Nd Show Finder Get Info window for one or more files 7 | .Sh SYNOPSIS 8 | .Nm 9 | .Op Fl svh 10 | .Op Ar 11 | .Sh DESCRIPTION 12 | .Nm 13 | is a command line tool which opens a Get Info window 14 | in the Mac OS X Finder for files passed as arguments. 15 | If no files are provided as arguments, 16 | .Nm 17 | will read standard input looking for paths. 18 | .Pp 19 | .Nm 20 | only works if the Mac OS Finder is running. 21 | .Pp 22 | The following flags are supported: 23 | .Bl -tag -width -indent 24 | .It Fl s, -single 25 | Show a single, combined Get Info window for all items 26 | .It Fl f, -force 27 | Ignore file limit. This flag is required to run the tool on 28 | a large number of files. 29 | .It Fl v, -version 30 | Print program version and exit 31 | .It Fl h, -help 32 | Print help and exit 33 | .El 34 | .Sh SEE ALSO 35 | .Xr copy 1 , 36 | .Xr open 1 , 37 | .Xr paths 1 , 38 | .Xr show 1 , 39 | .Xr snap 1 , 40 | .Xr trash 1 41 | .Sh AUTHORS 42 | .An Sveinbjorn Thordarson 43 | -------------------------------------------------------------------------------- /Tools/getinfo/getinfo.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2017, Sveinbjorn Thordarson 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | list of conditions and the following disclaimer in the documentation and/or other 13 | materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its contributors may 16 | be used to endorse or promote products derived from this software without specific 17 | prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #import "CLI.h" 32 | #import "NSWorkspace+Additions.h" 33 | 34 | static void PrintHelp(void); 35 | 36 | static const char optstring[] = "sivh"; 37 | 38 | static struct option long_options[] = { 39 | {"single", no_argument, 0, 's'}, 40 | {"force", no_argument, 0, 'f'}, 41 | {"version", no_argument, 0, 'v'}, 42 | {"help", no_argument, 0, 'h'}, 43 | {0, 0, 0, 0} 44 | }; 45 | 46 | int main(int argc, const char * argv[]) { @autoreleasepool { 47 | int optch; 48 | int long_index = 0; 49 | 50 | BOOL singleWindow = NO; 51 | BOOL force = NO; 52 | 53 | // parse getopt 54 | while ((optch = getopt_long(argc, (char *const *)argv, optstring, long_options, &long_index)) != -1) { 55 | switch (optch) { 56 | 57 | // open in new viewer 58 | case 'n': 59 | singleWindow = YES; 60 | break; 61 | 62 | // ignore file limit 63 | case 'f': 64 | force = YES; 65 | break; 66 | 67 | // print version 68 | case 'v': 69 | PrintProgramVersion(); 70 | break; 71 | 72 | // print help with list of options 73 | case 'h': 74 | default: 75 | { 76 | PrintHelp(); 77 | exit(EX_OK); 78 | } 79 | break; 80 | } 81 | } 82 | 83 | NSMutableArray *args = ReadRemainingArgs(argc, argv); 84 | BOOL readStdin = ([args count] == 0); 85 | 86 | NSMutableArray *filePaths = [NSMutableArray array]; 87 | 88 | if (readStdin) { 89 | filePaths = ReadPathsFromStandardInput(NO); 90 | } else { 91 | filePaths = ValidPathsInArguments(args); 92 | if ([filePaths count] < 1) { 93 | PrintHelp(); 94 | exit(EX_USAGE); 95 | } 96 | } 97 | 98 | // Make sure Finder is running 99 | if ([[NSWorkspace sharedWorkspace] isFinderRunning] == NO) { 100 | NSPrintErr(@"Unable to show Get Info window. Finder not running."); 101 | exit(EX_UNAVAILABLE); 102 | } 103 | 104 | // Check if number of files exceeds limit 105 | if (([filePaths count] > DANGEROUS_FILE_LIMIT) && !force) { 106 | NSPrintErr(@"File count exceeds safety limit of %d. Use -f flag to override.", 107 | DANGEROUS_FILE_LIMIT); 108 | exit(EX_USAGE); 109 | } 110 | 111 | // Show Get Info window for files 112 | for (NSString *path in filePaths) { 113 | if (singleWindow) { 114 | // TODO: do something differently 115 | } 116 | [[NSWorkspace sharedWorkspace] showFinderGetInfoForFile:path]; 117 | } 118 | 119 | return EX_OK; 120 | }} 121 | 122 | #pragma mark - 123 | 124 | static void PrintHelp(void) { 125 | NSPrint(@"usage: %@ [file1 file2 ...]", [[NSProcessInfo processInfo] processName]); 126 | } 127 | -------------------------------------------------------------------------------- /Tools/label/label.1: -------------------------------------------------------------------------------- 1 | .\"Modified from man(1) of FreeBSD, the NetBSD mdoc.template, and mdoc.samples. 2 | .\"See Also: 3 | .\"man mdoc.samples for a complete listing of options 4 | .\"man mdoc for the short list of editing options 5 | .\"/usr/share/misc/mdoc.template 6 | .Dd 2/5/12 \" DATE 7 | .Dt snap 1 \" Program name and manual section number 8 | .Os Darwin 9 | .Sh NAME \" Section Header - required - don't modify 10 | .Nm snap, 11 | .\" The following lines are read in generating the apropos(man -k) database. Use only key 12 | .\" words here as the database is built based on the words here and in the .ND line. 13 | .Nm Other_name_for_same_program(), 14 | .Nm Yet another name for the same program. 15 | .\" Use .Nm macro to designate other names for the documented program. 16 | .Nd This line parsed for whatis database. 17 | .Sh SYNOPSIS \" Section Header - required - don't modify 18 | .Nm 19 | .Op Fl abcd \" [-abcd] 20 | .Op Fl a Ar path \" [-a path] 21 | .Op Ar file \" [file] 22 | .Op Ar \" [file ...] 23 | .Ar arg0 \" Underlined argument - use .Ar anywhere to underline 24 | arg2 ... \" Arguments 25 | .Sh DESCRIPTION \" Section Header - required - don't modify 26 | Use the .Nm macro to refer to your program throughout the man page like such: 27 | .Nm 28 | Underlining is accomplished with the .Ar macro like this: 29 | .Ar underlined text . 30 | .Pp \" Inserts a space 31 | A list of items with descriptions: 32 | .Bl -tag -width -indent \" Begins a tagged list 33 | .It item a \" Each item preceded by .It macro 34 | Description of item a 35 | .It item b 36 | Description of item b 37 | .El \" Ends the list 38 | .Pp 39 | A list of flags and their descriptions: 40 | .Bl -tag -width -indent \" Differs from above in tag removed 41 | .It Fl a \"-a flag as a list item 42 | Description of -a flag 43 | .It Fl b 44 | Description of -b flag 45 | .El \" Ends the list 46 | .Pp 47 | .\" .Sh ENVIRONMENT \" May not be needed 48 | .\" .Bl -tag -width "ENV_VAR_1" -indent \" ENV_VAR_1 is width of the string ENV_VAR_1 49 | .\" .It Ev ENV_VAR_1 50 | .\" Description of ENV_VAR_1 51 | .\" .It Ev ENV_VAR_2 52 | .\" Description of ENV_VAR_2 53 | .\" .El 54 | .Sh FILES \" File used or created by the topic of the man page 55 | .Bl -tag -width "/Users/joeuser/Library/really_long_file_name" -compact 56 | .It Pa /usr/share/file_name 57 | FILE_1 description 58 | .It Pa /Users/joeuser/Library/really_long_file_name 59 | FILE_2 description 60 | .El \" Ends the list 61 | .\" .Sh DIAGNOSTICS \" May not be needed 62 | .\" .Bl -diag 63 | .\" .It Diagnostic Tag 64 | .\" Diagnostic informtion here. 65 | .\" .It Diagnostic Tag 66 | .\" Diagnostic informtion here. 67 | .\" .El 68 | .Sh SEE ALSO 69 | .\" List links in ascending order by section, alphabetically within a section. 70 | .\" Please do not reference files that do not exist without filing a bug report 71 | .Xr a 1 , 72 | .Xr b 1 , 73 | .Xr c 1 , 74 | .Xr a 2 , 75 | .Xr b 2 , 76 | .Xr a 3 , 77 | .Xr b 3 78 | .\" .Sh BUGS \" Document known, unremedied bugs 79 | .\" .Sh HISTORY \" Document history if command behaves in a unique manner 80 | -------------------------------------------------------------------------------- /Tools/label/label.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2017, Sveinbjorn Thordarson 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | list of conditions and the following disclaimer in the documentation and/or other 13 | materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its contributors may 16 | be used to endorse or promote products derived from this software without specific 17 | prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #import "CLI.h" 32 | #import "NSWorkspace+Additions.h" 33 | 34 | static void PrintHelp(void); 35 | 36 | static const char optstring[] = "fvh"; 37 | 38 | static struct option long_options[] = { 39 | {"force", no_argument, 0, 'f'}, 40 | {"version", no_argument, 0, 'v'}, 41 | {"help", no_argument, 0, 'h'}, 42 | {0, 0, 0, 0} 43 | }; 44 | 45 | int main(int argc, const char * argv[]) { @autoreleasepool { 46 | int optch; 47 | int long_index = 0; 48 | 49 | BOOL force = NO; 50 | 51 | // parse getopt 52 | while ((optch = getopt_long(argc, (char *const *)argv, optstring, long_options, &long_index)) != -1) { 53 | switch (optch) { 54 | 55 | // ignore file limit 56 | case 'f': 57 | force = YES; 58 | break; 59 | 60 | // print version 61 | case 'v': 62 | PrintProgramVersion(); 63 | break; 64 | 65 | // print help with list of options 66 | case 'h': 67 | default: 68 | { 69 | PrintHelp(); 70 | exit(EX_OK); 71 | } 72 | break; 73 | } 74 | } 75 | 76 | 77 | [[NSWorkspace sharedWorkspace] labelDictionary]; 78 | 79 | NSMutableArray *args = ReadRemainingArgs(argc, argv); 80 | if (![args count]) { 81 | PrintHelp(); 82 | exit(EX_USAGE); 83 | } 84 | 85 | // First arg is label identifier 86 | NSString *labelArg = args[0]; 87 | [args removeObjectAtIndex:0]; 88 | // TODO: Get label ID from label arg 89 | 90 | NSMutableArray *filePaths = [args count] ? ValidPathsInArguments(args) : ReadPathsFromStandardInput(NO); 91 | 92 | // Check if number of files exceeds limit 93 | if (([filePaths count] > DANGEROUS_FILE_LIMIT) && !force) { 94 | NSPrintErr(@"File count exceeds safety limit of %d. Use -f flag to override.", 95 | DANGEROUS_FILE_LIMIT); 96 | exit(EX_USAGE); 97 | } 98 | 99 | // Set label of the files 100 | int count = 0; 101 | for (NSString *path in filePaths) { 102 | // do label thing 103 | 104 | BOOL success = [[NSWorkspace sharedWorkspace] setLabelNamed:labelArg 105 | forFile:path]; 106 | if (!success) { 107 | NSPrintErr(@"error setting label of %@", path); 108 | continue; 109 | } 110 | 111 | [[NSWorkspace sharedWorkspace] notifyFinderFileChangedAtPath:[path stringByDeletingLastPathComponent]]; 112 | count += 1; 113 | //NSPrint(path); 114 | } 115 | 116 | if (count) { 117 | NSPrint(@"Label of %d file%@ set to \"%@\"", count, count > 1 ? @"s" : @"", labelArg); 118 | } 119 | 120 | return EX_OK; 121 | }} 122 | 123 | #pragma mark - 124 | 125 | static void PrintHelp(void) { 126 | NSPrint(@"usage: %@ identifier [file1 file2 ...]", [[NSProcessInfo processInfo] processName]); 127 | } 128 | -------------------------------------------------------------------------------- /Tools/man/.gitignore: -------------------------------------------------------------------------------- 1 | *.pdf 2 | *.html 3 | -------------------------------------------------------------------------------- /Tools/man/cat2html/build_cat2html.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | lex cat2html.l 4 | gcc -O2 lex.cat2html.c -o cat2html 5 | rm lex.cat2html.c 6 | strip -x cat2html 7 | chmod +x cat2html 8 | -------------------------------------------------------------------------------- /Tools/man/cat2html/cat2html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sveinbjornt/SnapTools/4e8552449247285e04df54b726c74e65143683d6/Tools/man/cat2html/cat2html -------------------------------------------------------------------------------- /Tools/man/cat2html/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 | #include 19 | 20 | #define BOLDFLAG 1 21 | #define ULINEFLAG 2 22 | 23 | int flags = 0, neededflags = 0; 24 | 25 | #define SETB neededflags |= BOLDFLAG 26 | #define UNSETB neededflags &= ~BOLDFLAG 27 | #define SETU neededflags |= ULINEFLAG 28 | #define UNSETU neededflags &= ~ULINEFLAG 29 | 30 | typedef size_t yy_size_t; 31 | 32 | /* 33 | * Default settings, may be changed using options... 34 | */ 35 | 36 | static char *startBold = ""; 37 | static char *stopBold = ""; 38 | static char *startULine = ""; 39 | static char *stopULine = ""; 40 | static char *startHeader = ""; 41 | static char *stopHeader = ""; 42 | static int addLinks = 0; 43 | static int markHeaders = 0; 44 | static int lineCount = 0; 45 | static int maxLineCount = 3; 46 | 47 | 48 | /* Decode a UTF8 sequence to return the unicode number */ 49 | static unsigned decodeUTF8(unsigned char *buf, yy_size_t len) 50 | { 51 | int n = buf[0] & (0x7f >> len); 52 | yy_size_t i; 53 | 54 | for (i=1; i
",stdout);
 68 |     }
 69 |     
 70 |     static void emitPostamble(void)
 71 |     {
 72 |         fputs("
\n",stdout); 73 | } 74 | 75 | #define adjust() if( neededflags!=flags ) _adjust() 76 | 77 | static void _adjust(void) { 78 | 79 | if( (flags^neededflags)&ULINEFLAG ) 80 | fputs(neededflags&ULINEFLAG? 81 | startULine:stopULine,stdout); 82 | if( (flags^neededflags)&BOLDFLAG ) 83 | fputs(neededflags&BOLDFLAG? 84 | startBold:stopBold,stdout); 85 | flags = neededflags; 86 | } 87 | 88 | static void emitChar(int ch) { 89 | adjust(); 90 | 91 | if (ch=='\n') { 92 | if (lineCount > maxLineCount) return; 93 | lineCount++; 94 | } 95 | else lineCount = 0; 96 | 97 | switch(ch) { 98 | case '"': fputs(""",stdout); break; 99 | case '<': fputs("<" ,stdout); break; 100 | case '>': fputs(">" ,stdout); break; 101 | case '&': fputs("&" ,stdout); break; 102 | default: fputc(ch,stdout); 103 | } 104 | } 105 | 106 | static void emitString(char *string) 107 | { 108 | int i, len=strlen(string); 109 | for (i=0;i= 4 && charblock[3] == '\010') 124 | return; 125 | 126 | /* If the characters are equal, they are printed on top of each other, so make it bold */ 127 | if( charblock[0] == charblock[2] ) { 128 | if (doBold) SETB; 129 | emitChar(charblock[0]); 130 | if (doBold) UNSETB; 131 | } 132 | /* Otherwise, just emit the second character. */ 133 | else { 134 | #ifdef DEBUGBACKSPACE 135 | fprintf(stderr, "Unknown backspace pair %c and %c\n", charblock[0], charblock[2]); 136 | #endif 137 | emitChar(charblock[2]); 138 | } 139 | } 140 | 141 | static void emitBackspacedText(char *text, yy_size_t length) 142 | { 143 | yy_size_t i=0; 144 | while (i < length) { 145 | if ((i < (length-1)) && text[i+1] == '\010') { 146 | emitBackspacedLetters(&text[i], 3, 0); 147 | i+=3; 148 | } 149 | else { 150 | emitChar(text[i]); 151 | i++; 152 | } 153 | } 154 | } 155 | 156 | /* Use hexidecimal entities */ 157 | static void emitUnicode(unsigned charNum) 158 | { 159 | char entityBuf[20]; 160 | sprintf(entityBuf, "&#x%x;", charNum); 161 | emitRaw(entityBuf); 162 | } 163 | 164 | 165 | ALLBUTUL [^\n_\010] 166 | NEEDQUOTE [<>&"] 167 | VERBATIM [^_\n\010\x1B<>&( \t"\xC2-\xF4] 168 | UPPER [A-Z] 169 | UPPERCONT [A-Z0-9 \t()] 170 | UPPERBS {UPPER}\010{UPPER} 171 | UPPERBSCONT ({UPPERBS}|[ \t()]) 172 | UTF8START [\xC2-\xF4] 173 | UTF8CONT [\x80-\xBF] 174 | UTF8SEQ {UTF8START}({UTF8CONT}{1,3}) 175 | SGRSTART \x1B\[ 176 | 177 | %option debug 178 | %option noyywrap 179 | %option noinput 180 | %option 8bit 181 | %option prefix="cat2html" 182 | 183 | %x FIRSTLINE 184 | 185 | %% 186 | 187 | /* 188 | * Start state FIRSTLINE is used to treat the first non-empty 189 | * line special. (First line contains header). 190 | */ 191 | 192 | /* Some X.org X11 pages have a weird #pragma at the start; strip it out. */ 193 | "#pragma".*\n {} 194 | 195 | . { SETB; emitChar(yytext[0]); } 196 | 197 | .\n { 198 | SETB; 199 | emitChar(yytext[0]); 200 | UNSETB; 201 | emitChar('\n'); 202 | BEGIN(INITIAL); 203 | } 204 | 205 | \n { UNSETB; emitChar('\n'); } 206 | 207 | /* Part of the X11 thing gets put on a separate line by nroff, sigh. */ 208 | ^"vate/var/tmp/X11".*\n {} 209 | 210 | /* 211 | * Put a special HREF around likely looking links to other man pages 212 | */ 213 | [_a-zA-Z][-a-zA-Z0-9._]*(-[ \t\n]+[-a-zA-Z0-9._]+)?[ \t\n]*"("[1-9n][a-zA-Z]?")" { 214 | 215 | if (!addLinks) 216 | { 217 | emitString(yytext); 218 | } 219 | else 220 | { 221 | int i; 222 | char href[yyleng+1]; 223 | 224 | /* Change newlines to spaces in the href portion */ 225 | strcpy(href, yytext); 226 | for(i=0; i"); 232 | emitString(yytext); 233 | emitRaw(""); 234 | } 235 | } 236 | 237 | 238 | /* 239 | * Non-empty, all-uppercase lines are treated as headers 240 | */ 241 | ^{UPPER}{UPPERCONT}*$ { 242 | SETB; 243 | if (markHeaders) emitRaw(startHeader); 244 | emitString(yytext); 245 | if (markHeaders) emitRaw(stopHeader); 246 | UNSETB; 247 | emitChar('\n'); 248 | } 249 | 250 | /* Similar for all-uppercase lines that use backspace for bolding */ 251 | ^{UPPERBS}{UPPERBSCONT}*$ { 252 | SETB; 253 | if (markHeaders) emitRaw(startHeader); 254 | emitBackspacedText(yytext, yyleng); 255 | if (markHeaders) emitRaw(stopHeader); 256 | UNSETB; 257 | emitChar('\n'); 258 | } 259 | 260 | /* 261 | * nroff +- 262 | */ 263 | 264 | "+"\010_ emitRaw("±"); 265 | 266 | /* 267 | * underline (part 1) 268 | */ 269 | 270 | {ALLBUTUL}\010_ { 271 | SETU; 272 | emitChar(yytext[0]); 273 | UNSETU; 274 | } 275 | 276 | /* 277 | * nroff bullets 278 | */ 279 | o\010"+" emitRaw("·"); // "•" doesn't work 280 | "+"\010o emitRaw("·"); 281 | o\010o\010"+"\010"+" emitRaw("·"); 282 | "+"\010"+\010"o\010o emitRaw("·"); 283 | 284 | /* 285 | * underline (part 2) 286 | */ 287 | 288 | _\010{ALLBUTUL} { 289 | SETU; 290 | emitChar(yytext[2]); 291 | UNSETU; 292 | } 293 | 294 | /* 295 | * handle further BS combinations 296 | */ 297 | 298 | .\010.\010? { 299 | emitBackspacedLetters(yytext, yyleng, 1); 300 | } 301 | 302 | /* Same idea but with UTF-8 characters */ 303 | {UTF8SEQ}\010{UTF8SEQ}\010? { 304 | if (yytext[yyleng-1] != '\010') { 305 | char *backspace = index(yytext, '\010'); 306 | if (backspace != NULL) { 307 | emitUnicode(decodeUTF8((unsigned char *)backspace+1, (yyleng - (backspace-yytext) - 1))); 308 | } 309 | } 310 | } 311 | 312 | /* If we find a UTF8 sequence, decode it */ 313 | {UTF8SEQ} { 314 | emitUnicode(decodeUTF8((unsigned char *)yytext, yyleng)); 315 | } 316 | 317 | /* Some versions of nroff/grotty use SGR escape sequences instead of the backspace hacks */ 318 | {SGRSTART}0?m UNSETU;UNSETB; 319 | {SGRSTART}1m SETB; 320 | {SGRSTART}[347]m SETU; 321 | {SGRSTART}(21|22)m UNSETB; 322 | {SGRSTART}(23|24|27)m UNSETU; 323 | {SGRSTART}[0-9;]+m {/*ignore any other codes*/} 324 | 325 | /* 326 | group characters in VERBATIM to make this 327 | filter faster... 328 | */ 329 | [ \t\n]+ emitString(yytext); 330 | {VERBATIM}+/[^\010] emitString(yytext); 331 | 332 | /* 333 | remaining specials 334 | */ 335 | 336 | /* \n emitChar('\n'); */ /*Matched by above whitespace matching rule */ 337 | . emitChar(yytext[0]); 338 | 339 | %% 340 | 341 | static void usage() { 342 | 343 | fprintf(stderr,"Usage: cat2html [-il] []\n" 344 | "\tTranslate output of (g)nroff to HTML. If no\n" 345 | "\t is given, cat2html reads stdin.\n" 346 | "\toption -i uses italic characters for underlining.\n" 347 | "\toption -l adds 'manpage:' HREF links to other man pages.\n" 348 | "\tHTML output is sent to stdout.\n"); 349 | exit(1); 350 | } 351 | 352 | int main(int argc, char *argv[]) 353 | { 354 | int c; 355 | 356 | yy_flex_debug = 0; 357 | 358 | /* Keep the same args as cat2rtf, even though -s doesn't really make much difference */ 359 | while ((c = getopt(argc, argv, "dgGiISs:lH")) != EOF) 360 | { 361 | switch( c ) { 362 | case 'd': 363 | yy_flex_debug = 1; 364 | break; 365 | case 'g': 366 | case 'G': 367 | startBold = ""; // ""; 368 | stopBold = ""; // ""; 369 | break; 370 | case 'i': 371 | case 'I': 372 | startULine = ""; 373 | stopULine = ""; 374 | break; 375 | case 's': 376 | maxLineCount = atoi(optarg); 377 | break; 378 | case 'S': 379 | maxLineCount = -1; 380 | break; 381 | case 'l': 382 | addLinks = 1; 383 | break; 384 | case 'H': 385 | markHeaders = 1; 386 | break; 387 | case '?': 388 | default: 389 | usage(); 390 | } 391 | } 392 | 393 | if( optind < argc ) 394 | yyin = fopen(argv[optind], "r"); 395 | else 396 | yyin = stdin; 397 | 398 | emitPreamble(); 399 | BEGIN(FIRSTLINE); 400 | yylex(); 401 | emitPostamble(); 402 | 403 | /* Shuts up a compiler warning */ 404 | if (0) unput('h'); 405 | 406 | return 0; 407 | } 408 | -------------------------------------------------------------------------------- /Tools/man/make_man_html.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # make_man_html.sh 4 | # Use cat2html from Carl Lindberg's ManOpen to convert man page to HTML 5 | 6 | /usr/bin/man ./snap.1 | ./cat2html/cat2html > snap.man.html 7 | -------------------------------------------------------------------------------- /Tools/man/update_website_docs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | scp snap.man.html root@sveinbjorn.org:/www/sveinbjorn/html/files/manpages/snap.man.html 4 | -------------------------------------------------------------------------------- /Tools/move/move.1: -------------------------------------------------------------------------------- 1 | .Dd Jun 5, 2017 2 | .Dt TRASH 1 3 | .Os Darwin 4 | .Sh NAME 5 | .Nm trash 6 | .Nd Move one or more files to the Trash 7 | .Sh SYNOPSIS 8 | .Nm 9 | .Op Fl vhf 10 | .Op Ar 11 | .Sh DESCRIPTION 12 | .Nm 13 | is a command line tool which moves files to the Mac OS Trash. 14 | .Pp 15 | The following flags are supported: 16 | .Bl -tag -width -indent 17 | Ignore file limit. This flag is required to run the tool on 18 | a large number of files. 19 | .It Fl v, -version 20 | Print program version and exit 21 | .It Fl h, -help 22 | Print help and exit 23 | .El 24 | .Sh SEE ALSO 25 | .Xr copy 1 , 26 | .Xr getinfo 1 , 27 | .Xr open 1 , 28 | .Xr paths 1 , 29 | .Xr show 1 , 30 | .Xr snap 1 31 | .Sh AUTHORS 32 | .An Sveinbjorn Thordarson 33 | -------------------------------------------------------------------------------- /Tools/move/move.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2017, Sveinbjorn Thordarson 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | list of conditions and the following disclaimer in the documentation and/or other 13 | materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its contributors may 16 | be used to endorse or promote products derived from this software without specific 17 | prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #import "CLI.h" 32 | #import "NSWorkspace+Additions.h" 33 | 34 | static void PrintHelp(void); 35 | 36 | static const char optstring[] = "vh"; 37 | 38 | static struct option long_options[] = { 39 | {"version", no_argument, 0, 'v'}, 40 | {"help", no_argument, 0, 'h'}, 41 | {0, 0, 0, 0} 42 | }; 43 | 44 | int main(int argc, const char * argv[]) { @autoreleasepool { 45 | int optch; 46 | int long_index = 0; 47 | 48 | // parse getopt 49 | while ((optch = getopt_long(argc, (char *const *)argv, optstring, long_options, &long_index)) != -1) { 50 | switch (optch) { 51 | 52 | // print version 53 | case 'v': 54 | PrintProgramVersion(); 55 | break; 56 | 57 | // print help with list of options 58 | case 'h': 59 | default: 60 | { 61 | PrintHelp(); 62 | exit(EX_OK); 63 | } 64 | break; 65 | } 66 | } 67 | 68 | NSMutableArray *args = ReadRemainingArgs(argc, argv); 69 | BOOL readStdin = ([args count] == 0); 70 | 71 | NSMutableArray *filePaths = [NSMutableArray array]; 72 | 73 | if (readStdin) { 74 | filePaths = ReadPathsFromStandardInput(NO); 75 | } else { 76 | filePaths = ValidPathsInArguments(args); 77 | if ([filePaths count] < 1) { 78 | PrintHelp(); 79 | exit(EX_USAGE); 80 | } 81 | } 82 | 83 | return EX_OK; 84 | }} 85 | 86 | #pragma mark - 87 | 88 | static void PrintHelp(void) { 89 | NSPrint(@"usage: %@ [file1 file2 ...] destinationDirectory", [[NSProcessInfo processInfo] processName]); 90 | } 91 | -------------------------------------------------------------------------------- /Tools/paths/paths.1: -------------------------------------------------------------------------------- 1 | .Dd May 14, 2017 2 | .Dt PATHS 1 3 | .Os Darwin 4 | .Sh NAME 5 | .Nm paths 6 | .Nd Find file system paths in input 7 | .Sh SYNOPSIS 8 | .Nm 9 | .Op Fl avh 10 | .Sh DESCRIPTION 11 | .Nm 12 | is a command line tool which finds file system paths in standard 13 | input and prints them to standard output. 14 | .Pp 15 | The following flags are supported: 16 | .Bl -tag -width -indent 17 | .It Fl a, -absolute-only 18 | Do not resolve and include relative paths 19 | .It Fl v, -version 20 | Print program version and exit 21 | .It Fl h, -help 22 | Print help and exit 23 | .El 24 | .Sh SEE ALSO 25 | .Xr copy 1 , 26 | .Xr getinfo 1 , 27 | .Xr open 1 , 28 | .Xr show 1 , 29 | .Xr snap 1 , 30 | .Xr trash 1 31 | .Sh AUTHORS 32 | .An Sveinbjorn Thordarson 33 | -------------------------------------------------------------------------------- /Tools/paths/paths.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2017, Sveinbjorn Thordarson 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | list of conditions and the following disclaimer in the documentation and/or other 13 | materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its contributors may 16 | be used to endorse or promote products derived from this software without specific 17 | prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #import "CLI.h" 32 | 33 | static void PrintHelp(void); 34 | 35 | static const char optstring[] = "avh"; 36 | 37 | static struct option long_options[] = { 38 | {"absolute-only", no_argument, 0, 'a'}, 39 | {"version", no_argument, 0, 'v'}, 40 | {"help", no_argument, 0, 'h'}, 41 | {0, 0, 0, 0} 42 | }; 43 | 44 | int main(int argc, const char * argv[]) { @autoreleasepool { 45 | int optch; 46 | int long_index = 0; 47 | 48 | BOOL absolutePathsOnly = NO; 49 | 50 | // parse getopt 51 | while ((optch = getopt_long(argc, (char *const *)argv, optstring, long_options, &long_index)) != -1) { 52 | switch (optch) { 53 | 54 | // only parse absolute paths 55 | case 'a': 56 | absolutePathsOnly = YES; 57 | break; 58 | 59 | // print version 60 | case 'v': 61 | PrintProgramVersion(); 62 | break; 63 | 64 | // print help with list of options 65 | case 'h': 66 | default: 67 | { 68 | PrintHelp(); 69 | exit(EX_OK); 70 | } 71 | break; 72 | } 73 | } 74 | 75 | // ignore any remaining command line args and read from stdin 76 | // TODO: Do something with absolutePathsOnly 77 | NSMutableArray *filePaths = ReadPathsFromStandardInput(absolutePathsOnly); 78 | 79 | for (NSString *path in filePaths) { 80 | NSPrint(path); 81 | } 82 | 83 | return EX_OK; 84 | }} 85 | 86 | #pragma mark - 87 | 88 | static void PrintHelp(void) { 89 | NSPrint(@"usage: %@ [avh]", [[NSProcessInfo processInfo] processName]); 90 | } 91 | -------------------------------------------------------------------------------- /Tools/show/show.1: -------------------------------------------------------------------------------- 1 | .Dd May 14, 2017 2 | .Dt SHOW 1 3 | .Os Darwin 4 | .Sh NAME 5 | .Nm show 6 | .Nd Show one or more files in the Mac OS X Finder 7 | .Sh SYNOPSIS 8 | .Nm 9 | .Op Fl nvh 10 | .Op Ar 11 | .Sh DESCRIPTION 12 | .Nm 13 | is a command line tool which shows file arguments in the Mac 14 | OS X Finder. It only works as expected if the Finder is running. 15 | .Pp 16 | The following flags are supported: 17 | .Bl -tag -width -indent 18 | .It Fl n, -new 19 | Open new file viewer when showing file(s) 20 | .It Fl f, -force 21 | Ignore file limit. This flag is required to run the tool on 22 | a large number of files. 23 | .It Fl v, -version 24 | Print program version and exit 25 | .It Fl h, -help 26 | Print help and exit 27 | .El 28 | .Sh SEE ALSO 29 | .Xr copy 1 , 30 | .Xr getinfo 1 , 31 | .Xr open 1 , 32 | .Xr paths 1 , 33 | .Xr show 1 , 34 | .Xr snap 1 , 35 | .Xr trash 1 36 | .Sh AUTHORS 37 | .An Sveinbjorn Thordarson 38 | -------------------------------------------------------------------------------- /Tools/show/show.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2017, Sveinbjorn Thordarson 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | list of conditions and the following disclaimer in the documentation and/or other 13 | materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its contributors may 16 | be used to endorse or promote products derived from this software without specific 17 | prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #import "CLI.h" 32 | #import "NSWorkspace+Additions.h" 33 | 34 | static void PrintHelp(void); 35 | 36 | static const char optstring[] = "nfvh"; 37 | 38 | static struct option long_options[] = { 39 | {"new", no_argument, 0, 'n'}, 40 | {"force", no_argument, 0, 'f'}, 41 | {"version", no_argument, 0, 'v'}, 42 | {"help", no_argument, 0, 'h'}, 43 | {0, 0, 0, 0} 44 | }; 45 | 46 | int main(int argc, const char * argv[]) { @autoreleasepool { 47 | int optch; 48 | int long_index = 0; 49 | 50 | BOOL inNewViewer = NO; 51 | BOOL force = NO; 52 | 53 | // parse getopt 54 | while ((optch = getopt_long(argc, (char *const *)argv, optstring, long_options, &long_index)) != -1) { 55 | switch (optch) { 56 | 57 | // open in new viewer 58 | case 'n': 59 | inNewViewer = YES; 60 | break; 61 | 62 | // ignore file limit 63 | case 'f': 64 | force = YES; 65 | break; 66 | 67 | // print version 68 | case 'v': 69 | PrintProgramVersion(); 70 | break; 71 | 72 | // print help with list of options 73 | case 'h': 74 | default: 75 | { 76 | PrintHelp(); 77 | exit(EX_OK); 78 | } 79 | break; 80 | } 81 | } 82 | 83 | NSMutableArray *args = ReadRemainingArgs(argc, argv); 84 | BOOL readStdin = ([args count] == 0); 85 | 86 | NSMutableArray *filePaths = [NSMutableArray array]; 87 | 88 | if (readStdin) { 89 | filePaths = ReadPathsFromStandardInput(NO); 90 | } else { 91 | filePaths = ValidPathsInArguments(args); 92 | if ([filePaths count] < 1) { 93 | PrintHelp(); 94 | exit(EX_USAGE); 95 | } 96 | } 97 | 98 | // Make sure Finder is running 99 | if ([[NSWorkspace sharedWorkspace] isFinderRunning] == NO) { 100 | NSPrintErr(@"Unable to show file in Finder: Finder not running."); 101 | exit(EX_UNAVAILABLE); 102 | } 103 | 104 | // Check if number of files exceeds limit 105 | if (([filePaths count] > DANGEROUS_FILE_LIMIT) && !force) { 106 | NSPrintErr(@"File count exceeds safety limit of %d. Use -f flag to override.", 107 | DANGEROUS_FILE_LIMIT); 108 | exit(EX_USAGE); 109 | } 110 | 111 | for (NSString *path in filePaths) { 112 | NSString *basename = [path lastPathComponent]; 113 | if ([basename hasPrefix:@"."]) { 114 | NSPrintErr(@"warning: %@ is hidden and may not be shown", basename); 115 | } 116 | 117 | /* Activate the Finder and open a window selecting the file at the given path. If fullPath is nil, this will instead open the directory specified by rootFullPath, and not select any file. If rootFullPath is the empty string (@""), the file is selected in the main viewer. Otherwise, a new file viewer is opened. 118 | */ 119 | NSString *parentDirectoryPath = [path stringByDeletingLastPathComponent]; 120 | NSString *rootFullPath = inNewViewer ? parentDirectoryPath : @""; 121 | 122 | if (inNewViewer) { 123 | [[NSWorkspace sharedWorkspace] openFile:parentDirectoryPath withApplication:@"Finder"]; 124 | } 125 | 126 | [[NSWorkspace sharedWorkspace] selectFile:path inFileViewerRootedAtPath:rootFullPath]; 127 | } 128 | 129 | return EX_OK; 130 | }} 131 | 132 | #pragma mark - 133 | 134 | static void PrintHelp(void) { 135 | NSPrint(@"usage: %@ [file1 file2 ...]", [[NSProcessInfo processInfo] processName]); 136 | } 137 | -------------------------------------------------------------------------------- /Tools/snap/snap.1: -------------------------------------------------------------------------------- 1 | .Dd May 14, 2017 2 | .Dt SNAP 1 3 | .Os Darwin 4 | .Sh NAME 5 | .Nm snap 6 | .Nd Open files in a Snap window 7 | .Sh SYNOPSIS 8 | .Nm 9 | .Op Fl avh 10 | .Sh DESCRIPTION 11 | .Nm 12 | is a command line tool which opens a Snap window containing 13 | any files passed as arguments. If no file args are supplied, 14 | it instead searches for file system paths in the standard input. 15 | .Pp 16 | The following flags are supported: 17 | .Bl -tag -width -indent 18 | .It Fl a, -absolute-only 19 | Do not resolve and include relative paths 20 | .It Fl v, -version 21 | Print program version and exit 22 | .It Fl h, -help 23 | Print help and exit 24 | .El 25 | .Sh SEE ALSO 26 | .Xr copy 1 , 27 | .Xr getinfo 1 , 28 | .Xr open 1 , 29 | .Xr paths 1 , 30 | .Xr show 1 , 31 | .Xr trash 1 32 | .Sh AUTHORS 33 | .An Sveinbjorn Thordarson 34 | -------------------------------------------------------------------------------- /Tools/snap/snap.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2017, Sveinbjorn Thordarson 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | list of conditions and the following disclaimer in the documentation and/or other 13 | materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its contributors may 16 | be used to endorse or promote products derived from this software without specific 17 | prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #import "CLI.h" 32 | 33 | static NSMutableSet *ReadDirectoryContents(NSString *dirPath); 34 | 35 | static BOOL SendOpenDocumentAppleEvent(NSSet *paths); 36 | static void PrintHelp(void); 37 | 38 | static const char optstring[] = "apvh"; 39 | 40 | static struct option long_options[] = { 41 | {"all", no_argument, 0, 'a'}, 42 | {"print", no_argument, 0, 'p'}, 43 | {"version", no_argument, 0, 'v'}, 44 | {"help", no_argument, 0, 'h'}, 45 | {0, 0, 0, 0} 46 | }; 47 | 48 | #pragma mark - 49 | 50 | int main(int argc, const char * argv[]) { @autoreleasepool { 51 | 52 | BOOL printOnly = NO; 53 | BOOL showAll = NO; 54 | BOOL openLastSnap = NO; 55 | 56 | int optch; 57 | int long_index = 0; 58 | 59 | // parse getopt 60 | while ((optch = getopt_long(argc, (char *const *)argv, optstring, long_options, &long_index)) != -1) { 61 | switch (optch) { 62 | 63 | // show all hidden files except . and .. 64 | case 'a': 65 | showAll = YES; 66 | break; 67 | 68 | // only print found paths 69 | case 'p': 70 | printOnly = YES; 71 | break; 72 | 73 | // open last snap 74 | case 'l': 75 | openLastSnap = YES; 76 | break; 77 | 78 | // don't sort 79 | 80 | 81 | // print version 82 | case 'v': 83 | PrintProgramVersion(); 84 | break; 85 | 86 | // print help with list of options 87 | case 'h': 88 | default: 89 | { 90 | PrintHelp(); 91 | exit(EX_OK); 92 | } 93 | break; 94 | } 95 | } 96 | 97 | if (openLastSnap) { 98 | // open last snap 99 | } 100 | 101 | // read remaining args 102 | NSMutableArray *remainingArgs = [NSMutableArray array]; 103 | while (optind < argc) { 104 | NSString *argStr = @(argv[optind]); 105 | [remainingArgs addObject:argStr]; 106 | optind += 1; 107 | } 108 | 109 | NSMutableSet *paths = [NSMutableSet set]; 110 | 111 | // read standard input if no file args 112 | if ([remainingArgs count] == 0) { 113 | 114 | NSString *standardInput = ReadStandardInput(); 115 | if (!standardInput) { 116 | return EX_NOINPUT; 117 | } 118 | NSLog(@"Parsing paths from input length %d", [standardInput length]); 119 | paths = [PathParser parse:standardInput]; 120 | NSLog(@"Found %d paths", [paths count]); 121 | } else { 122 | 123 | // process file args 124 | for (NSString *filePath in remainingArgs) { 125 | NSString *f = [PathParser makeAbsolutePath:filePath]; 126 | if ([FILEMGR fileExistsAtPath:f]) { 127 | [paths addObject:f]; 128 | } else { 129 | NSPrint(@"No such file or directory: %@", f); 130 | } 131 | } 132 | } 133 | 134 | // a single directory as arg means we should list contents 135 | if ([paths count] == 1) { 136 | NSString *p = [paths anyObject]; 137 | BOOL isDir = NO; 138 | if ([FILEMGR fileExistsAtPath:p isDirectory:&isDir] && isDir) { 139 | paths = ReadDirectoryContents(p); 140 | } 141 | } 142 | 143 | // Sort alphabetically 144 | NSArray *finalPaths = [paths allObjects]; 145 | finalPaths = [finalPaths sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]; 146 | 147 | // Hand paths over to GUI app via Apple Event 148 | if ([paths count]) { 149 | 150 | if (printOnly) { 151 | for (NSString *p in finalPaths) { 152 | NSPrint(@"%@", p); 153 | } 154 | } 155 | else { 156 | BOOL success = SendOpenDocumentAppleEvent(paths); 157 | if (!success) { 158 | NSPrintErr(@"Error launching Snap application"); 159 | exit(EX_UNAVAILABLE); 160 | } 161 | } 162 | 163 | } else { 164 | NSPrintErr(@"No paths provided"); 165 | exit(EX_USAGE); 166 | } 167 | 168 | return EX_OK; 169 | } } 170 | 171 | #pragma mark - 172 | 173 | static NSMutableSet *ReadDirectoryContents(NSString *dirPath) { 174 | NSMutableSet *pathSet = [NSMutableSet set]; 175 | NSArray *dirContents = [FILEMGR contentsOfDirectoryAtPath:dirPath error:nil]; 176 | for (NSString *item in dirContents) { 177 | NSString *fpath = [dirPath stringByAppendingPathComponent:item]; 178 | [pathSet addObject:fpath]; 179 | } 180 | return pathSet; 181 | } 182 | 183 | static BOOL SendOpenDocumentAppleEvent(NSSet *paths) { 184 | 185 | // convert path strings to url objects 186 | NSMutableArray *urls = [NSMutableArray array]; 187 | for (NSString *p in paths) { 188 | [urls addObject:[NSURL fileURLWithPath:p]]; 189 | } 190 | 191 | NSPrintErr(@"Sending AEvent to open %d URLs", [urls count]); 192 | 193 | NSWorkspaceLaunchOptions opt = NSWorkspaceLaunchAsync | NSWorkspaceLaunchWithErrorPresentation; 194 | BOOL succ = [[NSWorkspace sharedWorkspace] openURLs:urls 195 | withAppBundleIdentifier:PROGRAM_BUNDLE_IDENTIFIER 196 | options:opt 197 | additionalEventParamDescriptor:nil 198 | launchIdentifiers:NULL]; 199 | 200 | return succ; 201 | } 202 | 203 | #pragma mark - 204 | 205 | static void PrintHelp(void) { 206 | NSPrint(@"usage: %@ [file1 file2 ...]", [[NSProcessInfo processInfo] processName]); 207 | } 208 | -------------------------------------------------------------------------------- /Tools/trash/trash.1: -------------------------------------------------------------------------------- 1 | .Dd Jun 5, 2017 2 | .Dt TRASH 1 3 | .Os Darwin 4 | .Sh NAME 5 | .Nm trash 6 | .Nd Move one or more files to the Trash 7 | .Sh SYNOPSIS 8 | .Nm 9 | .Op Fl vh 10 | .Op Ar 11 | .Sh DESCRIPTION 12 | .Nm 13 | is a command line tool which moves files to the Mac OS Trash. 14 | .Pp 15 | The following flags are supported: 16 | .Bl -tag -width -indent 17 | Ignore file limit. This flag is required to run the tool on 18 | a large number of files. 19 | .It Fl v, -version 20 | Print program version and exit 21 | .It Fl h, -help 22 | Print help and exit 23 | .El 24 | .Sh SEE ALSO 25 | .Xr copy 1 , 26 | .Xr getinfo 1 , 27 | .Xr open 1 , 28 | .Xr paths 1 , 29 | .Xr show 1 , 30 | .Xr snap 1 31 | .Sh AUTHORS 32 | .An Sveinbjorn Thordarson 33 | -------------------------------------------------------------------------------- /Tools/trash/trash.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2017, Sveinbjorn Thordarson 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, this 12 | list of conditions and the following disclaimer in the documentation and/or other 13 | materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its contributors may 16 | be used to endorse or promote products derived from this software without specific 17 | prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #import "CLI.h" 32 | #import "NSWorkspace+Additions.h" 33 | 34 | static void PrintHelp(void); 35 | 36 | static const char optstring[] = "fvh"; 37 | 38 | static struct option long_options[] = { 39 | {"force", no_argument, 0, 'f'}, 40 | {"version", no_argument, 0, 'v'}, 41 | {"help", no_argument, 0, 'h'}, 42 | {0, 0, 0, 0} 43 | }; 44 | 45 | int main(int argc, const char * argv[]) { @autoreleasepool { 46 | int optch; 47 | int long_index = 0; 48 | 49 | BOOL force = NO; 50 | 51 | // parse getopt 52 | while ((optch = getopt_long(argc, (char *const *)argv, optstring, long_options, &long_index)) != -1) { 53 | switch (optch) { 54 | 55 | // ignore file limit 56 | case 'f': 57 | force = YES; 58 | break; 59 | 60 | // print version 61 | case 'v': 62 | PrintProgramVersion(); 63 | break; 64 | 65 | // print help with list of options 66 | case 'h': 67 | default: 68 | { 69 | PrintHelp(); 70 | exit(EX_OK); 71 | } 72 | break; 73 | } 74 | } 75 | 76 | NSMutableArray *args = ReadRemainingArgs(argc, argv); 77 | BOOL readStdin = ([args count] == 0); 78 | 79 | NSMutableArray *filePaths = [NSMutableArray array]; 80 | 81 | if (readStdin) { 82 | filePaths = ReadPathsFromStandardInput(NO); 83 | } else { 84 | filePaths = ValidPathsInArguments(args); 85 | if ([filePaths count] < 1) { 86 | PrintHelp(); 87 | exit(EX_USAGE); 88 | } 89 | } 90 | 91 | // Check if number of files exceeds limit 92 | if (([filePaths count] > DANGEROUS_FILE_LIMIT) && !force) { 93 | NSPrintErr(@"File count exceeds safety limit of %d. Use -f flag to override.", 94 | DANGEROUS_FILE_LIMIT); 95 | exit(EX_USAGE); 96 | } 97 | 98 | unsigned long trashed = 0; 99 | for (NSString *path in filePaths) { 100 | if ([[NSWorkspace sharedWorkspace] moveFileToTrash:path]) { 101 | trashed += 1; 102 | } 103 | } 104 | 105 | if (trashed) { 106 | NSPrint(@"%d file%@ moved to the Trash", trashed, trashed > 1 ? @"s" : @""); 107 | } 108 | 109 | return EX_OK; 110 | }} 111 | 112 | #pragma mark - 113 | 114 | static void PrintHelp(void) { 115 | NSPrint(@"usage: %@ [file1 file2 ...]", [[NSProcessInfo processInfo] processName]); 116 | } 117 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sveinbjornt/SnapTools/4e8552449247285e04df54b726c74e65143683d6/icon.png --------------------------------------------------------------------------------