diff -ruN msnlib-c2/INSTALL msnlib-d1/INSTALL
--- msnlib-c2/INSTALL	Tue Oct 22 22:07:44 2002
+++ msnlib-d1/INSTALL	Mon Oct 28 19:25:35 2002
@@ -9,6 +9,9 @@
 configuration file and directories (placed in ~/.msn); and finally run the
 client with 'msn'.
 
+Alternatively, you can create your own ~/.msn/msnrc file based on an example
+named 'msnrc.sample'.
+
 
 Here is a command line summary:
 
diff -ruN msnlib-c2/README msnlib-d1/README
--- msnlib-c2/README	Tue Oct 22 22:07:43 2002
+++ msnlib-d1/README	Mon Oct 28 19:25:35 2002
@@ -21,7 +21,8 @@
 
 If you're intrested, read the code to find out more; most of the documentation
 is there either as command or as python documentation strings. You might also
-want to check out the doc directory.
+want to check out the doc directory; specially if you use a non-unix platform,
+take a look at the 'portability' file.
 
 
 Comments and patches are always welcome.
diff -ruN msnlib-c2/doc/Changelog msnlib-d1/doc/Changelog
--- msnlib-c2/doc/Changelog	Tue Oct 22 22:07:43 2002
+++ msnlib-d1/doc/Changelog	Mon Oct 28 19:25:35 2002
@@ -1,3 +1,23 @@
+28 Oct 02 18.45.12 - Alberto <albertogli@telpin.com.ar>
+ * msn: handle screen width appropiatedly
+ * tag: d1 tag
+
+24 Oct 02 13.22.34 - Alberto <albertogli@telpin.com.ar>
+ * msn: implemented command line history using escape codes, and added a
+	configuration option for its size
+ * msn: implemented basic tab completion
+
+23 Oct 02 21.43.08 - Alberto <albertogli@telpin.com.ar>
+ * msn: framework for advanced terminal handling
+ * doc: added a 'portability' doc with some info
+ * doc: several minor modifications
+ * msn: minor modifications to the help text
+
+23 Oct 02 09.33.51 - Alberto <albertogli@telpin.com.ar>
+ * msn: improve output on server disconnect
+ * msn: change the interpreter to /usr/bin/python, which is a more standard
+	location
+
 22 Oct 02 21.49.07 - Alberto <albertogli@telpin.com.ar>
  * msn: use quit() instead of sys.exit() in several places
  * msn: handle network errors when logging in
diff -ruN msnlib-c2/doc/TODO msnlib-d1/doc/TODO
--- msnlib-c2/doc/TODO	Tue Oct 22 22:07:43 2002
+++ msnlib-d1/doc/TODO	Mon Oct 28 19:25:35 2002
@@ -2,15 +2,15 @@
 
 package TODO
 * more documentation (client manpages, basic api, etc.)
+* allow installer to detect python location
+	it is a possibility to have special installers for non-unix platforms
 
 msn client TODO
 * be able to use nick with spaces
-	worthy?
-* better stdin reading (ie. like micq/readline)
+	this can cause a lot of damage, is it worthy?
+* full-featured tab completion
+* line editing
 * find something out about the different char codes support (ñ, ç, accents, and so on)
