#!/usr/bin/env python ## Free Libraries Gopher Client (see version number in below tuple) ## Jul 2012 ## License: Public Domain ## Very Beta geo = (80, 24, "<-' go <- back ^ v scroll Free Libraries Gopher Client 0.2 (Public Domain)") import curses from os import popen from os import system from sys import argv ## changelog: ## ## 0.2: ## now targets both python 2 and 3 compatibility (print() / curses work in both) ## light support for url proxies (gopherurl://path/proxy?proxiedgopherurl) ## any movement of selection bar shows current or hovered url at bottom ## not new: using "back" autoscrolls to top... new: except for single step back ## (makes it a little less tedious to explore trees with minimal usage change) ## may have fixed cosmetic line width bug ## slightly better support of 40 columns if geo[0] < 80: geo = (geo[0], geo[1], "<-back ^v scr FLGC 0.2(Public Domain)") if geo[0] < 40: print("FLGC 0.2") print("(Public Domain)") print () print("This should probably") print("be run with at least") print("40 columns. hit enter") print("to try it anyway.") print() try: nul = input() except: pass yescurl = int(popen("which curl | wc -l").readline()) if not yescurl: print("This program will not work without curl.") print() print("Suggestion: sudo apt-get install curl (for Debian/Ubuntu/Trisquel/GNewSense)") print(" sudo yum install curl (for Fedora/Freedora/etc)") print("") exit() text = "" url = "" try: url = argv[1] except: pass if url == "": #try: # print("enter the name of a gopher site:", end=" ") ## python 3 #except SyntaxError: # print("enter the name of a gopher site:"), ## that failed, so bash will do it: system("echo -n \"enter the name of a gopher site: \"") try: url = raw_input().strip() ## python 2 except NameError: url = input().strip() def c(p): global url spaghetti = 0 ## this is related to the "back" function, sorry if not "gopher://" in url.lower(): url = "gopher://" + url if url.lower()[:9] != "gopher://": url = "gopher://" + url global text # for debugging output text = url history = [] yescaca = int(popen("which cacaview | wc -l").readline()) oneselected = 0 keeponescrolllocation = 0 ## save cursor position history (one deep) ## could do fully, but it really should reset x = 0 while x not in [27, ord("x"), ord("q"), ord("Q"), ord("X")]: yindex = 0 yib = 0 global geo thistype = "1" rurl = url if rurl[:9].lower() == "gopher://": rurl = rurl[9:] try: thistype = rurl.split("/")[1] text = thistype except: pass proxy = "gopher://127.0.0.1/1/cgi-bin/proxy?" proxy = "" ## remove or set this if you want to use the proxy feature f = list(popen("curl -s \"" + proxy + url + "\" 2> /dev/null").readlines()) history += [url] if len(history) > 50: history = history[-50:] ## conservative ; try 1000 or even remove for lenf in range(3): f = [""] + f if len(f) < geo[1]: while len(f) < geo[1]: f += [""] for lenf in range(geo[1]): f += [""] x = 0 while x not in [27, curses.KEY_ENTER, 10, ord("x"), ord("X"), ord("q"), ord("Q")]: ## because curses.KEY_ENTER wasn't being trapped; LF was, go figure if x == curses.KEY_PPAGE: oneselected = 1 yindex -= (geo[1] - 1) if yindex < 0: yindex = 0 if x == curses.KEY_NPAGE: oneselected = 1 yindex += geo[1] - 1 if yindex > len(f) - geo[1]: yindex = len(f) - geo[1] if x == curses.KEY_UP: oneselected = 1 yindex -= 1 if yindex < 0: yindex = 0 if x == curses.KEY_DOWN: oneselected = 1 yindex += 1 if yindex > len(f) - geo[1]: yindex = len(f) - geo[1] if x == curses.KEY_HOME: oneselected = 1 yindex = 0 if x == curses.KEY_LEFT: if len(history) > 1: try: ## lazy history = history[:-1] newurl = history[len(history) - 1] history = history[:-1] url = newurl text = newurl except: pass else: x = 126 if x == curses.KEY_END: oneselected = 1 yindex = len(f) - geo[1] -1 if spaghetti: ## if page is from url history yindex = keeponescrolllocation keeponescrolllocation = 0 ## otherwise it keeps using it if yindex != yib or yib == 0: hoverurl = "" try: hoverurl = rurl ## first try url that's being viewed... if thistype == "1" and f[yindex + 3][0] in "0I1": hoverurl = f[yindex + 3].split("\t")[2] + ":" + f[yindex + 3].split("\t")[3].rstrip() + "/" + f[yindex + 3][0] + f[yindex + 3].split("\t")[1] ## ...then if possible / applicable, url that's highlighted by user except: pass p.clear() for pp in range(0, geo[1] - 1): thisline = f[pp + yindex].rstrip() if thistype == "1" and len(thisline): hint = "" if thisline[0] == "0": hint = "(TEXT) " elif thisline[0] == "1": hint = " (DIR) " elif thisline[0] == "i": hint = "" elif thisline[0] == "I": hint = " (IMG) " if thisline[0] in "01iI": thisline = hint + thisline.split("\t")[0][1:] ntab = " " * 4 ## this might fix the line width issue if pp == 3: bar = ntab.join(str(thisline + (" " * geo[0])).split("\t")) ## for whatever reason, this doesn't exactly p.addstr(pp, 0, bar[:geo[0]], curses.A_REVERSE) p.addstr(pp + 1, 0, " " * geo[0]) else: p.addstr(pp, 0, ntab.join(thisline.split("\t"))[:geo[0]]) yib = yindex p.refresh() p.addstr(geo[1] - 1, 0, " " * (geo[0] - 1), curses.A_REVERSE) if hoverurl and oneselected: ### and int(geo[0]) > 79: ## 79 regardless of screen dim hoverurl = hoverurl[:geo[0] - 2] urlstatus = hoverurl + " " * (geo[0] - 2 - len(hoverurl)) p.addstr(geo[1] - 1, 0, " " + urlstatus, curses.A_REVERSE) else: p.addstr(geo[1] - 1, 0, " " + geo[2], curses.A_REVERSE) p.addstr(geo[1] - 1, geo[0] - 1, "", curses.A_REVERSE) p.refresh() spaghetti = 0 if x != curses.KEY_LEFT: x = p.getch() else: spaghetti = 1 ## make an exception for the back function x = 10 if x == 10 or x == curses.KEY_ENTER: oneselected = 0 ## fix this line if you want status ready on new pages if spaghetti == 0: rstr = f[yindex + 3] ## the contents of the selected line keeponescrolllocation = yindex typifany = "" ## typifany: derived from link you're hovering over ## thistype: type / page you're already viewing if len(rstr) and thistype == "1": ## if thistype isn't 1, ^ parsed line doesn't count ## (i.e. because you're viewing type 0) typifany = rstr[0] ## get gophertype, (or " ", or "") if typifany == "0": if len(rstr.split("\t")) > 3: typs = "/" + typifany desc = rstr.split("\t")[0][1:] rsel = rstr.split("\t")[1] serv = rstr.split("\t")[2] ## port field is often last, so remove \r\n port = rstr.split("\t")[3].rstrip() newurl = "gopher://" + serv + ":" + port + typs + rsel url = newurl elif typifany == "1": if len(rstr.split("\t")) > 3: typs = "/" + typifany desc = rstr.split("\t")[0][1:] rsel = rstr.split("\t")[1] serv = rstr.split("\t")[2] ## port field is often last, so remove \r\n port = rstr.split("\t")[3].rstrip() newurl = "gopher://" + serv + ":" + port + typs + rsel url = newurl elif typifany == "I": if len(rstr.split("\t")) > 3: typs = "/" + typifany desc = rstr.split("\t")[0][1:] rsel = rstr.split("\t")[1] serv = rstr.split("\t")[2] ## port field is often last, so remove \r\n port = rstr.split("\t")[3].rstrip() newurl = "gopher://" + serv + ":" + port + typs + rsel system("curl -s \"" + newurl + "\" > /tmp/cacav 2> /dev/null ; cacaview /tmp/cacav 2> /dev/null") else: x = 126 ## pretend enter wasn't pressed else: x = 126 ## no plans to map this curses.wrapper(c)