metasm 1.0.0 → 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (276) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +3 -0
  4. data/.gitignore +3 -0
  5. data/.hgtags +3 -0
  6. data/Gemfile +3 -0
  7. data/INSTALL +61 -0
  8. data/LICENCE +458 -0
  9. data/README +29 -21
  10. data/Rakefile +10 -0
  11. data/TODO +10 -12
  12. data/doc/code_organisation.txt +3 -1
  13. data/doc/core/DynLdr.txt +247 -0
  14. data/doc/core/ExeFormat.txt +43 -0
  15. data/doc/core/Expression.txt +220 -0
  16. data/doc/core/GNUExports.txt +27 -0
  17. data/doc/core/Ia32.txt +236 -0
  18. data/doc/core/SerialStruct.txt +108 -0
  19. data/doc/core/VirtualString.txt +145 -0
  20. data/doc/core/WindowsExports.txt +61 -0
  21. data/doc/core/index.txt +1 -0
  22. data/doc/style.css +6 -3
  23. data/doc/usage/debugger.txt +327 -0
  24. data/doc/usage/index.txt +1 -0
  25. data/doc/use_cases.txt +2 -2
  26. data/metasm.gemspec +23 -0
  27. data/{lib/metasm.rb → metasm.rb} +15 -3
  28. data/{lib/metasm → metasm}/compile_c.rb +15 -9
  29. data/metasm/cpu/arc.rb +8 -0
  30. data/metasm/cpu/arc/decode.rb +404 -0
  31. data/metasm/cpu/arc/main.rb +191 -0
  32. data/metasm/cpu/arc/opcodes.rb +588 -0
  33. data/metasm/cpu/arm.rb +14 -0
  34. data/{lib/metasm → metasm/cpu}/arm/debug.rb +2 -2
  35. data/{lib/metasm → metasm/cpu}/arm/decode.rb +15 -18
  36. data/{lib/metasm → metasm/cpu}/arm/encode.rb +23 -8
  37. data/{lib/metasm → metasm/cpu}/arm/main.rb +3 -6
  38. data/metasm/cpu/arm/opcodes.rb +324 -0
  39. data/{lib/metasm → metasm/cpu}/arm/parse.rb +25 -13
  40. data/{lib/metasm → metasm/cpu}/arm/render.rb +2 -2
  41. data/metasm/cpu/arm64.rb +15 -0
  42. data/metasm/cpu/arm64/debug.rb +38 -0
  43. data/metasm/cpu/arm64/decode.rb +285 -0
  44. data/metasm/cpu/arm64/encode.rb +41 -0
  45. data/metasm/cpu/arm64/main.rb +105 -0
  46. data/metasm/cpu/arm64/opcodes.rb +232 -0
  47. data/metasm/cpu/arm64/parse.rb +20 -0
  48. data/metasm/cpu/arm64/render.rb +95 -0
  49. data/{lib/metasm/mips/compile_c.rb → metasm/cpu/bpf.rb} +4 -2
  50. data/metasm/cpu/bpf/decode.rb +110 -0
  51. data/metasm/cpu/bpf/main.rb +60 -0
  52. data/metasm/cpu/bpf/opcodes.rb +81 -0
  53. data/metasm/cpu/bpf/render.rb +30 -0
  54. data/{lib/metasm/ppc.rb → metasm/cpu/cy16.rb} +2 -4
  55. data/metasm/cpu/cy16/decode.rb +247 -0
  56. data/metasm/cpu/cy16/main.rb +63 -0
  57. data/metasm/cpu/cy16/opcodes.rb +78 -0
  58. data/metasm/cpu/cy16/render.rb +30 -0
  59. data/metasm/cpu/dalvik.rb +11 -0
  60. data/{lib/metasm → metasm/cpu}/dalvik/decode.rb +34 -34
  61. data/{lib/metasm → metasm/cpu}/dalvik/main.rb +71 -4
  62. data/{lib/metasm → metasm/cpu}/dalvik/opcodes.rb +21 -12
  63. data/{lib/metasm/mips.rb → metasm/cpu/ebpf.rb} +3 -4
  64. data/metasm/cpu/ebpf/debug.rb +61 -0
  65. data/metasm/cpu/ebpf/decode.rb +142 -0
  66. data/metasm/cpu/ebpf/main.rb +58 -0
  67. data/metasm/cpu/ebpf/opcodes.rb +97 -0
  68. data/metasm/cpu/ebpf/render.rb +36 -0
  69. data/metasm/cpu/ia32.rb +17 -0
  70. data/{lib/metasm → metasm/cpu}/ia32/compile_c.rb +23 -9
  71. data/{lib/metasm → metasm/cpu}/ia32/debug.rb +44 -6
  72. data/{lib/metasm → metasm/cpu}/ia32/decode.rb +342 -128
  73. data/{lib/metasm → metasm/cpu}/ia32/decompile.rb +75 -53
  74. data/{lib/metasm → metasm/cpu}/ia32/encode.rb +19 -13
  75. data/{lib/metasm → metasm/cpu}/ia32/main.rb +66 -8
  76. data/metasm/cpu/ia32/opcodes.rb +1424 -0
  77. data/{lib/metasm → metasm/cpu}/ia32/parse.rb +55 -17
  78. data/{lib/metasm → metasm/cpu}/ia32/render.rb +32 -5
  79. data/metasm/cpu/mcs51.rb +8 -0
  80. data/metasm/cpu/mcs51/decode.rb +99 -0
  81. data/metasm/cpu/mcs51/main.rb +87 -0
  82. data/metasm/cpu/mcs51/opcodes.rb +120 -0
  83. data/metasm/cpu/mips.rb +14 -0
  84. data/metasm/cpu/mips/debug.rb +42 -0
  85. data/{lib/metasm → metasm/cpu}/mips/decode.rb +59 -38
  86. data/{lib/metasm → metasm/cpu}/mips/encode.rb +4 -3
  87. data/{lib/metasm → metasm/cpu}/mips/main.rb +13 -6
  88. data/{lib/metasm → metasm/cpu}/mips/opcodes.rb +87 -18
  89. data/{lib/metasm → metasm/cpu}/mips/parse.rb +1 -1
  90. data/{lib/metasm → metasm/cpu}/mips/render.rb +1 -1
  91. data/{lib/metasm/dalvik.rb → metasm/cpu/msp430.rb} +1 -1
  92. data/metasm/cpu/msp430/decode.rb +243 -0
  93. data/metasm/cpu/msp430/main.rb +62 -0
  94. data/metasm/cpu/msp430/opcodes.rb +101 -0
  95. data/metasm/cpu/openrisc.rb +11 -0
  96. data/metasm/cpu/openrisc/debug.rb +106 -0
  97. data/metasm/cpu/openrisc/decode.rb +182 -0
  98. data/metasm/cpu/openrisc/decompile.rb +350 -0
  99. data/metasm/cpu/openrisc/main.rb +70 -0
  100. data/metasm/cpu/openrisc/opcodes.rb +109 -0
  101. data/metasm/cpu/openrisc/render.rb +37 -0
  102. data/{lib/metasm → metasm/cpu}/pic16c/decode.rb +6 -7
  103. data/{lib/metasm → metasm/cpu}/pic16c/main.rb +0 -0
  104. data/{lib/metasm → metasm/cpu}/pic16c/opcodes.rb +1 -1
  105. data/metasm/cpu/ppc.rb +11 -0
  106. data/{lib/metasm → metasm/cpu}/ppc/decode.rb +18 -37
  107. data/{lib/metasm → metasm/cpu}/ppc/decompile.rb +3 -3
  108. data/{lib/metasm → metasm/cpu}/ppc/encode.rb +2 -2
  109. data/{lib/metasm → metasm/cpu}/ppc/main.rb +23 -18
  110. data/{lib/metasm → metasm/cpu}/ppc/opcodes.rb +11 -6
  111. data/metasm/cpu/ppc/parse.rb +55 -0
  112. data/metasm/cpu/python.rb +8 -0
  113. data/metasm/cpu/python/decode.rb +116 -0
  114. data/metasm/cpu/python/main.rb +36 -0
  115. data/metasm/cpu/python/opcodes.rb +180 -0
  116. data/{lib/metasm → metasm/cpu}/sh4.rb +1 -1
  117. data/{lib/metasm → metasm/cpu}/sh4/decode.rb +50 -23
  118. data/{lib/metasm → metasm/cpu}/sh4/main.rb +38 -27
  119. data/{lib/metasm → metasm/cpu}/sh4/opcodes.rb +7 -8
  120. data/metasm/cpu/st20.rb +9 -0
  121. data/metasm/cpu/st20/decode.rb +173 -0
  122. data/metasm/cpu/st20/decompile.rb +283 -0
  123. data/metasm/cpu/st20/main.rb +37 -0
  124. data/metasm/cpu/st20/opcodes.rb +140 -0
  125. data/{lib/metasm/arm.rb → metasm/cpu/webasm.rb} +4 -5
  126. data/metasm/cpu/webasm/debug.rb +31 -0
  127. data/metasm/cpu/webasm/decode.rb +321 -0
  128. data/metasm/cpu/webasm/decompile.rb +386 -0
  129. data/metasm/cpu/webasm/encode.rb +104 -0
  130. data/metasm/cpu/webasm/main.rb +81 -0
  131. data/metasm/cpu/webasm/opcodes.rb +214 -0
  132. data/metasm/cpu/x86_64.rb +15 -0
  133. data/{lib/metasm → metasm/cpu}/x86_64/compile_c.rb +40 -25
  134. data/{lib/metasm → metasm/cpu}/x86_64/debug.rb +4 -4
  135. data/{lib/metasm → metasm/cpu}/x86_64/decode.rb +58 -15
  136. data/{lib/metasm → metasm/cpu}/x86_64/encode.rb +59 -28
  137. data/{lib/metasm → metasm/cpu}/x86_64/main.rb +18 -6
  138. data/metasm/cpu/x86_64/opcodes.rb +138 -0
  139. data/{lib/metasm → metasm/cpu}/x86_64/parse.rb +12 -4
  140. data/metasm/cpu/x86_64/render.rb +35 -0
  141. data/metasm/cpu/z80.rb +9 -0
  142. data/metasm/cpu/z80/decode.rb +286 -0
  143. data/metasm/cpu/z80/main.rb +67 -0
  144. data/metasm/cpu/z80/opcodes.rb +224 -0
  145. data/metasm/cpu/z80/render.rb +48 -0
  146. data/{lib/metasm/os/main.rb → metasm/debug.rb} +201 -407
  147. data/{lib/metasm → metasm}/decode.rb +104 -24
  148. data/{lib/metasm → metasm}/decompile.rb +804 -478
  149. data/{lib/metasm → metasm}/disassemble.rb +385 -170
  150. data/{lib/metasm → metasm}/disassemble_api.rb +684 -105
  151. data/{lib/metasm → metasm}/dynldr.rb +231 -138
  152. data/{lib/metasm → metasm}/encode.rb +20 -5
  153. data/{lib/metasm → metasm}/exe_format/a_out.rb +9 -6
  154. data/{lib/metasm → metasm}/exe_format/autoexe.rb +3 -0
  155. data/{lib/metasm → metasm}/exe_format/bflt.rb +57 -27
  156. data/{lib/metasm → metasm}/exe_format/coff.rb +35 -7
  157. data/{lib/metasm → metasm}/exe_format/coff_decode.rb +70 -23
  158. data/{lib/metasm → metasm}/exe_format/coff_encode.rb +24 -22
  159. data/{lib/metasm → metasm}/exe_format/dex.rb +26 -8
  160. data/{lib/metasm → metasm}/exe_format/dol.rb +1 -0
  161. data/{lib/metasm → metasm}/exe_format/elf.rb +108 -58
  162. data/{lib/metasm → metasm}/exe_format/elf_decode.rb +202 -36
  163. data/{lib/metasm → metasm}/exe_format/elf_encode.rb +126 -32
  164. data/metasm/exe_format/gb.rb +65 -0
  165. data/metasm/exe_format/javaclass.rb +424 -0
  166. data/{lib/metasm → metasm}/exe_format/macho.rb +218 -16
  167. data/{lib/metasm → metasm}/exe_format/main.rb +28 -3
  168. data/{lib/metasm → metasm}/exe_format/mz.rb +2 -0
  169. data/{lib/metasm → metasm}/exe_format/nds.rb +7 -4
  170. data/{lib/metasm → metasm}/exe_format/pe.rb +96 -11
  171. data/metasm/exe_format/pyc.rb +167 -0
  172. data/{lib/metasm → metasm}/exe_format/serialstruct.rb +67 -14
  173. data/{lib/metasm → metasm}/exe_format/shellcode.rb +7 -3
  174. data/metasm/exe_format/shellcode_rwx.rb +114 -0
  175. data/metasm/exe_format/swf.rb +205 -0
  176. data/metasm/exe_format/wasm.rb +402 -0
  177. data/{lib/metasm → metasm}/exe_format/xcoff.rb +7 -7
  178. data/metasm/exe_format/zip.rb +335 -0
  179. data/metasm/gui.rb +13 -0
  180. data/{lib/metasm → metasm}/gui/cstruct.rb +35 -41
  181. data/{lib/metasm → metasm}/gui/dasm_coverage.rb +11 -11
  182. data/{lib/metasm → metasm}/gui/dasm_decomp.rb +177 -114
  183. data/{lib/metasm → metasm}/gui/dasm_funcgraph.rb +0 -0
  184. data/metasm/gui/dasm_graph.rb +1754 -0
  185. data/{lib/metasm → metasm}/gui/dasm_hex.rb +16 -12
  186. data/{lib/metasm → metasm}/gui/dasm_listing.rb +43 -28
  187. data/{lib/metasm → metasm}/gui/dasm_main.rb +360 -77
  188. data/{lib/metasm → metasm}/gui/dasm_opcodes.rb +5 -19
  189. data/{lib/metasm → metasm}/gui/debug.rb +109 -34
  190. data/{lib/metasm → metasm}/gui/gtk.rb +174 -44
  191. data/{lib/metasm → metasm}/gui/qt.rb +14 -4
  192. data/{lib/metasm → metasm}/gui/win32.rb +180 -43
  193. data/{lib/metasm → metasm}/gui/x11.rb +59 -59
  194. data/{lib/metasm → metasm}/main.rb +421 -286
  195. data/metasm/os/emulator.rb +175 -0
  196. data/{lib/metasm/os/remote.rb → metasm/os/gdbremote.rb} +146 -54
  197. data/{lib/metasm → metasm}/os/gnu_exports.rb +1 -1
  198. data/{lib/metasm → metasm}/os/linux.rb +628 -151
  199. data/metasm/os/main.rb +335 -0
  200. data/{lib/metasm → metasm}/os/windows.rb +151 -58
  201. data/{lib/metasm → metasm}/os/windows_exports.rb +141 -0
  202. data/{lib/metasm → metasm}/parse.rb +49 -36
  203. data/{lib/metasm → metasm}/parse_c.rb +405 -246
  204. data/{lib/metasm → metasm}/preprocessor.rb +71 -41
  205. data/{lib/metasm → metasm}/render.rb +14 -38
  206. data/misc/hexdump.rb +4 -3
  207. data/misc/lint.rb +58 -0
  208. data/misc/objdiff.rb +4 -1
  209. data/misc/objscan.rb +1 -1
  210. data/misc/openrisc-parser.rb +79 -0
  211. data/misc/txt2html.rb +9 -7
  212. data/samples/bindiff.rb +3 -4
  213. data/samples/dasm-plugins/bindiff.rb +15 -0
  214. data/samples/dasm-plugins/bookmark.rb +133 -0
  215. data/samples/dasm-plugins/c_constants.rb +57 -0
  216. data/samples/dasm-plugins/colortheme_solarized.rb +125 -0
  217. data/samples/dasm-plugins/cppobj_funcall.rb +60 -0
  218. data/samples/dasm-plugins/dasm_all.rb +70 -0
  219. data/samples/dasm-plugins/demangle_cpp.rb +31 -0
  220. data/samples/dasm-plugins/deobfuscate.rb +251 -0
  221. data/samples/dasm-plugins/dump_text.rb +35 -0
  222. data/samples/dasm-plugins/export_graph_svg.rb +86 -0
  223. data/samples/dasm-plugins/findgadget.rb +75 -0
  224. data/samples/dasm-plugins/hl_opcode.rb +32 -0
  225. data/samples/dasm-plugins/hotfix_gtk_dbg.rb +19 -0
  226. data/samples/dasm-plugins/imm2off.rb +34 -0
  227. data/samples/dasm-plugins/match_libsigs.rb +93 -0
  228. data/samples/dasm-plugins/patch_file.rb +95 -0
  229. data/samples/dasm-plugins/scanfuncstart.rb +36 -0
  230. data/samples/dasm-plugins/scanxrefs.rb +29 -0
  231. data/samples/dasm-plugins/selfmodify.rb +197 -0
  232. data/samples/dasm-plugins/stringsxrefs.rb +28 -0
  233. data/samples/dasmnavig.rb +1 -1
  234. data/samples/dbg-apihook.rb +24 -9
  235. data/samples/dbg-plugins/heapscan.rb +283 -0
  236. data/samples/dbg-plugins/heapscan/compiled_heapscan_lin.c +155 -0
  237. data/samples/dbg-plugins/heapscan/compiled_heapscan_win.c +128 -0
  238. data/samples/dbg-plugins/heapscan/graphheap.rb +616 -0
  239. data/samples/dbg-plugins/heapscan/heapscan.rb +709 -0
  240. data/samples/dbg-plugins/heapscan/winheap.h +174 -0
  241. data/samples/dbg-plugins/heapscan/winheap7.h +307 -0
  242. data/samples/dbg-plugins/trace_func.rb +214 -0
  243. data/samples/disassemble-gui.rb +48 -7
  244. data/samples/disassemble.rb +31 -6
  245. data/samples/dump_upx.rb +24 -12
  246. data/samples/dynamic_ruby.rb +35 -27
  247. data/samples/elfencode.rb +15 -0
  248. data/samples/emubios.rb +251 -0
  249. data/samples/emudbg.rb +127 -0
  250. data/samples/exeencode.rb +6 -5
  251. data/samples/factorize-headers-peimports.rb +1 -1
  252. data/samples/lindebug.rb +186 -391
  253. data/samples/metasm-shell.rb +68 -57
  254. data/samples/peldr.rb +2 -2
  255. data/tests/all.rb +1 -1
  256. data/tests/arc.rb +26 -0
  257. data/tests/dynldr.rb +22 -4
  258. data/tests/expression.rb +57 -0
  259. data/tests/graph_layout.rb +285 -0
  260. data/tests/ia32.rb +80 -26
  261. data/tests/mcs51.rb +27 -0
  262. data/tests/mips.rb +10 -3
  263. data/tests/preprocessor.rb +18 -0
  264. data/tests/x86_64.rb +66 -18
  265. metadata +465 -219
  266. metadata.gz.sig +2 -0
  267. data/lib/metasm/arm/opcodes.rb +0 -177
  268. data/lib/metasm/gui.rb +0 -23
  269. data/lib/metasm/gui/dasm_graph.rb +0 -1354
  270. data/lib/metasm/ia32.rb +0 -14
  271. data/lib/metasm/ia32/opcodes.rb +0 -872
  272. data/lib/metasm/ppc/parse.rb +0 -52
  273. data/lib/metasm/x86_64.rb +0 -12
  274. data/lib/metasm/x86_64/opcodes.rb +0 -118
  275. data/samples/gdbclient.rb +0 -583
  276. data/samples/rubstop.rb +0 -399
