about summary refs log tree commit diff stats
path: root/adapter/format/gopher2html.nim
blob: cfdeab1e8d7aa17ebe2b31076122ddadc35b621d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# This file is dedicated to the public domain.
# Gopher directory -> HTML converter for Chawan.

import std/os
import std/streams
import std/strutils

import utils/twtstr

type GopherType = enum
  UNKNOWN = "unsupported"
  TEXT_FILE = "text file"
  ERROR = "error"
  DIRECTORY = "directory"
  DOS_BINARY = "DOS binary"
  SEARCH = "search"
  MESSAGE = "message"
  SOUND = "sound"
  GIF = "gif"
  HTML = "HTML"
  INFO = ""
  IMAGE = "image"
  BINARY = "binary"
  PNG = "png"

func gopherType(c: char): GopherType =
  return case c
  of '0': TEXT_FILE
  of '1': DIRECTORY
  of '3': ERROR
  of '5': DOS_BINARY
  of '7': SEARCH
  of 'm': MESSAGE
  of 's': SOUND
  of 'g': GIF
  of 'h': HTML
  of 'i': INFO
  of 'I': IMAGE
  of '9': BINARY
  of 'p': PNG
  else: UNKNOWN

const ControlPercentEncodeSet = {char(0x00)..char(0x1F), char(0x7F)..char(0xFF)}
const QueryPercentEncodeSet = (ControlPercentEncodeSet + {' ', '"', '#', '<', '>'})
const PathPercentEncodeSet = (QueryPercentEncodeSet + {'?', '`', '{', '}'})
const HexCharsUpper = "0123456789ABCDEF"
proc percentEncode(s: string): string =
  result = ""
  for c in s:
    if c notin PathPercentEncodeSet:
      result &= c
    else:
      result &= '%'
      result &= HexCharsUpper[uint8(c) shr 4]
      result &= HexCharsUpper[uint8(c) and 0xF]

# returns URL
proc parseParams(): string =
  result = ""
  let params = commandLineParams()
  var was_u = false
  for param in params:
    if was_u:
      result = param
      was_u = false
    elif param == "-h" or param == "--help":
      stdout.write("Usage: gopher2html [-u URL]")
      quit(0)
    elif param == "-u":
      was_u = true
    else:
      stdout.write("Usage: gopher2html [-u URL]")
      quit(1)

proc main() =
  let url = parseParams()
  let escapedURL = htmlEscape(url)
  stdout.write("""
<!DOCTYPE HTML>
<HTML>
<HEAD>
<BASE HREF="""" & url & """">
<TITLE>Index of """ & escapedURL & """</TITLE>
</HEAD>
<BODY>
<H1>Index of """ & escapedURL & """</H1>""")
  let ins = newFileStream(stdin)
  var ispre = false
  while not ins.atEnd:
    let line = ins.readLine()
    if line.len == 0:
      break # invalid
    let tc = line[0]
    if tc == '.':
      break # end
    var i = 1
    template get_field(): string =
      let s = line.until('\t', i)
      i += s.len
      if i >= line.len or line[i] != '\t':
        break # invalid
      inc i
      s
    let t = gopherType(tc)
    let name = get_field()
    var file = get_field()
    let host = get_field()
    let port = line.until('\t', i) # ignore anything after port
    var outs = ""
    if t == INFO:
      if not ispre:
        outs &= "<PRE>"
        ispre = true
      outs &= htmlEscape(name) & '\n'
    else:
      if ispre:
        outs &= "</PRE>"
        ispre = false
      let ts = $t
      var names = ""
      if ts != "":
        names &= '[' & ts & ']'
      names &= htmlEscape(name)
      let ourls = if not file.startsWith("URL:"):
        if file.len == 0 or file[0] != '/':
          file = '/' & file
        let pefile = percentEncode(file)
        "gopher://" & host & ":" & port & "/" & tc & pefile
      else:
        file.substr("URL:".len)
      outs &= "<A HREF=\"" & htmlEscape(ourls) & "\">" & names & "</A><BR>\n"
    stdout.write(outs)

main()