1
1
# This file is a part of Julia. License is MIT: https://julialang.org/license
2
2
3
+ # Since this code is in the startup-path, we go to some effort to
4
+ # be easier on the compiler, such as using `map` over broadcasting.
5
+
3
6
include (" terminfo_data.jl" )
4
7
5
8
"""
@@ -15,7 +18,7 @@ particular capabilities, solely based on `term(5)`.
15
18
16
19
- `names::Vector{String}`: The names this terminal is known by.
17
20
- `flags::BitVector`: A list of 0–$(length (TERM_FLAGS)) flag values.
18
- - `numbers::Union{Vector{UInt16 }, Vector{UInt32 }}`: A list of 0–$(length (TERM_NUMBERS))
21
+ - `numbers::Union{Vector{Int16 }, Vector{Int32 }}`: A list of 0–$(length (TERM_NUMBERS))
19
22
number values. A value of `typemax(eltype(numbers))` is used to skip over
20
23
unspecified capabilities while ensuring value indices are correct.
21
24
- `strings::Vector{Union{String, Nothing}}`: A list of 0–$(length (TERM_STRINGS))
@@ -30,9 +33,9 @@ See also: `TermInfo` and `TermCapability`.
30
33
struct TermInfoRaw
31
34
names:: Vector{String}
32
35
flags:: BitVector
33
- numbers:: Union{ Vector{UInt16}, Vector{UInt32} }
36
+ numbers:: Vector{Int }
34
37
strings:: Vector{Union{String, Nothing}}
35
- extended:: Union{Nothing, Dict{Symbol, Union{Bool, Int, String}}}
38
+ extended:: Union{Nothing, Dict{Symbol, Union{Bool, Int, String, Nothing }}}
36
39
end
37
40
38
41
"""
@@ -59,94 +62,105 @@ See also: `TermInfoRaw` and `TermCapability`.
59
62
"""
60
63
struct TermInfo
61
64
names:: Vector{String}
62
- flags:: Int
63
- numbers:: BitVector
64
- strings:: BitVector
65
- extensions:: Vector{Symbol}
66
- capabilities:: Dict{Symbol, Union{Bool, Int, String}}
65
+ flags:: Dict{Symbol, Bool}
66
+ numbers:: Dict{Symbol, Int}
67
+ strings:: Dict{Symbol, String}
68
+ extensions:: Union{Nothing, Set{Symbol}}
67
69
end
68
70
69
- TermInfo () = TermInfo ([], 0 , [], [], [], Dict ())
71
+ TermInfo () = TermInfo ([], Dict (), Dict (), Dict (), nothing )
70
72
71
73
function read (data:: IO , :: Type{TermInfoRaw} )
72
74
# Parse according to `term(5)`
73
75
# Header
74
76
magic = read (data, UInt16) |> ltoh
75
77
NumInt = if magic == 0o0432
76
- UInt16
78
+ Int16
77
79
elseif magic == 0o01036
78
- UInt32
80
+ Int32
79
81
else
80
82
throw (ArgumentError (" Terminfo data did not start with the magic number 0o0432 or 0o01036" ))
81
83
end
82
- name_bytes = read (data, UInt16) |> ltoh
83
- flag_bytes = read (data, UInt16) |> ltoh
84
- numbers_count = read (data, UInt16) |> ltoh
85
- string_count = read (data, UInt16) |> ltoh
86
- table_bytes = read (data, UInt16) |> ltoh
84
+ name_bytes, flag_bytes, numbers_count, string_count, table_bytes =
85
+ @ntuple 5 _-> read (data, Int16) |> ltoh
87
86
# Terminal Names
88
- term_names = split (String (read (data, name_bytes - 1 )), ' |' ) .| > String
87
+ term_names = map (String, split (String (read (data, name_bytes - 1 )), ' |' ))
89
88
0x00 == read (data, UInt8) ||
90
89
throw (ArgumentError (" Terminfo data did not contain a null byte after the terminal names section" ))
91
90
# Boolean Flags
92
- flags = read (data, flag_bytes) .== 0x01
91
+ flags = map ( == ( 0x01 ), read (data, flag_bytes))
93
92
if position (data) % 2 != 0
94
93
0x00 == read (data, UInt8) ||
95
94
throw (ArgumentError (" Terminfo did not contain a null byte after the flag section, expected to position the start of the numbers section on an even byte" ))
96
95
end
97
96
# Numbers, Strings, Table
98
- numbers = map (ltoh, reinterpret (NumInt, read (data, numbers_count * sizeof (NumInt))))
99
- string_indices = map (ltoh, reinterpret (UInt16 , read (data, string_count * sizeof (UInt16 ))))
97
+ numbers = map (Int ∘ ltoh, reinterpret (NumInt, read (data, numbers_count * sizeof (NumInt))))
98
+ string_indices = map (ltoh, reinterpret (Int16 , read (data, string_count * sizeof (Int16 ))))
100
99
strings_table = read (data, table_bytes)
101
- strings = map (string_indices) do idx
102
- if idx ∉ (0xffff , 0xfffe )
103
- len = findfirst (== (0x00 ), view (strings_table, 1 + idx: length (strings_table)))
104
- ! isnothing (len) ||
105
- throw (ArgumentError (" Terminfo string table entry does not terminate with a null byte" ))
106
- String (strings_table[1 + idx: idx+ len- 1 ])
107
- end
108
- end
100
+ strings = _terminfo_read_strings (strings_table, string_indices)
109
101
TermInfoRaw (term_names, flags, numbers, strings,
110
102
if ! eof (data) extendedterminfo (data, NumInt) end )
111
103
end
112
104
113
105
"""
114
- extendedterminfo(data::IO; NumInt::Union{Type{UInt16 }, Type{UInt32 }})
106
+ extendedterminfo(data::IO; NumInt::Union{Type{Int16 }, Type{Int32 }})
115
107
116
108
Read an extended terminfo section from `data`, with `NumInt` as the numbers type.
117
109
118
110
This will accept any terminfo content that conforms with `term(5)`.
119
111
120
112
See also: `read(::IO, ::Type{TermInfoRaw})`
121
113
"""
122
- function extendedterminfo (data:: IO , NumInt:: Union{Type{UInt16 }, Type{UInt32 }} )
114
+ function extendedterminfo (data:: IO , NumInt:: Union{Type{Int16 }, Type{Int32 }} )
123
115
# Extended info
124
116
if position (data) % 2 != 0
125
117
0x00 == read (data, UInt8) ||
126
- throw (ArgumentError (" Terminfo did not contain a null byte before the extended section, expected to position the start on an even byte" ))
118
+ throw (ArgumentError (" Terminfo did not contain a null byte before the extended section; expected to position the start on an even byte" ))
127
119
end
128
120
# Extended header
129
- flag_bytes = read (data, UInt16) |> ltoh
130
- numbers_count = read (data, UInt16) |> ltoh
131
- string_count = read (data, UInt16) |> ltoh
132
- table_count = read (data, UInt16) |> ltoh
133
- table_bytes = read (data, UInt16) |> ltoh
121
+ flag_bytes, numbers_count, string_count, table_count, table_bytes =
122
+ @ntuple 5 _-> read (data, Int16) |> ltoh
134
123
# Extended flags/numbers/strings
135
- flags = read (data, flag_bytes) .== 0x01
124
+ flags = map ( == ( 0x01 ), read (data, flag_bytes))
136
125
if flag_bytes % 2 != 0
137
126
0x00 == read (data, UInt8) ||
138
- throw (ArgumentError (" Terminfo did not contain a null byte after the extended flag section, expected to position the start of the numbers section on an even byte" ))
127
+ throw (ArgumentError (" Terminfo did not contain a null byte after the extended flag section; expected to position the start of the numbers section on an even byte" ))
128
+ end
129
+ numbers = map (Int ∘ ltoh, reinterpret (NumInt, read (data, numbers_count * sizeof (NumInt))))
130
+ table_indices = map (ltoh, reinterpret (Int16, read (data, table_count * sizeof (Int16))))
131
+ table_data = read (data, table_bytes)
132
+ strings = _terminfo_read_strings (table_data, table_indices[1 : string_count])
133
+ table_halfoffset = Int16 (get (table_indices, string_count, 0 ) +
134
+ ncodeunits (something (get (strings, length (strings), " " ), " " )) + 1 )
135
+ for index in string_count+ 1 : lastindex (table_indices)
136
+ table_indices[index] += table_halfoffset
139
137
end
140
- numbers = map (n -> Int (ltoh (n)), reinterpret (NumInt, read (data, numbers_count * sizeof (NumInt))))
141
- table_indices = map (ltoh, reinterpret (UInt16, read (data, table_count * sizeof (UInt16))))
142
- table_strings = [String (readuntil (data, 0x00 )) for _ in 1 : length (table_indices)]
143
- info = Dict {Symbol, Union{Bool, Int, String}} ()
144
- strings = table_strings[1 : string_count]
145
- labels = table_strings[string_count+ 1 : end ]
146
- for (label, val) in zip (labels, vcat (flags, numbers, strings))
147
- info[Symbol (label)] = val
138
+ labels = map (Symbol, _terminfo_read_strings (table_data, table_indices[string_count+ 1 : end ]))
139
+ Dict {Symbol, Union{Bool, Int, String, Nothing}} (
140
+ zip (labels, Iterators. flatten ((flags, numbers, strings))))
141
+ end
142
+
143
+ """
144
+ _terminfo_read_strings(table::Vector{UInt8}, indices::Vector{Int16})
145
+
146
+ From `table`, read a string starting at each position in `indices`. Each string
147
+ must be null-terminated. Should an index be -1 or -2, `nothing` is given instead
148
+ of a string.
149
+ """
150
+ function _terminfo_read_strings (table:: Vector{UInt8} , indices:: Vector{Int16} )
151
+ strings = Vector {Union{Nothing, String}} (undef, length (indices))
152
+ map! (strings, indices) do idx
153
+ if idx >= 0
154
+ len = findfirst (== (0x00 ), view (table, 1 + idx: length (table)))
155
+ ! isnothing (len) ||
156
+ throw (ArgumentError (" Terminfo table entry @$idx does not terminate with a null byte" ))
157
+ String (table[1 + idx: idx+ len- 1 ])
158
+ elseif idx ∈ (- 1 , - 2 )
159
+ else
160
+ throw (ArgumentError (" Terminfo table index is invalid: -2 ≰ $idx " ))
161
+ end
148
162
end
149
- return info
163
+ strings
150
164
end
151
165
152
166
"""
@@ -158,45 +172,60 @@ NCurses 6.3, see `TERM_FLAGS`, `TERM_NUMBERS`, and `TERM_STRINGS`).
158
172
function TermInfo (raw:: TermInfoRaw )
159
173
capabilities = Dict {Symbol, Union{Bool, Int, String}} ()
160
174
sizehint! (capabilities, 2 * (length (raw. flags) + length (raw. numbers) + length (raw. strings)))
175
+ flags = Dict {Symbol, Bool} ()
176
+ numbers = Dict {Symbol, Int} ()
177
+ strings = Dict {Symbol, String} ()
178
+ extensions = nothing
161
179
for (flag, value) in zip (TERM_FLAGS, raw. flags)
162
- capabilities [flag. short] = value
163
- capabilities [flag. long] = value
180
+ flags [flag. short] = value
181
+ flags [flag. long] = value
164
182
end
165
183
for (num, value) in zip (TERM_NUMBERS, raw. numbers)
166
- if value != typemax (eltype (raw. numbers))
167
- capabilities[num. short] = Int (value)
168
- capabilities[num. long] = Int (value)
169
- end
184
+ numbers[num. short] = Int (value)
185
+ numbers[num. long] = Int (value)
170
186
end
171
187
for (str, value) in zip (TERM_STRINGS, raw. strings)
172
188
if ! isnothing (value)
173
- capabilities [str. short] = value
174
- capabilities [str. long] = value
189
+ strings [str. short] = value
190
+ strings [str. long] = value
175
191
end
176
192
end
177
- extensions = if ! isnothing (raw. extended)
178
- capabilities = merge (capabilities, raw. extended)
179
- keys (raw. extended) |> collect
180
- else
181
- Symbol[]
193
+ if ! isnothing (raw. extended)
194
+ extensions = Set {Symbol} ()
195
+ for (key, value) in raw. extended
196
+ push! (extensions, key)
197
+ if value isa Bool
198
+ flags[key] = value
199
+ elseif value isa Int
200
+ numbers[key] = value
201
+ elseif value isa String
202
+ strings[key] = value
203
+ end
204
+ end
182
205
end
183
- TermInfo (raw. names, length (raw. flags),
184
- map (n-> n != typemax (typeof (n)), raw. numbers),
185
- map (! isnothing, raw. strings),
186
- extensions, capabilities)
206
+ TermInfo (raw. names, flags, numbers, strings, extensions)
207
+ end
208
+
209
+ get (ti:: TermInfo , key:: Symbol , default:: Bool ) = get (ti. flags, key, default)
210
+ get (ti:: TermInfo , key:: Symbol , default:: Int ) = get (ti. numbers, key, default)
211
+ get (ti:: TermInfo , key:: Symbol , default:: String ) = get (ti. strings, key, default)
212
+
213
+ haskey (ti:: TermInfo , key:: Symbol ) =
214
+ haskey (ti. flags, key) || haskey (ti. numbers, key) || haskey (ti. strings, key)
215
+
216
+ function getindex (ti:: TermInfo , key:: Symbol )
217
+ haskey (ti. flags, key) && return ti. flags[key]
218
+ haskey (ti. numbers, key) && return ti. numbers[key]
219
+ haskey (ti. strings, key) && return ti. strings[key]
220
+ throw (KeyError (key))
187
221
end
188
222
189
- getindex (ti:: TermInfo , key:: Symbol ) = ti. capabilities[key]
190
- get (ti:: TermInfo , key:: Symbol , default:: D ) where D<: Union{Bool, Int, String} =
191
- get (ti. capabilities, key, default):: D
192
- get (ti:: TermInfo , key:: Symbol , default) = get (ti. capabilities, key, default)
193
- keys (ti:: TermInfo ) = keys (ti. capabilities)
194
- haskey (ti:: TermInfo , key:: Symbol ) = haskey (ti. capabilities, key)
223
+ keys (ti:: TermInfo ) = keys (ti. flags) ∪ keys (ti. numbers) ∪ keys (ti. strings)
195
224
196
225
function show (io:: IO , :: MIME"text/plain" , ti:: TermInfo )
197
- print (io, " TermInfo(" , ti. names, " ; " , ti. flags, " flags, " ,
198
- sum (ti. numbers), " numbers, " , sum (ti. strings), " strings" )
199
- ! isempty (ti. extensions) > 0 &&
226
+ print (io, " TermInfo(" , ti. names, " ; " , length ( ti. flags) , " flags, " ,
227
+ length (ti. numbers), " numbers, " , length (ti. strings), " strings" )
228
+ ! isnothing (ti. extensions) &&
200
229
print (io, " , " , length (ti. extensions), " extended capabilities" )
201
230
print (io, ' )' )
202
231
end
@@ -216,13 +245,15 @@ function find_terminfo_file(term::String)
216
245
[ENV [" TERMINFO" ]]
217
246
elseif isdir (joinpath (homedir (), " .terminfo" ))
218
247
[joinpath (homedir (), " .terminfo" )]
219
- elseif haskey (ENV , " TERMINFO_DIRS" )
220
- split (ENV [" TERMINFO_DIRS" ], ' :' )
221
- elseif Sys. isunix ()
222
- [" /usr/share/terminfo" ]
223
248
else
224
249
String[]
225
250
end
251
+ haskey (ENV , " TERMINFO_DIRS" ) &&
252
+ append! (terminfo_dirs,
253
+ replace (split (ENV [" TERMINFO_DIRS" ], ' :' ),
254
+ " " => " /usr/share/terminfo" ))
255
+ Sys. isunix () &&
256
+ push! (terminfo_dirs, " /etc/terminfo" , " /usr/share/terminfo" )
226
257
for dir in terminfo_dirs
227
258
if isfile (joinpath (dir, chr, term))
228
259
return joinpath (dir, chr, term)
0 commit comments