Explorar o código

Put OS specific things only in the OS files.

Steve Thielemann %!s(int64=3) %!d(string=hai) anos
pai
achega
eabbb6bedc
Modificáronse 9 ficheiros con 833 adicións e 1459 borrados
  1. 291 0
      door/door.go
  2. 2 285
      door/door_linux.go
  3. 3 281
      door/door_windows.go
  4. 337 0
      door/input.go
  5. 19 371
      door/input_linux.go
  6. 0 333
      door/input_windows.go
  7. 166 0
      door/write.go
  8. 0 162
      door/write_linux.go
  9. 15 27
      door/write_windows.go

+ 291 - 0
door/door.go

@@ -0,0 +1,291 @@
+/*
+Package door: a golang implementation of a BBS door for linux that
+support door32.sys.
+
+    import (
+			"door"
+		)
+
+		int main() {
+			d = door.Door{}
+			d.Init() // Process commandline switches, initialize door, detect screen size.
+
+			d.Write("Welcome to my awesome door, written in "+door.ColorText("BLINK BOLD WHITE")+"golang"+door.Reset+"."+door.CRNL)
+			d.Write("Press a key...")
+			d.Key()
+			d.Write(door.CRNL)
+		}
+*/
+package door
+
+import (
+	"bufio"
+	"flag"
+	"fmt"
+	"log"
+	"os"
+	"path/filepath"
+	"strconv"
+	"strings"
+	"time"
+)
+
+const SavePos = "\x1b[s"              // Save Cursor Position
+const RestorePos = "\x1b[u"           // Restore Cursor Position
+const CRNL = "\r\n"                   // BBS Line Ending
+const Clrscr = "\x1b[0m\x1b[2J\x1b[H" // Clear screen, home cursor
+const HideCursor = "\x1b[?25l"        // Hide Cursor
+const ShowCursor = "\x1b[?25h"        // Show Cursor
+var Reset string = Color(0)           // ANSI Color Reset
+var Unicode bool                      // Unicode support detected
+var CP437 bool                        // CP437 support detected
+var Full_CP437 bool                   // Full CP437 support detected (handles control codes properly)
+var Height int                        // Screen height detected
+var Width int                         // Screen width detected
+var Inactivity int64 = 120            // Inactivity timeout
+
+/*
+door32.sys:
+
+0                            Line 1 : Comm type (0=local, 1=serial, 2=telnet)
+0                            Line 2 : Comm or socket handle
+38400                        Line 3 : Baud rate
+Mystic 1.07                  Line 4 : BBSID (software name and version)
+1                            Line 5 : User record position (1-based)
+James Coyle                  Line 6 : User's real name
+g00r00                       Line 7 : User's handle/alias
+255                          Line 8 : User's security level
+58                           Line 9 : User's time left (in minutes)
+1                            Line 10: Emulation *See Below
+1                            Line 11: Current node number
+*/
+
+type DropfileConfig struct {
+	Comm_type      int
+	Comm_handle    int
+	Baudrate       int
+	BBSID          string
+	User_number    int
+	Real_name      string
+	Handle         string
+	Security_level int
+	Time_left      int
+	Emulation      int
+	Node           int
+}
+
+type Door struct {
+	Config       DropfileConfig
+	READFD       int
+	WRITEFD      int
+	Disconnected bool
+	TimeOut      time.Time // Fixed point in time, when time expires
+	StartTime    time.Time
+	Pushback     FIFOBuffer
+	LastColor    []int
+}
+
+// Return the amount of time left as time.Duration
+func (d *Door) TimeLeft() time.Duration {
+	return time.Until(d.TimeOut)
+	// return d.TimeOut.Sub(time.Now())
+}
+
+func (d *Door) TimeUsed() time.Duration {
+	return time.Since(d.StartTime)
+	// return time.Now().Sub(d.StartTime)
+}
+
+// Read the BBS door file.  We only support door32.sys.
+func (d *Door) ReadDropfile(filename string) {
+	file, err := os.Open(filename)
+	if err != nil {
+		log.Panicf("Open(%s): %s\n", filename, err)
+		// os.Exit(2)
+	}
+
+	defer file.Close()
+
+	var lines []string
+	// read line by line
+
+	// The scanner handles DOS and linux file endings.
+
+	scanner := bufio.NewScanner(file)
+	for scanner.Scan() {
+		line := scanner.Text()
+		lines = append(lines, line)
+	}
+
+	d.Config.Comm_type, err = strconv.Atoi(lines[0])
+	if err != nil {
+		log.Panicf("Door32 Comm Type (expected integer): %s\n", err)
+	}
+	d.Config.Comm_handle, err = strconv.Atoi(lines[1])
+	if err != nil {
+		log.Panicf("Door32 Comm Handle (expected integer): %s\n", err)
+	}
+	d.Config.Baudrate, err = strconv.Atoi(lines[2])
+	if err != nil {
+		log.Panicf("Door32 Baudrate (expected integer): %s\n", err)
+	}
+	d.Config.BBSID = lines[3]
+	d.Config.User_number, err = strconv.Atoi(lines[4])
+	if err != nil {
+		log.Panicf("Door32 User Number (expected integer): %s\n", err)
+	}
+	d.Config.Real_name = lines[5]
+	d.Config.Handle = lines[6]
+	d.Config.Security_level, err = strconv.Atoi(lines[7])
+	if err != nil {
+		log.Panicf("Door32 Security Level (expected integer): %s\n", err)
+	}
+	d.Config.Time_left, err = strconv.Atoi(lines[8])
+	if err != nil {
+		log.Panicf("Door32 Time Left (expected integer): %s\n", err)
+	}
+	d.Config.Emulation, err = strconv.Atoi(lines[9])
+	if err != nil {
+		log.Panicf("Door32 Emulation (expected integer): %s\n", err)
+	}
+	d.Config.Node, err = strconv.Atoi(lines[10])
+	if err != nil {
+		log.Panicf("Door32 Node Number (expected integer): %s\n", err)
+	}
+
+	d.READFD = d.Config.Comm_handle
+	d.WRITEFD = d.Config.Comm_handle
+
+	// Calculate the time when time expires.
+	d.StartTime = time.Now()
+	d.TimeOut = time.Now().Add(time.Duration(d.Config.Time_left) * time.Minute)
+}
+
+// Detect client terminal capabilities, Unicode, CP437, Full_CP437,
+// screen Height and Width.
+func (d *Door) detect() {
+	d.Write("\x1b[0;30;40m\x1b[2J\x1b[H")                          // black on black, clrscr, go home
+	d.Write("\x03\x04\x1b[6n")                                     // hearts and diamonds does CP437 work?
+	d.Write(CRNL + "\u2615\x1b[6n")                                // hot beverage
+	d.Write("\x1b[999C\x1b[999B\x1b[6n" + Reset + "\x1b[2J\x1b[H") // goto end of screen + cursor pos
+	// time.Sleep(50 * time.Millisecond)
+	time.Sleep(250 * time.Millisecond)
+	// read everything
+	// telnet term isn't in RAW mode, so keys are buffer until <CR>
+	var results string
+
+	for {
+		r := d.getch()
+		if r < 0 {
+			break
+		}
+		results += string(byte(r))
+	}
+
+	if len(results) > 0 {
+		output := strings.Replace(results, "\x1b", "^[", -1)
+		log.Println("DETECT:", output)
+	} else {
+		// local telnet echos the reply  :()
+		log.Println("DETECT: Nothing received.")
+		return
+	}
+
+	if (strings.Contains(results, "1;1R") ||
+		strings.Contains(results, "1;3R")) &&
+		(strings.Contains(results, "2:2R") ||
+			strings.Contains(results, "2;3R")) {
+		Unicode = true
+	} else {
+		Unicode = false
+		CP437 = true
+	}
+	if strings.Contains(results, "1;3R") {
+		Full_CP437 = true
+	}
+
+	// get screen size
+
+	pos := strings.LastIndex(results, "\x1b")
+	if pos != -1 {
+		pos++
+		if results[pos] == '[' {
+			pos++
+			results = results[pos:]
+			pos = strings.Index(results, ";")
+			if pos != -1 {
+				height := results[:pos]
+				Height, _ = strconv.Atoi(height)
+				pos++
+				results = results[pos:]
+
+				pos = strings.Index(results, "R")
+				if pos != -1 {
+					width := results[:pos]
+
+					Width, _ = strconv.Atoi(width)
+					// log.Printf("Width: %s, %d, %v\n", results, Width, err)
+				}
+			} else {
+				Height = 0
+				Width = 0
+			}
+		}
+	}
+	log.Printf("Unicode %v Screen: %d X %d\n", Unicode, Width, Height)
+}
+
+// Initialize door framework.  Parse commandline, read dropfile,
+// detect terminal capabilities.
+func (d *Door) Init(doorname string) {
+	var dropfile string
+	d.Pushback = NewFIFOBuffer(5)
+
+	// Get path to binary, and chdir to it.
+	binaryPath, _ := os.Executable()
+	binaryPath = filepath.Dir(binaryPath)
+	_ = os.Chdir(binaryPath)
+
+	flag.StringVar(&dropfile, "d", "", "Path to dropfile")
+	flag.Parse()
+	if len(dropfile) == 0 {
+		flag.PrintDefaults()
+		os.Exit(2)
+	}
+
+	d.ReadDropfile(dropfile)
+
+	// doorname - node #?
+	logfilename := fmt.Sprintf("%s-%d.log", doorname, d.Config.Node)
+	logf, err := os.OpenFile(logfilename, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0666)
+	if err != nil {
+		log.Panicf("Error creating log file %s: %v", logfilename, err)
+	}
+
+	log.SetOutput(logf)
+	log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
+	// log.SetPrefix(doorname + " ")
+
+	//= log.New(logf, fmt.Sprintf("%s-%d", doorname, d.Config.Node), log.Ldate|log.Ltime|log.Lshortfile)
+	log.Printf("Loading dropfile %s\n", dropfile)
+
+	log.Printf("BBS %s, User %s / Handle %s / File %d\n", d.Config.BBSID, d.Config.Real_name, d.Config.Handle, d.Config.Comm_handle)
+
+	readerChannel = make(chan byte)
+	writerChannel = make(chan string)
+
+	d.setupChannels()
+
+	d.detect()
+	if Unicode {
+		BOXES = BOXES_UNICODE
+		BARS = BARS_UNICODE
+	} else {
+		BOXES = BOXES_CP437
+		BARS = BARS_CP437
+	}
+}
+
+func Goto(x int, y int) string {
+	return fmt.Sprintf("\x1b[%d;%dH", y, x)
+}

