I know I've been high-density-posting. I'm like a shark. I can't get enough gopher. I wrote a great gopher browser, that you don't even need to have to use. This tutorial is openbsd based. Your unix-like OS is probably similar. If you can adapt it to your OS I will link to you. Dependency: Embeddable common lisp "ecl". The heir to Austin-Kyoto lisp. '''ksh pkg_add ecl ''' On obscure operating systems you might need to install openbsd-netcat. Let me know if it gives you trouble. Let's look at sdf.org using netcat. This is all that ever needs to happen. '''ksh printf "\n" | nc sdf.org 70 ''' But let's use ECL to make this a little easier. First, let's get granfalloon.lisp with our gopher browser. '''ksh printf "/users/screwtape/common-lisp/granfalloon.lisp\n" | nc sdf.org 70 > granfalloon.lisp ''' For security's sake, let's check the sha256. '''ksh sha256 granfalloon.lisp ''' I got >SHA256 (granfalloon.lisp) = 29ed96185066718932fc9b44b36d1239bfa642581f3eaf5f48f91c32931ef5e7 At this juncture you should peruse the 85 lines of code to your content. Let's fire her up. '''ksh ecl --load granfalloon.lisp ''' GETTING OUT OF ECL GRACEFULLY: If something has gone wrong, at the prompt normally you can :r1<enter> (or whatever number 'restart toplevel' is. If you want to get out to restart whereever do Ctrl+c, and instead of restarting type (si:quit)<enter>. (which is quit generally in ECL). Lisp is designed not for randomly quitting and restarting though. Now the default in lisp is to send lines of input to your running program. This is called a REPL. Something like this will be waiting for you: '''"ksh" but actually ecl. > ''' Let's enter the granfalloon package so we don't have to specify it all the time. '''ecl >(in-package gfln) ''' I bet I'm going to regret leaving the r out of that nickname. You can type granfalloon there if you want to. Now lets browse sdf.org again, and then look at the tools we have for it. '''ecl granfalloon>(bind-nl) ''' I got #<bytecompiled-closure #<bytecompiled-function READLINE-STAT 0x1eb5f2b050>> GRANFALLOON>. The bind-nl function has bound a closure that returns the next line of the gopher item, or nil if none is available. Let's get all the lines available with granfalloon's function, nconc-nls '''ecl granfalloon> (nconc-nls) ''' That's a bit more like it! Let's store it in a lisp variable, and use a macro that hides it from being printed. (-q &body) is gfln's macro that just hides output. (setq *lines* (form)) is lisp's way of making *lines* refer to the result of (forms). It will work whether you feel a deep understanding or not. We have to make a new connection, since we consumed all the lines before. That's okay. '''ecl granfalloon> (bind-nl) granfalloon> (-q (setq *lines* (nconc-nls))) granfalloon> *lines* ''' Yep, they're all there... In lisp we like to put *earmuffs* on global variables. Now we have the *lines* stored so we don't need to download them again. Let's print them plainly like a shell would using gfln:plain '''ecl granfalloon> (plain *lines*) ''' It's about time we started beating the competition, right? (Sez me.). Let's use common lisp's subseq to plainly display a subseq of those lines. '''ecl granfalloon> (plain (subseq *lines* 0 5)) ''' This gophermap is structured a little funny, but we are seeing what's really there. '''output Welcome to the SDF Public Access UNIX System .. est. 1987 null.host 1 i null.host 1 iOfficial Site of the Internet Gopher Club Underground Syndicate null.host 1 i null.host 1 iWe offer FREE and inexpensive memberships for people interested null.host 1 ''' Wandering around subseqs of the gophermap like this is the intended browsing. Let's be a little tricky and use common lisp to number the lines we print. '''ecl granfalloon> (plain (loop for x from 1 for line in *lines* collecting (cons x line))) ''' Using common lisp is an advantage of common lisp. Don't worry about the fact that that line works though. Line 14 looks good. We can print it in particular like this '''ecl granfalloon> (nth (- 14 1) *lines*) ''' nth indexes from 0 :-(. Lisps use prefix notation. Annoyingly, plain needs non-lists to be templated into lists, using backtick here. Don't worry about it. '''ecl granfalloon> (plain `(,(nth 13 *lines*))) ''' Let's go somewhere new. First, let's save sdf.org for later in *sdf.org* (quietly again). '''ecl (-q (setq *sdf.org* *lines*)) ''' Now *sdf.org* can be used anywhere and anyhow we used *lines*. Let's go to that /phlogs/ item specifier in 13. We're just going to type, since the point of granfalloon isn't directly to become lisp wizards. Quietly, since (length */phlogs/*) yields 371. '''ecl granfalloon> (bind-nl :spec "/phlogs/") granfalloon> (-q (setq */phlogs/* (nconc-nls))) granfalloon> (plain (subseq */phlogs/* 0 11)) ''' That's a fancy gopher.club banner. Last active phlog listings begin at line 12 (the 13th line) '''ecl granfalloon> (plain (subseq */phlogs/* 12 16)) ''' Let's talk about copying and pasting into an xterm using a mouse, since lots of people do that. First, let's start typing our command up until after the first double-quote. '''ecl granfalloon> (bind-nl :spec " ''' We could keep typing, but if you use a mouse it can be convenient to "sweep out" long item specifiers. To do that, you press down left click on the first letter you want, and drag it right along the line to the last letter you want. I went for /users/screwtape/. While it's highlighted in xterm, middle clicking the mouse will drop it at the cursor in the terminal/ecl. <one middle click later> '''ecl granfalloon> (bind-nl :spec "/users/screwtape/ ''' Same old... '''ecl granfalloon> (bind-nl :spec "/users/screwtape/") granfalloon> (-q (setq *screwtape* (nconc-nls))) granfalloon> (plain (subseq *screwtape* 0 20)) ''' Let's look at an item-type 0 text file (we've been looking at item-type 1, directories). They are both text files. >0Wed Feb 16 ;sam ple ed iting /users/screwtape/tmp.upObQMkxa1 sdf.org 70 '''ecl granfalloon> (bind-nl :spec "/users/screwtape/tmp.upObQMkxa1") granfalloon> (-q (setq *sam-d.txt* (nconc-nls))) granfalloon> (plain (subseq *sam-d.txt* 0 5)) ''' Let's wrap this up by visiting somewhere outside of sdf.org. I didn't look at jns's homegopher yet. He links it in his gophermap. We could look manually via (plain */phlogs/*) or creeping through using subseq, but I *am* a lisp wizard. '''ecl granfalloon> (find "jns" */phlogs/* :test 'search) granfalloon> (bind-nl :spec "/users/jns/") granfalloon> (-q (setq *jns* (nconc-nls))) granfalloon> (remove-if (lambda (x) (search "sdf.org" x)) *jns*) granfalloon> (bind-nl :address "gopher.linkerror.com" :spec "/phlog/2021") ''' Neat. You know, I sent him an email in which I had a deep misimpression of what termcap(3) was talking about. Well, that was a bit of a journey! Maybe we'll do binary file formats like sound and images next time after I post my rich media with C lisp. screwtape Postscript The pun I'm making is that gopher doesn't need a browser. So a gopher browser would be an absent browser. Say what you will, no 20+ hour builds. Further our (absent) gopher browser is the only browser featured in the books of bokonon. >If you want want to inspect a granfalloon, remove the skin from a toy balloon. #[!got log#] ----------------------------------------------- commit 560a6a0c7efa092c509466304db9d10a058144d9 from: <bokonon@san.lorenzo> date: Fri Feb 18 05:06:07 2022 UTC When bokonon was asked about his social media, he listed his sdf.org phlog. The people of san lorenzo panicked about needing to build a new browser. Bokonon explained that the browser they needed didn't need to exist but also wouldn't support gemini protocol.