Archive for the ‘Hacking’ Category

Piping stdout and stderr to Preview

A while back, I wrote about how handy it was to redirect a man page into Preview. This allows you to keep the man page open, search it, and generally have a better user experience than struggling with more (or less) to navigate through the information provided there.

man -t apropos | open -fa Preview

Recently, someone asked me about more modern command line interaction, specifically, commands that use --help or similar to provide their documentation. Could that information be opened in Preview as well.

So I put on my thinking hat and set to work. The first command line utility I decided to work with was screencapture because I’ve been using it fairly heavily over the last few days. However, it appears that Apple failed to build in an actual help system beyond man. It was a poor choice to try to use to render but I decided to keep plugging away at it because I wanted to be able to pipe both stdoutand stderr to Preview.

What I came up with looked something like this, all in one line of course:

bash -c "screencapture -? &> 
    $TMPDIR/previewrendertext.txt; 
    /usr/sbin/cupsfilter -i text/plain 
        -D $TMPDIR/previewrendertext.txt 
        2> /dev/null | 
    open -fa Preview"

This all relies on cupsfilter, which can convert a file of text to a printable form, which just happens to be readable by Preview as a PDF.

I’m doing quite a bit of conglomeration, joining the stderr and stdout streams using &> and saving them into my Mac’s $TMPDIR. That file is cleaned up by the -D option from cupsfilter.

I also am removing the incessant debug messages from cupsfilter by redirecting them to /dev/null before opening the print output in Preview.

Please note that I’m still using tcsh/zsh over bash on my main system, so that certainly affects things. Since I needed a little of the bash nuance, I decided to run it all squished as a single -c command. (I’m sure if I spent enough time, I could do it all in csh but I really didn’t want to spend that time.)

As you can see in the previous screenshot, an older utility meant for man output doesn’t really look all that hot shoved into Preview via cupsfilter, especially with line lengths. There’s also no nice groffing and troffing to make everything pretty, the way you get with man:

So how could would this kludge work with a modern command-line app, such as those produced using the Swift Argument Parser (https://github.com/apple/swift-argument-parser)? First, I built a utility that would let me run any command (well, so long as it was properly quoted) without having to type all the details out each time I ran it:

#! /bin/bash

$@ &> $TMPDIR/previewrendertext.txt ; /usr/sbin/cupsfilter -i text/plain -D $TMPDIR/previewrendertext.txt 2> /dev/null | open -fa Preview

This allowed me to call preview "now --help" to redirect the standard help message from my now utility (https://github.com/erica/now)  to Preview. Yeah, originally I wanted to just pipe stuff into it but I couldn’t figure out how to get the stderr and the stdout piped together into a single stream, let alone convert them into a file form because cupsfilter doesn’t know or do pipes.

It’s pretty readable and well-formatted due to the automatic configuration that the Swift Argument Parser provides from my code but it just feels, you know, very very plain.

So I went ahead and tried to see what would happen if I groffed it up a little by passing it through /usr/bin/groff -Tps -mandoc -c instead of using cupsfilter:

bash -c "now --help &> 
    $TMPDIR/previewrendertext.txt; 
    /usr/bin/groff -Tps -mandoc -c 
    $TMPDIR/previewrendertext.txt" | 
    open -fa preview

And it’s…pretty meh. I tried mandoc, mdoc, me, mm, ms, and www formats. They all came out the same, and none of the SAP tabs really worked. I think it looks a lot more “manny” than the straight printout but the indentation really bugged:

I decided to stop at about this point as there’s really a time when further effort just isn’t worth further investment — so I could throw it out there and see if this was of interest to anyone else.

Let me know.

Controlling Screen Sharing from the command line

My world often narrows to Xcode and Terminal. There are times I just want to check in on another computer quickly and I don’t want the hassle of creating a new Finder window, going to the network, waiting for the items to load, and clicking Screen Sharing.

Let me share a quick osascript solution I use instead (and there’s an even better solution for launching just using open, thanks Chris). This example connects to Esopus Spitzenburg, my Mojave computer (aka the one that still runs everything including Photoshop and Microsoft Office).

#! /bin/sh

/usr/bin/osascript -e 'tell application "Screen Sharing" to GetURL "vnc://Esopus-Spitzenburg.local"'

If closed, the Screen Sharing application launches in the foreground. If already running, it stays at its relative hierarchy. You can add a command to activate to bring it to the front on each invocation, even if the connection is already active.

If you’d like to let the utility toggle visibility on call instead, add a request to Finder and append it to the script:

/usr/bin/osascript -e 'tell application "Finder" to set visible of process "Screen Sharing" to not visible of process "Screen Sharing"'

I have written similar utilities to open Broxwood Foxwhelp and Glockenapfel, depending on the computer I’m using. As much as I’ve intended to write a single utility instead of two or three dedicated scripts, I’ve can’t convince myself it’s worth the effort.

(Bonus points: Why are my computers named this way?)

Crafting a custom word count service

I just happened to need to do a lot of word counts today so I put together a service to make my life easier. While, I performed my initial work on Mojave but the same approach works all the way to Big Sur and, presumably, the upcoming macOS Malibu Barbie.

Open Automator

To get started, launch automator and create a new document.

Select Quick Action, Choose from the new document dialog:

Add Scripting

Drop in a Run Shell Script and then an Apple Script. You can search from them in the top-left corner. Drag them in order into the right panel.

I used /bin/bash for my shell script, shocking, I know, as I am well known for my love of all things csh. Feel free to use whatever shell suits you. The first non-argument shell variable ($1 here) corresponds to highlighted text, which can be used by the system contextual menu:

echo `echo $1 | wc -w` words. `echo $1 | wc -c` characters.

Switch the pop-up for piping output (“passing input”) from “to stdin” to “as arguments”. This allows the AppleScript to read the selected data and present a dialog. If you forget, you’ll get empty input and something like “1 words, 0 characters” all the time.

on run {input, parameters}
    display dialog input as string buttons {"OK"}
end run

Save and Run

Save the action. I called mine “Word Count”.

The action automatically saves to ~/Library/Services, in case you want to find or delete it in the future.

% ls
Word Count.workflow/

Your new quick action automatically adds a custom service to your contextual pop-ups. Just highlight anything you want to count, from text on a web page to your work in a document and open the contextual menu:

Make sure the output looks reasonable. For easiest testing, copy the text to your clipboard and then use wc directly in terminal.

And, then boom, you are done.

Let me know if this was helpful.

How I got Rust working in Xcode

A while ago, I posted about how I set up Xcode to work with Python. Yesterday, I was taking a class on Rust and decided to use my friendly neighborhood (sp)IDE(rman) coding environment, namely Xcode.

I’m not going to say it was a stunning success but there was enough interest that I thought I’d share the steps so you too could embrace Rust through Xcode.

Install Rust. You start, as one does, by installing Rust. Hop over to https://www.rust-lang.org/tools/install to grab a copy of the tools. They install to ~/.cargo, for whatever reason. I put a link in to / usr/local/bin.

Create a Project. Create an external build system Xcode project by choosing File > New > Project > Cross-platform > External Build System > Next. Enter a product name (I called mine “Rust” because that’s exactly how creative I am.) and set your build tool (in my case, /usr/local/bin/rustc because of the link). Save it somewhere convenient.

Create a source file. Apparently “rs” (rust source?) is the proper extension. I went with “test” as my name. File > New > Empty > test.rs

fn main() {
    println!("hello world");
}

Don’t forget to add some code.

Compile. Edit your scheme.  Choose Run > Info > Build Executable > Other and select your compiler. Adding it to /usr/local/bin made it easier to select rustc for me. Then uncheck Debug executable because you’re not debugging the Rust compiler.

At this point you can click Run and you’ll see the standard option message because you haven’t specified what it should run.

Back in the scheme editor select Run > Arguments and add the source file and output file. Unfortunately, I could not get this to work with SRCROOT at all, so here it is in all its glory with complete paths.

The Pre-action removes any build product from a previous run:

So here we are. With luck, it compiles. If not, the errors appear in pretty horrible form in the Xcode console, where curses is what we do, not how the console interprets pretty text output.

You can get slightly less horrible feedback by adding the launch argument: –error-format=json

Yeah, it’s wordy but it’s slightly less awful.

Pick a path. Unlike python, rust is just a compiler. If you build, and then add a step execute, the execution output (unlike compiler errors) will not normally print at the Xcode console. The challenge is to get that information in some form where you can access it.

At first I went with a little post-action osascript and threw up the output in a separate window:

But I really wanted to make it work with the console So back I went to Applescripting. Instead of rustc, I changed my build tool to osascript:

I added this instead to my run scheme arguments.

Yep, I’m using osascript to run a shell script that just compiles with rust and then runs it, passing the output through back to Xcode.

I know this is bad. I know I should be ashamed. I hang my head.

But you know what? It works. Stray osascript-crud and all:

I’m not sure how much this makes me a programming outcast but it was kind of fun to figure out how far I could push my beloved enemy Xcode.

Pushing the gist envelope: gists with pics and zips

People often don’t realize how powerful GitHub’s gist pasting service is. It’s more than just a “paste” site. Gist offers a full version control system extension to GitHub’s main site. I’ve been working on developing version control training materials and gist is a great way to introduce the fundamentals.

Many gist users know that the site offers you pushbutton convenience to paste one or more files. You can create open gists and “secret” ones hidden from public view. Gist also lets you fork, revise, and explore diffs between revisions.

For example, you can work in groups when writing. Collaborators can fork and make changes to offer content feedback. You can then use diffs to see what edits were made.

Here are some diffs from a recent Raw String proposal I worked on:

And this is the corresponding “rich diff”, which is slightly prettier:

And there’s a lot more you can do with gist. That’s because gists, as version control repositories, can be cloned to your computer, modified, and pushed back to GitHub. This means you can, for example, set up albums of pictures or host an easy-to-distribute zip file.

Each gist URL is a repository’s address:

git clone https://gist.github.com/erica/7cd24c6ab2f737735a9ab2b95628c549

As a gist’s owner, you have commit privileges, allowing you to edit your gist from your computer.

The command line enables you to add binary files that you can’t from the web interface. I grabbed a bunch of kitten pictures from Pexels and added them to my gist. A nice way to create simple albums:

If you click “Download ZIP” at the top right, GitHub zips up the repository contents (in this case five kitten PNGs) and copies them to your computer. This is not cloning; the zip file just stores the source files, not the full git repo.

It’s just as easy to host an archive file. When you have an Xcode project or playground that you need to share, Gist provides a great intermediate service alternative to iCloud or Dropbox. If you need privacy, use the “secret gist”  button when creating the gist.

This isn’t, of course, the end of what you can do with gists. Because each gist is a git repository, you can perform all the same commands you would in any git repo. Gist, of course, has a limited interface, so you won’t be able to, for example, switch between branches from the gist website. On the other hand, you can perform other tasks that don’t depend on a GitHub UI like listing diffs:

% git diff cb9271da5070f11602d3ab436a05fb9705409fd2
diff --git a/raw.md b/raw.md
index 8ed7306..ea4b5ed 100644
--- a/raw.md
+++ b/raw.md
@@ -104,9 +104,8 @@ Escaping hinders readability and interferes with inspection, especially in the l
 
 ### Candidates
 
-A good candidate for raw strings:
+A good candidate for raw strings is non-trivial and is burdened by escaping because it:
 
-* Is non-trivial.
 * Is obscured by escaping. Escaping actively harms code review and validation.
 * Is already escaped. Escaped material should not be pre-interpreted by the compiler.
 * Requires easy transport between source and code in both directions, whether for testing or just updating source.

I think GitHub’s gists are pretty awesome. And now, at least for me, they’ve gone from handy but mindless pastes to something really special.

Do you have a special way to use gists? I’d love to hear about unconventional ways to use this utility site to push boundaries and introduce new functionality. Drop a comment or an email and let me know.

Creating a low-cost ADD/ADHD refocusing band

My middle child recently had an evaluation regarding her processing and retention in reading. She has diagnosed ADD, and aside from the specific results of her tests, the specialist recommended we look for and purchase a device that would buzz her wrist at regular intervals when doing homework and reading. The goal is to refocus when one is easily distractable. It’s a bit like Apple’s “stand up and move” reminder.

However, when we headed over to Amazon, we were met by two realities. First, these things cost a lot. Second, they have terrible reviews. It occurred to me that I could probably put together a band with things we already had around the house.

Last summer, I did a little work exploring BLE, the low energy form of Bluetooth that works with iOS. My test platform was the 1st generation Mi Band step tracker, which I purchased for under $20. The second generation is similar and appears to cost under $30. (Here’s a link to the first gen model, which is sold by third parties.)

My build consisted of the following components:

  • I built a basic single-view app and added a single centered segmented control. The control specifies the time-out interval, which is simply a standard Timer.
  • When the Timer activates, it uses my Bluetooth helper type to write a single byte to the band, which causes it to buzz. It’s slightly more complicated than that because the code needs to scan for the device, discover it, stop scanning, and then write to the peripheral, but that’s all covered in my previous posts.
  • To keep the app running longer than 3 or 10 minutes in the background, I resort to the standard “play a silent wav file over and over”. I based my code on this simple github repo, which handles audio interrupts and restarts.
  • I added every background mode I thought potentially applicable: plays audio/video streams, uses CoreBluetooth, provides VoIP services. I could probably have dropped the first one but it does no harm so I kept it.

Testing was, as you’d expect, a bit tedious, especially trying to figure out whether I had beaten the automatic time out (which is why went from my custom code to the github version for keeping alive). I put the pedometer portion on an empty diet coke can, to make it buzz a lot louder without having to wear the band.

In the end for a few hours of my time and under $20 capital investment, I ended up with a handy little tool. My daughter has only been using it a few days so it’s still too early to see whether the refocusing component is actually effective.

If you want to give this a try, I’ve put up gists for the primary view controller and the bluetooth controller. You can grab the background handler from the above github  repo link. I didn’t bother cleaning up any of my code, so it is what it is, which is a working prototype. Don’t forget to add the background modes to your Info.plist.

If you build this yourself (or just intend to try it out), drop me a note or a comment and let me know how it goes.

Building automatic `OptionSet` entries

Last night Zev Eisenberg was asking about option sets. “Do you still have to specify 1 << _n_ manually for OptionSet conformance? There’s no magic?” So I decided to build him some magic. There’s really no reason you should have to manually put in numbers like this:

public struct Traits: OptionSet {
    public typealias RawValue = Int
    public var rawValue = 0
    public init(rawValue: Traits.RawValue) {
        self.rawValue = rawValue
    }
    
    public static let bolded = 1 << 0
    public static let italicized = 1 << 1
    public static let monospaced = 1 << 2
    public static let underlined = 1 << 3
    public static let outlined = 1 << 4
}

This approach requires unnecessary bookkeeping. You have to keep track of the bits you’ve used, especially if you add or insert new options, or reorder the options  you have. It gives unnecessary prominence to the implementation detail values. There should be a more magic way.

So I decided to write him a solution that automatically generated the bit flags and hid those details from the implementation. The result looks like this:

 public static let bolded = generateOption()
 public static let italicized = generateOption()
 public static let monospaced = generateOption()
 public static let underlined = generateOption()
 public static let outlined = generateOption()

You can move things around, add new items, delete old items. It really doesn’t make a difference from a code maintenance point of view (assuming you’re doing this all during development, not after deployment, where you’d want to use availability and deprecations).

To get here, I needed to create a function that would add type-specific options to any type that conforms to OptionSet. I created a global dictionary to store option counts:

private var _optionSetDict: [AnyHashable: Int] = [:]

To be honest, I hate unparented globals like this. However, Swift does not allow adding static stored values in extensions. I couldn’t think of another better way to handle this. I also built a second global to ensure this dictionary would prevent concurrent access, so my counts would be secure:

private var _optionSetAccessQueue = DispatchQueue(
    label: "sadun.OptionSetGeneration", attributes: [.concurrent])

I needed to box my type references since Swift doesn’t allow types to conform to Hashable. They won’t work out of the box with dictionaries. This solution let me use types as keys:

/// Wraps a type to enable it for use as a dictionary key
public struct TypeWrapper<Wrapped>: Hashable {
    private let type: Wrapped.Type
    public init(_ type: Wrapped.Type) {
        self.type = type
    }
    
    /// Conforms to Equatable
    public static func ==(lhs: TypeWrapper, rhs: TypeWrapper) -> Bool {
        return lhs.type == rhs.type
    }
    
    /// Conforms to Hashable
    public var hashValue: Int {
        return ObjectIdentifier(type).hashValue
    }
}

To create a hashable type entry, I just instantiate TypeWrapper with the type.

Sven Weidauer points out I can use ObjectIdentifier directly
Here’s the OptionSet extension that implements the generateOption() magic:

public extension OptionSet where RawValue == Int {
    public static func generateOption() -> Self { 
        let key = ObjectIdentifier(Self.self)
        return _optionSetAccessQueue.sync(flags: [.barrier]) {
            // This should be locked so there's a guarantee that
            // counts are unique for each generated option
            let count = _optionSetDict[key, default: 0]
            _optionSetDict[key] = count + 1
            return .init(rawValue: 1 << count)
        }
    }
}

I’m not sure that I’d ever actually use this approach in code but it was a fun exercise in problem solving. Sven W. adds “Another thing to keep in mind is that statics are initialised the first time they are used. So in different runs of the program the values can differ. Better not persist OptionSets created by this technique.”

You can see the full implementation over at Github. And if you’re curious, you can go back through the change history to see some earlier takes on the problem.

Like it? Hate it? Got suggestions and improvements? (I always mess something up, so there’s a pretty much 100% chance there’s room for improvement.) Drop a note, a tweet, an email, or a comment.

Thanks to Ian Keen, who suggested extending OptionSet directly.

Fixing Mail Plugins for High Sierra

Are all your flagged emails back? Do you want them gone? Mail plugins help make macOS mail a bit less awful. If your Mail bundles stopped working, it’s not hard to get them back up and running. The secret lies in adding a `Supported10.13PluginCompatibilityUUIDs` entry in to any mail plugin bundle’s internal Info.plist file. You can then move the plugin back from the disabled folder to the main plugin folder.

The overall technique changed in 10.12, requiring a separate entry field that’s OS-specific. You can pull the current compatibility key by issuing:

defaults read /Applications/Mail.app/Contents/Info.plist PluginCompatibilityUUID

This reads the compatibility UUID and prints it to the terminal command line. You need this UUID to edit the Info.plist file. Starting in 10.12, you need to use the XX.YY format as part of the key name. For example, here’s what the 10.13 version looks like:

<key>Supported10.13PluginCompatibilityUUIDs</key>
<array>
	<string>CompatibilityKeyHere</string>
</array>

Once you’ve done this, move the bundle back to the right folder and restart mail. The bundle should hopefully continue working.

Bluetooth Lessons I: Manager and Scanning

Last June, Izzy inspired me to do something with Bluetooth and playgrounds but honestly, I haven’t had the time and I couldn’t afford a Sphereo. I’ve wrapped up Swift Style. Attempting to write meaningfully about drawing while the Denver Public School system has for reasons I cannot begin to comprehend released my child to my recognizance for two entire weeks seems unlikely. (Another child has half days. Fun.)

To prepare, I purchased one of the cheapest BLE devices I could find, a Mi wristband (Amazon cost under $20 shipped), which has a reverse engineered API that lets you control vibration. A friend of mine just purchased the hugely expensive Buzzies for Autism bands. I’m  hoping I can mimic some of that functionality with a playground, a low-rent BLE device, and a full-price child.

Have I mentioned recently how awesome playgrounds are for playing around with and learning about new tech? They really are, especially because you can integrate just one concept at a time, and then test it live before expanding to the next.

I decided to go with Cocoa for my BLE exploration instead of iOS, although the tech is more or less the same on both platforms. When you work in Cocoa, using a macOS playground, the startup speed is phenomenal because you don’t have to work with a simulator.

My first project simply sets up a central manager (CBCentralManager), monitors its state, and lists any devices it finds. I’m pretty happy for this as a first day, not many hours to spend on it, playing around and doing something marginally useful result.

The CoreBluetooth documentation is pretty dire. For example, this is the Swift docs for CBManagerStatePoweredOn.  After SE-0005, the constant is actually .poweredOn, as you see in the following sample code, not CBManagerStatePoweredOn. And there’s no documentation in that documentation.

Nonetheless, I persevered and my first child-full day produced a basic helper class. You really need to work in NSObject land for this because of all the delegation. So I set up an objc-friendly class, set it as a manager delegate, and implemented the one required callback method, which follows the manger state.

Try sticking the Bluetooth icon in your system menu bar.  (System Preferences > Bluetooth > Show Bluetooth in menu bar.) It’s a lot of fun to toggle it on and off and watch your playground keep tabs on that state.

Next, I added a basic peripheral scan. You need to scan only when the manager achieves poweredOn state.

Apple writes, “Before you call CBCentralManager methods, the state of the central manager object must be powered on, as indicated by the CBCentralManagerStatePoweredOn constant. This state indicates that the central device (your iPhone or iPad, for instance) supports Bluetooth low energy and that Bluetooth is on and available to use.”

That’s why I added the scan to the playground’s “update state” callback. You’ll want to stop scans when the BLE powers off.

Finally, I implemented one more callback, which asynchronously lists discovered peripherals. It picked up nicely on my Apple TV and when I enabled and disabled a hotspot on my iPhone. Great fun.

Here’s the code involved. You can see how very short it is. The struggle wasn’t in the lines of code or complexity, it’s mostly about how very badly documented most everything seems to be.

I’ll post more as time allows.

https://gist.github.com/erica/d249ff13aec353e8a8d72a1f5e77d3f8