----------------------------------------
       Cosmic Voyage - Part 1
       January 20th, 2019
       ----------------------------------------
       
       slackz [0] asked me to do some write-ups on cosmic.voyage from the
       architect POV. Here's part 1 of who knows how many.
 (DIR) [0] slackz
       
       If you don't know what cosmic.voyage is read this first [1]
 (DIR) [1] Cosmic Voyage
       
       Being a text-based writing adventure, and me being me,
       I immediately set out to make Cosmic sit primarily on gopher
       rather than web. At first I didn't plan to have a web presence for
       it at all, but that changed as things went along. Really, my goals
       and understanding of the system are ever-evolving and greatly
       informed by the active users.
       
       Regardless, things started with gopher. I knew I wanted a few
       things right away:
       
       - A single gopher structure, not individual user folders.
       - Users to be able to fully control their ships and stories, but
         not mess with other users' things
       - An ongoing log that linked everything together.
       
       I use motsognir [2] on gopher.black, but I thought it might be
       easier to write scripts as gophermaps for cosmic if I stuck with
       gophernicus. I could have made things in cgi scripts, but I like
       the transparent heirarchical structure I get to this way instead.
 (DIR) [2] motsognir
       
       The structure of cosmic's gopher hole looks like this:
         ├── gophermap
         ├── intro.gophermap
         ├── listing.gophermap
         ├── log
         │   ├── gophermap
         │   └── intro.gophermap
         ├── Melchizedek (or any old ship)
         │   ├── 001.txt
         │   ├── 002.txt
         │   ├── 003.txt
         ├── Melvin P Feltersnatch (another example ship)
         │   ├── 001.txt
         │   ├── 002.txt
         │   └── 003.txt
         ├── rss.xml
         └── ships
             ├── gophermap
             ├── Melchizedek (example ship page directory)
             │   └── gophermap -> /var/gopher/ships/ship/gophermap
             ├── Melvin P Feltersnatch (another example ship page dir)
             │   └── gophermap -> /var/gopher/ships/ship/gophermap
             ├── ship
             │   └── gophermap
             └── ships.gophermap
       
       At the root level I have a gophermap that pulls in and processes
       other gophermaps, does a little shell scripting, and displays the
       basic content. Let's start by having a look at that code:
       
         =intro.gophermap
         1Complete Transmission Log /log
         1Ships, Colonies, Outposts /ships
         0RSS Feed /rss.xml
       
         Most recent (20) log entries: 
         =head -n 20 listing.gophermap | sed 's|^0||' | awk -v tot="$(wc -l listing.gophermap)" '{print 0 int(tot)-NR+1 " >> " $0}'
       
       Gophernicus allows us to source in other gophermaps by using
       saying =gophermapname. In this way I can keep my intro text for
       cosmic in a separate file (which becomes helpful later when we
       look at web generation). Following that I have a few regular
       gophermap links using gophernicus' friendly shorthand syntax (no
       servername or port required). Finally I list the most recent log
       entries.
       
       In this, I grab the top 20 lines from listing.gophermap and number
       them properly.
       
       listing.gophermap is the heart of the cosmic log system. Each time
       a user adds a log to the site, it gets prepended to that file.
       That file in turn generates the log page, the short listings here,
       and content for the individual ship pages. It's a sensitive file
       because it needs to be writable by all users on the system. I've
       kicked around better ways to do that, but for now it seems to
       work. At least I back it up regularly.
       
       The /log/ page works very much like the root gophermap, but with
       a simpler rendering.
       
         =intro.gophermap
         =sed 's|^0||' /var/gopher/listing.gophermap | awk -v tot="$(wc -l /var/gopher/listing.gophermap)" '{print 0 int(tot)-NR+1 " >> " $0'}  
       
       Simple, right? awk is the best.
       
       So what about the ship pages? Those are a little funkier. On the
       one hand, they pretty much just sed/awk their way through the
       listing.gophermap file as well, but there's also the matter of
       a navigable directory.
       
       From the list above you can see that there's a ships directory.
       Lets look more closely at an example:
         └── ships
             ├── gophermap
             ├── Enterprise NCC-1701
             │   └── gophermap -> /var/gopher/ships/ship/gophermap
             ├── ship
             │   └── gophermap
             └── ships.gophermap
       
       The root gophermap of the ships page does a little more hard work.
       This has a few long lines that might not render well on your
       screen. I apologize!:
       
         #!/bin/sh
         cat "ships.gophermap"
         find "/var/gopher/" -maxdepth 1 ! -path "/var/gopher/" ! -path "/var/gopher/ships" ! -path "/var/gopher/log" -type d -print | sed 's|/var/gopher/||' | sort | while read -r ship
         do
           entry_num=$(grep -c "^0${ship}" "/var/gopher/listing.gophermap")
           if [ "$entry_num" != "0" ]; then
             printf "1%s (%s)\\t/ships/%s\\n" "$ship" "$entry_num" "$ship"
           fi
         done
       
       We start by catting out the intro for the ships page from
       ships.gophermap, then we loop through all the ship directories on
       the system. Using each of those ships, we then check against
       listing.gophermap to see how many entries they have. If they don't
       have any entries, we leave them out of the listings. That was
       a nice idea from one of the cosmic users to avoid cluttering up
       the ship page with empty ships. If there ARE entries, we print
       a link to the ship page.
       
       The ship pages themselves are a bit of trickery. There's really
       only one ship page /ships/ship/gophermap. All the rest are just
       symlinks to it. But how!!?? What manner of sorcery is this?
       
         #!/bin/sh
       
         ship="$(/bin/pwd -L | sed 's|.*/||')"
         desc="/var/gopher/${ship}/.description"
         if [ -f "$desc" ]; then
           cat "$desc"
           printf "\\n"
         fi
       
         printf "%s - Ship Log\\n" "$ship"
         tac "/var/gopher/listing.gophermap" | sed "s|^0||" | awk '{print 0 NR " >> " $0}' | grep "^.*>>\\ ${ship}" 
       
       pwd -L gets the path of the current symlink, which reveals the
       ship name! Then we look to see if there's a ship description and
       finally we grab all the relevant logs from listings and number
       everything.
       
       Huzzah!
       
       That's it. That's the whole gopher structure that drives Cosmic
       Voyage. Next time I'll talk about the web generation and RSS
       feeds.
       
       If this inspired you to join the system, instructions can be found
       on cosmic's home page. See you in the stars!