14710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm#! /usr/bin/env python 24710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 34710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# A simple gopher client. 44710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# 54710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Usage: gopher [ [selector] host [port] ] 64710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 74710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport string 84710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport sys 94710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport os 104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport socket 114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Default selector, host and port 134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmDEF_SELECTOR = '' 144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmDEF_HOST = 'gopher.micro.umn.edu' 154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmDEF_PORT = 70 164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Recognized file types 184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmT_TEXTFILE = '0' 194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmT_MENU = '1' 204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmT_CSO = '2' 214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmT_ERROR = '3' 224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmT_BINHEX = '4' 234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmT_DOS = '5' 244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmT_UUENCODE = '6' 254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmT_SEARCH = '7' 264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmT_TELNET = '8' 274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmT_BINARY = '9' 284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmT_REDUNDANT = '+' 294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmT_SOUND = 's' 304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Dictionary mapping types to strings 324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmtypename = {'0': '<TEXT>', '1': '<DIR>', '2': '<CSO>', '3': '<ERROR>', \ 334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm '4': '<BINHEX>', '5': '<DOS>', '6': '<UUENCODE>', '7': '<SEARCH>', \ 344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm '8': '<TELNET>', '9': '<BINARY>', '+': '<REDUNDANT>', 's': '<SOUND>'} 354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Oft-used characters and strings 374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmCRLF = '\r\n' 384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmTAB = '\t' 394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Open a TCP connection to a given host and port 414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdef open_socket(host, port): 424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if not port: 434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm port = DEF_PORT 444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm elif type(port) == type(''): 454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm port = string.atoi(port) 464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm s.connect((host, port)) 484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return s 494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Send a selector to a given host and port, return a file with the reply 514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdef send_request(selector, host, port): 524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm s = open_socket(host, port) 534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm s.send(selector + CRLF) 544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm s.shutdown(1) 554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return s.makefile('r') 564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Get a menu in the form of a list of entries 584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdef get_menu(selector, host, port): 594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm f = send_request(selector, host, port) 604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm list = [] 614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm while 1: 624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm line = f.readline() 634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if not line: 644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print '(Unexpected EOF from server)' 654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm break 664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if line[-2:] == CRLF: 674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm line = line[:-2] 684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm elif line[-1:] in CRLF: 694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm line = line[:-1] 704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if line == '.': 714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm break 724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if not line: 734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print '(Empty line from server)' 744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm continue 754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm typechar = line[0] 764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm parts = string.splitfields(line[1:], TAB) 774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if len(parts) < 4: 784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print '(Bad line from server: %r)' % (line,) 794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm continue 804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if len(parts) > 4: 814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print '(Extra info from server: %r)' % (parts[4:],) 824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm parts.insert(0, typechar) 834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm list.append(parts) 844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm f.close() 854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return list 864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Get a text file as a list of lines, with trailing CRLF stripped 884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdef get_textfile(selector, host, port): 894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm list = [] 904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm get_alt_textfile(selector, host, port, list.append) 914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return list 924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Get a text file and pass each line to a function, with trailing CRLF stripped 944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdef get_alt_textfile(selector, host, port, func): 954710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm f = send_request(selector, host, port) 964710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm while 1: 974710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm line = f.readline() 984710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if not line: 994710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print '(Unexpected EOF from server)' 1004710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm break 1014710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if line[-2:] == CRLF: 1024710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm line = line[:-2] 1034710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm elif line[-1:] in CRLF: 1044710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm line = line[:-1] 1054710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if line == '.': 1064710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm break 1074710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if line[:2] == '..': 1084710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm line = line[1:] 1094710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm func(line) 1104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm f.close() 1114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Get a binary file as one solid data block 1134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdef get_binary(selector, host, port): 1144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm f = send_request(selector, host, port) 1154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm data = f.read() 1164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm f.close() 1174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return data 1184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Get a binary file and pass each block to a function 1204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdef get_alt_binary(selector, host, port, func, blocksize): 1214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm f = send_request(selector, host, port) 1224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm while 1: 1234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm data = f.read(blocksize) 1244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if not data: 1254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm break 1264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm func(data) 1274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# A *very* simple interactive browser 1294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Browser main command, has default arguments 1314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdef browser(*args): 1324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm selector = DEF_SELECTOR 1334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm host = DEF_HOST 1344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm port = DEF_PORT 1354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm n = len(args) 1364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if n > 0 and args[0]: 1374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm selector = args[0] 1384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if n > 1 and args[1]: 1394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm host = args[1] 1404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if n > 2 and args[2]: 1414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm port = args[2] 1424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if n > 3: 1434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm raise RuntimeError, 'too many args' 1444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm try: 1454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm browse_menu(selector, host, port) 1464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm except socket.error, msg: 1474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print 'Socket error:', msg 1484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm sys.exit(1) 1494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm except KeyboardInterrupt: 1504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print '\n[Goodbye]' 1514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Browse a menu 1534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdef browse_menu(selector, host, port): 1544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm list = get_menu(selector, host, port) 1554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm while 1: 1564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print '----- MENU -----' 1574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print 'Selector:', repr(selector) 1584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print 'Host:', host, ' Port:', port 1594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print 1604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm for i in range(len(list)): 1614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm item = list[i] 1624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm typechar, description = item[0], item[1] 1634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print string.rjust(repr(i+1), 3) + ':', description, 1644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if typename.has_key(typechar): 1654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print typename[typechar] 1664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm else: 1674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print '<TYPE=' + repr(typechar) + '>' 1684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print 1694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm while 1: 1704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm try: 1714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm str = raw_input('Choice [CR == up a level]: ') 1724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm except EOFError: 1734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print 1744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return 1754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if not str: 1764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return 1774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm try: 1784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm choice = string.atoi(str) 1794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm except string.atoi_error: 1804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print 'Choice must be a number; try again:' 1814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm continue 1824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if not 0 < choice <= len(list): 1834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print 'Choice out of range; try again:' 1844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm continue 1854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm break 1864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm item = list[choice-1] 1874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm typechar = item[0] 1884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm [i_selector, i_host, i_port] = item[2:5] 1894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if typebrowser.has_key(typechar): 1904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm browserfunc = typebrowser[typechar] 1914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm try: 1924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm browserfunc(i_selector, i_host, i_port) 1934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm except (IOError, socket.error): 1944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print '***', sys.exc_type, ':', sys.exc_value 1954710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm else: 1964710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print 'Unsupported object type' 1974710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1984710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Browse a text file 1994710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdef browse_textfile(selector, host, port): 2004710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm x = None 2014710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm try: 2024710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm p = os.popen('${PAGER-more}', 'w') 2034710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm x = SaveLines(p) 2044710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm get_alt_textfile(selector, host, port, x.writeln) 2054710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm except IOError, msg: 2064710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print 'IOError:', msg 2074710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if x: 2084710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm x.close() 2094710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm f = open_savefile() 2104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if not f: 2114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return 2124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm x = SaveLines(f) 2134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm try: 2144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm get_alt_textfile(selector, host, port, x.writeln) 2154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print 'Done.' 2164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm except IOError, msg: 2174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print 'IOError:', msg 2184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm x.close() 2194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Browse a search index 2214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdef browse_search(selector, host, port): 2224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm while 1: 2234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print '----- SEARCH -----' 2244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print 'Selector:', repr(selector) 2254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print 'Host:', host, ' Port:', port 2264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print 2274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm try: 2284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm query = raw_input('Query [CR == up a level]: ') 2294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm except EOFError: 2304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print 2314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm break 2324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm query = string.strip(query) 2334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if not query: 2344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm break 2354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if '\t' in query: 2364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print 'Sorry, queries cannot contain tabs' 2374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm continue 2384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm browse_menu(selector + TAB + query, host, port) 2394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# "Browse" telnet-based information, i.e. open a telnet session 2414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdef browse_telnet(selector, host, port): 2424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if selector: 2434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print 'Log in as', repr(selector) 2444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if type(port) <> type(''): 2454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm port = repr(port) 2464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm sts = os.system('set -x; exec telnet ' + host + ' ' + port) 2474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if sts: 2484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print 'Exit status:', sts 2494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# "Browse" a binary file, i.e. save it to a file 2514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdef browse_binary(selector, host, port): 2524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm f = open_savefile() 2534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if not f: 2544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return 2554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm x = SaveWithProgress(f) 2564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm get_alt_binary(selector, host, port, x.write, 8*1024) 2574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm x.close() 2584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# "Browse" a sound file, i.e. play it or save it 2604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdef browse_sound(selector, host, port): 2614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm browse_binary(selector, host, port) 2624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Dictionary mapping types to browser functions 2644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmtypebrowser = {'0': browse_textfile, '1': browse_menu, \ 2654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm '4': browse_binary, '5': browse_binary, '6': browse_textfile, \ 2664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm '7': browse_search, \ 2674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm '8': browse_telnet, '9': browse_binary, 's': browse_sound} 2684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Class used to save lines, appending a newline to each line 2704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmclass SaveLines: 2714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def __init__(self, f): 2724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.f = f 2734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def writeln(self, line): 2744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.f.write(line + '\n') 2754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def close(self): 2764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm sts = self.f.close() 2774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if sts: 2784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print 'Exit status:', sts 2794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Class used to save data while showing progress 2814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmclass SaveWithProgress: 2824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def __init__(self, f): 2834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.f = f 2844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def write(self, data): 2854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm sys.stdout.write('#') 2864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm sys.stdout.flush() 2874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm self.f.write(data) 2884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm def close(self): 2894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print 2904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm sts = self.f.close() 2914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if sts: 2924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print 'Exit status:', sts 2934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Ask for and open a save file, or return None if not to save 2954710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdef open_savefile(): 2964710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm try: 2974710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm savefile = raw_input( \ 2984710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 'Save as file [CR == don\'t save; |pipeline or ~user/... OK]: ') 2994710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm except EOFError: 3004710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print 3014710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return None 3024710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm savefile = string.strip(savefile) 3034710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if not savefile: 3044710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return None 3054710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if savefile[0] == '|': 3064710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm cmd = string.strip(savefile[1:]) 3074710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm try: 3084710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm p = os.popen(cmd, 'w') 3094710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm except IOError, msg: 3104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print repr(cmd), ':', msg 3114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return None 3124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print 'Piping through', repr(cmd), '...' 3134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return p 3144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if savefile[0] == '~': 3154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm savefile = os.path.expanduser(savefile) 3164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm try: 3174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm f = open(savefile, 'w') 3184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm except IOError, msg: 3194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print repr(savefile), ':', msg 3204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return None 3214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print 'Saving to', repr(savefile), '...' 3224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return f 3234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 3244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Test program 3254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdef test(): 3264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm if sys.argv[4:]: 3274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm print 'usage: gopher [ [selector] host [port] ]' 3284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm sys.exit(2) 3294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm elif sys.argv[3:]: 3304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm browser(sys.argv[1], sys.argv[2], sys.argv[3]) 3314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm elif sys.argv[2:]: 3324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm try: 3334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm port = string.atoi(sys.argv[2]) 3344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm selector = '' 3354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm host = sys.argv[1] 3364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm except string.atoi_error: 3374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm selector = sys.argv[1] 3384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm host = sys.argv[2] 3394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm port = '' 3404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm browser(selector, host, port) 3414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm elif sys.argv[1:]: 3424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm browser('', sys.argv[1]) 3434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm else: 3444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm browser() 3454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 3464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm# Call the test program as a main program 3474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmtest() 348