-* make colors configurable (either as colors or as random strings (so you can,
-	for instance, output in html)
-
 
 msn lib TODO
 * blocked and allowed lists
@@ -27,12 +27,17 @@
 * signal handling
 	when the signal module becames popular on distros, we can get rid of
 	the select() ugly stuff for auto-away and re-implement it using
-	alarm() or things like that
+	alarm() or things like that; add support for SIGWINCH, send syncs
+	often, etc.
 * create a detached client with a server/client model
 	this can be either a one-pc-only detached mode or maybe some kind of
 	web interface; after all it should have very much in common, and with
 	a couple of cookie handling and a browser which supports auto-refresh,
 	a piece of cake (and java-free)
+* make colors configurable
+	either as colors or as random strings, so you can, for instance,
+	output in html. this is here just because it's really low priority
+
 
 msn lib 
 * higher level callbacks
diff -ruN msnlib-c2/doc/URL msnlib-d1/doc/URL
--- msnlib-c2/doc/URL	Tue Oct 22 22:07:43 2002
+++ msnlib-d1/doc/URL	Mon Oct 28 19:25:35 2002
@@ -1,6 +1,9 @@
 
 The URL for the project is http://kbs59.informatik.uni-bremen.de/~alb
-
 I want to thank Henne Vogelsang for providing the hosting.
 
+There is also a freshmeat project page that it's easier to remember:
+http://freshmeat.net/projects/msnlib
+
+You can contact me (Alberto Bertogli) via email at albertogli@telpin.com.ar
 
diff -ruN msnlib-c2/doc/log_format msnlib-d1/doc/log_format
--- msnlib-c2/doc/log_format	Tue Oct 22 22:07:43 2002
+++ msnlib-d1/doc/log_format	Mon Oct 28 19:25:35 2002
@@ -24,3 +24,6 @@
 	linen
 
 
+In the utils directory you can find the file "msnlog.vim", which is a vim
+syntax highlighting file for this log format.
+
diff -ruN msnlib-c2/doc/portability msnlib-d1/doc/portability
--- msnlib-c2/doc/portability	Wed Dec 31 21:00:00 1969
+++ msnlib-d1/doc/portability	Mon Oct 28 19:25:35 2002
@@ -0,0 +1,44 @@
+
+The code should be portable, as it doesn't contain any specific stuff.
+I tend to code based on posix/sus, but I think it's pretty much generic
+python both the library and the client, specially the former.
+I'm almost sure it will run unmodified on unix platforms (and the only reason
+i say 'almost' is because i didn't test it myself, but it certanly should).
+
+
+The only thing that is tied to a unix environment is the client terminal
+handling (which requires termios and fcntl modules), but it's isolated and has
+runtime detection, so if you don't have any of these modules, or they fail for
+some reason, the client will fall back to the normal behaviour.
+
+A thing that might be conflictive for non-unix platforms is that I assume the
+python interpreter lives in "/usr/bin/python", and it's callable using
+"/usr/bin/env python"; these are the closest thing to a standard location on
+unix boxes. If you need to change this, the places are the first line of
+'msn', and somewhere inside 'install'.
+
+The next possible hot point (always talking about non-unix platforms) is
+'msnsetup' and the configuration file location; the first one requires bash,
+so if you don't have it, you can just create your own msnrc file based on
+'msnrc.sample'; but the location is assumed to be $HOME/.msn/msnrc, and maybe
+you don't have '$HOME' (or you don't even have environment variables at all),
+in this case you specify the location on the command line, as the first and
+only argument to msn: "msn /path/to/msnrc".
+
+
+Note that the code is only tested initially with Python 2.2 under Linux, which
+is my development platform.
+
+It has been reported by users also to run successfuly under:
+ * BeOS (Dano release) - Python 2.1
+	Needed to change the python interpreter location, as expected. Also,
+	there was a problem with BeOS's bash that didn't like the 'read'
+	command, which is used in msnsetup.
+
+
+If you run it under a different platform, please let me know; specially if you
+had (or have) any problems.
+
+Thanks,
+		Alberto
+
diff -ruN msnlib-c2/msn msnlib-d1/msn
--- msnlib-c2/msn	Tue Oct 22 22:07:43 2002
+++ msnlib-d1/msn	Mon Oct 28 19:59:38 2002
@@ -1,4 +1,4 @@
-#!/usr/local/bin/python -OQnew
+#!/usr/bin/python -OQnew
 
 
 import sys
@@ -50,12 +50,13 @@
 def printl(line, color = c.normal, bold = 0):
 	"Prints a line with a color"
 	out = ''
+	if line and line[0] == '\r':
+		clear_line()
 	if bold:
 		out = c.bold
 	out = color + out + line + c.normal
 	sys.stdout.write(out)
 	sys.stdout.flush()
-	
 
 def perror(line):
 	"Prints an error"
@@ -151,7 +152,7 @@
 	printl('%s ' % ctime, c.blue)
 	printl('%s' % nick, c.cyan, 1)
 	printl(' >>> ', c.yellow, 1)
-	printl('%s\n' % msg)
+	printl('%s' % msg)
 
 
 def beep(q = 0):
@@ -169,8 +170,11 @@
 	printl('Closing\n', c.green, 1)
 	try:
 		m.disconnect()