@@ -9,7 +9,7 @@ class AsmOpcodeWidget < DrawableWidget
9
9
  attr_accessor :dasm
10
10
  # nr of raw data bytes to display next to decoded instructions
11
11
  attr_accessor :raw_data_length
12
-
12
+
13
13
  def initialize_widget(dasm, parent_widget)
14
14
  @dasm = dasm
15
15
  @parent_widget = parent_widget
@@ -22,9 +22,7 @@ class AsmOpcodeWidget < DrawableWidget
22
22
  @view_max = @dasm.sections.map { |s, e| s + e.length }.max rescue nil
23
23
  @view_addr = @dasm.prog_binding['entrypoint'] || @view_min || 0
24
24
 
25
- @default_color_association = { :comment => :darkblue, :label => :darkgreen, :text => :black,
26
- :instruction => :black, :address => :blue, :caret => :black, :raw_data => :black,
27
- :background => :white, :cursorline_bg => :paleyellow, :hl_word => :palered }
25
+ @default_color_association = ColorTheme.merge :raw_data => :black
28
26
  end
29
27
 
30
28
  def resized(w, h)
@@ -124,19 +122,7 @@ class AsmOpcodeWidget < DrawableWidget
124
122
  # must not include newline
125
123
  render = lambda { |str, color|
126
124
  fullstr << str
127
- if @hl_word
128
- stmp = str
129
- pre_x = 0
130
- while stmp =~ /^(.*?)(\b#{Regexp.escape @hl_word}\b)/
131
- s1, s2 = $1, $2
132
- pre_x += s1.length * @font_width
133
- hl_x = s2.length * @font_width
134
- draw_rectangle_color(:hl_word, x+pre_x, y, hl_x, @font_height)
135
- pre_x += hl_x
136
- stmp = stmp[s1.length+s2.length..-1]
137
- end
138
- end
139
- draw_string_color(color, x, y, str)
125
+ draw_string_hl(color, x, y, str)
140
126
  x += str.length * @font_width
141
127
  }
142
128
 
@@ -154,7 +140,7 @@ class AsmOpcodeWidget < DrawableWidget
154
140
 
155
141
  # draw text until screen is full
156
142
  while y < height
157
- if label = invb[curaddr]
143
+ if invb[curaddr]
158
144
  nl[]
159
145
  @dasm.label_alias[curaddr].to_a.each { |name|
160
146
  render["#{name}:", :label]
@@ -251,7 +237,7 @@ class AsmOpcodeWidget < DrawableWidget
251
237
  # redraws the caret, change the hilighted word, redraw if needed
252
238
  def update_caret
253
239
  if update_hl_word(@line_text[@caret_y], @caret_x) or @caret_y != @oldcaret_y
254
- redraw
240
+ redraw
255
241
  elsif @oldcaret_x != @caret_x
256
242
  invalidate_caret(@oldcaret_x, @oldcaret_y)
257
243
  invalidate_caret(@caret_x, @caret_y)
@@ -30,7 +30,7 @@ class DbgWidget < ContainerVBoxWidget
30
30
  oldcb = @code.bg_color_callback
31
31
  @code.bg_color_callback = lambda { |a|
32
32
  if a == @dbg.pc
33
- 'f88'
33
+ :red_bg
34
34
  # TODO breakpoints & stuff
35
35
  elsif oldcb; oldcb[a]
36
36
  end
@@ -48,8 +48,8 @@ class DbgWidget < ContainerVBoxWidget
48
48
 
49
49
  pc = @dbg.resolve_expr(@watchpoint[@code])
50
50
  graph = :graph if @dbg.disassembler.function_blocks(pc).to_a.length < 100
51
- @code.focus_addr(pc, graph)
52
- @mem.focus_addr(0, :hex)
51
+ @code.focus_addr(pc, graph, true)
52
+ @mem.focus_addr(0, :hex, true)
53
53
  end
54
54
 
55
55
  def swapin_tid
@@ -98,10 +98,16 @@ class DbgWidget < ContainerVBoxWidget
98
98
 
99
99
  # TODO check_target always, incl when :stopped
100
100
  def post_dbg_run
101
+ # focus currently stopped threads
102
+ if @dbg.state == :running and tt = @dbg.tid_stuff.find { |tid, tstuff| tstuff[:state] != :running }
103
+ @dbg.tid = tt[0]
104
+ end
105
+
101
106
  want_redraw = true
102
107
  return if @idle_checking ||= nil # load only one bg proc
103
108
  @idle_checking = true
104
109
  Gui.idle_add {
110
+ protect {
105
111
  @dbg.check_target
106
112
  if @dbg.state == :running
107
113
  redraw if want_redraw # redraw once if the target is running (less flicker with singlestep)
@@ -113,7 +119,7 @@ class DbgWidget < ContainerVBoxWidget
113
119
  @dbg.dasm_invalidate
114
120
  @mem.gui_update
115
121
  @dbg.disassembler.sections.clear if @dbg.state == :dead
116
- @dbg.disassembler.disassemble_fast(@dbg.pc)
122
+ @dbg.disassembler.disassemble_fast(@dbg.pc) if not @dbg.disassembler.di_at(@dbg.pc)
117
123
  @children.each { |c|
118
124
  if wp = @watchpoint[c]
119
125
  c.focus_addr @dbg.resolve_expr(wp), nil, true
@@ -121,6 +127,7 @@ class DbgWidget < ContainerVBoxWidget
121
127
  }
122
128
  redraw
123
129
  false
130
+ }
124
131
  }
125
132
  end
126
133
 
@@ -147,7 +154,7 @@ class DbgWidget < ContainerVBoxWidget
147
154
  @children.each { |c| c.gui_update }
148
155
  end
149
156
 
150
- def prompt_attach(caption='chose target')
157
+ def prompt_attach(caption='choose target')
151
158
  l = nil
152
159
  i = inputbox(caption) { |name|
153
160
  i = nil ; l.destroy if l and not l.destroyed?
@@ -166,7 +173,10 @@ class DbgWidget < ContainerVBoxWidget
166
173
  l = listwindow('running processes', list,
167
174
  :noshow => true,
168
175
  :color_callback => lambda { |le| [:grey, :palegrey] if le[0] == me }
169
- ) { |e| i.text = e[0] }
176
+ ) { |e|
177
+ i.text = e[0]
178
+ i.keypress(:enter) if l.destroyed?
179
+ }
170
180
  l.x += l.width
171
181
  l.show
172
182
  false
@@ -174,7 +184,7 @@ class DbgWidget < ContainerVBoxWidget
174
184
  } if not list_pr.empty?
175
185
  end
176
186
 
177
- def prompt_createprocess(caption='chose path')
187
+ def prompt_createprocess(caption='choose path')
178
188
  openfile(caption) { |path|
179
189
  path = '"' + path + '"' if @dbg.shortname == 'windbg' and path =~ /\s/
180
190
  inputbox('target args?', :text => path) { |pa|
@@ -198,10 +208,28 @@ class DbgWidget < ContainerVBoxWidget
198
208
  case f
199
209
  when /\.(c|h|cpp)$/; @dbg.disassembler.parse_c_file(f)
200
210
  when /\.map$/; @dbg.load_map(f)
201
- when /\.rb$/; @dbg.load_plugin(f)
211
+ when /\.rb$/; @dbg.load_plugin(f) ; @console.add_log "loaded plugin #{File.basename(f, '.rb')}"
202
212
  else messagebox("unsupported file extension #{f}")
203
213
  end
204
214
  end
215
+
216
+ def extend_contextmenu(tg, menu, addr=nil)
217
+ if addr
218
+ bm = tg.new_menu
219
+ bl = @dbg.all_breakpoints(addr)
220
+ if not bl.empty?
221
+ tg.addsubmenu(bm, '_clear breakpoint') { bl.each { |b| @dbg.del_bp(b) } }
222
+ end
223
+ tg.addsubmenu(bm, '_go here') { @dbg.bpx(addr, true) ; dbg_continue }
224
+ tg.addsubmenu(bm, '_bpx') { @dbg.bpx(addr) }
225
+ tg.addsubmenu(bm, 'bpm _read') { @dbg.hwbp(addr, :r, 1) }
226
+ tg.addsubmenu(bm, 'bpm _write') { @dbg.hwbp(addr, :w, 1) }
227
+ tg.addsubmenu(menu, '_bp', bm)
228
+ end
229
+ if @parent_widget.respond_to?(:extend_contextmenu)
230
+ @parent_widget.extend_contextmenu(tg, menu, addr)
231
+ end
232
+ end
205
233
  end
206
234
 
207
235
 
@@ -221,10 +249,9 @@ class DbgRegWidget < DrawableWidget
221
249
  swapin_tid
222
250
 
223
251
  @reg_pos = [] # list of x y w h vx of the reg drawing on widget, vx is x of value
224
-
225
- @default_color_association = { :label => :black, :data => :blue, :write_pending => :darkred,
226
- :changed => :darkgreen, :caret => :black, :background => :white,
227
- :inactive => :palegrey }
252
+
253
+ @default_color_association = ColorTheme.merge :label => :text, :data => :blue, :write_pending => :darkred,
254
+ :changed => :darkgreen, :caret => :text, :inactive => :palegrey
228
255
  end
229
256
 
230
257
  def swapin_tid
@@ -262,7 +289,6 @@ class DbgRegWidget < DrawableWidget
262
289
  end
263
290
 
264
291
  def paint
265
- curaddr = 0
266
292
  x = 1
267
293
  y = 0
268
294
 
@@ -287,7 +313,11 @@ class DbgRegWidget < DrawableWidget
287
313
  render["#{reg}=".ljust(regstrlen), :label]
288
314
  v = @write_pending[reg] || @reg_cache[reg]
289
315
  col = running ? :inactive : @write_pending[reg] ? :write_pending : @reg_cache_old.fetch(reg, v) != v ? :changed : :data
290
- render["%0#{@register_size[reg]}x " % v, col]
316
+ if v.kind_of?(::Integer)
317
+ render["%0#{@register_size[reg]}x " % v, col]
318
+ else
319
+ render[v.to_s, col]
320
+ end
291
321
  x += @font_width # space
292
322
  }
293
323
 
@@ -384,8 +414,8 @@ class DbgRegWidget < DrawableWidget
384
414
  case v = key
385
415
  when ?\x20; v = nil
386
416
  when ?0..?9; v -= ?0
387
- when ?a..?f; v -= ?a-10
388
- when ?A..?F; v -= ?A-10
417
+ when ?a..?f; v -= ?a - 10
418
+ when ?A..?F; v -= ?A - 10
389
419
  else return false
390
420
  end
391
421
  end
@@ -402,7 +432,7 @@ class DbgRegWidget < DrawableWidget
402
432
  @write_pending[reg] = v
403
433
  rsz = 1
404
434
  end
405
-
435
+
406
436
  if rsz == 1
407
437
  @caret_reg += 1
408
438
  @caret_reg = @registers.length if @caret_reg >= @registers.length + @flags.length
@@ -472,8 +502,8 @@ class DbgConsoleWidget < DrawableWidget
472
502
 
473
503
  @dbg.set_log_proc { |l| add_log l }
474
504
 
475
- @default_color_association = { :log => :palegrey, :curline => :white, :caret => :yellow,
476
- :background => :black, :status => :black, :status_bg => '088' }
505
+ @default_color_association = ColorTheme.merge :log => :palegrey, :curline => :white, :caret => :yellow,
506
+ :background => :black, :status => :black, :status_bg => '088'
477
507
 
478
508
  init_commands
479
509
  end
@@ -513,6 +543,21 @@ class DbgConsoleWidget < DrawableWidget
513
543
  clipboard_copy(txt)
514
544
  end
515
545
 
546
+ # copy/paste word under cursor (paste when on last line)
547
+ def rightclick(x, y)
548
+ y -= height % @font_height
549
+ y = y.to_i / @font_height
550
+ hc = height / @font_height
551
+ x /= @font_width
552
+ if y >= hc - 2
553
+ keypress_ctrl ?v
554
+ else
555
+ txt = @log.reverse[@log_offset + hc - y - 3].to_s
556
+ word = txt[0...x].to_s[/\w*$/] << txt[x..-1].to_s[/^\w*/]
557
+ clipboard_copy(word)
558
+ end
559
+ end
560
+
516
561
  def mouse_wheel(dir, x, y)
517
562
  case dir
518
563
  when :up; @log_offset += 3
@@ -531,12 +576,12 @@ class DbgConsoleWidget < DrawableWidget
531
576
 
532
577
  w_w = width
533
578
 
534
- y -= @font_height
579
+ y -= @font_height
535
580
  draw_rectangle_color(:status_bg, 0, y, w_w, @font_height)
536
581
  str = "#{@dbg.pid}:#{@dbg.tid} #{@dbg.state} #{@dbg.info}"
537
582
  draw_string_color(:status, w_w-str.length*@font_width-1, y, str)
538
583
  draw_string_color(:status, 1+@font_width, y, @statusline)
539
- y -= @font_height
584
+ y -= @font_height
540
585
 
541
586
  w_w_c = w_w/@font_width
542
587
  @caret_y = y
@@ -744,7 +789,7 @@ class DbgConsoleWidget < DrawableWidget
744
789
  def cmd_dd(addr, dlen=nil, len=nil)
745
790
  if addr.kind_of? String
746
791
  s = addr.strip
747
- addr = solve_expr!(s)
792
+ addr = solve_expr!(s) || @parent_widget.mem.curaddr
748
793
  if not s.empty?
749
794
  s = s[1..-1] if s[0] == ?,
750
795
  len ||= solve_expr(s)
@@ -757,7 +802,7 @@ class DbgConsoleWidget < DrawableWidget
757
802
  le = (@dbg.cpu.endianness == :little)
758
803
  data = '' if @dbg.memory.page_invalid?(addr)
759
804
  case dlen
760
- when nil; add_log "#{Expression[addr]} #{data.unpack('C*').map { |c| '%02X' % c }.join(' ').ljust(2*16+15)} #{data.tr("\0-\x1f\x7f-\xff", '.')}"
805
+ when nil; add_log "#{Expression[addr]} #{data.unpack('C*').map { |c| '%02X' % c }.join(' ').ljust(2*16+15)} #{data.tr("^\x20-\x7e", '.')}"
761
806
  when 1; add_log "#{Expression[addr]} #{data.unpack('C*').map { |c| '%02X' % c }.join(' ')}"
762
807
  when 2; add_log "#{Expression[addr]} #{data.unpack(le ? 'v*' : 'n*').map { |c| '%04X' % c }.join(' ')}"
763
808
  when 4; add_log "#{Expression[addr]} #{data.unpack(le ? 'V*' : 'N*').map { |c| '%08X' % c }.join(' ')}"
@@ -767,8 +812,12 @@ class DbgConsoleWidget < DrawableWidget
767
812
  len -= 16
768
813
  end
769
814
  else
770
- @parent_widget.mem.view(:hex).data_size = dlen if dlen
771
- @parent_widget.mem.focus_addr(solve_expr(addr)) if addr and addr != ''
815
+ if dlen
816
+ @parent_widget.mem.view(:hex).data_size = dlen
817
+ @parent_widget.mem.view(:hex).resized
818
+ @parent_widget.mem.showview(:hex)
819
+ end
820
+ @parent_widget.mem.focus_addr(solve_expr(addr))
772
821
  @parent_widget.mem.gui_update
773
822
  end
774
823
  end
@@ -783,6 +832,18 @@ class DbgConsoleWidget < DrawableWidget
783
832
  new_command('dw', 'dump/focus words in data window') { |arg| cmd_dd(arg, 2) }
784
833
  new_command('dd', 'dump/focus dwords in data window') { |arg| cmd_dd(arg, 4) }
785
834
  new_command('dq', 'dump/focus qwords in data window') { |arg| cmd_dd(arg, 8) }
835
+ new_command('dc', 'focus C struct in data window: <name> <addr>') { |arg|
836
+ name, addr = arg.strip.split(/\s+/, 2)
837
+ addr = (addr ? solve_expr(addr) : @parent_widget.mem.curaddr)
838
+ @parent_widget.mem.focus_addr(addr, :cstruct, false, name)
839
+ }
840
+ new_command('dC', 'dump C struct: dC <name> <addr>') { |arg|
841
+ name, addr = arg.strip.split(/\s+/, 2)
842
+ addr = (addr ? solve_expr(addr) : @parent_widget.mem.curaddr)
843
+ if st = @dbg.disassembler.c_parser.decode_c_struct(name, @dbg.memory, addr)
844
+ add_log st.to_s.gsub("\t", ' ')
845
+ end
846
+ }
786
847
  new_command('u', 'focus code window on an address') { |arg| p.code.focus_addr(solve_expr(arg)) }
787
848
  new_command('.', 'focus code window on current address') { p.code.focus_addr(solve_expr(@dbg.register_pc.to_s)) }
788
849
  new_command('wc', 'set code window height') { |arg|
@@ -828,12 +889,14 @@ class DbgConsoleWidget < DrawableWidget
828
889
  cb = lambda { a.split(';').each { |aaa| run_command(aaa) } } if a
829
890
  @dbg.bpx(solve_expr(e), o, cd, &cb)
830
891
  }
831
- new_command('hwbp', 'set a hardware breakpoint') { |arg|
832
- arg =~ /^(.*?)(?: if (.*?))?(?: do (.*?))?(?: if (.*?))?$/i
833
- e, c, a = $1, ($2 || $4), $3
892
+ new_command('hwbp', 'set a hardware breakpoint (hwbp 0x2345 w)') { |arg|
893
+ arg =~ /^(.*?)( once)?( [rwx])?(?: if (.*?))?(?: do (.*?))?(?: if (.*?))?$/i
894
+ e, o, t, c, a = $1, $2, $3, ($4 || $6), $5
895
+ o = o ? true : false
896
+ t = (t || 'x').strip.to_sym
834
897
  cd = parse_expr(c) if c
835
898
  cb = lambda { a.split(';').each { |aaa| run_command(aaa) } } if a
836
- @dbg.hwbp(solve_expr(e), :x, 1, false, cd, &cb)
899
+ @dbg.hwbp(solve_expr(e), t, 1, o, cd, &cb)
837
900
  }
838
901
  new_command('bpm', 'set a hardware memory breakpoint: bpm r 0x4800ff 16') { |arg|
839
902
  arg =~ /^(.*?)(?: if (.*?))?(?: do (.*?))?(?: if (.*?))?$/i
@@ -846,7 +909,7 @@ class DbgConsoleWidget < DrawableWidget
846
909
  exp = solve_expr!(e)
847
910
  len = solve_expr(e) if e != ''
848
911
  len ||= 1
849
- @dbg.hwbp(exp, mode, len, false, cd, &cb)
912
+ @dbg.bpm(exp, mode, len, false, cd, &cb)
850
913
  }
851
914
  new_command('g', 'wait until target reaches the specified address') { |arg|
852
915
  arg =~ /^(.*?)(?: if (.*?))?(?: do (.*?))?(?: if (.*?))?$/i
@@ -915,7 +978,11 @@ class DbgConsoleWidget < DrawableWidget
915
978
  }
916
979
  new_command('?', 'display a value') { |arg|
917
980
  next if not v = solve_expr(arg)
918
- add_log "#{v} 0x#{v.to_s(16)} #{[v & 0xffff_ffff].pack('L').inspect} #{@dbg.addrname!(v)}"
981
+ if v.kind_of?(Expression)
982
+ add_log "#{v}"
983
+ else
984
+ add_log "#{v} 0x#{v.to_s(16)} #{[v & 0xffff_ffff].pack('L').inspect} #{@dbg.addrname!(v)}"
985
+ end
919
986
  }
920
987
  new_command('exit', 'quit', 'quit the debugger interface') { p.win.destroy }
921
988
  new_command('ruby', 'execute arbitrary ruby code') { |arg|
@@ -1038,7 +1105,7 @@ class DbgConsoleWidget < DrawableWidget
1038
1105
  add_log "#{t} #{stf[:state]} #{stf[:info]}"
1039
1106
  }
1040
1107
  }
1041
-
1108
+
1042
1109
  new_command('pid', 'select a pid') { |arg|
1043
1110
  if pid = solve_expr(arg)
1044
1111
  @dbg.pid = pid
@@ -1072,7 +1139,7 @@ class DbgConsoleWidget < DrawableWidget
1072
1139
  @dbg.ignore_newthread = false
1073
1140
  @dbg.ignore_endthread = false
1074
1141
  }
1075
- new_command('thread_event_ignore', 'ignore thread creation/termination') {
1142
+ new_command('thread_events_ignore', 'ignore thread creation/termination') {
1076
1143
  @dbg.ignore_newthread = true
1077
1144
  @dbg.ignore_endthread = true
1078
1145
  }
@@ -1101,6 +1168,12 @@ class DbgConsoleWidget < DrawableWidget
1101
1168
  @dbg.create_process(arg)
1102
1169
  }
1103
1170
 
1171
+ new_command('plugin', 'load', 'load a debugger plugin') { |arg|
1172
+ @dbg.load_plugin arg
1173
+ add_log "loaded plugin #{File.basename(arg, '.rb')}"
1174
+ }
1175
+
1176
+
1104
1177
  @dbg.ui_command_setup(self) if @dbg.respond_to? :ui_command_setup
1105
1178
  end
1106
1179
 
@@ -1122,12 +1195,13 @@ class DbgConsoleWidget < DrawableWidget
1122
1195
  end
1123
1196
 
1124
1197
  def run_command(cmd)
1198
+ cmd = cmd.sub(/^\s+/, '')
1125
1199
  cn = cmd.split.first
1126
1200
  if not @commands[cn]
1127
1201
  a = @commands.keys.find_all { |k| k[0, cn.length] == cn }
1128
1202
  cn = a.first if a.length == 1
1129
1203
  end
1130
- if pc = @commands[cn]
1204
+ if pc = @commands[cn]
1131
1205
  pc[cmd.split(/\s+/, 2)[1].to_s]
1132
1206
  else
1133
1207
  add_log 'unknown command'
@@ -1166,6 +1240,7 @@ end
1166
1240
  class DbgWindow < Window
1167
1241
  attr_accessor :dbg_widget
1168
1242
  def initialize_window(dbg = nil, title='metasm debugger')
1243
+ dbg, title = title, dbg if dbg.kind_of?(::String) or title.kind_of?(Debugger)
1169
1244
  self.title = title
1170
1245
  display(dbg) if dbg
1171
1246
  end
@@ -45,7 +45,7 @@ module Msgbox
45
45
  InputBox.new(toplevel, *a) { |*ya| protect { yield(*ya) } }
46
46
  end
47
47
 
48
- # asks to chose a file to open, yields filename
48
+ # asks to choose a file to open, yields filename
49
49
  # args: title, :path => path
50
50
  def openfile(*a)
51
51
  OpenFile.new(toplevel, *a) { |*ya| protect { yield(*ya) } }
@@ -128,13 +128,18 @@ class ContainerVBoxWidget < Gtk::VBox
128
128
  def resize_child(cld, w, h)
129
129
  pk = query_child_packing(cld)
130
130
  if h <= 0
131
- pk[0] = true
131
+ if pk[0] != true
132
+ pk[0] = true
133
+ set_child_packing(cld, *pk)
134
+ end
132
135
  h = 1
133
136
  else
134
- pk[0] = false
137
+ if pk[0] == true
138
+ pk[0] = false
139
+ set_child_packing(cld, *pk)
140
+ end
135
141
  end
136
142
  return if h == cld.allocation.height
137
- set_child_packing(cld, *pk)
138
143
  cld.set_height_request(h)
139
144
  end
140
145
 
@@ -145,7 +150,7 @@ end
145
150
  class DrawableWidget < Gtk::DrawingArea
146
151
  include Msgbox
147
152
 
148
- attr_accessor :parent_widget, :caret_x, :caret_y, :hl_word
153
+ attr_accessor :parent_widget, :caret_x, :caret_y, :hl_word, :hl_word_re
149
154
  # this hash is used to determine the colors of the Gui elements (background, caret, ...)
150
155
  # modifications to it are only useful before the widget is first rendered (IE before Gui.main)
151
156
  attr_accessor :default_color_association
@@ -153,7 +158,7 @@ class DrawableWidget < Gtk::DrawingArea
153
158
  # keypress event keyval traduction table
154
159
  Keyboard_trad = Gdk::Keyval.constants.grep(/^GDK_/).inject({}) { |h, cst|
155
160
  v = Gdk::Keyval.const_get(cst)
156
- key = cst.to_s.sub(/^GDK_/, '').sub(/^KP_/, '')
161
+ key = cst.to_s.sub(/^GDK_/, '').sub(/^KEY_/, '').sub(/^KP_/, '')
157
162
  if key.length == 1
158
163
  key = key[0] # ?a, ?b etc
159
164
  else
@@ -161,7 +166,7 @@ class DrawableWidget < Gtk::DrawingArea
161
166
  key = {
162
167
  :page_up => :pgup, :page_down => :pgdown, :next => :pgdown,
163
168
  :escape => :esc, :return => :enter, :l1 => :f11, :l2 => :f12,
164
- :prior => :pgup,
169
+ :prior => :pgup, :menu => :popupmenu,
165
170
 
166
171
  :space => ?\ ,
167
172
  :asciitilde => ?~, :quoteleft => ?`,
@@ -189,6 +194,15 @@ class DrawableWidget < Gtk::DrawingArea
189
194
  h.update v => key
190
195
  }
191
196
 
197
+ BasicColor = { :white => 'fff', :palegrey => 'ddd', :black => '000', :grey => '444',
198
+ :red => 'f44', :darkred => '800', :palered => 'faa',
199
+ :green => '4f4', :darkgreen => '080', :palegreen => 'afa',
200
+ :blue => '44f', :darkblue => '008', :paleblue => 'aaf',
201
+ :yellow => 'ff4', :darkyellow => '440', :paleyellow => 'ffa',
202
+ :orange => 'fc8',
203
+
204
+ }
205
+
192
206
  def initialize(*a, &b)
193
207
  @parent_widget = nil
194
208
 
@@ -226,7 +240,6 @@ class DrawableWidget < Gtk::DrawingArea
226
240
  grab_focus
227
241
  case ev.button
228
242
  when 1; protect { click(ev.x, ev.y) } if respond_to? :click
229
- when 3; protect { rightclick(ev.x, ev.y) } if respond_to? :rightclick
230
243
  end
231
244
  when Gdk::Event::Type::BUTTON2_PRESS
232
245
  case ev.button
@@ -245,8 +258,11 @@ class DrawableWidget < Gtk::DrawingArea
245
258
  } if respond_to? :mousemove
246
259
 
247
260
  signal_connect('button_release_event') { |w, ev|
248
- protect { mouserelease(ev.x, ev.y) } if ev.button == 1
249
- } if respond_to? :mouserelease
261
+ case ev.button
262
+ when 1; protect { mouserelease(ev.x, ev.y) } if respond_to? :mouserelease
263
+ when 3; protect { rightclick(ev.x, ev.y) } if respond_to? :rightclick
264
+ end
265
+ }
250
266
 
251
267
  signal_connect('scroll_event') { |w, ev|
252
268
  dir = case ev.direction
@@ -273,12 +289,7 @@ class DrawableWidget < Gtk::DrawingArea
273
289
  }
274
290
 
275
291
  signal_connect('realize') {
276
- { :white => 'fff', :palegrey => 'ddd', :black => '000', :grey => '444',
277
- :red => 'f00', :darkred => '800', :palered => 'fcc',
278
- :green => '0f0', :darkgreen => '080', :palegreen => 'cfc',
279
- :blue => '00f', :darkblue => '008', :paleblue => 'ccf',
280
- :yellow => 'ff0', :darkyellow => '440', :paleyellow => 'ffc',
281
- }.each { |tag, val|
292
+ BasicColor.each { |tag, val|
282
293
  @color[tag] = color(val)
283
294
  }
284
295
 
@@ -302,7 +313,11 @@ class DrawableWidget < Gtk::DrawingArea
302
313
  # create a color from a 'rgb' description
303
314
  def color(val)
304
315
  if not @color[val]
305
- @color[val] = Gdk::Color.new(*val.unpack('CCC').map { |c| (c.chr*4).hex })
316
+ v = case val.length
317
+ when 3; val.scan(/./).map { |c| (c*4).to_i(16) }
318
+ when 6; val.scan(/../).map { |c| (c+c).to_i(16) }
319
+ end
320
+ @color[val] = Gdk::Color.new(*v)
306
321
  window.colormap.alloc_color(@color[val], true, true)
307
322
  end
308
323
  @color[val]
@@ -325,20 +340,48 @@ class DrawableWidget < Gtk::DrawingArea
325
340
 
326
341
  # change the color association
327
342
  # arg is a hash function symbol => color symbol
328
- # color must be allocated
329
343
  # check #initialize/sig('realize') for initial function/color list
344
+ # if called before the widget is first displayed onscreen, will register a hook to re-call itself later
330
345
  def set_color_association(hash)
331
- hash.each { |k, v| @color[k] = color(v) }
332
- modify_bg Gtk::STATE_NORMAL, @color[:background]
333
- gui_update
346
+ if not realized?
347
+ sid = signal_connect('realize') {
348
+ signal_handler_disconnect(sid)
349
+ set_color_association(hash)
350
+ }
351
+ else
352
+ hord = Hash.new { |h, k| h[k] = (hash[k] ? h[hash[k]] + 1 : 0) }
353
+ hash.sort_by { |k, v| hord[k] }.each { |k, v| @color[k] = color(v) }
354
+ modify_bg Gtk::STATE_NORMAL, @color[:background]
355
+ gui_update
356
+ end
357
+ end
358
+
359
+ def new_menu
360
+ toplevel.new_menu
361
+ end
362
+ def addsubmenu(*a, &b)
363
+ toplevel.addsubmenu(*a, &b)
364
+ end
365
+ def popupmenu(m, x, y)
366
+ toplevel.popupmenu(m, (x+allocation.x).to_i, (y+allocation.y).to_i)
334
367
  end
335
368
 
336
369
  # update @hl_word from a line & offset, return nil if unchanged
337
- def update_hl_word(line, offset)
370
+ def update_hl_word(line, offset, mode=:asm)
338
371
  return if not line
339
372
  word = line[0...offset].to_s[/\w*$/] << line[offset..-1].to_s[/^\w*/]
340
373
  word = nil if word == ''
341
- @hl_word = word if @hl_word != word
374
+ if @hl_word != word
375
+ if word
376
+ if mode == :asm and defined?(@dasm) and @dasm
377
+ re = @dasm.gui_hilight_word_regexp(word)
378
+ else
379
+ re = Regexp.escape word
380
+ end
381
+ @hl_word_re = /^(.*?)(\b(?:#{re})\b)/
382
+ end
383
+ @hl_word = word
384
+ end
342
385
  end
343
386
 
344
387
  def paint
@@ -385,6 +428,20 @@ class DrawableWidget < Gtk::DrawingArea
385
428
  end
386
429
 
387
430
  def draw_rectangle(x, y, w, h)
431
+ # GTK clips coords around 0x8000
432
+ return if x > 0x7000 or y > 0x7000
433
+ if x < -0x7000
434
+ w += x + 100
435
+ x = -100
436
+ end
437
+ if y < -0x7000
438
+ h += y + 100
439
+ y = -100
440
+ end
441
+ return if w <= 0 or h <= 0
442
+ w = 0x7000 if w > 0x7000
443
+ h = 0x7000 if h > 0x7000
444
+
388
445
  @w.draw_rectangle(@gc, true, x, y, w, h)
389
446
  end
390
447
 
@@ -394,6 +451,29 @@ class DrawableWidget < Gtk::DrawingArea
394
451
  end
395
452
 
396
453
  def draw_line(x, y, ex, ey)
454
+ if x.abs > 0x7000
455
+ return if ex.abs > 0x7000 and ((ex < 0) == (x < 0))
456
+ ox = x
457
+ x = ((x > 0) ? 0x7000 : -0x7000)
458
+ y = ey+(x-ex)*(y-ey)/(ox-ex)
459
+ end
460
+ if ex.abs > 0x7000
461
+ oex = ex
462
+ ex = ((ex > 0) ? 0x7000 : -0x7000)
463
+ ey = y+(ex-x)*(ey-y)/(oex-x)
464
+ end
465
+ if y.abs > 0x7000
466
+ return if ey.abs > 0x7000 and ((ey < 0) == (y < 0))
467
+ oy = y
468
+ y = ((y > 0) ? 0x7000 : -0x7000)
469
+ x = ex+(y-ey)*(x-ex)/(oy-ey)
470
+ end
471
+ if ey.abs > 0x7000
472
+ oey = ey
473
+ ey = ((ey > 0) ? 0x7000 : -0x7000)
474
+ ex = x+(ey-y)*(ex-x)/(oey-y)
475
+ end
476
+
397
477
  @w.draw_line(@gc, x, y, ex, ey)
398
478
  end
399
479
 
@@ -403,6 +483,7 @@ class DrawableWidget < Gtk::DrawingArea
403
483
  end
404
484
 
405
485
  def draw_string(x, y, str)
486
+ return if x.abs > 0x7000 or y.abs > 0x7000
406
487
  @layout.text = str
407
488
  @w.draw_layout(@gc, x, y, @layout)
408
489
  end
@@ -412,6 +493,23 @@ class DrawableWidget < Gtk::DrawingArea
412
493
  draw_string(x, y, str)
413
494
  end
414
495
 
496
+ # same as draw_string_color + hilight @hl_word_re
497
+ def draw_string_hl(col, x, y, str)
498
+ if @hl_word
499
+ while str =~ @hl_word_re
500
+ s1, s2 = $1, $2
501
+ draw_string_color(col, x, y, s1)
502
+ x += s1.length*@font_width
503
+ hl_w = s2.length*@font_width
504
+ draw_rectangle_color(:hl_word_bg, x, y, hl_w, @font_height)
505
+ draw_string_color(:hl_word, x, y, s2)
506
+ x += hl_w
507
+ str = str[s1.length+s2.length..-1]
508
+ end
509
+ end
510
+ draw_string_color(col, x, y, str)
511
+ end
512
+
415
513
  def clipboard_copy(buf)
416
514
  clipboard = Gtk::Clipboard.get(Gdk::Selection::PRIMARY)
417
515
  clipboard.text = buf
@@ -477,15 +575,38 @@ class InputBox < Gtk::Dialog
477
575
  @textwidget = Gtk::TextView.new
478
576
  if opts[:text]
479
577
  @textwidget.buffer.text = opts[:text].to_s
480
- @textwidget.buffer.move_mark('selection_bound', @textwidget.buffer.start_iter)
481
- @textwidget.buffer.move_mark('insert', @textwidget.buffer.end_iter)
578
+ text_select_all
482
579
  end
483
580
 
581
+ @@history ||= {}
582
+ histkey = opts[:history] || str[0, 10]
583
+ @history = (@@history[histkey] ||= [])
584
+ @history_off = @history.length
585
+
484
586
  @textwidget.signal_connect('key_press_event') { |w, ev|
485
587
  key = DrawableWidget::Keyboard_trad[ev.keyval]
486
588
  case key
487
- when :escape; response(RESPONSE_REJECT) ; true
488
- when :enter; response(RESPONSE_ACCEPT) ; true
589
+ when :escape
590
+ @history_off = @history.length
591
+ response(RESPONSE_REJECT)
592
+ true
593
+ when :enter
594
+ @history.pop if @history.last == ''
595
+ @history << @textwidget.buffer.text.to_s
596
+ @history.pop if @history.last == ''
597
+ @history.pop if @history.last == @history[-2]
598
+ @history_off = @history.length
599
+ response(RESPONSE_ACCEPT)
600
+ true
601
+ when :up, :down
602
+ txt = @textwidget.buffer.text.to_s
603
+ if (@history_off < @history.length or @history.last != txt)
604
+ @history[@history_off] = txt
605
+ end
606
+ @history_off += (key == :up ? -1 : 1)
607
+ @history_off %= (@history.length > 0 ? @history.length : 1)
608
+ @textwidget.buffer.text = @history[@history_off].to_s
609
+ text_select_all
489
610
  end
490
611
  }
491
612
 
@@ -502,9 +623,9 @@ class InputBox < Gtk::Dialog
502
623
  Gtk::Drag.dest_set(self,
503
624
  Gtk::Drag::DEST_DEFAULT_MOTION |
504
625
  Gtk::Drag::DEST_DEFAULT_DROP,
505
- [['text/plain', 0, 0], ['text/uri-list', 0, 0]],
626
+ [['text/plain', 0, 0], ['text/uri-list', 0, 0]],
506
627
  Gdk::DragContext::ACTION_COPY | Gdk::DragContext::ACTION_MOVE)
507
-
628
+
508
629
  signal_connect('drag_data_received') { |w, dc, x, y, data, info, time|
509
630
  dc.targets.each { |target|
510
631
  next if target.name != 'text/plain' and target.name != 'text/uri-list'
@@ -521,6 +642,11 @@ class InputBox < Gtk::Dialog
521
642
  present
522
643
  end
523
644
 
645
+ def text_select_all
646
+ @textwidget.buffer.move_mark('selection_bound', @textwidget.buffer.start_iter)
647
+ @textwidget.buffer.move_mark('insert', @textwidget.buffer.end_iter)
648
+ end
649
+
524
650
  def text ; @textwidget.buffer.text ; end
525
651
  def text=(nt) ; @textwidget.buffer.text = nt ; end
526
652
  end
@@ -604,7 +730,7 @@ class ListWindow < Gtk::Dialog
604
730
  tvc = Gtk::TreeViewColumn.new(col, crt)
605
731
  tvc.sort_column_id = i
606
732
  tvc.set_cell_data_func(crt) { |_tvc, _crt, model, iter|
607
- _crt.text = iter[i]
733
+ _crt.text = iter[i]
608
734
  if @color_callback
609
735
  fu = (0...cols.length).map { |ii| iter[ii] }
610
736
  fg, bg = @color_callback[fu]
@@ -635,7 +761,7 @@ class ListWindow < Gtk::Dialog
635
761
 
636
762
  remove vbox
637
763
  add Gtk::ScrolledWindow.new.add(treeview)
638
- toplevel.set_default_size cols.length*120, 400
764
+ toplevel.set_default_size cols.length*240, 400
639
765
 
640
766
  show if not h[:noshow]
641
767
 
@@ -666,6 +792,7 @@ class Window < Gtk::Window
666
792
  @menubar = Gtk::MenuBar.new
667
793
  @accel_group = Gtk::AccelGroup.new
668
794
 
795
+ set_gravity Gdk::Window::GRAVITY_STATIC
669
796
  @vbox.add @menubar, 'expand' => false
670
797
  @child = nil
671
798
  s = Gdk::Screen.default
@@ -678,14 +805,13 @@ class Window < Gtk::Window
678
805
  initialize_window(*a, &b)
679
806
  build_menu
680
807
  update_menu
681
-
682
-
808
+
683
809
  Gtk::Drag.dest_set(self,
684
810
  Gtk::Drag::DEST_DEFAULT_MOTION |
685
811
  Gtk::Drag::DEST_DEFAULT_DROP,
686
- [['text/plain', 0, 0], ['text/uri-list', 0, 0]],
812
+ [['text/plain', 0, 0], ['text/uri-list', 0, 0]],
687
813
  Gdk::DragContext::ACTION_COPY | Gdk::DragContext::ACTION_MOVE)
688
-
814
+
689
815
  signal_connect('drag_data_received') { |w, dc, x, y, data, info, time|
690
816
  dc.targets.each { |target|
691
817
  next if target.name != 'text/plain' and target.name != 'text/uri-list'
@@ -734,6 +860,13 @@ class Window < Gtk::Window
734
860
  l.grep(::Array).first if l
735
861
  end
736
862
 
863
+ def popupmenu(m, x, y)
864
+ mh = Gtk::Menu.new
865
+ m.each { |e| create_menu_item(mh, e) }
866
+ mh.show_all
867
+ mh.popup(nil, nil, 2, 0) { |_m, _x, _y, _p| [position[0]+x, position[1]+y, true] }
868
+ end
869
+
737
870
  # append stuff to a menu
738
871
  # arglist:
739
872
  # empty = menu separator
@@ -801,13 +934,10 @@ class Window < Gtk::Window
801
934
  key = accel[-1]
802
935
  if key == ?>
803
936
  key = accel[/<(.*)>/, 1]
804
- key = case key
805
- when 'enter'; Gdk::Keyval::GDK_Return
806
- when 'esc'; Gdk::Keyval::GDK_Escape
807
- when 'tab'; Gdk::Keyval::GDK_Tab
808
- when /^f(\d\d?)$/i; Gdk::Keyval.const_get("GDK_#{key.upcase}")
809
- else ??
810
- end
937
+ key = DrawableWidget::Keyboard_trad.index(case key
938
+ when 'enter', 'esc', 'tab', /^f(\d\d?)$/i; key.downcase.to_sym
939
+ else ??
940
+ end)
811
941
  end
812
942
  key = key.unpack('C')[0] if key.kind_of? String # yay rb19
813
943
  item.add_accelerator('activate', @accel_group, key, (accel[0] == ?^ ? Gdk::Window::CONTROL_MASK : 0), Gtk::ACCEL_VISIBLE)
@@ -815,7 +945,7 @@ class Window < Gtk::Window
815
945
  if action
816
946
  a = action
817
947
  if check
818
- a = lambda { item.active = action.call(item.active?) }
948
+ a = lambda { |it| it.active = action.call(it.active?) }
819
949
  end
820
950
  item.signal_connect('activate') { protect { a.call(item) } }
821
951
  end
@@ -838,7 +968,7 @@ class ToolWindow < Gtk::Dialog
838
968
  initialize_window(*a, &b)
839
969
  show_all
840
970
  end
841
-
971
+
842
972
  def widget=(w)
843
973
  remove @child if @child
844
974
  @child = w