+ 2 - 285
door/door_linux.go

@@ -1,289 +1,6 @@
-/*
-Package door: a golang implementation of a BBS door for linux that
-support door32.sys.
-
-    import (
-			"door"
-		)
-
-		int main() {
-			d = door.Door{}
-			d.Init() // Process commandline switches, initialize door, detect screen size.
-
-			d.Write("Welcome to my awesome door, written in "+door.ColorText("BLINK BOLD WHITE")+"golang"+door.Reset+"."+door.CRNL)
-			d.Write("Press a key...")
-			d.Key()
-			d.Write(door.CRNL)
-		}
-*/
 package door
 
-import (
-	"bufio"
-	"flag"
-	"fmt"
-	"log"
-	"os"
-	"path/filepath"
-	"strconv"
-	"strings"
-	"syscall"
-	"time"
-)
-
-const SavePos = "\x1b[s"              // Save Cursor Position
-const RestorePos = "\x1b[u"           // Restore Cursor Position
-const CRNL = "\r\n"                   // BBS Line Ending
-const Clrscr = "\x1b[0m\x1b[2J\x1b[H" // Clear screen, home cursor
-const HideCursor = "\x1b[?25l"        // Hide Cursor
-const ShowCursor = "\x1b[?25h"        // Show Cursor
-var Reset string = Color(0)           // ANSI Color Reset
-var Unicode bool                      // Unicode support detected
-var CP437 bool                        // CP437 support detected
-var Full_CP437 bool                   // Full CP437 support detected (handles control codes properly)
-var Height int                        // Screen height detected
-var Width int                         // Screen width detected
-var Inactivity int64 = 120            // Inactivity timeout
-
-/*
-door32.sys:
-
-0                            Line 1 : Comm type (0=local, 1=serial, 2=telnet)
-0                            Line 2 : Comm or socket handle
-38400                        Line 3 : Baud rate
-Mystic 1.07                  Line 4 : BBSID (software name and version)
-1                            Line 5 : User record position (1-based)
-James Coyle                  Line 6 : User's real name
-g00r00                       Line 7 : User's handle/alias
-255                          Line 8 : User's security level
-58                           Line 9 : User's time left (in minutes)
-1                            Line 10: Emulation *See Below
-1                            Line 11: Current node number
-*/
-
-type DropfileConfig struct {
-	Comm_type      int
-	Comm_handle    int
-	Baudrate       int
-	BBSID          string
-	User_number    int
-	Real_name      string
-	Handle         string
-	Security_level int
-	Time_left      int
-	Emulation      int
-	Node           int
-}
-
-type Door struct {
-	Config       DropfileConfig
-	READFD       int
-	WRITEFD      int
-	Disconnected bool
-	TimeOut      time.Time // Fixed point in time, when time expires
-	StartTime    time.Time
-	Pushback     FIFOBuffer
-	LastColor    []int
-}
-
-// Return the amount of time left as time.Duration
-func (d *Door) TimeLeft() time.Duration {
-	return time.Until(d.TimeOut)
-	// return d.TimeOut.Sub(time.Now())
-}
-
-func (d *Door) TimeUsed() time.Duration {
-	return time.Since(d.StartTime)
-	// return time.Now().Sub(d.StartTime)
-}
-
-// Read the BBS door file.  We only support door32.sys.
-func (d *Door) ReadDropfile(filename string) {
-	file, err := os.Open(filename)
-	if err != nil {
-		log.Panicf("Open(%s): %s\n", filename, err)
-		// os.Exit(2)
-	}
-
-	defer file.Close()
-
-	var lines []string
-	// read line by line
-
-	// The scanner handles DOS and linux file endings.
-
-	scanner := bufio.NewScanner(file)
-	for scanner.Scan() {
-		line := scanner.Text()
-		lines = append(lines, line)
-	}
-
-	d.Config.Comm_type, err = strconv.Atoi(lines[0])
-	if err != nil {
-		log.Panicf("Door32 Comm Type (expected integer): %s\n", err)
-	}
-	d.Config.Comm_handle, err = strconv.Atoi(lines[1])
-	if err != nil {
-		log.Panicf("Door32 Comm Handle (expected integer): %s\n", err)
-	}
-	d.Config.Baudrate, err = strconv.Atoi(lines[2])
-	if err != nil {
-		log.Panicf("Door32 Baudrate (expected integer): %s\n", err)
-	}
-	d.Config.BBSID = lines[3]
-	d.Config.User_number, err = strconv.Atoi(lines[4])
-	if err != nil {
-		log.Panicf("Door32 User Number (expected integer): %s\n", err)
-	}
-	d.Config.Real_name = lines[5]
-	d.Config.Handle = lines[6]
-	d.Config.Security_level, err = strconv.Atoi(lines[7])
-	if err != nil {
-		log.Panicf("Door32 Security Level (expected integer): %s\n", err)
-	}
-	d.Config.Time_left, err = strconv.Atoi(lines[8])
-	if err != nil {
-		log.Panicf("Door32 Time Left (expected integer): %s\n", err)
-	}
-	d.Config.Emulation, err = strconv.Atoi(lines[9])
-	if err != nil {
-		log.Panicf("Door32 Emulation (expected integer): %s\n", err)
-	}
-	d.Config.Node, err = strconv.Atoi(lines[10])
-	if err != nil {
-		log.Panicf("Door32 Node Number (expected integer): %s\n", err)
-	}
-
-	d.READFD = d.Config.Comm_handle
-	d.WRITEFD = d.Config.Comm_handle
-
-	// Calculate the time when time expires.
-	d.StartTime = time.Now()
-	d.TimeOut = time.Now().Add(time.Duration(d.Config.Time_left) * time.Minute)
-}
-
-// Detect client terminal capabilities, Unicode, CP437, Full_CP437,
-// screen Height and Width.
-func (d *Door) detect() {
-	d.Write("\x1b[0;30;40m\x1b[2J\x1b[H")                          // black on black, clrscr, go home
-	d.Write("\x03\x04\x1b[6n")                                     // hearts and diamonds does CP437 work?
-	d.Write(CRNL + "\u2615\x1b[6n")                                // hot beverage
-	d.Write("\x1b[999C\x1b[999B\x1b[6n" + Reset + "\x1b[2J\x1b[H") // goto end of screen + cursor pos
-	// time.Sleep(50 * time.Millisecond)
-	time.Sleep(250 * time.Millisecond)
-	// read everything
-	// telnet term isn't in RAW mode, so keys are buffer until <CR>
-	var results string
-
-	if d.HasKey() {
-		buffer := make([]byte, 100)
-		r, err := syscall.Read(d.READFD, buffer)
-		if r == -1 {
-			d.Disconnected = true
-			return
-		}
-		results = string(buffer[:r])
-		output := strings.Replace(results, "\x1b", "^[", -1)
-		log.Println("DETECT:", r, err, output)
-	} else {
-		// local telnet echos the reply  :()
-		log.Println("DETECT: Nothing received.")
-		return
-	}
-
-	if (strings.Contains(results, "1;1R") ||
-		strings.Contains(results, "1;3R")) &&
-		(strings.Contains(results, "2:2R") ||
-			strings.Contains(results, "2;3R")) {
-		Unicode = true
-	} else {
-		Unicode = false
-		CP437 = true
-	}
-	if strings.Contains(results, "1;3R") {
-		Full_CP437 = true
-	}
-
-	// get screen size
-
-	pos := strings.LastIndex(results, "\x1b")
-	if pos != -1 {
-		pos++
-		if results[pos] == '[' {
-			pos++
-			results = results[pos:]
-			pos = strings.Index(results, ";")
-			if pos != -1 {
-				height := results[:pos]
-				Height, _ = strconv.Atoi(height)
-				pos++
-				results = results[pos:]
-
-				pos = strings.Index(results, "R")
-				if pos != -1 {
-					width := results[:pos]
-
-					Width, _ = strconv.Atoi(width)
-					// log.Printf("Width: %s, %d, %v\n", results, Width, err)
-				}
-			} else {
-				Height = 0
-				Width = 0
-			}
-		}
-	}
-	log.Printf("Unicode %v Screen: %d X %d\n", Unicode, Width, Height)
-}
-
-// Initialize door framework.  Parse commandline, read dropfile,
-// detect terminal capabilities.
-func (d *Door) Init(doorname string) {
-	var dropfile string
-	d.Pushback = NewFIFOBuffer(5)
-
-	// Get path to binary, and chdir to it.
-	binaryPath, _ := os.Executable()
-	binaryPath = filepath.Dir(binaryPath)
-	_ = os.Chdir(binaryPath)
-
-	flag.StringVar(&dropfile, "d", "", "Path to dropfile")
-	flag.Parse()
-	if len(dropfile) == 0 {
-		flag.PrintDefaults()
-		os.Exit(2)
-	}
-
-	d.ReadDropfile(dropfile)
-
-	// doorname - node #?
-	logfilename := fmt.Sprintf("%s-%d.log", doorname, d.Config.Node)
-	logf, err := os.OpenFile(logfilename, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0666)
-	if err != nil {
-		log.Panicf("Error creating log file %s: %v", logfilename, err)
-	}
-
-	log.SetOutput(logf)
-	log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
-	// log.SetPrefix(doorname + " ")
-
-	//= log.New(logf, fmt.Sprintf("%s-%d", doorname, d.Config.Node), log.Ldate|log.Ltime|log.Lshortfile)
-	log.Printf("Loading dropfile %s\n", dropfile)
-
-	log.Printf("BBS %s, User %s / Handle %s / File %d\n", d.Config.BBSID, d.Config.Real_name, d.Config.Handle, d.Config.Comm_handle)
-
-	writerChannel = make(chan string)
+func (d *Door) setupChannels() {
+	go Reader(d.Config.Comm_handle)
 	go Writer(d.Config.Comm_handle)
-
-	d.detect()
-	if Unicode {
-		BOXES = BOXES_UNICODE
-		BARS = BARS_UNICODE
-	} else {
-		BOXES = BOXES_CP437
-		BARS = BARS_CP437
-	}
-}
-
-func Goto(x int, y int) string {
-	return fmt.Sprintf("\x1b[%d;%dH", y, x)
 }

+ 3 - 281
door/door_windows.go

@@ -1,286 +1,8 @@
-/*
-Package door: a golang implementation of a BBS door for linux that
-support door32.sys.
-
-    import (
-			"door"
-		)
-
-		int main() {
-			d = door.Door{}
-			d.Init() // Process commandline switches, initialize door, detect screen size.
-
-			d.Write("Welcome to my awesome door, written in "+door.ColorText("BLINK BOLD WHITE")+"golang"+door.Reset+"."+door.CRNL)
-			d.Write("Press a key...")
-			d.Key()
-			d.Write(door.CRNL)
-		}
-*/
 package door
 
-import (
-	"bufio"
-	"flag"
-	"fmt"
-	"log"
-	"os"
-	"path/filepath"
-	"strconv"
-	"strings"
-	"syscall"
-	"time"
-)
-
-const CRNL = "\r\n"                   // BBS Line Ending
-const Clrscr = "\x1b[0m\x1b[2J\x1b[H" // Clear screen, home cursor
-const HideCursor = "\x1b[?25l"        // Hide Cursor
-const ShowCursor = "\x1b[?25h"        // Show Cursor
-var Reset string = Color(0)           // ANSI Color Reset
-var Unicode bool                      // Unicode support detected
-var CP437 bool                        // CP437 support detected
-var Full_CP437 bool                   // Full CP437 support detected (handles control codes properly)
-var Height int                        // Screen height detected
-var Width int                         // Screen width detected
-var Inactivity int64 = 120            // Inactivity timeout
-
-/*
-door32.sys:
-
-0                            Line 1 : Comm type (0=local, 1=serial, 2=telnet)
-0                            Line 2 : Comm or socket handle
-38400                        Line 3 : Baud rate
-Mystic 1.07                  Line 4 : BBSID (software name and version)
-1                            Line 5 : User record position (1-based)
-James Coyle                  Line 6 : User's real name
-g00r00                       Line 7 : User's handle/alias
-255                          Line 8 : User's security level
-58                           Line 9 : User's time left (in minutes)
-1                            Line 10: Emulation *See Below
-1                            Line 11: Current node number
-*/
-
-type DropfileConfig struct {
-	Comm_type      int
-	Comm_handle    int
-	Baudrate       int
-	BBSID          string
-	User_number    int
-	Real_name      string
-	Handle         string
-	Security_level int
-	Time_left      int
-	Emulation      int
-	Node           int
-}
-
-type Door struct {
-	Config       DropfileConfig
-	READFD       int
-	WRITEFD      int
-	Disconnected bool
-	TimeOut      time.Time // Fixed point in time, when time expires
-	StartTime    time.Time
-	Pushback     FIFOBuffer
-}
-
-// Return the amount of time left as time.Duration
-func (d *Door) TimeLeft() time.Duration {
-	return d.TimeOut.Sub(time.Now())
-}
-
-func (d *Door) TimeUsed() time.Duration {
-	return time.Now().Sub(d.StartTime)
-}
-
-// Read the BBS door file.  We only support door32.sys.
-func (d *Door) ReadDropfile(filename string) {
-	file, err := os.Open(filename)
-	if err != nil {
-		panic(fmt.Sprintf("Open(%s): %s\n", filename, err))
-		// os.Exit(2)
-	}
-
-	defer file.Close()
-
-	var lines []string
-	// read line by line
-
-	// The scanner handles DOS and linux file endings.
-
-	scanner := bufio.NewScanner(file)
-	for scanner.Scan() {
-		line := scanner.Text()
-		lines = append(lines, line)
-	}
-
-	d.Config.Comm_type, err = strconv.Atoi(lines[0])
-	if err != nil {
-		log.Panicf("Door32 Comm Type (expected integer): %s\n", err)
-	}
-	d.Config.Comm_handle, err = strconv.Atoi(lines[1])
-	if err != nil {
-		log.Panicf("Door32 Comm Handle (expected integer): %s\n", err)
-	}
-	d.Config.Baudrate, err = strconv.Atoi(lines[2])
-	if err != nil {
-		log.Panicf("Door32 Baudrate (expected integer): %s\n", err)
-	}
-	d.Config.BBSID = lines[3]
-	d.Config.User_number, err = strconv.Atoi(lines[4])
-	if err != nil {
-		log.Panicf("Door32 User Number (expected integer): %s\n", err)
-	}
-	d.Config.Real_name = lines[5]
-	d.Config.Handle = lines[6]
-	d.Config.Security_level, err = strconv.Atoi(lines[7])
-	if err != nil {
-		log.Panicf("Door32 Security Level (expected integer): %s\n", err)
-	}
-	d.Config.Time_left, err = strconv.Atoi(lines[8])
-	if err != nil {
-		log.Panicf("Door32 Time Left (expected integer): %s\n", err)
-	}
-	d.Config.Emulation, err = strconv.Atoi(lines[9])
-	if err != nil {
-		log.Panicf("Door32 Emulation (expected integer): %s\n", err)
-	}
-	d.Config.Node, err = strconv.Atoi(lines[10])
-	if err != nil {
-		log.Panicf("Door32 Node Number (expected integer): %s\n", err)
-	}
-
-	d.READFD = d.Config.Comm_handle
-	d.WRITEFD = d.Config.Comm_handle
+import "syscall"
 
-	// Calculate the time when time expires.
-	d.StartTime = time.Now()
-	d.TimeOut = time.Now().Add(time.Duration(d.Config.Time_left) * time.Minute)
-}
-
-// Detect client terminal capabilities, Unicode, CP437, Full_CP437,
-// screen Height and Width.
-func (d *Door) detect() {
-	d.Write("\x1b[0;30;40m\x1b[2J\x1b[H")                          // black on black, clrscr, go home
-	d.Write("\x03\x04\x1b[6n")                                     // hearts and diamonds does CP437 work?
-	d.Write(CRNL + "\u2615\x1b[6n")                                // hot beverage
-	d.Write("\x1b[999C\x1b[999B\x1b[6n" + Reset + "\x1b[2J\x1b[H") // goto end of screen + cursor pos
-	// time.Sleep(50 * time.Millisecond)
-	time.Sleep(250 * time.Millisecond)
-	// read everything
-	// telnet term isn't in RAW mode, so keys are buffer until <CR>
-	var results string = ""
-
-	for {
-		r := d.getch()
-		if r < 0 {
-			// Timeout or Disconnect
-			break
-		}
-		results += string(byte(r))
-	}
-
-	if len(results) > 0 {
-		output := strings.Replace(results, "\x1b", "^[", -1)
-		fmt.Println("DETECT:", output)
-	} else {
-		// local telnet echos the reply  :()
-		fmt.Println("DETECT: Nothing received.")
-		return
-	}
-
-	if ((strings.Index(results, "1;1R") != -1) ||
-		(strings.Index(results, "1;3R") != -1)) &&
-		((strings.Index(results, "2:2R") != -1) ||
-			(strings.Index(results, "2;3R") != -1)) {
-		Unicode = true
-	} else {
-		Unicode = false
-		CP437 = true
-	}
-	if strings.Index(results, "1;3R") != -1 {
-		Full_CP437 = true
-	}
-
-	// get screen size
-	var err error
-
-	pos := strings.LastIndex(results, "\x1b")
-	if pos != -1 {
-		pos++
-		if results[pos] == '[' {
-			pos++
-			results = results[pos:]
-			pos = strings.Index(results, ";")
-			if pos != -1 {
-				height := results[:pos]
-				Height, err = strconv.Atoi(height)
-				pos++
-				results = results[pos:]
-
-				pos = strings.Index(results, "R")
-				if pos != -1 {
-					width := results[:pos]
-
-					Width, err = strconv.Atoi(width)
-					fmt.Printf("Width: %s, %d, %v\n", results, Width, err)
-				}
-			} else {
-				Height = 0
-				Width = 0
-			}
-		}
-	}
-	fmt.Printf("Unicode %v Screen: %d X %d\n", Unicode, Width, Height)
-}
-
-// Initialize door framework.  Parse commandline, read dropfile,
-// detect terminal capabilities.
-func (d *Door) Init(doorname string) {
-	var dropfile string
-	d.Pushback = NewFIFOBuffer(5)
-
-	// Get path to binary, and chdir to it.
-	binaryPath, _ := os.Executable()
-	binaryPath = filepath.Dir(binaryPath)
-	_ = os.Chdir(binaryPath)
-
-	flag.StringVar(&dropfile, "d", "", "Path to dropfile")
-	flag.Parse()
-	if len(dropfile) == 0 {
-		flag.PrintDefaults()
-		os.Exit(2)
-	}
-	// fmt.Printf("Loading: %s\n", dropfile)
-
-	d.ReadDropfile(dropfile)
-
-	// doorname - node #?
-	logfilename := fmt.Sprintf("%s-%d.log", doorname, d.Config.Node)
-	logf, err := os.OpenFile(logfilename, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0666)
-	if err != nil {
-		log.Panicf("Error creating log file %s: %v", logfilename, err)
-	}
-
-	log.SetOutput(logf)
-	log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
-
-	log.Printf("Loading dropfile %s\n", dropfile)
-	log.Printf("BBS %s, User %s / Handle %s / File %d\n", d.Config.BBSID, d.Config.Real_name, d.Config.Handle, d.Config.Comm_handle)
-
-	// Init Windows Reader Channel
-	readerChannel = make(chan byte)
+func (d *Door) setupChannels() {
 	go Reader(syscall.Handle(d.Config.Comm_handle))
-
-	d.detect()
-	if Unicode {
-		BOXES = BOXES_UNICODE
-		BARS = BARS_UNICODE
-	} else {
-		BOXES = BOXES_CP437
-		BARS = BARS_CP437
-	}
-}
-
-func Goto(x int, y int) string {
-	return fmt.Sprintf("\x1b[%d;%dH", y, x)
+	go Writer(syscall.Handle(d.Config.Comm_handle))
 }

+ 337 - 0
door/input.go

@@ -0,0 +1,337 @@
+package door
+
+import (
+	"log"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// This is the current list of Extended keys we support:
+const (
+	XKEY_UP_ARROW    = 0x1001
+	XKEY_DOWN_ARROW  = 0x1002
+	XKEY_RIGHT_ARROW = 0x1003
+	XKEY_LEFT_ARROW  = 0x1004
+	XKEY_HOME        = 0x1010
+	XKEY_END         = 0x1011
+	XKEY_PGUP        = 0x1012
+	XKEY_PGDN        = 0x1023
+	XKEY_INSERT      = 0x1024
+	XKEY_DELETE      = 0x7f
+	XKEY_F1          = 0x1021
+	XKEY_F2          = 0x1022
+	XKEY_F3          = 0x1023
+	XKEY_F4          = 0x1024
+	XKEY_F5          = 0x1025
+	XKEY_F6          = 0x1026
+	XKEY_F7          = 0x1027
+	XKEY_F8          = 0x1028
+	XKEY_F9          = 0x1029
+	XKEY_F10         = 0x102a
+	XKEY_F11         = 0x102b
+	XKEY_F12         = 0x102c
+	XKEY_UNKNOWN     = 0x1111
+)
+
+var readerChannel chan byte
+
+// Low level read key function.
+// This gets the raw keys from the client, it doesn't handle extended keys,
+// functions, arrows.
+// Return key, or -1 (Timeout/No key available), -2 hangup
+func (d *Door) getch() int {
+	select {
+	case res, ok := <-readerChannel:
+		if ok {
+			return int(res)
+		} else {
+			d.Disconnected = true
+			return -2
+		}
+	case <-time.After(time.Duration(100) * time.Millisecond):
+		return -1
+	}
+}
+
+func (d *Door) getkey_or_pushback() int {
+	if !d.Pushback.Empty() {
+		return d.Pushback.Pop()
+	}
+
+	if false {
+		key := d.getch()
+		log.Printf("%d / %X\n", key, key)
+		return key
+	} else {
+		return d.getch()
+	}
+}
+
+// Return key received, or XKEY_* values.
+// -1 timeout/no key
+// -2 hangup
+// -3 out of time
+func (d *Door) GetKey() int {
+	var c, c2 int
+
+	if d.Disconnected {
+		return -2
+	}
+
+	if d.TimeLeft() < 0 {
+		return -3
+	}
+	c = d.getkey_or_pushback()
+
+	if c < 0 {
+		return c
+	}
+
+	// We get 0x0d 0x00, or 0x0d 0x0a from syncterm.
+	if c == 0x0d {
+		c2 = d.getkey_or_pushback()
+		if c2 > 0 {
+			// wasn't an error
+			if c2 != 0x00 && c2 != 0x0a {
+				// wasn't 0x00 or 0x0a
+				d.Pushback.Push(c2)
+				// log.Printf("Push 0x0d trailer %d / %x\n", c2, c2)
+			}
+		}
+		return c
+	}
+
+	if c == 0 {
+		// possibly doorway mode
+		tries := 0
+		c2 = d.getkey_or_pushback()
+		for c2 < 0 {
+			if tries > 7 {
+				return c
+			}
+			c2 = d.getkey_or_pushback()
+			tries++
+		}
+
+		switch c2 {
+		case 0x50:
+			return XKEY_DOWN_ARROW
+		case 0x48:
+			return XKEY_UP_ARROW
+		case 0x4b:
+			return XKEY_LEFT_ARROW
+		case 0x4d:
+			return XKEY_RIGHT_ARROW
+		case 0x47:
+			return XKEY_HOME
+		case 0x4f:
+			return XKEY_END
+		case 0x49:
+			return XKEY_PGUP
+		case 0x51:
+			return XKEY_PGDN
+		case 0x3b:
+			return XKEY_F1
+		case 0x3c:
+			return XKEY_F2
+		case 0x3d:
+			return XKEY_F3
+		case 0x3e:
+			return XKEY_F4
+		case 0x3f:
+			return XKEY_F5
+		case 0x40:
+			return XKEY_F6
+		case 0x41:
+			return XKEY_F7
+		case 0x42:
+			return XKEY_F8
+		case 0x43:
+			return XKEY_F9
+		case 0x44:
+			return XKEY_F10
+		/*
+			case 0x45:
+				return XKEY_F11
+			case 0x46:
+				return XKEY_F12
+		*/
+		case 0x52:
+			return XKEY_INSERT
+		case 0x53:
+			return XKEY_DELETE
+		default:
+			log.Printf("ERROR Doorway mode: 0x00 %x\n", c2)
+			return XKEY_UNKNOWN
+		}
+	}
+
+	if c == 0x1b {
+		// Escape key?
+		c2 = d.getkey_or_pushback()
+		if c2 < 0 {
+			// Just escape key
+			return c
+		}
+		var extended string = string(byte(c2))
+
+		c2 = d.getkey_or_pushback()
+		for c2 > 0 {
+			if c2 == 0x1b {
+				d.Pushback.Push(c2)
+				break
+			}
+			extended += string(byte(c2))
+			c2 = d.getkey_or_pushback()
+		}
+
+		switch extended {
+		case "[A":
+			return XKEY_UP_ARROW
+		case "[B":
+			return XKEY_DOWN_ARROW
+		case "[C":
+			return XKEY_RIGHT_ARROW
+		case "[D":
+			return XKEY_LEFT_ARROW
+		case "[H":
+			return XKEY_HOME
+		case "[F":
+			return XKEY_END // terminal
+		case "[K":
+			return XKEY_END
+		case "[V":
+			return XKEY_PGUP
+		case "[U":
+			return XKEY_PGDN
+		case "[@":
+			return XKEY_INSERT
+		case "[1":
+			// Syncterm is lost, could be F1..F5?
+			log.Printf("ERROR (Syncterm) Extended %#v\n", extended)
+			return XKEY_UNKNOWN
+		case "[2~":
+			return XKEY_INSERT // terminal
+		case "[3~":
+			return XKEY_DELETE // terminal
+		case "[5~":
+			return XKEY_PGUP // terminal
+		case "[6~":
+			return XKEY_PGDN // terminal
+		case "[15~":
+			return XKEY_F5 // terminal
+		case "[17~":
+			return XKEY_F6 // terminal
+		case "[18~":
+			return XKEY_F7 // terminal
+		case "[19~":
+			return XKEY_F8 // terminal
+		case "[20~":
+			return XKEY_F9 // terminal
+		case "[21~":
+			return XKEY_F10 // terminal
+		case "[23~":
+			return XKEY_F11
+		case "[24~":
+			return XKEY_F12 // terminal
+		case "OP":
+			return XKEY_F1
+		case "OQ":
+			return XKEY_F2
+		case "OR":
+			return XKEY_F3
+		case "OS":
+			return XKEY_F4
+		case "Ot":
+			return XKEY_F5 // syncterm
+		default:
+			log.Printf("ERROR Extended %#v\n", extended)
+			return XKEY_UNKNOWN
+		}
+
+	}
+
+	return c
+}
+
+func (d *Door) Key() int {
+	return d.WaitKey(Inactivity, 0)
+}
+
+func (d *Door) WaitKey(secs int64, usecs int64) int {
+	if d.Disconnected {
+		return -2
+	}
+
+	if !d.Pushback.Empty() {
+		return d.GetKey()
+	}
+
+	timeout := time.Duration(secs)*time.Second + time.Duration(usecs)*time.Millisecond
+
+	select {
+	case res, ok := <-readerChannel:
+		if ok {
+			d.Pushback.Push(int(res))
+			return d.GetKey()
+		} else {
+			d.Disconnected = true
+			return -2
+		}
+	case <-time.After(timeout):
+		return -1
+	}
+}
+
+// Outputs spaces and backspaces
+// If you have set a background color, this shows the input area.
+func DisplayInput(max int) string {
+	return strings.Repeat(" ", max) + strings.Repeat("\x08", max)
+}
+
+// Input a string of max length.
+// This displays the input area if a bg color was set.
+// This handles timeout, input, backspace, and enter.
+func (d *Door) Input(max int) string {
+	var line string
+
+	// draw input area
+	d.Write(DisplayInput(max))
+
+	var c int
+
+	for {
+		c = d.WaitKey(Inactivity, 0)
+		if c < 0 {
+			// timeout/hangup
+			return ""
+		}
+
+		if c > 1000 {
+			continue
+		}
+
+		if strconv.IsPrint(rune(c)) {
+			if len(line) < max {
+				d.Write(string(byte(c)))
+				line += string(byte(c))
+			} else {
+				d.Write("\x07")
+			}
+		} else {
+			// Non-print
+			switch c {
+			case 0x7f, 0x08:
+				if len(line) > 0 {
+					d.Write("\x08 \x08")
+					line = line[:len(line)-1]
+				}
+			case 0x0d:
+				return line
+			}
+		}
+	}
+	// this is never reached
+	return line
+}

+ 19 - 371
door/input_linux.go

@@ -2,82 +2,31 @@ package door
 
 import (
 	"log"
-	"strconv"
-	"strings"
 	"syscall"
 )
 
-// This is the current list of Extended keys we support:
-const (
-	XKEY_UP_ARROW    = 0x1001
-	XKEY_DOWN_ARROW  = 0x1002
-	XKEY_RIGHT_ARROW = 0x1003
-	XKEY_LEFT_ARROW  = 0x1004
-	XKEY_HOME        = 0x1010
-	XKEY_END         = 0x1011
-	XKEY_PGUP        = 0x1012
-	XKEY_PGDN        = 0x1023
-	XKEY_INSERT      = 0x1024
-	XKEY_DELETE      = 0x7f
-	XKEY_F1          = 0x1021
-	XKEY_F2          = 0x1022
-	XKEY_F3          = 0x1023
-	XKEY_F4          = 0x1024
-	XKEY_F5          = 0x1025
-	XKEY_F6          = 0x1026
-	XKEY_F7          = 0x1027
-	XKEY_F8          = 0x1028
-	XKEY_F9          = 0x1029
-	XKEY_F10         = 0x102a
-	XKEY_F11         = 0x102b
-	XKEY_F12         = 0x102c
-	XKEY_UNKNOWN     = 0x1111
-)
-
-// from: https://github.com/yubo/dea_ng
-// https://github.com/yubo/dea_ng/blob/master/go/src/directoryserver/streaming.go
-
-func set(fdSetPtr *syscall.FdSet, fd int) {
-	(*fdSetPtr).Bits[fd/64] |= 1 << uint64(fd%64)
-}
-
-/*
-func isSet(fdSetPtr *syscall.FdSet, fd int) bool {
-	return ((*fdSetPtr).Bits[fd/64] & (1 << uint64(fd%64))) != 0
-}
-*/
-
-func clearAll(fdSetPtr *syscall.FdSet) {
-	for index := range (*fdSetPtr).Bits {
-		(*fdSetPtr).Bits[index] = 0
-	}
-}
-
-// Is there a key waiting?
-// Returns true on key, or if hungup/time out
-func (d *Door) HasKey() bool {
-	if d.Disconnected {
-		return true
-	}
+func Reader(handle int) {
+	// I don't need the select anymore.  Let the read block.
 
-	if d.TimeLeft() < 0 {
-		return true
-	}
-
-	var fdsetRead = syscall.FdSet{}
-	clearAll(&fdsetRead)
-	set(&fdsetRead, d.READFD)
-	timeout := syscall.Timeval{Sec: 0, Usec: 1}
-	v, _ := syscall.Select(d.READFD+1, &fdsetRead, nil, nil, &timeout)
-	if v == -1 {
-		return false
-	}
-	if v == 0 {
-		return false
+	buffer := make([]byte, 1)
+	for {
+		r, err := syscall.Read(d.READFD, buffer)
+		if err != nil {
+			log.Printf("Reader ERR: %#v\n", err)
+			close(readerChannel)
+			break
+		}
+		if read == 1 {
+			readerChannel <- buffer[0]
+		} else {
+			log.Printf("READ FAILED %d\n", read)
+			close(readerChannel)
+			break
+		}
 	}
-	return true
 }
 
+/*
 // Low level read key function.
 // This gets the raw keys from the client, it doesn't handle extended keys,
 // functions, arrows.
@@ -111,305 +60,4 @@ func (d *Door) getch() int {
 	return int(buffer[0])
 }
 
-func (d *Door) getkey_or_pushback() int {
-	if !d.Pushback.Empty() {
-		return d.Pushback.Pop()
-	}
-
-	if false {
-		key := d.getch()
-		log.Printf("%d / %X\n", key, key)
-		return key
-	} else {
-		return d.getch()
-	}
-}
-
-// Return key received, or XKEY_* values.
-// -1 timeout/no key
-// -2 hangup
-// -3 out of time
-func (d *Door) GetKey() int {
-	var c, c2 int
-
-	if d.Disconnected {
-		return -2
-	}
-
-	if d.TimeLeft() < 0 {
-		return -3
-	}
-	c = d.getkey_or_pushback()
-
-	if c < 0 {
-		return c
-	}
-
-	// We get 0x0d 0x00, or 0x0d 0x0a from syncterm.
-	if c == 0x0d {
-		c2 = d.getkey_or_pushback()
-		if c2 > 0 {
-			// wasn't an error
-			if c2 != 0x00 && c2 != 0x0a {
-				// wasn't 0x00 or 0x0a
-				d.Pushback.Push(c2)
-				// log.Printf("Push 0x0d trailer %d / %x\n", c2, c2)
-			}
-		}
-		return c
-	}
-
-	if c == 0 {
-		// possibly doorway mode
-		tries := 0
-		c2 = d.getkey_or_pushback()
-		for c2 < 0 {
-			if tries > 7 {
-				return c
-			}
-			c2 = d.getkey_or_pushback()
-			tries++
-		}
-
-		switch c2 {
-		case 0x50:
-			return XKEY_DOWN_ARROW
-		case 0x48:
-			return XKEY_UP_ARROW
-		case 0x4b:
-			return XKEY_LEFT_ARROW
-		case 0x4d:
-			return XKEY_RIGHT_ARROW
-		case 0x47:
-			return XKEY_HOME
-		case 0x4f:
-			return XKEY_END
-		case 0x49:
-			return XKEY_PGUP
-		case 0x51:
-			return XKEY_PGDN
-		case 0x3b:
-			return XKEY_F1
-		case 0x3c:
-			return XKEY_F2
-		case 0x3d:
-			return XKEY_F3
-		case 0x3e:
-			return XKEY_F4
-		case 0x3f:
-			return XKEY_F5
-		case 0x40:
-			return XKEY_F6
-		case 0x41:
-			return XKEY_F7
-		case 0x42:
-			return XKEY_F8
-		case 0x43:
-			return XKEY_F9
-		case 0x44:
-			return XKEY_F10
-		/*
-			case 0x45:
-				return XKEY_F11
-			case 0x46:
-				return XKEY_F12
-		*/
-		case 0x52:
-			return XKEY_INSERT
-		case 0x53:
-			return XKEY_DELETE
-		default:
-			log.Printf("ERROR Doorway mode: 0x00 %x\n", c2)
-			return XKEY_UNKNOWN
-		}
-	}
-
-	if c == 0x1b {
-		// Escape key?
-		c2 = d.getkey_or_pushback()
-		if c2 < 0 {
-			// Just escape key
-			return c
-		}
-		var extended string = string(byte(c2))
-
-		c2 = d.getkey_or_pushback()
-		for c2 > 0 {
-			if c2 == 0x1b {
-				d.Pushback.Push(c2)
-				break
-			}
-			extended += string(byte(c2))
-			c2 = d.getkey_or_pushback()
-		}
-
-		switch extended {
-		case "[A":
-			return XKEY_UP_ARROW
-		case "[B":
-			return XKEY_DOWN_ARROW
-		case "[C":
-			return XKEY_RIGHT_ARROW
-		case "[D":
-			return XKEY_LEFT_ARROW
-		case "[H":
-			return XKEY_HOME
-		case "[F":
-			return XKEY_END // terminal
-		case "[K":
-			return XKEY_END
-		case "[V":
-			return XKEY_PGUP
-		case "[U":
-			return XKEY_PGDN
-		case "[@":
-			return XKEY_INSERT
-		case "[1":
-			// Syncterm is lost, could be F1..F5?
-			log.Printf("ERROR (Syncterm) Extended %#v\n", extended)
-			return XKEY_UNKNOWN
-		case "[2~":
-			return XKEY_INSERT // terminal
-		case "[3~":
-			return XKEY_DELETE // terminal
-		case "[5~":
-			return XKEY_PGUP // terminal
-		case "[6~":
-			return XKEY_PGDN // terminal
-		case "[15~":
-			return XKEY_F5 // terminal
-		case "[17~":
-			return XKEY_F6 // terminal
-		case "[18~":
-			return XKEY_F7 // terminal
-		case "[19~":
-			return XKEY_F8 // terminal
-		case "[20~":
-			return XKEY_F9 // terminal
-		case "[21~":
-			return XKEY_F10 // terminal
-		case "[23~":
-			return XKEY_F11
-		case "[24~":
-			return XKEY_F12 // terminal
-		case "OP":
-			return XKEY_F1
-		case "OQ":
-			return XKEY_F2
-		case "OR":
-			return XKEY_F3
-		case "OS":
-			return XKEY_F4
-		case "Ot":
-			return XKEY_F5 // syncterm
-		default:
-			log.Printf("ERROR Extended %#v\n", extended)
-			return XKEY_UNKNOWN
-		}
-
-	}
-
-	return c
-}
-
-func (d *Door) Key() int {
-	return d.WaitKey(Inactivity, 0)
-}
-
-func (d *Door) WaitKey(secs int64, usecs int64) int {
-	if d.Disconnected {
-		return -2
-	}
-
-	if !d.Pushback.Empty() {
-		return d.GetKey()
-	}
-
-	var fdsetRead syscall.FdSet
-
-retry:
-	clearAll(&fdsetRead)
-	set(&fdsetRead, d.READFD)
-
-	timeout := syscall.Timeval{Sec: secs, Usec: usecs}
-	v, err := syscall.Select(d.READFD+1, &fdsetRead, nil, nil, &timeout)
-
-	if v == -1 {
-		errno, ok := err.(syscall.Errno)
-		if ok && errno == syscall.EINTR {
-			// Should I retry my syscall.Select here?  Yes! -1 is not return value.
-			log.Printf("WaitKey: EINTR\n")
-			goto retry
-			// return -1
-		}
-
-		/*
-			Interrupted system call ???  What does this mean?
-			This isn't hangup.  I'm still connected.
-			-1 :  interrupted system call
-			This seemed to happen when I called WaitKey many times with usecs.
-		*/
-
-		log.Println("-1 : ", err)
-		// hangup ?!
-		return -2
-	}
-	if v == 0 {
-		// timeout
-		return -1
-	}
-
-	return d.GetKey()
-}
-
-// Outputs spaces and backspaces
-// If you have set a background color, this shows the input area.
-func DisplayInput(max int) string {
-	return strings.Repeat(" ", max) + strings.Repeat("\x08", max)
-}
-
-// Input a string of max length.
-// This displays the input area if a bg color was set.
-// This handles timeout, input, backspace, and enter.
-func (d *Door) Input(max int) string {
-	var line string
-
-	// draw input area
-	d.Write(DisplayInput(max))
-
-	var c int
-
-	for {
-		c = d.WaitKey(Inactivity, 0)
-		if c < 0 {
-			// timeout/hangup
-			return ""
-		}
-
-		if c > 1000 {
-			continue
-		}
-
-		if strconv.IsPrint(rune(c)) {
-			if len(line) < max {
-				d.Write(string(byte(c)))
-				line += string(byte(c))
-			} else {
-				d.Write("\x07")
-			}
-		} else {
-			// Non-print
-			switch c {
-			case 0x7f, 0x08:
-				if len(line) > 0 {
-					d.Write("\x08 \x08")
-					line = line[:len(line)-1]
-				}
-			case 0x0d:
-				return line
-			}
-		}
-	}
-	// this is never reached
-	return line
-}
+*/

+ 0 - 333
door/input_windows.go

@@ -2,42 +2,9 @@ package door
 
 import (
 	"log"
-	"strconv"
-	"strings"
 	"syscall"
-	"time"
 )
 
-// This is the current list of Extended keys we support:
-const (
-	XKEY_UP_ARROW    = 0x1001
-	XKEY_DOWN_ARROW  = 0x1002
-	XKEY_RIGHT_ARROW = 0x1003
-	XKEY_LEFT_ARROW  = 0x1004
-	XKEY_HOME        = 0x1010
-	XKEY_END         = 0x1011
-	XKEY_PGUP        = 0x1012
-	XKEY_PGDN        = 0x1023
-	XKEY_INSERT      = 0x1024
-	XKEY_DELETE      = 0x7f
-	XKEY_F1          = 0x1021
-	XKEY_F2          = 0x1022
-	XKEY_F3          = 0x1023
-	XKEY_F4          = 0x1024
-	XKEY_F5          = 0x1025
-	XKEY_F6          = 0x1026
-	XKEY_F7          = 0x1027
-	XKEY_F8          = 0x1028
-	XKEY_F9          = 0x1029
-	XKEY_F10         = 0x102a
-	XKEY_F11         = 0x102b
-	XKEY_F12         = 0x102c
-	XKEY_UNKNOWN     = 0x1111
-)
-
-// go routine to read from the socket
-var readerChannel chan byte
-
 func Reader(handle syscall.Handle) {
 	buffer := make([]byte, 1)
 	WSA_Buffer := syscall.WSABuf{Len: 1, Buf: &buffer[0]}
@@ -61,303 +28,3 @@ func Reader(handle syscall.Handle) {
 		}
 	}
 }
-
-// Low level read key function.
-// This gets the raw keys from the client, it doesn't handle extended keys,
-// functions, arrows.
-// Return key, or -1 (Timeout/No key available), -2 hangup
-func (d *Door) getch() int {
-	select {
-	case res, ok := <-readerChannel:
-		if ok {
-			return int(res)
-		} else {
-			d.Disconnected = true
-			return -2
-		}
-	case <-time.After(time.Duration(100) * time.Millisecond):
-		return -1
-	}
-}
-
-func (d *Door) getkey_or_pushback() int {
-	if !d.Pushback.Empty() {
-		return d.Pushback.Pop()
-	}
-
-	if false {
-		key := d.getch()
-		log.Printf("%d / %X\n", key, key)
-		return key
-	} else {
-		return d.getch()
-	}
-}
-
-// Return key received, or XKEY_* values.
-// -1 timeout/no key
-// -2 hangup
-// -3 out of time
-func (d *Door) GetKey() int {
-	var c, c2 int
-
-	if d.Disconnected {
-		return -2
-	}
-
-	if d.TimeLeft() < 0 {
-		return -3
-	}
-	c = d.getkey_or_pushback()
-
-	if c < 0 {
-		return c
-	}
-
-	// We get 0x0d 0x00, or 0x0d 0x0a from syncterm.
-	if c == 0x0d {
-		c2 = d.getkey_or_pushback()
-		if c2 > 0 {
-			// wasn't an error
-			if c2 != 0x00 && c2 != 0x0a {
-				// wasn't 0x00 or 0x0a
-				d.Pushback.Push(c2)
-				// log.Printf("Push 0x0d trailer %d / %x\n", c2, c2)
-			}
-		}
-		return c
-	}
-
-	if c == 0 {
-		// possibly doorway mode
-		tries := 0
-		c2 = d.getkey_or_pushback()
-		for c2 < 0 {
-			if tries > 7 {
-				return c
-			}
-			c2 = d.getkey_or_pushback()
-			tries++
-		}
-
-		switch c2 {
-		case 0x50:
-			return XKEY_DOWN_ARROW
-		case 0x48:
-			return XKEY_UP_ARROW
-		case 0x4b:
-			return XKEY_LEFT_ARROW
-		case 0x4d:
-			return XKEY_RIGHT_ARROW
-		case 0x47:
-			return XKEY_HOME
-		case 0x4f:
-			return XKEY_END
-		case 0x49:
-			return XKEY_PGUP
-		case 0x51:
-			return XKEY_PGDN
-		case 0x3b:
-			return XKEY_F1
-		case 0x3c:
-			return XKEY_F2
-		case 0x3d:
-			return XKEY_F3
-		case 0x3e:
-			return XKEY_F4
-		case 0x3f:
-			return XKEY_F5
-		case 0x40:
-			return XKEY_F6
-		case 0x41:
-			return XKEY_F7
-		case 0x42:
-			return XKEY_F8
-		case 0x43:
-			return XKEY_F9
-		case 0x44:
-			return XKEY_F10
-		/*
-			case 0x45:
-				return XKEY_F11
-			case 0x46:
-				return XKEY_F12
-		*/
-		case 0x52:
-			return XKEY_INSERT
-		case 0x53:
-			return XKEY_DELETE
-		default:
-			log.Printf("ERROR Doorway mode: 0x00 %x\n", c2)
-			return XKEY_UNKNOWN
-		}
-	}
-
-	if c == 0x1b {
-		// Escape key?
-		c2 = d.getkey_or_pushback()
-		if c2 < 0 {
-			// Just escape key
-			return c
-		}
-		var extended string = string(byte(c2))
-
-		c2 = d.getkey_or_pushback()
-		for c2 > 0 {
-			if c2 == 0x1b {
-				d.Pushback.Push(c2)
-				break
-			}
-			extended += string(byte(c2))
-			c2 = d.getkey_or_pushback()
-		}
-
-		switch extended {
-		case "[A":
-			return XKEY_UP_ARROW
-		case "[B":
-			return XKEY_DOWN_ARROW
-		case "[C":
-			return XKEY_RIGHT_ARROW
-		case "[D":
-			return XKEY_LEFT_ARROW
-		case "[H":
-			return XKEY_HOME
-		case "[F":
-			return XKEY_END // terminal
-		case "[K":
-			return XKEY_END
-		case "[V":
-			return XKEY_PGUP
-		case "[U":
-			return XKEY_PGDN
-		case "[@":
-			return XKEY_INSERT
-		case "[1":
-			// Syncterm is lost, could be F1..F5?
-			log.Printf("ERROR (Syncterm) Extended %#v\n", extended)
-			return XKEY_UNKNOWN
-		case "[2~":
-			return XKEY_INSERT // terminal
-		case "[3~":
-			return XKEY_DELETE // terminal
-		case "[5~":
-			return XKEY_PGUP // terminal
-		case "[6~":
-			return XKEY_PGDN // terminal
-		case "[15~":
-			return XKEY_F5 // terminal
-		case "[17~":
-			return XKEY_F6 // terminal
-		case "[18~":
-			return XKEY_F7 // terminal
-		case "[19~":
-			return XKEY_F8 // terminal
-		case "[20~":
-			return XKEY_F9 // terminal
-		case "[21~":
-			return XKEY_F10 // terminal
-		case "[23~":
-			return XKEY_F11
-		case "[24~":
-			return XKEY_F12 // terminal
-		case "OP":
-			return XKEY_F1
-		case "OQ":
-			return XKEY_F2
-		case "OR":
-			return XKEY_F3
-		case "OS":
-			return XKEY_F4
-		case "Ot":
-			return XKEY_F5 // syncterm
-		default:
-			log.Printf("ERROR Extended %#v\n", extended)
-			return XKEY_UNKNOWN
-		}
-
-	}
-
-	return c
-}
-
-func (d *Door) Key() int {
-	return d.WaitKey(Inactivity, 0)
-}
-
-func (d *Door) WaitKey(secs int64, usecs int64) int {
-	if d.Disconnected {
-		return -2
-	}
-
-	if !d.Pushback.Empty() {
-		return d.GetKey()
-	}
-
-	timeout := time.Duration(secs)*time.Second + time.Duration(usecs)*time.Millisecond
-
-	select {
-	case res, ok := <-readerChannel:
-		if ok {
-			d.Pushback.Push(int(res))
-			return d.GetKey()
-		} else {
-			d.Disconnected = true
-			return -2
-		}
-	case <-time.After(timeout):
-		return -1
-	}
-}
-
-// Outputs spaces and backspaces
-// If you have set a background color, this shows the input area.
-func DisplayInput(max int) string {
-	return strings.Repeat(" ", max) + strings.Repeat("\x08", max)
-}
-
-// Input a string of max length.
-// This displays the input area if a bg color was set.
-// This handles timeout, input, backspace, and enter.
-func (d *Door) Input(max int) string {
-	var line string
-
-	// draw input area
-	d.Write(DisplayInput(max))
-
-	var c int
-
-	for {
-		c = d.WaitKey(Inactivity, 0)
-		if c < 0 {
-			// timeout/hangup
-			return ""
-		}
-
-		if c > 1000 {
-			continue
-		}
-
-		if strconv.IsPrint(rune(c)) {
-			if len(line) < max {
-				d.Write(string(byte(c)))
-				line += string(byte(c))
-			} else {
-				d.Write("\x07")
-			}
-		} else {
-			// Non-print
-			switch c {
-			case 0x7f, 0x08:
-				if len(line) > 0 {
-					d.Write("\x08 \x08")
-					line = line[:len(line)-1]
-				}
-			case 0x0d:
-				return line
-			}
-		}
-	}
-	// this is never reached
-	return line
-}

+ 166 - 0
door/write.go

@@ -0,0 +1,166 @@
+package door
+
+import (
+	"log"
+	"regexp"
+	"strconv"
+	"strings"
+)
+
+var FindANSIColor *regexp.Regexp
+
+func init() {
+	FindANSIColor = regexp.MustCompile("\x1b\\[([0-9;]*)m")
+}
+
+func find_ansicolor(text string) [][]int {
+	var color_codes [][]int
+
+	// word, _ := regexp.Compile("\x1b\\[([0-9;]+)m")
+	colors := FindANSIColor.FindAllStringIndex(text, -1)
+	// regexp seems to be ignoring the capture groups.
+	// colors := word.FindAllSubmatchIndex([]byte(text), len(text))
+	for _, pos := range colors {
+		txt := text[pos[0]+2 : pos[1]-1]
+		if txt == "" {
+			txt = "0"
+		}
+		// log.Printf("Text: [%s]\n", txt)
+		codes := strings.Split(txt, ";")
+		// log.Printf("Codes: [%#v]\n", codes)
+		code := make([]int, len(codes))
+		for idx, c := range codes {
+			var err error
+			code[idx], err = strconv.Atoi(c)
+			if err != nil {
+				log.Printf("Atoi: %#v [%s]\n", err, c)
+			}
+			color_codes = append(color_codes, code)
+		}
+
+	}
+	return color_codes
+}
+
+type ANSIColorParts struct {
+	Bold  bool
+	Blink bool
+	FG    int
+	BG    int
+}
+
+func ParseColorArray(colorarray []int) ANSIColorParts {
+	var acp ANSIColorParts
+	acp.FG = -1
+	acp.BG = -1
+
+	for _, c := range colorarray {
+		switch c {
+		case 0:
+			acp.FG = 7
+			acp.BG = 0
+		case 1:
+			acp.Bold = true
+		case 5:
+			acp.Blink = true
+		case 30, 31, 32, 33, 34, 35, 36, 37:
+			acp.FG = c - 30
+		case 40, 41, 42, 43, 44, 45, 46, 47:
+			acp.BG = c - 40
+		}
+	}
+	return acp
+}
+
+func (d *Door) ParseLastColor(output string) {
+	// use the last color information + whatever is in the string to
+	// track the last color set
+	updated := ParseColorArray(d.LastColor)
+	colors := find_ansicolor(output)
+	for _, codes := range colors {
+		if codes[0] == 0 {
+			updated = ParseColorArray(codes)
+		} else {
+			newCode := ParseColorArray(codes)
+			if newCode.Bold {
+				updated.Bold = true
+			}
+			if newCode.Blink {
+				updated.Blink = true
+			}
+			if (newCode.FG != -1) && (newCode.FG != updated.FG) {
+				updated.FG = newCode.FG
+			}
+			if (newCode.BG != -1) && (newCode.BG != updated.BG) {
+				updated.BG = newCode.BG
+			}
+		}
+	}
+
+	d.LastColor = make([]int, 1)
+	d.LastColor[0] = 0
+	if updated.Blink {
+		d.LastColor = append(d.LastColor, 5)
+	}
+	if updated.Bold {
+		d.LastColor = append(d.LastColor, 1)
+	}
+	if updated.FG != -1 {
+		d.LastColor = append(d.LastColor, updated.FG+30)
+	}
+	if updated.BG != -1 {
+		d.LastColor = append(d.LastColor, updated.BG+40)
+	}
+}
+
+/*
+reading from a closed channel is easy to detect.
+
+res, ok := <-channel
+ok == false
+
+writing to a closed channel is a panic.
+*/
+
+var writerChannel chan string
+
+// Write string to client.
+func (d *Door) Write(output string) {
+	if d.Disconnected {
+		return
+	}
+
+	defer func() {
+		if r := recover(); r != nil {
+			log.Println("Write error/HANGUP.")
+			d.Disconnected = true
+		}
+	}()
+
+	if strings.HasSuffix(output, RestorePos) {
+		output += Color(d.LastColor...)
+	} else {
+		d.ParseLastColor(output)
+	}
+
+	/*
+		temp := strings.Replace(output, "\x1b", "^[", -1)
+		log.Printf("Parse: [%s]\n", temp)
+	*/
+
+	writerChannel <- output
+
+	/*
+		buffer := []byte(output)
+		n, err := syscall.Write(d.WRITEFD, buffer)
+		if err != nil {
+			fmt.Println("Write error/HANGUP?", n)
+			d.Disconnected = true
+		}
+		// No, this isn't it.  The # of bytes in buffer == bytes written.
+		if n != len(buffer) {
+			fmt.Printf("Write fail: %d != %d\n", len(buffer), n)
+		}
+	*/
+
+}

+ 0 - 162
door/write_linux.go

@@ -1,15 +1,9 @@
 package door
 
 import (
-	"log"
-	"regexp"
-	"strconv"
-	"strings"
 	"syscall"
 )
 
-var writerChannel chan string
-
 func Writer(handle int) {
 	for output := range writerChannel {
 		buffer := []byte(output)
@@ -20,159 +14,3 @@ func Writer(handle int) {
 		}
 	}
 }
-
-var FindANSIColor *regexp.Regexp
-
-func init() {
-	FindANSIColor = regexp.MustCompile("\x1b\\[([0-9;]*)m")
-}
-
-/*
-reading from a closed channel is easy to detect.
-
-res, ok := <-channel
-ok == false
-
-writing to a closed channel is a panic.
-
-*/
-func find_ansicolor(text string) [][]int {
-	var color_codes [][]int
-
-	// word, _ := regexp.Compile("\x1b\\[([0-9;]+)m")
-	colors := FindANSIColor.FindAllStringIndex(text, -1)
-	// regexp seems to be ignoring the capture groups.
-	// colors := word.FindAllSubmatchIndex([]byte(text), len(text))
-	for _, pos := range colors {
-		txt := text[pos[0]+2 : pos[1]-1]
-		if txt == "" {
-			txt = "0"
-		}
-		// log.Printf("Text: [%s]\n", txt)
-		codes := strings.Split(txt, ";")
-		// log.Printf("Codes: [%#v]\n", codes)
-		code := make([]int, len(codes))
-		for idx, c := range codes {
-			var err error
-			code[idx], err = strconv.Atoi(c)
-			if err != nil {
-				log.Printf("Atoi: %#v [%s]\n", err, c)
-			}
-			color_codes = append(color_codes, code)
-		}
-
-	}
-	return color_codes
-}
-
-type ANSIColorParts struct {
-	Bold  bool
-	Blink bool
-	FG    int
-	BG    int
-}
-
-func ParseColorArray(colorarray []int) ANSIColorParts {
-	var acp ANSIColorParts
-	acp.FG = -1
-	acp.BG = -1
-
-	for _, c := range colorarray {
-		switch c {
-		case 0:
-			acp.FG = 7
-			acp.BG = 0
-		case 1:
-			acp.Bold = true
-		case 5:
-			acp.Blink = true
-		case 30, 31, 32, 33, 34, 35, 36, 37:
-			acp.FG = c - 30
-		case 40, 41, 42, 43, 44, 45, 46, 47:
-			acp.BG = c - 40
-		}
-	}
-	return acp
-}
-
-func (d *Door) ParseLastColor(output string) {
-	// use the last color information + whatever is in the string to
-	// track the last color set
-	updated := ParseColorArray(d.LastColor)
-	colors := find_ansicolor(output)
-	for _, codes := range colors {
-		if codes[0] == 0 {
-			updated = ParseColorArray(codes)
-		} else {
-			newCode := ParseColorArray(codes)
-			if newCode.Bold {
-				updated.Bold = true
-			}
-			if newCode.Blink {
-				updated.Blink = true
-			}
-			if (newCode.FG != -1) && (newCode.FG != updated.FG) {
-				updated.FG = newCode.FG
-			}
-			if (newCode.BG != -1) && (newCode.BG != updated.BG) {
-				updated.BG = newCode.BG
-			}
-		}
-	}
-
-	d.LastColor = make([]int, 1)
-	d.LastColor[0] = 0
-	if updated.Blink {
-		d.LastColor = append(d.LastColor, 5)
-	}
-	if updated.Bold {
-		d.LastColor = append(d.LastColor, 1)
-	}
-	if updated.FG != -1 {
-		d.LastColor = append(d.LastColor, updated.FG+30)
-	}
-	if updated.BG != -1 {
-		d.LastColor = append(d.LastColor, updated.BG+40)
-	}
-}
-
-// Write string to client.
-func (d *Door) Write(output string) {
-	if d.Disconnected {
-		return
-	}
-
-	defer func() {
-		if r := recover(); r != nil {
-			log.Println("Write error/HANGUP.")
-			d.Disconnected = true
-		}
-	}()
-
-	if strings.HasSuffix(output, RestorePos) {
-		output += Color(d.LastColor...)
-	} else {
-		d.ParseLastColor(output)
-	}
-
-	/*
-		temp := strings.Replace(output, "\x1b", "^[", -1)
-		log.Printf("Parse: [%s]\n", temp)
-	*/
-
-	writerChannel <- output
-
-	/*
-		buffer := []byte(output)
-		n, err := syscall.Write(d.WRITEFD, buffer)
-		if err != nil {
-			fmt.Println("Write error/HANGUP?", n)
-			d.Disconnected = true
-		}
-		// No, this isn't it.  The # of bytes in buffer == bytes written.
-		if n != len(buffer) {
-			fmt.Printf("Write fail: %d != %d\n", len(buffer), n)
-		}
-	*/
-
-}

+ 15 - 27
door/write_windows.go

@@ -5,33 +5,21 @@ import (
 	"syscall"
 )
 
-func Write(handle syscall.Handle, output string) {
-	l := len(output)
-	buffer := []byte(output)
-	WSA_Buffer := syscall.WSABuf{Len: uint32(l), Buf: &buffer[0]}
-	UitnZero_1 := uint32(0)
-	DataWrite := uint32(0)
-	err := syscall.WSASend(handle, &WSA_Buffer, 1, &DataWrite, UitnZero_1, nil, nil)
-	if err != nil {
-		fmt.Printf("write: %d bytes, error: %#v\n", DataWrite, err)
-	}
-}
-
-// Write string to client.
-func (d *Door) Write(output string) {
-	if d.Disconnected {
-		return
-	}
+func Writer(handle syscall.Handle) {
+	for output := range writerChannel {
+		l := uint32(len(output))
+		buffer := []byte(output)
+		WSA_Buffer := syscall.WSABuf{Len: uint32(l), Buf: &buffer[0]}
+		UitnZero_1 := uint32(0)
+		DataWrite := uint32(0)
+		err := syscall.WSASend(handle, &WSA_Buffer, 1, &DataWrite, UitnZero_1, nil, nil)
+		if err != nil {
+			fmt.Printf("write: %d bytes, error: %#v\n", DataWrite, err)
+		}
 
-	handle := syscall.Handle(d.WRITEFD)
-	l := len(output)
-	buffer := []byte(output)
-	WSA_Buffer := syscall.WSABuf{Len: uint32(l), Buf: &buffer[0]}
-	UitnZero_1 := uint32(0)
-	DataWrite := uint32(0)
-	err := syscall.WSASend(handle, &WSA_Buffer, 1, &DataWrite, UitnZero_1, nil, nil)
-	if err != nil {
-		fmt.Printf("write: %d bytes, error: %#v\n", DataWrite, err)
-		d.Disconnected = true
+		if (err != nil) || (l != DataWrite) {
+			close(writerChannel)
+			break
+		}
 	}
 }