summaryrefslogtreecommitdiffstats
path: root/Lib/gopherlib.py
diff options
context:
space:
mode:
authorGuido van Rossum <guido@python.org>1994-02-21 16:36:04 (GMT)
committerGuido van Rossum <guido@python.org>1994-02-21 16:36:04 (GMT)
commit23acc9590c89d1e230ba6f8faaba55f7250a0b88 (patch)
treed22b1f1d128500530c2ca478d395f8db78cbca1c /Lib/gopherlib.py
parent3da56c3686d7e917cfa04fb07511159dc0623423 (diff)
downloadcpython-23acc9590c89d1e230ba6f8faaba55f7250a0b88.zip
cpython-23acc9590c89d1e230ba6f8faaba55f7250a0b88.tar.gz
cpython-23acc9590c89d1e230ba6f8faaba55f7250a0b88.tar.bz2
Towards a standard access mechanism for URLs.
Diffstat (limited to 'Lib/gopherlib.py')
-rw-r--r--Lib/gopherlib.py195
1 files changed, 195 insertions, 0 deletions
diff --git a/Lib/gopherlib.py b/Lib/gopherlib.py
new file mode 100644
index 0000000..71413ba
--- /dev/null
+++ b/Lib/gopherlib.py
@@ -0,0 +1,195 @@
+# Gopher protocol client interface
+
+import string
+
+# Default selector, host and port
+DEF_SELECTOR = '1/'
+DEF_HOST = 'gopher.micro.umn.edu'
+DEF_PORT = 70
+
+# Recognized file types
+A_TEXT = '0'
+A_MENU = '1'
+A_CSO = '2'
+A_ERROR = '3'
+A_MACBINHEX = '4'
+A_PCBINHEX = '5'
+A_UUENCODED = '6'
+A_INDEX = '7'
+A_TELNET = '8'
+A_BINARY = '9'
+A_DUPLICATE = '+'
+A_SOUND = 's'
+A_EVENT = 'e'
+A_CALENDAR = 'c'
+A_HTML = 'h'
+A_TN3270 = 'T'
+A_MIME = 'M'
+A_IMAGE = 'I'
+A_WHOIS = 'w'
+A_QUERY = 'q'
+A_GIF = 'g'
+A_HTML = 'h' # HTML file
+A_WWW = 'w' # WWW address
+A_PLUS_IMAGE = ':'
+A_PLUS_MOVIE = ';'
+A_PLUS_SOUND = '<'
+
+
+# Function mapping all file types to strings; unknown types become TYPE='x'
+_names = dir()
+_type_to_name_map = None
+def type_to_name(gtype):
+ global _type_to_name_map
+ if not _type_to_name_map:
+ for name in _names:
+ if name[:2] == 'A_':
+ _type_to_name_map[eval(name)] = name[2:]
+ if _type_to_name_map.has_key(gtype):
+ return _type_to_name_map[gtype]
+ return 'TYPE=' + `gtype`
+
+# Names for characters and strings
+CRLF = '\r\n'
+TAB = '\t'
+
+# Send a selector to a given host and port, return a file with the reply
+def send_selector(selector, host, *args):
+ import socket
+ import string
+ if args:
+ if args[1:]: raise TypeError, 'too many args'
+ port = args[0]
+ else:
+ port = None
+ i = string.find(host, ':')
+ if i >= 0:
+ host, port = host[:i], string.atoi(host[i+1:])
+ if not port:
+ port = DEF_PORT
+ elif type(port) == type(''):
+ port = string.atoi(port)
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ s.connect(host, port)
+ s.send(selector + CRLF)
+ s.shutdown(1)
+ return s.makefile('r')
+
+# Send a selector and a query string
+def send_query(selector, query, host, *args):
+ return apply(send_selector, (selector + '\t' + query, host) + args)
+
+# The following functions interpret the data returned by the gopher
+# server according to the expected type, e.g. textfile or directory
+
+# Get a directory in the form of a list of entries
+def get_directory(f):
+ import string
+ list = []
+ while 1:
+ line = f.readline()
+ if not line:
+ print '(Unexpected EOF from server)'
+ break
+ if line[-2:] == CRLF:
+ line = line[:-2]
+ elif line[-1:] in CRLF:
+ line = line[:-1]
+ if line == '.':
+ break
+ if not line:
+ print '(Empty line from server)'
+ continue
+ gtype = line[0]
+ parts = string.splitfields(line[1:], TAB)
+ if len(parts) < 4:
+ print '(Bad line from server:', `line`, ')'
+ continue
+ if len(parts) > 4:
+ if parts[4:] != ['+']:
+ print '(Extra info from server:', parts[4:], ')'
+ else:
+ parts.append('')
+ parts.insert(0, gtype)
+ list.append(parts)
+ return list
+
+# Get a text file as a list of lines, with trailing CRLF stripped
+def get_textfile(f):
+ list = []
+ get_alt_textfile(f, list.append)
+ return list
+
+# Get a text file and pass each line to a function, with trailing CRLF stripped
+def get_alt_textfile(f, func):
+ while 1:
+ line = f.readline()
+ if not line:
+ print '(Unexpected EOF from server)'
+ break
+ if line[-2:] == CRLF:
+ line = line[:-2]
+ elif line[-1:] in CRLF:
+ line = line[:-1]
+ if line == '.':
+ break
+ if line[:2] == '..':
+ line = line[1:]
+ func(line)
+
+# Get a binary file as one solid data block
+def get_binary(f):
+ data = f.read()
+ return data
+
+# Get a binary file and pass each block to a function
+def get_alt_binary(f, func, blocksize):
+ while 1:
+ data = f.read(blocksize)
+ if not data:
+ break
+ func(data)
+
+# Trivial test program
+def test():
+ import sys
+ import getopt
+ opts, args = getopt.getopt(sys.argv[1:], '')
+ selector = DEF_SELECTOR
+ type = selector[0]
+ host = DEF_HOST
+ port = DEF_PORT
+ if args:
+ host = args[0]
+ args = args[1:]
+ if args:
+ type = args[0]
+ args = args[1:]
+ if len(type) > 1:
+ type, selector = type[0], type
+ else:
+ selector = ''
+ if args:
+ selector = args[0]
+ args = args[1:]
+ query = ''
+ if args:
+ query = args[0]
+ args = args[1:]
+ if type == A_INDEX:
+ f = send_query(selector, query, host)
+ else:
+ f = send_selector(selector, host)
+ if type == A_TEXT:
+ list = get_textfile(f)
+ for item in list: print item
+ elif type in (A_MENU, A_INDEX):
+ list = get_directory(f)
+ for item in list: print item
+ else:
+ data = get_binary(f)
+ print 'binary data:', len(data), 'bytes:', `data[:100]`[:40]
+
+# Run the test when run as script
+if __name__ == '__main__':
+ test()