+		global oldtermattr
+		termios.tcsetattr(sys.stdin.fileno(), termios.TCSAFLUSH, oldtermattr)
 	except:
 		pass
+	redraw_cli()
 	sys.exit(code)
 
 def nick2email(nick):
@@ -242,6 +246,187 @@
 	return 1
 
 
+
+#
+# terminal handling
+#
+
+# all this is _ugly_, a real mess; luckily it's pretty much self contained.
+# if you're trying to follow the code, i highly recommend you to skip this
+# section; you really don't need to know it, just think of redraw_cli() pretty
+# much as print_prompt(), stdin_read() as sys.stdin.readline(), and
+# clear_line() as printf('\r'). actually that's quite near true when we don't
+# use termios.
+# it has been written in a way that if termios is not available, we fall back
+# to the normal and old behaviour which is guaranteed to work.
+
+try:
+	# all of this disables line-buffering on the terminal (thus allowing
+	# char-by-char reads) and echoing (so we output whatever we want); and
+	# finally sets the file nonblocking so we can read all that's
+	# available without complications.
+	# you should read termios and fcntl manpages to find out the details
+	import termios
+	stdinfd = sys.stdin.fileno()
+	oldtermattr = termios.tcgetattr(stdinfd)
+	newtermattr = termios.tcgetattr(stdinfd)
+	newtermattr[3] = newtermattr[3] & ~termios.ICANON & ~termios.ECHO
+	termios.tcsetattr(stdinfd, termios.TCSANOW, newtermattr)
+	import fcntl, os
+	fcntl.fcntl(stdinfd, fcntl.F_SETFL, os.O_NONBLOCK)
+	del(newtermattr)
+	use_termios = 1
+except:
+	use_termios = 0
+
+# now we try to find out the console size; if we fail we fall back to
+# the good old 80x24.
+# note that the (' ' * 10) is just awful, but there is no sane way of
+# doing this without using a C module. it's based on 'struct winsize',
+# but as we only use the first 4 bytes, we don't ask for more; then we
+# unpack the two shorts into (lenght, width)
+try:
+	import struct
+	winsize = fcntl.ioctl(stdinfd, termios.TIOCGWINSZ, ' ' * 10)
+	winsize = struct.unpack('hh', winsize[:4])
+except:
+	winsize = (24, 80)
+screenwidth = winsize[1]
+
+# input buffer, where all the characters written by the user are stored in
+inbuf = ''
+
+# input history buffer, to store previous commands. 
+# we use a list [buffer, pointer] to avoid namespace pollution
+inbuf_history = [[], -1]
+
+def stdin_read():
+	"""Reads from stdin, and acts in consecuense. If you don't use
+	termios, it's almost the same as calling readline(); but otherwise it
+	handles all the input reading."""
+	global inbuf
+	if not use_termios:
+		inbuf = sys.stdin.readline()
+		tmpbuf = inbuf
+		inbuf = ''
+		out = parse_cmd(tmpbuf)
+		printl(out + '\n', c.green, 1)
+		redraw_cli()
+		return
+	
+	in_esc = 0
+	input = sys.stdin.read()
+	for char in input:
+		inbuf = inbuf + char
+		if char == '\n':
+			# command history
+			if len(inbuf_history[0]) > config['input history size']:
+				del(inbuf_history[0][0])
+			inbuf_history[0].append(inbuf[:-1])
+			inbuf_history[1] = len(inbuf_history[0]) - 1 # moves the pointer
+			
+			sys.stdout.write(char)
+			tmpbuf = inbuf
+			inbuf = ''
+			out = parse_cmd(tmpbuf)
+			printl(out + '\n', c.green, 1)
+			redraw_cli()
+		elif char == '\b' or ord(char) == 127:		# ^H / DEL
+			inbuf = inbuf[:-2]
+			redraw_cli()
+		elif char == '\t':				# tab
+			if len(inbuf) == 1 and email2nick(last_sent):
+				inbuf = 'm ' + email2nick(last_sent) + ' '
+			elif len(inbuf) == 1 and email2nick(last_received):
+				inbuf = 'm ' + email2nick(last_received) + ' '
+			else:
+				inbuf = inbuf[:-1]
+				beep()
+			redraw_cli()
+		elif ord(char) == 4:				# EOT
+			sys.stdout.write('\n')
+			out = parse_cmd('')
+			printl(out + '\n', c.green, 1)
+		elif ord(char) == 27:				# ESC
+			# we use in_esc for escape secuenses (composed of
+			# ESC + '[' + LETTER). 1 means got ESC, 2 means got
+			# '['. Here we set to 1, and the rest are in the
+			# generic handling
+			in_esc = 1
+			inbuf = inbuf[:-1]
+		elif ord(char) < 32:				# unhandled control
+			print 'Got weird char: %d' % ord(char)
+			redraw_cli_cond(char)
+		else:						# normal
+			if not in_esc:
+				redraw_cli_cond(char)
+				continue
+
+			# comes from a escape code
+			elif in_esc == 1:
+				if char == '[':
+					in_esc = 2
+				else:
+					in_esc = 0
+			elif in_esc == 2:
+				if char == 'A':			# up
+					if inbuf_history[1] == -1:
+						# hit the top, or it's empty
+						pass
+					else:
+						clear_line()
+						pos = inbuf_history[1]
+						inbuf = inbuf_history[0][pos]
+						inbuf_history[1] -= 1
+						redraw_cli()
+				elif char == 'B':		# down
+					if not inbuf_history[0]:
+						# empty
+						pass
+					elif inbuf_history[1] == len(inbuf_history[0]) - 1:
+						# hit the bottom, clear the buffer
+						clear_line()
+						inbuf = ''
+						redraw_cli()
+					else:
+						inbuf_history[1] += 1
+						clear_line()
+						pos = inbuf_history[1]
+						inbuf = inbuf_history[0][pos]
+						redraw_cli()
+				in_esc = 0
+			inbuf = inbuf[:-1]
+
+def redraw_cli():
+	"""Redraws the current prompt line, including user input; it first
+	clears the line, either automatically or up to 'lenght' chars."""
+	global inbuf, screenwidth
+	clear_line()
+	print_prompt()
+	lenght = screenwidth - 7	# we subsctract the prompt lenght + 1
+	sys.stdout.write(inbuf[-lenght:])
+	sys.stdout.flush()
+
+def redraw_cli_cond(char):
+	"""Same as redraw_cli, but conditional over the lenght of stdin. That
+	means that if inbuf is getting too big, we redraw; otherwise we just
+	write the character. It's used mostly to avoid innecesary redraw
+	overhead (it avoids 90% of cases)."""
+	global inbuf, screenwidth
+	if len(inbuf) >= (screenwidth - 7):
+		redraw_cli()
+	else:
+		sys.stdout.write(char)
+		sys.stdout.flush()
+
+def clear_line():
+	"""Clears the current line by overwriting it with spaces."""
+	global inbuf, screenwidth
+	if use_termios:
+		sys.stdout.write('\r' + (screenwidth - 1) * ' ' + '\r')
+
+
+
 #
 # stdin command parser
 #
