Преглед на файлове

Missing some windows files.

Steve Thielemann преди 3 години
родител
ревизия
b15c1d86e0
променени са 2 файла, в които са добавени 631 реда и са изтрити 0 реда
  1. 269 0
      door/door_windows.go
  2. 362 0
      door/input_windows.go

+ 269 - 0
door/door_windows.go

@@ -0,0 +1,269 @@
+/*
+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"
+	"os"
+	"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
+
+type LIFOBuffer struct {
+	data  []int
+	index int
+}
+
+func (b *LIFOBuffer) Empty() bool {
+	return b.index == 0
+}
+
+func (b *LIFOBuffer) Push(value int) {
+	if b.index+1 > len(b.data) {
+		b.data = append(b.data, value)
+		b.index++
+	} else {
+		b.data[b.index] = value
+		b.index++
+	}
+}
+
+func (b *LIFOBuffer) Pop() int {
+	if b.index == 0 {
+		panic("Attempting to Pop from empty LIFOBuffer.")
+	}
+
+	b.index--
+	return b.data[b.index]
+}
+
+/*
+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     LIFOBuffer
+}
+
+// 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])
+	d.Config.Comm_handle, err = strconv.Atoi(lines[1])
+	d.Config.Baudrate, err = strconv.Atoi(lines[2])
+	d.Config.BBSID = lines[3]
+	d.Config.User_number, err = strconv.Atoi(lines[4])
+	d.Config.Real_name = lines[5]
+	d.Config.Handle = lines[6]
+	d.Config.Security_level, err = strconv.Atoi(lines[7])
+	d.Config.Time_left, err = strconv.Atoi(lines[8])
+	d.Config.Emulation, err = strconv.Atoi(lines[9])
+	d.Config.Node, err = strconv.Atoi(lines[10])
+
+	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 == -1 {
+			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() {
+	var dropfile string
+
+	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)
+
+	fmt.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)
+
+	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)
+}

+ 362 - 0
door/input_windows.go

@@ -0,0 +1,362 @@
+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]}
+	read := uint32(0)
+	flags := uint32(0)
+
+	for {
+		err := syscall.WSARecv(handle, &WSA_Buffer, 1, &read, &flags, nil, nil)
+		if err != nil {
+			log.Printf("Reader ERR: %#v\n", err)
+			break
+		}
+
+		if read == 1 {
+			readerChannel <- buffer[0]
+		} else {
+			log.Printf("READ FAILED %d\n", read)
+			close(readerChannel)
+			break
+		}
+	}
+}
+
+// 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 true {
+		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
+}