Guest User

Untitled

a guest
Nov 1st, 2020
258
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Go 6.78 KB | None | 0 0
  1.  
  2. import (
  3.     "bufio"
  4.     "errors"
  5.     "fmt"
  6.     "net"
  7.     "os"
  8.     "strconv"
  9.     "strings"
  10. )
  11.  
  12. type resource struct {
  13.     itemType rune
  14.     name     string
  15.     selector string
  16.     host     string
  17.     port     int
  18.     accessor string
  19. }
  20.  
  21. type context struct {
  22.     history []resource
  23.     home    resource
  24. }
  25.  
  26. // Create new context
  27. func NewContext() context {
  28.     var ctx context
  29.     ctx.home = resource{itemType: '1', name: "floodgap", host: "gopher.floodgap.com", selector: "", port: 70}
  30.     ctx.history = append(ctx.history, ctx.home)
  31.     return ctx
  32. }
  33.  
  34. // Set context to specific resource
  35. func (ctx *context) Navigate(res resource) {
  36.     ctx.history = append(ctx.history, res)
  37. }
  38.  
  39. // Navigate to host
  40. func (ctx *context) NavigateHost(host string) {
  41.     res := resource{itemType: '1', name: "", host: host, selector: "", port: 70}
  42.     ctx.history = append(ctx.history, res)
  43. }
  44. // Get current Resource
  45. func (ctx *context) Resource() resource {
  46.     depth := len(ctx.history)
  47.     if depth == 0 {
  48.         return ctx.home
  49.     }
  50.     return ctx.history[depth-1]
  51. }
  52.  
  53. // Navigate to home page
  54. func (ctx *context) Home() {
  55.     ctx.history = append(ctx.history, ctx.home)
  56. }
  57.  
  58. // Go back to the previous resource
  59. func (ctx *context) Back() {
  60.     depth := len(ctx.history)
  61.     if depth == 0 {
  62.         return
  63.     }
  64.     ctx.history = ctx.history[:depth-1]
  65. }
  66.  
  67. type AccessorIterator struct {
  68.     current int
  69. }
  70.  
  71. func NewAccessorIterator() AccessorIterator {
  72.     return AccessorIterator{current: 0}
  73. }
  74.  
  75. func max(a, b int) int {
  76.     if a > b {
  77.         return a
  78.     }
  79.     return b
  80. }
  81.  
  82. // Get next accessor in resource
  83. func (i *AccessorIterator) Next() string {
  84.     const accessorSpace string = "abcdefghijklmnopqrstuvwxyz"
  85.     var n, r, figures int = i.current, 0, 0
  86.     var out string = ""
  87.     for n > 0 {
  88.         n, r = n/26, n%26
  89.         out = string(accessorSpace[r]) + out
  90.         figures++
  91.     }
  92.     out = "aa" + out
  93.     i.current++
  94.     // return at least two characters
  95.     return out[len(out)-max(figures, 2):]
  96. }
  97.  
  98. type parseError struct {
  99. }
  100.  
  101. func (e *parseError) Error() string {
  102.     return "parse error"
  103. }
  104.  
  105. // Parse single menu resource
  106. func parseMenuResource(reader *bufio.Reader) (resource, error) {
  107.     res := resource{}
  108.  
  109.     // read one rune for item type
  110.     itemType, _, err := reader.ReadRune()
  111.     if err != nil {
  112.         return res, &parseError{}
  113.     }
  114.     res.itemType = itemType
  115.  
  116.     // read name until tab
  117.     name, err := reader.ReadString('\t')
  118.     if err != nil {
  119.         return res, &parseError{}
  120.     }
  121.     res.name = strings.TrimSuffix(name, "\t")
  122.  
  123.     // read selector until tab
  124.     selector, err := reader.ReadString('\t')
  125.     if err != nil {
  126.         return res, &parseError{}
  127.     }
  128.     res.selector = strings.TrimSuffix(selector, "\t")
  129.  
  130.     // read host until tab
  131.     host, err := reader.ReadString('\t')
  132.     if err != nil {
  133.         return res, &parseError{}
  134.     }
  135.     res.host = strings.TrimSuffix(host, "\t")
  136.  
  137.     // read port until crlf
  138.     port, err := reader.ReadString('\r')
  139.     if err != nil {
  140.         return res, &parseError{}
  141.     }
  142.     port = strings.TrimSuffix(port, "\r")
  143.     // convert port to integer
  144.     pi, err := strconv.Atoi(port)
  145.     if err != nil {
  146.         fmt.Println(err)
  147.         return res, &parseError{}
  148.     }
  149.     res.port = pi
  150.  
  151.     // read newline
  152.     reader.ReadByte()
  153.  
  154.     return res, nil
  155. }
  156.  
  157. // Print menu resource
  158. func printMenuResource(res resource) {
  159.     switch res.itemType {
  160.     case 'i', '3':
  161.         fmt.Printf("     %s\n", res.name)
  162.     default:
  163.         fmt.Printf("[\u001b[32;1m%s\u001b[0m] \u001b[36m%s\u001b[0m\n", res.accessor, res.name)
  164.     }
  165. }
  166.  
  167. // handle a menu response and return a selector dict
  168. func handleMenu(reader *bufio.Reader) map[string]resource {
  169.     iter := NewAccessorIterator()
  170.     accessors := make(map[string]resource)
  171.     for {
  172.         // parse resource
  173.         res, err := parseMenuResource(reader)
  174.         if err != nil {
  175.             return accessors
  176.         }
  177.  
  178.         // add accessor if required and optimise retrieval
  179.         if res.itemType != 'i' {
  180.             res.accessor = iter.Next()
  181.             accessors[res.accessor] = res
  182.         }
  183.  
  184.         printMenuResource(res)
  185.     }
  186.  
  187. }
  188. func handleTextFile(reader *bufio.Reader) map[string]resource {
  189.     for {
  190.         line, _, err := reader.ReadLine()
  191.         if err != nil {
  192.             return nil
  193.         }
  194.         fmt.Printf("%s\n", string(line))
  195.     }
  196.     return nil
  197. }
  198.  
  199. // handle a resource request
  200. func handle(res resource) (map[string]resource, error) {
  201.     // dial out to remote server
  202.     conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", res.host, res.port))
  203.     if err != nil {
  204.         fmt.Printf("could not connect to %s:%d: %s\n", res.host, res.port, err)
  205.         return nil, err
  206.     }
  207.     defer conn.Close()
  208.     // send selector and get a connection reader
  209.     conn.Write([]byte(fmt.Sprintf("%s\r\n", res.selector)))
  210.     reader := bufio.NewReader(conn)
  211.  
  212.     // dispatch to handler
  213.     switch res.itemType {
  214.     case '0':
  215.         return handleTextFile(reader), nil
  216.     case '1':
  217.         return handleMenu(reader), nil
  218.     default:
  219.         fmt.Printf("no handler for item type '%s'\n", string(res.itemType))
  220.         return nil, errors.New("no handler")
  221.     }
  222.     return nil, nil
  223. }
  224. func repl(ctx context) {
  225.     m := make(map[string]resource)
  226.     reader := bufio.NewReader(os.Stdin)
  227.  
  228.     for {
  229.         res := ctx.Resource()
  230.         lastMap, err := handle(res)
  231.         if err != nil {
  232.             ctx.Back()
  233.             res = ctx.Resource()
  234.         } else {
  235.             m = lastMap
  236.         }
  237.         fmt.Printf("\n[\u001b[32;1mh\u001b[0m]=home [\u001b[32;1mq\u001b[0m]=quit [\u001b[32;1m.\u001b[0m]=back [\u001b[32;1mg host\u001b[0m]=goto\n\u001b[36m%s:%s\u001b[0m > ", res.host, res.selector)
  238.         command, err := reader.ReadString('\n')
  239.  
  240.         // exit if we receive an EOF from ctrl+d
  241.         if err != nil {
  242.             return
  243.         }
  244.  
  245.         // dispatch command
  246.         command = strings.TrimSuffix(command, "\n")
  247.         switch command {
  248.         // home
  249.         case "h":
  250.             ctx.Home()
  251.         // quit
  252.         case "q":
  253.             return
  254.         // refresh
  255.         case "*":
  256.             break
  257.         // back
  258.         case ".":
  259.             ctx.Back()
  260.         // accessor
  261.         default:
  262.             // handle goto
  263.             if strings.HasPrefix(command, "g ") {
  264.                 ctx.NavigateHost(command[2:])
  265.                 break
  266.             }
  267.             // handle accessor
  268.             if val, ok := m[command]; ok {
  269.                 ctx.Navigate(val)
  270.             } else {
  271.                 fmt.Printf("Unknown accessor %s\n", command)
  272.             }
  273.         }
  274.     }
  275. }
  276.  
  277. func main() {
  278.     ctx := NewContext()
  279.     repl(ctx)
  280. }
  281.  
Advertisement
Add Comment
Please, Sign In to add comment