termtime 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "termtime/db"
4
+
5
+ module TermTime
6
+ class TermInfo
7
+ class Number
8
+ attr_reader :value, :db
9
+
10
+ def initialize v, db
11
+ @value = v
12
+ @db = db
13
+ freeze
14
+ end
15
+
16
+ def tiname; @db.tiname; end
17
+ end
18
+
19
+ class String
20
+ attr_reader :value, :db
21
+
22
+ def initialize v, db
23
+ @value = v
24
+ @db = db
25
+ freeze
26
+ end
27
+
28
+ def tiname; @db.tiname; end
29
+
30
+ def tparm(...)
31
+ TermTime.tparm(value, ...)
32
+ end
33
+ end
34
+
35
+ def initialize fname = ENV["TERM"]
36
+ unless File.file? fname
37
+ fname = "/usr/share/terminfo/#{fname.getbyte(0).to_s(16)}/#{fname}"
38
+ end
39
+
40
+ File.open fname, "rb" do |f|
41
+ magic,
42
+ names_size,
43
+ booleans_size,
44
+ short_int_count,
45
+ offset_count,
46
+ string_table_size = f.read((2 * 6)).unpack("ssssss")
47
+
48
+ raise unless magic == 0432
49
+ @names = f.read(names_size - 1).force_encoding("UTF-8").freeze
50
+ raise unless f.readbyte == 0
51
+ @flags = f.read(booleans_size - 1)
52
+
53
+ f.readbyte
54
+ f.readbyte if f.pos % 2 != 0 # alignment
55
+
56
+ @numbers = f.read(short_int_count * 2)
57
+ strings = f.read(offset_count * 2)
58
+ string_table = f.read(string_table_size)
59
+ @strings = decode_strings(strings, string_table)
60
+ end
61
+ end
62
+
63
+ def flags
64
+ flags = []
65
+ @flags.bytesize.times do |i|
66
+ if @flags.getbyte(i) > 0
67
+ flags << DB::BOOLEAN_LIST.fetch(i)
68
+ end
69
+ end
70
+ flags
71
+ end
72
+
73
+ def numbers
74
+ numbers = []
75
+ @numbers.unpack("s*").each_with_index do |v, i|
76
+ next if v == -1
77
+ numbers << Number.new(v, DB::NUMBER_LIST.fetch(i))
78
+ end
79
+ numbers
80
+ end
81
+
82
+ def strings
83
+ @strings.values
84
+ end
85
+
86
+ def getnum name
87
+ offset = DB::NUMBERS.fetch(name).i * 2
88
+ @numbers.getbyte(offset) | (@numbers.getbyte(offset + 1) << 8)
89
+ end
90
+
91
+ def getflag name
92
+ @flags.getbyte(DB::BOOLEANS.fetch(name).i) == 1
93
+ end
94
+
95
+ def getstr name
96
+ str(name).value
97
+ end
98
+
99
+ def str name
100
+ @strings.fetch(name)
101
+ end
102
+
103
+ def str? name
104
+ @strings.key?(name)
105
+ end
106
+
107
+ def names
108
+ @names.split("|")
109
+ end
110
+
111
+ private
112
+
113
+ def decode_strings str_offsets, string_table
114
+ strings = {}
115
+ str_offsets.unpack("s*").each_with_index do |v, i|
116
+ next if v == -1
117
+ next if i >= DB::STRING_LIST.length
118
+
119
+ len = string_table.byteindex("\0", v) - v
120
+ str = string_table.byteslice(v, len).freeze
121
+ x = DB::STRING_LIST.fetch(i)
122
+ strings[x.tiname] = String.new(str, x)
123
+ end
124
+ strings
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,3 @@
1
+ module TermTime
2
+ VERSION = "1.0.0"
3
+ end
data/lib/termtime.rb ADDED
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "strscan"
4
+
5
+ module TermTime
6
+ autoload :TermInfo, "termtime/terminfo"
7
+ autoload :DB, "termtime/db"
8
+
9
+ def self.tparm fmt, p1 = 0, p2 = 0, p3 = 0, p4 = 0, p5 = 0, p6 = 0, p7 = 0, p8 = 0, p9 = 0
10
+ return fmt unless fmt =~ /%/
11
+
12
+ regs = [p1, p2, p3, p4, p5, p6, p7, p8, p9]
13
+ stack = []
14
+
15
+ scan = StringScanner.new fmt
16
+ buff = ''.b
17
+
18
+ while !scan.eos?
19
+ start = scan.pos
20
+ case
21
+ when scan.skip(/%i/)
22
+ regs[0] += 1
23
+ regs[1] += 1
24
+ when scan.skip(/%t/)
25
+ if stack.pop == 0
26
+ scan.skip_until(/%e/)
27
+ else
28
+ end
29
+ when scan.skip(/%c/)
30
+ buff << stack.pop
31
+ when scan.skip(/%</)
32
+ y = stack.pop
33
+ x = stack.pop
34
+ stack << (x < y ? 1 : 0)
35
+ when scan.skip(/%>/)
36
+ y = stack.pop
37
+ x = stack.pop
38
+ stack << (x > y ? 1 : 0)
39
+ when scan.skip(/%\+/) then stack << (stack.pop + stack.pop)
40
+ when scan.skip(/%\*/) then stack << (stack.pop * stack.pop)
41
+ when scan.skip(/%-/)
42
+ y = stack.pop
43
+ x = stack.pop
44
+ stack << (x - y)
45
+ when scan.skip(/%\//)
46
+ y = stack.pop
47
+ x = stack.pop
48
+ stack << (x / y)
49
+ when scan.skip(/%m/)
50
+ y = stack.pop
51
+ x = stack.pop
52
+ stack << (x % y)
53
+ when scan.skip(/%!/) then stack << (stack.pop == 0 ? 1 : 0)
54
+ when scan.skip(/%=/) then stack << (stack.pop == stack.pop ? 1 : 0)
55
+ when scan.skip(/%%/) then buff << "%"
56
+ when scan.skip(/%e/)
57
+ # if we made it to %e, we must have been in the true branch,
58
+ # so skip until after %;
59
+ if !scan.skip_until(/%;/)
60
+ break;
61
+ end
62
+ when scan.skip(/%l/) then raise NotImplementedError
63
+ when scan.skip(/%&/) then stack << (stack.pop & stack.pop)
64
+ when scan.skip(/%\|/) then stack << (stack.pop | stack.pop)
65
+ when scan.skip(/%\^/) then stack << (stack.pop ^ stack.pop)
66
+ when scan.skip(/%~/) then stack << (~stack.pop)
67
+ when scan.skip(/%'.'/) then stack << fmt.getbyte(start + 2)
68
+ when scan.skip(/%A/)
69
+ stack << (stack.pop != 0 && stack.pop != 0 ? 1 : 0)
70
+ when scan.skip(/%O/)
71
+ stack << (stack.pop != 0 || stack.pop != 0 ? 1 : 0)
72
+ when scan.skip(/%[;?]/)
73
+ when scan.skip(/%{\d+}/)
74
+ stack << fmt.byteslice(start + 2, scan.pos - start - 3).to_i
75
+ when scan.skip(/%p[0-9]/)
76
+ stack.push regs[(fmt.getbyte(scan.pos - 1) - 0x30) - 1]
77
+ when str = scan.scan(/%([0-9].[0-9])?[dsoxX]/)
78
+ buff << sprintf(str, stack.pop)
79
+ when str = scan.scan(/[^%]+/)
80
+ buff << str
81
+ else
82
+ p scan
83
+ raise
84
+ end
85
+ end
86
+ buff
87
+ end
88
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ # GENERATED FILE, DO NOT EDIT
4
+ # :stopdoc:
5
+ module TermTime
6
+ module DB
7
+ class Capability
8
+ attr_reader :name, :tiname, :titype, :tcname, :key_name, :value, :flags, :desc, :i
9
+
10
+ def initialize(name, tiname, titype, tcname, key_name, value, flags, desc, i)
11
+ @name = name
12
+ @tiname = tiname
13
+ @titype = titype
14
+ @tcname = tcname
15
+ @key_name = key_name
16
+ @value = value
17
+ @flags = flags
18
+ @desc = desc
19
+ @i = i
20
+ end
21
+ end
22
+ class Boolean < Capability; end
23
+ class Number < Capability; end
24
+ class String < Capability; end
25
+
26
+ BOOLEANS = {
27
+ <% info["bool"].each_with_index do |bool, i| %>
28
+ <%= bool[1].dump %> => Boolean.new(
29
+ <% bool.map(&:dump).each do |f| %>
30
+ <%= f %>,
31
+ <% end %>
32
+ <%= i %>,
33
+ ).freeze,
34
+ <% end %>
35
+ }.freeze
36
+ BOOLEAN_LIST = BOOLEANS.values
37
+
38
+ NUMBERS = {
39
+ <% info["num"].each_with_index do |bool, i| %>
40
+ <%= bool[1].dump %> => Number.new(
41
+ <% bool.map(&:dump).each do |f| %>
42
+ <%= f %>,
43
+ <% end %>
44
+ <%= i %>,
45
+ ).freeze,
46
+ <% end %>
47
+ }.freeze
48
+ NUMBER_LIST = NUMBERS.values
49
+
50
+ STRINGS = {
51
+ <% info["str"].each_with_index do |bool, i| %>
52
+ <%= bool[1].dump %> => String.new(
53
+ <% bool.map(&:dump).each do |f| %>
54
+ <%= f %>,
55
+ <% end %>
56
+ <%= i %>,
57
+ ).freeze,
58
+ <% end %>
59
+ }.freeze
60
+ STRING_LIST = STRINGS.values
61
+ end
62
+ end
63
+ # :startdoc:
data/termtime.gemspec ADDED
@@ -0,0 +1,17 @@
1
+ require_relative "lib/termtime/version"
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = "termtime"
5
+ s.version = TermTime::VERSION
6
+ s.summary = "Pure Ruby terminfo library"
7
+ s.description = "This is a pure Ruby terminfo library"
8
+ s.authors = [ "Aaron Patterson" ]
9
+ s.email = "tenderlove@ruby-lang.org"
10
+ s.files = `git ls-files -z`.split("\x0")
11
+ s.test_files = s.files.grep(%r{^test/})
12
+ s.homepage = "https://github.com/tenderlove/termtime"
13
+ s.license = "Apache-2.0"
14
+
15
+ s.add_development_dependency("rake", "~> 13.0")
16
+ s.add_development_dependency("minitest", "~> 5.20")
17
+ end
Binary file
Binary file
Binary file
data/test/helper.rb ADDED
@@ -0,0 +1,7 @@
1
+ require "minitest/autorun"
2
+
3
+ module TermTime
4
+ class Test < Minitest::Test
5
+ FIXTURES = File.expand_path(File.join(File.dirname(__FILE__), "fixtures"))
6
+ end
7
+ end
@@ -0,0 +1,105 @@
1
+ require "helper"
2
+ require "termtime"
3
+
4
+ module TermTime
5
+ class TermInfoTest < Test
6
+ def test_info
7
+ ti = TermInfo.new File.join(FIXTURES, "xterm-256color")
8
+ assert_equal ["xterm-256color", "xterm with 256 colors"], ti.names
9
+ assert_equal ["am", "bce", "ccc", "xenl", "km", "mir", "msgr", "npc", "mc5i"].sort, ti.flags.map(&:tiname).sort
10
+ assert ti.getflag("am")
11
+ assert ti.getflag("npc")
12
+ assert_equal ["colors", "cols", "it", "lines", "pairs"].sort, ti.numbers.map(&:tiname).sort
13
+ assert_equal [256, 80, 8, 24, 32767], ti.numbers.sort_by(&:tiname).map(&:value)
14
+
15
+ assert_equal 256, ti.getnum("colors")
16
+ assert_equal 24, ti.getnum("lines")
17
+
18
+ assert_equal "\e[H\e[2J", ti.getstr("clear")
19
+ end
20
+
21
+ def test_adm3a
22
+ ti = TermInfo.new File.join(FIXTURES, "adm3a")
23
+ assert_equal ["adm3a", "lsi adm3a"], ti.names
24
+ assert_equal ["am"], ti.flags.map(&:tiname)
25
+ assert ti.getflag("am")
26
+ refute ti.getflag("npc")
27
+
28
+ assert_equal ["cols", "lines"].sort, ti.numbers.map(&:tiname).sort
29
+ assert_equal [80, 24], ti.numbers.sort_by(&:tiname).map(&:value)
30
+
31
+ assert_equal ["bel", "clear", "cr", "cub1", "cud1", "cuf1", "cup", "cuu1", "home", "ind", "kcub1", "kcud1", "kcuf1", "kcuu1", "rs2"], ti.strings.map(&:tiname).sort
32
+
33
+ assert_equal ["\a", "\x1A$<1/>", "\r", "\b", "\n", "\f", "\e=%p1%' '%+%c%p2%' '%+%c", "\v", "\x1E", "\n", "\b", "\n", "\f", "\v", "\x0E"], ti.strings.sort_by(&:tiname).map(&:value)
34
+ end
35
+
36
+ def test_getstr_tparm
37
+ ti = TermInfo.new File.join(FIXTURES, "xterm-256color")
38
+ assert_equal "\e[2D", ti.str("cub").tparm(2)
39
+ end
40
+
41
+ def test_tparm
42
+ assert_equal "1;1", TermTime.tparm("%i%p1%d;%p2%d")
43
+ assert_equal "0;0", TermTime.tparm("%p1%d;%p2%d")
44
+ assert_equal "1;2", TermTime.tparm("%i%p1%d;%p2%d", 0, 1)
45
+ assert_equal "true", TermTime.tparm("%p1%ttrue%efalse%;", 1)
46
+ assert_equal "false", TermTime.tparm("%p1%ttrue%efalse%;", 0)
47
+ assert_equal "8", TermTime.tparm("%{8}%d")
48
+ assert_equal "%", TermTime.tparm("%%")
49
+ assert_equal [40].pack("C"), TermTime.tparm("%{40}%c")
50
+ end
51
+
52
+ def test_tparm_lt
53
+ assert_equal "less", TermTime.tparm("%?%p1%{8}%<%tless%emore%?", 0)
54
+ assert_equal "less", TermTime.tparm("%?%p1%{8}%<%tless%emore%;", 0)
55
+ assert_equal "more", TermTime.tparm("%?%p1%{8}%<%tless%emore%;", 8)
56
+ end
57
+
58
+ def test_tparm_gt
59
+ assert_equal "less", TermTime.tparm("%?%p1%{8}%>%tmore%eless%;", 0)
60
+ assert_equal "more", TermTime.tparm("%?%p1%{8}%>%tmore%eless%;", 9)
61
+ end
62
+
63
+ def test_tparm_eq
64
+ assert_equal "no", TermTime.tparm("%?%p1%{8}%=%tyes%eno%;", 0)
65
+ assert_equal "yes", TermTime.tparm("%?%p1%{8}%=%tyes%eno%;", 8)
66
+ end
67
+
68
+ def test_tparm_math
69
+ assert_equal "9", TermTime.tparm("%{8}%p1%+%d", 1)
70
+ assert_equal "7", TermTime.tparm("%{8}%p1%-%d", 1)
71
+ assert_equal "16", TermTime.tparm("%{8}%p1%*%d", 2)
72
+ assert_equal "4", TermTime.tparm("%{8}%p1%/%d", 2)
73
+ assert_equal "1", TermTime.tparm("%{9}%p1%m%d", 2)
74
+ end
75
+
76
+ def test_tparm_bits
77
+ assert_equal "1", TermTime.tparm("%{9}%p1%&%d", 1)
78
+ assert_equal "9", TermTime.tparm("%{8}%p1%|%d", 1)
79
+ assert_equal "6", TermTime.tparm("%{5}%p1%^%d", 3)
80
+ assert_equal "-6", TermTime.tparm("%{5}%~%d")
81
+ end
82
+
83
+ def test_tparm_bool
84
+ assert_equal "1", TermTime.tparm("%{0}%!%d")
85
+ assert_equal "0", TermTime.tparm("%{5}%!%d")
86
+ end
87
+
88
+ def test_tparm_logic
89
+ assert_equal "1", TermTime.tparm("%p1%p2%A%d", 1, 5)
90
+ assert_equal "0", TermTime.tparm("%p1%p2%A%d", 1, 0)
91
+ assert_equal "1", TermTime.tparm("%p1%p2%O%d", 1, 5)
92
+ assert_equal "1", TermTime.tparm("%p1%p2%O%d", 1, 0)
93
+ end
94
+
95
+ def test_tparm_char_constant
96
+ assert_equal "65", TermTime.tparm("%'A'%d")
97
+ end
98
+
99
+ def test_odk
100
+ x = "\e]4;1;rgb:00/00/01\e\\"
101
+ y = TermTime.tparm("\e]4;%p1%d;rgb:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\e\\", 1, 2, 3, 4)
102
+ assert_equal x, y
103
+ end
104
+ end
105
+ end
metadata ADDED
@@ -0,0 +1,91 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: termtime
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ original_platform: ''
7
+ authors:
8
+ - Aaron Patterson
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2024-12-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '13.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '13.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: minitest
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '5.20'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '5.20'
41
+ description: This is a pure Ruby terminfo library
42
+ email: tenderlove@ruby-lang.org
43
+ executables: []
44
+ extensions: []
45
+ extra_rdoc_files: []
46
+ files:
47
+ - ".github/workflows/ci.yml"
48
+ - CODE_OF_CONDUCT.md
49
+ - Gemfile
50
+ - LICENSE
51
+ - README.md
52
+ - Rakefile
53
+ - assets/termtime.png
54
+ - lib/termtime.rb
55
+ - lib/termtime/db.rb
56
+ - lib/termtime/terminfo.rb
57
+ - lib/termtime/version.rb
58
+ - templates/lib/termtime/db.rb.erb
59
+ - termtime.gemspec
60
+ - test/fixtures/adm3
61
+ - test/fixtures/adm3a
62
+ - test/fixtures/xterm-256color
63
+ - test/helper.rb
64
+ - test/terminfo_test.rb
65
+ homepage: https://github.com/tenderlove/termtime
66
+ licenses:
67
+ - Apache-2.0
68
+ metadata: {}
69
+ rdoc_options: []
70
+ require_paths:
71
+ - lib
72
+ required_ruby_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ required_rubygems_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ requirements: []
83
+ rubygems_version: 3.6.0.dev
84
+ specification_version: 4
85
+ summary: Pure Ruby terminfo library
86
+ test_files:
87
+ - test/fixtures/adm3
88
+ - test/fixtures/adm3a
89
+ - test/fixtures/xterm-256color
90
+ - test/helper.rb
91
+ - test/terminfo_test.rb