@@ -474,6 +659,9 @@
 		r += 'm nick text\t Sends a message to "nick" with the "text"\n'
 		r += 'a text\t\t Sends a message with "text" to the last person you sent a message to\n'
 		r += 'r text\t\t Sends a message with "text" to the last person that sent you a message\n'
+		r += '\n'
+		r += 'In most cases, where you are asked for a nick, you can alternatively enter the email.\n'
+		r += 'This makes it easier to handle people with weird or long nicks.\n'
 		return r
 	else:
 		return 'Unknown command, type "help" for help'
@@ -498,7 +686,7 @@
 	email = t[1]
 	nick = md.users[email].nick
 	ctime = time.strftime('%I:%M:%S%p')
-	printl('\n%s ' % ctime, c.blue)
+	printl('\r%s ' % ctime, c.blue)
 	printl(nick, c.blue, 1)
 	printl(' changed status to ', c.magenta)
 	printl('%s\n' % status, c.magenta, 1)
@@ -512,7 +700,7 @@
 	email = t[0]
 	nick = md.users[email].nick
 	ctime = time.strftime('%I:%M:%S%p')
-	printl('\n%s ' % ctime, c.blue)
+	printl('\r%s ' % ctime, c.blue)
 	printl(nick, c.blue, 1)
 	printl(' changed status to ', c.magenta)
 	printl('%s\n' % status, c.magenta, 1)
