i 	/	gopher.someodd.zip	70
iWaffle: Gopher Client	/	gopher.someodd.zip	70
i 	/	gopher.someodd.zip	70
i __      __          __    __   _         _ 	/	gopher.someodd.zip	70
i \ \    / /  __ _   / _|  / _| | |  ___  (_)	/	gopher.someodd.zip	70
i  \ \/\/ /  / _` | |  _| |  _| | | / -_)  _ 	/	gopher.someodd.zip	70
i   \_/\_/   \__,_| |_|   |_|   |_| \___| (_)	/	gopher.someodd.zip	70
i                                            	/	gopher.someodd.zip	70
i 	/	gopher.someodd.zip	70
i   ___                _                	/	gopher.someodd.zip	70
i  / __|  ___   _ __  | |_    ___   _ _ 	/	gopher.someodd.zip	70
i | (_ | / _ \ | '_ \ | ' \  / -_) | '_|	/	gopher.someodd.zip	70
i  \___| \___/ | .__/ |_||_| \___| |_|  	/	gopher.someodd.zip	70
i              |_|                      	/	gopher.someodd.zip	70
i 	/	gopher.someodd.zip	70
i   ___   _   _                _   	/	gopher.someodd.zip	70
i  / __| | | (_)  ___   _ _   | |_ 	/	gopher.someodd.zip	70
i | (__  | | | | / -_) | ' \  |  _|	/	gopher.someodd.zip	70
i  \___| |_| |_| \___| |_||_|  \__|	/	gopher.someodd.zip	70
i                                  	/	gopher.someodd.zip	70
i 	/	gopher.someodd.zip	70
i╔─*──*──*──*──*──*──*──*──*──*──*──*──*──*──*──*──*──*──*──╗	/	gopher.someodd.zip	70
i║1   ..................................................   1║	/	gopher.someodd.zip	70
i║2*  ..................................................  *2║	/	gopher.someodd.zip	70
i║3   ..................................................   3║	/	gopher.someodd.zip	70
i║1   ................Posted: 2024-03-10................   1║	/	gopher.someodd.zip	70
i║2*  Tags: nix gopher haskell debian showcase my_warez   *2║	/	gopher.someodd.zip	70
i║3   ..................................................   3║	/	gopher.someodd.zip	70
i║1   ..................................................   1║	/	gopher.someodd.zip	70
i╚──────────────────────────────────────────────────────────╝	/	gopher.someodd.zip	70
i 	/	gopher.someodd.zip	70
iI made a Gopher client called waffle[1]. Basically, a web browser, but for the	/	gopher.someodd.zip	70
iGopher Protocol. I started it about 4 years ago now (2020?).	/	gopher.someodd.zip	70
i 	/	gopher.someodd.zip	70
iA truly big, complicated beast of a project for me.	/	gopher.someodd.zip	70
i 	/	gopher.someodd.zip	70
iI feel like this project touched upon some core areas:	/	gopher.someodd.zip	70
i 	/	gopher.someodd.zip	70
i* Rapid acceleration of my Haskell proficiency	/	gopher.someodd.zip	70
i* Ability to read and implement RFCs (namely RFC 1436[2])	/	gopher.someodd.zip	70
i* Network programming	/	gopher.someodd.zip	70
i* Concurrency	/	gopher.someodd.zip	70
i* TUI (I used bricks, I think)	/	gopher.someodd.zip	70
i* Large project, requiring careful architecture and separation of concerns	/	gopher.someodd.zip	70
i 	/	gopher.someodd.zip	70
iSome other highlights:	/	gopher.someodd.zip	70
i 	/	gopher.someodd.zip	70
i* I got to talk to one of the people who worked on the Gopher Protocol (P.	/	gopher.someodd.zip	70
i  Lindner).	/	gopher.someodd.zip	70
i* Caching system	/	gopher.someodd.zip	70
i* Titlebar thing	/	gopher.someodd.zip	70
i* Search	/	gopher.someodd.zip	70
i* Profiling for efficiency, working with someone who made a tool I used to	/	gopher.someodd.zip	70
i  analyze profiler output	/	gopher.someodd.zip	70
i* Bookmarks	/	gopher.someodd.zip	70
i* Color	/	gopher.someodd.zip	70
i* UTF-8 support, emojis	/	gopher.someodd.zip	70
i* Just using Cabal, basically, not Stack	/	gopher.someodd.zip	70
i* Build/run with nix!	/	gopher.someodd.zip	70
i 	/	gopher.someodd.zip	70
i## Examples of Waffle	/	gopher.someodd.zip	70
i 	/	gopher.someodd.zip	70
iI believe Waffle has an INI file for customization:	/	gopher.someodd.zip	70
i 	/	gopher.someodd.zip	70
gWaffle style configuration	/assets/showcase/waffle/waffle-customization.gif	gopher.someodd.zip	70
i 	/	gopher.someodd.zip	70
iPlaying a hacking game in Waffle on mozz.us:	/	gopher.someodd.zip	70
i 	/	gopher.someodd.zip	70
gPlaying a hacking game with Waffle	/assets/showcase/waffle/waffle-playing-hacking-game.gif	gopher.someodd.zip	70
i 	/	gopher.someodd.zip	70
iWaffle playing solitaire:	/	gopher.someodd.zip	70
i 	/	gopher.someodd.zip	70
IWaffle playing solitaire	/assets/showcase/waffle/waffle-playing-solitaire.png	gopher.someodd.zip	70
i 	/	gopher.someodd.zip	70
iWaffle playing Zork:	/	gopher.someodd.zip	70
i 	/	gopher.someodd.zip	70
IWaffle playing Zork	/assets/showcase/waffle/waffle-playing-zork.png	gopher.someodd.zip	70
i 	/	gopher.someodd.zip	70
iAn example of Waffle's search and refresh abilities (also kind of showing off	/	gopher.someodd.zip	70
icaching):	/	gopher.someodd.zip	70
i 	/	gopher.someodd.zip	70
gSearch and refresh in Waffle	/assets/showcase/waffle/waffle-search-and-refresh-quotes.gif	gopher.someodd.zip	70
i 	/	gopher.someodd.zip	70
i## The development journey	/	gopher.someodd.zip	70
i 	/	gopher.someodd.zip	70
iI found it very satisfying to create a project based around reading an RFC and	/	gopher.someodd.zip	70
iother research.	/	gopher.someodd.zip	70
i 	/	gopher.someodd.zip	70
i### Gopher: High Level	/	gopher.someodd.zip	70
i 	/	gopher.someodd.zip	70
iThe Gopher client, hell, even the Gopher Protocol in general, is very	/	gopher.someodd.zip	70
ilines-based. As in everything is just composed by a serires of lines, it can	/	gopher.someodd.zip	70
ifeel.	/	gopher.someodd.zip	70
i 	/	gopher.someodd.zip	70
iI believe the Gopher Protocol was designed around being simple, easy to hack	/	gopher.someodd.zip	70
ithings together for.	/	gopher.someodd.zip	70
i 	/	gopher.someodd.zip	70
iYou browse Gopherspace using menus. Menus are basically a bunch of lines that	/	gopher.someodd.zip	70
iindicate some kind of option you can select to do something, like go to a new	/	gopher.someodd.zip	70
idirectory, download a file, or start a query.	/	gopher.someodd.zip	70
i 	/	gopher.someodd.zip	70
iThe menus themselves are simple text files that are primarily tab-delimited,	/	gopher.someodd.zip	70
ilooking like this:	/	gopher.someodd.zip	70
i 	/	gopher.someodd.zip	70
i```	/	gopher.someodd.zip	70
1About Gopher	/about	gopher.server.com	70
0What's New!	/whatsnew.txt	gopher.server.com	70
1Gopher Clients	/clients	gopher.server.com	70
1Search Gopher Space	/search	gopher.server.com	70
0Gopher Protocol Specification	/rfc1436.txt	gopher.server.com	70
iThis is an information line		fake	0
1Fun & Games	/fun	gopher.server.com	70
1University Information	/university	gopher.server.com	70
1Libraries around the World	/libraries	gopher.server.com	70
7Search a gopherhole	/search	gopher.example.org	70
i.	/	gopher.someodd.zip	70
i```	/	gopher.someodd.zip	70
i 	/	gopher.someodd.zip	70
iAs you can see these menus are pretty easy to parse. Each line represents	/	gopher.someodd.zip	70
isomething actionable. For example if the first line is selected in the client,	/	gopher.someodd.zip	70
iwe know the following information:	/	gopher.someodd.zip	70
i 	/	gopher.someodd.zip	70
i* Represent the line as `About Gopher` to the user/client	/	gopher.someodd.zip	70
i* The line intends to bring the user to another gopher menu (I also see menus	/	gopher.someodd.zip	70
i  called "directories"), because the first character on the line is `1`, which	/	gopher.someodd.zip	70
i  is the directory (menu) type. Check out RFC1436, 3.8: Item Type Characters[3]	/	gopher.someodd.zip	70
i  for a comprehensive list of such characters.	/	gopher.someodd.zip	70
i* The "selector string" is `/about` (the path to request)	/	gopher.someodd.zip	70
i* The server to make the request to is `gopher.server.com` on port `70`	/	gopher.someodd.zip	70
i 	/	gopher.someodd.zip	70
iAn `i` line is just an information line that's just there for display. There are	/	gopher.someodd.zip	70
ivarious kinds of downloadable files, like type `9` (binary file) and even a	/	gopher.someodd.zip	70
ispecific type for GIF (`g`).	/	gopher.someodd.zip	70
i 	/	gopher.someodd.zip	70
iMaybe the most interesting type is the `7` item type character. It indicates	/	gopher.someodd.zip	70
ithat the entry intends to perform a query if selected, where a query string is	/	gopher.someodd.zip	70
isent to the server and a response is (hopefully) given based off that query.	/	gopher.someodd.zip	70
iSome gopher enthusiasts, myself included, really enjoy abusing this feature for	/	gopher.someodd.zip	70
ieverything from games (I've included some screenshots and even video of games in	/	gopher.someodd.zip	70
iWaffle!) to, say, my gopher forum software[4].	/	gopher.someodd.zip	70
i 	/	gopher.someodd.zip	70
iClients are also kind of expected to view type `0` in the client, I think.	/	gopher.someodd.zip	70
iThat's text files.	/	gopher.someodd.zip	70
i 	/	gopher.someodd.zip	70
i### Interesting bits	/	gopher.someodd.zip	70
i 	/	gopher.someodd.zip	70
i* I found it interesting that the way Gopher Clients, Waffle included, tend to	/	gopher.someodd.zip	70
i  get the title bar text or the like, is from the label of the menu item which	/	gopher.someodd.zip	70
i  lead to it.	/	gopher.someodd.zip	70
i* I decided to implement a loading screen (there's a whole interesting	/	gopher.someodd.zip	70
i  `Progress.hs`[5] module), which uses `Brick.BChan`[6], and does some fun	/	gopher.someodd.zip	70
i  networking/socket things	/	gopher.someodd.zip	70
i* Caching and history	/	gopher.someodd.zip	70
i* The TUI/BrickApp gets broken up into different states I call a `ModeAction`,	/	gopher.someodd.zip	70
i  so this could be things like bookmark, help, homepage, menu, open, progress,	/	gopher.someodd.zip	70
i  search, or stuff to do with the menu. The user interface is kind of thought of	/	gopher.someodd.zip	70
i  in relation to a state machine.	/	gopher.someodd.zip	70
i* INI configuration and storage for bookmarks, styling, default associations,	/	gopher.someodd.zip	70
i  homepage	/	gopher.someodd.zip	70
i* Can be piped with tor or whatever, so you can access hidden service	/	gopher.someodd.zip	70
i  Gopherholes	/	gopher.someodd.zip	70
i 	/	gopher.someodd.zip	70
i### Profiling, benchmarking, optimizing	/	gopher.someodd.zip	70
i 	/	gopher.someodd.zip	70
iOne of the first major issues I came across was Waffle was extremely slow. I	/	gopher.someodd.zip	70
iwasn't sure why, because it felt like I was using brick[7] the way I was	/	gopher.someodd.zip	70
isupposed to.	/	gopher.someodd.zip	70
i 	/	gopher.someodd.zip	70
iI used a terminal-based viewer called profold[8] for GHC `.prof` files generated	/	gopher.someodd.zip	70
iby profiling with `+RTS -p`, made by someone I had the pleasure of talking to	/	gopher.someodd.zip	70
i(@garmelon[9]). I believe it allowed to to suss out what Waffle was spending the	/	gopher.someodd.zip	70
imost time doing and why, specifically, scrolling up and down felt so laggy and	/	gopher.someodd.zip	70
iintensive on very long menus. I can't remember what I was doing before, but the	/	gopher.someodd.zip	70
isolution turned out to be to instead use `BrickList` to display menus,	/	gopher.someodd.zip	70
ibasically.	/	gopher.someodd.zip	70
i 	/	gopher.someodd.zip	70
i## Footnotes	/	gopher.someodd.zip	70
i 	/	gopher.someodd.zip	70
h[1]: waffle: https://github.com/someodd/waffle	URL:https://github.com/someodd/waffle	gopher.someodd.zip	70
h[2]: RFC 1436: https://www.rfc-editor.org/rfc/rfc1436	URL:https://www.rfc-editor.org/rfc/rfc1436	gopher.someodd.zip	70
h[3]: RFC1436, 3.8: Item Type Characters: https://www.rfc-editor.org/rfc/rfc1436	URL:https://www.rfc-editor.org/rfc/rfc1436	gopher.someodd.zip	70
0[4]: my gopher forum software: /showcase/gopherden	/showcase/gopherden	gopher.someodd.zip	70
h[5]: `Progress.hs`: https://github.com/someodd/waffle/blob/master/src/BrickApp/ModeAction/Progress.hs	URL:https://github.com/someodd/waffle/blob/master/src/BrickApp/ModeAction/Progress.hs	gopher.someodd.zip	70
h[6]: `Brick.BChan`: https://hackage.haskell.org/package/brick-2.3.1/docs/Brick-BChan.html	URL:https://hackage.haskell.org/package/brick-2.3.1/docs/Brick-BChan.html	gopher.someodd.zip	70
h[7]: brick: https://hackage.haskell.org/package/brick	URL:https://hackage.haskell.org/package/brick	gopher.someodd.zip	70
h[8]: profold: https://github.com/Garmelon/profold	URL:https://github.com/Garmelon/profold	gopher.someodd.zip	70
h[9]: @garmelon: https://github.com/Garmelon/	URL:https://github.com/Garmelon/	gopher.someodd.zip	70