# Copyright (C) 2009, 2010 Roman Zimbelmann <romanz@lavabit.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""Helper functions"""
from errno import EEXIST
import os.path
import sys
from ranger import *
LOGFILE = '/tmp/errorlog'
def parse_arguments():
"""Parse the program arguments"""
from optparse import OptionParser, SUPPRESS_HELP
from ranger import pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */# Helper to print an int32 in decimal.
== code
# instruction effective address register displacement immediate
# . op subop mod rm32 base index scale r32
# . 1-3 bytes 3 bits 2 bits 3 bits 3 bits 3 bits 2 bits 2 bits 0/1/2/4 bytes 0/1/2/4 bytes
write-int32-decimal: # out: (addr stream byte), n: int
# works by generating characters from lowest to highest and pushing them
# to the stack, before popping them one by one into the stream
#
# pseudocode:
# push sentinel
# eax = abs(n)
# while true
# sign-extend eax into edx
# eax, edx = eax/10, eax%10
# edx += '0'
# push edx
# if (eax == 0) break
# if n < 0
# push '-'
# w = out->write
# curr = &out->data[out->write]
# max = &out->data[out->size]
# while true
# pop into eax
# if (eax == sentinel) break
# if (curr >= max) abort
# *curr = AL
# ++curr
# ++w
# out->write = w
# (based on K&R itoa: https://en.wikibooks.org/wiki/C_Programming/stdlib.h/itoa)
# (this pseudocode contains registers because operations like division
# require specific registers in x86)
#
# . prologue
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# . save registers
50/push-eax
51/push-ecx
52/push-edx
53/push-ebx
57/push-edi
# const ten/ecx = 10
b9/copy-to-ecx 0xa/imm32
# push sentinel
68/push 0/imm32/sentinel
# var eax: int = abs(n)
8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 0xc/disp8 . # copy *(ebp+12) to eax
3d/compare-eax-with 0/imm32
7d/jump-if->= $write-int32-decimal:read-loop/disp8
$write-int32-decimal:negative:
f7 3/subop/negate 3/mod/direct 0/rm32/eax . . . . . . # negate eax
$write-int32-decimal:read-loop:
# eax, edx = eax / 10, eax % 10
99/sign-extend-eax-into-edx
f7 7/subop/idiv 3/mod/direct 1/rm32/ecx . . . . . . # divide edx:eax by ecx, storing quotient in eax and remainder in edx
# edx += '0'
81 0/subop/add 3/mod/direct 2/rm32/edx . . . . . 0x30/imm32 # add to edx
# push edx
52/push-edx
# if (eax == 0) break
3d/compare-eax-and 0/imm32
7f/jump-if-> $write-int32-decimal:read-loop/disp8
$write-int32-decimal:read-break:
# if (n < 0) push('-')
81 7/subop/compare 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 0/imm32 # compare *(ebp+12)
7d/jump-if->= $write-int32-decimal:write/disp8
$write-int32-decimal:push-negative:
68/push 0x2d/imm32/-
$write-int32-decimal:write:
# edi = out
8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 7/r32/edi 8/disp8 . # copy *(ebp+8) to edi
# var w/edx: int = out->write
8b/copy 0/mod/indirect 7/rm32/edi . . . 2/r32/edx . . # copy *edi to edx
# var curr/ecx: (addr byte) = &out->data[out->write]
8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/edi 2/index/edx . 1/r32/ecx 0xc/disp8 . # copy ebx+edx+12 to ecx
# var max/ebx: (addr byte) = &out->data[out->size]
8b/copy 1/mod/*+disp8 7/rm32/edi . . . 3/r32/ebx 8/disp8 . # copy *(edi+8) to ebx
8d/copy-address 1/mod/*+disp8 4/rm32/sib 7/base/edi 3/index/ebx . 3/r32/ebx 0xc/disp8 . # copy edi+ebx+12 to ebx
$write-int32-decimal:write-loop:
# pop into eax
58/pop-to-eax
# if (eax == sentinel) break
3d/compare-eax-and 0/imm32/sentinel
74/jump-if-= $write-int32-decimal:write-break/disp8
# if (curr >= max) abort
39/compare 3/mod/direct 1/rm32/ecx . . . 3/r32/ebx . . # compare ecx with ebx
73/jump-if-addr>= $write-int32-decimal:abort/disp8
$write-int32-decimal:write-char:
# *curr = AL
88/copy-byte 0/mod/indirect 1/rm32/ecx . . . 0/r32/AL . . # copy AL to byte at *ecx
# ++curr
41/increment-ecx
# ++w
42/increment-edx
eb/jump $write-int32-decimal:write-loop/disp8
$write-int32-decimal:write-break:
# out->write = w
89/copy 0/mod/indirect 7/rm32/edi . . . 2/r32/edx . . # copy edx to *edi
$write-int32-decimal:end:
# . restore registers
5f/pop-to-edi
5b/pop-to-ebx
5a/pop-to-edx
59/pop-to-ecx
58/pop-to-eax
# . epilogue
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
$write-int32-decimal:abort:
(draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "write-int32-decimal: stream out of space" 3 0) # 3=cyan
{
eb/jump loop/disp8
}
# never gets here
test-write-int32-decimal:
# - check that a single-digit number converts correctly
# setup
# . clear-stream(_test-stream)
# . . push args
68/push _test-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# write-int32-decimal(_test-stream, 9)
# . . push args
68/push 9/imm32
68/push _test-stream/imm32
# . . call
e8/call write-int32-decimal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# check-stream-equal(_test-stream, "9", msg)
# . . push args
68/push "F - test-write-int32-decimal"/imm32
68/push "9"/imm32
68/push _test-stream/imm32
# . . call
e8/call check-stream-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . end
c3/return
test-write-int32-decimal-zero:
# - check that 0 converts correctly
# setup
# . clear-stream(_test-stream)
# . . push args
68/push _test-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# write-int32-decimal(_test-stream, 0)
# . . push args
68/push 0/imm32
68/push _test-stream/imm32
# . . call
e8/call write-int32-decimal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# check-stream-equal(_test-stream, "0", msg)
# . . push args
68/push "F - test-write-int32-decimal-zero"/imm32
68/push "0"/imm32
68/push _test-stream/imm32
# . . call
e8/call check-stream-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . end
c3/return
test-write-int32-decimal-multiple-digits:
# - check that a multi-digit number converts correctly
# setup
# . clear-stream(_test-stream)
# . . push args
68/push _test-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# write-int32-decimal(_test-stream, 10)
# . . push args
68/push 0xa/imm32
68/push _test-stream/imm32
# . . call
e8/call write-int32-decimal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# check-stream-equal(_test-stream, "10", msg)
# . . push args
68/push "F - test-write-int32-decimal-multiple-digits"/imm32
68/push "10"/imm32
68/push _test-stream/imm32
# . . call
e8/call check-stream-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . end
c3/return
test-write-int32-decimal-negative:
# - check that a negative single-digit number converts correctly
# setup
# . clear-stream(_test-stream)
# . . push args
68/push _test-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# write-int32-decimal(_test-stream, -9)
# . . push args
68/push -9/imm32
68/push _test-stream/imm32
# . . call
e8/call write-int32-decimal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # dump _test-stream {{{
#? # . write(2/stderr, "^")
#? # . . push args
#? 68/push "^"/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . write-stream(2/stderr, _test-stream)
#? # . . push args
#? 68/push _test-stream/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write-stream/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # . write(2/stderr, "$\n")
#? # . . push args
#? 68/push "$\n"/imm32
#? 68/push 2/imm32/stderr
#? # . . call
#? e8/call write/disp32
#? # . . discard args
#? 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
#? # }}}
# check-stream-equal(_test-stream, "-9", msg)
# . . push args
68/push "F - test-write-int32-decimal-negative"/imm32
68/push "-9"/imm32
68/push _test-stream/imm32
# . . call
e8/call check-stream-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . end
c3/return
test-write-int32-decimal-negative-multiple-digits:
# - check that a multi-digit number converts correctly
# setup
# . clear-stream(_test-stream)
# . . push args
68/push _test-stream/imm32
# . . call
e8/call clear-stream/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# write-int32-decimal(_test-stream, -10)
# . . push args
68/push -0xa/imm32
68/push _test-stream/imm32
# . . call
e8/call write-int32-decimal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
# check-stream-equal(_test-stream, "-10", msg)
# . . push args
68/push "F - test-write-int32-decimal-negative-multiple-digits"/imm32
68/push "-10"/imm32
68/push _test-stream/imm32
# . . call
e8/call check-stream-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# . end
c3/return
is-decimal-digit?: # c: grapheme -> result/eax: boolean
# . prologue
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# . save registers
51/push-ecx
# ecx = c
8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 8/disp8 . # copy *(ebp+8) to ecx
# result = false
b8/copy-to-eax 0/imm32/false
# return false if c < '0'
81 7/subop/compare 3/mod/direct 1/rm32/ecx . . . . . 0x30/imm32 # compare ecx
7c/jump-if-< $is-decimal-digit?:end/disp8
# return (c <= '9')
81 7/subop/compare 3/mod/direct 1/rm32/ecx . . . . . 0x39/imm32 # compare ecx
7f/jump-if-> $is-decimal-digit?:end/disp8
$is-decimal-digit?:true:
b8/copy-to-eax 1/imm32/true
$is-decimal-digit?:end:
# . restore registers
59/pop-to-ecx
# . epilogue
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
test-is-decimal-digit-below-0:
# eax = is-decimal-digit?(0x2f)
# . . push args
68/push 0x2f/imm32
# . . call
e8/call is-decimal-digit?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# check-ints-equal(eax, 0, msg)
# . . push args
68/push "F - test-is-decimal-digit-below-0"/imm32
68/push 0/imm32/false
50/push-eax
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
c3/return
test-is-decimal-digit-0-to-9:
# eax = is-decimal-digit?(0x30)
# . . push args
68/push 0x30/imm32
# . . call
e8/call is-decimal-digit?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# check-ints-equal(eax, 1, msg)
# . . push args
68/push "F - test-is-decimal-digit-at-0"/imm32
68/push 1/imm32/true
50/push-eax
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
# eax = is-decimal-digit?(0x39)
# . . push args
68/push 0x39/imm32
# . . call
e8/call is-decimal-digit?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# check-ints-equal(eax, 1, msg)
# . . push args
68/push "F - test-is-decimal-digit-at-9"/imm32
68/push 1/imm32/true
50/push-eax
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
c3/return
test-is-decimal-digit-above-9:
# eax = is-decimal-digit?(0x3a)
# . . push args
68/push 0x3a/imm32
# . . call
e8/call is-decimal-digit?/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp
# check-ints-equal(eax, 0, msg)
# . . push args
68/push "F - test-is-decimal-digit-above-9"/imm32
68/push 0/imm32/false
50/push-eax
# . . call
e8/call check-ints-equal/disp32
# . . discard args
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
c3/return
to-decimal-digit: # in: grapheme -> out/eax: int
# . prologue
55/push-ebp
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
# eax = in
8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 8/disp8 . # copy *(ebp+8) to eax
$to-decimal-digit:check0:
# if (eax < '0') goto abort
3d/compare-eax-with 0x30/imm32/0
7c/jump-if-< $to-decimal-digit:abort/disp8
$to-decimal-digit:check1:
# if (eax > '9') goto abort
3d/compare-eax-with 0x39/imm32/f
7f/jump-if-> $to-decimal-digit:abort/disp8
$to-decimal-digit:digit:
# return eax - '0'
2d/subtract-from-eax 0x30/imm32/0
$to-decimal-digit:end:
# . epilogue
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
5d/pop-to-ebp
c3/return
$to-decimal-digit:abort:
(draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "to-decimal-digit: not a digit character" 3 0) # 3=cyan
{
eb/jump loop/disp8
}
# never gets here
# . . vim:nowrap:textwidth=0