@@ -524,7 +712,7 @@
 	email = tid
 	nick = md.users[email].nick
 	ctime = time.strftime('%I:%M:%S%p')
-	printl('\n%s ' % ctime, c.blue)
+	printl('\r%s ' % ctime, c.blue)
 	printl(nick, c.blue, 1)
 	printl(' disconnected\n', c.magenta)
 	log_msg(email, 'status', 'disconnect')
@@ -533,7 +721,7 @@
 
 # server disconnect
 def cb_out(md, type, tid, params):
-	printl('\nServer sent disconnect', c.red)
+	printl('\rServer sent disconnect (probably you logged in somewhere else)\n', c.green, 1)
 	msncb.cb_out(md, type, tid, params)
 m.cb.out = cb_out
 
@@ -575,7 +763,7 @@
 		if not m.users[email].priv.has_key('typing'):
 			m.users[email].priv['typing'] = 0
 		if not m.users[email].priv['typing']:
-			printl('\n')
+			printl('\r')
 			ctime = time.strftime('%I:%M:%S%p')
 			printl('%s ' % ctime, c.blue)
 			printl('%s' % nick, c.cyan, 1)
@@ -584,7 +772,7 @@
 	else:
 		# messages
 		m.users[email].priv['typing'] = 0
-		print
+		printl('\r')
 		print_inc_msg(email, lines, eoh)
 		log_msg(email, 'in', string.join(lines[eoh:], '\n'))
 
@@ -604,9 +792,10 @@
 	if len(sbd.msgqueue) > 0:
 		nick = email2nick(email)
 		if not nick: nick = email
-		printl('\nFlushing messages for %s:\n' % nick, c.green, 1)
+		printl('\rFlushing messages for %s:\n' % nick, c.green, 1)
 		for msg in sbd.msgqueue:
 			print_out_msg(nick, msg)
+			printl('\n')
 			log_msg(email, 'out', msg)
 	msncb.cb_joi(md, type, tid, params, sbd)
 m.cb.joi = cb_joi
@@ -617,7 +806,7 @@
 		desc = 'Unknown'
 	else:
 		desc = msncb.error_table[errno]
-	desc = 'Server sent error %d: %s\n' % (errno, desc)
+	desc = '\rServer sent error %d: %s\n' % (errno, desc)
 	perror(desc)
 	msncb.cb_err(md, errno, params)
 m.cb.err = cb_err
@@ -630,11 +819,11 @@
 		email = t[2]
 		nick = urllib.unquote(t[3])
 	if type == 'RL':
-		out = c.blue + c.bold + ('\n%s (%s) ' % (email, nick)) + c.magenta + 'has added you\n'
+		out = '\r' + c.blue + c.bold + ('%s (%s) ' % (email, nick)) + c.magenta + 'has added you to his contact list\n'
 		printl(out)
 		beep()
 	elif type == 'FL':
-		out = c.blue + c.bold + ('\n%s (%s) ' % (email, nick)) + c.magenta + 'has been added to your contact list\n'
+		out = '\r' + c.blue + c.bold + ('%s (%s) ' % (email, nick)) + c.magenta + 'has been added to your contact list\n'
 		printl(out)
 	msncb.cb_add(md, type, tid, params)
 m.cb.add = cb_add
@@ -645,11 +834,11 @@
 	if type == 'RL' or type == 'FL':
 		email = t[2]
 	if type == 'RL':
-		out = '\n' + c.blue + c.bold + email + ' ' + c.magenta + 'has removed you\n'
+		out = '\r' + c.blue + c.bold + email + ' ' + c.magenta + 'has removed you from his contact list\n'
 		printl(out)
 		beep()
 	elif type == 'FL':
-		out = '\n' + c.blue + c.bold + email + ' ' + c.magenta + 'has been removed from your contact list\n'
+		out = '\r' + c.blue + c.bold + email + ' ' + c.magenta + 'has been removed from your contact list\n'
 		printl(out)
 	msncb.cb_rem(md, type, tid, params)
 m.cb.rem = cb_rem
