Mac Protip: Open URL in Browser

Not all browsers have an "Open in" service. I tend to use Chromium for media so it's a different crashy app than my main Safari. I've been manually copying URLs, pasting into it.

Open Automator, create a new Quick Action, pick Run Shell Script, paste in:

read -r url
open -a Chromium "$url"

Save it as "Open URL in Chromium". Quit Automator.

You can now right-click on any URL, Services menu, and send it there.

Old Man in the Woods Way of Argument Parsing

So, my premise is that only developers use command lines anymore. And after years of corporate enslavement, it's nice to run off to the woods, make a log cabin and all your tools yourself.

Therefore the best way to parse arguments in Scheme is:

(define debug )
(define outfile )
(define infiles '())

(define (main argv)
  (set! debug (if (member "--debug" argv)  ))  ;; boolean
  (set! outfile (if (member "--out" argv) (cadr (member "--out" argv)) ))  ;; key-value
  (set! infiles (if (member "--" argv) (cdr (member "--" argv)) ))  ;; all after --
  (unless outfile (error 'main "No outfile given")) ;; maybe show a whole usage & exit
)

This has some disadvantages. It's only discoverable by reading docs or even code, and you have to write the docs yourself. If you want short args, you have to duplicate lines and maybe set the arg twice.

But it's trivial to set up, you can't really get it wrong, and the amount of effort is appropriate to a developer interface.

(You might complain I'm using globals, you can just change those to let)

For the young over-engineering crowd, there are a variety of arg parsing libraries. And I'm too lazy to demonstrate each of them. But in the set of generates usage, is easy to use, and you'll be able to remember how it works in a year, they all get maybe 1, and need to be 3.

Gone from Suck to Blow

Want to move a URL or other text between your local computers, and they're not all Mac/iOS where universal pasteboard mostly works? There's smart ways, and then there's how I do it:

# note: needs Apache turned on. sudo apachectl start
mac% cd /Library/WebServer/Documents
mac% sudo ln -s $HOME/Sites
mac% cd
mac% cat bin/blow
#!/bin/zsh
pbpaste >$HOME/Sites/suckblow.txt

raspi% sudo apt-get xclip
…
raspi% cat bin/suck
#!/bin/zsh
curl -s "http://mac.local/Sites/suckblow.txt" |xclip -i
xclip -o

And in the reverse set, pbcopy is the Mac equivalent of xclip -i. In practice, I don't run a server on my RasPi but I rarely need to paste the other way, just sometimes scp files.

Now on the Mac, I copy some text, type "blow" in iTerm2. On the RasPi, I grab terminal and type "suck". It can take a few seconds, and then the text is in clipboard.

Without running Apache (or other web server, but I'm a caveman), you can use scp to grab the file, then cat it into xclip -i.

Happy blowing & sucking!

Internet Archive Favorites

Part of my workflow with Internet Archive is to favorite things I go back to a lot. But the fav page there is nigh-unusable, it lists in order from most recent fave to oldest, including duplicates (Huh?), and even sorting by title doesn't put related things together. So I made a tool, and generated
Internet Archive Favorites which I'll update every so often.

My first attempt was simply scraping an RSS feed, but they only publish the last 50 faves! Bogus! Even if I cached them, I'd still have to check it often and reorganize things. Then I learned they have a developer interface, usable with an ia script or right from Python, which is more useful. It's slow without caching, but after first run it's very fast, mostly 1 API call.

Read the docs at the top of the script, look at the example config file (almost a Markdown outline, but I do some clever/stupid things in it). As usual license is BSD, an ye harm none, do what thou wilt shall be the whole of the law.

Now all I have to do is write a cfg file:

The stuff I've found that I like on [Internet Archive](https://archive.org), loosely sorted.

## Retrocomputing

+ Basic_Computer_Games_Microcomputer_Edition_1978_Creative_Computing
+ More_BASIC_Computer_Games
+ Basic_Computer_Adventures_1986_MS_Press
+ Best_of_Creative_Computing_Vol_1_1978_Creative_Computing_Press
+ creativecomputing
…

% archive-fav-extract.py -q mdhughes

And it makes a nice html file, tells me about any errors, and I paste the output file into a wordpress page.

A Computer is Like a Violin

LATITUDE OF EXPRESSION AND SPECIFICITY OF IDEAS

Finally we come to the question of what to do when we want to write a program but our idea of what is to be done, or how to do it, is incompletely specified. The non sequitur that put everyone off about this problem is very simple:

Major Premise: If I write a program it will do something particular, for every program does something definite.
Minor Premise: My idea is vague. I don't have any particular result in mind.
Conclusion: Ergo, the program won't do what I want.

So, everyone thinks, programs aren't expressive of vague ideas.

There are really two fallacies. First, it isn't enough to say that one doesn't have a particular result in mind. Instead, one has an (ill-defined) range of acceptable performances, and would be delighted if the machine's performance lies in the range. The wider the range, then, the wider is one's latitude in specifying the program. This isn't necessarily nullified, even when one writes down particular words or instructions, for one is still free to regard that program as an instance. In this sense, one could consider a particular written-down story as an instance of the concept that still may remain indefinite in the author's mind.
This may sound like an evasion, and in part it is. The second fallacy turns around the assertion that I have to write down a particular process. In each domain of uncertainty 1 am at liberty to specify (instead of particular procedures) procedure-generators, selection rules, courts of advice concerning choices, etc. So the behavior can have wide ranges-it need never twice follow the same lines, it can be made to cover roughly the same latitude of tolerance that lies in the author's mind.

At this point there might be a final objection: does it lie exactly over this range? Remember, I'm not saying that programming is an easy way to express poorly defined ideas! To take advantage of the unsurpassed flexibility of this medium requires tremendous skill-technical, intellectual, and esthetic. To constrain the behavior of a program precisely to a range may be very hard, just as a writer will need some skill to express just a certain degree of ambiguity. A computer is like a violin. You can imagine a novice trying first a phonograph and then a violin. The latter, he says, sounds terrible. That is the argument we have heard from our humanists and most of our computer scientists. Computer programs are good, they say, for particular purposes, but they aren't flexible. Neither is a violin, or a typewriter, until you learn how to use it.

Printing Code Like It's 1989

Which made me laugh, the 2016-2021 dialogue and still not implemented feature in that bug report is everything you love about Microsoft-controlled "open source but not really".

I've only ever printed code from BBEdit in the last couple decades, let's see how other editors fare:

  • BBEdit: Perfect. Print command in menu (it's a native Mac app!), prints headers, page & line numbers, nice margins. Looks perfect, like an old-timey print job. I should probably have switched from dark to light theme, but it'd be fine. ★★★★★
  • Geany: Print command, several settings in Preferences, prints headers, page & line numbers (in settings, choose either headers or page numbers at bottom, or it'll be duplicated). Hideous non-native GTK app and dialogs, but does the job perfectly well. ★★★★½
  • MacVim: Print command, produces a postscript file which immediately opens in Preview, spins a while, converts to PDF. Default has no line numbers, add set popt=header:2,number:y to your .vimrc. ★★★½☆
  • Atom: No print command in the menu, but there's multiple packages 5-6 years old, they work fine. Only shows line numbers, bad margins, it's literally a web page being printed. ★★★☆☆
  • Sublime Text: Print command, but no settings (in the horrible JSON text editor where you do settings) to make it nice. Literally dumps a web page, no page or line numbers. What's the least you could do and check off the feature? That's how subl does everything. ★★☆☆☆
  • Xcode: Takes forever to start. Prints in grayscale, which is nice, but no headers or line numbers. Fail. ★★☆☆☆
  • Panic Nova: … My demo expired, and cleaning the configs out doesn't let me try again, so who knows. Can't even give them a second try without paying $100. Trashed.

I'd be interested in seeing how others fare, and on non-Mac platforms if they have any consistent print command.

Here's a zip of PDF prints - maze2.py is a silly Python program, but at just 3 pages it's a good test case.

Mac Icons for PDFs

I have a great many folders of PDFs, mostly grabbed from archive.org magazine_rack, ataribooks, etc. The trouble is when I open a folder of these, Finder makes preview icons for a few of them, then gives up and they all show a generic "PDF" icon. What I want is a persistent icon for the first page!

First, you need osxutils:

% sudo port install osxutils
% man seticon

And my icontool.

sips (Scriptable Image Processing System) is a built-in tool on the Mac, incredibly powerful image converter. I'm not gonna do anything fancy with perspective ratios or padding, just use it to get an image.

Now create pdficonset.zsh:

#!/bin/zsh
export CG_PDF_VERBOSE=1
find . -type f -d -iname "*.pdf" |while read -r f; do
    echo $f
    sips -s format png $f -o thumb.png && \
    icontool.zsh thumb.png thumb.icns && \
    seticon -d thumb.icns $f
    rm -f thumb.png thumb.icns
done

Run it in the parent directory, and boom! All nice icons.

[update: added a little better error-safety. CG_PDF_VERBOSE just gives better but still not useful error messages.]

I don't have the problem as bad with CBZ/CBR comics; they'd be trivial to extract the first page from, since they're just ZIP/BZIP files.

Formatting Strings in Scheme

Most of the time I use primitive display, or print functions:

;; displays a series of args to stdout
(define (print . args) (for-each display args) (flush-output-port) )

;; displays a series of args to stdout, then newline
(define (println . args) (for-each display args) (newline) (flush-output-port) )

;; displays a series of args to given port
(define (fprint port . args) (for-each (λ (x) (display x port)) args) (flush-output-port port) )

;; displays a series of args to given port, then newline
(define (fprintln port . args) (for-each (λ (x) (display x port)) args) (newline port) (flush-output-port port) )

;; displays a series of args to stderr, then newline
(define (errprintln . args)  (let [ (port (current-error-port)) ]
    (for-each (λ (x) (display x port)) args) (newline port) (flush-output-port port)
))

but sometimes I actually need to format things:

(import (prefix (srfi s19 time) tm: ))

(format  "~8a ~10:d ~20a" name score
    (tm:date->string (tm:current-date) "~Y-~m-~d ~H:~M:~S") )

Common Lisp format works as described in Chez Scheme, using /#f for destination, and some other Schemes as well; but most Schemes only have the nearly-useless SRFI 28. I'm aware of cat/fox/etc combinatorial formatters, but they're very verbose.

Chez also has date/time functions, but no formatter, so using SRFI 19 - nicely, SRFI 19 mostly does sane things, it's not like C's strftime.

Gambit hits a mark

(see, the X-Men Gambit has perfect aim and a stupid accent, which still makes him more interesting than Hawkeye; and of course I'm Mark and so is Marc)

With much appreciated help from Marc Feeley, got maintest running.

A couple of lessons: I very much think include paths should include the path of the main source doing the including. Chibi's default is correct, Gambit's default is wrong and requires fixing in every user program. It's "more secure", but if you're running source code from a directory, you can probably trust whatever else is in that dir.

main was frustrating: Gambit manual 2.6 (highlighting mine)

After the script is loaded the procedure main is called with the command line arguments. The way this is done depends on the language specifying token. For scheme-r4rs, scheme-r5rs, scheme-ieee-1178-1990, and scheme-srfi-0, the main procedure is called with the equivalent of (main (cdr (command-line))) and main is expected to return a process exit status code in the range 0 to 255. This conforms to the “Running Scheme Scripts on Unix SRFI” (SRFI 22). For gsi-script and six-script the main procedure is called with the equivalent of (apply main (cdr (command-line))) and the process exit status code is 0 (main’s result is ignored). The Gambit system has a predefined main procedure which accepts any number of arguments and returns 0, so it is perfectly valid for a script to not define main and to do all its processing with top-level expressions (examples are given in the next section).

So your code that looks fine with 1 arg will break with 2, depending on the version. (main . argv) works. I'm in the process of making sure every one of my maintests parses args consistently, and every Scheme disagrees.

Gambit's compiler worked very simply once I got the library on the command line; it doesn't seek out & include them the way Chez does, even though it takes what looks like a search path.

The upside of all this is at least now there's one maintained, fast, R7-compatible Scheme compiler. I'm sticking with Chez (R6) for my code, but it's nice having something 100x faster (gut feeling, not benchmarked) than Chibi to test R7 code on.

Gambit is a risky scheme

(puns about "scheme" names are mandatory)

Neat: New version 4.9.4 of Gambit Scheme is out and they have a web site again after like 3 years.

OK: So I start adapting my little module/how do you run example: here

Bad: Not only does the R7 library system not work, their version of this hello example
will load code from fucking github at runtime! NPM viruses & sabotage are baked into the system. See Modules in Gambit at 30

SIGH.