@@ -659,7 +848,7 @@
 #
 # now the real thing
 #
-printl('Starting up MSN Client C2\n', c.yellow, 1)
+printl('Starting up MSN Client D1\n', c.yellow, 1)
 
 # first, the configuration
 printl('Loading config... ', c.green, 1)
@@ -697,6 +886,16 @@
 		perror('history size must be integer, using default\n')
 		config['history size'] = 10
 
+# input history size
+if not config.has_key('input history size'):
+	config['input history size'] = 10
+else:
+	try:
+		config['history size'] = int(config['history size'])
+	except:
+		error('input history size must be integer, using default\n')
+		config['input history size'] = 10
+
 # initial status
 if not config.has_key('initial status'): 
 	config['initial status'] = 'online'
@@ -791,13 +990,12 @@
 
 
 # loop
-print_prompt()
+redraw_cli()
 while 1:
 	fds = m.pollable()
 	infd = fds[0]
 	outfd = fds[1]
 	infd.append(sys.stdin)
-	print_prompt()
 	try:
 		fds = select.select(infd, outfd, [], timeout)
 	except KeyboardInterrupt:
@@ -808,7 +1006,7 @@
 		if m.status == 'NLN':
 			m.change_status('away')
 			auto_away = 1
-			printl('\nAutomatically changing status to away\n', c.green, 1)
+			printl('\rAutomatically changing status to away\n', c.green, 1)
 	
 	for i in fds[0] + fds[1]:		# see msnlib.msnd.pollable.__doc__
 		if i == sys.stdin:
@@ -816,10 +1014,9 @@
 			if auto_away:
 				auto_away = 0
 				m.change_status('online')
-				printl('\nAutomatically changing status back to online\n', c.green, 1)
-			# parse the output
-			out = parse_cmd(i.readline())
-			printl(out + '\n', c.green, 1)
+				printl('\rAutomatically changing status back to online\n', c.green, 1)
+			# read from stdin
+			stdin_read()
 		else:
 			try:
 				m.read(i)
@@ -827,13 +1024,14 @@
 				if i != m:
 					if i.msgqueue:
 						nick = email2nick(i.emails[0])
-						printl("\nConnection with %s closed - the following messages couldn't be sent:\n" % (nick), c.green, 1)
+						printl("\rConnection with %s closed - the following messages couldn't be sent:\n" % (nick), c.green, 1)
 						for msg in i.msgqueue:
 							printl(c.bold + '\t>>> ' + c.normal + msg + '\n')
 					m.close(i)
 				else:
 					printl('\nMain socket closed (%s)\n' % str(err), c.red)
 					quit(1)
-
+			# always redraw after network event
+			redraw_cli()
 
 
diff -ruN msnlib-c2/msnrc.sample msnlib-d1/msnrc.sample
--- msnlib-c2/msnrc.sample	Tue Oct 22 22:07:43 2002
+++ msnlib-d1/msnrc.sample	Mon Oct 28 19:25:35 2002
@@ -21,6 +21,10 @@
 # defaults to 10
 history size = 10
 
+# number of user input lines to keep in memory
+# defaults to 10
+input history size = 10
+
 # defines if you want to log your incoming/outgoing messages to the disk, must
 # be yes or no.
 # the default is yes
diff -ruN msnlib-c2/msnsetup msnlib-d1/msnsetup
--- msnlib-c2/msnsetup	Tue Oct 22 22:07:43 2002
+++ msnlib-d1/msnsetup	Mon Oct 28 19:25:35 2002
@@ -1,6 +1,6 @@
 #!/bin/bash
 
-VER="C2"
+VER="D1"
 
 function intro() {
 	echo
diff -ruN msnlib-c2/setup.py msnlib-d1/setup.py
--- msnlib-c2/setup.py	Tue Oct 22 22:07:43 2002
+++ msnlib-d1/setup.py	Mon Oct 28 19:25:35 2002
@@ -1,9 +1,8 @@
-#!/usr/bin/env python
 
 from distutils.core import setup
 
 setup(name="msnlib",
-	version="C2",
+	version="D1",
 	description="MSN Messenger Library and Client",
 	author="Alberto Bertogli",
 	author_email="albertogli@telpin.com.ar",
