diff --git a/.config/dunst/dunstrc b/.config/dunst/dunstrc
new file mode 100644
index 0000000..5915d74
--- /dev/null
+++ b/.config/dunst/dunstrc
@@ -0,0 +1,248 @@
+[global]
+ font = Mono 16
+
+ # Allow a small subset of html markup:
+ # bold
+ # italic
+ # strikethrough
+ # underline
+ #
+ # For a complete reference see
+ # .
+ # If markup is not allowed, those tags will be stripped out of the
+ # message.
+ allow_markup = yes
+
+ # The format of the message. Possible variables are:
+ # %a appname
+ # %s summary
+ # %b body
+ # %i iconname (including its path)
+ # %I iconname (without its path)
+ # %p progress value if set ([ 0%] to [100%]) or nothing
+ # Markup is allowed
+ format = "%s\n%b"
+
+ # Sort messages by urgency.
+ sort = no #yes
+
+ # Show how many messages are currently hidden (because of geometry).
+ indicate_hidden = no
+
+ # Alignment of message text.
+ # Possible values are "left", "center" and "right".
+ alignment = left
+
+ # The frequency with which text that is longer than the notification
+ # window allows bounces back and forth.
+ # This option conflicts with "word_wrap".
+ # Set to 0 to disable.
+ bounce_freq = 0
+
+ # Show age of message if message is older than show_age_threshold
+ # seconds.
+ # Set to -1 to disable.
+ show_age_threshold = -1 #60
+
+ # Split notifications into multiple lines if they don't fit into
+ # geometry.
+ word_wrap = yes
+
+ # Ignore newlines '\n' in notifications.
+ ignore_newline = no
+
+
+ # The geometry of the window:
+ # [{width}]x{height}[+/-{x}+/-{y}]
+ # The geometry of the message window.
+ # The height is measured in number of notifications everything else
+ # in pixels. If the width is omitted but the height is given
+ # ("-geometry x2"), the message window expands over the whole screen
+ # (dmenu-like). If width is 0, the window expands to the longest
+ # message displayed. A positive x is measured from the left, a
+ # negative from the right side of the screen. Y is measured from
+ # the top and down respectively.
+ # The width can be negative. In this case the actual width is the
+ # screen width minus the width defined in within the geometry option.
+ geometry = "400x10-40+30"
+
+ # Shrink window if it's smaller than the width. Will be ignored if
+ # width is 0.
+ shrink = no
+
+ # The transparency of the window. Range: [0; 100].
+ # This option will only work if a compositing window manager is
+ # present (e.g. xcompmgr, compiz, etc.).
+ transparency = 0
+
+ # Don't remove messages, if the user is idle (no mouse or keyboard input)
+ # for longer than idle_threshold seconds.
+ # Set to 0 to disable.
+ idle_threshold = 120
+
+ # Which monitor should the notifications be displayed on.
+ monitor = 0
+
+ # Display notification on focused monitor. Possible modes are:
+ # mouse: follow mouse pointer
+ # keyboard: follow window with keyboard focus
+ # none: don't follow anything
+ #
+ # "keyboard" needs a window manager that exports the
+ # _NET_ACTIVE_WINDOW property.
+ # This should be the case for almost all modern window managers.
+ #
+ # If this option is set to mouse or keyboard, the monitor option
+ # will be ignored.
+ follow = mouse
+
+ # Should a notification popped up from history be sticky or timeout
+ # as if it would normally do.
+ sticky_history = no # yes
+
+ # Maximum amount of notifications kept in history
+ history_length = 20
+
+ # Display indicators for URLs (U) and actions (A).
+ show_indicators = no
+
+ # The height of a single line. If the height is smaller than the
+ # font height, it will get raised to the font height.
+ # This adds empty space above and under the text.
+ line_height = 0
+
+ # Draw a line of "separator_height" pixel height between two
+ # notifications.
+ # Set to 0 to disable.
+ separator_height = 2
+
+ # Padding between text and separator.
+ padding = 8
+
+ # Horizontal padding.
+ horizontal_padding = 8
+
+ # Define a color for the separator.
+ # possible values are:
+ # * auto: dunst tries to find a color fitting to the background;
+ # * foreground: use the same color as the foreground;
+ # * frame: use the same color as the frame;
+ # * anything else will be interpreted as a X color.
+ separator_color = frame
+
+ # Print a notification on startup.
+ # This is mainly for error detection, since dbus (re-)starts dunst
+ # automatically after a crash.
+ startup_notification = false
+
+ # dmenu path.
+ dmenu = /usr/bin/dmenu -p dunst:
+
+ # Browser for opening urls in context menu.
+ browser = /usr/bin/firefox -new-tab
+
+ # Align icons left/right/off
+ icon_position = off
+
+ # Paths to default icons.
+ icon_folders = /usr/share/icons/gnome/16x16/status/:/usr/share/icons/gnome/16x16/devices/
+
+[frame]
+ width = 1
+ color = "#424242"
+
+[shortcuts]
+
+ # Shortcuts are specified as [modifier+][modifier+]...key
+ # Available modifiers are "ctrl", "mod1" (the alt-key), "mod2",
+ # "mod3" and "mod4" (windows-key).
+ # Xev might be helpful to find names for keys.
+
+ # Close notification.
+ close = ctrl+space
+
+ # Close all notifications.
+ close_all = ctrl+shift+space
+
+ # Redisplay last message(s).
+ # On the US keyboard layout "grave" is normally above TAB and left
+ # of "1".
+ history = ctrl+grave
+
+ # Context menu.
+ context = ctrl+shift+period
+
+[urgency_low]
+ # IMPORTANT: colors have to be defined in quotation marks.
+ # Otherwise the "#" and following would be interpreted as a comment.
+ background = "#222222"
+ #foreground = ${xrdb:color6}
+ foreground = "#dfdfdf"
+ timeout = 5
+
+[urgency_normal]
+ background = "#222222"
+ #foreground = "${xrdb:color6}
+ foreground = "#dfdfdf"
+ timeout = 5
+
+[urgency_critical]
+ background = "#222222"
+ #foreground = ${xrdb:color6}
+ foreground = "#dfdfdf"
+ timeout = 5
+
+# Every section that isn't one of the above is interpreted as a rules to
+# override settings for certain messages.
+# Messages can be matched by "appname", "summary", "body", "icon", "category",
+# "msg_urgency" and you can override the "timeout", "urgency", "foreground",
+# "background", "new_icon" and "format".
+# Shell-like globbing will get expanded.
+#
+# SCRIPTING
+# You can specify a script that gets run when the rule matches by
+# setting the "script" option.
+# The script will be called as follows:
+# script appname summary body icon urgency
+# where urgency can be "LOW", "NORMAL" or "CRITICAL".
+#
+# NOTE: if you don't want a notification to be displayed, set the format
+# to "".
+# NOTE: It might be helpful to run dunst -print in a terminal in order
+# to find fitting options for rules.
+
+#[espeak]
+# summary = "*"
+# script = dunst_espeak.sh
+
+#[script-test]
+# summary = "*script*"
+# script = dunst_test.sh
+
+#[ignore]
+# # This notification will not be displayed
+# summary = "foobar"
+# format = ""
+
+#[signed_on]
+# appname = Pidgin
+# summary = "*signed on*"
+# urgency = low
+#
+#[signed_off]
+# appname = Pidgin
+# summary = *signed off*
+# urgency = low
+#
+#[says]
+# appname = Pidgin
+# summary = *says*
+# urgency = critical
+#
+#[twitter]
+# appname = Pidgin
+# summary = *twitter.com*
+# urgency = normal
+#
+# vim: ft=cfg
+
diff --git a/.config/iptables b/.config/iptables
new file mode 100755
index 0000000..930c637
--- /dev/null
+++ b/.config/iptables
@@ -0,0 +1,37 @@
+# CONFIGURATION:
+# Accept on localhost
+iptables -A INPUT -i lo -j ACCEPT
+iptables -A OUTPUT -o lo -j ACCEPT
+
+# Accept outgoing network
+iptables -A OUTPUT -o eth0 -j ACCEPT
+
+# Allow established sessions to receive traffic
+iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
+iptables -A INPUT -m state --state NEW,ESTABLISHED -j ACCEPT
+
+# Enable SSH (to internet)
+#iptables -A OUTPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
+#iptables -A OUTPUT -p tcp -m state --state NEW -m tcp --dport 10571 -j ACCEPT
+
+# Disable SSH (to that machine)
+iptables -A INPUT -p tcp -m state --state NEW -m tcp -s 192.168.0.0/24 --sport 22 -j DROP
+iptables -A INPUT -p tcp -m state --state NEW -m tcp -s 192.168.0.0/24 --dport 22 -j DROP
+
+# Enable INZ
+#iptables -A INPUT -p tcp -m state --state NEW -m tcp -s 192.168.0.0/24 --dport 1285 -j ACCEPT
+#iptables -A INPUT -p tcp -m state --state NEW -m tcp -s 192.168.0.0/24 --dport 3000 -j ACCEPT
+#iptables -A INPUT -p tcp -m state --state NEW -m tcp -s 192.168.0.0/24 --dport 8080 -j ACCEPT
+
+#Enable CSGO
+iptables -A INPUT -p tcp -m state --state NEW -m tcp -s 192.168.0.0/24 --dport 27015 -j ACCEPT
+iptables -A INPUT -p tcp -m state --state NEW -m tcp -s 192.168.0.0/24 --dport 27020 -j ACCEPT
+iptables -A INPUT -p udp -m state --state NEW -m tcp -s 192.168.0.0/24 --dport 27015 -j ACCEPT
+iptables -A INPUT -p udp -m state --state NEW -m tcp -s 192.168.0.0/24 --dport 27020 -j ACCEPT
+
+iptables -A INPUT -i eth0 -m state --state NEW,INVALID -j DROP
+iptables -A FORWARD -i eth0 -m state --state NEW,INVALID -j DROP
+# END OF CONFIG
+
+# END CONFIGURATION:
+# END OF CONFIGURATION
diff --git a/.config/lutris/lutris.conf b/.config/lutris/lutris.conf
new file mode 100644
index 0000000..072c3f1
--- /dev/null
+++ b/.config/lutris/lutris.conf
@@ -0,0 +1,8 @@
+[lutris]
+library_ignores =
+migration_version = 10
+width = 1908
+height = 1001
+maximized = False
+selected_category = runner:linux
+
diff --git a/.config/mpv/mpv.conf b/.config/mpv/mpv.conf
new file mode 100644
index 0000000..6d42a61
--- /dev/null
+++ b/.config/mpv/mpv.conf
@@ -0,0 +1,17 @@
+# Always allow seeking, e.g. allow seeking within a local cache of HTTP stream
+force-seekable=yes
+# Always open a video window even with no video
+force-window=yes
+# Don't exit when the end of playlist is reached
+keep-open=yes
+# Always save the current playback position on exit
+save-position-on-quit=yes
+
+# Create 'high-quality' profile
+[high-quality]
+# Describe this profile
+profile-desc="High quality rendering"
+# Include all settings from the default 'opengl-hq' profile
+profile=opengl-hq
+# Disable debanding for better performance
+deband=no
diff --git a/.config/mpv/scripts/iptv.lua b/.config/mpv/scripts/iptv.lua
new file mode 100644
index 0000000..ab43a6d
--- /dev/null
+++ b/.config/mpv/scripts/iptv.lua
@@ -0,0 +1,505 @@
+--redefine keybindings here if needed; multiple bindings are possible
+keybinds = {
+ activate = {'\\', 'MOUSE_BTN2'},
+ plsup = {'UP', 'MOUSE_BTN3'},
+ plsdown = {'DOWN', 'MOUSE_BTN4'},
+ plsenter = {'ENTER', 'MOUSE_BTN0'}
+ }
+--hide playlist after specified number of seconds
+osd_time=10
+--show only specified number of playlist entries
+window=7
+--fade video when showing playlist
+fade=false
+--if fade=true; -100 — black, 0 — normal
+plsbrightness=-70
+--favorites get promotion to the top of the pls
+favorites = {}
+-- END OF CONFIGURABLE VARIABLES
+
+-- put your settings in (SCRIPTS DIR)/_iptvconf.lua
+pcall(require, "_iptvconf")
+
+local timer
+--local plscount
+local pattern=""
+local is_active
+local is_playlist_loaded
+
+-- UTF-8 lower/upper conversion
+local utf8_lc_uc = {
+ ["a"] = "A",
+ ["b"] = "B",
+ ["c"] = "C",
+ ["d"] = "D",
+ ["e"] = "E",
+ ["f"] = "F",
+ ["g"] = "G",
+ ["h"] = "H",
+ ["i"] = "I",
+ ["j"] = "J",
+ ["k"] = "K",
+ ["l"] = "L",
+ ["m"] = "M",
+ ["n"] = "N",
+ ["o"] = "O",
+ ["p"] = "P",
+ ["q"] = "Q",
+ ["r"] = "R",
+ ["s"] = "S",
+ ["t"] = "T",
+ ["u"] = "U",
+ ["v"] = "V",
+ ["w"] = "W",
+ ["x"] = "X",
+ ["y"] = "Y",
+ ["z"] = "Z",
+ ["а"] = "А",
+ ["б"] = "Б",
+ ["в"] = "В",
+ ["г"] = "Г",
+ ["д"] = "Д",
+ ["е"] = "Е",
+ ["ж"] = "Ж",
+ ["з"] = "З",
+ ["и"] = "И",
+ ["й"] = "Й",
+ ["к"] = "К",
+ ["л"] = "Л",
+ ["м"] = "М",
+ ["н"] = "Н",
+ ["о"] = "О",
+ ["п"] = "П",
+ ["р"] = "Р",
+ ["с"] = "С",
+ ["т"] = "Т",
+ ["у"] = "У",
+ ["ф"] = "Ф",
+ ["х"] = "Х",
+ ["ц"] = "Ц",
+ ["ч"] = "Ч",
+ ["ш"] = "Ш",
+ ["щ"] = "Щ",
+ ["ъ"] = "Ъ",
+ ["ы"] = "Ы",
+ ["ь"] = "Ь",
+ ["э"] = "Э",
+ ["ю"] = "Ю",
+ ["я"] = "Я",
+ ["ё"] = "Ё"
+}
+
+local utf8_uc_lc = {
+ ["A"] = "a",
+ ["B"] = "b",
+ ["C"] = "c",
+ ["D"] = "d",
+ ["E"] = "e",
+ ["F"] = "f",
+ ["G"] = "g",
+ ["H"] = "h",
+ ["I"] = "i",
+ ["J"] = "j",
+ ["K"] = "k",
+ ["L"] = "l",
+ ["M"] = "m",
+ ["N"] = "n",
+ ["O"] = "o",
+ ["P"] = "p",
+ ["Q"] = "q",
+ ["R"] = "r",
+ ["S"] = "s",
+ ["T"] = "t",
+ ["U"] = "u",
+ ["V"] = "v",
+ ["W"] = "w",
+ ["X"] = "x",
+ ["Y"] = "y",
+ ["Z"] = "z",
+ ["А"] = "а",
+ ["Б"] = "б",
+ ["В"] = "в",
+ ["Г"] = "г",
+ ["Д"] = "д",
+ ["Е"] = "е",
+ ["Ж"] = "ж",
+ ["З"] = "з",
+ ["И"] = "и",
+ ["Й"] = "й",
+ ["К"] = "к",
+ ["Л"] = "л",
+ ["М"] = "м",
+ ["Н"] = "н",
+ ["О"] = "о",
+ ["П"] = "п",
+ ["Р"] = "р",
+ ["С"] = "с",
+ ["Т"] = "т",
+ ["У"] = "у",
+ ["Ф"] = "ф",
+ ["Х"] = "х",
+ ["Ц"] = "ц",
+ ["Ч"] = "ч",
+ ["Ш"] = "ш",
+ ["Щ"] = "щ",
+ ["Ъ"] = "ъ",
+ ["Ы"] = "ы",
+ ["Ь"] = "ь",
+ ["Э"] = "э",
+ ["Ю"] = "ю",
+ ["Я"] = "я",
+ ["Ё"] = "ё"
+}
+
+--utf8 char pattern
+local utf8_char="[\1-\127\192-\223][\128-\191]*"
+
+local cyr_chars={'а','б','в','г','д','е','ё','ж','з','и','й','к','л','м','н','о','п','р','с','т','у','ф','х','ц','ч','ш','щ','ъ','ы','ь','э','ю','я'}
+
+-- символы, которые возможно вводить для поиска
+local chars={}
+for i=string.byte('a'),string.byte('z') do
+ table.insert(chars,i)
+end
+for i=string.byte('A'),string.byte('Z') do
+ table.insert(chars,i)
+end
+for i=string.byte('0'),string.byte('9') do
+ table.insert(chars,i)
+end
+for _,v in ipairs({',','^','$','(',')','%','.','[',']','*','+','-','?','`',"'",";"}) do
+ table.insert(chars,string.byte(v))
+end
+
+local keybinder = {
+ remove = function(action)
+ for i,_ in ipairs(keybinds[action]) do
+ mp.remove_key_binding(action..tostring(i))
+ end
+ end,
+ add = function(action, func, repeatable)
+ for i,key in ipairs(keybinds[action]) do
+ assert(type(func)=="function", "not a function")
+ if repeatable then
+ mp.add_forced_key_binding(key, action..tostring(i), func, "repeatable")
+ else
+ mp.add_forced_key_binding(key, action..tostring(i), func)
+ end
+ end
+ end
+}
+
+local fader = {
+ saved_brtns,
+ on = function(self)
+ if fade and not self.saved_brtns then
+ self.saved_brtns = mp.get_property("brightness")
+ mp.set_property("brightness", plsbrightness)
+ end
+ end,
+ off = function(self)
+ if fade and self.saved_brtns then
+ mp.set_property("brightness", self.saved_brtns)
+ self.saved_brtns=nil
+ end
+ end
+}
+
+local playlister = {
+-- pls — список элементов плейлиста
+ pls,
+-- plsfiltered — список индексов выбранных фильтром элементов плейлиста
+ plsfiltered,
+ plspos,
+ wndstart,
+ wndend,
+ cursor,
+
+ init = function(self)
+ if not self.pls then
+ self.pls = mp.get_property_native("playlist")
+ end
+ mp.commandv("stop")
+ --need to mark first entry non-current (mpv bug?)
+ if self.pls[1] then
+ self.pls[1].current = false
+ end
+ if favorites and #favorites>0 then
+ self:sortfavs()
+ end
+ pattern = ""
+ self.plsfiltered = tablekeys(self.pls)
+ end,
+
+ show = function(self)
+ local i
+ local newpos
+ local msg
+ --media-title
+ --playlist t[2].title
+
+ if not self.plsfiltered then
+ return
+ end
+ if not self.plspos then
+ self.plspos=mp.get_property_native("playlist-pos-1")
+ --plscount=mp.get_property_native("playlist-count")
+ end
+ if not self.wndstart or not self.cursor then
+ self.wndstart=1
+ self.cursor=0
+ end
+
+ msg=""
+ i = self.wndstart
+ local prefix
+ while self.plsfiltered[i] and i<=self.wndstart+window-1 do
+ if self.pls[self.plsfiltered[i]].current then
+ prefix="*"
+ elseif i==self.wndstart+self.cursor then
+ prefix=">"
+ else
+ prefix=" "
+ end
+ msg = msg..prefix..(self.pls[self.plsfiltered[i]].title or "").."\n"
+ i=i+1
+ end
+ if self.wndstart>1 then
+ msg = "...\n"..msg
+ else
+ msg = " \n"..msg
+ end
+ if self.wndstart+window-1<#self.plsfiltered then
+ msg = msg.."..."
+ end
+ msg="/"..pattern.."\n"..msg
+ mp.osd_message(msg, osd_time)
+ end,
+
+ sortfavs = function(self)
+ --favorites bubbles to the top
+ local favs={}
+ local nonfavs={}
+ for _,v in ipairs(self.pls) do
+ if in_array(favorites,v.title) then
+ favs[#favs+1] = v
+ else
+ nonfavs[#nonfavs+1] = v
+ end
+ end
+ for i=1,#nonfavs do
+ favs[#favs+1] = nonfavs[i]
+ end
+ self.pls = favs
+ end,
+
+ filter = function(self)
+ self.plsfiltered={}
+ for i,v in ipairs(self.pls) do
+ if string.match(mylower(v.title),'.*'..prepat(pattern)..'.*') then
+ table.insert(self.plsfiltered,i)
+ end
+ end
+ self.wndstart=1
+ self.cursor=0
+ end,
+
+ down = function(self)
+ if self.cursor >= #self.plsfiltered-1 then return end
+ if self.cursor0 then
+ self.cursor=self.cursor-1
+ self.show(self)
+ else
+ if self.wndstart>1 then
+ self.wndstart=self.wndstart-1
+ self.show(self)
+ end
+ end
+ end,
+
+ play = function(self)
+ mp.commandv("loadfile",self.pls[self.plsfiltered[self.wndstart+self.cursor]].filename)
+ if self.plspos then
+ self.pls[self.plspos].current=false
+ end
+ self.plspos=self.plsfiltered[self.wndstart+self.cursor]
+ self.pls[self.plspos].current=true
+ end
+}
+
+function add_bindings()
+ keybinder.add("plsup", up, true)
+ keybinder.add("plsdown", down, true)
+ for i,v in ipairs(chars) do
+ c=string.char(v)
+ mp.add_forced_key_binding(c, 'search'..v, typing(c),"repeatable")
+ end
+ mp.add_forced_key_binding('SPACE', 'search32', typing(' '),"repeatable")
+
+--[[ mp.add_key_binding('а', 'search1000', typing('а'),"repeatable")
+ mp.add_key_binding('с', 'search1001', typing('с'),"repeatable")]]
+
+ mp.add_forced_key_binding('BS', 'searchbs', backspace,"repeatable")
+ keybinder.add("plsenter", play)
+ for i,v in ipairs(cyr_chars) do
+ mp.add_forced_key_binding(v, 'search'..i+1000, typing(v),"repeatable")
+ end
+end
+
+function remove_bindings()
+ keybinder.remove('plsup')
+ keybinder.remove('plsdown')
+ keybinder.remove('plsenter')
+ for i,v in ipairs(chars) do
+ c=string.char(v)
+ mp.remove_key_binding('search'..v)
+ end
+ mp.remove_key_binding('search32')
+ mp.remove_key_binding('searchbs')
+ for i,v in ipairs(cyr_chars) do
+ mp.remove_key_binding('search'..i+1000)
+ end
+end
+
+function activate()
+ if is_active then
+ shutdown()
+ return
+ else
+ is_active=true
+ fader:on()
+ playlister:show()
+ add_bindings()
+ if not timer then
+ timer=mp.add_periodic_timer(osd_time, shutdown)
+ timer.oneshot=true
+ else
+ resumetimer()
+ end
+ end
+end
+
+function tablekeys(t)
+ local result={}
+ for i,v in ipairs(t) do
+ table.insert(result,i)
+ end
+ return result
+end
+
+function in_array(array, value)
+ for _,v in ipairs(array) do
+ if v==value then
+ return true
+ end
+ end
+ return false
+end
+
+function mylower(s)
+ local res,n = string.gsub(s,utf8_char,function (c)
+ return utf8_uc_lc[c]
+ end)
+ return res
+end
+
+function myupper(s)
+ local res,n = string.gsub(s,utf8_char,function (c)
+ return utf8_lc_uc[c]
+ end)
+ return res
+end
+
+function prepat(s)
+--prepare nocase and magic chars
+ s = string.gsub(s, "[%^%$%(%)%%%.%[%]%*%+%-%?]",function (c)
+ return '%'..c
+ end)
+--[[ s = string.gsub(s, utf8_char, function (c)
+ return string.format("[%s%s]", utf8_uc_lc[c] or c, utf8_lc_uc[c] or c)
+ end)]]
+ return s
+end
+
+function resumetimer()
+ timer:kill()
+ timer:resume()
+end
+
+function typing(char)
+ return function()
+ local c=string.lower(char)
+ pattern = pattern..c
+ playlister:filter()
+ playlister:show()
+ resumetimer()
+ end
+end
+
+function backspace()
+ if string.len(pattern)>0 then
+-- pattern = string.sub(pattern,1,-2)
+-- for unicode
+ pattern = string.match(pattern,"(.*)"..utf8_char.."$")
+ playlister:filter()
+ playlister:show()
+ resumetimer()
+ end
+end
+
+function play()
+-- mp.commandv("playlist-move", wndstart+cursor, 1)
+-- mp.commandv("playlist-clear")
+-- mp.commandv("playlist-next")
+ fader:off()
+ playlister:play()
+ playlister:show()
+ resumetimer()
+end
+
+function shutdown()
+ fader:off()
+ remove_bindings()
+ is_active=false
+ mp.osd_message("", 1)
+end
+
+function down()
+ fader:on()
+ playlister:down()
+ resumetimer()
+end
+
+function up()
+ fader:on()
+ playlister:up()
+ resumetimer()
+end
+
+function on_start_file()
+ if is_playlist_loaded then
+ playlister:init()
+ mp.unregister_event(on_start_file)
+ activate()
+ else
+ is_playlist_loaded = true
+ end
+end
+
+if mp.get_opt("iptv") then
+ mp.set_property_bool("idle", true)
+ mp.set_property_bool("force-window", true)
+ mp.register_event("start-file", on_start_file)
+ keybinder.add("activate", activate)
+end
+
diff --git a/.config/newsboat/config b/.config/newsboat/config
new file mode 100644
index 0000000..5b0fed3
--- /dev/null
+++ b/.config/newsboat/config
@@ -0,0 +1,26 @@
+auto-reload yes
+refresh-on-startup yes
+reload-time 3600
+notify-program "/home/yorune/.local/bin/notify-program"
+notify-always yes
+
+unbind-key o
+bind-key o open-in-browser
+
+macro , open-in-browser
+macro p set browser "mpv --ytdl-format='bestvideo[ext=mp4][height<=?1080]+bestaudio[ext=m4a]' --no-resume-playback" ; open-in-browser ; set browser "/home/yorune/.local/bin/browser-x -new-tab %u"
+
+unbind-key n
+unbind-key p
+bind-key n next
+bind-key p prev
+
+color listnormal cyan default
+color listfocus black yellow standout bold
+color listnormal_unread blue default
+color listfocus_unread yellow default bold
+color info red black bold
+color article white default bold
+
+browser "/home/yorune/.local/bin/browser-x -new-tab %u"
+player mpv
diff --git a/.config/newsboat/urls b/.config/newsboat/urls
new file mode 100644
index 0000000..9c46c95
--- /dev/null
+++ b/.config/newsboat/urls
@@ -0,0 +1,70 @@
+=====================NEWS====================================
+https://wtf.roflcopter.fr/rss-bridge/?action=display&bridge=Facebook&context=User&u=infoKONINpl&media_type=all&limit=-1&format=Mrss "~infoKONIN - Facebook"
+shttps://wtf.roflcopter.fr/rss-bridge/?action=display&bridge=Facebook&context=User&u=gminakleczew&media_type=all&limit=-1&format=Mrss "~Gmina Kleczew - Facebook"
+https://wtf.roflcopter.fr/rss-bridge/?action=display&bridge=Facebook&context=User&u=portalLM&media_type=all&limit=-1&format=Mrss "~Portal lm.pl - Facebook"
+https://nitter.net/MZ_GOV_PL/rss "~MZ_GOV_PL - Twitter"
+http://www.epoznan.pl/rss.php "~Epoznan - Website"
+http://rss.slashdot.org/Slashdot/slashdotMain "SlashDot"
+https://sekurak.pl/feed/ "~Sekurak"
+https://feeds.feedburner.com/niebezpiecznik/ "~Niebezpiecznik"
+https://www.cybsecurity.org/feed/podcast/ "~CybSecurity"
+============================================================
+https://bugs.gentoo.org/buglist.cgi?bug_status=UNCONFIRMED&chfieldfrom=24h&ctype=atom&title=NEW&list_id=4468306&order=changeddate%20DESC%2Cbug_status%2Cpriority%2Cassigned_to%2Cbug_id&query_format=advanced Gentoo
+https://bugs.gentoo.org/buglist.cgi?component=Package%20issues&product=GURU&query_format=advanced&resolution=---&title=Bug%20List&ctype=atom GURU-BUGS
+https://bugs.gentoo.org/buglist.cgi?component=Trusted%20Contributor%20ascension&product=GURU&query_format=advanced&resolution=---&title=Bug%20List&ctype=atom
+https://www.reddit.com/r/gentoo/new.rss "~Gentoo - Reddit"
+============================================================
+http://rss.uptimerobot.com/u674346-753d02ab763f3725330347d4419a7569 "~UpTimeRoboot"
+=====================ANIME===================================
+https://mirror.animetosho.org/feed/rss2?only_tor=1&q=%5BHorribleSubs%5D%20Sword%20Art%20Online%20720p&filter%5B0%5D%5Bt%5D=nyaa_class&filter%5B0%5D%5Bv%5D=trusted "~Sword Art Online"
+=====================YOUTUBE=================================
+https://www.youtube.com/feeds/videos.xml?channel_id=UCzuvRWjh7k1SZm1RvqvIx4w "~Krzysztof Gonciarz"
+https://www.youtube.com/feeds/videos.xml?channel_id=UC3QOjSZmJuYgb98BAzKfzig "~Zapytaj Beczkę"
+https://www.youtube.com/feeds/videos.xml?channel_id=UC2eYFnH61tmytImy1mTYvhA "~Luke Smith"
+https://www.youtube.com/feeds/videos.xml?channel_id=UC2qQkGwReBfucb6yWr7idRA "~Asia Ladowska"
+https://www.youtube.com/feeds/videos.xml?channel_id=UC5ZSM0kOGhtLdtZiqM2noWw "~StudyWithInSpo"
+https://www.youtube.com/feeds/videos.xml?channel_id=UC84AaEtr7hlaYYwXDSKAbNg "~Ignacy z Japonii"
+https://www.youtube.com/feeds/videos.xml?channel_id=UC8JbbaZ_jgdsoUqrZ2bXtQQ "~Lekko Stronniczy"
+https://www.youtube.com/feeds/videos.xml?channel_id=UC8q23MpiyWjv9vd4r85oj1A "~Daniel Rakowiecki"
+https://www.youtube.com/feeds/videos.xml?channel_id=UC9Vi25H6lQvPqcepJCJ3sEg "~Emma"
+https://www.youtube.com/feeds/videos.xml?channel_id=UCAD-xOOaUI6N7Uq9laOVbcw "~Rene Rene"
+https://www.youtube.com/feeds/videos.xml?channel_id=UCAEQl0BbYrPyTnsWVBJuIqQ
+https://www.youtube.com/feeds/videos.xml?channel_id=UCaG-Uk3PSpHexeUJaJC_TPw
+https://www.youtube.com/feeds/videos.xml?channel_id=UCAkBZtIl-a0fBLcY7JCPSQw
+https://www.youtube.com/feeds/videos.xml?channel_id=UCB-4hoTjsE3VIFI862C5SZA
+https://www.youtube.com/feeds/videos.xml?channel_id=UCE5Au4LfcBHxTQR_yLbncrQ
+https://www.youtube.com/feeds/videos.xml?channel_id=UCe6nK69Yc1zna7QSJEfA9pw
+https://www.youtube.com/feeds/videos.xml?channel_id=UCEbYhDd6c6vngsF5PQpFVWg
+https://www.youtube.com/feeds/videos.xml?channel_id=UCELsE3w85wy7hXFmU2eRn_w
+https://www.youtube.com/feeds/videos.xml?channel_id=UCfegHHvKywKjJ69Ymcb6j0w
+https://www.youtube.com/feeds/videos.xml?channel_id=UCFGud6qsbaAjuP6WaXAJj3Q
+https://www.youtube.com/feeds/videos.xml?channel_id=UCH2OHdQF5c2r2M_l_vPiXRA
+https://www.youtube.com/feeds/videos.xml?channel_id=UCIDf8pzX2oZmjXtqMwHn0xg
+https://www.youtube.com/feeds/videos.xml?channel_id=UCIj6yjWVKPKO5zNLBjQ8Beg
+https://www.youtube.com/feeds/videos.xml?channel_id=UCJ24N4O0bP7LGLBDvye7oCA
+https://www.youtube.com/feeds/videos.xml?channel_id=UCJkmbV_cndEe6Af8b5qVyoQ
+https://www.youtube.com/feeds/videos.xml?channel_id=UCJLLl6AraX1POemgLfhirwg
+https://www.youtube.com/feeds/videos.xml?channel_id=UCjS2aGCvsnhExcWRAI8T4Pw
+https://www.youtube.com/feeds/videos.xml?channel_id=UCMsi03EhTUsUs2OtPus6XDQ
+https://www.youtube.com/feeds/videos.xml?channel_id=UCMTkC2dA25yFsmtaevpYWfg
+https://www.youtube.com/feeds/videos.xml?channel_id=UCnOo8dtBgwJbdwl4HxqPHwg
+https://www.youtube.com/feeds/videos.xml?channel_id=UCO8DQrSp5yEP937qNqTooOw
+https://www.youtube.com/feeds/videos.xml?channel_id=UCoJjEsNkU-Md3xB6BursT0Q
+https://www.youtube.com/feeds/videos.xml?channel_id=UCoXxgqIOTa8qCM7Hd7RiURw
+https://www.youtube.com/feeds/videos.xml?channel_id=UCPc_saLjikyw-1sscFsHIsA
+https://www.youtube.com/feeds/videos.xml?channel_id=UCPKgIhTC3BdkAwMw6s-GEug
+https://www.youtube.com/feeds/videos.xml?channel_id=UCRxAgfYexGLlu1WHGIMUDqw
+https://www.youtube.com/feeds/videos.xml?channel_id=UCs02jgjMVeirmDHaxAXApkg
+https://www.youtube.com/feeds/videos.xml?channel_id=UCsnGwSIHyoYN0kiINAGUKxg
+https://www.youtube.com/feeds/videos.xml?channel_id=UCv9wKjBogC5AVG54s_Imn0A
+https://www.youtube.com/feeds/videos.xml?channel_id=UCvbEYH_h8k-hHq2oS-7zIdQ
+https://www.youtube.com/feeds/videos.xml?channel_id=UCVbn813ctsoChuTT4LuLqrA
+https://www.youtube.com/feeds/videos.xml?channel_id=UCVVt5tbZEzfQSIsHUh3b_vg
+https://www.youtube.com/feeds/videos.xml?channel_id=UCw3T7SvPYDHCkdMhC0NcKsw
+https://www.youtube.com/feeds/videos.xml?channel_id=UCWcz2SQMCcijkVta0hSERIA
+https://www.youtube.com/feeds/videos.xml?channel_id=UCWFKCr40YwOZQx8FHU_ZqqQ
+https://www.youtube.com/feeds/videos.xml?channel_id=UCXRJfVWEjv9qxooSbOkFmkw
+https://www.youtube.com/feeds/videos.xml?channel_id=UCXuqSBlHAE6Xw-yeJA0Tunw
+https://www.youtube.com/feeds/videos.xml?channel_id=UCZgyxIrQnTDLwwEr9VSkoPA
+https://www.youtube.com/feeds/videos.xml?channel_id=UCZKqIdmPtIU6DOTmWxzudKw
+=============================================================
diff --git a/.config/ranger/rc.conf b/.config/ranger/rc.conf
new file mode 100644
index 0000000..ca4fc2c
--- /dev/null
+++ b/.config/ranger/rc.conf
@@ -0,0 +1,567 @@
+#set viewmode miller
+set viewmode multipane
+set column_ratios 1,3,4
+set hidden_filter ^\.|\.(?:pyc|pyo|bak|swp)$|^lost\+found$|^__(py)?cache__$
+set show_hidden false
+
+# Ask for a confirmation when running the "delete" command?
+# Valid values are "always", "never", "multiple" (default)
+# With "multiple", ranger will ask only if you delete multiple files at once.
+#set confirm_on_delete multiple
+set confirm_on_delete never
+set use_preview_script true
+set automatically_count_files true
+set open_all_images true
+set vcs_aware false
+set vcs_backend_git enabled
+set vcs_backend_hg disabled
+set vcs_backend_bzr disabled
+set vcs_backend_svn disabled
+set preview_images true
+set preview_images_method urxvt
+set w3m_delay 0.02
+set iterm2_font_width 8
+set iterm2_font_height 11
+set unicode_ellipsis false
+set bidi_support false
+set show_hidden_bookmarks true
+set colorscheme default
+set preview_files true
+set preview_directories true
+set collapse_preview true
+set save_console_history true
+set status_bar_on_top false
+set draw_progress_bar_in_status_bar true
+set draw_borders true
+set respect draw_borders
+set dirname_in_tabs false
+set mouse_enabled true
+set display_size_in_main_column true
+set display_size_in_status_bar true
+
+# Display the free disk space in the status bar?
+set display_free_space_in_status_bar true
+
+# Display files tags in all columns or only in main column?
+set display_tags_in_all_columns true
+
+# Set a title for the window?
+set update_title false
+
+# Set the title to "ranger" in the tmux program?
+set update_tmux_title true
+
+# Shorten the title if it gets long? The number defines how many
+# directories are displayed at once, 0 turns off this feature.
+set shorten_title 3
+
+# Show hostname in titlebar?
+set hostname_in_titlebar false
+
+# Abbreviate $HOME with ~ in the titlebar (first line) of ranger?
+set tilde_in_titlebar true
+
+# How many directory-changes or console-commands should be kept in history?
+set max_history_size 20
+set max_console_history_size 50
+
+# Try to keep so much space between the top/bottom border when scrolling:
+set scroll_offset 10
+
+# Flush the input after each key hit? (Noticeable when ranger lags)
+set flushinput true
+
+# Padding on the right when there's no preview?
+# This allows you to click into the space to run the file.
+set padding_right true
+
+# Save bookmarks (used with mX and `X) instantly?
+# This helps to synchronize bookmarks between multiple ranger
+# instances but leads to *slight* performance loss.
+# When false, bookmarks are saved when ranger is exited.
+set autosave_bookmarks true
+
+# Save the "`" bookmark to disk. This can be used to switch to the last
+# directory by typing "``".
+set save_backtick_bookmark true
+
+# You can display the "real" cumulative size of directories by using the
+# command :get_cumulative_size or typing "dc". The size is expensive to
+# calculate and will not be updated automatically. You can choose
+# to update it automatically though by turning on this option:
+set autoupdate_cumulative_size false
+
+# Turning this on makes sense for screen readers:
+set show_cursor false
+
+# One of: size, natural, basename, atime, ctime, mtime, type, random
+set sort natural
+
+# Additional sorting options
+set sort_reverse false
+set sort_case_insensitive true
+set sort_directories_first true
+set sort_unicode false
+
+# Enable this if key combinations with the Alt Key don't work for you.
+# (Especially on xterm)
+set xterm_alt_key false
+
+# Whether to include bookmarks in cd command
+set cd_bookmarks true
+
+# Changes case sensitivity for the cd command tab completion
+set cd_tab_case sensitive
+
+# Use fuzzy tab completion with the "cd" command. For example,
+# ":cd /u/lo/b" expands to ":cd /usr/local/bin".
+set cd_tab_fuzzy false
+set preview_max_size 0
+set hint_collapse_threshold 10
+set show_selection_in_titlebar true
+set idle_delay 2000
+set metadata_deep_search false
+set clear_filters_on_dir_change false
+set line_numbers false
+set relative_current_zero false
+set one_indexed false
+set save_tabs_on_exit false
+set wrap_scroll false
+set global_inode_type_filter
+set freeze_files false
+
+# ===================================================================
+# == My settings
+# ===================================================================
+
+setlocal path=~/Screenshots sort mtime
+setlocal path=~/Screenshots sort_reverse False
+
+
+# ===================================================================
+# == Local Options
+# ===================================================================
+# You can set local options that only affect a single directory.
+
+# Examples:
+# setlocal path=~/downloads sort mtime
+
+# ===================================================================
+# == Command Aliases in the Console
+# ===================================================================
+
+alias e edit
+alias q quit
+alias q! quit!
+alias qa quitall
+alias qa! quitall!
+alias qall quitall
+alias qall! quitall!
+alias setl setlocal
+
+alias filter scout -prts
+alias find scout -aets
+alias mark scout -mr
+alias unmark scout -Mr
+alias search scout -rs
+alias search_inc scout -rts
+alias travel scout -aefklst
+
+# ===================================================================
+# == Define keys for the browser
+# ===================================================================
+
+# Basic
+map bg shell set-wallpaper %f
+map bc shell wall -i %f
+map bh cd ~
+
+map Q quitall
+map q quit
+copymap q ZZ ZQ
+
+map R reload_cwd
+map F set freeze_files!
+map reset
+map redraw_window
+map abort
+map change_mode normal
+map ~ set viewmode!
+
+map i display_file
+map ? help
+map W display_log
+map w taskview_open
+map S shell $SHELL
+
+map : console
+map ; console
+map ! console shell%space
+map @ console -p6 shell %%s
+map # console shell -p%space
+map s console shell%space
+map r chain draw_possible_programs; console open_with%%space
+map f console find%space
+map cd console cd%space
+
+map chain console; eval fm.ui.console.history_move(-1)
+
+# Change the line mode
+map Mf linemode filename
+map Mi linemode fileinfo
+map Mm linemode mtime
+map Mp linemode permissions
+map Ms linemode sizemtime
+map Mt linemode metatitle
+
+# Tagging / Marking
+map t tag_toggle
+map ut tag_remove
+map mark_files toggle=True
+map v mark_files all=True toggle=True
+map uv mark_files all=True val=False
+map V toggle_visual_mode
+map uV toggle_visual_mode reverse=True
+
+# For the nostalgics: Midnight Commander bindings
+map help
+map rename_append
+map display_file
+map edit
+map copy
+map cut
+map console mkdir%space
+map console delete
+map exit
+
+# In case you work on a keyboard with dvorak layout
+map move up=1
+map move down=1
+map move left=1
+map move right=1
+map move to=0
+map move to=-1
+map move down=1 pages=True
+map move up=1 pages=True
+map move right=1
+#map console delete
+map console touch%space
+
+# VIM-like
+copymap k
+copymap j
+copymap h
+copymap l
+copymap gg
+copymap G
+copymap
+copymap
+
+map J move down=0.5 pages=True
+map K move up=0.5 pages=True
+copymap J
+copymap K
+
+# Jumping around
+map H history_go -1
+map L history_go 1
+map ] move_parent 1
+map [ move_parent -1
+map } traverse
+map { traverse_backwards
+map ) jump_non
+
+#DEFAULT MOVEMENT
+map ge cd /etc
+map gh cd ~
+map gu cd /usr
+#map gl cd -r .
+map gL cd -r %f
+#map gv cd /var
+map gM cd /mnt
+map gr cd /
+map gR eval fm.cd(ranger.RANGERDIR)
+map g? cd /usr/share/doc/ranger
+
+# External Programs
+map E edit
+map du shell -p du --max-depth=1 -h --apparent-size
+map dU shell -p du --max-depth=1 -h --apparent-size | sort -rh
+map yp yank path
+map yd yank dir
+map yn yank name
+map y. yank name_without_extension
+
+# Filesystem Operations
+map = chmod
+
+map cw console rename%space
+map a rename_append
+map A eval fm.open_console('rename ' + fm.thisfile.relative_path.replace("%", "%%"))
+map I eval fm.open_console('rename ' + fm.thisfile.relative_path.replace("%", "%%"), position=7)
+
+map pp paste
+map po paste overwrite=True
+map pP paste append=True
+map pO paste overwrite=True append=True
+map pl paste_symlink relative=False
+map pL paste_symlink relative=True
+map phl paste_hardlink
+map pht paste_hardlinked_subtree
+
+map dD console delete
+
+map dd cut
+map ud uncut
+map da cut mode=add
+map dr cut mode=remove
+map dt cut mode=toggle
+
+map yy copy
+map uy uncut
+map ya copy mode=add
+map yr copy mode=remove
+map yt copy mode=toggle
+
+# Temporary workarounds
+map dgg eval fm.cut(dirarg=dict(to=0), narg=quantifier)
+map dG eval fm.cut(dirarg=dict(to=-1), narg=quantifier)
+map dj eval fm.cut(dirarg=dict(down=1), narg=quantifier)
+map dk eval fm.cut(dirarg=dict(up=1), narg=quantifier)
+map ygg eval fm.copy(dirarg=dict(to=0), narg=quantifier)
+map yG eval fm.copy(dirarg=dict(to=-1), narg=quantifier)
+map yj eval fm.copy(dirarg=dict(down=1), narg=quantifier)
+map yk eval fm.copy(dirarg=dict(up=1), narg=quantifier)
+
+# Searching
+map / console search%space
+map n search_next
+map N search_next forward=False
+map ct search_next order=tag
+map cs search_next order=size
+map ci search_next order=mimetype
+map cc search_next order=ctime
+map cm search_next order=mtime
+map ca search_next order=atime
+
+# Tabs
+map tab_new
+map tab_close
+map tab_move 1
+map tab_move -1
+map tab_move 1
+map tab_move -1
+map uq tab_restore
+map tab_open 1
+map tab_open 2
+map tab_open 3
+map tab_open 4
+map tab_open 5
+map tab_open 6
+map tab_open 7
+map tab_open 8
+map tab_open 9
+map tab_shift 1
+map tab_shift -1
+
+# Sorting
+map or set sort_reverse!
+map oz set sort=random
+map os chain set sort=size; set sort_reverse=False
+map ob chain set sort=basename; set sort_reverse=False
+map on chain set sort=natural; set sort_reverse=False
+map om chain set sort=mtime; set sort_reverse=False
+map oc chain set sort=ctime; set sort_reverse=False
+map oa chain set sort=atime; set sort_reverse=False
+map ot chain set sort=type; set sort_reverse=False
+map oe chain set sort=extension; set sort_reverse=False
+
+map oS chain set sort=size; set sort_reverse=True
+map oB chain set sort=basename; set sort_reverse=True
+map oN chain set sort=natural; set sort_reverse=True
+map oM chain set sort=mtime; set sort_reverse=True
+map oC chain set sort=ctime; set sort_reverse=True
+map oA chain set sort=atime; set sort_reverse=True
+map oT chain set sort=type; set sort_reverse=True
+map oE chain set sort=extension; set sort_reverse=True
+
+map dc get_cumulative_size
+
+# Settings
+map zc set collapse_preview!
+map zd set sort_directories_first!
+map zh set show_hidden!
+map set show_hidden!
+copymap
+copymap
+map zI set flushinput!
+map zi set preview_images!
+map zm set mouse_enabled!
+map zp set preview_files!
+map zP set preview_directories!
+map zs set sort_case_insensitive!
+map zu set autoupdate_cumulative_size!
+map zv set use_preview_script!
+map zf console filter%space
+copymap zf zz
+
+# Filter stack
+map .n console filter_stack add name%space
+map .m console filter_stack add mime%space
+map .d filter_stack add type d
+map .f filter_stack add type f
+map .l filter_stack add type l
+map .| filter_stack add or
+map .& filter_stack add and
+map .! filter_stack add not
+map .r console filter_stack rotate
+map .c filter_stack clear
+map .* filter_stack decompose
+map .p filter_stack pop
+map .. filter_stack show
+
+# Bookmarks
+map ` enter_bookmark %any
+map ' enter_bookmark %any
+map m set_bookmark %any
+map um unset_bookmark %any
+
+map m draw_bookmarks
+copymap m um ` '
+
+# Generate all the chmod bindings with some python help:
+eval for arg in "rwxXst": cmd("map +u{0} shell -f chmod u+{0} %s".format(arg))
+eval for arg in "rwxXst": cmd("map +g{0} shell -f chmod g+{0} %s".format(arg))
+eval for arg in "rwxXst": cmd("map +o{0} shell -f chmod o+{0} %s".format(arg))
+eval for arg in "rwxXst": cmd("map +a{0} shell -f chmod a+{0} %s".format(arg))
+eval for arg in "rwxXst": cmd("map +{0} shell -f chmod u+{0} %s".format(arg))
+
+eval for arg in "rwxXst": cmd("map -u{0} shell -f chmod u-{0} %s".format(arg))
+eval for arg in "rwxXst": cmd("map -g{0} shell -f chmod g-{0} %s".format(arg))
+eval for arg in "rwxXst": cmd("map -o{0} shell -f chmod o-{0} %s".format(arg))
+eval for arg in "rwxXst": cmd("map -a{0} shell -f chmod a-{0} %s".format(arg))
+eval for arg in "rwxXst": cmd("map -{0} shell -f chmod u-{0} %s".format(arg))
+
+# ===================================================================
+# == Define keys for the console
+# ===================================================================
+# Note: Unmapped keys are passed directly to the console.
+
+# Basic
+cmap eval fm.ui.console.tab()
+cmap eval fm.ui.console.tab(-1)
+cmap eval fm.ui.console.close()
+cmap eval fm.ui.console.execute()
+cmap redraw_window
+
+copycmap
+copycmap
+
+# Move around
+cmap eval fm.ui.console.history_move(-1)
+cmap eval fm.ui.console.history_move(1)
+cmap eval fm.ui.console.move(left=1)
+cmap eval fm.ui.console.move(right=1)
+cmap eval fm.ui.console.move(right=0, absolute=True)
+cmap eval fm.ui.console.move(right=-1, absolute=True)
+cmap eval fm.ui.console.move_word(left=1)
+cmap eval fm.ui.console.move_word(right=1)
+
+copycmap
+copycmap
+
+# Line Editing
+cmap eval fm.ui.console.delete(-1)
+cmap eval fm.ui.console.delete(0)
+cmap eval fm.ui.console.delete_word()
+cmap eval fm.ui.console.delete_word(backward=False)
+cmap eval fm.ui.console.delete_rest(1)
+cmap eval fm.ui.console.delete_rest(-1)
+cmap eval fm.ui.console.paste()
+
+# And of course the emacs way
+copycmap
+copycmap
+copycmap
+copycmap
+copycmap
+copycmap
+copycmap
+copycmap
+copycmap
+
+# Note: There are multiple ways to express backspaces. (code 263)
+# and (code 127). To be sure, use both.
+copycmap
+
+# This special expression allows typing in numerals:
+cmap false
+
+# ===================================================================
+# == Pager Keybindings
+# ===================================================================
+
+# Movement
+pmap pager_move down=1
+pmap pager_move up=1
+pmap pager_move left=4
+pmap pager_move right=4
+pmap pager_move to=0
+pmap pager_move to=-1
+pmap pager_move down=1.0 pages=True
+pmap pager_move up=1.0 pages=True
+pmap pager_move down=0.5 pages=True
+pmap pager_move up=0.5 pages=True
+
+copypmap k
+copypmap j
+copypmap h
+copypmap l
+copypmap g
+copypmap G
+copypmap d
+copypmap u
+copypmap n f
+copypmap p b
+
+# Basic
+pmap redraw_window
+pmap pager_close
+copypmap q Q i
+pmap E edit_file
+
+# ===================================================================
+# == Taskview Keybindings
+# ===================================================================
+
+# Movement
+tmap taskview_move up=1
+tmap taskview_move down=1
+tmap taskview_move to=0
+tmap taskview_move to=-1
+tmap taskview_move down=1.0 pages=True
+tmap taskview_move up=1.0 pages=True
+tmap taskview_move down=0.5 pages=True
+tmap taskview_move up=0.5 pages=True
+
+copytmap k
+copytmap j
+copytmap g
+copytmap G
+copytmap u
+copytmap n f
+copytmap p b
+
+# Changing priority and deleting tasks
+tmap J eval -q fm.ui.taskview.task_move(-1)
+tmap K eval -q fm.ui.taskview.task_move(0)
+tmap dd eval -q fm.ui.taskview.task_remove()
+tmap eval -q fm.ui.taskview.task_move(-1)
+tmap eval -q fm.ui.taskview.task_move(0)
+tmap eval -q fm.ui.taskview.task_remove()
+
+# Basic
+tmap redraw_window
+tmap taskview_close
+copytmap q Q w
+
diff --git a/.config/ranger/rifle.conf b/.config/ranger/rifle.conf
new file mode 100644
index 0000000..c524a9c
--- /dev/null
+++ b/.config/ranger/rifle.conf
@@ -0,0 +1,210 @@
+ext jpg = feh --scale-down "$@"
+#ext doc = openoffice4 "$@"
+#ext odt = openoffice4 "$@"
+
+#-------------------------------------------
+# Websites
+#-------------------------------------------
+# Rarely installed browsers get higher priority; It is assumed that if you
+# install a rare browser, you probably use it. Firefox/konqueror/w3m on the
+# other hand are often only installed as fallback browsers.
+ext x?html?, has surf, X, flag f = surf -- file://"$1"
+ext x?html?, has vimprobable, X, flag f = vimprobable -- "$@"
+ext x?html?, has vimprobable2, X, flag f = vimprobable2 -- "$@"
+ext x?html?, has qutebrowser, X, flag f = qutebrowser -- "$@"
+ext x?html?, has dwb, X, flag f = dwb -- "$@"
+ext x?html?, has jumanji, X, flag f = jumanji -- "$@"
+ext x?html?, has luakit, X, flag f = luakit -- "$@"
+ext x?html?, has uzbl, X, flag f = uzbl -- "$@"
+ext x?html?, has uzbl-tabbed, X, flag f = uzbl-tabbed -- "$@"
+ext x?html?, has uzbl-browser, X, flag f = uzbl-browser -- "$@"
+ext x?html?, has uzbl-core, X, flag f = uzbl-core -- "$@"
+ext x?html?, has midori, X, flag f = midori -- "$@"
+ext x?html?, has chromium-browser, X, flag f = chromium-browser -- "$@"
+ext x?html?, has chromium, X, flag f = chromium -- "$@"
+ext x?html?, has google-chrome, X, flag f = google-chrome -- "$@"
+ext x?html?, has opera, X, flag f = opera -- "$@"
+ext x?html?, has firefox, X, flag f = firefox -- "$@"
+ext x?html?, has seamonkey, X, flag f = seamonkey -- "$@"
+ext x?html?, has iceweasel, X, flag f = iceweasel -- "$@"
+ext x?html?, has epiphany, X, flag f = epiphany -- "$@"
+ext x?html?, has konqueror, X, flag f = konqueror -- "$@"
+ext x?html?, has elinks, terminal = elinks "$@"
+ext x?html?, has links2, terminal = links2 "$@"
+ext x?html?, has links, terminal = links "$@"
+ext x?html?, has lynx, terminal = lynx -- "$@"
+ext x?html?, has w3m, terminal = w3m "$@"
+
+#-------------------------------------------
+# Misc
+#-------------------------------------------
+# Define the "editor" for text files as first action
+mime ^text, label editor = ${VISUAL:-$EDITOR} -- "$@"
+mime ^text, label pager = "$PAGER" -- "$@"
+!mime ^text, label editor, ext xml|json|csv|tex|py|pl|rb|js|sh|php = ${VISUAL:-$EDITOR} -- "$@"
+!mime ^text, label pager, ext xml|json|csv|tex|py|pl|rb|js|sh|php = "$PAGER" -- "$@"
+
+ext 1 = man "$1"
+ext s[wmf]c, has zsnes, X = zsnes "$1"
+ext s[wmf]c, has snes9x-gtk,X = snes9x-gtk "$1"
+ext nes, has fceux, X = fceux "$1"
+ext exe = wine "$1"
+name ^[mM]akefile$ = make
+
+#--------------------------------------------
+# Code
+#-------------------------------------------
+ext py = python -- "$1"
+ext pl = perl -- "$1"
+ext rb = ruby -- "$1"
+ext js = node -- "$1"
+ext sh = sh -- "$1"
+ext php = php -- "$1"
+
+#--------------------------------------------
+# Audio without X
+#-------------------------------------------
+mime ^audio|ogg$, terminal, has mpv = mpv -- "$@"
+mime ^audio|ogg$, terminal, has mplayer2 = mplayer2 -- "$@"
+mime ^audio|ogg$, terminal, has mplayer = mplayer -- "$@"
+ext midi?, terminal, has wildmidi = wildmidi -- "$@"
+
+#--------------------------------------------
+# Video/Audio with a GUI
+#-------------------------------------------
+mime ^video|audio, has gmplayer, X, flag f = gmplayer -- "$@"
+mime ^video|audio, has smplayer, X, flag f = smplayer "$@"
+mime ^video, has mpv, X, flag f = mpv -- "$@"
+mime ^video, has mpv, X, flag f = mpv --fs -- "$@"
+mime ^video, has mplayer2, X, flag f = mplayer2 -- "$@"
+mime ^video, has mplayer2, X, flag f = mplayer2 -fs -- "$@"
+mime ^video, has mplayer, X, flag f = mplayer -- "$@"
+mime ^video, has mplayer, X, flag f = mplayer -fs -- "$@"
+mime ^video|audio, has vlc, X, flag f = vlc -- "$@"
+mime ^video|audio, has totem, X, flag f = totem -- "$@"
+mime ^video|audio, has totem, X, flag f = totem --fullscreen -- "$@"
+
+#--------------------------------------------
+# Video without X:
+#-------------------------------------------
+mime ^video, terminal, !X, has mpv = mpv -- "$@"
+mime ^video, terminal, !X, has mplayer2 = mplayer2 -- "$@"
+mime ^video, terminal, !X, has mplayer = mplayer -- "$@"
+
+#-------------------------------------------
+# Documents
+#-------------------------------------------
+ext pdf, has llpp, X, flag f = llpp "$@"
+ext pdf, has zathura, X, flag f = zathura -- "$@"
+ext pdf, has mupdf, X, flag f = mupdf "$@"
+ext pdf, has mupdf-x11,X, flag f = mupdf-x11 "$@"
+ext pdf, has apvlv, X, flag f = apvlv -- "$@"
+ext pdf, has xpdf, X, flag f = xpdf -- "$@"
+ext pdf, has evince, X, flag f = evince -- "$@"
+ext pdf, has atril, X, flag f = atril -- "$@"
+ext pdf, has okular, X, flag f = okular -- "$@"
+ext pdf, has epdfview, X, flag f = epdfview -- "$@"
+ext pdf, has qpdfview, X, flag f = qpdfview "$@"
+ext pdf, has open, X, flag f = open "$@"
+
+ext docx?, has catdoc, terminal = catdoc -- "$@" | "$PAGER"
+
+ext sxc|xlsx?|xlt|xlw|gnm|gnumeric, has gnumeric, X, flag f = gnumeric -- "$@"
+ext sxc|xlsx?|xlt|xlw|gnm|gnumeric, has kspread, X, flag f = kspread -- "$@"
+ext pptx?|od[dfgpst]|docx?|sxc|xlsx?|xlt|xlw|gnm|gnumeric, has libreoffice, X, flag f = libreoffice "$@"
+ext pptx?|od[dfgpst]|docx?|sxc|xlsx?|xlt|xlw|gnm|gnumeric, has soffice, X, flag f = soffice "$@"
+ext pptx?|od[dfgpst]|docx?|sxc|xlsx?|xlt|xlw|gnm|gnumeric, has ooffice, X, flag f = ooffice "$@"
+
+ext djvu, has zathura,X, flag f = zathura -- "$@"
+ext djvu, has evince, X, flag f = evince -- "$@"
+ext djvu, has atril, X, flag f = atril -- "$@"
+ext djvu, has djview, X, flag f = djview -- "$@"
+
+ext epub, has ebook-viewer, X, flag f = ebook-viewer -- "$@"
+ext mobi, has ebook-viewer, X, flag f = ebook-viewer -- "$@"
+
+#-------------------------------------------
+# Image Viewing:
+#-------------------------------------------
+mime ^image/svg, has inkscape, X, flag f = inkscape -- "$@"
+mime ^image/svg, has display, X, flag f = display -- "$@"
+
+mime ^image, has pqiv, X, flag f = pqiv -- "$@"
+mime ^image, has sxiv, X, flag f = sxiv -- "$@"
+mime ^image, has feh, X, flag f = feh -- "$@"
+mime ^image, has mirage, X, flag f = mirage -- "$@"
+mime ^image, has ristretto, X, flag f = ristretto "$@"
+mime ^image, has eog, X, flag f = eog -- "$@"
+mime ^image, has eom, X, flag f = eom -- "$@"
+mime ^image, has nomacs, X, flag f = nomacs -- "$@"
+mime ^image, has geeqie, X, flag f = geeqie -- "$@"
+mime ^image, has gwenview, X, flag f = gwenview -- "$@"
+mime ^image, has gimp, X, flag f = gimp -- "$@"
+ext xcf, X, flag f = gimp -- "$@"
+
+#-------------------------------------------
+# Archives
+#-------------------------------------------
+
+# avoid password prompt by providing empty password
+ext 7z, has 7z = 7z -p l "$@" | "$PAGER"
+# This requires atool
+ext ace|ar|arc|bz2?|cab|cpio|cpt|deb|dgc|dmg|gz, has atool = atool --list --each -- "$@" | "$PAGER"
+ext iso|jar|msi|pkg|rar|shar|tar|tgz|xar|xpi|xz|zip, has atool = atool --list --each -- "$@" | "$PAGER"
+ext 7z|ace|ar|arc|bz2?|cab|cpio|cpt|deb|dgc|dmg|gz, has atool = atool --extract --each -- "$@"
+ext iso|jar|msi|pkg|rar|shar|tar|tgz|xar|xpi|xz|zip, has atool = atool --extract --each -- "$@"
+
+# Listing and extracting archives without atool:
+ext tar|gz|bz2|xz, has tar = tar vvtf "$1" | "$PAGER"
+ext tar|gz|bz2|xz, has tar = for file in "$@"; do tar vvxf "$file"; done
+ext bz2, has bzip2 = for file in "$@"; do bzip2 -dk "$file"; done
+ext zip, has unzip = unzip -l "$1" | less
+ext zip, has unzip = for file in "$@"; do unzip -d "${file%.*}" "$file"; done
+ext ace, has unace = unace l "$1" | less
+ext ace, has unace = for file in "$@"; do unace e "$file"; done
+ext rar, has unrar = unrar l "$1" | less
+ext rar, has unrar = for file in "$@"; do unrar x "$file"; done
+
+#-------------------------------------------
+# Flag t fallback terminals
+#-------------------------------------------
+# Rarely installed terminal emulators get higher priority; It is assumed that
+# if you install a rare terminal emulator, you probably use it.
+# gnome-terminal/konsole/xterm on the other hand are often installed as part of
+# a desktop environment or as fallback terminal emulators.
+mime ^ranger/x-terminal-emulator, has terminology = terminology -e "$@"
+mime ^ranger/x-terminal-emulator, has kitty = kitty -- "$@"
+mime ^ranger/x-terminal-emulator, has alacritty = alacritty -e "$@"
+mime ^ranger/x-terminal-emulator, has sakura = sakura -e "$@"
+mime ^ranger/x-terminal-emulator, has lilyterm = lilyterm -e "$@"
+#mime ^ranger/x-terminal-emulator, has cool-retro-term = cool-retro-term -e "$@"
+mime ^ranger/x-terminal-emulator, has termite = termite -x '"$@"'
+#mime ^ranger/x-terminal-emulator, has yakuake = yakuake -e "$@"
+mime ^ranger/x-terminal-emulator, has guake = guake -ne "$@"
+mime ^ranger/x-terminal-emulator, has tilda = tilda -c "$@"
+mime ^ranger/x-terminal-emulator, has st = st -e "$@"
+mime ^ranger/x-terminal-emulator, has terminator = terminator -x "$@"
+mime ^ranger/x-terminal-emulator, has urxvt = urxvt -e "$@"
+mime ^ranger/x-terminal-emulator, has pantheon-terminal = pantheon-terminal -e "$@"
+mime ^ranger/x-terminal-emulator, has lxterminal = lxterminal -e "$@"
+mime ^ranger/x-terminal-emulator, has mate-terminal = mate-terminal -x "$@"
+mime ^ranger/x-terminal-emulator, has xfce4-terminal = xfce4-terminal -x "$@"
+mime ^ranger/x-terminal-emulator, has konsole = konsole -e "$@"
+mime ^ranger/x-terminal-emulator, has gnome-terminal = gnome-terminal -- "$@"
+mime ^ranger/x-terminal-emulator, has xterm = xterm -e "$@"
+
+#-------------------------------------------
+# Misc
+#-------------------------------------------
+label wallpaper, number 11, mime ^image, has feh, X = feh --bg-scale "$1"
+label wallpaper, number 12, mime ^image, has feh, X = feh --bg-tile "$1"
+label wallpaper, number 13, mime ^image, has feh, X = feh --bg-center "$1"
+label wallpaper, number 14, mime ^image, has feh, X = feh --bg-fill "$1"
+
+# Define the editor for non-text files + pager as last action
+ !mime ^text, !ext xml|json|csv|tex|py|pl|rb|js|sh|php = ask
+label editor, !mime ^text, !ext xml|json|csv|tex|py|pl|rb|js|sh|php = ${VISUAL:-$EDITOR} -- "$@"
+label pager, !mime ^text, !ext xml|json|csv|tex|py|pl|rb|js|sh|php = "$PAGER" -- "$@"
+
+# The very last action, so that it's never triggered accidentally, is to execute a program:
+mime application/x-executable = "$1"
diff --git a/.emacs.d/emacs-custom.el b/.emacs.d/emacs-custom.el
new file mode 100644
index 0000000..b89167d
--- /dev/null
+++ b/.emacs.d/emacs-custom.el
@@ -0,0 +1,14 @@
+(custom-set-variables
+ ;; custom-set-variables was added by Custom.
+ ;; If you edit it by hand, you could mess it up, so be careful.
+ ;; Your init file should contain only one such instance.
+ ;; If there is more than one, they won't work right.
+ '(custom-safe-themes
+ '("2dff5f0b44a9e6c8644b2159414af72261e38686072e063aa66ee98a2faecf0e" default))
+ '(markdown-command "/usr/bin/pandoc"))
+(custom-set-faces
+ ;; custom-set-faces was added by Custom.
+ ;; If you edit it by hand, you could mess it up, so be careful.
+ ;; Your init file should contain only one such instance.
+ ;; If there is more than one, they won't work right.
+ )
diff --git a/.emacs.d/init.el b/.emacs.d/init.el
new file mode 100644
index 0000000..7cd6534
--- /dev/null
+++ b/.emacs.d/init.el
@@ -0,0 +1,529 @@
+;Packages
+
+;; package archives
+(require 'package)
+(setq package-enable-at-startup nil)
+(setq package-archives
+ '(
+ ("melpa" . "https://melpa.org/packages/")
+ ("ELPA" . "http://tromey.com/elpa/")
+ ("gnu" . "http://elpa.gnu.org/packages/")
+ ("ORG" . "https://orgmode.org/elpa/")
+ )
+ )
+(package-initialize)
+(setq gnutls-algorithm-priority "NORMAL:-VERS-TLS1.3")
+
+;; Remove welcome screen
+(setq inhibit-startup-screen t)
+
+;; Disable menu
+(menu-bar-mode 0)
+
+;; Enable IDO mode
+(setq ido-enable-flex-matching t)
+;;(setq ido-everywhere t)
+;;(ido-mode 1)
+(global-set-key (kbd "C-x b") 'ido-switch-buffer)
+
+;; Remove working cl
+(require 'cl-lib)
+(setq byte-compile-warnings '(cl-functions))
+(advice-add 'sh-set-shell :around
+ (lambda (orig-fun &rest args)
+ (cl-letf (((symbol-function 'message) #'ignore))
+ (apply orig-fun args))))
+
+;; install use-package
+(unless (package-installed-p 'use-package)
+ (package-refresh-contents)
+ (package-install 'use-package)
+ )
+
+;; Set path to store "custom-set"
+(setq custom-file "~/.emacs.d/emacs-custom.el")
+
+;; Enable awesome-tab-mode
+(add-to-list 'load-path (expand-file-name "~/.emacs.d/plugins"))
+(require 'awesome-tab)
+(awesome-tab-mode t)
+
+(use-package awesome-tab
+ :load-path "~/.emacs.d/plugins"
+ :config
+ (awesome-tab-mode t))
+(awesome-tab-mode t)
+
+(global-set-key (kbd "C-x j") 'awesome-tab-backward-tab)
+(global-set-key (kbd "C-x k") 'awesome-tab-forward-tab)
+
+;; 80-charaters mode
+(add-hook 'text-mode-hook 'auto-fill-mode)
+(setq-default fill-column 80)
+
+; Global turn on flycheck
+(add-hook 'after-init-hook #'global-flycheck-mode)
+
+; Org Files
+(add-hook 'org-mode-hook '(lambda () (setq fill-column 80)))
+(add-hook 'org-mode-hook 'auto-fill-mode)
+(add-hook 'org-mode-hook 'turn-on-flyspell)
+
+;; Latex files
+(add-hook 'latex-mode-hook 'turn-on-flyspell)
+(setq ispell-dictionary "pl")
+
+;; Broswer
+(setq browse-url-browser-function 'browse-url-generic
+ browse-url-generic-program "browser-x")
+
+;; Switch-window
+(use-package switch-window
+ :ensure t
+ :config
+ (setq
+ switch-window-increase 4
+ switch-window-input-style 'minibuffer
+ switch-window-shortcut-style 'qwerty
+ switch-window-threshold 2
+ )
+ (setq
+ switch-window-qwerty-shortcuts
+ '( "a" "s" "d" "f" "g" "h" "j" "k" "l")
+ )
+ :bind
+ ([remap other-window] . switch-window)
+ )
+
+;; Enable japanese
+(if (condition-case nil (require 'mozc)(error nil))
+ (setq ecb-be-more-like-better-yes-p t)
+ (message "Monz not available; not configuring") )
+(setq default-input-method "japanese-mozc")
+
+;; Enable Smex
+(use-package smex
+ :ensure t
+ :init
+ (smex-initialize)
+ :bind
+ ("M-x" . smex)
+ )
+
+;; reveal dependency
+(use-package htmlize
+ :ensure t
+ )
+(use-package ox-reveal
+ :ensure t
+ :config
+ ;; maybe add auto-installer in the future
+ (setq org-reveal-root "https://cdn.jsdelivr.net/npm/reveal.js")
+)
+
+;; moveline
+(use-package move-text
+ :ensure t
+ :config)
+
+(global-set-key (kbd "M-") 'move-text-up)
+(global-set-key (kbd "M-") 'move-text-down)
+
+;; company
+(use-package company
+ :ensure t
+ )
+(global-company-mode)
+
+;; Shell - bash
+(use-package flymake-shellcheck
+ :ensure t
+ )
+
+(use-package flycheck-bashate
+ :ensure t
+ )
+
+(require 'bash-completion)
+(bash-completion-setup)
+
+(use-package flymake-shell
+ :ensure t
+ )
+
+(require 'flymake-shell)
+(add-hook 'sh-set-shell-hook 'flymake-shell-load)
+
+;; Default font
+(defun rc/get-default-font ()
+ (cond
+ ((eq system-type 'windows-nt) "Consolas-13")
+ ((eq system-type 'gnu/linux) "xos4 Terminus Bold 16")))
+
+(add-to-list 'default-frame-alist `(font . ,(rc/get-default-font)))
+
+;; Theme
+(use-package dracula-theme
+ :ensure t
+ :config
+ (load-theme 'dracula t))
+
+;; Sitebar dirred
+(use-package dired-sidebar
+ :ensure t
+ :commands (dired-sidebar-toggle-sidebar))
+
+(require 'dired-sidebar)
+(global-set-key (kbd "C-x d") 'dired-sidebar-toggle-sidebar)
+
+;; Smex
+(require 'smex)
+(smex-initialize)
+(global-set-key (kbd "M-x") 'smex)
+
+;;buffer-move
+(require 'buffer-move)
+(global-set-key (kbd "") 'buf-move-up)
+(global-set-key (kbd "") 'buf-move-down)
+(global-set-key (kbd "") 'buf-move-left)
+(global-set-key (kbd "") 'buf-move-right)
+
+;; Multiple-cursors
+(use-package multiple-cursors
+ :ensure t
+ :config
+)
+
+(global-set-key (kbd "C-S-c C-S-c") 'mc/edit-lines)
+(global-set-key (kbd "C->") 'mc/mark-next-like-this)
+(global-set-key (kbd "C-<") 'mc/mark-previous-like-this)
+(global-set-key (kbd "C-c C-<") 'mc/mark-all-like-this)
+
+;; Magit
+(use-package magit
+ :ensure t
+ :config
+ )
+(global-set-key (kbd "C-x g") 'magit-status)
+
+;; Helpers for easily building Emacs flymake checkers.
+(use-package flymake-easy
+ :ensure t
+ :config
+ )
+
+;; Error list
+(define-key flymake-mode-map (kbd "M-n") 'flymake-goto-next-error)
+(define-key flymake-mode-map (kbd "M-p") 'flymake-goto-prev-error)
+
+;; Ruby
+(use-package flymake-ruby
+ :ensure t
+ :config
+ )
+
+;;robe
+(use-package robe
+ :ensure t
+ :config
+ )
+
+(require 'robe)
+(add-hook 'ruby-mode-hook 'robe-mode)
+(add-hook 'robe-mode-hook 'ac-robe-setup)
+(eval-after-load 'company
+ '(push 'company-robe company-backends))
+
+(require 'flymake-ruby)
+(add-hook 'ruby-mode-hook 'flymake-ruby-load)
+
+;; Docker
+(use-package dockerfile-mode
+ :ensure t
+ :defer t)
+
+;; YAML
+(require 'flymake-yaml)
+
+(use-package yaml-mode
+ :ensure t
+ :config
+ )
+
+;; Ansible
+(use-package ansible
+ :ensure t
+ :config
+ )
+
+(use-package ansible-doc
+ :ensure t
+ :config
+ )
+
+(use-package company-ansible
+ :ensure t
+ :config
+ )
+
+(add-to-list 'company-backends 'company-ansible)
+(add-hook 'yaml-mode-hook '(lambda () (ansible 1)))
+(add-hook 'yaml-mode-hook #'ansible-doc-mode)
+
+;; Markdown-mode
+(custom-set-variables
+ '(markdown-command "/usr/bin/pandoc"))
+(use-package markdown-mode
+ :ensure t
+ :config
+ )
+
+;; C++ C
+(use-package auto-complete-clang
+ :ensure t
+ :config
+ )
+
+;; Apache
+(use-package apache-mode
+ :ensure t
+ :config
+ )
+
+;; Haskell
+(use-package haskell-mode
+ :ensure t
+ :config
+)
+
+(use-package flycheck-haskell
+ :ensure t
+ :config
+ )
+(add-hook 'flycheck-mode-hook #'flycheck-haskell-setup)
+
+
+;; Java
+;; lsp-install-server
+;; jdtls
+(use-package lsp-java
+ :ensure t
+ :config
+ (global-set-key (kbd "C-.") 'lsp-execute-code-action)
+)
+
+(global-set-key (kbd "") 'dap-breakpoint-toggle)
+(global-set-key (kbd "") 'dap-next)
+(global-set-key (kbd "") 'dap-step-in)
+(global-set-key (kbd "") 'lsp-jt-browser)
+(global-set-key (kbd "") 'dap-stop-thread)
+(global-set-key (kbd "") 'comment-or-uncomment-region)
+
+(add-hook 'dap-stopped-hook
+ (lambda (arg) (call-interactively #'dap-hydra)))
+
+(setq dap-auto-configure-features '(sessions locals controls tooltip))
+
+
+(require 'lsp-java)
+(add-hook 'java-mode-hook #'lsp)
+
+(require 'dap-java)
+
+;; Treemacs
+(use-package treemacs
+ :ensure t
+ :defer t
+ :init
+ (with-eval-after-load 'winum
+ (define-key winum-keymap (kbd "M-0") #'treemacs-select-window))
+ :config
+ (progn
+ (setq treemacs-collapse-dirs (if treemacs-python-executable 3 0)
+ treemacs-deferred-git-apply-delay 0.5
+ treemacs-directory-name-transformer #'identity
+ treemacs-display-in-side-window t
+ treemacs-eldoc-display t
+ treemacs-file-event-delay 5000
+ treemacs-file-extension-regex treemacs-last-period-regex-value
+ treemacs-file-follow-delay 0.2
+ treemacs-file-name-transformer #'identity
+ treemacs-follow-after-init t
+ treemacs-git-command-pipe ""
+ treemacs-goto-tag-strategy 'refetch-index
+ treemacs-indentation 2
+ treemacs-indentation-string " "
+ treemacs-is-never-other-window nil
+ treemacs-max-git-entries 5000
+ treemacs-missing-project-action 'ask
+ treemacs-move-forward-on-expand nil
+ treemacs-no-png-images nil
+ treemacs-no-delete-other-windows t
+ treemacs-project-follow-cleanup nil
+ treemacs-persist-file (expand-file-name ".cache/treemacs-persist" user-emacs-directory)
+ treemacs-position 'left
+ treemacs-read-string-input 'from-child-frame
+ treemacs-recenter-distance 0.1
+ treemacs-recenter-after-file-follow nil
+ treemacs-recenter-after-tag-follow nil
+ treemacs-recenter-after-project-jump 'always
+ treemacs-recenter-after-project-expand 'on-distance
+ treemacs-show-cursor nil
+ treemacs-show-hidden-files t
+ treemacs-silent-filewatch nil
+ treemacs-silent-refresh nil
+ treemacs-sorting 'alphabetic-asc
+ treemacs-space-between-root-nodes t
+ treemacs-tag-follow-cleanup t
+ treemacs-tag-follow-delay 1.5
+ treemacs-user-mode-line-format nil
+ treemacs-user-header-line-format nil
+ treemacs-width 35
+ treemacs-workspace-switch-cleanup nil)
+
+ ;; The default width and height of the icons is 22 pixels. If you are
+ ;; using a Hi-DPI display, uncomment this to double the icon size.
+ ;;(treemacs-resize-icons 44)
+
+ (treemacs-follow-mode t)
+ (treemacs-filewatch-mode t)
+ (treemacs-fringe-indicator-mode 'always)
+ (pcase (cons (not (null (executable-find "git")))
+ (not (null treemacs-python-executable)))
+ (`(t . t)
+ (treemacs-git-mode 'deferred))
+ (`(t . _)
+ (treemacs-git-mode 'simple))))
+ :bind
+ (:map global-map
+ ("M-0" . treemacs-select-window)
+ ("C-x t 1" . treemacs-delete-other-windows)
+ ("C-x t t" . treemacs)
+ ("C-x t B" . treemacs-bookmark)
+ ("C-x t C-t" . treemacs-find-file)
+ ("C-x t M-t" . treemacs-find-tag)))
+
+(use-package treemacs-evil
+ :after treemacs evil
+ :ensure t)
+
+(use-package treemacs-projectile
+ :after treemacs projectile
+ :ensure t)
+
+(use-package treemacs-icons-dired
+ :after treemacs dired
+ :ensure t
+ :config (treemacs-icons-dired-mode))
+
+(use-package treemacs-magit
+ :after treemacs magit
+ :ensure t)
+
+(use-package treemacs-persp ;;treemacs-perspective if you use perspective.el vs. persp-mode
+ :after treemacs persp-mode ;;or perspective vs. persp-mode
+ :ensure t
+ :config (treemacs-set-scope-type 'Perspectives))
+
+;;; --- Look & Feel ---
+
+;; Helm
+(use-package helm
+ :ensure t
+ :config
+)
+
+(add-hook 'helm-minibuffer-set-up-hook
+ 'helm-hide-minibuffer-maybe)
+
+(setq helm-autoresize-max-height 0)
+(setq helm-autoresize-min-height 20)
+
+(helm-autoresize-mode 1)
+(helm-mode 1)
+
+;; Disable scroll bar
+;; no toolbar:
+(if (display-graphic-p)
+ (progn
+ (tool-bar-mode -1)
+ (scroll-bar-mode -1)))
+
+;; Copy
+(setq select-active-regions nil)
+(setq mouse-drag-copy-region t)
+(global-set-key [mouse-2] 'mouse-yank-at-click)
+
+;; Zoom in/out.
+(global-set-key (kbd "M-+") 'text-scale-increase)
+(global-set-key (kbd "M--") 'text-scale-decrease)
+
+;; line numbers:
+(global-display-line-numbers-mode 1)
+
+;; scrolling:
+(setq scroll-conservatively 100)
+
+;; Whitespaces
+(global-whitespace-mode 1)
+(setq whitespace-display-mappings '((space-mark 32 [?·])))
+(set-face-attribute 'whitespace-space nil :background nil :foreground "gray30")
+(setq whitespace-style (quote (face tabs spaces trailing space-before-tab newline indentation empty space-after-tab space-mark tab-mark)))
+(add-hook 'before-save-hook (lambda () (delete-trailing-whitespace)))
+
+;; no "bell" (audible notification):
+(setq ring-bell-function 'ignore)
+
+;; auto reloading (reverting) buffers
+(global-auto-revert-mode 1)
+
+;; disable lock files:
+(setq create-lockfiles nil)
+
+;; disable autosave:
+(setq auto-save-default nil)
+
+;; disable backups:
+(setq make-backup-files nil)
+
+;; Pass "y or n" instead of "yes or no"
+(defalias 'yes-or-no-p 'y-or-n-p)
+
+;; Highlight parens
+(show-paren-mode 1)
+
+;; Candy
+(global-prettify-symbols-mode 1)
+
+;; Modeline
+(column-number-mode 1)
+(size-indication-mode 1)
+
+;; Horizontal splitting
+(defun split-and-follow-horizontally ()
+ (interactive)
+ (split-window-below)
+ (balance-windows)
+ (other-window 1)
+ )
+(global-set-key (kbd "C-x 2") 'split-and-follow-horizontally)
+
+;; Vertical splitting
+(defun split-and-follow-vertically ()
+ (interactive)
+ (split-window-right)
+ (balance-windows)
+ (other-window 1)
+ )
+(global-set-key (kbd "C-x 3") 'split-and-follow-vertically)
+
+;; Kill & remove split
+(defun kill-and-remove-split ()
+ "Kill and remove split."
+ (interactive)
+ (kill-buffer)
+ (delete-window)
+ (balance-windows)
+ (other-window 1)
+ )
+(global-set-key (kbd "C-x x") 'kill-and-remove-split)
diff --git a/.emacs.d/plugins/awesome-tab.el b/.emacs.d/plugins/awesome-tab.el
new file mode 100644
index 0000000..bb1ab42
--- /dev/null
+++ b/.emacs.d/plugins/awesome-tab.el
@@ -0,0 +1,1924 @@
+;;; awesome-tab.el --- Provide an out of box configuration to use tab in Emacs.
+
+;; Filename: awesome-tab.el
+;; Description: Provide an out of box configuration to use awesome-tab in Emacs.
+;; Author: Andy Stewart
+;; Maintainer: Andy Stewart
+;; Copyright (C) 2018, Andy Stewart, all rights reserved.
+;; Created: 2018-09-17 22:14:34
+;; Version: 7.3
+;; Last-Updated: 2020-04-10 12:36:52
+;; By: Andy Stewart
+;; URL: http://www.emacswiki.org/emacs/download/awesome-tab.el
+;; Keywords:
+;; Compatibility: GNU Emacs 27.0.50
+;;
+;; Features that might be required by this library:
+;;
+;; `cl-lib' `color' `which-func'
+;;
+
+;;; This file is NOT part of GNU Emacs
+
+;;; License
+;;
+;; 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, 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; see the file COPYING. If not, write to
+;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
+;; Floor, Boston, MA 02110-1301, USA.
+
+;;; Commentary:
+;;
+;; Provide an out of box configuration to use tab in Emacs.
+;;
+
+;;; Installation:
+;;
+;; Put awesome-tab.el to your load-path.
+;; The load-path is usually ~/elisp/.
+;; It's set in your ~/.emacs like this:
+;; (add-to-list 'load-path (expand-file-name "~/elisp"))
+;;
+;; And the following to your ~/.emacs startup file.
+;;
+;; (require 'awesome-tab)
+;; (awesome-tab-mode t)
+;;
+;; No need more.
+;;
+;; You can use below commands for you need:
+;;
+;; `awesome-tab-switch-group'
+;; `awesome-tab-select-beg-tab'
+;; `awesome-tab-select-end-tab'
+;; `awesome-tab-forward-tab-other-window'
+;; `awesome-tab-backward-tab-other-window'
+;; `awesome-tab-kill-other-buffers-in-current-group'
+;; `awesome-tab-kill-all-buffers-in-current-group'
+;; `awesome-tab-kill-match-buffers-in-current-group'
+;; `awesome-tab-keep-match-buffers-in-current-group'
+;; `awesome-tab-move-current-tab-to-left'
+;; `awesome-tab-move-current-tab-to-right'
+;;
+;; If you're helm fans, you need add below code in your helm config:
+;;
+;; (awesome-tab-build-helm-source)
+;;
+
+;;; Customize:
+;;
+;; `awesome-tab-cycle-scope'
+;; `awesome-tab-label-fixed-length'
+;; `awesome-tab-auto-scroll-flag'
+;; `awesome-tab-common-group-name'
+;; `awesometab-hide-tabs-hooks'
+;; `awesome-tab-height'
+;; `awesome-tab-display-sticky-function-name'
+;; `awesome-tab-display-icon'
+;; `awesome-tab-show-tab-index'
+;;
+
+;;; Change log:
+;;
+;; 2020/04/10
+;; * Fix issue #81
+;;
+;; 2020/03/22
+;; * Support EAF mode.
+;; * Add options for customize active bar.
+;;
+;; 2020/03/21
+;; * Remove unnecessary tab style and include active bar.
+;;
+;; 2020/02/13
+;; * Add `awesome-tab-all-the-icons-is-load-p' option.
+;; * Fix window-live-p error when click tab.
+;;
+;; 2020/01/13
+;; * Add new option `awesome-tab-label-max-length'.
+;;
+;; 2019/12/22
+;; * Add flycheck temp buffer in hide black list.
+;;
+;; 2019/10/10
+;; * Fix issue #58 click tab select window of tab first.
+;;
+;; 2019/09/12
+;; * Fix top-line above tab issue.
+;;
+;; 2019/08/13
+;; * Add new option `awesome-tab-height'.
+;;
+;; 2019/08/03
+;; * Adjust default value of `awesome-tab-ace-quit-keys'.
+;;
+;; 2019/08/02
+;; * Refactroy `awesome-tab-ace-jump'.
+;; * Refactory variable name.
+;; * Remove `awesome-tab-adjust-buffer-order' since `awesome-tab-ace-jump' is enough.
+;;
+;; 2019/08/01
+;; * Quit when user press Ctrl + g.
+;; * Make ace string use foreground same as `font-lock-function-name-face'.
+;; * Remove `awesome-tab-prefix-map'
+;;
+;; 2019/07/18
+;; * Use ema2159's way to render icon.
+;;
+;; 2019/07/17
+;; * Init `header-line' height from `default' face,
+;; `header-line' default inhibit from `mode-line',
+;; awesome-tab icon will disappear if `mode-line' height set with 0.1 by other plugins (such as awesome-tray).
+;; * Use `stringp' instead `ignore-errors' in `awesome-tab-icon-for-tab'.
+;;
+;; 2019/07/15
+;; * Don't call `awesome-tab-adjust-buffer-order' if user use mouse click tab.
+;;
+;; 2019/07/01
+;; * Make awesome-tab's colors change with user selected theme, thank you so much AmaiKinono.
+;; * Adjust dark mode background tab's color.
+;; * Remove local-mode code.
+;; * Refactory code.
+;;
+;; 2019/06/30
+;; * Add customize option `awesome-tab-display-icon' .
+;;
+;; 2019/06/28
+;; * Fix messages buffer icon an FontAwesome errors, thanks ema2159. ;)
+;; * Set height of tab face, avoid tab render error when user don't load any third theme.
+;; * Make `header-line' background same as default face.
+;;
+;; 2019/06/26
+;; * Fix error of void function awesome-tab-separator-separator-height
+;;
+;; 2019/06/25
+;; * Still display tab if all-the-icons cause "Error during redisplay" error in MacOS.
+;;
+;; 2019/06/24
+;; * Use ema2159's patch to fix icon face performance.
+;;
+;; 2019/06/23
+;; * Render file icon in tab when `all-the-icons' is load.
+;; * Use `all-the-icons-icon-for-buffer' to display icon for dired mode.
+;; * Support color icon.
+;; * Don't customize background of tab, use `default' face's background as tab background.
+;;
+;; 2019/04/14
+;; * Make `awesome-tab-last-sticky-func-name' default with nil.
+;;
+;; 2019/03/21
+;; * Make `awesome-tab-last-sticky-func-name' as buffer variable.
+;;
+;; 2019/03/19
+;; * If `tab-index' more than length of visible tabs, selet the last tab.
+;;
+;; 2019/03/18
+;; * Add new command `awesome-tab-select-visible-tab'.
+;;
+;; 2019/03/16
+;; * Fix integerp error.
+;;
+;; 2019/03/14
+;; * Try to fix numberp error.
+;;
+;; 2019/03/12
+;; * Display sticky function name in tab.
+;;
+;; 2019/03/09
+;; * Absorb powerline code, keep single file.
+;; * Remove some separator face that not suitable for displaying tab.
+;; * Add option `awesome-tab-style'.
+;;
+;; 2019/03/07
+;; * Add `cl' dependence.
+;;
+;; 2019/03/03
+;; * Automatically adsorb tabs after switching tabs, making switch tabs quickly.
+;; * Fix many typo errors.
+;; * Add `awesome-tab-adjust-buffer-order-function'.
+;; * Don't trigger by awesome-tab command, it's annoying.
+;;
+;; 2019/02/23
+;; * Significantly optimize the performance of switching tab by avoiding excessive calls `project-current'.
+;; * Use `powerline' render tab, it's beautiful!!!
+;;
+;; 2018/12/27
+;; * Tab will hide if ```awesome-tab-hide-tab-function``` return t, you can write your own code to customize hide rules.
+;;
+;; 2018/11/16
+;; * Open new tab on right of current one.
+;;
+;; 2018/11/14
+;; * Remove wheel features, emacser should only use the keyboard to operate Emacs.
+;;
+;; 2018/11/01
+;; * Remove `projectile' depend.
+;;
+;; 2018/10/29
+;; * Add `mwheel' depend.
+;;
+;; 2018/09/29
+;; * Add new command `awesome-tab-kill-other-buffers-in-current-group'
+;; * Not enable mode default.
+;;
+;; 2018/09/25
+;; * Adjust magit regexp to only match magit buffer, not file that named with magit.
+;;
+;; 2018/09/22
+;; * Adjust `awesome-tab-buffer-list' to hide unused buffer to user.
+;;
+;; 2018/09/20
+;; * Remove empty header line from magit buffers.
+;; * Add new function `awesome-tab-kill-match-buffers-in-current-group', it's handy in mixin mode, such as web-mode.
+;; * Add new function `awesome-tab-keep-match-buffers-in-current-group', it's handy in mixin mode, such as web-mode.
+;; * Fix error cause by `awesome-tab-kill-buffer-match-rule'.
+;;
+;; 2018/09/18
+;; * Fix unselect tab height and add option `awesome-tab-hide-tab-rules'
+;; * Use `awesome-tab-groups-hash' store every buffer's project name avoid performance issue cause by `projectile-project-name'.
+;;
+;; 2018/09/17
+;; * First released.
+;;
+
+;;; Acknowledgements:
+;;
+;; casouri: documentation and many useful patches.
+;; AmaiKinono: contributed to the patch that make tab color change with the theme automatically
+;;
+
+;;; TODO
+;;
+;;
+;;
+
+;;; Require
+(require 'cl-lib)
+(require 'color)
+
+;;; Code:
+;;;;;;;;;;;;;;;;;;;;;;; Awesome-Tab source code ;;;;;;;;;;;;;;;;;;;;;;;
+
+(defgroup awesome-tab nil
+ "Display a tab bar in the header line."
+ :group 'convenience)
+
+(defcustom awesome-tab-cycle-scope nil
+ "*Specify the scope of cyclic navigation through tabs.
+The following scopes are possible:
+
+- `tabs'
+ Navigate through visible tabs only.
+- `groups'
+ Navigate through tab groups only.
+- default
+ Navigate through visible tabs, then through tab groups."
+ :group 'awesome-tab
+ :type '(choice :tag "Cycle through..."
+ (const :tag "Visible Tabs Only" tabs)
+ (const :tag "Tab Groups Only" groups)
+ (const :tag "Visible Tabs then Tab Groups" nil)))
+
+(defcustom awesome-tab-auto-scroll-flag t
+ "*Non-nil means to automatically scroll the tab bar.
+That is, when a tab is selected outside of the tab bar visible area,
+the tab bar is scrolled horizontally so the selected tab becomes
+visible."
+ :group 'awesome-tab
+ :type 'boolean)
+
+(defcustom awesome-tab-common-group-name "Common"
+ "If the current buffer does not belong to any project,
+the group name uses the name of this variable."
+ :group 'awesome-tab
+ :type 'string)
+
+(defcustom awesome-tab-label-fixed-length 0
+ "Fixed length of label. Set to 0 if dynamic."
+ :group 'awesome-tab
+ :type 'int)
+
+(defcustom awesome-tab-label-max-length 30
+ "Max length of label. Set to 0 if dynamic."
+ :group 'awesome-tab
+ :type 'int)
+
+(defcustom awesometab-hide-tabs-hooks
+ '(magit-status-mode-hook magit-popup-mode-hook reb-mode-hook)
+ "Some buffer's header line is empty that make its window insufficient of space to display all content.
+Feel free to add hook in this option. ;)"
+ :type '(repeat symbol)
+ :group 'awesome-tab)
+
+(defcustom awesome-tab-display-sticky-function-name nil
+ "Non-nil to display sticky function name in tab.
+Sticky function is the function at the top of the current window sticky."
+ :group 'awesome-tab
+ :type 'boolean)
+
+(defcustom awesome-tab-display-icon t
+ "Non-nil to display icon in tab, this feature need `all-the-icons' is loaded.
+Set this option with nil if you don't like icon in tab."
+ :group 'awesome-tab
+ :type 'boolean)
+
+(defcustom awesome-tab-ace-keys '(?j ?k ?l ?s ?d ?f)
+ "Keys used for `awesome-tab-ace-jump'."
+ :group 'awesome-tab
+ :set #'(lambda (symbol value)
+ (set-default symbol value)
+ (let ((1k-seqs nil)
+ (2k-seqs nil))
+ (dolist (a value)
+ (dolist (b value)
+ (push (list a b) 2k-seqs))
+ (push (list a) 1k-seqs))
+ (setq awesome-tab-ace-2-key-seqs (nreverse 2k-seqs))
+ (setq awesome-tab-ace-1-key-seqs (nreverse 1k-seqs))))
+ :type '(repeat :tag "Keys" character))
+
+(defcustom awesome-tab-ace-quit-keys '(?\C-g ?q ?\s)
+ "Keys used to quit from ace jumping."
+ :group 'awesome-tab
+ :type '(repeat :tag "Keys" character))
+
+(defcustom awesome-tab-ace-str-style 'replace-icon
+ "Position of ace strings."
+ :group 'awesome-tab
+ :type '(choice
+ (const :tag "Replace icon" replace-icon)
+ (const :tag "Left" left)
+ (const :tag "Right" right)))
+
+(defcustom awesome-tab-height 190
+ "The height of tab face."
+ :group 'awesome-tab
+ :type 'int)
+
+(defcustom awesome-tab-icon-v-adjust 0
+ "The v-adjust of tab icon."
+ :group 'awesome-tab
+ :type 'float)
+
+(defcustom awesome-tab-icon-height 0.6
+ "The height of icon.
+It will render top-line on tab when you set this variable bigger than 0.9."
+ :group 'awesome-tab
+ :type 'float)
+
+(defcustom awesome-tab-show-tab-index nil
+ "Non-nil to display index in tab."
+ :group 'awesome-tab
+ :type 'boolean)
+
+(defcustom awesome-tab-index-format-str "[%d] "
+ "Format string for tab index."
+ :group 'awesome-tab
+ :type 'string)
+
+(defcustom awesome-tab-dark-selected-foreground-color nil
+ "Foreground for selected tab in dark theme."
+ :group 'awesome-tab
+ :type '(choice (const nil) string))
+
+(defcustom awesome-tab-dark-unselected-foreground-color nil
+ "Foreground for unselected tab in dark theme."
+ :group 'awesome-tab
+ :type '(choice (const nil) string))
+
+(defcustom awesome-tab-light-selected-foreground-color nil
+ "Foreground for selected tab in light theme."
+ :group 'awesome-tab
+ :type '(choice (const nil) string))
+
+(defcustom awesome-tab-light-unselected-foreground-color nil
+ "Foreground for unselected tab in light theme."
+ :group 'awesome-tab
+ :type '(choice (const nil) string))
+
+(defcustom awesome-tab-terminal-dark-select-background-color "#222222"
+ "Select background color for terminal dark mode."
+ :group 'awesome-tab
+ :type 'string)
+
+(defcustom awesome-tab-terminal-dark-select-foreground-color "#AAAAAA"
+ "Select foreground color for terminal dark mode."
+ :group 'awesome-tab
+ :type 'string)
+
+(defcustom awesome-tab-terminal-dark-unselect-background-color "#000000"
+ "Unselect background color for terminal dark mode."
+ :group 'awesome-tab
+ :type 'string)
+
+(defcustom awesome-tab-terminal-dark-unselect-foreground-color "#AAAAAA"
+ "Unselect foreground color for terminal dark mode."
+ :group 'awesome-tab
+ :type 'string)
+
+(defcustom awesome-tab-terminal-light-select-background-color "#AAAAAA"
+ "Select background color for terminal light mode."
+ :group 'awesome-tab
+ :type 'string)
+
+(defcustom awesome-tab-terminal-light-select-foreground-color "#333333"
+ "Select foreground color for terminal light mode."
+ :group 'awesome-tab
+ :type 'string)
+
+(defcustom awesome-tab-terminal-light-unselect-background-color "#333333"
+ "Unselect background color for terminal light mode."
+ :group 'awesome-tab
+ :type 'string)
+
+(defcustom awesome-tab-terminal-light-unselect-foreground-color "#AAAAAA"
+ "Unselect foreground color for terminal light mode."
+ :group 'awesome-tab
+ :type 'string)
+
+(defcustom awesome-tab-dark-active-bar-color nil
+ "The bar color for active tab in dark theme."
+ :group 'awesome-tab
+ :type '(choice (const nil) string))
+
+(defcustom awesome-tab-light-active-bar-color nil
+ "The bar color for active tab in light theme."
+ :group 'awesome-tab
+ :type '(choice (const nil) string))
+
+(defcustom awesome-tab-dark-unselected-blend 0.8
+ "The blend value for unselected background of dark mode.
+Lower value means more contrast."
+ :group 'awesome-tab
+ :type 'float)
+
+(defcustom awesome-tab-light-unselected-blend 0.9
+ "The blend value for unselected background of light mode.
+Lower value means more contrast."
+ :group 'awesome-tab
+ :type 'float)
+
+(defcustom awesome-tab-active-bar-width 3
+ "The width of active bar."
+ :group 'awesome-tab
+ :type 'integer)
+
+(defcustom awesome-tab-active-bar-height 30
+ "The height of active bar."
+ :group 'awesome-tab
+ :type 'integer)
+
+(defvar-local awesome-tab-ace-state nil
+ "Whether current buffer is doing `awesome-tab-ace-jump' or not.")
+
+(defvar awesome-tab-hide-tab-function 'awesome-tab-hide-tab
+ "Function to hide tab.
+This fucntion accepet tab name, tab will hide if this function return ni.")
+
+(defvar awesome-tab-current-tabset-function nil
+ "Function called with no argument to obtain the current tab set.
+This is the tab set displayed on the tab bar.")
+
+(defvar awesome-tab-select-tab-function nil
+ "Function that select a tab.
+The function is passed a tab, and should make it the
+selected tab.")
+
+(defvar awesome-tab-buffer-list-function 'awesome-tab-buffer-list
+ "Function that returns the list of buffers to show in tabs.
+That function is called with no arguments and must return a list of
+buffers.")
+
+(defvar awesome-tab-buffer-groups-function 'awesome-tab-buffer-groups
+ "Function that gives the group names the current buffer belongs to.
+It must return a list of group names, or nil if the buffer has no
+group. Notice that it is better that a buffer belongs to one group.")
+
+(defvar awesome-tab-ace-1-key-seqs nil
+ "List of 1-key sequences used by `awesome-tab-ace-jump'")
+
+(defvar awesome-tab-ace-2-key-seqs nil
+ "List of 2-key sequences used by `awesome-tab-ace-jump'")
+
+(defvar awesome-tab-all-the-icons-is-load-p (ignore-errors (require 'all-the-icons))
+ "Return non-nil if `all-the-icons' is load, `require' will have performance problem, so don't call it dynamically.")
+
+;;; Misc.
+;;
+(eval-and-compile
+ (defalias 'awesome-tab-display-update
+ (if (fboundp 'force-window-update)
+ #'(lambda () (force-window-update (selected-window)))
+ 'force-mode-line-update)))
+
+(defmacro awesome-tab-kill-buffer-match-rule (match-rule)
+ `(save-excursion
+ (mapc #'(lambda (buffer)
+ (with-current-buffer buffer
+ (when (string-equal current-group-name (cdr (awesome-tab-selected-tab (awesome-tab-current-tabset t))))
+ (when (funcall ,match-rule buffer)
+ (kill-buffer buffer))
+ )))
+ (buffer-list))))
+
+;; Copied from s.el
+(defun awesome-tab-truncate-string (len s &optional ellipsis)
+ "If S is longer than LEN, cut it down and add ELLIPSIS to the end.
+
+The resulting string, including ellipsis, will be LEN characters
+long.
+
+When not specified, ELLIPSIS defaults to ‘...’."
+ (declare (pure t) (side-effect-free t))
+ (unless ellipsis
+ (setq ellipsis "..."))
+ (if (> (length s) len)
+ (format "%s%s" (substring s 0 (- len (length ellipsis))) ellipsis)
+ (concat s (make-string (- len (length s)) ? ))))
+
+(defun awesome-tab-refresh-display ()
+ "Refresh the display of tabs. Put this in your user-defined hooks to
+make sure the face colors are always right."
+ (interactive)
+ (awesome-tab-map-tabsets (lambda (x) (awesome-tab-set-template x nil)))
+ (awesome-tab-display-update))
+
+;;; Tab and tab set
+;;
+(defsubst awesome-tab-make-tab (object tabset)
+ "Return a new tab with value OBJECT.
+TABSET is the tab set the tab belongs to."
+ (cons object tabset))
+
+(defsubst awesome-tab-tab-value (tab)
+ "Return the value of tab TAB."
+ (car tab))
+
+(defsubst awesome-tab-tab-tabset (tab)
+ "Return the tab set TAB belongs to."
+ (cdr tab))
+
+(defvar awesome-tab-tabsets nil
+ "The tab sets store.")
+
+(defvar awesome-tab-tabsets-tabset nil
+ "The special tab set of existing tab sets.")
+
+(defvar awesome-tab-current-tabset nil
+ "The tab set currently displayed on the tab bar.")
+(make-variable-buffer-local 'awesome-tab-current-tabset)
+
+(defvar awesome-tab-init-hook nil
+ "Hook run after tab bar data has been initialized.
+You should use this hook to initialize dependent data.")
+
+(defvar awesome-tab-active-bar nil)
+
+(defsubst awesome-tab-init-tabsets-store ()
+ "Initialize the tab set store."
+ (setq awesome-tab-tabsets (make-vector 31 0)
+ awesome-tab-tabsets-tabset (make-symbol "awesome-tab-tabsets-tabset"))
+ (put awesome-tab-tabsets-tabset 'start 0)
+ (run-hooks 'awesome-tab-init-hook))
+
+(defvar awesome-tab-quit-hook nil
+ "Hook run after tab bar data has been freed.
+You should use this hook to reset dependent data.")
+
+(defsubst awesome-tab-free-tabsets-store ()
+ "Free the tab set store."
+ (setq awesome-tab-tabsets nil
+ awesome-tab-tabsets-tabset nil)
+ (run-hooks 'awesome-tab-quit-hook))
+
+;; Define an "hygienic" function free of side effect between its local
+;; variables and those of the callee.
+(eval-and-compile
+ (defalias 'awesome-tab-map-tabsets
+ (let ((function (make-symbol "function"))
+ (result (make-symbol "result"))
+ (tabset (make-symbol "tabset")))
+ `(lambda (,function)
+ "Apply FUNCTION to each tab set, and make a list of the results.
+The result is a list just as long as the number of existing tab sets."
+ (let (,result)
+ (mapatoms
+ #'(lambda (,tabset)
+ (push (funcall ,function ,tabset) ,result))
+ awesome-tab-tabsets)
+ ,result)))))
+
+(defun awesome-tab-make-tabset (name &rest objects)
+ "Make a new tab set whose name is the string NAME.
+It is initialized with tabs build from the list of OBJECTS."
+ (let* ((tabset (intern name awesome-tab-tabsets))
+ (tabs (mapcar #'(lambda (object)
+ (awesome-tab-make-tab object tabset))
+ objects)))
+ (set tabset tabs)
+ (put tabset 'select (car tabs))
+ (put tabset 'start 0)
+ tabset))
+
+(defsubst awesome-tab-get-tabset (name)
+ "Return the tab set whose name is the string NAME.
+Return nil if not found."
+ (intern-soft name awesome-tab-tabsets))
+
+(defsubst awesome-tab-delete-tabset (tabset)
+ "Delete the tab set TABSET.
+That is, remove it from the tab sets store."
+ (unintern tabset awesome-tab-tabsets))
+
+(defsubst awesome-tab-tabs (tabset)
+ "Return the list of tabs in TABSET."
+ (symbol-value tabset))
+
+(defsubst awesome-tab-tab-values (tabset)
+ "Return the list of tab values in TABSET."
+ (mapcar 'awesome-tab-tab-value (awesome-tab-tabs tabset)))
+
+(defsubst awesome-tab-get-tab (object tabset)
+ "Search for a tab with value OBJECT in TABSET.
+Return the tab found, or nil if not found."
+ (assoc object (awesome-tab-tabs tabset)))
+
+(defsubst awesome-tab-member (tab tabset)
+ "Return non-nil if TAB is in TABSET."
+ (or (eq (awesome-tab-tab-tabset tab) tabset)
+ (memq tab (awesome-tab-tabs tabset))))
+
+(defsubst awesome-tab-template (tabset)
+ "Return the cached visual representation of TABSET.
+That is, a `header-line-format' template, or nil if the cache is
+empty."
+ (get tabset 'template))
+
+(defsubst awesome-tab-set-template (tabset template)
+ "Set the cached visual representation of TABSET to TEMPLATE.
+TEMPLATE must be a valid `header-line-format' template, or nil to
+cleanup the cache."
+ (put tabset 'template template))
+
+(defsubst awesome-tab-selected-tab (tabset)
+ "Return the tab selected in TABSET."
+ (get tabset 'select))
+
+(defsubst awesome-tab-selected-value (tabset)
+ "Return the value of the tab selected in TABSET."
+ (awesome-tab-tab-value (awesome-tab-selected-tab tabset)))
+
+(defsubst awesome-tab-selected-p (tab tabset)
+ "Return non-nil if TAB is the selected tab in TABSET."
+ (eq tab (awesome-tab-selected-tab tabset)))
+
+(defvar awesome-tab--track-selected nil)
+
+(defsubst awesome-tab-select-tab (tab tabset)
+ "Make TAB the selected tab in TABSET.
+Does nothing if TAB is not found in TABSET.
+Return TAB if selected, nil if not."
+ (when (awesome-tab-member tab tabset)
+ (unless (awesome-tab-selected-p tab tabset)
+ (awesome-tab-set-template tabset nil)
+ (setq awesome-tab--track-selected awesome-tab-auto-scroll-flag))
+ (put tabset 'select tab)))
+
+(defsubst awesome-tab-select-tab-value (object tabset)
+ "Make the tab with value OBJECT, the selected tab in TABSET.
+Does nothing if a tab with value OBJECT is not found in TABSET.
+Return the tab selected, or nil if nothing was selected."
+ (awesome-tab-select-tab (awesome-tab-get-tab object tabset) tabset))
+
+(defsubst awesome-tab-start (tabset)
+ "Return the index of the first visible tab in TABSET."
+ (get tabset 'start))
+
+(defsubst awesome-tab-view (tabset)
+ "Return the list of visible tabs in TABSET.
+That is, the sub-list of tabs starting at the first visible one."
+ (nthcdr (awesome-tab-start tabset) (awesome-tab-tabs tabset)))
+
+(defun awesome-tab-add-tab (tabset object)
+ "Return tab if it has opend.
+Otherwise insert new tab on right of current tab."
+ (let ((tabs (awesome-tab-tabs tabset)))
+ (if (awesome-tab-get-tab object tabset)
+ tabs
+ (let* ((tab (awesome-tab-make-tab object tabset))
+ (selected (awesome-tab-selected-tab tabset))
+ (selected-index (cl-position (car selected) (mapcar 'car tabs))))
+ (awesome-tab-set-template tabset nil)
+ (set tabset (awesome-tab-insert-at tabs selected-index tab))
+ ))))
+
+(defun awesome-tab-insert-at (list index insert-element)
+ (let ((counter 0)
+ (result '()))
+ (dolist (element list)
+ (if (equal counter index)
+ (setq result (append result (list element insert-element)))
+ (setq result (append result (list element))))
+ (setq counter (+ 1 counter)))
+ result))
+
+(defun awesome-tab-delete-tab (tab)
+ "Remove TAB from its tab set."
+ (let* ((tabset (awesome-tab-tab-tabset tab))
+ (tabs (awesome-tab-tabs tabset))
+ (sel (eq tab (awesome-tab-selected-tab tabset)))
+ (next (and sel (cdr (memq tab tabs)))))
+ (awesome-tab-set-template tabset nil)
+ (setq tabs (delq tab tabs))
+ ;; When the selected tab is deleted, select the next one, if
+ ;; available, or the last one otherwise.
+ (and sel (awesome-tab-select-tab (car (or next (last tabs))) tabset))
+ (set tabset tabs)))
+
+(defun awesome-tab-scroll (tabset count)
+ "Scroll the visible tabs in TABSET of COUNT units.
+If COUNT is positive move the view on right. If COUNT is negative,
+move the view on left."
+ (let ((start (min (max 0 (+ (awesome-tab-start tabset) count))
+ (1- (length (awesome-tab-tabs tabset))))))
+ (when (/= start (awesome-tab-start tabset))
+ (awesome-tab-set-template tabset nil)
+ (put tabset 'start start))))
+
+(defun awesome-tab-tab-next (tabset tab &optional before)
+ "Search in TABSET for the tab after TAB.
+If optional argument BEFORE is non-nil, search for the tab before
+TAB. Return the tab found, or nil otherwise."
+ (let* (last (tabs (awesome-tab-tabs tabset)))
+ (while (and tabs (not (eq tab (car tabs))))
+ (setq last (car tabs)
+ tabs (cdr tabs)))
+ (and tabs (if before last (nth 1 tabs)))))
+
+(defun awesome-tab-current-tabset (&optional update)
+ "Return the tab set currently displayed on the tab bar.
+If optional argument UPDATE is non-nil, call the user defined function
+`awesome-tab-current-tabset-function' to obtain it. Otherwise return the
+current cached copy."
+ (and update awesome-tab-current-tabset-function
+ (setq awesome-tab-current-tabset
+ (funcall awesome-tab-current-tabset-function)))
+ awesome-tab-current-tabset)
+
+(defun awesome-tab-get-tabsets-tabset ()
+ "Return the tab set of selected tabs in existing tab sets."
+ (set awesome-tab-tabsets-tabset (awesome-tab-map-tabsets 'awesome-tab-selected-tab))
+ (awesome-tab-scroll awesome-tab-tabsets-tabset 0)
+ (awesome-tab-set-template awesome-tab-tabsets-tabset nil)
+ awesome-tab-tabsets-tabset)
+
+;;; Faces
+;;
+
+(defface awesome-tab-unselected-face
+ '((t))
+ "Face used for unselected tabs.
+Do not customize this. It's attribute will be calculated on the
+fly to fit your theme."
+ :group 'awesome-tab)
+
+(defface awesome-tab-selected-face
+ '((t))
+ "Face used for the selected tab.
+Do not customize this. It's attribute will be calculated on the
+fly to fit your theme."
+ :group 'awesome-tab)
+
+(defface awesome-tab-unselected-ace-face
+ '((t (:inherit 'font-lock-function-name-face)))
+ "Face used for ace string on unselected tabs.
+Note that the background will be calculated on the fly, so
+customize the background will not have any effect."
+ :group 'awesome-tab)
+
+(defface awesome-tab-selected-ace-face
+ '((t (:inherit 'font-lock-function-name-face)))
+ "Face used for ace string on selected tabs.
+Note that the background will be calculated on the fly, so
+customize the background will not have any effect."
+ :group 'awesome-tab)
+
+(defface awesome-tab-unselected-index-face
+ '((t (:inherit 'font-lock-function-name-face)))
+ "Face used for index on unselected tabs.
+Note that the background will be calculated on the fly, so
+customize the background will not have any effect."
+ :group 'awesome-tab)
+
+(defface awesome-tab-selected-index-face
+ '((t (:inherit 'font-lock-function-name-face)))
+ "Face used for index on selected tabs.
+Note that the background will be calculated on the fly, so
+customize the background will not have any effect."
+ :group 'awesome-tab)
+
+;;; Tabs
+;;
+(defun awesome-tab-make-header-line-mouse-map (mouse function)
+ (let ((map (make-sparse-keymap)))
+ (define-key map (vector 'header-line mouse) function)
+ map))
+
+(defun awesome-tab-color-blend (c1 c2 alpha)
+ "Blend two colors C1 and C2 with ALPHA.
+C1 and C2 are hexidecimal strings.
+ALPHA is a number between 0.0 and 1.0 which corresponds to the
+influence of C1 on the result."
+ (apply #'(lambda (r g b)
+ (format "#%02x%02x%02x"
+ (ash r -8)
+ (ash g -8)
+ (ash b -8)))
+ (cl-mapcar
+ (lambda (x y)
+ (round (+ (* x alpha) (* y (- 1 alpha)))))
+ (color-values c1) (color-values c2))))
+
+(defun awesome-tab-adjust-color-with-theme ()
+ "We need adjust awesome-tab's colors when user switch new theme."
+ (let* ((bg-mode (frame-parameter nil 'background-mode))
+ (select-tab-background (awesome-tab-get-select-background-color))
+ (unselect-tab-background (awesome-tab-get-unslect-background-color)))
+ (when (display-graphic-p)
+ (setq awesome-tab-active-bar (awesome-tab-make-xpm awesome-tab-active-bar-width awesome-tab-active-bar-height)))
+
+ (set-face-attribute 'header-line nil :height awesome-tab-height)
+
+ (set-face-attribute 'awesome-tab-selected-face nil
+ :foreground (awesome-tab-get-select-foreground-color)
+ :distant-foreground (awesome-tab-get-select-foreground-color)
+ :background select-tab-background)
+ (set-face-attribute 'awesome-tab-unselected-face nil
+ :foreground (awesome-tab-get-unselect-foreground-color)
+ :distant-foreground (awesome-tab-get-unselect-foreground-color)
+ :background unselect-tab-background)
+
+ (set-face-attribute 'awesome-tab-selected-ace-face nil
+ :background select-tab-background)
+ (set-face-attribute 'awesome-tab-unselected-ace-face nil
+ :background unselect-tab-background)
+
+ (set-face-attribute 'awesome-tab-selected-index-face nil
+ :background select-tab-background)
+ (set-face-attribute 'awesome-tab-unselected-index-face nil
+ :background unselect-tab-background)))
+
+(defun awesome-tab-get-select-foreground-color ()
+ (let ((bg-mode (frame-parameter nil 'background-mode)))
+ (if (display-graphic-p)
+ (cond ((eq bg-mode 'dark) (or awesome-tab-dark-selected-foreground-color
+ (face-foreground 'font-lock-doc-face)))
+ ((eq bg-mode 'light) (or awesome-tab-light-selected-foreground-color
+ (face-foreground 'font-lock-doc-face))))
+ (cond ((eq bg-mode 'dark) awesome-tab-terminal-dark-select-foreground-color)
+ ((eq bg-mode 'light) awesome-tab-terminal-light-select-foreground-color))
+ )))
+
+(defun awesome-tab-get-unselect-foreground-color ()
+ (let ((bg-mode (frame-parameter nil 'background-mode)))
+ (if (display-graphic-p)
+ (cond ((eq bg-mode 'dark) (or awesome-tab-dark-unselected-foreground-color
+ (face-foreground 'font-lock-comment-face)))
+ ((eq bg-mode 'light) (or awesome-tab-light-unselected-foreground-color
+ (face-foreground 'font-lock-comment-face))))
+ (cond ((eq bg-mode 'dark) awesome-tab-terminal-dark-unselect-foreground-color)
+ ((eq bg-mode 'light) awesome-tab-terminal-light-unselect-foreground-color))
+ )))
+
+(defun awesome-tab-get-select-background-color ()
+ (let ((bg-mode (frame-parameter nil 'background-mode)))
+ (if (display-graphic-p)
+ (face-background 'default)
+ (cond ((eq bg-mode 'dark) awesome-tab-terminal-dark-select-background-color)
+ ((eq bg-mode 'light) awesome-tab-terminal-light-select-background-color))
+ )))
+
+(defun awesome-tab-get-unslect-background-color ()
+ (let* ((bg-mode (frame-parameter nil 'background-mode)))
+ (if (display-graphic-p)
+ (cond ((eq bg-mode 'dark)
+ (awesome-tab-color-blend (face-background 'default) "#000000" awesome-tab-dark-unselected-blend))
+ ((eq bg-mode 'light)
+ (awesome-tab-color-blend (face-background 'default) "#000000" awesome-tab-light-unselected-blend)))
+ (cond ((eq bg-mode 'dark) awesome-tab-terminal-dark-unselect-background-color)
+ ((eq bg-mode 'light) awesome-tab-terminal-light-unselect-background-color))
+ )))
+
+(defun awesome-tab-line-format (tabset)
+ "Return the `header-line-format' value to display TABSET."
+ ;; Adjust color with theme.
+ (awesome-tab-adjust-color-with-theme)
+ ;; Reder tab line.
+ (let* ((sel (awesome-tab-selected-tab tabset))
+ (tabs (awesome-tab-view tabset))
+ (bg-mode (frame-parameter nil 'background-mode))
+ (header-line-color (awesome-tab-get-unslect-background-color))
+ atsel elts)
+ ;; Track the selected tab to ensure it is always visible.
+ (when awesome-tab--track-selected
+ (while (not (memq sel tabs))
+ (awesome-tab-scroll tabset -1)
+ (setq tabs (awesome-tab-view tabset)))
+ (while (and tabs (not atsel))
+ (setq elts (cons (awesome-tab-line-tab (car tabs)) elts)
+ atsel (eq (car tabs) sel)
+ tabs (cdr tabs)))
+ (setq elts (nreverse elts))
+ ;; At this point the selected tab is the last elt in ELTS.
+ ;; Scroll TABSET and ELTS until the selected tab becomes
+ ;; visible.
+ (let (buffer-list-update-hook)
+ (with-temp-buffer
+ (let ((truncate-partial-width-windows nil)
+ (inhibit-modification-hooks t)
+ deactivate-mark ;; Prevent deactivation of the mark!
+ start)
+ (setq truncate-lines nil
+ buffer-undo-list t)
+ (setq start (point))
+ (while (and (cdr elts) ;; Always show the selected tab!
+ (progn
+ (delete-region start (point-max))
+ (goto-char (point-max))
+ (apply 'insert elts)
+ (goto-char (point-min))
+ (> (vertical-motion 1) 0)))
+ (awesome-tab-scroll tabset 1)
+ (setq elts (cdr elts))))))
+ (setq elts (nreverse elts))
+ (setq awesome-tab--track-selected nil))
+ ;; Format remaining tabs.
+ (while tabs
+ (setq elts (cons (awesome-tab-line-tab (car tabs)) elts)
+ tabs (cdr tabs)))
+ ;; Cache and return the new tab bar.
+ (awesome-tab-set-template
+ tabset
+ (list (nreverse elts)
+ (propertize "%-"
+ 'face (list :background header-line-color
+ :foreground header-line-color
+ :distant-foreground header-line-color)
+ 'pointer 'arrow)))))
+
+(defun awesome-tab-line ()
+ "Return the header line templates that represent the tab bar.
+Inhibit display of the tab bar in current window `awesome-tab-hide-tab-function' return nil."
+ (cond
+ ((awesome-tab-hide-tab-cached (current-buffer))
+ ;; Don't show the tab bar.
+ (setq header-line-format nil))
+ ((awesome-tab-current-tabset t)
+ ;; When available, use a cached tab bar value, else recompute it.
+ (or (awesome-tab-template awesome-tab-current-tabset)
+ (awesome-tab-line-format awesome-tab-current-tabset)))))
+
+(defconst awesome-tab-header-line-format '(:eval (awesome-tab-line))
+ "The tab bar header line format.")
+
+;;; Cyclic navigation through tabs
+;;
+(defun awesome-tab-cycle (&optional backward type)
+ "Cycle to the next available tab.
+The scope of the cyclic navigation through tabs is specified by the
+option `awesome-tab-cycle-scope'.
+If optional argument BACKWARD is non-nil, cycle to the previous tab
+instead."
+ (let* ((tabset (awesome-tab-current-tabset t))
+ (ttabset (awesome-tab-get-tabsets-tabset))
+ ;; If navigation through groups is requested, and there is
+ ;; only one group, navigate through visible tabs.
+ (cycle (if (and (eq awesome-tab-cycle-scope 'groups)
+ (not (cdr (awesome-tab-tabs ttabset))))
+ 'tabs
+ awesome-tab-cycle-scope))
+ selected tab)
+ (when tabset
+ (setq selected (awesome-tab-selected-tab tabset))
+ (cond
+ ;; Cycle through visible tabs only.
+ ((eq cycle 'tabs)
+ (setq tab (awesome-tab-tab-next tabset selected backward))
+ ;; When there is no tab after/before the selected one, cycle
+ ;; to the first/last visible tab.
+ (unless tab
+ (setq tabset (awesome-tab-tabs tabset)
+ tab (car (if backward (last tabset) tabset))))
+ )
+ ;; Cycle through tab groups only.
+ ((eq cycle 'groups)
+ (setq tab (awesome-tab-tab-next ttabset selected backward))
+ ;; When there is no group after/before the selected one, cycle
+ ;; to the first/last available group.
+ (unless tab
+ (setq tabset (awesome-tab-tabs ttabset)
+ tab (car (if backward (last tabset) tabset))))
+ )
+ (t
+ ;; Cycle through visible tabs then tab groups.
+ (setq tab (awesome-tab-tab-next tabset selected backward))
+ ;; When there is no visible tab after/before the selected one,
+ ;; cycle to the next/previous available group.
+ (unless tab
+ (setq tab (awesome-tab-tab-next ttabset selected backward))
+ ;; When there is no next/previous group, cycle to the
+ ;; first/last available group.
+ (unless tab
+ (setq tabset (awesome-tab-tabs ttabset)
+ tab (car (if backward (last tabset) tabset))))
+ ;; Select the first/last visible tab of the new group.
+ (setq tabset (awesome-tab-tabs (awesome-tab-tab-tabset tab))
+ tab (car (if backward (last tabset) tabset))))
+ ))
+ (awesome-tab-buffer-select-tab tab))))
+
+;;;###autoload
+(defun awesome-tab-backward ()
+ "Select the previous available tab.
+Depend on the setting of the option `awesome-tab-cycle-scope'."
+ (interactive)
+ (awesome-tab-cycle t))
+
+;;;###autoload
+(defun awesome-tab-forward ()
+ "Select the next available tab.
+Depend on the setting of the option `awesome-tab-cycle-scope'."
+ (interactive)
+ (awesome-tab-cycle))
+
+;;;###autoload
+(defun awesome-tab-backward-group ()
+ "Go to selected tab in the previous available group."
+ (interactive)
+ (let ((awesome-tab-cycle-scope 'groups))
+ (awesome-tab-cycle t)))
+
+;;;###autoload
+(defun awesome-tab-forward-group ()
+ "Go to selected tab in the next available group."
+ (interactive)
+ (let ((awesome-tab-cycle-scope 'groups))
+ (awesome-tab-cycle)))
+
+;;;###autoload
+(defun awesome-tab-backward-tab ()
+ "Select the previous visible tab."
+ (interactive)
+ (let ((awesome-tab-cycle-scope 'tabs))
+ (awesome-tab-cycle t)))
+
+;;;###autoload
+(defun awesome-tab-forward-tab ()
+ "Select the next visible tab."
+ (interactive)
+ (let ((awesome-tab-cycle-scope 'tabs))
+ (awesome-tab-cycle)))
+
+;;; Minor modes
+;;
+(defsubst awesome-tab-mode-on-p ()
+ "Return non-nil if Awesome-Tab mode is on."
+ (eq (default-value 'header-line-format)
+ awesome-tab-header-line-format))
+
+;;; Awesome-Tab mode
+;;
+(defvar awesome-tab-mode-map
+ (let ((km (make-sparse-keymap)))
+ km)
+ "Keymap to use in Awesome-Tab mode.")
+
+(defvar awesome-tab--global-hlf nil)
+
+;;;###autoload
+(define-minor-mode awesome-tab-mode
+ "Toggle display of a tab bar in the header line.
+With prefix argument ARG, turn on if positive, otherwise off.
+Returns non-nil if the new state is enabled.
+
+\\{awesome-tab-mode-map}"
+ :group 'awesome-tab
+ :require 'awesome-tab
+ :global t
+ :keymap awesome-tab-mode-map
+ (if awesome-tab-mode
+;;; ON
+ (unless (awesome-tab-mode-on-p)
+ ;; Save current default value of `header-line-format'.
+ (setq awesome-tab--global-hlf (default-value 'header-line-format))
+ (awesome-tab-init-tabsets-store)
+ (setq-default header-line-format awesome-tab-header-line-format))
+;;; OFF
+ (when (awesome-tab-mode-on-p)
+ ;; Restore previous `header-line-format'.
+ (setq-default header-line-format awesome-tab--global-hlf)
+ (awesome-tab-free-tabsets-store))
+ ))
+
+;;; Buffer tabs
+;;
+(defgroup awesome-tab-buffer nil
+ "Display buffers in the tab bar."
+ :group 'awesome-tab)
+
+(defun awesome-tab-filter (condp lst)
+ (delq nil
+ (mapcar (lambda (x) (and (funcall condp x) x)) lst)))
+
+(defun awesome-tab-filter-out (condp lst)
+ (delq nil
+ (mapcar (lambda (x) (if (funcall condp x) nil x)) lst)))
+
+(defun awesome-tab-buffer-list ()
+ "Return the list of buffers to show in tabs.
+Exclude buffers whose name starts with a space, when they are not
+visiting a file. The current buffer is always included."
+ (awesome-tab-filter-out
+ 'awesome-tab-hide-tab-cached
+ (delq nil
+ (mapcar #'(lambda (b)
+ (cond
+ ;; Always include the current buffer.
+ ((eq (current-buffer) b) b)
+ ((buffer-file-name b) b)
+ ((char-equal ?\ (aref (buffer-name b) 0)) nil)
+ ((buffer-live-p b) b)))
+ (buffer-list)))))
+
+(defun awesome-tab-buffer-mode-derived-p (mode parents)
+ "Return non-nil if MODE derives from a mode in PARENTS."
+ (let (derived)
+ (while (and (not derived) mode)
+ (if (memq mode parents)
+ (setq derived t)
+ (setq mode (get mode 'derived-mode-parent))))
+ derived))
+
+;;; Group buffers in tab sets.
+;;
+(defvar awesome-tab--buffers nil)
+
+(defun awesome-tab-buffer-update-groups ()
+ "Update tab sets from groups of existing buffers.
+Return the the first group where the current buffer is."
+ (let ((bl (sort
+ (mapcar
+ #'(lambda (b)
+ (with-current-buffer b
+ (list (current-buffer)
+ (buffer-name)
+ (if awesome-tab-buffer-groups-function
+ (funcall awesome-tab-buffer-groups-function)
+ '(awesome-tab-common-group-name)))))
+ (and awesome-tab-buffer-list-function
+ (funcall awesome-tab-buffer-list-function)))
+ #'(lambda (e1 e2)
+ (string-lessp (nth 1 e1) (nth 1 e2))))))
+ ;; If the cache has changed, update the tab sets.
+ (unless (equal bl awesome-tab--buffers)
+ ;; Add new buffers, or update changed ones.
+ (dolist (e bl)
+ (dolist (g (nth 2 e))
+ (let ((tabset (awesome-tab-get-tabset g)))
+ (if tabset
+ (unless (equal e (assq (car e) awesome-tab--buffers))
+ ;; This is a new buffer, or a previously existing
+ ;; buffer that has been renamed, or moved to another
+ ;; group. Update the tab set, and the display.
+ (awesome-tab-add-tab tabset (car e))
+ (awesome-tab-set-template tabset nil))
+ (awesome-tab-make-tabset g (car e))))))
+ ;; Remove tabs for buffers not found in cache or moved to other
+ ;; groups, and remove empty tabsets.
+ (mapc 'awesome-tab-delete-tabset
+ (awesome-tab-map-tabsets
+ #'(lambda (tabset)
+ (dolist (tab (awesome-tab-tabs tabset))
+ (let ((e (assq (awesome-tab-tab-value tab) bl)))
+ (or (and e (memq tabset
+ (mapcar 'awesome-tab-get-tabset
+ (nth 2 e))))
+ (awesome-tab-delete-tab tab))))
+ ;; Return empty tab sets
+ (unless (awesome-tab-tabs tabset)
+ tabset))))
+ ;; The new cache becomes the current one.
+ (setq awesome-tab--buffers bl)))
+ ;; Return the first group the current buffer belongs to.
+ (car (nth 2 (assq (current-buffer) awesome-tab--buffers))))
+
+;;; Tab bar callbacks
+;;
+(defvar awesome-tab--buffer-show-groups nil)
+
+(defsubst awesome-tab-buffer-show-groups (flag)
+ "Set display of tabs for groups of buffers to FLAG."
+ (setq awesome-tab--buffer-show-groups flag))
+
+(defun awesome-tab-buffer-tabs ()
+ "Return the buffers to display on the tab bar, in a tab set."
+ (let ((tabset (awesome-tab-get-tabset (awesome-tab-buffer-update-groups))))
+ (awesome-tab-select-tab-value (current-buffer) tabset)
+ (when awesome-tab--buffer-show-groups
+ (setq tabset (awesome-tab-get-tabsets-tabset))
+ (awesome-tab-select-tab-value (current-buffer) tabset))
+ tabset))
+
+(defsubst awesome-tab-line-tab (tab)
+ "Return the display representation of tab TAB.
+That is, a propertized string used as an `header-line-format' template
+element."
+ (propertize
+ (awesome-tab-buffer-tab-label tab)
+ 'pointer 'hand
+ 'local-map (purecopy (awesome-tab-make-header-line-mouse-map
+ 'mouse-1
+ `(lambda (event) (interactive "e")
+ (let ((tab-window (window-at (cadr (mouse-position))
+ (cddr (mouse-position))
+ (car (mouse-position)))))
+ (when tab-window
+ (select-window tab-window)
+ (awesome-tab-buffer-select-tab ',tab))))))))
+
+(defun awesome-tab-buffer-tab-label (tab)
+ "Return a label for TAB.
+That is, a string used to represent it on the tab bar."
+ (let* ((is-active-tab (awesome-tab-selected-p tab (awesome-tab-current-tabset)))
+ (tab-face (if is-active-tab 'awesome-tab-selected-face 'awesome-tab-unselected-face))
+ (index-face (if is-active-tab 'awesome-tab-selected-index-face 'awesome-tab-unselected-index-face))
+ (current-buffer-index (cl-position tab (awesome-tab-view (awesome-tab-current-tabset t))))
+ (ace-str-face (if is-active-tab 'awesome-tab-selected-ace-face 'awesome-tab-unselected-ace-face))
+ (current-buffer-index (cl-position tab (awesome-tab-view awesome-tab-current-tabset)))
+ (ace-str (if awesome-tab-ace-state (elt ace-strs current-buffer-index) ""))
+ (ace-state awesome-tab-ace-state))
+ (concat
+ ;; Active bar.
+ (when (and is-active-tab awesome-tab-active-bar)
+ (propertize awesome-tab-active-bar))
+ ;; Ace string.
+ (when (and ace-state (eq awesome-tab-ace-str-style 'left))
+ (propertize ace-str 'face ace-str-face))
+ ;; Tab icon.
+ (when (and awesome-tab-display-icon
+ awesome-tab-all-the-icons-is-load-p)
+ (propertize " " 'face tab-face))
+ (if (and ace-state (eq awesome-tab-ace-str-style 'replace-icon))
+ (propertize ace-str 'face ace-str-face)
+ (awesome-tab-icon-for-tab tab tab-face))
+ ;; Tab label.
+ (propertize (awesome-tab-tab-name tab) 'face tab-face)
+ ;; Ace string.
+ (when (and ace-state (eq awesome-tab-ace-str-style 'right))
+ (propertize ace-str 'face ace-str-face))
+ ;; Tab index.
+ (when awesome-tab-show-tab-index
+ (propertize (format awesome-tab-index-format-str (+ current-buffer-index 1)) 'face index-face))
+ )))
+
+(defun awesome-tab-make-xpm (width height)
+ "Create an XPM bitmap via WIDTH and HEIGHT."
+ (when (and (display-graphic-p)
+ (image-type-available-p 'xpm))
+ (propertize
+ " " 'display
+ (let ((data (make-list height (make-list width 1)))
+ (color (pcase (frame-parameter nil 'background-mode)
+ ('dark (or awesome-tab-dark-active-bar-color
+ (face-background 'highlight)))
+ ('light (or awesome-tab-light-active-bar-color
+ (face-background 'highlight))))))
+ (ignore-errors
+ (create-image
+ (concat
+ (format
+ "/* XPM */\nstatic char * percent[] = {\n\"%i %i 2 1\",\n\". c %s\",\n\" c %s\","
+ (length (car data)) (length data) color color)
+ (apply #'concat
+ (cl-loop with idx = 0
+ with len = (length data)
+ for dl in data
+ do (cl-incf idx)
+ collect
+ (concat
+ "\""
+ (cl-loop for d in dl
+ if (= d 0) collect (string-to-char " ")
+ else collect (string-to-char "."))
+ (if (eq idx len) "\"};" "\",\n")))))
+ 'xpm t :ascent 'center))))))
+
+(defun awesome-tab-tab-name (tab)
+ "Render tab's name.
+Tab name will truncate if option `awesome-tab-truncate-string' big than zero."
+ (format " %s "
+ (let ((bufname (awesome-tab-buffer-name (car tab))))
+ (cond ((> awesome-tab-label-fixed-length 0)
+ (awesome-tab-truncate-string awesome-tab-label-fixed-length bufname))
+ ((> awesome-tab-label-max-length 0)
+ (let ((ellipsis "..."))
+ (if (> (length bufname) awesome-tab-label-max-length)
+ (format "%s%s" (substring bufname 0 (- awesome-tab-label-max-length (length ellipsis))) ellipsis)
+ bufname)))
+ (t
+ bufname))
+ )))
+
+(defun awesome-tab-icon-for-tab (tab face)
+ "When tab buffer's file is exists, use `all-the-icons-icon-for-file' to fetch file icon.
+Otherwise use `all-the-icons-icon-for-buffer' to fetch icon for buffer."
+ (when (and awesome-tab-display-icon
+ awesome-tab-all-the-icons-is-load-p)
+ (let* ((tab-buffer (car tab))
+ (tab-file (buffer-file-name tab-buffer))
+ (background (face-background face))
+ (icon
+ (cond
+ ;; Use `all-the-icons-icon-for-file' if current file is exists.
+ ((and
+ tab-file
+ (file-exists-p tab-file))
+ (all-the-icons-icon-for-file tab-file :v-adjust awesome-tab-icon-v-adjust :height awesome-tab-icon-height))
+ ;; Use `all-the-icons-icon-for-mode' for current tab buffer at last.
+ (t
+ (with-current-buffer tab-buffer
+ (if (derived-mode-p tab-buffer 'eaf-mode)
+ (all-the-icons-faicon "html5" :v-adjust awesome-tab-icon-v-adjust :height awesome-tab-icon-height)
+ (all-the-icons-icon-for-mode major-mode :v-adjust awesome-tab-icon-v-adjust :height awesome-tab-icon-height))
+ )))))
+ (when (and icon
+ ;; `get-text-property' need icon is string type.
+ (stringp icon))
+ ;; Thanks ema2159 for code block ;)
+ (propertize icon 'face `(:inherit ,(get-text-property 0 'face icon) :background ,background))))))
+
+(defun awesome-tab-buffer-name (tab-buffer)
+ "Get buffer name of tab.
+Will merge sticky function name in tab if option `awesome-tab-display-sticky-function-name' is non-nil."
+ (if (and awesome-tab-display-sticky-function-name
+ (boundp 'awesome-tab-last-sticky-func-name)
+ awesome-tab-last-sticky-func-name
+ (equal tab-buffer (current-buffer)))
+ (format "%s [%s]" (buffer-name tab-buffer) awesome-tab-last-sticky-func-name)
+ (buffer-name tab-buffer)))
+
+(defvar awesome-tab-last-scroll-y 0
+ "Holds the scroll y of window from the last run of post-command-hooks.")
+
+(defun awesome-tab-monitor-window-scroll ()
+ "This function is used to monitor the window scroll.
+Currently, this function is only use for option `awesome-tab-display-sticky-function-name'."
+ (when awesome-tab-display-sticky-function-name
+ (let ((scroll-y (window-start)))
+ (when (and scroll-y
+ (integerp scroll-y))
+ (unless (equal scroll-y awesome-tab-last-scroll-y)
+ (let ((func-name (save-excursion
+ (goto-char scroll-y)
+ (require 'which-func)
+ (which-function))))
+ (when (or
+ (not (boundp 'awesome-tab-last-sticky-func-name))
+ (not (equal func-name awesome-tab-last-sticky-func-name)))
+ (set (make-local-variable 'awesome-tab-last-sticky-func-name) func-name)
+
+ ;; Use `ignore-errors' avoid integerp error when execute `awesome-tab-line-format'.
+ (ignore-errors
+ (awesome-tab-line-format awesome-tab-current-tabset))
+ ))))
+ (setq awesome-tab-last-scroll-y scroll-y))))
+
+(add-hook 'post-command-hook 'awesome-tab-monitor-window-scroll)
+
+(defun awesome-tab-buffer-select-tab (tab)
+ "Select tab."
+ (let ((buffer (awesome-tab-tab-value tab)))
+ (switch-to-buffer buffer)
+ (awesome-tab-buffer-show-groups nil)
+ (awesome-tab-display-update)
+ ))
+
+(defun awesome-tab-buffer-track-killed ()
+ "Hook run just before actually killing a buffer.
+In Awesome-Tab mode, try to switch to a buffer in the current tab bar,
+after the current buffer has been killed. Try first the buffer in tab
+after the current one, then the buffer in tab before. On success, put
+the sibling buffer in front of the buffer list, so it will be selected
+first."
+ (and (eq header-line-format awesome-tab-header-line-format)
+ (eq awesome-tab-current-tabset-function 'awesome-tab-buffer-tabs)
+ (eq (current-buffer) (window-buffer (selected-window)))
+ (let ((bl (awesome-tab-tab-values (awesome-tab-current-tabset)))
+ (b (current-buffer))
+ found sibling)
+ (while (and bl (not found))
+ (if (eq b (car bl))
+ (setq found t)
+ (setq sibling (car bl)))
+ (setq bl (cdr bl)))
+ (when (and (setq sibling (or (car bl) sibling))
+ (buffer-live-p sibling))
+ ;; Move sibling buffer in front of the buffer list.
+ (save-current-buffer
+ (switch-to-buffer sibling))))))
+
+;;; Tab bar buffer setup
+;;
+(defun awesome-tab-buffer-init ()
+ "Initialize tab bar buffer data.
+Run as `awesome-tab-init-hook'."
+ (setq awesome-tab--buffers nil
+ awesome-tab--buffer-show-groups nil
+ awesome-tab-current-tabset-function 'awesome-tab-buffer-tabs
+ awesome-tab-select-tab-function 'awesome-tab-buffer-select-tab
+ )
+ (add-hook 'kill-buffer-hook 'awesome-tab-buffer-track-killed))
+
+(defun awesome-tab-buffer-quit ()
+ "Quit tab bar buffer.
+Run as `awesome-tab-quit-hook'."
+ (setq awesome-tab--buffers nil
+ awesome-tab--buffer-show-groups nil
+ awesome-tab-current-tabset-function nil
+ awesome-tab-select-tab-function nil
+ )
+ (remove-hook 'kill-buffer-hook 'awesome-tab-buffer-track-killed))
+
+(add-hook 'awesome-tab-init-hook 'awesome-tab-buffer-init)
+(add-hook 'awesome-tab-quit-hook 'awesome-tab-buffer-quit)
+
+;;;;;;;;;;;;;;;;;;;;;;; Interactive functions ;;;;;;;;;;;;;;;;;;;;;;;
+(defun awesome-tab-switch-group (&optional groupname)
+ "Switch tab groups using ido."
+ (interactive)
+ (let* ((tab-buffer-list (mapcar
+ #'(lambda (b)
+ (with-current-buffer b
+ (list (current-buffer)
+ (buffer-name)
+ (funcall awesome-tab-buffer-groups-function) )))
+ (funcall awesome-tab-buffer-list-function)))
+ (groups (awesome-tab-get-groups))
+ (group-name (or groupname
+ (completing-read "Switch to group: "
+ groups nil t))) )
+ (catch 'done
+ (mapc
+ #'(lambda (group)
+ (when (equal group-name (car (car (cdr (cdr group)))))
+ (throw 'done (switch-to-buffer (car (cdr group))))))
+ tab-buffer-list) )))
+
+(defun awesome-tab-select-end-tab ()
+ "Select end tab of current tabset."
+ (interactive)
+ (awesome-tab-select-beg-tab t))
+
+(defun awesome-tab-select-beg-tab (&optional backward type)
+ "Select beginning tab of current tabs.
+If BACKWARD is non-nil, move backward, otherwise move forward.
+TYPE is default option."
+ (interactive)
+ (let* ((tabset (awesome-tab-current-tabset t))
+ (ttabset (awesome-tab-get-tabsets-tabset))
+ (cycle (if (and (eq awesome-tab-cycle-scope 'groups)
+ (not (cdr (awesome-tab-tabs ttabset))))
+ 'tabs
+ awesome-tab-cycle-scope))
+ selected tab)
+ (when tabset
+ (setq selected (awesome-tab-selected-tab tabset))
+ (setq tabset (awesome-tab-tabs tabset)
+ tab (car (if backward (last tabset) tabset)))
+ (awesome-tab-buffer-select-tab tab))))
+
+(defun awesome-tab-backward-tab-other-window (&optional reversed)
+ "Move to left tab in other window.
+Optional argument REVERSED default is move backward, if reversed is non-nil move forward."
+ (interactive)
+ (other-window 1)
+ (if reversed
+ (awesome-tab-forward-tab)
+ (awesome-tab-backward-tab))
+ (other-window -1))
+
+(defun awesome-tab-forward-tab-other-window ()
+ "Move to right tab in other window."
+ (interactive)
+ (awesome-tab-backward-tab-other-window t))
+
+(defun awesome-tab-move-current-tab-to-right ()
+ "Move current tab one place right, unless it's already the rightmost."
+ (interactive)
+ (let* ((bufset (awesome-tab-current-tabset t))
+ (old-bufs (awesome-tab-tabs bufset))
+ (first-buf (car old-bufs))
+ (new-bufs (list)))
+ (while (and
+ old-bufs
+ (not (string= (buffer-name) (format "%s" (car (car old-bufs))))))
+ (push (car old-bufs) new-bufs)
+ (setq old-bufs (cdr old-bufs)))
+ (if old-bufs ; if this is false, then the current tab's buffer name is mysteriously missing
+ (progn
+ (setq the-buffer (car old-bufs))
+ (setq old-bufs (cdr old-bufs))
+ (if old-bufs ; if this is false, then the current tab is the rightmost
+ (push (car old-bufs) new-bufs))
+ (push the-buffer new-bufs)) ; this is the tab that was to be moved
+ (error "Error: current buffer's name was not found in Awesome-Tab's buffer list."))
+ (setq new-bufs (reverse new-bufs))
+ (setq new-bufs (append new-bufs (cdr old-bufs)))
+ (set bufset new-bufs)
+ (awesome-tab-set-template bufset nil)
+ (awesome-tab-display-update)))
+
+(defun awesome-tab-move-current-tab-to-left ()
+ "Move current tab one place left, unless it's already the leftmost."
+ (interactive)
+ (let* ((bufset (awesome-tab-current-tabset t))
+ (old-bufs (awesome-tab-tabs bufset))
+ (first-buf (car old-bufs))
+ (new-bufs (list)))
+ (if (string= (buffer-name) (format "%s" (car first-buf)))
+ old-bufs ; the current tab is the leftmost
+ (setq not-yet-this-buf first-buf)
+ (setq old-bufs (cdr old-bufs))
+ (while (and
+ old-bufs
+ (not (string= (buffer-name) (format "%s" (car (car old-bufs))))))
+ (push not-yet-this-buf new-bufs)
+ (setq not-yet-this-buf (car old-bufs))
+ (setq old-bufs (cdr old-bufs)))
+ (if old-bufs ; if this is false, then the current tab's buffer name is mysteriously missing
+ (progn
+ (push (car old-bufs) new-bufs) ; this is the tab that was to be moved
+ (push not-yet-this-buf new-bufs)
+ (setq new-bufs (reverse new-bufs))
+ (setq new-bufs (append new-bufs (cdr old-bufs))))
+ (error "Error: current buffer's name was not found in Awesome-Tab's buffer list."))
+ (set bufset new-bufs)
+ (awesome-tab-set-template bufset nil)
+ (awesome-tab-display-update))))
+
+(defun awesome-tab-move-current-tab-to-beg ()
+ "Move current tab to the first position."
+ (interactive)
+ (let* ((bufset (awesome-tab-current-tabset t))
+ (bufs (copy-sequence (awesome-tab-tabs bufset)))
+ (current-tab-index
+ (cl-position (current-buffer) (mapcar #'car bufs)))
+ (current-tab (elt bufs current-tab-index)))
+ (setq bufs (delete current-tab bufs))
+ (push current-tab bufs)
+ (set bufset bufs)
+ (awesome-tab-set-template bufset nil)
+ (awesome-tab-display-update)))
+
+(defun awesome-tab-kill-all-buffers-in-current-group ()
+ "Kill all buffers in current group."
+ (interactive)
+ (let* ((current-group-name (cdr (awesome-tab-selected-tab (awesome-tab-current-tabset t)))))
+ ;; Kill all buffers in current group.
+ (awesome-tab-kill-buffer-match-rule
+ (lambda (buffer) t))
+ ;; Switch to next group.
+ (awesome-tab-forward-group)
+ ))
+
+(defun awesome-tab-kill-other-buffers-in-current-group ()
+ "Kill all buffers except current buffer in current group."
+ (interactive)
+ (let* ((current-group-name (cdr (awesome-tab-selected-tab (awesome-tab-current-tabset t))))
+ (currentbuffer (current-buffer)))
+ ;; Kill all buffers in current group.
+ (awesome-tab-kill-buffer-match-rule
+ (lambda (buffer) (not (equal buffer currentbuffer))))
+ ))
+
+(defun awesome-tab-kill-match-buffers-in-current-group ()
+ "Kill all buffers match extension in current group."
+ (interactive)
+ (let* ((current-group-name (cdr (awesome-tab-selected-tab (awesome-tab-current-tabset t))))
+ (extension-names (awesome-tab-get-extensions))
+ match-extension)
+ ;; Read extension need to kill.
+ (setq match-extension (completing-read "Kill buffers suffix with: "
+ extension-names nil t))
+ ;; Kill all buffers match extension in current group.
+ (awesome-tab-kill-buffer-match-rule
+ (lambda (buffer)
+ (let ((filename (buffer-file-name buffer)))
+ (and filename (string-equal (file-name-extension filename) match-extension))
+ )))
+ ;; Switch to next group if last file killed.
+ (when (equal (length extension-names) 1)
+ (awesome-tab-forward-group))
+ ))
+
+(defun awesome-tab-keep-match-buffers-in-current-group ()
+ "Keep all buffers match extension in current group."
+ (interactive)
+ (let* ((current-group-name (cdr (awesome-tab-selected-tab (awesome-tab-current-tabset t))))
+ (extension-names (awesome-tab-get-extensions))
+ match-extension)
+ ;; Read extension need to kill.
+ (setq match-extension (completing-read "Keep buffers suffix with: "
+ extension-names nil t))
+ ;; Kill all buffers match extension in current group.
+ (awesome-tab-kill-buffer-match-rule
+ (lambda (buffer)
+ (let ((filename (buffer-file-name buffer)))
+ (and filename (not (string-equal (file-name-extension filename) match-extension)))
+ )))))
+
+(defun awesome-tab-select-visible-nth-tab (tab-index)
+ "Select visible tab with `tab-index'.
+Example, when `tab-index' is 1, this function will select the leftmost label in the visible area,
+instead of the first label in the current group.
+
+If `tab-index' more than length of visible tabs, selet the last tab.
+
+If `tab-index' is 0, select last tab."
+ (let ((visible-tabs (awesome-tab-view awesome-tab-current-tabset)))
+ (switch-to-buffer
+ (car
+ (if (or (equal tab-index 0)
+ (> tab-index (length visible-tabs)))
+ (car (last visible-tabs))
+ (nth (- tab-index 1) visible-tabs))))))
+
+(defun awesome-tab-select-visible-tab ()
+ "Bind this function with number keystroke, such as s-1, s-2, s-3 ... etc.
+
+This function automatically recognizes the number at the end of the keystroke
+and switches to the tab of the corresponding index.
+
+Note that this function switches to the visible range,
+not the actual logical index position of the current group."
+ (interactive)
+ (let* ((event last-command-event)
+ (key (make-vector 1 event))
+ (key-desc (key-description key))
+ (tab-index (string-to-number
+ ;; Fix issue #81 that `last-command-event' is not keystroke.
+ (cond ((equal (length key-desc) 1)
+ key-desc)
+ (t
+ (nth 1 (split-string key-desc "-")))))))
+ (awesome-tab-select-visible-nth-tab tab-index)))
+
+(defun awesome-tab-build-ace-strs (len key-number seqs)
+ "Build strings for `awesome-tab-ace-jump'.
+LEN is the number of strings, should be the number of current visible
+tabs. NKEYS should be 1 or 2."
+ (let ((i 0)
+ (str nil))
+ (when (>= key-number 3)
+ (error "NKEYS should be 1 or 2"))
+ (while (< i len)
+ (push (apply #'string (elt seqs i)) str)
+ (setq i (1+ i)))
+ (nreverse str)))
+
+(defun awesome-tab-open-tab-in-current-group ()
+ "Open a tab in current group."
+ (interactive)
+ (let* ((generate-candidate (lambda (tab)
+ (propertize
+ (string-trim (awesome-tab-tab-name tab))
+ 'awesome-tab-tab-entity tab)))
+ (candidates (mapcar generate-candidate
+ (awesome-tab-tabs (awesome-tab-current-tabset)))))
+ (awesome-tab-buffer-select-tab
+ (get-text-property 0 'awesome-tab-tab-entity
+ (completing-read "Open tab:" candidates nil t)))))
+
+(defun awesome-tab-ace-jump ()
+ "Jump to a visible tab by 1 or 2 chars."
+ (interactive)
+ (catch 'quit
+ (let* ((visible-tabs (awesome-tab-view awesome-tab-current-tabset))
+ (visible-tabs-length (length visible-tabs))
+ done-flag
+ (lower-bound 0)
+ (upper-bound visible-tabs-length)
+ (ace-keys (length awesome-tab-ace-keys))
+ (key-number (cond
+ ((<= visible-tabs-length ace-keys) 1)
+ ((<= visible-tabs-length (* ace-keys ace-keys)) 2)
+ (t (error "Too many visible tabs. Put more keys into `awesome-tab-ace-keys'."))))
+ (visible-seqs
+ (cl-subseq
+ (symbol-value
+ (intern
+ (concat "awesome-tab-ace-" (number-to-string key-number) "-key-seqs")))
+ 0 visible-tabs-length))
+ (ace-strs (awesome-tab-build-ace-strs visible-tabs-length key-number visible-seqs)))
+ (setq awesome-tab-ace-state t)
+ (awesome-tab-refresh-display)
+ (dotimes (i key-number)
+ (while (not done-flag)
+ (let ((char (with-local-quit (read-key (format "Awesome Tab Ace Jump (%d):" (1+ i))))))
+ (if (not (member char awesome-tab-ace-quit-keys))
+ (let ((current-chars (mapcar #'car visible-seqs)))
+ (when (member char current-chars)
+ (setq done-flag t)
+ (setq lower-bound (cl-position char current-chars))
+ (setq upper-bound (1- (- visible-tabs-length (cl-position char (nreverse current-chars)))))
+ (dotimes (lower-index lower-bound)
+ (setcar (nthcdr lower-index visible-seqs) nil))
+ (setq upper-index (1+ upper-bound))
+ (while (< upper-index visible-tabs-length)
+ (setcar (nthcdr upper-index visible-seqs) nil)
+ (setq upper-index (1+ upper-index)))
+ (setq upper-index 0)
+ ))
+ ;; Quit when user press Ctrl + g.
+ (setq awesome-tab-ace-state nil)
+ (awesome-tab-refresh-display)
+ (throw 'quit nil))))
+ (setq done-flag nil)
+ (setq visible-seqs (mapcar #'cdr visible-seqs))
+ (setq ace-strs (awesome-tab-build-ace-strs visible-tabs-length key-number visible-seqs))
+ (awesome-tab-refresh-display))
+ (setq awesome-tab-ace-state nil)
+ (awesome-tab-refresh-display)
+ (awesome-tab-buffer-select-tab (nth lower-bound visible-tabs)))))
+
+;;;;;;;;;;;;;;;;;;;;;;; Utils functions ;;;;;;;;;;;;;;;;;;;;;;;
+(defun awesome-tab-get-groups ()
+ ;; Refresh groups.
+ (set awesome-tab-tabsets-tabset (awesome-tab-map-tabsets 'awesome-tab-selected-tab))
+ (mapcar #'(lambda (group)
+ (format "%s" (cdr group)))
+ (awesome-tab-tabs awesome-tab-tabsets-tabset)))
+
+(defun awesome-tab-get-extensions ()
+ ;; Refresh groups.
+ (set awesome-tab-tabsets-tabset (awesome-tab-map-tabsets 'awesome-tab-selected-tab))
+ (let ((extension-names '()))
+ (mapc #'(lambda (buffer)
+ (with-current-buffer buffer
+ (when (string-equal current-group-name (cdr (awesome-tab-selected-tab (awesome-tab-current-tabset t))))
+ (when (buffer-file-name buffer)
+ (add-to-list 'extension-names (file-name-extension (buffer-file-name buffer))))
+ )))
+ (buffer-list))
+ extension-names))
+
+;;;;;;;;;;;;;;;;;;;;;;; Default configurations ;;;;;;;;;;;;;;;;;;;;;;;
+
+;; Uniquify tab name when open multiple buffers with same filename.
+(setq uniquify-separator "/")
+(setq uniquify-buffer-name-style 'post-forward-angle-brackets)
+(setq uniquify-after-kill-buffer-p t)
+
+(dolist (hook awesometab-hide-tabs-hooks)
+ (add-hook hook '(lambda () (setq-local header-line-format nil))))
+
+;; Rules to control buffer's group rules.
+(defvar awesome-tab-groups-hash (make-hash-table :test 'equal))
+(defvar awesome-tab-hide-hash (make-hash-table :test 'equal))
+
+(defun awesome-tab-project-name ()
+ (let ((project-name (cdr (project-current))))
+ (if project-name
+ (format "Project: %s" (expand-file-name project-name))
+ awesome-tab-common-group-name)))
+
+(defun awesome-tab-get-group-name (buf)
+ (let ((group-name (gethash buf awesome-tab-groups-hash)))
+ ;; Return group name cache if it exists for improve performance.
+ (if group-name
+ group-name
+ ;; Otherwise try get group name with `project-current'.
+ ;; `project-current' is very slow, it will slow down Emacs if you call it when switch buffer.
+ (with-current-buffer buf
+ (let ((project-name (awesome-tab-project-name)))
+ (puthash buf project-name awesome-tab-groups-hash)
+ project-name)))))
+
+(defun awesome-tab-buffer-groups ()
+ "`awesome-tab-buffer-groups' control buffers' group rules.
+
+Group awesome-tab with mode if buffer is derived from `eshell-mode' `emacs-lisp-mode' `dired-mode' `org-mode' `magit-mode'.
+All buffer name start with * will group to \"Emacs\".
+Other buffer group by `awesome-tab-get-group-name' with project name."
+ (list
+ (cond
+ ((or (string-equal "*" (substring (buffer-name) 0 1))
+ (memq major-mode '(magit-process-mode
+ magit-status-mode
+ magit-diff-mode
+ magit-log-mode
+ magit-file-mode
+ magit-blob-mode
+ magit-blame-mode
+ )))
+ "Emacs")
+ ((derived-mode-p 'eshell-mode)
+ "EShell")
+ ((derived-mode-p 'emacs-lisp-mode)
+ "Elisp")
+ ((derived-mode-p 'dired-mode)
+ "Dired")
+ ((memq major-mode '(org-mode org-agenda-mode diary-mode))
+ "OrgMode")
+ ((derived-mode-p 'eaf-mode)
+ "EAF")
+ (t
+ (awesome-tab-get-group-name (current-buffer))))))
+
+;; Helm source for switching group in helm.
+(defvar helm-source-awesome-tab-group nil)
+
+(defun awesome-tab-build-helm-source ()
+ (interactive)
+ (setq helm-source-awesome-tab-group
+ (when (ignore-errors (require 'helm))
+ (helm-build-sync-source "Awesome-Tab Group"
+ :candidates #'awesome-tab-get-groups
+ :action '(("Switch to group" . awesome-tab-switch-group))))))
+
+;;;###autoload
+(defun awesome-tab-counsel-switch-group ()
+ "Switch group of awesome-tab."
+ (interactive)
+ (when (ignore-errors (require 'ivy))
+ (ivy-read
+ "Awesome-Tab Groups:"
+ (awesome-tab-get-groups)
+ :action #'awesome-tab-switch-group
+ :caller 'awesome-tab-counsel-switch-group)))
+
+(defun awesome-tab-hide-tab (x)
+ (let ((name (format "%s" x)))
+ (or
+ ;; Current window is not dedicated window.
+ (window-dedicated-p (selected-window))
+
+ ;; Buffer name not match below blacklist.
+ (string-prefix-p "*epc" name)
+ (string-prefix-p "*helm" name)
+ (string-prefix-p "*Compile-Log*" name)
+ (string-prefix-p "*lsp" name)
+ (string-prefix-p "*flycheck" name)
+
+ ;; Is not magit buffer.
+ (and (string-prefix-p "magit" name)
+ (not (file-name-extension name)))
+ )))
+
+(defun awesome-tab-hide-tab-cached (buf)
+ (let ((hide (gethash buf awesome-tab-hide-hash 'not-found)))
+ (when (eq hide 'not-found)
+ (setq hide (funcall awesome-tab-hide-tab-function buf))
+ (puthash buf hide awesome-tab-hide-hash))
+ hide))
+
+(defvar awesome-tab-last-focus-buffer nil
+ "The last focus buffer.")
+
+(defvar awesome-tab-last-focus-buffer-group nil
+ "The group name of last focus buffer.")
+
+(defun awesome-tab-remove-nth-element (nth list)
+ (if (zerop nth) (cdr list)
+ (let ((last (nthcdr (1- nth) list)))
+ (setcdr last (cddr last))
+ list)))
+
+(defun awesome-tab-insert-after (list aft-el el)
+ "Insert EL after AFT-EL in LIST."
+ (push el (cdr (member aft-el list)))
+ list)
+
+(defun awesome-tab-insert-before (list bef-el el)
+ "Insert EL before BEF-EL in LIST."
+ (nreverse (awesome-tab-insert-after (nreverse list) bef-el el)))
+
+(advice-add 'load-theme :around #'awesome-tab-load-theme)
+(defun awesome-tab-load-theme (orig-fun &optional arg &rest args)
+ "Update tab after execute `load-theme'."
+ (apply orig-fun arg args)
+ (awesome-tab-refresh-display))
+
+(provide 'awesome-tab)
+
+;;; awesome-tab.el ends here
diff --git a/.emacs.d/plugins/bash-completion.el b/.emacs.d/plugins/bash-completion.el
new file mode 100644
index 0000000..5c391b9
--- /dev/null
+++ b/.emacs.d/plugins/bash-completion.el
@@ -0,0 +1,1723 @@
+;;; bash-completion.el --- BASH completion for the shell buffer -*- lexical-binding: t -*-
+
+;; Copyright (C) 2009 Stephane Zermatten
+
+;; Author: Stephane Zermatten
+;; Maintainer: Stephane Zermatten
+;; Version: 3.1.0
+;; Keywords: shell bash bash-completion
+;; URL: http://github.com/szermatt/emacs-bash-completion
+;; Package-Requires: ((emacs "24.3"))
+
+;; 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 2 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/'.
+
+;;; Commentary:
+;;
+;; This file defines dynamic completion hooks for shell-mode and
+;; shell-command prompts that are based on bash completion.
+;;
+;; Bash completion for emacs:
+;; - is aware of bash builtins, aliases and functions
+;; - does file expansion inside of colon-separated variables
+;; and after redirections (> or <)
+;; - escapes special characters when expanding file names
+;; - is configurable through programmable bash completion
+;;
+;; When the first completion is requested in shell model or a shell
+;; command, bash-completion.el starts a separate bash
+;; process. Bash-completion.el then uses this process to do the actual
+;; completion and includes it into Emacs completion suggestions.
+;;
+;; A simpler and more complete alternative to bash-completion.el is to
+;; run a bash shell in a buffer in term mode(M-x `ansi-term').
+;; Unfortunately, many Emacs editing features are not available when
+;; running in term mode. Also, term mode is not available in
+;; shell-command prompts.
+;;
+;; Bash completion can also be run programmatically, outside of a
+;; shell-mode command, by calling
+;; `bash-completion-dynamic-complete-nocomint'
+;;
+;; INSTALLATION
+;;
+;; 1. copy bash-completion.el into a directory that's on Emacs load-path
+;; 2. add this into your .emacs file:
+;; (autoload 'bash-completion-dynamic-complete \"bash-completion\"
+;; \"BASH completion hook\")
+;; (add-hook 'shell-dynamic-complete-functions
+;; 'bash-completion-dynamic-complete)
+;;
+;; or simpler, but forces you to load this file at startup:
+;;
+;; (require 'bash-completion)
+;; (bash-completion-setup)
+;;
+;; 3. reload your .emacs (M-x `eval-buffer') or restart
+;;
+;; Once this is done, use as usual to do dynamic completion from
+;; shell mode or a shell command minibuffer, such as the one started
+;; for M-x `compile'. Note that the first completion is slow, as emacs
+;; launches a new bash process.
+;;
+;; Naturally, you'll get better results if you turn on programmable
+;; bash completion in your shell. Depending on how your system is set
+;; up, this might requires calling:
+;; . /etc/bash_completion
+;; from your ~/.bashrc.
+;;
+;; When called from a bash shell buffer,
+;; `bash-completion-dynamic-complete' communicates with the current shell
+;; to reproduce, as closely as possible the normal bash auto-completion,
+;; available on full terminals.
+;;
+;; When called from non-shell buffers, such as the prompt of M-x
+;; compile, `bash-completion-dynamic-complete' creates a separate bash
+;; process just for doing completion. Such processes have the
+;; environment variable EMACS_BASH_COMPLETE set to t, to help
+;; distinguish them from normal shell processes.
+;;
+;; See the documentation of the function
+;; `bash-completion-dynamic-complete-nocomint' to do bash completion
+;; from other buffers or completion engines.
+;;
+;; COMPATIBILITY
+;;
+;; bash-completion.el is known to work with Bash 3, 4 and 5, on Emacs,
+;; starting with version 24.3, under Linux and OSX. It does not work
+;; on XEmacs.
+;;
+
+;;; History:
+;;
+;; Full history is available on
+;; https://github.com/szermatt/emacs-bash-completion
+
+;;; Code:
+
+(require 'comint)
+(require 'cl-lib)
+(require 'shell)
+
+;;; ---------- Customization
+(defgroup bash-completion nil
+ "BASH configurable command-line completion "
+ :group 'shell
+ :group 'shell-command)
+
+(defcustom bash-completion-enabled t
+ "Enable/Disable BASH configurable command-line completion globally.
+
+This flag is useful for temporarily disabling bash completion
+once it's been installed.
+
+Setting this variable to t is NOT enough to enable BASH completion.
+BASH completion is only available in the environment for which
+`bash-completion-dynamic-complete' has been registered. See
+`bash-completion-setup' for that."
+ :type '(boolean)
+ :group 'bash-completion)
+
+(defcustom bash-completion-use-separate-processes nil
+ "Enable/disable the use of separate processes to perform completion.
+
+When set to a non-nil value, separate processes will always be
+used to perform completion. If nil, process associated with the
+current buffer will be used to perform completion from a shell
+buffer associated to a bash shell, and otherwise a separate process
+will be started to do completion."
+ :type 'boolean
+ :group 'bash-completion)
+
+(defcustom bash-completion-prog (executable-find "bash")
+ "Name or path of the BASH executable to run for command-line completion.
+
+This should be either an absolute path to the BASH executable or
+the name of the bash command if it is on Emacs' PATH. This should
+point to a recent version of BASH, 3 or 4, with support for
+command-line completion.
+
+This variable is only used when creating separate processes for
+performing completion. See
+`bash-completion-use-separate-processes' for further
+explanation."
+ :type '(file :must-match t)
+ :group 'bash-completion)
+
+(defcustom bash-completion-remote-prog "bash"
+ "Name or path of the remote BASH executable to use.
+
+This is the path of an BASH executable available on the remote machine.
+Best is to just specify \"bash\" and rely on the PATH being set correctly
+for the remote connection.
+
+This variable is only used when creating separate processes for
+performing completion. See
+`bash-completion-use-separate-processes' for further
+explanation."
+ :type '(string)
+ :group 'bash-completion)
+
+(defcustom bash-completion-args '("--noediting")
+ "Args passed to the BASH shell.
+
+This variable is only used when creating separate processes for
+performing completion. See
+`bash-completion-use-separate-processes' for further
+explanation."
+ :type '(repeat (string :tag "Argument"))
+ :group 'bash-completion)
+
+(defcustom bash-completion-process-timeout 2.5
+ "Number of seconds to wait for an answer from bash.
+
+If bash takes longer than that to answer, the answer will be
+ignored."
+ :type '(float)
+ :group 'bash-completion)
+
+(defcustom bash-completion-command-timeout 30
+ "Number of seconds to wait for an answer from programmable
+completion functions.
+
+Programmable completion functions might take an arbitrary long
+time to run, so this should be long."
+ :type '(float)
+ :group 'bash-completion)
+
+(defcustom bash-completion-message-delay 0.4
+ "Time to wait before displaying a message while waiting for results.
+
+If completion takes longer than that time, a message is displayed
+on the minibuffer to make it clear what's happening. Set to nil
+to never display any such message. 0 to always display it.
+
+Only relevant when using bash completion in a shell, through
+`bash-completion-dynamic-complete'."
+ :type '(float)
+ :group 'bash-completion)
+
+(defcustom bash-completion-initial-timeout 30
+ "Timeout value to apply when talking to bash for the first time.
+
+The first thing bash is supposed to do is process /etc/bash_complete,
+which typically takes a long time.
+
+This variable is only used when creating separate processes for
+performing completion. See
+`bash-completion-use-separate-processes' for further
+explanation."
+ :type '(float)
+ :group 'bash-completion)
+
+(defcustom bash-completion-nospace nil
+ "Never let bash add a final space at the end of a completion.
+
+When there is only one completion candidate, bash sometimes adds
+a space at the end of the completion to move the cursor at the
+appropriate position to add more command-line arguments. This
+feature doesn't always work perfectly with programmable completion.
+
+Enable this option if you find yourself having to often backtrack
+to remove the extra space bash adds after a completion."
+ :type '(boolean)
+ :group 'bash-completion)
+
+(defvar bash-completion-start-files
+ '("~/.emacs_bash.sh" "~/.emacs.d/init_bash.sh")
+ "Shell files that, if they exist, will be sourced at the beginning of a bash completion subprocess.
+
+This variable is only used when creating separate processes for
+performing completion. See
+`bash-completion-use-separate-processes' for further
+explanation.")
+
+(defvar bash-completion-wordbreaks ""
+ "Extra wordbreaks to use when tokenizing, in `bash-completion-tokenize'.")
+
+(defvar bash-completion-output-buffer " *bash-completion*"
+ "Buffer storing completion results.
+
+This buffer is only used when creating separate processes for
+performing completion. See
+`bash-completion-use-separate-processes' for further
+explanation.")
+
+;;; ---------- Internal variables and constants
+
+(defvar bash-completion-processes nil
+ "Bash processes alist.
+
+Mapping between remote paths as returned by `file-remote-p' and
+Bash processes.")
+
+(defconst bash-completion-special-chars "[ -$&-*,:-<>?[-^`{-}]"
+ "Regexp of characters that must be escaped or quoted.")
+
+(defconst bash-completion--ps1 "'\t$?\v'"
+ "Value for the special PS1 prompt set for completions, quoted.")
+
+(eval-when-compile
+ (unless (or (and (= emacs-major-version 24) (>= emacs-minor-version 3))
+ (>= emacs-major-version 25))
+ (error
+ (concat
+ "Emacs version 24.3 or later is required to run emacs-bash-completion.\n"
+ "Download emacs-bash-completion version 2.1 to run on older Emacs "
+ "versions, from 22 to 24."))))
+
+(defvar bash-completion--debug-info nil
+ "Alist that stores info about the last call to `bash-completion-send'.
+
+Created by `bash-completion-send' and printed by
+`bash-completion-debug'.")
+
+;;; ---------- Struct
+
+;; The main, completion structure, keeping track of the definition and
+;; state of the current completion.
+(cl-defstruct (completion (:constructor bash-completion--make)
+ (:conc-name bash-completion--)
+ (:copier nil))
+ line ; the relevant command (string)
+ words ; line split into words, unescaped (list of strings)
+ cword ; 0-based index of the word to be completed in words (number)
+ unparsed-stub ; unparsed version of the thing we are completing,
+ ; that is, the part of the last word after the last
+ ; wordbreak separator.
+ stub-start ; start position of the thing we are completing
+ stub ; parsed version of the stub
+ open-quote ; quote open at stub end: nil, ?' or ?\""
+ compgen-args ; compgen arguments for this command (list of strings)
+ wordbreaks ; value of COMP_WORDBREAKS active for this completion
+ compopt ; options forced with compopt nil or `(nospace . ,bool)
+)
+
+(defun bash-completion--type (comp)
+ "Returns the type of COMP.
+
+Completion type is 'command, if completing a command (cword = 0),
+'custom if there's a custom completion for the current command or
+'default if there isn't or if the completion hasn't been
+customized, usually by `bash-completion--customize'.
+"
+ (cond
+ ((zerop (bash-completion--cword comp)) 'command)
+ ((bash-completion--compgen-args comp) 'custom)
+ (t 'default)))
+
+(defun bash-completion--nospace (comp)
+ "Returns the value of the nospace option to use for COMP.
+
+The option can be:
+ - set globally, by setting `bash-completion-nospace' to t
+ - set for a customized completion, in bash, with
+ '-o' 'nospace'."
+ (let ((cell))
+ (cond
+ (bash-completion-nospace t) ; set globally
+ ((setq cell (assq 'nospace (bash-completion--compopt comp)))
+ (cdr cell))
+ (t (bash-completion--has-compgen-option
+ (bash-completion--compgen-args comp) "nospace")))))
+
+(defun bash-completion--command (comp)
+ "Return the current command for the completion, if there is one."
+ (file-name-nondirectory (car (bash-completion--words comp))))
+
+(defun bash-completion--get-buffer (process)
+ "Return the buffer used to store completion results.
+
+PROCESS is the bash process from which completions are
+retrieved. When `bash-completion-use-separate-processes' is nil,
+PROCESS is not used and `bash-completion-output-buffer' is
+returned."
+ (if bash-completion-use-separate-processes
+ (process-buffer process)
+ (get-buffer-create bash-completion-output-buffer)))
+
+(defun bash-completion--setup-bash-common (process)
+ "Setup PROCESS to be ready for completion."
+ (let (bash-major-version)
+ (bash-completion-send "complete -p" process)
+ (process-put process 'complete-p
+ (bash-completion-build-alist (bash-completion--get-buffer process)))
+ (bash-completion-send "echo -n ${BASH_VERSINFO[0]}" process)
+ (setq bash-major-version
+ (with-current-buffer (bash-completion--get-buffer process)
+ (string-to-number (buffer-substring-no-properties
+ (point-min) (point-max)))))
+ (bash-completion-send
+ (concat "function __emacs_complete_wrapper {"
+ (if (>= bash-major-version 4)
+ " COMP_TYPE=9; COMP_KEY=9; _EMACS_COMPOPT=\"\";"
+ "")
+ " eval $__EMACS_COMPLETE_WRAPPER;"
+ " n=$?;"
+ " if [[ $n = 124 ]]; then"
+ (bash-completion--side-channel-data "wrapped-status" "124")
+ " return 1; "
+ " fi; "
+ (when (>= bash-major-version 4)
+ (concat
+ " if [[ -n \"${_EMACS_COMPOPT}\" ]]; then"
+ (bash-completion--side-channel-data "compopt" "${_EMACS_COMPOPT}")
+ " fi;"))
+ " return $n;"
+ "}")
+ process)
+ (if (>= bash-major-version 4)
+ (bash-completion-send
+ (concat
+ "function compopt {"
+ " command compopt \"$@\" 2>/dev/null;"
+ " ret=$?; "
+ " if [[ $ret == 1 && \"$*\" = *\"-o nospace\"* ]]; then"
+ " _EMACS_COMPOPT='-o nospace';"
+ " return 0;"
+ " fi;"
+ " if [[ $ret == 1 && \"$*\" = *\"+o nospace\"* ]]; then"
+ " _EMACS_COMPOPT='+o nospace';"
+ " return 0;"
+ " fi;"
+ " return $ret; "
+ "}")
+ process))
+
+ ;; some bash completion functions use quote_readline
+ ;; to double-quote strings - which compgen understands
+ ;; but only in some environment. disable this dreadful
+ ;; business to get a saner way of handling spaces.
+ ;; Noticed in bash_completion v1.872.
+ (bash-completion-send "function quote_readline { echo \"$1\"; }" process)
+
+ (bash-completion-send "echo -n ${COMP_WORDBREAKS}" process)
+ (process-put process 'wordbreaks
+ (with-current-buffer (bash-completion--get-buffer process)
+ (buffer-substring-no-properties
+ (point-min) (point-max))))
+ (process-put process 'bash-major-version bash-major-version)
+
+ (bash-completion-send "bind -v 2>/dev/null" process)
+ (process-put process 'completion-ignore-case
+ (with-current-buffer (bash-completion--get-buffer process)
+ (save-excursion
+ (goto-char (point-min))
+ (and (search-forward "completion-ignore-case on" nil 'noerror) t))))
+
+ (process-put process 'setup-done t)))
+
+;;; ---------- Inline functions
+
+(defsubst bash-completion-tokenize-get-range (token)
+ "Return the TOKEN range as a cons: (start . end)."
+ (cdr (assq 'range token)))
+
+(defsubst bash-completion-tokenize-set-end (token)
+ "Set the end position of TOKEN to the cursor position."
+ (setcdr (bash-completion-tokenize-get-range token) (point)))
+
+(defsubst bash-completion-tokenize-append-str (token str)
+ "Append to TOKEN the string STR."
+ (let ((str-cons (assq 'str token)))
+ (setcdr str-cons (concat (cdr str-cons) str))))
+
+(defsubst bash-completion-tokenize-get-str (token)
+ "Return the TOKEN string."
+ (cdr (assq 'str token)))
+
+(defsubst bash-completion-tokenize-open-quote (tokens)
+ "Return the quote character that was still open in the last token.
+
+TOKENS is a list of token as returned by
+`bash-completion-tokenize'."
+ (cdr (assq 'quote (car (last tokens)))))
+
+;;; ---------- Functions: completion
+
+;;;###autoload
+(defun bash-completion-setup ()
+ "Register bash completion for the shell buffer and shell command line.
+
+This function adds `bash-completion-dynamic-complete' to the completion
+function list of shell mode, `shell-dynamic-complete-functions'.
+
+This function is convenient, but it might not be the best way of enabling
+bash completion in your .emacs file because it forces you to load the module
+before it is needed. For an autoload version, add:
+
+ (autoload 'bash-completion-dynamic-complete \"bash-completion\"
+ \"BASH completion hook\")
+ (add-hook 'shell-dynamic-complete-functions
+ 'bash-completion-dynamic-complete)
+"
+ (add-hook 'shell-dynamic-complete-functions
+ 'bash-completion-dynamic-complete))
+
+;;;###autoload
+(defun bash-completion-dynamic-complete ()
+ "Return the completion table for bash command at point.
+
+This function is meant to be added into
+`shell-dynamic-complete-functions'. It uses `comint' to figure
+out what the current command is and returns a completion table or
+nil if no completions available.
+
+When doing completion outside of a comint buffer, call
+`bash-completion-dynamic-complete-nocomint' instead."
+ (let ((message-timer
+ (if (and (not (window-minibuffer-p))
+ (not (null bash-completion-message-delay)))
+ (run-at-time
+ bash-completion-message-delay nil
+ (lambda () (message "Bash completion..."))))))
+ (unwind-protect
+ (bash-completion-dynamic-complete-nocomint
+ (comint-line-beginning-position)
+ (point)
+ 'dynamic-table)
+ ;; cleanup
+ (if message-timer
+ (cancel-timer message-timer)))))
+
+;;;###autoload
+(defun bash-completion-dynamic-complete-nocomint
+ (comp-start &optional comp-pos dynamic-table)
+ "Return completion information for bash command at an arbitrary position.
+
+The bash command to be completed begins at COMP-START in the
+current buffer. This must specify where the current command
+starts, usually right after the prompt.
+
+COMP-POS is the point where completion should happen, usually
+just (point). Note that a bash command can span across multiple
+line, so COMP-START is not necessarily on the same line as
+COMP-POS.
+
+This function does not assume that the current buffer is a shell
+or even comint buffer. It can safely be called from any buffer
+where a bash command appears, including `completion-at-point'.
+
+If DYNAMIC-TABLE is passed a non-nil value, the resulting
+collection will be a function that fetches the result lazily,
+when it's called.
+
+When calling from `completion-at-point', make sure to pass a
+non-nil value to DYNAMIC-TABLE. This isn't just an optimization:
+returning a function instead of a list tells Emacs it should
+avoids post-filtering the results and possibly discarding useful
+completion from bash.
+
+When calling from another completion engine, make sure to treat
+the returned completion as reliable and not post-process them
+further.
+
+Returns (list stub-start stub-end completions) with
+ - stub-start, the position at which the completed region starts
+ - stub-end, the position at which the completed region ends
+ - completions, a possibly empty list of completion candidates
+ or a function, if DYNAMIC-TABLE is non-nil, a lambda such as the one
+ returned by `completion-table-dynamic'"
+ (when bash-completion-enabled
+ (let ((comp-start (or comp-start (line-beginning-position)))
+ (comp-pos (or comp-pos (point)))
+ (bash-completion-use-separate-processes
+ bash-completion-use-separate-processes)
+ (process (bash-completion--get-process)))
+ (when (and (not process) (not bash-completion-use-separate-processes))
+ ;; no process associated with the current buffer, create a
+ ;; separate completion process
+ (setq bash-completion-use-separate-processes t)
+ (setq process (bash-completion--get-process)))
+ (let* ((comp (bash-completion--parse
+ comp-start comp-pos
+ (process-get process 'wordbreaks)
+ (process-get process 'bash-major-version)))
+ (stub-start (bash-completion--stub-start comp)))
+
+ (bash-completion--customize comp process)
+ (list
+ stub-start
+ comp-pos
+ (if dynamic-table
+ (bash-completion--completion-table-with-cache
+ comp process)
+ (bash-completion-comm comp process)))))))
+
+(defun bash-completion--find-last (elt array)
+ "Return the position of the last instance of ELT in array or nil."
+ (catch 'bash-completion-return
+ (let ((array-len (length array)))
+ (dotimes (index array-len)
+ (if (eq elt (aref array (- array-len index 1)))
+ (throw 'bash-completion-return (- array-len index 1)))))
+ nil))
+
+;;; ---------- Functions: parsing and tokenizing
+
+(defun bash-completion-join (words)
+ "Join WORDS into a shell command line.
+
+All words that contain even mildly suspicious characters are
+quoted using single quotes to avoid the shell interpreting them
+when it shouldn't.
+
+Return one string containing WORDS."
+ (if words
+ (mapconcat
+ 'bash-completion-quote
+ words " ")
+ ""))
+
+(defun bash-completion-quote (word)
+ "Put single quotes around WORD unless it's crearly unnecessary.
+
+If WORD contains characters that aren't known to be harmless, this
+functions adds single quotes around it and return the result."
+ (cond
+ ((string= "" word)
+ "''")
+ ((string-match "^[a-zA-Z0-9_./-]*$" word)
+ word)
+ (t
+ (concat "'"
+ (replace-regexp-in-string "'" "'\\''" word nil t)
+ "'"))))
+
+(defun bash-completion--parse (comp-start comp-pos wordbreaks bash-major-version)
+ "Process a command from COMP-START to COMP-POS.
+
+WORDBREAK is the value of COMP_WORDBREAKS to use for this completion,
+usually taken from the current process.
+
+Returns a completion struct."
+ (let* ((all-tokens (bash-completion-tokenize
+ comp-start comp-pos (if (>= bash-major-version 4)
+ wordbreaks "")))
+ (line-tokens (bash-completion-parse-current-command all-tokens))
+ (first-token (car line-tokens))
+ (last-token (car (last line-tokens)))
+ (open-quote (bash-completion-tokenize-open-quote line-tokens))
+ (start (or (car (bash-completion-tokenize-get-range first-token)) comp-pos))
+ (end (or (cdr (bash-completion-tokenize-get-range last-token)) comp-pos))
+ (words (bash-completion-strings-from-tokens line-tokens))
+ (rebuilt-line) (stub-start) (unparsed-stub) (parsed-stub))
+ ;; Note about rebuilt-line: When using readline, line and words
+ ;; would be passed unquoted to the functions. This doesn't work,
+ ;; however, when called from Emacs as when readline 'compgen -f'
+ ;; behaves differently and does not unquote the string it's
+ ;; passed. This is why words and the last word of the line are
+ ;; passed unquoted. This makes the standard bash completion
+ ;; scripts work - possibly at the cost of more inconsistencies
+ ;; with other scripts.
+ (if (or (> comp-pos end) (= start end))
+ (setq stub-start comp-pos
+ unparsed-stub ""
+ parsed-stub ""
+ words (append words '(""))
+ rebuilt-line (buffer-substring-no-properties start comp-pos))
+ (if (< bash-major-version 4)
+ (setq last-token (car (last (bash-completion-tokenize
+ start comp-pos wordbreaks)))))
+ (setq stub-start (car (bash-completion-tokenize-get-range last-token))
+ parsed-stub (bash-completion-tokenize-get-str last-token)
+ unparsed-stub (buffer-substring-no-properties stub-start comp-pos)
+ rebuilt-line (concat
+ (buffer-substring-no-properties
+ start (car (cdr (assq 'range (car (last line-tokens))))))
+ (cdr (assq 'str (car (last line-tokens)))))))
+ (bash-completion--make
+ :line rebuilt-line
+ :cword (- (length words) 1)
+ :words words
+ :stub-start stub-start
+ :unparsed-stub unparsed-stub
+ :stub parsed-stub
+ :open-quote open-quote
+ :wordbreaks wordbreaks)))
+
+(defun bash-completion-parse-current-command (tokens)
+ "Extract from TOKENS the tokens forming the current command.
+
+This function takes a list of TOKENS created by
+`bash-completion-tokenize' for the current buffer and select the
+tokens on this list that form the current command given that the
+word to be completed is the last token.
+
+For example, given this stream of tokens:
+ cd /var/tmp && ls -l
+if the last token is -l, it will select:
+ ls -l
+if the last token is /var/tmp, it will select:
+ cd /var/tmp
+
+Return a sublist of TOKENS."
+ (nreverse
+ (catch 'bash-completion-return
+ (let ((command nil) (state 'initial))
+ (dolist (token tokens)
+ (let* ((string (bash-completion-tokenize-get-str token))
+ (is-terminal
+ (and (member string '(";" "&" "|" "&&" "||" "\n"))
+ (let ((range (bash-completion-tokenize-get-range token)))
+ (= (- (cdr range) (car range))
+ (length string))))))
+ (cond
+ (is-terminal
+ (setq state 'initial)
+ (setq command nil))
+
+ ((and (eq state 'initial)
+ (null (string-match "=" string)))
+ (setq state 'args)
+ (push token command))
+
+ ((eq state 'args)
+ (push token command)))))
+ (or command (last tokens))))))
+
+(defun bash-completion-strings-from-tokens (tokens)
+ "Extract the strings from TOKENS.
+
+This function takes all strings from TOKENS and return it as a
+list of strings.
+
+TOKENS should be in the format returned by `bash-completion-tokenize'."
+ (mapcar 'bash-completion-tokenize-get-str tokens))
+
+(defun bash-completion-tokenize (start end &optional wordbreaks)
+ "Tokenize the portion of the current buffer between START and END.
+
+This function splits a BASH command line into tokens. It knows
+about quotes, escape characters and special command separators such
+as ;, | and &&. If specified WORDBREAKS contains extra word breaks,
+usually taken from COMP_WORDBREAKS, to apply while tokenizing.
+
+This method returns a list of tokens found between START and END,
+ordered by position. Tokens contain a string and a range.
+
+The string in a token is an unescaped version of the token. For
+example, if the token is 'hello world', the string contains
+\"hello world\", without the quotes. It can be accessed using
+`bash-completion-tokenize-get-str'. It can be modified using
+`bash-completion-tokenize-append-str'.
+
+The range is a cons containing the start and end position of the
+token (start . end). Start is the position of the first character
+that belongs to the token. End is the position of the first
+character that doesn't belong to the token. For example in the
+string \" hello world \", the first token range is (2 . 7) and
+the second token range (9 . 14). It can be accessed using
+`bash-completion-tokenize-get-range'. The end position can be
+set using `bash-completion-tokenize-set-end'.
+
+Tokens should always be accessed using the functions specified above,
+never directly as they're likely to change as this code evolves.
+The current format of a token is '(string . (start . end))."
+ (let ((tokens nil)
+ (bash-completion-wordbreaks
+ (mapconcat 'char-to-string
+ (delq nil (mapcar
+ (lambda (c)
+ (if (memq c '(?\; ?& ?| ?' ?\")) nil c))
+ (or wordbreaks "")))
+ "")))
+ (save-excursion
+ (goto-char start)
+ (while (progn (skip-chars-forward " \t\r" end)
+ (< (point) end))
+ (setq tokens
+ (bash-completion-tokenize-new-element end tokens)))
+ (nreverse tokens))))
+
+(defun bash-completion-tokenize-new-element (limit tokens)
+ "Tokenize an element from point, up until LIMIT and complete TOKENS.
+
+This function is meant to be called exclusively from
+`bash-completion-tokenize' and `bash-completion-tokenize-0'.
+
+This function expects the point to be at the start of a new
+element to be added to the list of tokens. It parses the line
+until the limit of that element or until LIMIT.
+
+It leaves the point at the position where parsing should
+continue.
+
+Return TOKENS with new tokens prepended."
+ (skip-chars-forward " \t\r" limit)
+ (if (eq ?\n (char-after))
+ (progn
+ (goto-char (1+ (point)))
+ (cons `((str . "\n") (range ,(point) . ,(1+ (point)))) tokens))
+ (bash-completion-tokenize-0
+ limit tokens
+ (list
+ (cons 'str "")
+ (cons 'range (cons (point) nil))))))
+
+(defun bash-completion-tokenize-0 (end tokens token)
+ "Tokenize the rest of the token until END and add it into TOKENS.
+
+This function is meant to be called exclusively from
+`bash-completion-tokenize-new-element'.
+
+This function expect the point to be at the start of a new token
+section, either at the start of the token or just after a quote
+has been closed in the token. It detects new opening quotes and
+calls `bash-completion-tokenize-1'.
+
+END specifies the point at which tokenization should stop.
+
+TOKENS is the list of tokens built so farin reverse order.
+
+TOKEN is the token currently being built.
+
+Return TOKENS with new tokens prepended to it."
+ (let ((char-start (char-after))
+ (quote nil) )
+ (when (and char-start (or (= char-start ?') (= char-start ?\")))
+ (forward-char)
+ (setq quote char-start))
+ (bash-completion-tokenize-1 end quote tokens token)))
+
+(defun bash-completion-tokenize-1 (end quote tokens token)
+ "Tokenize the rest of the token.
+
+This function is meant to be called exclusively from
+`bash-completion-tokenize-0'.
+
+This function tokenizes the rest of the token and either calls
+itself and `bash-completion-tokenize-0' recursively or appends
+the token to the list of token and calls
+`bash-completion-tokenize-new-element' to look for the next
+token.
+
+END specifies the point at which tokenization should stop, even
+if the token is not complete.
+
+QUOTE specifies the current quote. It should be nil, ?' or ?\"
+
+TOKENS is the list of tokens built so far in reverse order.
+
+TOKEN is the token currently being built.
+
+Sets the point at the position of the next token. Returns TOKENS
+with new tokens prepended to it."
+ ;; parse the token elements at the current position and
+ ;; append them
+ (let ((local-start (point)))
+ (when (= (skip-chars-forward
+ (concat "[;&|" bash-completion-wordbreaks "]")
+ end)
+ 0)
+ (skip-chars-forward
+ (bash-completion-nonsep quote bash-completion-wordbreaks) end))
+ (bash-completion-tokenize-append-str
+ token
+ (buffer-substring-no-properties local-start (point))))
+ (cond
+ ;; an escaped char, skip, whatever it is
+ ((and (char-before) (= ?\\ (char-before)))
+ (forward-char)
+ (let ((str (bash-completion-tokenize-get-str token)))
+ (aset str (1- (length str)) (char-before)))
+ (bash-completion-tokenize-1 end quote tokens token))
+ ;; opening quote
+ ((and (not quote) (char-after) (or (= ?' (char-after)) (= ?\" (char-after))))
+ (bash-completion-tokenize-0 end tokens token))
+ ;; closing quote
+ ((and quote (char-after) (= quote (char-after)))
+ (forward-char)
+ (bash-completion-tokenize-0 end tokens token))
+ ;; inside a quote
+ ((and quote (char-after) (not (= quote (char-after))))
+ (forward-char)
+ (bash-completion-tokenize-append-str token (char-to-string (char-before)))
+ (bash-completion-tokenize-1 end quote tokens token))
+ ;; word end
+ (t
+ (bash-completion-tokenize-set-end token)
+ (when quote
+ (push (cons 'quote quote) token))
+ (push token tokens))))
+
+(defun bash-completion-nonsep (quote wordbreaks)
+ "Return the set of non-breaking characters when QUOTE is the current quote.
+
+QUOTE should be nil, ?' or ?\"."
+ (concat
+ "^ \t\n\r"
+ (if (null quote) (concat ";&|'\"" wordbreaks)
+ (char-to-string quote))))
+
+;;; ---------- Functions: getting candidates from bash
+
+(defun bash-completion-comm (comp process)
+ "Call compgen on COMP for PROCESS, return the result.
+
+COMP should be a struct returned by `bash-completion--parse'
+
+This function starts a separate bash process if necessary, sets
+up the completion environment (COMP_LINE, COMP_POINT, COMP_WORDS,
+COMP_CWORD) and calls compgen.
+
+The result is a list of candidates, which might be empty."
+ (let* ((buffer (bash-completion--get-buffer process))
+ (cmd-timeout (if (eq 'custom (bash-completion--type comp))
+ bash-completion-command-timeout
+ bash-completion-process-timeout))
+ (completion-status))
+ (setq completion-status (bash-completion-send
+ (bash-completion-generate-line comp)
+ process cmd-timeout comp))
+ (when (eq 124 completion-status)
+ ;; Special 'retry-completion' exit status, typically returned by
+ ;; functions bound by complete -D. Presumably, the function has
+ ;; just setup completion for the current command and is asking
+ ;; us to retry once with the new configuration.
+ (bash-completion-send "complete -p" process cmd-timeout comp)
+ (process-put process 'complete-p (bash-completion-build-alist buffer))
+ (bash-completion--customize comp process 'nodefault)
+ (setq completion-status (bash-completion-send
+ (bash-completion-generate-line comp)
+ process cmd-timeout comp)))
+ (when (eq 0 completion-status)
+ (bash-completion-extract-candidates comp buffer))))
+
+(defun bash-completion-extract-candidates (comp buffer)
+ "Extract the completion candidates for COMP form BUFFER.
+
+This command takes the content of the completion process buffer,
+splits it by newlines, post-process the candidates and returns
+them as a list of strings.
+
+It should be invoked with the comint buffer as the current buffer
+for directory name detection to work.
+
+Post-processing includes escaping special characters, adding a /
+to directory names, replacing STUB with UNPARSED-STUB in the
+result. See `bash-completion-fix' for more details."
+ (let ((output) (candidates) (pwd))
+ (with-current-buffer buffer
+ (setq pwd (bash-completion--parse-side-channel-data "pwd"))
+ (let ((compopt (bash-completion--parse-side-channel-data "compopt")))
+ (cond
+ ((string= "-o nospace" compopt)
+ (setf (bash-completion--compopt comp) '((nospace . t))))
+ ((string= "+o nospace" compopt)
+ (setf (bash-completion--compopt comp) '((nospace . nil))))))
+ (setq output (buffer-string)))
+ (setq candidates (delete-dups (split-string output "\n" t)))
+ (let ((default-directory (if pwd
+ (concat (file-remote-p default-directory) pwd)
+ default-directory)))
+ (if (eq 1 (length candidates))
+ (list (bash-completion-fix (car candidates) comp t))
+ ;; multiple candidates
+ (let ((result (list)))
+ (dolist (completion candidates)
+ (push (bash-completion-fix completion comp nil) result))
+ (delete-dups (nreverse result)))))))
+
+(defun bash-completion-fix (str comp single)
+ "Fix completion candidate in STR for COMP
+
+STR is the completion candidate to modify, COMP the current
+completion operation.
+
+If STR is the single candidate, SINGLE is t.
+
+Return a modified version of STR.
+
+Modification include:
+ - escaping of special characters in STR
+ - prepending the stub if STR does not contain all of it, when
+ completion was done after a wordbreak
+ - adding / to recognized directory names
+
+It should be invoked with the comint buffer as the current buffer
+for directory name detection to work."
+ (let ((parsed-prefix (bash-completion--stub comp))
+ (unparsed-prefix (bash-completion--unparsed-stub comp))
+ (open-quote (bash-completion--open-quote comp))
+ (nospace (bash-completion--nospace comp))
+ (wordbreaks (bash-completion--wordbreaks comp))
+ (suffix "")
+ (rest) ; the part between the prefix and the suffix
+ (rebuilt))
+
+ ;; build rest by removing parsed-prefix from str
+ (cond
+ ((bash-completion-starts-with str parsed-prefix)
+ (setq rest (substring str (length parsed-prefix))))
+
+ ;; completion sometimes only applies to the last word, as
+ ;; defined by COMP_WORDBREAKS. This detects and works around
+ ;; this feature.
+ ((bash-completion-starts-with
+ (setq rebuilt (concat (bash-completion-before-last-wordbreak parsed-prefix wordbreaks) str))
+ parsed-prefix)
+ (setq rest (substring rebuilt (length parsed-prefix))))
+
+ ;; there is no meaningful link between the prefix and the string.
+ ;; Bypass the whole prefix/suffix logic and replace the string
+ ;; being completed with the string provided by the completion
+ ;; logic.
+ ((string-match "^~.*?\\($\\|/\\)" str)
+ (setq parsed-prefix (substring str 0 (match-end 0))
+ unparsed-prefix
+ (concat (substring str 0 (match-end 0))
+ (if open-quote (char-to-string open-quote) ""))
+ rest (substring str (match-end 0))))
+
+ (t
+ (setq parsed-prefix ""
+ unparsed-prefix (if open-quote (char-to-string open-quote) "")
+ rest str)))
+
+ ;; build suffix
+ (let ((last-char (bash-completion-last-char rest))
+ (close-quote-str (if open-quote (char-to-string open-quote) ""))
+ (final-space-str (if nospace "" " ")))
+ (cond
+ ((eq ?\ last-char)
+ (setq rest (substring rest 0 -1))
+ (setq suffix (concat close-quote-str final-space-str)))
+ ((or (bash-completion--find-last last-char wordbreaks)
+ (eq ?/ last-char))
+ (setq suffix ""))
+ ((file-accessible-directory-p
+ (bash-completion--expand-file-name (bash-completion-unescape
+ open-quote (concat parsed-prefix rest))))
+ (setq suffix "/"))
+ (single
+ (setq suffix (concat close-quote-str final-space-str)))
+ (t (setq suffix close-quote-str))))
+
+ ;; put everything back together
+ (concat unparsed-prefix
+ (bash-completion-escape-candidate rest open-quote)
+ suffix)))
+
+(defun bash-completion-escape-candidate (completion-candidate open-quote)
+ "Escapes COMPLETION-CANDIDATE.
+
+This function escapes all special characters in the result of
+bash completion. It does nothing if COMPLETION-CANDIDATE looks
+like a quoted string.
+
+It uses escape characters appropriate for the quote defined in
+OPEN-QUOTE, either nil, ' or \".
+
+Return a possibly escaped version of COMPLETION-CANDIDATE."
+ (cond
+ ((zerop (length completion-candidate)) "")
+ ((null open-quote)
+ (replace-regexp-in-string
+ "\n" "'\n'"
+ (replace-regexp-in-string
+ bash-completion-special-chars "\\\\\\&" completion-candidate)))
+ ((eq ?' open-quote)
+ (replace-regexp-in-string "'" "'\\''" completion-candidate nil t))
+ ((eq ?\" open-quote)
+ ;; quote '$', '`' or '"'
+ (replace-regexp-in-string
+ "[$`\"]" "\\\\\\&"
+ ;; quote backslash if it's followed by '$', '`' or '"'
+ (replace-regexp-in-string "\\\\\\([$`\"]\\)" "\\\\\\\\\\1" completion-candidate)))
+ (t
+ completion-candidate)))
+
+(defun bash-completion-unescape (open-quote string)
+ "Unescapes a possibly QUOTE'ed STRING."
+ (if (eq ?' open-quote)
+ (replace-regexp-in-string "'\\\\''" "'" string)
+ (replace-regexp-in-string "\\(\\\\\\)\\(.\\)" "\\2" string)))
+
+(defun bash-completion-before-last-wordbreak (str wordbreaks)
+ "Return the part of STR that comes after the last WORDBREAKS character.
+The return value does not include the worbreak character itself.
+
+If no wordbreak was found, it returns STR."
+ (nth 0 (bash-completion-last-wordbreak-split str wordbreaks)))
+
+(defun bash-completion-last-wordbreak-split (str wordbreaks)
+ "Split STR at the last WORDBREAKS character.
+
+The part before the last wordbreak character includes the
+wordbreak character itself. It is \"\" if no wordbreak character
+was found.
+
+The part after the last wordbreak character does not include the
+wordbreak character. It is STR if no wordbreak character was
+found.
+
+Return a CONS containing (before . after)."
+ (catch 'bash-completion-return
+ (let ((end (- (length str) 1)))
+ (while (>= end 0)
+ (when (bash-completion--find-last (aref str end) wordbreaks)
+ (throw 'bash-completion-return
+ (list (substring str 0 (1+ end))
+ (substring str (1+ end))
+ (aref str end))))
+ (setq end (1- end))))
+ (list "" str ?\0)))
+
+(defun bash-completion-last-char (str)
+ "Returns the last char of STR or nil."
+ (let ((str-len (length str)))
+ (and (>= str-len 1)
+ (aref str (1- str-len)))))
+
+(defun bash-completion-starts-with (str prefix)
+ "Return t if STR starts with PREFIX."
+ (let ((prefix-len (length prefix))
+ (str-len (length str)))
+ (and
+ (>= str-len prefix-len)
+ (string= (substring str 0 prefix-len) prefix))))
+
+;;; ---------- Functions: bash subprocess
+
+(defun bash-completion--get-or-create-separate-process ()
+ "Return the bash completion process or start it.
+
+If a bash completion process is already running, return it.
+
+Otherwise, create a bash completion process and return the
+result. This can take a since bash needs to start completely
+before this function returns to be sure everything has been
+initialized correctly.
+
+The process uses `bash-completion-prog' to figure out the path to
+bash on the current system.
+
+To get an environment consistent with shells started with `shell',
+the first file found in the following list are sourced if they exist:
+ ~/.emacs_bash.sh
+ ~/.emacs.d/init_bash.sh
+Or, to be more exact, ~/.emacs_$(basename `bash-completion-prog').sh)
+and ~/.emacs.d/init_$(basename `bash-completion-prog').sh)
+
+To allow scripts to tell the difference between shells launched
+by bash-completion, the environment variable EMACS_BASH_COMPLETE
+is set to t."
+ (let ((remote (file-remote-p default-directory)))
+ (if (bash-completion-is-running)
+ (cdr (assoc remote bash-completion-processes))
+ ;; start process
+ (let (process (oldterm (getenv "TERM")) (cleanup t))
+ (unwind-protect
+ (progn
+ (setenv "TERM" "dumb")
+ (setenv "EMACS_BASH_COMPLETE" "t")
+ (let* ((start-proc-fun (if remote #'start-file-process #'start-process))
+ (buffer-name (generate-new-buffer-name " bash-completion"))
+ (args `("*bash-completion*"
+ ,buffer-name
+ ,(if remote bash-completion-remote-prog bash-completion-prog)
+ ,@bash-completion-args)))
+ (when remote
+ ;; See http://lists.gnu.org/archive/html/tramp-devel/2016-05/msg00004.html
+ (get-buffer-create buffer-name))
+ (let ((non-essential (if remote nil non-essential)))
+ ;; Set `non-essential' to nil when spawning a remote
+ ;; shell to ensure that Tramp will try to open a
+ ;; connection to the remote host. Otherwise the
+ ;; process will be launched on the localhost. This
+ ;; is need because some completion framework (e.g
+ ;; company) set `non-essential' to a non-nil value
+ ;; when the completion has not been requested by the
+ ;; user
+ (setq process (apply start-proc-fun args))))
+ (set-process-query-on-exit-flag process nil)
+ (if remote
+ ;; Set EMACS_BASH_COMPLETE now for remote
+ ;; completion, since setenv doesn't work. This will
+ ;; unfortunately not be available in .bashrc or
+ ;; .bash_profile. TODO: Find a way of getting it to
+ ;; work from the very beginning.
+ (process-send-string process "EMACS_BASH_COMPLETE=t\n"))
+ (dolist (start-file bash-completion-start-files)
+ (when (file-exists-p (bash-completion--expand-file-name start-file))
+ (process-send-string process (concat ". " start-file "\n"))))
+ (process-send-string
+ process
+ (concat
+ ;; attempt to turn off unexpected status messages from
+ ;; bash if the current version of bash does not
+ ;; support these options, the commands will fail
+ ;; silently and be ignored.
+ "shopt -u checkjobs;"
+ "shopt -u mailwarn;"
+ "export MAILCHECK=-1;"
+ "export -n MAIL;"
+ "export -n MAILPATH;"
+ "unset HISTFILE;"
+ ;; User's profiles can turn line editing back on,
+ ;; so make sure it's off
+ "set +o emacs;"
+ "set +o vi\n"))
+
+ (bash-completion-send
+ (concat "PROMPT_COMMAND='' PS1=" bash-completion--ps1)
+ process bash-completion-initial-timeout)
+ (bash-completion--setup-bash-common process)
+ (push (cons remote process) bash-completion-processes)
+ (setq cleanup nil)
+ process)
+ ;; finally
+ (progn
+ (setenv "EMACS_BASH_COMPLETE" nil)
+ (setenv "TERM" oldterm)
+ (when cleanup
+ (condition-case nil
+ (bash-completion-kill process)
+ (error nil)))))))))
+
+(defun bash-completion--current-shell ()
+ "Figure out what the shell associated with the current buffer is."
+ (let ((prog (or
+ (if (derived-mode-p 'shell-mode)
+ (or explicit-shell-file-name
+ (getenv "ESHELL")
+ shell-file-name))
+ (let ((process (get-buffer-process (current-buffer))))
+ (when process
+ (car (process-command process)))))))
+ (when prog
+ (file-name-nondirectory prog))))
+
+(defun bash-completion--get-same-process ()
+ "Return the BASH process associated with the current buffer.
+
+Return nil if the current buffer is not a comint buffer or is not
+associated with a command that looks like a bash shell.
+Completion will fallback to creating a separate process
+completion in these cases."
+ (when (derived-mode-p 'comint-mode)
+ (let* ((process (get-buffer-process (current-buffer)))
+ (shell (if process (bash-completion--current-shell))))
+ (when (and shell (bash-completion-starts-with shell "bash"))
+ (unless (process-get process 'setup-done)
+ ;; The following disables the emacs and vi options. This
+ ;; cannot be done by bash-completion-send as these options
+ ;; interfere with bash-completion-send detecting the end
+ ;; of a command. It disables prompt to avoid interference
+ ;; from commands run by prompts.
+ (comint-send-string
+ process
+ (concat
+ "set +o emacs;"
+ "set +o vi;"
+ "if [[ -z \"$__emacs_complete_ps1\" ]]; then"
+ " __emacs_complete_ps1=\"$PS1\";"
+ " __emacs_complete_pc=\"$PROMPT_COMMAND\";"
+ "fi;"
+ "PS1='' PROMPT_COMMAND='';"
+ "history &>/dev/null -d $((HISTCMD - 1)) || true\n"))
+
+ ;; The following is a bootstrap command for
+ ;; bash-completion-send itself.
+ (bash-completion-send
+ (concat
+ "function __emacs_complete_pre_command {"
+ " if [[ -z \"$__emacs_complete_ps1\" ]]; then"
+ " __emacs_complete_ps1=\"$PS1\";"
+ " __emacs_complete_pc=\"$PROMPT_COMMAND\";"
+ " fi;"
+ " PROMPT_COMMAND=__emacs_complete_prompt;"
+ " history &>/dev/null -d $((HISTCMD - 1)) || true;"
+ "} &&"
+ "function __emacs_complete_prompt {"
+ " PS1=" bash-completion--ps1 ";"
+ " PROMPT_COMMAND=__emacs_complete_recover_prompt;"
+ "} &&"
+ "function __emacs_complete_recover_prompt {"
+ " local r=$?;"
+ " PS1=\"${__emacs_complete_ps1}\";"
+ " PROMPT_COMMAND=\"${__emacs_complete_pc}\";"
+ " unset __emacs_complete_ps1 __emacs_complete_pc;"
+ " if [[ -n \"$PROMPT_COMMAND\" ]]; then"
+ " (exit $r); eval \"$PROMPT_COMMAND\";"
+ " fi;"
+ "} &&"
+ "__emacs_complete_pre_command")
+ process)
+ (bash-completion--setup-bash-common process))
+ process))))
+
+(defun bash-completion--get-process ()
+ "Setup and return a bash completion process.
+
+If `bash-completion-use-separate-processes' is non-nil,
+`bash-completion--get-or-create-separate-process' is called to
+get the process, otherwise `bash-completion--get-same-process' is
+used. "
+ (if bash-completion-use-separate-processes
+ (bash-completion--get-or-create-separate-process)
+ (bash-completion--get-same-process)))
+
+(defun bash-completion-cd-command-prefix ()
+ "Build a command-line that CD to default-directory.
+
+Return a bash command-line for going to default-directory or \"\"."
+ (let ((dir (or (file-remote-p (or default-directory "") 'localname)
+ default-directory)))
+ (if dir
+ (concat "cd >/dev/null 2>&1 "
+ (bash-completion-quote (bash-completion--expand-file-name dir t))
+ " && ")
+ "")))
+
+(defun bash-completion-build-alist (buffer)
+ "Parse the content of BUFFER into an alist.
+
+BUFFER should contains the output of:
+ complete -p
+
+The returned alist is a slightly parsed version of the output of
+\"complete -p\"."
+ (let ((alist (list)))
+ (with-current-buffer buffer
+ (save-excursion
+ (goto-char (point-min))
+ (when (search-forward-regexp "^complete" nil 'noerror)
+ (let ((tokens (bash-completion-strings-from-tokens
+ (bash-completion-tokenize
+ (match-beginning 0) (point-max)))))
+ (while tokens
+ (let ((command tokens)
+ (command-end (member "\n" tokens)))
+ (setq tokens (cdr command-end))
+ (when command-end
+ (setcdr command-end nil))
+ (when (string= "complete" (car command))
+ (setq command (nreverse (cdr command)))
+ (when (equal "\n" (car command))
+ (setq command (cdr command)))
+ (if (member "-D" command)
+ ;; default completion
+ (push (cons nil (nreverse (delete "-D" command))) alist)
+ ;; normal completion
+ (let ((command-name (car command))
+ (options (nreverse (cdr command))))
+ (when (and command-name options)
+ (push (cons command-name options) alist)))))))))))
+ (nreverse alist)))
+
+(defun bash-completion--customize (comp process &optional nodefault)
+ (unless (eq 'command (bash-completion--type comp))
+ (let ((compgen-args-alist
+ (process-get process 'complete-p))
+ (command-name (bash-completion--command comp)))
+ ;; TODO: first lookup the full command path, then only the
+ ;; command name.
+ (setf (bash-completion--compgen-args comp)
+ (or (cdr (assoc command-name compgen-args-alist))
+ (and (not nodefault) (cdr (assoc nil compgen-args-alist))))))))
+
+(defun bash-completion-generate-line (comp)
+ "Generate a command-line that calls compgen for COMP.
+
+COMP is a struct returned by `bash-completion--parse'. It is
+normally configured using `bash-completion--customize' before
+calling this command.
+
+If the compgen argument set specifies a custom function or command, the
+arguments will be passed to this function or command as:
+ COMP_LINE, taken from (bash-completion--line COMP)
+ COMP_POINT, taken from (bash-completion--point COMP)
+ COMP_WORDS, taken from (bash-completion--words COMP) (a bash array)
+ COMP_CWORD, taken for (bash-completion--cword COMP)
+
+Return a cons containing the completion type (command default or
+custom) and a bash command-line that calls compgen to get the
+completion candidates."
+ (let ((quoted-stub (bash-completion-quote (bash-completion--stub comp)))
+ (completion-type (bash-completion--type comp))
+ (compgen-args (bash-completion--compgen-args comp)))
+ (concat
+ (if bash-completion-use-separate-processes
+ (bash-completion-cd-command-prefix)
+ (bash-completion--side-channel-data "pwd" "${PWD}"))
+ (cond
+ ((eq 'command completion-type)
+ (concat "compgen -b -c -a -A function -- " quoted-stub))
+
+ ((eq 'default completion-type)
+ (concat "compgen -o default -- " quoted-stub))
+
+ ((and (eq 'custom completion-type) (or (member "-F" compgen-args)
+ (member "-C" compgen-args)))
+ ;; custom completion with a function of command
+ (let* ((args (copy-tree compgen-args))
+ (function (or (member "-F" args) (member "-C" args)))
+ (function-name (car (cdr function))))
+ (setcar function "-F")
+ (setcar (cdr function) "__emacs_complete_wrapper")
+ (format "__EMACS_COMPLETE_WRAPPER=%s compgen %s -- %s"
+ (bash-completion-quote
+ (format "COMP_LINE=%s; COMP_POINT=$(( 1 + ${#COMP_LINE} )); COMP_CWORD=%s; COMP_WORDS=( %s ); %s %s %s %s"
+ (bash-completion-quote (bash-completion--line comp))
+ (bash-completion--cword comp)
+ (bash-completion-join (bash-completion--words comp))
+ (bash-completion-quote function-name)
+ (bash-completion-quote (bash-completion--command comp))
+ (bash-completion-quote (bash-completion--stub comp))
+ (bash-completion-quote (or (nth (1- (bash-completion--cword comp))
+ (bash-completion--words comp))
+ ""))))
+ (bash-completion-join args)
+ quoted-stub)))
+ ((eq 'custom completion-type)
+ ;; simple custom completion
+ (format "compgen %s -- %s"
+ (bash-completion-join compgen-args)
+ quoted-stub))
+ (t (error "Unsupported completion type: %s" completion-type)))
+ " 2>/dev/null")))
+
+;;;###autoload
+(defun bash-completion-refresh ()
+ "Force a refresh the completion table.
+
+This can be called after changing the completion table on BASH,
+or after starting a new BASH job.
+
+This is only useful when `bash-completion-use-separate-processes'
+is t."
+ (interactive)
+ (let* ((process (get-buffer-process (current-buffer))))
+ (unless process
+ (error "No process is available in this buffer."))
+ (process-put process 'setup-done nil)
+ (bash-completion--get-process)))
+
+;;;###autoload
+(defun bash-completion-reset ()
+ "Force the next completion command to start with a fresh BASH process.
+
+This function kills any existing BASH completion process. This
+way, the next time BASH completion is requested, a new process
+will be created with the latest configuration. The BASH
+completion process that will be killed depends on the
+default-directory of the buffer where the command is executed.
+
+Call this method if you have updated your .bashrc or any bash init scripts
+and would like bash completion in Emacs to take these changes into account."
+ (interactive)
+ (let* ((remote (and default-directory (file-remote-p default-directory)))
+ (entry (assoc remote bash-completion-processes))
+ (proc (cdr entry)))
+ (when proc
+ (bash-completion-kill proc)
+ (setq bash-completion-processes (delq entry bash-completion-processes)))))
+
+(defun bash-completion-reset-all ()
+ (interactive)
+ (mapcar (lambda (entry)
+ (let ((default-directory (car entry)))
+ (bash-completion-reset)))
+ bash-completion-processes))
+
+(defun bash-completion-kill (process)
+ "Kill PROCESS and its buffer."
+ (when process
+ (when (eq 'run (process-status process))
+ (kill-process process))
+ (let ((buffer (bash-completion--get-buffer process)))
+ (when (buffer-live-p buffer)
+ (kill-buffer buffer)))))
+
+(defun bash-completion-buffer ()
+ "Return the buffer of the BASH process, create the BASH process if necessary."
+ (bash-completion--get-buffer (bash-completion--get-process)))
+
+(defun bash-completion-is-running ()
+ "Check whether the bash completion process is running."
+ (let* ((entry (assoc (file-remote-p default-directory)
+ bash-completion-processes))
+ (proc (cdr entry))
+ (running (and proc (eq 'run (process-status proc)))))
+ (unless (and entry running)
+ (setq bash-completion-processes (delq entry bash-completion-processes)))
+ running))
+
+(defun bash-completion--output-filter (output)
+ (with-current-buffer (bash-completion--get-buffer nil)
+ (let ((begin (point-max)))
+ (goto-char begin)
+ (insert output)
+ (save-excursion
+ (goto-char (point-min))
+ (while (search-forward "\r" nil 'noerror)
+ (delete-char -1)))
+ (ansi-color-filter-region begin (point))
+ "")))
+
+(defun bash-completion--wait-for-regexp (process prompt-regexp timeout &optional limit)
+ (let ((no-timeout t))
+ (while (and no-timeout
+ (not (re-search-backward prompt-regexp limit t)))
+ (setq no-timeout (accept-process-output process timeout nil t)))
+ no-timeout))
+
+(defun bash-completion-send (commandline &optional process timeout debug-context)
+ "Send a command to the bash completion process.
+
+COMMANDLINE should be a bash command, without the final newline.
+
+PROCESS should be the bash process, if nil this function calls
+`bash-completion--get-process' which might start a new process
+depending on the value of
+`bash-completion-use-separate-processes'.
+
+TIMEOUT is the timeout value for this operation, if nil the value of
+`bash-completion-process-timeout' is used.
+
+DEBUG-CONTEXT, if specified, is appended to the debug info under
+the key 'debug-context.
+
+Once this command has run without errors, you will find the
+result of the command in the bash completion process buffer or in
+`bash-completion-output-buffer' if
+`bash-completion-use-separate-processes' is nil.
+
+Return the status code of the command, as a number."
+ (let* ((process (or process (bash-completion--get-process)))
+ (timeout (or timeout bash-completion-process-timeout))
+ (comint-preoutput-filter-functions
+ (if bash-completion-use-separate-processes
+ comint-preoutput-filter-functions
+ '(bash-completion--output-filter)))
+ (send-string (if bash-completion-use-separate-processes
+ #'process-send-string
+ #'comint-send-string))
+ (pre-command (unless bash-completion-use-separate-processes
+ "__emacs_complete_pre_command; "))
+ (complete-command (concat pre-command commandline "\n")))
+ (setq bash-completion--debug-info
+ (list (cons 'commandline complete-command)
+ (cons 'process process)
+ (cons 'use-separate-processes bash-completion-use-separate-processes)
+ (cons 'context debug-context)))
+ (with-current-buffer (bash-completion--get-buffer process)
+ (erase-buffer)
+ (funcall send-string process complete-command)
+ (unless (bash-completion--wait-for-regexp process "\t-?[[:digit:]]+\v" timeout)
+ (push (cons 'error "timeout") bash-completion--debug-info)
+ (push (cons 'buffer-string (buffer-substring-no-properties (point-min) (point-max)))
+ bash-completion--debug-info)
+ (error "Bash completion failed. M-x bash-completion-debug for details."))
+ (when pre-command
+ ;; Detect the command having been echoed and remove it
+ (save-excursion
+ (goto-char (point-min))
+ (when (looking-at pre-command)
+ (delete-region (match-beginning 0) (line-beginning-position 2)))))
+ (let ((status (string-to-number
+ (buffer-substring-no-properties
+ (1+ (point))
+ (1- (line-end-position)))))
+ (wrapped-status (bash-completion--parse-side-channel-data "wrapped-status")))
+ (push (cons 'status status) bash-completion--debug-info)
+ (push (cons 'wrapped-status wrapped-status) bash-completion--debug-info)
+ (delete-region (point) (point-max))
+ (if (string= "124" wrapped-status)
+ 124
+ status)))))
+
+(defun bash-completion-debug ()
+ (interactive)
+ (with-help-window "*bash-completion-debug*"
+ (unless bash-completion--debug-info
+ (error "No debug information available for bash-completion. Please try it out first."))
+ (princ "This buffer contains information about the last completion command\n")
+ (princ "and the BASH process it was sent to. This can help you figure out\n")
+ (princ "what's happening.\n\n")
+ (princ "If it doesn't, go to\n")
+ (princ "https://github.com/szermatt/emacs-bash-completion/issues/new\n")
+ (princ "to create a new issue that describes:\n")
+ (princ "- what you were trying to do\n")
+ (princ "- what you expected to happen\n")
+ (princ "- what actually happened\n\n")
+ (princ "Then add a copy of the information below:\n\n")
+ (bash-completion--debug-print-info 'commandline 'eof)
+ (bash-completion--debug-print-info 'error)
+ (bash-completion--debug-print-info 'buffer-string 'eof)
+ (bash-completion--debug-print-info 'status)
+ (bash-completion--debug-print-info 'wrapped-status)
+ (bash-completion--debug-print-info 'process)
+ (bash-completion--debug-print-info 'use-separate-processes)
+
+ (let* ((debug-info bash-completion--debug-info)
+ (process (cdr (assq 'process debug-info)))
+ (bash-completion-use-separate-processes
+ (cdr (assq 'use-separate-processes debug-info))))
+ (if (process-live-p process)
+ (bash-completion--debug-print
+ 'output-buffer
+ (with-current-buffer (bash-completion--get-buffer process)
+ (buffer-substring-no-properties (point-min) (point-max)))
+ 'eof)
+ (princ "\nERROR: Process is dead. ")
+ (princ "Information collection is incomplete.\n")
+ (princ "Please retry\n\n")))
+
+ (bash-completion--debug-print-info 'use-separate-processes)
+ (bash-completion--debug-print-procinfo 'bash-major-version)
+ (bash-completion--debug-print 'emacs-version emacs-version)
+ (bash-completion--debug-print-procinfo 'completion-ignore-case)
+ (bash-completion--debug-print-info 'context)
+ (bash-completion--debug-print-procinfo 'complete-p)))
+
+(defun bash-completion--debug-print-info (symbol &optional eof)
+ "Print variable SYMBOL from `bash-completion-debug-info'.
+
+If EOF is non-nil, VALUE might contain newlines and other special
+characters. These are output as-is."
+ (bash-completion--debug-print
+ symbol (cdr (assq symbol bash-completion--debug-info)) eof))
+
+(defun bash-completion--debug-print-procinfo (symbol &optional eof)
+ "Print variable SYMBOL from `bash-completion-debug-info''s process.
+
+If EOF is non-nil, VALUE might contain newlines and other special
+characters. These are output as-is."
+ (let ((process (cdr (assq 'process bash-completion--debug-info))))
+ (when (process-live-p process)
+ (bash-completion--debug-print
+ symbol (process-get process symbol) eof))))
+
+(defun bash-completion--debug-print (name value &optional eof)
+ "Print debugging information NAME and VALUE.
+
+If EOF is non-nil, VALUE might contain newlines and other special
+characters. These are output as-is."
+ (when value
+ (princ name)
+ (princ ": ")
+ (if eof
+ (progn
+ (princ "<
+
+;; Author: Lucas Bonnet
+;; Keywords: lisp,convenience
+;; Version: 0.5
+;; URL : https://github.com/lukhas/buffer-move
+
+;; 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 2
+;; 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, write to the Free Software
+;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+;; 02111-1307, USA.
+
+;;; Commentary:
+
+;; This file is for lazy people wanting to swap buffers without
+;; typing C-x b on each window. This is useful when you have :
+
+;; +--------------+-------------+
+;; | | |
+;; | #emacs | #gnus |
+;; | | |
+;; +--------------+-------------+
+;; | |
+;; | .emacs |
+;; | |
+;; +----------------------------+
+
+;; and you want to have :
+
+;; +--------------+-------------+
+;; | | |
+;; | #gnus | .emacs |
+;; | | |
+;; +--------------+-------------+
+;; | |
+;; | #emacs |
+;; | |
+;; +----------------------------+
+
+;; With buffer-move, just go in #gnus, do buf-move-left, go to #emacs
+;; (which now should be on top right) and do buf-move-down.
+
+;; To use it, simply put a (require 'buffer-move) in your ~/.emacs and
+;; define some keybindings. For example, i use :
+
+;; (global-set-key (kbd "") 'buf-move-up)
+;; (global-set-key (kbd "") 'buf-move-down)
+;; (global-set-key (kbd "") 'buf-move-left)
+;; (global-set-key (kbd "") 'buf-move-right)
+
+
+;;; Code:
+
+
+(require 'windmove)
+
+;;;###autoload
+(defun buf-move-up ()
+ "Swap the current buffer and the buffer above the split.
+If there is no split, ie now window above the current one, an
+error is signaled."
+;; "Switches between the current buffer, and the buffer above the
+;; split, if possible."
+ (interactive)
+ (let* ((other-win (windmove-find-other-window 'up))
+ (buf-this-buf (window-buffer (selected-window))))
+ (if (null other-win)
+ (error "No window above this one")
+ ;; swap top with this one
+ (set-window-buffer (selected-window) (window-buffer other-win))
+ ;; move this one to top
+ (set-window-buffer other-win buf-this-buf)
+ (select-window other-win))))
+
+;;;###autoload
+(defun buf-move-down ()
+"Swap the current buffer and the buffer under the split.
+If there is no split, ie now window under the current one, an
+error is signaled."
+ (interactive)
+ (let* ((other-win (windmove-find-other-window 'down))
+ (buf-this-buf (window-buffer (selected-window))))
+ (if (or (null other-win)
+ (string-match "^ \\*Minibuf" (buffer-name (window-buffer other-win))))
+ (error "No window under this one")
+ ;; swap top with this one
+ (set-window-buffer (selected-window) (window-buffer other-win))
+ ;; move this one to top
+ (set-window-buffer other-win buf-this-buf)
+ (select-window other-win))))
+
+;;;###autoload
+(defun buf-move-left ()
+"Swap the current buffer and the buffer on the left of the split.
+If there is no split, ie now window on the left of the current
+one, an error is signaled."
+ (interactive)
+ (let* ((other-win (windmove-find-other-window 'left))
+ (buf-this-buf (window-buffer (selected-window))))
+ (if (null other-win)
+ (error "No left split")
+ ;; swap top with this one
+ (set-window-buffer (selected-window) (window-buffer other-win))
+ ;; move this one to top
+ (set-window-buffer other-win buf-this-buf)
+ (select-window other-win))))
+
+;;;###autoload
+(defun buf-move-right ()
+"Swap the current buffer and the buffer on the right of the split.
+If there is no split, ie now window on the right of the current
+one, an error is signaled."
+ (interactive)
+ (let* ((other-win (windmove-find-other-window 'right))
+ (buf-this-buf (window-buffer (selected-window))))
+ (if (null other-win)
+ (error "No right split")
+ ;; swap top with this one
+ (set-window-buffer (selected-window) (window-buffer other-win))
+ ;; move this one to top
+ (set-window-buffer other-win buf-this-buf)
+ (select-window other-win))))
+
+
+(provide 'buffer-move)
+;;; buffer-move.el ends here
diff --git a/.emacs.d/plugins/dired-sidebar.el b/.emacs.d/plugins/dired-sidebar.el
new file mode 100755
index 0000000..6308ca8
--- /dev/null
+++ b/.emacs.d/plugins/dired-sidebar.el
@@ -0,0 +1,1195 @@
+;;; dired-sidebar.el --- Tree browser leveraging dired -*- lexical-binding: t -*-
+
+;; Copyright (C) 2017 James Nguyen
+
+;; Author: James Nguyen
+;; Maintainer: James Nguyen
+;; URL: https://github.com/jojojames/dired-sidebar
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "25.1") (dired-subtree "0.0.1"))
+;; Keywords: dired, files, tools
+;; HomePage: https://github.com/jojojames/dired-sidebar
+
+;; 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 .
+
+;;; Commentary:
+;; This package provides a tree browser similar to `neotree' or `treemacs'
+;; but leverages `dired' to do the job of display.
+
+;; (use-package dired-sidebar
+;; :bind (("C-x C-n" . dired-sidebar-toggle-sidebar))
+;; :ensure nil
+;; :commands (dired-sidebar-toggle-sidebar))
+
+;;; Code:
+
+(require 'dired)
+(require 'dired-subtree)
+(require 'face-remap)
+(eval-when-compile (require 'subr-x)) ; `if-let*' and `when-let*'
+
+;; Compatibility
+
+(eval-and-compile
+ (with-no-warnings
+ (if (version< emacs-version "26")
+ (progn
+ (defalias 'dired-sidebar-if-let* #'if-let)
+ (defalias 'dired-sidebar-when-let* #'when-let)
+ (function-put #'dired-sidebar-if-let* 'lisp-indent-function 2)
+ (function-put #'dired-sidebar-when-let* 'lisp-indent-function 1))
+ (defalias 'dired-sidebar-if-let* #'if-let*)
+ (defalias 'dired-sidebar-when-let* #'when-let*))))
+
+;; Customizations
+
+(defgroup dired-sidebar nil
+ "A major mode leveraging `dired-mode' to display a filesystem in a tree
+layout."
+ :group 'files)
+
+(defcustom dired-sidebar-use-custom-font nil
+ "Show `dired-sidebar' with custom font.
+
+This face can be customized using `dired-sidebar-face'."
+ :type 'boolean
+ :group 'dired-sidebar)
+
+(defcustom dired-sidebar-face nil
+ "Face used by `dired-sidebar' for custom font.
+
+This only takes effect if `dired-sidebar-use-custom-font' is true."
+ :type 'list
+ :group 'dired-sidebar)
+
+(defcustom dired-sidebar-use-custom-modeline t
+ "Show `dired-sidebar' with custom modeline.
+
+This uses format specified by `dired-sidebar-mode-line-format'."
+ :type 'boolean
+ :group 'dired-sidebar)
+
+(defcustom dired-sidebar-mode-line-format
+ '("%e" mode-line-front-space
+ mode-line-buffer-identification
+ " " mode-line-end-spaces)
+ "Mode line format for `dired-sidebar'."
+ :type 'list
+ :group 'dired-sidebar)
+
+(defcustom dired-sidebar-theme 'icons
+ "*The tree style to display.
+`ascii' is the simplest style, it will use +/- to display the fold state,
+it is suitable for terminal.
+`icons' use `all-the-icons'.
+`nerd' use the nerdtree indentation mode and arrow.
+`none' use no theme.
+`vscode' use `vscode' icons.
+
+This only takes effect if on a local connection. (e.g. Not Tramp)"
+ :group 'dired-sidebar
+ :type '(choice (const ascii)
+ (const icons)
+ (const nerd)
+ (const none)
+ (const vscode)))
+
+(defcustom dired-sidebar-width 35
+ "Width of the `dired-sidebar' buffer."
+ :type 'integer
+ :group 'dired-sidebar)
+
+(defcustom dired-sidebar-refresh-on-projectile-switch t
+ "Refresh sidebar when `projectile' changes projects."
+ :type 'boolean
+ :group 'dired-sidebar)
+
+(defcustom dired-sidebar-should-follow-file nil
+ "Refresh sidebar to match current file."
+ :type 'boolean
+ :group 'dired-sidebar)
+
+(defcustom dired-sidebar-skip-subtree-parent t
+ "Whether to skip subtree parent directory when jumping up."
+ :type 'boolean
+ :group 'dired-sidebar)
+
+(defcustom dired-sidebar-pop-to-sidebar-on-toggle-open t
+ "Whether to jump to sidebar upon toggling open.
+
+This is used in conjunction with `dired-sidebar-toggle-sidebar'."
+ :type 'boolean
+ :group 'dired-sidebar)
+
+(defcustom dired-sidebar-follow-file-at-point-on-toggle-open t
+ "Whether to recursively cycle the subtree and put point on file.
+
+Similar to `dired-jump'. This moves point inside sidebar buffer
+to where current-buffer-file is \(if it exists\) but does not necessarily
+select the sidebar window."
+ :type 'boolean
+ :group 'dired-sidebar)
+
+
+
+(defcustom dired-sidebar-use-magit-integration t
+ "Whether to integrate with `magit-mode'.
+
+When true:
+
+When finding file to point at for
+`dired-sidebar-follow-file-at-point-on-toggle-open', use file at point
+in `magit' buffer.
+
+When finding root directory for sidebar, use directory specified by `magit'."
+ :type 'boolean
+ :group 'dired-sidebar)
+
+(defcustom dired-sidebar-use-term-integration nil
+ "Whether to integrate with `term-mode'.
+
+When true:
+
+When finding root directory for sidebar, use PWD of `term-mode'. This is turned
+off by default due to the experimental nature of getting the PWD from the
+terminal.
+
+Look at `dired-sidebar-term-get-pwd' for implementation."
+ :type 'boolean
+ :group 'dired-sidebar)
+
+(defcustom dired-sidebar-use-wdired-integration t
+ "Whether to integrate with `wdired'."
+ :type 'boolean
+ :group 'dired-sidebar)
+
+(defcustom dired-sidebar-cycle-subtree-on-click t
+ "Whether to cycle subtree on click."
+ :type 'boolean
+ :group 'dired-sidebar)
+
+(defcustom dired-sidebar-delay-auto-revert-updates t
+ "Whether to delay automatically reverting buffer.
+
+When true, only allow function `auto-revert-mode' to update every
+`dird-sidebar-stale-buffer-time-idle-delay' seconds."
+ :type 'boolean
+ :group 'dired-sidebar)
+
+(defcustom dired-sidebar-stale-buffer-time-idle-delay 1.5
+ "The time in idle seconds to wait before checking if buffer is stale."
+ :type 'number
+ :group 'dired-sidebar)
+
+(defcustom dired-sidebar-follow-file-idle-delay 2
+ "The time in idle seconds to wait before checking if sidebar should
+follow file."
+ :type 'number
+ :group 'dired-sidebar)
+
+(defcustom dired-sidebar-tui-update-delay 0.02
+ "The time in idle seconds to wait before updating tui interface.
+
+This only takes effect if `all-the-icons-dired' is disabled."
+ :type 'number
+ :group 'dired-sidebar)
+
+(defcustom dired-sidebar-refresh-on-special-commands t
+ "Whether or not to trigger auto-revert after certain functions.
+
+Warning: This is implemented by advising specific dired functions."
+ :type 'boolean
+ :group 'dired-sidebar)
+
+(defcustom dired-sidebar-disable-dired-collapse t
+ "Whether or not to disable `dired-collapse' if it's enabled."
+ :type 'boolean
+ :group 'dired-sidebar)
+
+(defcustom dired-sidebar-special-refresh-commands
+ '(dired-do-delete
+ dired-do-rename
+ dired-do-copy
+ dired-do-flagged-delete
+ dired-create-directory
+ (delete-file . 5)
+ (save-buffer . 5)
+ magit-format-patch)
+ "A list of commands that will trigger a refresh of the sidebar.
+
+The command can be an alist with the CDR of the alist being the amount of time
+to wait to refresh the sidebar after the CAR of the alist is called.
+
+Set this to nil or set `dired-sidebar-refresh-on-special-commands' to nil
+to disable automatic refresh when a special command is triggered."
+ :type 'list
+ :group 'dired-sidebar)
+
+(defcustom dired-sidebar-toggle-hidden-commands
+ '(balance-windows)
+ "A list of commands that won't work when `dired-sidebar' is visible.
+
+When the command is triggered, `dired-sidebar' will hide temporarily until
+command is completed.
+
+This functionality is implemented using advice.
+
+Set this to nil to disable this advice."
+ :type 'list
+ :group 'dired-sidebar)
+
+(defcustom dired-sidebar-alternate-select-window-function
+ #'dired-sidebar-default-alternate-select-window
+ "Function to call when using alternative window selection.
+
+Alternative window selection is used when `dired-sidebar-find-file' is called
+with a prefix arg or when `dired-sidebar-find-file-alt' is called."
+ :type 'function
+ :group 'dired-sidebar)
+
+(defcustom dired-sidebar-recenter-cursor-on-follow-file t
+ "Whether or not to center cursor when pointing at file."
+ :type 'boolean
+ :group 'dired-sidebar)
+
+(defcustom dired-sidebar-recenter-cursor-on-tui-update nil
+ "Whether or not to center cursor when updating tui interface."
+ :type 'boolean
+ :group 'dired-sidebar)
+
+(defcustom dired-sidebar-display-autorevert-messages nil
+ "Whether or not to display `autorevert' messages."
+ :type 'boolean
+ :group 'dired-sidebar)
+
+(defcustom dired-sidebar-open-file-in-most-recently-used-window t
+ "Whether or not to open files in most recently used window."
+ :type 'boolean
+ :group 'dired-sidebar)
+
+(defcustom dired-sidebar-subtree-line-prefix dired-subtree-line-prefix
+ "The line prefix to use when subtree is cycled."
+ :type 'string
+ :group 'dired-sidebar)
+
+(defcustom dired-sidebar-display-alist '((side . left) (slot . -1))
+ "Alist used in `display-buffer-in-side-window'.
+
+e.g. (display-buffer-in-side-window buffer '((side . left) (slot . -1)))"
+ :type 'alist
+ :group 'dired-sidebar)
+
+(defcustom dired-sidebar-close-sidebar-on-file-open nil
+ "Whether or not to close sidebar when `dired-sidebar-find-file' is called.
+
+This behavior only triggers if `dired-sidebar-find-file' is triggered on
+a file."
+ :type 'boolean
+ :group 'dired-sidebar)
+
+(defcustom dired-sidebar-icon-scale .18
+ "The scale of icons \(currently only applies to vscode theme.\)."
+ :type 'number
+ :group 'dired-sidebar)
+
+(defcustom dired-sidebar-no-delete-other-windows nil
+ "Whether or not to add `no-delete-other-window' parameter to window.
+
+If this is true, when calling `delete-other-windows', `dired-sidebar' window
+will continue showing.
+
+For more information, look up `delete-other-windows'."
+ :type 'boolean
+ :group 'dired-sidebar)
+
+(defcustom dired-sidebar-one-instance-p nil
+ "Only show one buffer instance for dired-sidebar for each frame."
+ :type 'boolean
+ :group 'dired-sidebar)
+
+;; Internal
+
+(defvar dired-sidebar-basedir (file-name-directory load-file-name)
+ "Store the directory dired-sidebar.el was loaded from.")
+
+(defvar dired-sidebar-icons-dir (format "%sicons/" dired-sidebar-basedir)
+ "Store the icons directory of `dired-sidebar'.")
+
+(defvar dired-sidebar-alist '()
+ "An alist that maps from frame to currently opened `dired-sidebar' buffer.")
+
+(defvar-local dired-sidebar-stale-buffer-timer nil
+ "Timer used for setting `dired-sidebar-check-for-stale-buffer-p'.
+
+This is buffer local.")
+
+(defvar-local dired-sidebar-follow-file-timer nil
+ "Timer used when `dired-sidebar-should-follow-file' is true.")
+
+(defvar-local dired-sidebar-check-for-stale-buffer-p nil
+ "Whether to check if buffer is stale.
+
+When this is true `dired-sidebar-buffer-stale-p'
+will check if buffer is stale through `auto-revert-mode'.")
+
+;; Mode
+
+(defmacro dired-sidebar-with-no-dedication (&rest body)
+ "Run BODY after undedicating window."
+ (declare (debug (&rest form)))
+ `(progn
+ (let ((window (get-buffer-window (current-buffer))))
+ (set-window-dedicated-p window nil)
+ ,@body
+ (set-window-dedicated-p window t))))
+
+(defvar dired-sidebar-mode-map
+ (let ((map (make-sparse-keymap)))
+ (define-key map (kbd "TAB") 'dired-sidebar-subtree-toggle)
+ (define-key map [tab] 'dired-sidebar-subtree-toggle)
+ (define-key map (kbd "C-m") 'dired-sidebar-find-file)
+ (define-key map (kbd "RET") 'dired-sidebar-find-file)
+ (define-key map (kbd "") 'dired-sidebar-find-file)
+ (define-key map "^" 'dired-sidebar-up-directory)
+ (define-key map "-" 'dired-sidebar-up-directory)
+ (define-key map (kbd "C-o") 'dired-sidebar-find-file-alt)
+ (define-key map [mouse-2] 'dired-sidebar-mouse-subtree-cycle-or-find-file)
+ map)
+ "Keymap used for symbol `dired-sidebar-mode'.")
+
+(define-derived-mode dired-sidebar-mode dired-mode
+ "Dired-sidebar"
+ "A major mode that puts `dired' in a sidebar."
+ :group 'dired-sidebar
+
+ ;; Hack for https://github.com/jojojames/dired-sidebar/issues/18.
+ ;; Would be open to a better fix...
+ ;; `dired-remember-hidden' in Emacs 25 (terminal?) seems to throw
+ ;; an error upon calling `goto-char'.
+ (when (<= emacs-major-version 25)
+ (defun dired-sidebar-remember-hidden-hack (f &rest args)
+ "Return nil for `dired-remember-hidden'.
+
+Works around marker pointing to wrong buffer in Emacs 25."
+ (if (eq major-mode 'dired-sidebar-mode)
+ nil
+ (apply f args)))
+ (advice-remove 'dired-remember-hidden 'dired-sidebar-remember-hidden-hack)
+ (advice-add 'dired-remember-hidden :around 'dired-sidebar-remember-hidden-hack))
+
+ ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=32392
+ (when dired-sidebar-use-wdired-integration
+ (advice-remove 'wdired-change-to-dired-mode
+ 'dired-sidebar-wdired-change-to-dired-mode-advice)
+ (advice-remove 'wdired-change-to-wdired-mode
+ 'dired-sidebar-wdired-change-to-wdired-mode-advice)
+
+ (advice-add 'wdired-change-to-dired-mode
+ :around 'dired-sidebar-wdired-change-to-dired-mode-advice)
+ (advice-add 'wdired-change-to-wdired-mode
+ :around 'dired-sidebar-wdired-change-to-wdired-mode-advice))
+
+ (setq window-size-fixed 'width)
+
+ ;; Match backgrounds.
+ (setq-local dired-subtree-use-backgrounds nil)
+
+ ;; `dired-subtree''s line prefix is determined by `dired-sidebar'.
+ (setq-local dired-subtree-line-prefix dired-sidebar-subtree-line-prefix)
+
+ ;; https://github.com/jojojames/dired-sidebar/issues/7
+ ;; Symlinks are displayed incorrectly when these three things happen.
+ ;; 1. `dired-hide-details-mode' is on.
+ ;; 2. `dired-subtree' toggles a symlink folder via `dired-subtree-toggle'.
+ ;; 3. `dired-hide-details-hide-symlink-targets' is set to true.
+ ;; Since we use both 1 & 2, disable 3 to avoid the issue.
+ ;; This needs to be set to nil before `dired-hide-details-mode' is called.
+ (setq-local dired-hide-details-hide-symlink-targets nil)
+
+ ;; Use `dired-sidebar-revert' instead that wraps `dired-revert'.
+ (setq-local revert-buffer-function 'dired-sidebar-revert)
+
+ ;; We don't want extra details in the sidebar.
+ (dired-hide-details-mode)
+
+ (when (and dired-sidebar-disable-dired-collapse
+ (fboundp 'dired-collapse-mode))
+ (add-hook 'dired-mode-hook
+ (lambda ()
+ (when (bound-and-true-p dired-collapse-mode)
+ (dired-collapse-mode -1)))
+ :append :local))
+
+ (when (and
+ (not dired-sidebar-display-autorevert-messages)
+ (boundp 'auto-revert-verbose))
+ (setq-local auto-revert-verbose nil))
+
+ (when dired-sidebar-delay-auto-revert-updates
+ (setq-local buffer-stale-function #'dired-sidebar-buffer-stale-p)
+ (let ((current-buffer (current-buffer)))
+ (setq dired-sidebar-stale-buffer-timer
+ (run-with-idle-timer
+ dired-sidebar-stale-buffer-time-idle-delay
+ t (lambda ()
+ ;; Only do a check if `dired-sidebar' buffer is in the foreground.
+ (when (get-buffer-window current-buffer)
+ (with-current-buffer current-buffer
+ (setq dired-sidebar-check-for-stale-buffer-p t))))))
+
+ (add-hook 'kill-buffer-hook
+ (lambda ()
+ (when (timerp dired-sidebar-stale-buffer-timer)
+ (cancel-timer dired-sidebar-stale-buffer-timer)))
+ nil t)))
+
+ (when dired-sidebar-refresh-on-special-commands
+ (mapc
+ (lambda (x)
+ (if (consp x)
+ (let ((command (car x))
+ (delay (cdr x)))
+ (advice-add
+ command
+ :after
+ (defalias (intern (format "dired-sidebar-refresh-after-%S" command))
+ (function
+ (lambda (&rest _)
+ (let ((timer-symbol
+ (intern
+ (format
+ "dired-sidebar-refresh-%S-timer" command))))
+ (when (and (boundp timer-symbol)
+ (timerp (symbol-value timer-symbol)))
+ (cancel-timer (symbol-value timer-symbol)))
+ (setf
+ (symbol-value timer-symbol)
+ (run-with-idle-timer
+ delay
+ nil
+ #'dired-sidebar-refresh-buffer))))))))
+ (advice-add x :after #'dired-sidebar-refresh-buffer)))
+ dired-sidebar-special-refresh-commands))
+
+ (when dired-sidebar-toggle-hidden-commands
+ (mapc
+ (lambda (x)
+ (advice-add x :around #'dired-sidebar-advice-hide-temporarily))
+ dired-sidebar-toggle-hidden-commands))
+
+ (when dired-sidebar-use-custom-font
+ (dired-sidebar-set-font))
+
+ (when dired-sidebar-use-custom-modeline
+ (dired-sidebar-set-mode-line))
+
+ (when dired-sidebar-refresh-on-projectile-switch
+ (add-hook 'projectile-after-switch-project-hook
+ #'dired-sidebar-follow-file))
+
+ (when dired-sidebar-should-follow-file
+ (setq dired-sidebar-follow-file-timer
+ (run-with-idle-timer
+ dired-sidebar-follow-file-idle-delay
+ t #'dired-sidebar-follow-file)))
+
+ ;; This comment is taken from `dired-readin'.
+ ;; Begin --- Copied comment from dired.el.
+ ;; Must first make alist buffer local and set it to nil because
+ ;; dired-build-subdir-alist will call dired-clear-alist first
+ ;; End --- Copied comment from dired.el.
+ (setq-local dired-subdir-alist nil)
+ (dired-build-subdir-alist)
+
+ (dired-unadvertise (dired-current-directory))
+ (dired-sidebar-update-buffer-name)
+ (dired-sidebar-update-state (current-buffer))
+
+ ;; Move setting theme until the end after `dired-sidebar' has set up
+ ;; its directory structure.
+ ;; https://github.com/jojojames/dired-sidebar/issues/29
+ (unless (file-remote-p default-directory)
+ (cond
+ ((dired-sidebar-using-tui-p)
+ (dired-sidebar-setup-tui))
+ ((and (eq dired-sidebar-theme 'icons)
+ (display-graphic-p)
+ (or
+ (fboundp 'all-the-icons-dired-mode)
+ (autoloadp (symbol-function 'all-the-icons-dired-mode))))
+ (with-no-warnings
+ (all-the-icons-dired-mode)))
+ (:default :no-theme))))
+
+;; User Interface
+
+;;;###autoload
+(defun dired-sidebar-toggle-sidebar (&optional dir)
+ "Toggle the project explorer window.
+Optional argument DIR Use DIR as sidebar root if available.
+
+With universal argument, use current directory."
+ (interactive)
+ (if (dired-sidebar-showing-sidebar-p)
+ (dired-sidebar-hide-sidebar)
+ (let* ((old-buffer (dired-sidebar-buffer (selected-frame)))
+ (file-to-show (dired-sidebar-get-file-to-show))
+ (dir-to-show (or dir
+ (when current-prefix-arg
+ (expand-file-name default-directory))
+ (dired-sidebar-get-dir-to-show)))
+ (sidebar-buffer (dired-sidebar-get-or-create-buffer dir-to-show)))
+ (dired-sidebar-show-sidebar sidebar-buffer)
+ (when (and dired-sidebar-one-instance-p old-buffer (not (eq sidebar-buffer old-buffer)))
+ (kill-buffer old-buffer))
+ (if (and dired-sidebar-follow-file-at-point-on-toggle-open
+ file-to-show)
+ (if dired-sidebar-pop-to-sidebar-on-toggle-open
+ (dired-sidebar-point-at-file file-to-show dir-to-show)
+ (with-selected-window (selected-window)
+ (dired-sidebar-point-at-file file-to-show dir-to-show)))
+ (when dired-sidebar-pop-to-sidebar-on-toggle-open
+ (pop-to-buffer (dired-sidebar-buffer)))))))
+
+(defun dired-sidebar-point-at-file (name root)
+ "Try to point at NAME from sidebar.
+
+Keep `dired' pointed at ROOT while cycling directories until
+NAME is found in ROOT path.
+
+This is dependent on `dired-subtree-cycle'."
+ (let ((sidebar (dired-sidebar-buffer)))
+ (pop-to-buffer sidebar)
+ (when (and name
+ ;; Checking for a private method. *shrug*
+ (fboundp 'dired-subtree--is-expanded-p))
+ (pop-to-buffer sidebar)
+ (goto-char 0)
+ (let* ((path root)
+ ;; Imagine root is /root/var/ and name is
+ ;; /root/var/a/b/c.
+ ;; This will return a list of '\("a" "b" "c"\).
+ (dirs (when (cadr (split-string name root))
+ (split-string (cadr (split-string name root)) "/"))))
+ (dolist (dir dirs)
+ (let ((path-regex (concat "^.*[[:space:]]" (regexp-quote dir))))
+ (setq path (concat path dir))
+ (if (file-regular-p path)
+ ;; Try to use `dired-goto-file' to go to the correct
+ ;; file. If that fails, just search for the text.
+ (let ((default-directory (file-name-directory path)))
+ (unless (dired-goto-file path)
+ (condition-case nil
+ ;; It's hard to get this right so just using a
+ ;; heuristic will get 90% of the way there.
+ ;; Making sure there's a space in front of the name
+ ;; skips matches that contains the name as a
+ ;; substring which is probably good enough...
+ (re-search-forward path-regex)
+ ;; Sometimes `dired' gets out of sync with the file.
+ ;; Refresh the buffer and try the search again.
+ ;; One way to reproduce this:
+ ;; 1. Open file A as buffer B.
+ ;; 2. Delete file A in `dired'.
+ ;; 3. Hide `dired-sidebar'.
+ ;; 4. Save buffer B.
+ ;; 5. Re-open `dired-sidebar'.
+ (error
+ (revert-buffer)
+ (re-search-forward path-regex nil :no-error)))))
+ (re-search-forward path-regex)
+ ;; Check if subtree has already been expanded.
+ ;; Basically, we're using `dired-subtree-cycle' more
+ ;; like dired-subtree-expand.
+ (when (not (dired-subtree--is-expanded-p))
+ ;; This will probably throw an error when trying to expand
+ ;; directories that have been collapsed by `dired-collapse'.
+ (dired-subtree-cycle))
+ (setq path (concat path "/"))))))
+ (when dired-sidebar-recenter-cursor-on-follow-file
+ (recenter nil))
+ (dired-sidebar-redisplay-icons))))
+
+;;;###autoload
+(defun dired-sidebar-toggle-with-current-directory ()
+ "Like `dired-sidebar-toggle-sidebar' but use current-directory."
+ (interactive)
+ (let ((current-prefix-arg '(4))) ; C-u
+ (call-interactively #'dired-sidebar-toggle-sidebar)))
+
+;;;###autoload
+(defun dired-sidebar-show-sidebar (&optional b)
+ "Show sidebar displaying buffer B."
+ (interactive)
+ (let ((buffer (or b
+ ;; Only expect this to be hit when called interactively.
+ (dired-sidebar-get-or-create-buffer
+ (dired-sidebar-get-dir-to-show)))))
+ (display-buffer-in-side-window buffer dired-sidebar-display-alist)
+ (let ((window (get-buffer-window buffer)))
+ (when dired-sidebar-no-delete-other-windows
+ (set-window-parameter window 'no-delete-other-windows t))
+ (set-window-dedicated-p window t)
+ (with-selected-window window
+ (let ((window-size-fixed))
+ (dired-sidebar-set-width dired-sidebar-width))))
+ (with-current-buffer buffer
+ (if (eq major-mode 'dired-sidebar-mode)
+ (dired-build-subdir-alist)
+ (dired-sidebar-mode)))
+ (dired-sidebar-update-state buffer)))
+
+;;;###autoload
+(defun dired-sidebar-hide-sidebar ()
+ "Hide the sidebar in the selected frame."
+ (interactive)
+ (dired-sidebar-when-let* ((buffer (dired-sidebar-buffer)))
+ (delete-window (get-buffer-window buffer))
+ (dired-sidebar-update-state nil)))
+
+;;;###autoload
+(defun dired-sidebar-jump-to-sidebar ()
+ "Jump to `dired-sidebar' buffer if it is showing.
+
+If it's not showing, act as `dired-sidebar-toggle-sidebar'."
+ (interactive)
+ (if (dired-sidebar-showing-sidebar-p)
+ (select-window
+ (get-buffer-window (dired-sidebar-buffer (selected-frame))))
+ (call-interactively #'dired-sidebar-toggle-sidebar)))
+
+(defun dired-sidebar-find-file (&optional dir)
+ "Wrapper over `dired-find-file'.
+Optional argument DIR Fine file using DIR of available.
+
+With prefix argument, use `dired-sidebar-alternate-select-window-function' for
+window selection."
+ (interactive)
+ (let ((find-file-run-dired t)
+ (dired-file-name (or dir (dired-get-file-for-visit)))
+ (select-with-alt-window-function current-prefix-arg))
+ (if (and (file-directory-p dired-file-name)
+ ;; For "." open a full-blown dired buffer, since the directory is
+ ;; already open in the sidebar.
+ (not (string= (file-name-nondirectory dired-file-name)
+ ".")))
+ (dired-sidebar-with-no-dedication
+ (let ((buf-name (dired-sidebar-buffer-name
+ dired-file-name)))
+ (if (dired-sidebar-buffer-exists-p buf-name)
+ (progn
+ (switch-to-buffer buf-name)
+ (dired-sidebar-update-state (current-buffer)))
+ (if (and dired-sidebar-one-instance-p (file-directory-p dired-file-name))
+ (find-alternate-file dired-file-name)
+ ;; Copied from `dired-find-file'.
+ (find-file dired-file-name))
+ (dired-sidebar-mode)
+ (dired-sidebar-update-state (current-buffer)))))
+ ;; Select the sidebar window so that `next-window' is consistent
+ ;; in picking the window next to the sidebar.
+ ;; This is useful for when `dired-sidebar-find-file' is called
+ ;; from a buffer that is not already in the sidebar buffer.
+ ;; e.g. A mouse click event.
+ (switch-to-buffer (dired-sidebar-buffer))
+ (select-window
+ (if select-with-alt-window-function
+ (funcall dired-sidebar-alternate-select-window-function)
+ (if dired-sidebar-open-file-in-most-recently-used-window
+ (get-mru-window nil nil t)
+ (next-window))))
+ (find-file dired-file-name)
+ (when dired-sidebar-close-sidebar-on-file-open
+ (dired-sidebar-hide-sidebar)))))
+
+(defun dired-sidebar-find-file-alt ()
+ "Like `dired-sidebar-find-file' but select window with alterate method.
+
+Select alternate window using `dired-sidebar-alternate-select-window-function'."
+ (interactive)
+ (let ((current-prefix-arg '(4))) ; C-u
+ (call-interactively 'dired-sidebar-find-file)))
+
+(defun dired-sidebar-up-directory ()
+ "Wrapper over `dired-up-directory'."
+ (interactive)
+ (dired-sidebar-with-no-dedication
+ ;; If `dired-subtree' is used, `dired-current-directory' is redefined.
+ ;; So move point to the top of the buffer to get the actual directory and
+ ;; not the one at point.
+ (when dired-sidebar-skip-subtree-parent
+ (goto-char (point-min)))
+ (let* ((dir (dired-current-directory))
+ (up (file-name-directory (directory-file-name dir)))
+ (up-name (dired-sidebar-buffer-name up)))
+ (if (dired-sidebar-buffer-exists-p up-name)
+ (progn
+ (switch-to-buffer up-name)
+ (dired-sidebar-update-state (current-buffer)))
+ (if dired-sidebar-one-instance-p
+ (find-alternate-file "..")
+ (dired-up-directory))
+ (dired-sidebar-mode)
+ (dired-sidebar-update-state (current-buffer)))
+ (let ((default-directory up))
+ (dired-goto-file dir)))))
+
+(defun dired-sidebar-mouse-subtree-cycle-or-find-file (event)
+ "Handle a mouse click EVENT in `dired-sidebar'.
+
+For directories, if `dired-sidebar-cycle-subtree-on-click' is true,
+cycle the directory.
+
+Otherwise, behaves the same as if user clicked on a file.
+
+For files, use `dired-sidebar-find-file'.
+
+This uses the same code as `dired-mouse-find-file-other-window' to find
+the relevant file-directory clicked on by the mouse."
+ (interactive "e")
+ (let (window pos file)
+ (save-excursion
+ (setq window (posn-window (event-end event))
+ pos (posn-point (event-end event)))
+ (if (not (windowp window))
+ (error "No file chosen"))
+ (set-buffer (window-buffer window))
+ (goto-char pos)
+ (setq file (dired-get-file-for-visit)))
+ ;; There's a flicker doing this but it doesn't seem like
+ ;; `dired-subtree-cycle' works without first selecting the window.
+ (with-selected-window window
+ (if (and dired-sidebar-cycle-subtree-on-click
+ (file-directory-p file)
+ (not (string-suffix-p "." file)))
+ (dired-subtree-cycle)
+ (dired-sidebar-find-file file)))))
+
+;; Helpers
+
+(defun dired-sidebar-buffer-exists-p (buffer-name)
+ "Check if a `dired-sidebar' buffer exists for BUFFER-NAME."
+ (get-buffer buffer-name))
+
+(defun dired-sidebar-sidebar-root ()
+ "Return directory using `projectile', `project' or current directory."
+ (if (featurep 'projectile)
+ (condition-case nil
+ (if (fboundp 'projectile-project-root)
+ (or (projectile-project-root) default-directory)
+ default-directory)
+ (error default-directory))
+ ;; Use `project' if `projectile' is not loaded yet.
+ ;; `projectile' is a big package and takes a while to load so it's better
+ ;; to defer loading it as long as possible (until the user chooses).
+ (dired-sidebar-if-let* ((project (project-current)))
+ (cdr project)
+ default-directory)))
+
+(defun dired-sidebar-buffer-name (dir)
+ "Return name of `dired-sidebar' buffer given DIR."
+ (let ((b (cond
+ ((string-suffix-p ".." dir)
+ ;; ~/.emacs.d/elpa/.. -> ~/.emacs.d/
+ (file-name-directory (substring dir 0 (- (length dir) 3))))
+ ((not (string-suffix-p "/" dir))
+ (concat dir "/"))
+ (:default
+ dir))))
+ (concat ":" (abbreviate-file-name b))))
+
+(defun dired-sidebar-get-or-create-buffer (root)
+ "Get or create a `dired-sidebar' buffer matching ROOT."
+ (interactive)
+ (let ((name (dired-sidebar-buffer-name root)))
+ (dired-sidebar-if-let* ((existing-buffer (get-buffer name)))
+ existing-buffer
+ (let ((buffer (dired-noselect root)))
+ ;; When opening a sidebar while in a dired buffer that matches
+ ;; the sidebar's root directory.
+ (if (eq (current-buffer) buffer)
+ ;; https://github.com/Fuco1/dired-hacks/issues/102
+ (if (member 'dired-collapse-mode dired-mode-hook)
+ (progn
+ (remove-hook 'dired-mode-hook 'dired-collapse-mode)
+ (let ((clone (clone-buffer)))
+ (add-hook 'dired-mode-hook 'dired-collapse-mode)
+ clone))
+ (clone-buffer))
+ ;; Rename the buffer generated by `dired-noselect'.
+ (when (not (string-equal (buffer-name buffer) name))
+ (with-current-buffer buffer
+ (rename-buffer name)))
+ buffer)))))
+
+(defun dired-sidebar-set-font ()
+ "Customize font in `dired-sidebar'.
+
+Set font to a variable width (proportional) in the current buffer."
+ (interactive)
+ (setq-local buffer-face-mode-face dired-sidebar-face)
+ (buffer-face-mode))
+
+(defun dired-sidebar-set-mode-line ()
+ "Customize modeline in `dired-sidebar'."
+ (setq mode-line-format dired-sidebar-mode-line-format))
+
+(defun dired-sidebar-set-width (width)
+ "Set the width of the buffer to WIDTH when it is created."
+ ;; Copied from `treemacs--set-width' as well as `neotree'.
+ (unless (one-window-p)
+ (let ((window-size-fixed)
+ (w (max width window-min-width)))
+ (cond
+ ((> (window-width) w)
+ (shrink-window-horizontally (- (window-width) w)))
+ ((< (window-width) w)
+ (enlarge-window-horizontally (- w (window-width))))))))
+
+(defun dired-sidebar-update-buffer-name ()
+ "Change buffer name to avoid collision with regular `dired' buffers."
+ (rename-buffer
+ (dired-sidebar-buffer-name (dired-current-directory))))
+
+(defun dired-sidebar-update-state (buffer &optional f)
+ "Update current state with BUFFER for sidebar in F or selected frame."
+ (let ((frame (or f (selected-frame))))
+ (if (assq frame dired-sidebar-alist)
+ (setcdr (assq frame dired-sidebar-alist) buffer)
+ (push `(,frame . ,buffer) dired-sidebar-alist))))
+
+(defun dired-sidebar-showing-sidebar-p (&optional f)
+ "Whether F or selected frame is showing a sidebar.
+
+Check if F or selected frame contains a sidebar and return
+corresponding buffer if buffer has a window attached to it.
+
+Return buffer if so."
+ (dired-sidebar-when-let* ((buffer (dired-sidebar-buffer f)))
+ (get-buffer-window buffer)))
+
+(defun dired-sidebar-buffer (&optional f)
+ "Return the current sidebar buffer in F or selected frame.
+
+This can return nil if the buffer has been killed."
+ (let* ((frame (or f (selected-frame)))
+ (buffer (alist-get frame dired-sidebar-alist)))
+ ;; The buffer can be killed for a variety of reasons.
+ ;; This side effect is kind of messy but it's the simplest place
+ ;; to put the clean up code for `dired-sidebar-alist'.
+ (if (buffer-live-p buffer)
+ buffer
+ ;; https://www.gnu.org/software/emacs/manual/html_node/elisp/Association-Lists.html
+ ;; Documentation for `assq-delete-all'.
+ ;; What kind of API is this?? :()
+ ;; Why does it only modify 'often' and not 'always'? ¯\_(ツ)_/¯
+ ;; It returns the shortened alist, and often modifies the original list
+ ;; structure of alist.
+ ;; For correct results, use the return value of assq-delete-all rather
+ ;; than looking at the saved value of alist.
+ (setq dired-sidebar-alist
+ (assq-delete-all frame dired-sidebar-alist))
+ nil)))
+
+(defun dired-sidebar-switch-to-dir (dir)
+ "Update buffer with DIR as root."
+ (when (dired-sidebar-showing-sidebar-p)
+ (let ((buffer (dired-sidebar-get-or-create-buffer dir)))
+ (dired-sidebar-show-sidebar buffer))))
+
+(defun dired-sidebar-buffer-stale-p (&optional noconfirm)
+ "Wrapper over `dired-buffer-stale-p'.
+
+Check if buffer is stale only if `dired-sidebar-stale-buffer-time-idle-delay'
+
+has elapsed.
+
+Optional argument NOCONFIRM Pass NOCONFIRM on to `dired-buffer-stale-p'."
+ (when dired-sidebar-check-for-stale-buffer-p
+ (setq dired-sidebar-check-for-stale-buffer-p nil)
+ (dired-buffer-stale-p noconfirm)))
+
+(defun dired-sidebar-refresh-buffer (&rest _)
+ "Refresh sidebar buffer."
+ (dired-sidebar-when-let* ((sidebar (dired-sidebar-buffer)))
+ (with-current-buffer sidebar
+ (let ((auto-revert-verbose nil))
+ (ignore auto-revert-verbose) ;; Make byte compiler happy.
+ (revert-buffer)))))
+
+(defun dired-sidebar-follow-file ()
+ "Follow new file.
+
+The root of the sidebar will be determined by `dired-sidebar-get-dir-to-show'
+and the file followed is will be determined by `dired-sidebar-get-file-to-show',
+
+both accounting for the currently selected window."
+ (when (dired-sidebar-showing-sidebar-p)
+ ;; Wrap in `with-selected-window' because we don't want to pop to
+ ;; the sidebar buffer.
+ ;; We also need to pick the correct selected-window so that
+ ;; `dired-sidebar-get-dir-to-show' can get the correct root to change to.
+ (with-selected-window (selected-window)
+ (let ((root (dired-sidebar-get-dir-to-show)))
+ (dired-sidebar-switch-to-dir root)
+ (when dired-sidebar-follow-file-at-point-on-toggle-open
+ (dired-sidebar-when-let* ((file (dired-sidebar-get-file-to-show)))
+ (dired-sidebar-point-at-file file root)))))))
+
+(defun dired-sidebar-default-alternate-select-window ()
+ "Default function for `dired-sidebar-alternate-select-window-function'."
+ (if (fboundp 'aw-select)
+ (aw-select "Select Window")
+ (next-window)))
+
+(defun dired-sidebar-get-dir-to-show ()
+ "Return the directory `dired-sidebar' should open to."
+ (expand-file-name
+ (cond
+ ((and (derived-mode-p 'magit-mode)
+ dired-sidebar-use-magit-integration
+ (fboundp 'magit-toplevel))
+ (magit-toplevel))
+ ((and (eq major-mode 'term-mode)
+ dired-sidebar-use-term-integration)
+ (dired-sidebar-term-get-pwd))
+ ((and (eq major-mode 'dired-mode))
+ default-directory)
+ ((and (eq major-mode 'ibuffer-mode)
+ (fboundp 'ibuffer-current-buffer)
+ (ibuffer-current-buffer))
+ (let ((buffer-at-point (ibuffer-current-buffer)))
+ (if (fboundp 'ibuffer-projectile-root)
+ (dired-sidebar-if-let* ((ibuffer-projectile-root
+ (ibuffer-projectile-root buffer-at-point)))
+ (cdr ibuffer-projectile-root)
+ (with-current-buffer buffer-at-point
+ default-directory))
+ (with-current-buffer buffer-at-point
+ default-directory))))
+ (:default
+ (dired-sidebar-sidebar-root)))))
+
+(defun dired-sidebar-get-file-to-show ()
+ "Return the file `dired-sidebar' should open to.
+
+This may return nil if there's no suitable file to show."
+ (cond
+ ((and dired-sidebar-use-magit-integration
+ (derived-mode-p 'magit-mode)
+ (fboundp 'magit-file-at-point)
+ (magit-file-at-point))
+ (expand-file-name (magit-file-at-point)))
+ ((and (eq major-mode 'dired-mode))
+ ;; Not sure if `dired-get-filename' is more appropriate.
+ (condition-case nil
+ (dired-get-file-for-visit)
+ (error nil)))
+ ((and (eq major-mode 'ibuffer-mode)
+ (fboundp 'ibuffer-current-buffer))
+ (let ((bf-name (buffer-file-name (ibuffer-current-buffer))))
+ (and bf-name (file-exists-p bf-name) bf-name)))
+ (:default
+ (and buffer-file-name (file-exists-p buffer-file-name) buffer-file-name))))
+
+(defun dired-sidebar-term-get-pwd ()
+ "Get current directory of `term-mode'.
+
+This is somewhat experimental/hacky."
+ (interactive)
+ (condition-case nil
+ (progn
+ (forward-paragraph)
+ (when (fboundp 'term-previous-prompt)
+ (term-previous-prompt 1))
+ (when (fboundp 'term-simple-send)
+ (term-simple-send (get-buffer-process (current-buffer)) "pwd"))
+ (sleep-for 0 50)
+ (forward-line 1)
+ (let ((result (string-trim (thing-at-point 'line))))
+ (kill-whole-line)
+ (forward-line -1)
+ (kill-whole-line)
+ result))
+ (error
+ default-directory)))
+
+(defun dired-sidebar-subtree-toggle ()
+ "Wrapper over `dired-subtree-toggle' that accounts for `all-the-icons-dired'."
+ (interactive)
+ (dired-subtree-toggle)
+ (dired-sidebar-redisplay-icons))
+
+(defun dired-sidebar-redisplay-icons ()
+ "Redisplay icon themes unless over TRAMP."
+ (unless (file-remote-p default-directory)
+ (when (and (eq dired-sidebar-theme 'icons)
+ (fboundp 'all-the-icons-dired--refresh))
+ ;; Refresh `all-the-icons-dired'.
+ (dired-sidebar-revert)
+ (all-the-icons-dired--refresh))
+ (when (dired-sidebar-using-tui-p)
+ (dired-sidebar-tui-update-with-delay))))
+
+(defun dired-sidebar-advice-hide-temporarily (f &rest args)
+ "A function meant to be used with advice to temporarily hide itself.
+
+This function hides the sidebar before executing F and then reshows itself after."
+ (if (not (dired-sidebar-showing-sidebar-p))
+ (apply f args)
+ (let ((sidebar (dired-sidebar-buffer)))
+ (dired-sidebar-hide-sidebar)
+ (apply f args)
+ (dired-sidebar-show-sidebar sidebar))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Text User Interface ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defvar-local dired-sidebar-tui-dired-displayed nil
+ "Flags whether icons have been added.")
+
+(defun dired-sidebar-tui-dired-reset (&optional _arg _noconfirm)
+ "Function used as advice when redisplaying buffer."
+ (setq-local dired-sidebar-tui-dired-displayed nil))
+
+(defun dired-sidebar-tui-dired-display ()
+ "Display the icons of files in a dired buffer."
+ (interactive)
+ (when (or t (and (not dired-sidebar-tui-dired-displayed) dired-subdir-alist))
+ (setq-local dired-sidebar-tui-dired-displayed t)
+ (let ((inhibit-read-only t)
+ (collapsible-icon (if (eq dired-sidebar-theme 'nerd) "▾" "-"))
+ (expandable-icon (if (eq dired-sidebar-theme 'nerd) "▸" "+")))
+ (save-excursion
+ (goto-char (point-min))
+ (while (not (eobp))
+ (when (dired-move-to-filename nil)
+ (dired-move-to-filename)
+ (let ((file (dired-get-filename 'verbatim t)))
+ (unless (member file '("." ".."))
+ (let ((filename (dired-get-filename nil t)))
+ (if (eq dired-sidebar-theme 'vscode)
+ (progn
+ (require 'vscode-icon)
+ (when (fboundp 'vscode-icon-for-file)
+ (insert-image
+ (vscode-icon-for-file filename) " "))
+ (insert " "))
+ (if (file-directory-p filename)
+ (if (dired-subtree--is-expanded-p)
+ (insert (concat collapsible-icon " "))
+ (insert (concat expandable-icon " ")))
+ (insert "")))))))
+ (forward-line 1))))))
+
+(defun dired-sidebar-tui-update-with-delay (&rest _)
+ "Update tui interface after a delay."
+ (run-with-idle-timer
+ dired-sidebar-tui-update-delay nil
+ #'dired-sidebar-tui-update))
+
+(defun dired-sidebar-tui-update ()
+ "Workhorse function to update tui interface."
+ (dired-sidebar-when-let* ((buffer (dired-sidebar-buffer)))
+ (with-current-buffer buffer
+ (dired-sidebar-revert)
+ (when dired-sidebar-recenter-cursor-on-tui-update
+ (recenter)))))
+
+(defun dired-sidebar-revert (&rest _)
+ "Wrapper around `dired-revert' but saves window position."
+ (dired-sidebar-when-let* ((window (get-buffer-window
+ (dired-sidebar-buffer))))
+ (with-selected-window window
+ (let ((old-window-start (window-start)))
+ (when (dired-sidebar-using-tui-p)
+ (dired-sidebar-tui-reset-in-sidebar))
+ (dired-revert)
+ (set-window-start window old-window-start)))))
+
+(defun dired-sidebar-tui-reset-in-sidebar (&rest _)
+ "Runs `dired-sidebar-tui-dired-reset' in current `dired-sidebar' buffer."
+ (dired-sidebar-when-let* ((buffer (dired-sidebar-buffer)))
+ (with-current-buffer buffer
+ (dired-sidebar-tui-dired-reset))))
+
+(defun dired-sidebar-setup-tui ()
+ "Sets up text user interface for `dired-sidebar'.
+
+This is used in place of `all-the-icons' to add directory indicators.
+
+e.g. + and -."
+ (add-hook 'dired-after-readin-hook
+ 'dired-sidebar-tui-dired-display :append :local)
+ (setq-local dired-subtree-line-prefix " ")
+ (dired-build-subdir-alist)
+ (dired-sidebar-revert))
+
+(defun dired-sidebar-using-tui-p ()
+ "Return t if `dired-sidebar-theme' is using tui code path."
+ (or
+ (eq dired-sidebar-theme 'ascii)
+ (eq dired-sidebar-theme 'nerd)
+ (eq dired-sidebar-theme 'vscode)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Text User Interface ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;; `wdired' Hack ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=32392
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;; `wdired' Hack ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defvar-local dired-sidebar-wdired-tracking-major-mode nil
+ "Track current `major-mode' when toggling to `wdired'.")
+
+(defun dired-sidebar-wdired-change-to-dired-mode-advice (f &rest args)
+ "Advice for `wdired-change-to-dired-mode'."
+ (if (eq dired-sidebar-wdired-tracking-major-mode 'dired-sidebar-mode)
+ (dired-sidebar-wdired-change-to-dired-mode)
+ (apply f args)))
+
+(defun dired-sidebar-wdired-change-to-dired-mode ()
+ "Change the mode back to dired-sidebar.
+
+This is an exact copy of `wdired-change-to-dired-mode' but changes the
+`major-mode' to `dired-sidebar-mode' instead of `dired-mode'."
+ (let ((inhibit-read-only t))
+ (remove-text-properties
+ (point-min) (point-max)
+ '(front-sticky nil rear-nonsticky nil read-only nil keymap nil)))
+ (use-local-map dired-mode-map)
+ (force-mode-line-update)
+ (setq buffer-read-only t)
+ (setq major-mode 'dired-sidebar-mode)
+ (setq mode-name "Dired-sidebar")
+ (dired-advertise)
+ (remove-hook 'kill-buffer-hook 'wdired-check-kill-buffer t)
+ (set (make-local-variable 'revert-buffer-function) 'dired-sidebar-revert))
+
+(defun dired-sidebar-wdired-change-to-wdired-mode-advice (f &rest args)
+ "Forward to `wdired-change-to-wdired-mode'.
+
+`wdired' expected the `major-mode' to be `dired-mode' first.
+
+Track the current `major-mode' and revert to that upon exiting `wdired'."
+ (setq dired-sidebar-wdired-tracking-major-mode major-mode)
+ (if (eq major-mode 'dired-mode)
+ (apply f args)
+ (let ((major-mode 'dired-mode))
+ (apply f args))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;; `wdired' Hack ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(provide 'dired-sidebar)
+;;; dired-sidebar.el ends here
diff --git a/.emacs.d/plugins/flymake-yaml.el b/.emacs.d/plugins/flymake-yaml.el
new file mode 100644
index 0000000..fe6aa5b
--- /dev/null
+++ b/.emacs.d/plugins/flymake-yaml.el
@@ -0,0 +1,68 @@
+;;; flymake-yaml.el --- A flymake handler for YAML
+
+;; Copyright (C) 2013 Yasuyuki Oka
+
+;; Author: Yasuyuki Oka
+;; Version: 0.0.2
+;; URL: https://github.com/yasuyk/flymake-yaml
+;; Package-Requires: ((flymake-easy "0.1"))
+;; Keywords: yaml
+
+;; 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 .
+
+;;; Commentary:
+;;
+;; Based in part on http://d.hatena.ne.jp/kitokitoki/20120306/p1
+;;
+;; Usage:
+;;
+;; (require 'flymake-yaml) ;; Not necessary if using ELPA package
+;; (add-hook 'yaml-mode-hook 'flymake-yaml-load)
+;;
+;; Uses flymake-easy, from https://github.com/purcell/flymake-easy
+
+;;; Code:
+
+(require 'flymake-easy)
+
+(defconst flymake-yaml-err-line-patterns
+ ;; Syck error message
+ '(("syntax error on line \\([0-9]+\\), col \\([0-9]+\\): `\\(.*\\)'" nil 1 2 3)
+ ;; Psych error message
+ (".*: \\(.*\\) at line \\([0-9]+\\) column \\([0-9]+\\)" nil 2 3 1)))
+
+(defun flymake-yaml-command (filename)
+ "Construct a command that flymake can use to check yaml source.
+Argument FILENAME
+ YAML file name."
+ (list "ruby" "-ryaml" "-e" "YAML.load(ARGF) rescue warn $!" filename))
+
+;;;###autoload
+(defun flymake-yaml-load ()
+ "Configure flymake mode to check the current buffer's YAML syntax."
+ (interactive)
+ (when (eq major-mode 'yaml-mode)
+ (flymake-easy-load 'flymake-yaml-command
+ flymake-yaml-err-line-patterns
+ 'tempdir
+ "yml")))
+
+(provide 'flymake-yaml)
+
+;; Local Variables:
+;; coding: utf-8
+;; eval: (checkdoc-minor-mode 1)
+;; End:
+
+;;; flymake-yaml.el ends here
diff --git a/.emacs.d/plugins/latexmk-mode.el b/.emacs.d/plugins/latexmk-mode.el
new file mode 100644
index 0000000..e717fe5
--- /dev/null
+++ b/.emacs.d/plugins/latexmk-mode.el
@@ -0,0 +1,27 @@
+;;; latexmk-mode.el --- LatexMK minor mode
+;;; Commentary:
+;;; none
+;;; Code:
+
+(define-minor-mode latexmk-mode
+ "Toggle LatexMK mode."
+ :init-value nil
+ :lighter " LatexMK "
+)
+
+(defun my/run-latexmk ()
+ (interactive)
+ (start-process "latexmk" "latexmk out" "latexmk" "--silent" "--pdf" (buffer-file-name (current-buffer)))
+)
+
+(defun my/try-run-latexmk ()
+ "Try to run latexmk."
+
+ (if (bound-and-true-p latexmk-mode)
+ (my/run-latexmk)
+ )
+ )
+
+(add-hook 'after-save-hook 'my/try-run-latexmk)
+(add-hook 'latex-mode-hook 'latexmk-mode)
+;;; latexmk-mode.el ends here
diff --git a/.emacs.d/plugins/livedown.el b/.emacs.d/plugins/livedown.el
new file mode 100644
index 0000000..07762b8
--- /dev/null
+++ b/.emacs.d/plugins/livedown.el
@@ -0,0 +1,75 @@
+;;; livedown.el --- Realtime Markdown previews for Emacs.
+
+;; Copyright (C) 2014-2016 Hrvoje Simic
+
+;; Author: Hrvoje Simic
+;; Version: 1.0.0
+;; Keywords: markdown, preview, live
+;; URL: https://github.com/shime/emacs-livedown
+
+;;; Commentary:
+
+;; Realtime Markdown previews for Emacs.
+
+;;; Code:
+
+(defgroup livedown nil
+ "Realtime Markdown previews"
+ :group 'livedown
+ :prefix "livedown-")
+
+(defcustom livedown-port 1337
+ "Port on which livedown server will run."
+ :type 'integer
+ :group 'livedown)
+
+(defcustom livedown-open t
+ "Open browser automatically."
+ :type 'boolean
+ :group 'livedown)
+
+(defcustom livedown-browser nil
+ "Open alternative browser."
+ :type 'string
+ :group 'livedown)
+
+(defcustom livedown-autostart nil
+ "Auto-open previews when opening markdown files."
+ :type 'boolean
+ :group 'livedown)
+
+;;;###autoload
+(defun livedown-preview ()
+ "Preview the current file in livedown."
+ (interactive)
+
+ (call-process-shell-command
+ (format "livedown stop --port %s &"
+ livedown-port))
+
+ (start-process-shell-command
+ (format "emacs-livedown")
+ (format "emacs-livedown-buffer")
+ (format "livedown start %s --port %s %s %s "
+ buffer-file-name
+ livedown-port
+ (if livedown-browser (concat "--browser " livedown-browser) "")
+ (if livedown-open "--open" "")))
+ (print (format "%s rendered @ %s" buffer-file-name livedown-port) (get-buffer "emacs-livedown-buffer")))
+
+;;;###autoload
+(defun livedown-kill (&optional async)
+ "Stops the livedown process."
+ (interactive)
+ (let ((stop-livedown (if async 'async-shell-command 'call-process-shell-command)))
+ (funcall stop-livedown
+ (format "livedown stop --port %s &"
+ livedown-port))))
+
+(if livedown-autostart
+ (eval-after-load 'markdown-mode '(livedown-preview)))
+
+(add-hook 'kill-emacs-query-functions (lambda () (livedown-kill t)))
+
+(provide 'livedown)
+;;; livedown.el ends here
diff --git a/.emacs.d/plugins/org-ac.el b/.emacs.d/plugins/org-ac.el
new file mode 100644
index 0000000..d34ff8c
--- /dev/null
+++ b/.emacs.d/plugins/org-ac.el
@@ -0,0 +1,261 @@
+;;; org-ac.el --- Some auto-complete sources for org-mode
+
+;; Copyright (C) 2014 Hiroaki Otsu
+
+;; Author: Hiroaki Otsu
+;; Keywords: org, completion
+;; URL: https://github.com/aki2o/org-ac
+;; Version: 0.0.2
+;; Package-Requires: ((auto-complete-pcmp "0.0.1") (log4e "0.2.0") (yaxception "0.1"))
+
+;; 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 file 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 .
+
+;;; Commentary:
+;;
+;; This extension provides auto-complete sources for org-mode.
+
+;;; Dependency:
+;;
+;; - auto-complete-pcmp.el ( see )
+;; - yaxception.el ( see )
+;; - log4e.el ( see )
+
+;;; Installation:
+;;
+;; Put this to your load-path.
+;; And put the following lines in your .emacs or site-start.el file.
+;;
+;; (require 'org-ac)
+
+;;; Configuration:
+;;
+;; ;; Make config suit for you. About the config item, see Customization or eval the following sexp.
+;; ;; (customize-group "org-ac")
+;;
+;; (org-ac/config-default)
+
+;;; Customization:
+;;
+;; [EVAL] (autodoc-document-lisp-buffer :type 'user-variable :prefix "org-ac/" :docstring t)
+;; `org-ac/ac-trigger-command-keys'
+;; Keystrokes for doing `ac-start' with self insert.
+;;
+;; *** END auto-documentation
+
+;;; API:
+;;
+;; [EVAL] (autodoc-document-lisp-buffer :type 'command :prefix "org-ac/" :docstring t)
+;; `org-ac/setup-current-buffer'
+;; Do setup for using org-ac in current buffer.
+;;
+;; *** END auto-documentation
+;; [Note] Functions and variables other than listed above, Those specifications may be changed without notice.
+
+;;; Tested On:
+;;
+;; - Emacs ... GNU Emacs 24.3.1 (i686-pc-linux-gnu, GTK+ Version 3.4.2) of 2013-08-22 on chindi02, modified by Debian
+;; - auto-complete-pcmp.el ... Version 0.0.1
+;; - yaxception.el ... Version 0.1
+;; - log4e.el ... Version 0.2.0
+
+
+;; Enjoy!!!
+
+
+(eval-when-compile (require 'cl))
+(require 'org)
+(require 'auto-complete-pcmp)
+(require 'rx)
+(require 'log4e)
+(require 'yaxception)
+
+(defgroup org-ac nil
+ "Auto completion for org-mode."
+ :group 'org
+ :prefix "org-ac/")
+
+(defcustom org-ac/ac-trigger-command-keys '("\\" "*" "SPC" ":" "[" "+")
+ "Keystrokes for doing `ac-start' with self insert."
+ :type '(repeat string)
+ :group 'org-ac)
+
+
+(log4e:deflogger "org-ac" "%t [%l] %m" "%H:%M:%S" '((fatal . "fatal")
+ (error . "error")
+ (warn . "warn")
+ (info . "info")
+ (debug . "debug")
+ (trace . "trace")))
+(org-ac--log-set-level 'trace)
+
+
+(defun* org-ac--show-message (msg &rest args)
+ (apply 'message (concat "[ORG-AC] " msg) args)
+ nil)
+
+(defun org-ac--complete-close-option-at-current-point ()
+ (let ((pt (point)))
+ (yaxception:$
+ (yaxception:try
+ (org-ac--trace "start complete close option at current point")
+ (when (save-excursion
+ (re-search-backward "#\\+\\(begin\\|BEGIN\\)_\\([a-zA-Z0-9]+\\) *\\=" nil t))
+ (let* ((opennm (match-string-no-properties 1))
+ (typenm (match-string-no-properties 2))
+ (closenm (cond ((string= opennm "begin") "end")
+ ((string= opennm "BEGIN") "END")))
+ (case-fold-search t))
+ (if (or (not (re-search-forward "^[ \t]*#\\+" nil t))
+ (not (re-search-forward (concat "\\=" closenm "_") nil t)))
+ (progn (goto-char pt)
+ (insert "\n#+" closenm "_" typenm)
+ (org-cycle))
+ (let ((currtypenm (if (re-search-forward "\\=\\([a-zA-Z0-9]+\\)" nil t)
+ (match-string-no-properties 1)
+ "")))
+ (backward-delete-char (+ (length closenm)
+ 1
+ (length currtypenm)))
+ (insert closenm "_" typenm)))
+ (goto-char pt))))
+ (yaxception:catch 'error e
+ (org-ac--show-message "Failed complete close option : %s" (yaxception:get-text e))
+ (org-ac--error "failed complete close option at current point : %s\n%s"
+ (yaxception:get-text e)
+ (yaxception:get-stack-trace-string e))
+ (goto-char pt)))))
+
+(defun org-ac--get-link-head-candidates ()
+ (append (ac-pcmp/get-ac-candidates)
+ (mapcar (lambda (x) (concat x ":")) org-link-types)))
+
+(defvar ac-source-org-ac-tex
+ '((candidates . ac-pcmp/get-ac-candidates)
+ (prefix . "\\\\\\([a-zA-Z0-9_-]*\\)")
+ (symbol . "t")
+ (requires . 0)
+ (cache)
+ (action . ac-pcmp/do-ac-action)))
+
+(defvar ac-source-org-ac-head
+ '((candidates . ac-pcmp/get-ac-candidates)
+ (prefix . "[^\r\n*]\\*\\([^\t\r\n]*\\)")
+ (symbol . "h")
+ (requires . 0)
+ (cache)
+ (action . ac-pcmp/do-ac-action)))
+
+(defvar ac-source-org-ac-todo
+ '((candidates . ac-pcmp/get-ac-candidates)
+ (prefix . "^\\*+ \\([a-zA-Z0-9_-]*\\)")
+ (symbol . "d")
+ (requires . 0)
+ (cache)
+ (action . ac-pcmp/do-ac-action)))
+
+(defvar ac-source-org-ac-tag
+ '((candidates . ac-pcmp/get-ac-candidates)
+ (prefix . "[ \t]:\\([a-zA-Z0-9_-]*\\)")
+ (symbol . "t")
+ (requires . 0)
+ (cache)
+ (action . ac-pcmp/do-ac-action)))
+
+(defvar org-ac--regexp-link-head (rx-to-string `(and "["
+ (* (any " \t"))
+ "["
+ (group (* (not (any ":*]")))))))
+(defvar ac-source-org-ac-link-head
+ `((candidates . org-ac--get-link-head-candidates)
+ (prefix . ,org-ac--regexp-link-head)
+ (symbol . "l")
+ (requires . 0)
+ (cache)
+ (action . (lambda ()
+ (ac-pcmp/do-ac-action)
+ (ac-start)))))
+
+(defvar ac-source-org-ac-option
+ '((candidates . ac-pcmp/get-ac-candidates)
+ (prefix . "^[ \t]*#\\+\\([a-zA-Z0-9_:=-]*\\)")
+ (symbol . "o")
+ (requires . 0)
+ (cache)
+ (action . (lambda ()
+ (ac-pcmp/do-ac-action)
+ (org-ac--complete-close-option-at-current-point)
+ (auto-complete '(ac-source-org-ac-option-key))))))
+
+(defvar ac-source-org-ac-option-key
+ '((candidates . ac-pcmp/get-ac-candidates)
+ (prefix . "^[ \t]*#\\+[a-zA-Z0-9_:=-]+ +\\([a-zA-Z0-9_-]*\\)")
+ (symbol . "k")
+ (requires . 0)
+ (cache)
+ (action . ac-pcmp/do-ac-action)))
+
+(defvar ac-source-org-ac-option-options
+ '((candidates . ac-pcmp/get-ac-candidates)
+ (prefix . "^[ \t]*#\\+\\(?:options\\|OPTIONS\\):.* +\\([a-zA-Z0-9_-]*\\)")
+ (symbol . "x")
+ (requires . 0)
+ (cache)
+ (action . ac-pcmp/do-ac-action)))
+
+(defvar ac-source-org-ac-file
+ '((init . (setq ac-filename-cache nil))
+ (candidates . org-ac/file-candidate)
+ (prefix . "\\[file:\\(.*\\)")
+ (symbol . "f")
+ (requires . 0)
+ (action . ac-start)
+ (limit . nil)))
+
+
+;;;###autoload
+(defun org-ac/setup-current-buffer ()
+ "Do setup for using org-ac in current buffer."
+ (interactive)
+ (when (eq major-mode 'org-mode)
+ (loop for stroke in org-ac/ac-trigger-command-keys
+ do (local-set-key (read-kbd-macro stroke) 'ac-pcmp/self-insert-command-with-ac-start))
+ (add-to-list 'ac-sources 'ac-source-org-ac-tex)
+ (add-to-list 'ac-sources 'ac-source-org-ac-head)
+ (add-to-list 'ac-sources 'ac-source-org-ac-todo)
+ (add-to-list 'ac-sources 'ac-source-org-ac-tag)
+ (add-to-list 'ac-sources 'ac-source-org-ac-link-head)
+ (add-to-list 'ac-sources 'ac-source-org-ac-option)
+ (add-to-list 'ac-sources 'ac-source-org-ac-option-key)
+ (add-to-list 'ac-sources 'ac-source-org-ac-option-options)
+ (add-to-list 'ac-sources 'ac-source-org-ac-file)
+ (auto-complete-mode t)))
+
+;;;###autoload
+(defun org-ac/config-default ()
+ "Do setting recommemded configuration."
+ (add-to-list 'ac-modes 'org-mode)
+ (add-hook 'org-mode-hook 'org-ac/setup-current-buffer t))
+
+
+(defun org-ac/file-candidate ()
+ "Adds [file: to the normal file completition, plus allows relative paths"
+ (if (string-match "^[~./]+" ac-prefix)
+ (ac-filename-candidate)
+ (let ((ac-prefix (concat "./" ac-prefix)))
+ (mapcar (lambda (path) (substring path 2))
+ (ac-filename-candidate)))))
+
+
+(provide 'org-ac)
+;;; org-ac.el ends here
diff --git a/.emacs.d/plugins/smex.el b/.emacs.d/plugins/smex.el
new file mode 100644
index 0000000..6d4a72b
--- /dev/null
+++ b/.emacs.d/plugins/smex.el
@@ -0,0 +1,483 @@
+;;; smex.el --- M-x interface with Ido-style fuzzy matching. -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2009-2014 Cornelius Mika and contributors
+;;
+;; Author: Cornelius Mika and contributors
+;; URL: http://github.com/nonsequitur/smex/
+;; Package-Requires: ((emacs "24"))
+;; Version: 3.0
+;; Keywords: convenience, usability
+
+;; This file is not part of GNU Emacs.
+
+;;; License:
+
+;; Licensed under the same terms as Emacs.
+
+;;; Commentary:
+
+;; Quick start:
+;; run (smex-initialize)
+;;
+;; Bind the following commands:
+;; smex, smex-major-mode-commands
+;;
+;; For a detailed introduction see:
+;; http://github.com/nonsequitur/smex/blob/master/README.markdown
+
+;;; Code:
+
+(require 'ido)
+
+(defgroup smex nil
+ "M-x interface with Ido-style fuzzy matching and ranking heuristics."
+ :group 'extensions
+ :group 'convenience
+ :link '(emacs-library-link :tag "Lisp File" "smex.el"))
+
+(defcustom smex-auto-update t
+ "If non-nil, `Smex' checks for new commands each time it is run.
+Turn it off for minor speed improvements on older systems."
+ :type 'boolean
+ :group 'smex)
+
+(defcustom smex-save-file (locate-user-emacs-file "smex-items" ".smex-items")
+ "File in which the smex state is saved between Emacs sessions.
+Variables stored are: `smex-data', `smex-history'.
+Must be set before initializing Smex."
+ :type 'string
+ :group 'smex)
+
+(defcustom smex-history-length 7
+ "Determines on how many recently executed commands
+Smex should keep a record.
+Must be set before initializing Smex."
+ :type 'integer
+ :group 'smex)
+
+(defcustom smex-prompt-string "M-x "
+ "String to display in the Smex prompt."
+ :type 'string
+ :group 'smex)
+
+(defcustom smex-flex-matching t
+ "Enables Ido flex matching. On by default.
+Set this to nil to disable fuzzy matching."
+ :type 'boolean
+ :group 'smex)
+
+(defvar smex-initialized-p nil)
+(defvar smex-cache)
+(defvar smex-ido-cache)
+(defvar smex-data)
+(defvar smex-history)
+(defvar smex-command-count 0)
+(defvar smex-custom-action nil)
+
+;; Check if Smex is supported
+(when (equal (cons 1 1)
+ (ignore-errors
+ (subr-arity (symbol-function 'execute-extended-command))))
+ (error "Your Emacs has a non-elisp version of `execute-extended-command', which is incompatible with Smex"))
+
+;;--------------------------------------------------------------------------------
+;; Smex Interface
+
+;;;###autoload
+(defun smex ()
+ (interactive)
+ (unless smex-initialized-p
+ (smex-initialize))
+ (if (smex-already-running)
+ (smex-update-and-rerun)
+ (and smex-auto-update
+ (smex-detect-new-commands)
+ (smex-update))
+ (smex-read-and-run smex-ido-cache)))
+
+(defun smex-already-running ()
+ (and (boundp 'ido-choice-list)
+ (eql ido-choice-list smex-ido-cache)
+ (minibuffer-window-active-p (selected-window))))
+
+(defun smex-update-and-rerun ()
+ (smex-do-with-selected-item
+ (lambda (_) (smex-update) (smex-read-and-run smex-ido-cache ido-text))))
+
+(defun smex-read-and-run (commands &optional initial-input)
+ (let* ((chosen-item-name (smex-completing-read commands initial-input))
+ (chosen-item (intern chosen-item-name)))
+ (if smex-custom-action
+ (let ((action smex-custom-action))
+ (setq smex-custom-action nil)
+ (funcall action chosen-item))
+ (unwind-protect
+ (with-no-warnings ; Don't warn about interactive use of `execute-extended-command'
+ (execute-extended-command current-prefix-arg chosen-item-name))
+ (smex-rank chosen-item)))))
+
+;;;###autoload
+(defun smex-major-mode-commands ()
+ "Like `smex', but limited to commands that are relevant to the active major mode."
+ (interactive)
+ (unless smex-initialized-p
+ (smex-initialize))
+ (let ((commands (delete-dups (append (smex-extract-commands-from-keymap (current-local-map))
+ (smex-extract-commands-from-features major-mode)))))
+ (setq commands (smex-sort-according-to-cache commands))
+ (setq commands (mapcar #'symbol-name commands))
+ (smex-read-and-run commands)))
+
+(defun smex-completing-read (choices initial-input)
+ (let ((ido-completion-map ido-completion-map)
+ (ido-setup-hook (cons 'smex-prepare-ido-bindings ido-setup-hook))
+ (ido-enable-prefix nil)
+ (ido-enable-flex-matching smex-flex-matching)
+ (ido-max-prospects 10)
+ (minibuffer-completion-table choices))
+ (ido-completing-read (smex-prompt-with-prefix-arg) choices nil nil
+ initial-input 'extended-command-history (car choices))))
+
+(defun smex-prompt-with-prefix-arg ()
+ (if (not current-prefix-arg)
+ smex-prompt-string
+ (concat
+ (if (eq current-prefix-arg '-)
+ "- "
+ (if (integerp current-prefix-arg)
+ (format "%d " current-prefix-arg)
+ (if (= (car current-prefix-arg) 4)
+ "C-u "
+ (format "%d " (car current-prefix-arg)))))
+ smex-prompt-string)))
+
+(defun smex-prepare-ido-bindings ()
+ (define-key ido-completion-map (kbd "TAB") 'minibuffer-complete)
+ (define-key ido-completion-map (kbd "C-h f") 'smex-describe-function)
+ (define-key ido-completion-map (kbd "C-h w") 'smex-where-is)
+ (define-key ido-completion-map (kbd "M-.") 'smex-find-function)
+ (define-key ido-completion-map (kbd "C-a") 'move-beginning-of-line))
+
+;;--------------------------------------------------------------------------------
+;; Cache and Maintenance
+
+(defun smex-rebuild-cache ()
+ (interactive)
+ (setq smex-cache nil)
+
+ ;; Build up list 'new-commands' and later put it at the end of 'smex-cache'.
+ ;; This speeds up sorting.
+ (let (new-commands)
+ (mapatoms (lambda (symbol)
+ (when (commandp symbol)
+ (let ((known-command (assq symbol smex-data)))
+ (if known-command
+ (setq smex-cache (cons known-command smex-cache))
+ (setq new-commands (cons (list symbol) new-commands)))))))
+ (if (eq (length smex-cache) 0)
+ (setq smex-cache new-commands)
+ (setcdr (last smex-cache) new-commands)))
+
+ (setq smex-cache (sort smex-cache 'smex-sorting-rules))
+ (smex-restore-history)
+ (setq smex-ido-cache (smex-convert-for-ido smex-cache)))
+
+(defun smex-convert-for-ido (command-items)
+ (mapcar (lambda (command-item) (symbol-name (car command-item))) command-items))
+
+(defun smex-restore-history ()
+ "Rearranges `smex-cache' according to `smex-history'"
+ (if (> (length smex-history) smex-history-length)
+ (setcdr (nthcdr (- smex-history-length 1) smex-history) nil))
+ (mapc (lambda (command)
+ (unless (eq command (caar smex-cache))
+ (let ((command-cell-position (smex-detect-position
+ smex-cache
+ (lambda (cell)
+ (eq command (caar cell))))))
+ (when command-cell-position
+ (let ((command-cell (smex-remove-nth-cell
+ command-cell-position smex-cache)))
+ (setcdr command-cell smex-cache)
+ (setq smex-cache command-cell))))))
+ (reverse smex-history)))
+
+(defun smex-sort-according-to-cache (list)
+ "Sorts a list of commands by their order in `smex-cache'"
+ (let (sorted)
+ (dolist (command-item smex-cache)
+ (let ((command (car command-item)))
+ (when (memq command list)
+ (setq sorted (cons command sorted))
+ (setq list (delq command list)))))
+ (nreverse (append list sorted))))
+
+(defun smex-update ()
+ (interactive)
+ (smex-save-history)
+ (smex-rebuild-cache))
+
+(defun smex-detect-new-commands ()
+ (let ((i 0))
+ (mapatoms (lambda (symbol) (if (commandp symbol) (setq i (1+ i)))))
+ (unless (= i smex-command-count)
+ (setq smex-command-count i))))
+
+(defun smex-auto-update (&optional idle-time)
+ "Update Smex when Emacs has been idle for IDLE-TIME."
+ (unless idle-time (setq idle-time 60))
+ (run-with-idle-timer idle-time t
+ '(lambda () (if (smex-detect-new-commands) (smex-update)))))
+
+;;;###autoload
+(defun smex-initialize ()
+ (interactive)
+ (unless ido-mode (smex-initialize-ido))
+ (smex-load-save-file)
+ (smex-detect-new-commands)
+ (smex-rebuild-cache)
+ (add-hook 'kill-emacs-hook 'smex-save-to-file)
+ (setq smex-initialized-p t))
+
+(defun smex-initialize-ido ()
+ "Sets up a minimal Ido environment for `ido-completing-read'."
+ (with-no-warnings ; `ido-init-completion-maps' is deprecated in Emacs 25
+ (ido-init-completion-maps))
+ (add-hook 'minibuffer-setup-hook 'ido-minibuffer-setup))
+
+(defsubst smex-save-file-not-empty-p ()
+ (string-match-p "\[^[:space:]\]" (buffer-string)))
+
+(defun smex-load-save-file ()
+ "Loads `smex-history' and `smex-data' from `smex-save-file'"
+ (let ((save-file (expand-file-name smex-save-file)))
+ (if (file-readable-p save-file)
+ (with-temp-buffer
+ (insert-file-contents save-file)
+ (condition-case nil
+ (setq smex-history (read (current-buffer))
+ smex-data (read (current-buffer)))
+ (error (if (smex-save-file-not-empty-p)
+ (error "Invalid data in smex-save-file (%s). Can't restore history."
+ smex-save-file)
+ (unless (boundp 'smex-history) (setq smex-history nil))
+ (unless (boundp 'smex-data) (setq smex-data nil))))))
+ (setq smex-history nil smex-data nil))))
+
+(defun smex-save-history ()
+ "Updates `smex-history'"
+ (setq smex-history nil)
+ (let ((cell smex-cache))
+ (dotimes (_ smex-history-length)
+ (setq smex-history (cons (caar cell) smex-history))
+ (setq cell (cdr cell))))
+ (setq smex-history (nreverse smex-history)))
+
+(defmacro smex-pp (list-var)
+ `(smex-pp* ,list-var ,(symbol-name list-var)))
+
+(defun smex-save-to-file ()
+ (interactive)
+ (smex-save-history)
+ (with-temp-file (expand-file-name smex-save-file)
+ (smex-pp smex-history)
+ (smex-pp smex-data)))
+
+;;--------------------------------------------------------------------------------
+;; Ranking
+
+(defun smex-sorting-rules (command-item other-command-item)
+ "Returns true if COMMAND-ITEM should sort before OTHER-COMMAND-ITEM."
+ (let* ((count (or (cdr command-item ) 0))
+ (other-count (or (cdr other-command-item) 0))
+ (name (car command-item))
+ (other-name (car other-command-item))
+ (length (length (symbol-name name)))
+ (other-length (length (symbol-name other-name))))
+ (or (> count other-count) ; 1. Frequency of use
+ (and (= count other-count)
+ (or (< length other-length) ; 2. Command length
+ (and (= length other-length)
+ (string< name other-name))))))) ; 3. Alphabetical order
+
+(defun smex-rank (command)
+ (let ((command-item (or (assq command smex-cache)
+ ;; Update caches and try again if not found.
+ (progn (smex-update)
+ (assq command smex-cache)))))
+ (when command-item
+ (smex-update-counter command-item)
+
+ ;; Don't touch the cache order if the chosen command
+ ;; has just been execucted previously.
+ (unless (eq command-item (car smex-cache))
+ (let (command-cell
+ (pos (smex-detect-position smex-cache (lambda (cell)
+ (eq command-item (car cell))))))
+ ;; Remove the just executed command.
+ (setq command-cell (smex-remove-nth-cell pos smex-cache))
+ ;; And put it on top of the cache.
+ (setcdr command-cell smex-cache)
+ (setq smex-cache command-cell)
+
+ ;; Repeat the same for the ido cache. Should this be DRYed?
+ (setq command-cell (smex-remove-nth-cell pos smex-ido-cache))
+ (setcdr command-cell smex-ido-cache)
+ (setq smex-ido-cache command-cell)
+
+ ;; Now put the last history item back to its normal place.
+ (smex-sort-item-at smex-history-length))))))
+
+(defun smex-update-counter (command-item)
+ (let ((count (cdr command-item)))
+ (setcdr command-item
+ (if count
+ (1+ count)
+ ;; Else: Command has just been executed for the first time.
+ ;; Add it to `smex-data'.
+ (if smex-data
+ (setcdr (last smex-data) (list command-item))
+ (setq smex-data (list command-item)))
+ 1))))
+
+(defun smex-sort-item-at (n)
+ "Sorts item at position N in `smex-cache'."
+ (let* ((command-cell (nthcdr n smex-cache))
+ (command-item (car command-cell)))
+ (let ((insert-at (smex-detect-position
+ command-cell
+ (lambda (cell)
+ (smex-sorting-rules command-item (car cell))))))
+ ;; TODO: Should we handle the case of 'insert-at' being nil?
+ ;; This will never happen in practice.
+ (when (> insert-at 1)
+ (setq command-cell (smex-remove-nth-cell n smex-cache))
+ ;; smex-cache just got shorter by one element, so subtract '1' from insert-at.
+ (setq insert-at (+ n (- insert-at 1)))
+ (smex-insert-cell command-cell insert-at smex-cache)
+
+ ;; Repeat the same for the ido cache. DRY?
+ (setq command-cell (smex-remove-nth-cell n smex-ido-cache))
+ (smex-insert-cell command-cell insert-at smex-ido-cache)))))
+
+(defun smex-detect-position (cell function)
+ "Detects, relatively to CELL, the position of the cell
+on which FUNCTION returns true.
+Only checks cells after CELL, starting with the cell right after CELL.
+Returns nil when reaching the end of the list."
+ (let ((pos 1))
+ (catch 'break
+ (while t
+ (setq cell (cdr cell))
+ (if (not cell)
+ (throw 'break nil)
+ (if (funcall function cell) (throw 'break pos))
+ (setq pos (1+ pos)))))))
+
+(defun smex-remove-nth-cell (n list)
+ "Removes and returns the Nth cell in LIST."
+ (let* ((previous-cell (nthcdr (- n 1) list))
+ (result (cdr previous-cell)))
+ (setcdr previous-cell (cdr result))
+ result))
+
+(defun smex-insert-cell (new-cell n list)
+ "Inserts cell at position N in LIST."
+ (let* ((cell (nthcdr (- n 1) list))
+ (next-cell (cdr cell)))
+ (setcdr (setcdr cell new-cell) next-cell)))
+
+;;--------------------------------------------------------------------------------
+;; Help and Reference
+
+(defun smex-do-with-selected-item (fn)
+ (setq smex-custom-action fn)
+ (ido-exit-minibuffer))
+
+(defun smex-describe-function ()
+ (interactive)
+ (smex-do-with-selected-item (lambda (chosen)
+ (describe-function chosen)
+ (pop-to-buffer "*Help*"))))
+
+(defun smex-where-is ()
+ (interactive)
+ (smex-do-with-selected-item 'where-is))
+
+(defun smex-find-function ()
+ (interactive)
+ (smex-do-with-selected-item 'find-function))
+
+(defun smex-extract-commands-from-keymap (map)
+ (let (commands)
+ (smex-parse-keymap map commands)
+ commands))
+
+(defun smex-parse-keymap (map commands)
+ (map-keymap (lambda (_binding element)
+ (if (and (listp element) (eq 'keymap (car element)))
+ (smex-parse-keymap element commands)
+ ;; Strings are commands, too. Reject them.
+ (if (and (symbolp element) (commandp element))
+ (push element commands))))
+ map))
+
+(defun smex-extract-commands-from-features (mode)
+ (let ((library-path (symbol-file mode))
+ (mode-name (symbol-name mode))
+ commands)
+
+ (string-match "\\(.+?\\)\\(-mode\\)?$" mode-name)
+ ;; 'lisp-mode' -> 'lisp'
+ (setq mode-name (match-string 1 mode-name))
+ (if (string= mode-name "c") (setq mode-name "cc"))
+ (setq mode-name (regexp-quote mode-name))
+
+ (dolist (feature load-history)
+ (let ((feature-path (car feature)))
+ (when (and feature-path (or (equal feature-path library-path)
+ (string-match mode-name (file-name-nondirectory
+ feature-path))))
+ (dolist (item (cdr feature))
+ (if (and (listp item) (eq 'defun (car item)))
+ (let ((function (cdr item)))
+ (when (commandp function)
+ (setq commands (append commands (list function))))))))))
+ commands))
+
+(defun smex-show-unbound-commands ()
+ "Shows unbound commands in a new buffer,
+sorted by frequency of use."
+ (interactive)
+ (setq smex-data (sort smex-data 'smex-sorting-rules))
+ (let ((unbound-commands (delq nil
+ (mapcar (lambda (command-item)
+ (unless (where-is-internal (car command-item))
+ command-item))
+ smex-data))))
+ (view-buffer-other-window "*Smex: Unbound Commands*")
+ (setq buffer-read-only t)
+ (let ((inhibit-read-only t))
+ (erase-buffer)
+ (smex-pp unbound-commands))
+ (set-buffer-modified-p nil)
+ (goto-char (point-min))))
+
+;; A copy of `ido-pp' that's compatible with lexical bindings
+(defun smex-pp* (list list-name)
+ (let ((print-level nil) (eval-expression-print-level nil)
+ (print-length nil) (eval-expression-print-length nil))
+ (insert "\n;; ----- " list-name " -----\n(\n ")
+ (while list
+ (let* ((elt (car list))
+ (s (if (consp elt) (car elt) elt)))
+ (if (and (stringp s) (= (length s) 0))
+ (setq s nil))
+ (if s
+ (prin1 elt (current-buffer)))
+ (if (and (setq list (cdr list)) s)
+ (insert "\n "))))
+ (insert "\n)\n")))
+
+(provide 'smex)
+;;; smex.el ends here
diff --git a/.emacs.d/smex-items b/.emacs.d/smex-items
new file mode 100644
index 0000000..9ceb053
--- /dev/null
+++ b/.emacs.d/smex-items
@@ -0,0 +1,42 @@
+
+;; ----- smex-history -----
+(
+ goto-line
+ overwrite-mode
+ shell
+ load-theme
+ treemacs
+ inf-ruby-console-auto
+ compile
+)
+
+;; ----- smex-data -----
+(
+ (lsp-install-server . 6)
+ (treemacs . 17)
+ (lsp-java-update-server . 1)
+ (set-frame-font . 1)
+ (lsp-jt-browser . 6)
+ (sort-lines . 5)
+ (lsp-jt-report-open . 17)
+ (treemacs-edit-workspaces . 2)
+ (treemacs-refresh . 1)
+ (treemacs-mode . 3)
+ (menu-set-font . 2)
+ (magit-commit . 1)
+ (column-number-mode . 1)
+ (set-fill-column . 3)
+ (dap-java-debug . 7)
+ (ruby-indent-exp . 1)
+ (ruby-indent-line . 1)
+ (ruby-mode-menu . 1)
+ (inf-ruby-console-auto . 8)
+ (shell . 4)
+ (package-install-selected-packages . 1)
+ (package-install . 1)
+ (compile . 2)
+ (lsp-mode . 2)
+ (load-theme . 14)
+ (overwrite-mode . 1)
+ (goto-line . 2)
+)
diff --git a/.emacs.d/tramp b/.emacs.d/tramp
new file mode 100644
index 0000000..76983ed
--- /dev/null
+++ b/.emacs.d/tramp
@@ -0,0 +1,6 @@
+;; -*- emacs-lisp -*- <21/03/22 00:03:53 /home/yorune/.emacs.d/tramp>
+;; Tramp connection history. Don't change this file.
+;; Run `M-x tramp-cleanup-all-connections' instead.
+
+(((tramp-file-name "sudo" "root" nil "Gentoo" nil nil nil)
+ nil))
diff --git a/.emacs.d/transient/history.el b/.emacs.d/transient/history.el
new file mode 100644
index 0000000..ef0bc18
--- /dev/null
+++ b/.emacs.d/transient/history.el
@@ -0,0 +1,3 @@
+((magit-commit nil)
+ (magit-dispatch nil)
+ (magit-push nil))
diff --git a/.local/bin/Checking-repo b/.local/bin/Checking-repo
new file mode 100755
index 0000000..d50ca53
--- /dev/null
+++ b/.local/bin/Checking-repo
@@ -0,0 +1,104 @@
+#!/usr/bin/env bash
+
+####################################################
+#
+# SRC_PREPARE
+#
+# Marcin Woźniak
+# y0rune@aol.com
+#
+# Last edit: 16-09-2020
+#
+###################################################
+
+mainteiner="Marcin Woźniak"
+
+function scanning(){
+ EUSCAN=$(euscan --nocolor --quiet "$1")
+ if [ -n "$EUSCAN" ]; then
+ echo $1
+ echo "=============== NOW: $(find ./* -mindepth 2 -maxdepth 2 -name ''"$1"'*.ebuild' | sort | tail -1) =================
+ $(echo -e "$EUSCAN" | tail -1)"
+ sleep 1
+ fi
+}
+
+function folder(){
+ cd "$1"; git pull || exit
+
+ PACKAGES=()
+
+ for FILE in */*
+ do
+ PACKAGE=$(echo "$FILE" | grep -Eo '[A-z0-9_-]+$')
+ PACKAGES+=("$PACKAGE")
+ done
+
+ for i in "${PACKAGES[@]}"
+ do
+ scanning "$i" &
+ done
+
+ for j in $(jobs -p)
+ do
+ wait "$j"
+ done
+
+ echo -n ">>> Done scanning $1"
+}
+
+function nofolder(){
+ if [ -z "${1}" ]; then
+ echo "No overlay names given"
+ echo "Please give at least one overlay name as a commandline argument"
+ echo "Exiting"
+ exit 1
+ fi
+
+for overlay in "${@}"
+do
+ for ebuild in $(EIX_LIMIT=0 eix --only-names --in-overlay "${overlay}")
+ do
+ euscan --nocolor --quiet "${ebuild}" &
+ sleep 1
+ done
+
+ for j in $(jobs -p)
+ do
+ wait "$j"
+ done
+
+ echo -n ">>> Done scanning ${overlay}"
+done
+}
+
+function help(){
+ echo "You can use:"
+ echo "* -r or --repo "
+ echo "* -f or --folder "
+ echo
+ echo "Example of usage"
+ echo "./logeuscan -r src_prepare-overlay"
+ echo "./logeuscan -f ~/git/src_prepare-overlay"
+}
+
+function main(){
+ [ "$(whereis eix | wc -w)" -le "1" ] && { echo "The eix is NOT installed"; exit; }
+ [ "$(whereis euscan | wc -w)" -le "1" ] && { echo "The euscan is NOT installed"; exit; }
+ case $1 in
+ -h|--help)
+ help
+ ;;
+ -r|--repo)
+ nofolder "$2" | tee -a "euscan-$(date -I).log"
+ ;;
+ -f|--folder)
+ folder "$2" | tee -a "euscan-$(date -I).log"
+
+ ;;
+ *)
+ echo "No found variable"; echo; help
+ esac
+}
+
+main "$@"
diff --git a/.local/bin/Logs b/.local/bin/Logs
new file mode 120000
index 0000000..554b400
--- /dev/null
+++ b/.local/bin/Logs
@@ -0,0 +1 @@
+emerge-logs
\ No newline at end of file
diff --git a/.local/bin/__pycache__/pwiz.cpython-38.pyc b/.local/bin/__pycache__/pwiz.cpython-38.pyc
new file mode 100644
index 0000000..a1974fc
Binary files /dev/null and b/.local/bin/__pycache__/pwiz.cpython-38.pyc differ
diff --git a/.local/bin/ansible b/.local/bin/ansible
new file mode 100755
index 0000000..5d48dfe
--- /dev/null
+++ b/.local/bin/ansible
@@ -0,0 +1,177 @@
+#!/usr/bin/python3
+# -*- coding: utf-8 -*-
+# (c) 2012, Michael DeHaan
+#
+# This file is part of Ansible
+#
+# Ansible 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.
+#
+# Ansible 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 Ansible. If not, see .
+
+# PYTHON_ARGCOMPLETE_OK
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+__requires__ = ['ansible_core']
+
+
+import errno
+import os
+import shutil
+import sys
+import traceback
+
+from ansible import context
+from ansible.errors import AnsibleError, AnsibleOptionsError, AnsibleParserError
+from ansible.module_utils._text import to_text
+
+
+# Used for determining if the system is running a new enough python version
+# and should only restrict on our documented minimum versions
+_PY38_MIN = sys.version_info[:2] >= (3, 8)
+_PY3_MIN = sys.version_info[:2] >= (3, 5)
+_PY2_MIN = (2, 6) <= sys.version_info[:2] < (3,)
+_PY_MIN = _PY3_MIN or _PY2_MIN
+if not _PY_MIN:
+ raise SystemExit('ERROR: Ansible requires a minimum of Python2 version 2.6 or Python3 version 3.5. Current version: %s' % ''.join(sys.version.splitlines()))
+
+
+class LastResort(object):
+ # OUTPUT OF LAST RESORT
+ def display(self, msg, log_only=None):
+ print(msg, file=sys.stderr)
+
+ def error(self, msg, wrap_text=None):
+ print(msg, file=sys.stderr)
+
+
+if __name__ == '__main__':
+
+ display = LastResort()
+
+ try: # bad ANSIBLE_CONFIG or config options can force ugly stacktrace
+ import ansible.constants as C
+ from ansible.utils.display import Display, initialize_locale
+ except AnsibleOptionsError as e:
+ display.error(to_text(e), wrap_text=False)
+ sys.exit(5)
+
+ initialize_locale()
+
+ cli = None
+ me = os.path.basename(sys.argv[0])
+
+ try:
+ display = Display()
+ if C.CONTROLLER_PYTHON_WARNING and not _PY38_MIN:
+ display.deprecated(
+ (
+ 'Ansible will require Python 3.8 or newer on the controller starting with Ansible 2.12. '
+ 'Current version: %s' % ''.join(sys.version.splitlines())
+ ),
+ version='2.12',
+ collection_name='ansible.builtin',
+ )
+ display.debug("starting run")
+
+ sub = None
+ target = me.split('-')
+ if target[-1][0].isdigit():
+ # Remove any version or python version info as downstreams
+ # sometimes add that
+ target = target[:-1]
+
+ if len(target) > 1:
+ sub = target[1]
+ myclass = "%sCLI" % sub.capitalize()
+ elif target[0] == 'ansible':
+ sub = 'adhoc'
+ myclass = 'AdHocCLI'
+ else:
+ raise AnsibleError("Unknown Ansible alias: %s" % me)
+
+ try:
+ mycli = getattr(__import__("ansible.cli.%s" % sub, fromlist=[myclass]), myclass)
+ except ImportError as e:
+ # ImportError members have changed in py3
+ if 'msg' in dir(e):
+ msg = e.msg
+ else:
+ msg = e.message
+ if msg.endswith(' %s' % sub):
+ raise AnsibleError("Ansible sub-program not implemented: %s" % me)
+ else:
+ raise
+
+ b_ansible_dir = os.path.expanduser(os.path.expandvars(b"~/.ansible"))
+ try:
+ os.mkdir(b_ansible_dir, 0o700)
+ except OSError as exc:
+ if exc.errno != errno.EEXIST:
+ display.warning("Failed to create the directory '%s': %s"
+ % (to_text(b_ansible_dir, errors='surrogate_or_replace'),
+ to_text(exc, errors='surrogate_or_replace')))
+ else:
+ display.debug("Created the '%s' directory" % to_text(b_ansible_dir, errors='surrogate_or_replace'))
+
+ try:
+ args = [to_text(a, errors='surrogate_or_strict') for a in sys.argv]
+ except UnicodeError:
+ display.error('Command line args are not in utf-8, unable to continue. Ansible currently only understands utf-8')
+ display.display(u"The full traceback was:\n\n%s" % to_text(traceback.format_exc()))
+ exit_code = 6
+ else:
+ cli = mycli(args)
+ exit_code = cli.run()
+
+ except AnsibleOptionsError as e:
+ cli.parser.print_help()
+ display.error(to_text(e), wrap_text=False)
+ exit_code = 5
+ except AnsibleParserError as e:
+ display.error(to_text(e), wrap_text=False)
+ exit_code = 4
+# TQM takes care of these, but leaving comment to reserve the exit codes
+# except AnsibleHostUnreachable as e:
+# display.error(str(e))
+# exit_code = 3
+# except AnsibleHostFailed as e:
+# display.error(str(e))
+# exit_code = 2
+ except AnsibleError as e:
+ display.error(to_text(e), wrap_text=False)
+ exit_code = 1
+ except KeyboardInterrupt:
+ display.error("User interrupted execution")
+ exit_code = 99
+ except Exception as e:
+ if C.DEFAULT_DEBUG:
+ # Show raw stacktraces in debug mode, It also allow pdb to
+ # enter post mortem mode.
+ raise
+ have_cli_options = bool(context.CLIARGS)
+ display.error("Unexpected Exception, this is probably a bug: %s" % to_text(e), wrap_text=False)
+ if not have_cli_options or have_cli_options and context.CLIARGS['verbosity'] > 2:
+ log_only = False
+ if hasattr(e, 'orig_exc'):
+ display.vvv('\nexception type: %s' % to_text(type(e.orig_exc)))
+ why = to_text(e.orig_exc)
+ if to_text(e) != why:
+ display.vvv('\noriginal msg: %s' % why)
+ else:
+ display.display("to see the full traceback, use -vvv")
+ log_only = True
+ display.display(u"the full traceback was:\n\n%s" % to_text(traceback.format_exc()), log_only=log_only)
+ exit_code = 250
+
+ sys.exit(exit_code)
diff --git a/.local/bin/ansible-config b/.local/bin/ansible-config
new file mode 120000
index 0000000..cabb1f5
--- /dev/null
+++ b/.local/bin/ansible-config
@@ -0,0 +1 @@
+ansible
\ No newline at end of file
diff --git a/.local/bin/ansible-connection b/.local/bin/ansible-connection
new file mode 100755
index 0000000..2dd431c
--- /dev/null
+++ b/.local/bin/ansible-connection
@@ -0,0 +1,342 @@
+#!/usr/bin/python3
+# Copyright: (c) 2017, Ansible Project
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+from __future__ import (absolute_import, division, print_function)
+
+__metaclass__ = type
+__requires__ = ['ansible_core']
+
+
+import fcntl
+import hashlib
+import os
+import signal
+import socket
+import sys
+import time
+import traceback
+import errno
+import json
+
+from contextlib import contextmanager
+
+from ansible import constants as C
+from ansible.module_utils._text import to_bytes, to_text
+from ansible.module_utils.six import PY3
+from ansible.module_utils.six.moves import cPickle, StringIO
+from ansible.module_utils.connection import Connection, ConnectionError, send_data, recv_data
+from ansible.module_utils.service import fork_process
+from ansible.parsing.ajson import AnsibleJSONEncoder, AnsibleJSONDecoder
+from ansible.playbook.play_context import PlayContext
+from ansible.plugins.loader import connection_loader
+from ansible.utils.path import unfrackpath, makedirs_safe
+from ansible.utils.display import Display
+from ansible.utils.jsonrpc import JsonRpcServer
+
+
+def read_stream(byte_stream):
+ size = int(byte_stream.readline().strip())
+
+ data = byte_stream.read(size)
+ if len(data) < size:
+ raise Exception("EOF found before data was complete")
+
+ data_hash = to_text(byte_stream.readline().strip())
+ if data_hash != hashlib.sha1(data).hexdigest():
+ raise Exception("Read {0} bytes, but data did not match checksum".format(size))
+
+ # restore escaped loose \r characters
+ data = data.replace(br'\r', b'\r')
+
+ return data
+
+
+@contextmanager
+def file_lock(lock_path):
+ """
+ Uses contextmanager to create and release a file lock based on the
+ given path. This allows us to create locks using `with file_lock()`
+ to prevent deadlocks related to failure to unlock properly.
+ """
+
+ lock_fd = os.open(lock_path, os.O_RDWR | os.O_CREAT, 0o600)
+ fcntl.lockf(lock_fd, fcntl.LOCK_EX)
+ yield
+ fcntl.lockf(lock_fd, fcntl.LOCK_UN)
+ os.close(lock_fd)
+
+
+class ConnectionProcess(object):
+ '''
+ The connection process wraps around a Connection object that manages
+ the connection to a remote device that persists over the playbook
+ '''
+ def __init__(self, fd, play_context, socket_path, original_path, task_uuid=None, ansible_playbook_pid=None):
+ self.play_context = play_context
+ self.socket_path = socket_path
+ self.original_path = original_path
+ self._task_uuid = task_uuid
+
+ self.fd = fd
+ self.exception = None
+
+ self.srv = JsonRpcServer()
+ self.sock = None
+
+ self.connection = None
+ self._ansible_playbook_pid = ansible_playbook_pid
+
+ def start(self, variables):
+ try:
+ messages = list()
+ result = {}
+
+ messages.append(('vvvv', 'control socket path is %s' % self.socket_path))
+
+ # If this is a relative path (~ gets expanded later) then plug the
+ # key's path on to the directory we originally came from, so we can
+ # find it now that our cwd is /
+ if self.play_context.private_key_file and self.play_context.private_key_file[0] not in '~/':
+ self.play_context.private_key_file = os.path.join(self.original_path, self.play_context.private_key_file)
+ self.connection = connection_loader.get(self.play_context.connection, self.play_context, '/dev/null',
+ task_uuid=self._task_uuid, ansible_playbook_pid=self._ansible_playbook_pid)
+ self.connection.set_options(var_options=variables)
+
+ self.connection._socket_path = self.socket_path
+ self.srv.register(self.connection)
+ messages.extend([('vvvv', msg) for msg in sys.stdout.getvalue().splitlines()])
+
+ self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ self.sock.bind(self.socket_path)
+ self.sock.listen(1)
+ messages.append(('vvvv', 'local domain socket listeners started successfully'))
+ except Exception as exc:
+ messages.extend(self.connection.pop_messages())
+ result['error'] = to_text(exc)
+ result['exception'] = traceback.format_exc()
+ finally:
+ result['messages'] = messages
+ self.fd.write(json.dumps(result, cls=AnsibleJSONEncoder))
+ self.fd.close()
+
+ def run(self):
+ try:
+ while not self.connection._conn_closed:
+ signal.signal(signal.SIGALRM, self.connect_timeout)
+ signal.signal(signal.SIGTERM, self.handler)
+ signal.alarm(self.connection.get_option('persistent_connect_timeout'))
+
+ self.exception = None
+ (s, addr) = self.sock.accept()
+ signal.alarm(0)
+ signal.signal(signal.SIGALRM, self.command_timeout)
+ while True:
+ data = recv_data(s)
+ if not data:
+ break
+ log_messages = self.connection.get_option('persistent_log_messages')
+
+ if log_messages:
+ display.display("jsonrpc request: %s" % data, log_only=True)
+
+ request = json.loads(to_text(data, errors='surrogate_or_strict'))
+ if request.get('method') == "exec_command" and not self.connection.connected:
+ self.connection._connect()
+
+ signal.alarm(self.connection.get_option('persistent_command_timeout'))
+
+ resp = self.srv.handle_request(data)
+ signal.alarm(0)
+
+ if log_messages:
+ display.display("jsonrpc response: %s" % resp, log_only=True)
+
+ send_data(s, to_bytes(resp))
+
+ s.close()
+
+ except Exception as e:
+ # socket.accept() will raise EINTR if the socket.close() is called
+ if hasattr(e, 'errno'):
+ if e.errno != errno.EINTR:
+ self.exception = traceback.format_exc()
+ else:
+ self.exception = traceback.format_exc()
+
+ finally:
+ # allow time for any exception msg send over socket to receive at other end before shutting down
+ time.sleep(0.1)
+
+ # when done, close the connection properly and cleanup the socket file so it can be recreated
+ self.shutdown()
+
+ def connect_timeout(self, signum, frame):
+ msg = 'persistent connection idle timeout triggered, timeout value is %s secs.\nSee the timeout setting options in the Network Debug and ' \
+ 'Troubleshooting Guide.' % self.connection.get_option('persistent_connect_timeout')
+ display.display(msg, log_only=True)
+ raise Exception(msg)
+
+ def command_timeout(self, signum, frame):
+ msg = 'command timeout triggered, timeout value is %s secs.\nSee the timeout setting options in the Network Debug and Troubleshooting Guide.'\
+ % self.connection.get_option('persistent_command_timeout')
+ display.display(msg, log_only=True)
+ raise Exception(msg)
+
+ def handler(self, signum, frame):
+ msg = 'signal handler called with signal %s.' % signum
+ display.display(msg, log_only=True)
+ raise Exception(msg)
+
+ def shutdown(self):
+ """ Shuts down the local domain socket
+ """
+ lock_path = unfrackpath("%s/.ansible_pc_lock_%s" % os.path.split(self.socket_path))
+ if os.path.exists(self.socket_path):
+ try:
+ if self.sock:
+ self.sock.close()
+ if self.connection:
+ self.connection.close()
+ if self.connection.get_option("persistent_log_messages"):
+ for _level, message in self.connection.pop_messages():
+ display.display(message, log_only=True)
+ except Exception:
+ pass
+ finally:
+ if os.path.exists(self.socket_path):
+ os.remove(self.socket_path)
+ setattr(self.connection, '_socket_path', None)
+ setattr(self.connection, '_connected', False)
+
+ if os.path.exists(lock_path):
+ os.remove(lock_path)
+
+ display.display('shutdown complete', log_only=True)
+
+
+def main():
+ """ Called to initiate the connect to the remote device
+ """
+ rc = 0
+ result = {}
+ messages = list()
+ socket_path = None
+
+ # Need stdin as a byte stream
+ if PY3:
+ stdin = sys.stdin.buffer
+ else:
+ stdin = sys.stdin
+
+ # Note: update the below log capture code after Display.display() is refactored.
+ saved_stdout = sys.stdout
+ sys.stdout = StringIO()
+
+ try:
+ # read the play context data via stdin, which means depickling it
+ vars_data = read_stream(stdin)
+ init_data = read_stream(stdin)
+
+ if PY3:
+ pc_data = cPickle.loads(init_data, encoding='bytes')
+ variables = cPickle.loads(vars_data, encoding='bytes')
+ else:
+ pc_data = cPickle.loads(init_data)
+ variables = cPickle.loads(vars_data)
+
+ play_context = PlayContext()
+ play_context.deserialize(pc_data)
+ display.verbosity = play_context.verbosity
+
+ except Exception as e:
+ rc = 1
+ result.update({
+ 'error': to_text(e),
+ 'exception': traceback.format_exc()
+ })
+
+ if rc == 0:
+ ssh = connection_loader.get('ssh', class_only=True)
+ ansible_playbook_pid = sys.argv[1]
+ task_uuid = sys.argv[2]
+ cp = ssh._create_control_path(play_context.remote_addr, play_context.port, play_context.remote_user, play_context.connection, ansible_playbook_pid)
+ # create the persistent connection dir if need be and create the paths
+ # which we will be using later
+ tmp_path = unfrackpath(C.PERSISTENT_CONTROL_PATH_DIR)
+ makedirs_safe(tmp_path)
+
+ socket_path = unfrackpath(cp % dict(directory=tmp_path))
+ lock_path = unfrackpath("%s/.ansible_pc_lock_%s" % os.path.split(socket_path))
+
+ with file_lock(lock_path):
+ if not os.path.exists(socket_path):
+ messages.append(('vvvv', 'local domain socket does not exist, starting it'))
+ original_path = os.getcwd()
+ r, w = os.pipe()
+ pid = fork_process()
+
+ if pid == 0:
+ try:
+ os.close(r)
+ wfd = os.fdopen(w, 'w')
+ process = ConnectionProcess(wfd, play_context, socket_path, original_path, task_uuid, ansible_playbook_pid)
+ process.start(variables)
+ except Exception:
+ messages.append(('error', traceback.format_exc()))
+ rc = 1
+
+ if rc == 0:
+ process.run()
+ else:
+ process.shutdown()
+
+ sys.exit(rc)
+
+ else:
+ os.close(w)
+ rfd = os.fdopen(r, 'r')
+ data = json.loads(rfd.read(), cls=AnsibleJSONDecoder)
+ messages.extend(data.pop('messages'))
+ result.update(data)
+
+ else:
+ messages.append(('vvvv', 'found existing local domain socket, using it!'))
+ conn = Connection(socket_path)
+ conn.set_options(var_options=variables)
+ pc_data = to_text(init_data)
+ try:
+ conn.update_play_context(pc_data)
+ conn.set_check_prompt(task_uuid)
+ except Exception as exc:
+ # Only network_cli has update_play context and set_check_prompt, so missing this is
+ # not fatal e.g. netconf
+ if isinstance(exc, ConnectionError) and getattr(exc, 'code', None) == -32601:
+ pass
+ else:
+ result.update({
+ 'error': to_text(exc),
+ 'exception': traceback.format_exc()
+ })
+
+ if os.path.exists(socket_path):
+ messages.extend(Connection(socket_path).pop_messages())
+ messages.append(('vvvv', sys.stdout.getvalue()))
+ result.update({
+ 'messages': messages,
+ 'socket_path': socket_path
+ })
+
+ sys.stdout = saved_stdout
+ if 'exception' in result:
+ rc = 1
+ sys.stderr.write(json.dumps(result, cls=AnsibleJSONEncoder))
+ else:
+ rc = 0
+ sys.stdout.write(json.dumps(result, cls=AnsibleJSONEncoder))
+
+ sys.exit(rc)
+
+
+if __name__ == '__main__':
+ display = Display()
+ main()
diff --git a/.local/bin/ansible-console b/.local/bin/ansible-console
new file mode 120000
index 0000000..cabb1f5
--- /dev/null
+++ b/.local/bin/ansible-console
@@ -0,0 +1 @@
+ansible
\ No newline at end of file
diff --git a/.local/bin/ansible-doc b/.local/bin/ansible-doc
new file mode 120000
index 0000000..cabb1f5
--- /dev/null
+++ b/.local/bin/ansible-doc
@@ -0,0 +1 @@
+ansible
\ No newline at end of file
diff --git a/.local/bin/ansible-galaxy b/.local/bin/ansible-galaxy
new file mode 120000
index 0000000..cabb1f5
--- /dev/null
+++ b/.local/bin/ansible-galaxy
@@ -0,0 +1 @@
+ansible
\ No newline at end of file
diff --git a/.local/bin/ansible-inventory b/.local/bin/ansible-inventory
new file mode 120000
index 0000000..cabb1f5
--- /dev/null
+++ b/.local/bin/ansible-inventory
@@ -0,0 +1 @@
+ansible
\ No newline at end of file
diff --git a/.local/bin/ansible-playbook b/.local/bin/ansible-playbook
new file mode 120000
index 0000000..cabb1f5
--- /dev/null
+++ b/.local/bin/ansible-playbook
@@ -0,0 +1 @@
+ansible
\ No newline at end of file
diff --git a/.local/bin/ansible-pull b/.local/bin/ansible-pull
new file mode 120000
index 0000000..cabb1f5
--- /dev/null
+++ b/.local/bin/ansible-pull
@@ -0,0 +1 @@
+ansible
\ No newline at end of file
diff --git a/.local/bin/ansible-test b/.local/bin/ansible-test
new file mode 100755
index 0000000..b96a777
--- /dev/null
+++ b/.local/bin/ansible-test
@@ -0,0 +1,28 @@
+#!/usr/bin/python3
+# PYTHON_ARGCOMPLETE_OK
+"""Command line entry point for ansible-test."""
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import os
+import sys
+
+
+def main():
+ """Main program entry point."""
+ ansible_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+ source_root = os.path.join(ansible_root, 'test', 'lib')
+
+ if os.path.exists(os.path.join(source_root, 'ansible_test', '_internal', 'cli.py')):
+ # running from source, use that version of ansible-test instead of any version that may already be installed
+ sys.path.insert(0, source_root)
+
+ # noinspection PyProtectedMember
+ from ansible_test._internal.cli import main as cli_main
+
+ cli_main()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/.local/bin/ansible-vault b/.local/bin/ansible-vault
new file mode 120000
index 0000000..cabb1f5
--- /dev/null
+++ b/.local/bin/ansible-vault
@@ -0,0 +1 @@
+ansible
\ No newline at end of file
diff --git a/.local/bin/backupSynology b/.local/bin/backupSynology
new file mode 100755
index 0000000..7014b6c
--- /dev/null
+++ b/.local/bin/backupSynology
@@ -0,0 +1,18 @@
+#!/bin/bash
+source ~/.password
+mkdir -p ~/Movies/{Anime,Videos}
+mkdir -p ~/Music
+mkdir -p ~/Collage
+
+#sudo mount -t cifs //192.168.0.220/Mega -o username=${USERNAME},password=${PASSWORD},vers=2\.0 /mnt/Synology
+#sudo mount -t cifs //192.168.0.220/music -o username=${USERNAME},password=${PASSWORD},vers=2\.0 ~/Music
+#sudo mount -t cifs //192.168.0.220/usbshare1/Video -o username=${USERNAME},password=${PASSWORD},vers=2\.0 ~/Movies/Videos
+#sudo mount -t cifs //192.168.0.220/usbshare1/Anime -o username=${USERNAME},password=${PASSWORD},vers=2\.0 ~/Movies/Anime
+#sudo mount -t cifs //192.168.0.220/Studia -o username=${USERNAME},password=${PASSWORD},vers=2\.0 ~/Collage
+
+if [ -e /mnt/Synology/Systems ]
+then
+ echo "Starting $(date)" > ~/.cache/.logSynology
+ sudo rsync -r --bwlimit=1024 --update --progress /usr/mega/ /mnt/Synology >> ~/.cache/.logSynology
+ echo "Ending $(date)" >> ~/.cache/.logSynology
+fi
diff --git a/.local/bin/bin-cp b/.local/bin/bin-cp
new file mode 100755
index 0000000..725956c
--- /dev/null
+++ b/.local/bin/bin-cp
@@ -0,0 +1,8 @@
+#!/bin/bash
+chmod +x *
+chown -R yorune: *
+sudo cp -pr * /bin/
+#sudo cp /etc/bash.bashrc ../configs
+#cp /home/yorune/Arch/configs/zshrc /home/yorune/.zshrc
+#cp /home/yorune/Arch/configs/vimrc /home/yorune/.vimrc
+echo DONE!!
diff --git a/.local/bin/browser-x b/.local/bin/browser-x
new file mode 100755
index 0000000..46a08b9
--- /dev/null
+++ b/.local/bin/browser-x
@@ -0,0 +1,12 @@
+#!/bin/bash
+export GTK_IM_MODULE=ibus
+export XMODIFIERS=@im=ibus
+export QT_IM_MODULE=ibus
+
+KERNEL=$(uname -sr)
+[[ $KERNEL =~ "icrosoft" ]] && "/mnt/c/Program Files/Mozilla Firefox/firefox.exe" "$@"
+[[ $KERNEL =~ "gentoo" ]] && GDK_DPI_SCALE="1.2" firefox-bin "$@"
+#[[ $KERNEL =~ "gentoo" ]] && __NV_PRIME_RENDER_OFFLOAD_PROVIDER=NVIDIA-G0 __GLX_VENDOR_LIBRARY_NAME=nvidia __GL_SYNC_TO_VBLANK=0 GDK_DPI_SCALE="1.2" firefox "$@"
+
+# Firefox
+# media.ffmpeg.vaapi.enabled -> true
diff --git a/.local/bin/chardetect b/.local/bin/chardetect
new file mode 100755
index 0000000..b854f52
--- /dev/null
+++ b/.local/bin/chardetect
@@ -0,0 +1,8 @@
+#!/usr/bin/python3.8
+# -*- coding: utf-8 -*-
+import re
+import sys
+from chardet.cli.chardetect import main
+if __name__ == '__main__':
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+ sys.exit(main())
diff --git a/.local/bin/cleaner b/.local/bin/cleaner
new file mode 100755
index 0000000..755f014
--- /dev/null
+++ b/.local/bin/cleaner
@@ -0,0 +1,15 @@
+#!/bin/bash
+set -x
+sudo rm -rf /var/tmp/portage/*
+sudo rm -rf /var/tmp/binpkgs/*
+sudo rm -rf /var/tmp/genkernel/*
+sudo rm -rf /var/cache/genkernel/*
+sudo emerge -a --depclean
+sudo emerge -a @preserved-rebuild
+sudo eclean -C -q packages
+sudo eclean -C -q -d -t1w distfiles
+sudo revdep-rebuild
+sudo perl-cleaner --all
+sudo etc-update
+sudo env-update
+source /etc/profile
diff --git a/.local/bin/cleanertmp b/.local/bin/cleanertmp
new file mode 100755
index 0000000..f7d56c6
--- /dev/null
+++ b/.local/bin/cleanertmp
@@ -0,0 +1,6 @@
+sudo rm -rf /var/tmp/portage/*
+sudo rm -rf /var/tmp/binpkgs/*
+sudo rm -rf /var/tmp/genkernel/*
+sudo rm -rf /var/cache/genkernel/*
+sudo eclean-dist -d
+sudo eclean-pkg -d
diff --git a/.local/bin/cmus-control b/.local/bin/cmus-control
new file mode 100755
index 0000000..795d0d2
--- /dev/null
+++ b/.local/bin/cmus-control
@@ -0,0 +1,5 @@
+#!/bin/bash
+pkill -RTMIN+11 dwmblocks
+[ "$1" = "play" ] && cmus-remote -u
+[ "$1" = "next" ] && cmus-remote -n
+[ "$1" = "prev" ] && cmus-remote -r
diff --git a/.local/bin/cmus-shell b/.local/bin/cmus-shell
new file mode 100755
index 0000000..35a46ef
--- /dev/null
+++ b/.local/bin/cmus-shell
@@ -0,0 +1,4 @@
+#!/bin/bash
+if ! screen -r -D cmus >/dev/null ; then
+ screen -S cmus /usr/bin/cmus "$@"
+fi
diff --git a/.local/bin/dmenumount b/.local/bin/dmenumount
new file mode 100755
index 0000000..dcdf08f
--- /dev/null
+++ b/.local/bin/dmenumount
@@ -0,0 +1,59 @@
+#!/bin/sh
+# Gives a dmenu prompt to mount unmounted drives.
+# If they're in /etc/fstab, they'll be mounted automatically.
+# Otherwise, you'll be prompted to give a mountpoint from already existsing directories.
+# If you input a novel directory, it will prompt you to create that directory.
+
+getmount() { \
+ [ -z "$chosen" ] && exit 1
+ mp="$(find $1 | dmenu -i -p "Type in mount point.")"
+ [ "$mp" = "" ] && exit 1
+ if [ ! -d "$mp" ]; then
+ mkdiryn=$(printf "No\\nYes" | dmenu -i -p "$mp does not exist. Create it?")
+ [ "$mkdiryn" = "Yes" ] && (mkdir -p "$mp" || sudo -A mkdir -p "$mp")
+ fi
+ }
+
+mountusb() { \
+ chosen="$(echo "$usbdrives" | dmenu -i -p "Mount which drive?" | awk '{print $1}')"
+ sudo -A mount "$chosen" && notify-send "💻 USB mounting" "$chosen mounted." && exit 0
+ alreadymounted=$(lsblk -nrpo "name,type,mountpoint" | awk '$2=="part"&&$3!~/\/boot|\/home$|SWAP/&&length($3)>1{printf "-not \( -path *%s -prune \) \ \n",$3}')
+ getmount "/mnt /media -maxdepth 5 -type d $alreadymounted"
+ partitiontype="$(lsblk -no "fstype" "$chosen")"
+ case "$partitiontype" in
+ "vfat") sudo -A mount -t vfat "$chosen" "$mp" -o rw,umask=0000;;
+ *) sudo -A mount "$chosen" "$mp"; user="$(whoami)"; ug="$(groups | awk '{print $1}')"; sudo -A chown "$user":"$ug" 741 "$mp";;
+ esac
+ notify-send "💻 USB mounting" "$chosen mounted to $mp."
+ }
+
+mountandroid() { \
+ chosen=$(echo "$anddrives" | dmenu -i -p "Which Android device?" | cut -d : -f 1)
+ getmount "/media -maxdepth 3 -type d"
+ sudo simple-mtpfs --device "$chosen" "$mp"
+ notify-send "🤖 Android Mounting" "Android device mounted to $mp."
+ }
+
+asktype() { \
+ case $(printf "USB\\nAndroid" | dmenu -i -p "Mount a USB drive or Android device?") in
+ USB) mountusb ;;
+ Android) mountandroid ;;
+ esac
+ }
+
+anddrives=$(simple-mtpfs -l 2>/dev/null)
+usbdrives="$(lsblk -rpo "name,type,size,mountpoint" | awk '$2=="part"&&$4==""{printf "%s (%s)\n",$1,$3}')"
+
+if [ -z "$usbdrives" ]; then
+ [ -z "$anddrives" ] && echo "No USB drive or Android device detected" && exit
+ echo "Android device(s) detected."
+ mountandroid
+else
+ if [ -z "$anddrives" ]; then
+ echo "USB drive(s) detected."
+ mountusb
+ else
+ echo "Mountable USB drive(s) and Android device(s) detected."
+ asktype
+ fi
+fi
diff --git a/.local/bin/dmenuumount b/.local/bin/dmenuumount
new file mode 100755
index 0000000..dee53e7
--- /dev/null
+++ b/.local/bin/dmenuumount
@@ -0,0 +1,41 @@
+#!/bin/sh
+# A dmenu prompt to unmount drives.
+# Provides you with mounted partitions, select one to unmount.
+# Drives mounted at /, /boot and /home will not be options to unmount.
+
+unmountusb() {
+ [ -z "$drives" ] && exit
+ chosen=$(echo "$drives" | dmenu -i -p "Unmount which drive?" | awk '{print $1}')
+ [ -z "$chosen" ] && exit
+ sudo -A umount "$chosen" && notify-send "💻 USB unmounting" "$chosen unmounted."
+ }
+
+unmountandroid() { \
+ chosen=$(awk '/simple-mtpfs/ {print $2}' /etc/mtab | dmenu -i -p "Unmount which device?")
+ [ -z "$chosen" ] && exit
+ sudo -A umount -l "$chosen" && notify-send "🤖 Android unmounting" "$chosen unmounted."
+ }
+
+asktype() { \
+ case "$(printf "USB\\nAndroid" | dmenu -i -p "Unmount a USB drive or Android device?")" in
+ USB) unmountusb ;;
+ Android) unmountandroid ;;
+ esac
+ }
+
+drives=$(lsblk -nrpo "name,type,size,mountpoint" | awk '$2=="part"&&$4!~/\/boot|\/home$|SWAP/&&length($4)>1{printf "%s (%s)\n",$4,$3}')
+
+if ! grep simple-mtpfs /etc/mtab; then
+ [ -z "$drives" ] && echo "No drives to unmount." && exit
+ echo "Unmountable USB drive detected."
+ unmountusb
+else
+ if [ -z "$drives" ]
+ then
+ echo "Unmountable Android device detected."
+ unmountandroid
+ else
+ echo "Unmountable USB drive(s) and Android device(s) detected."
+ asktype
+ fi
+fi
diff --git a/.local/bin/dmenuunicode b/.local/bin/dmenuunicode
new file mode 100755
index 0000000..6b1a175
--- /dev/null
+++ b/.local/bin/dmenuunicode
@@ -0,0 +1,18 @@
+#!/bin/sh
+# Give dmenu list of all unicode characters to copy.
+# Shows the selected character in dunst if running.
+
+# Must have xclip installed to even show menu.
+xclip -h >/dev/null || exit
+
+chosen=$(grep -v "#" ~/dwm/emoji | dmenu -i -l 20 -fn Monospace-18)
+
+[ "$chosen" != "" ] || exit
+
+c=$(echo "$chosen" | sed "s/ .*//")
+echo "$c" | tr -d '\n' | xclip -selection clipboard
+notify-send "'$c' copied to clipboard." &
+
+s=$(echo "$chosen" | sed "s/.*; //" | awk '{print $1}')
+echo "$s" | tr -d '\n' | xclip
+notify-send "'$s' copied to primary." &
diff --git a/.local/bin/docker-start b/.local/bin/docker-start
new file mode 100755
index 0000000..cf3efe5
--- /dev/null
+++ b/.local/bin/docker-start
@@ -0,0 +1,17 @@
+#!/bin/bash -
+#===============================================================================
+#
+# FILE: docker-start.sh
+#
+# USAGE: ./docker-start.sh
+#
+# OPTIONS: ---
+# REQUIREMENTS: openrc system
+# AUTHOR: Marcin Woźniak, y0rune@aol.com
+# CREATED: 11/08/2020 11:21
+# REVISION: ---
+#===============================================================================
+
+sudo rc-service docker start
+sleep 10
+sudo docker ps
diff --git a/.local/bin/dwmstatusbar b/.local/bin/dwmstatusbar
new file mode 100755
index 0000000..2ce8159
--- /dev/null
+++ b/.local/bin/dwmstatusbar
@@ -0,0 +1,99 @@
+#!/bin/bash
+print_weather() {
+[ "$(stat -c %y "/home/yorune/.config/weatherreport" 2>/dev/null | cut -d' ' -f1)" != "$(date '+%Y-%m-%d')" ] && curl -s "wttr.in/$location" > "/home/yorune/.config/weatherreport"
+
+printf "%s" "$(sed '16q;d' "/home/yorune/.config/weatherreport" | grep -wo "[0-9]*%" | sort -n | sed -e '$!d' | sed -e "s/^/☔ /g" | tr -d '\n')" && sed '13q;d' "/home/yorune/.config/weatherreport" | grep -o "m\\(-\\)*[0-9]\\+" | sort -n -t 'm' -k 2n | sed -e 1b -e '$!d' | tr '\n|m' ' ' | awk '{print" ❄",$1"°","☀",$2"°"}'
+}
+
+print_volume() {
+[ "$(pulsemixer --get-mute)" = "1" ] && printf "🔇" && exit
+vol=$(pulsemixer --get-volume | awk '{print $1}')
+printf "%s%%\\n" "🔊 $vol"
+}
+
+print_wifi(){
+ echo -e "$(cat /sys/class/net/w*/operstate | sed "s/down/❌/;s/up/📶/") $(cat /sys/class/net/e*/operstate | sed "s/down/❌/;s/up/🌐/")"
+ #echo -e "$(cat /sys/class/net/w*/operstate | sed "s/down/❌/;s/up/📶/") $(cat /sys/class/net/e*/operstate | sed "s/down/❌/;s/up/🌐/") $( [[ $(ip -br a show | awk {'print $1'}) =~ "vpn" ]] && echo "📡" || echo "❌" )"
+}
+
+print_temp(){
+ echo -e "🔥 $(sensors | awk '/Core 0/ {print int($3)"°C"}') $(sudo nvidia-smi -q -d temperature | grep --color=no -i "GPU Current" |egrep --color=no -o '[0-9]*')°C"
+}
+
+print_date(){
+ echo -e "🕛 $(date +"%d/%m %H:%M")"
+}
+
+print_mem(){
+ free --mebi | sed -n '2{p;q}' | awk '{printf ("🧠 %2.2fGiB", ( $3 / 1024) )}'
+}
+
+print_music(){
+# Source: https://github.com/joestandring/dwm-bar
+# A dwm_bar function that shows the current artist, track, position, duration, and status from cmus
+# Joe Standring
+# GNU GPLv3
+# Dependencies: cmus
+
+if ps -C cmus > /dev/null; then
+ ARTIST=$(cmus-remote -Q | grep -a '^tag artist' | awk '{gsub("tag artist ", "");print}')
+ TRACK=$(cmus-remote -Q | grep -a '^tag title' | awk '{gsub("tag title ", "");print}')
+ POSITION=$(cmus-remote -Q | grep -a '^position' | awk '{gsub("position ", "");print}')
+ DURATION=$(cmus-remote -Q | grep -a '^duration' | awk '{gsub("duration ", "");print}')
+ STATUS=$(cmus-remote -Q | grep -a '^status' | awk '{gsub("status ", "");print}')
+ SHUFFLE=$(cmus-remote -Q | grep -a '^set shuffle' | awk '{gsub("set shuffle ", "");print}')
+
+ if [ "$STATUS" = "playing" ]; then
+ STATUS="▶"
+ else
+ STATUS="⏸"
+ fi
+
+ #printf "%s%s %s - %s " "$STATUS" "$ARTIST" "$TRACK"
+ printf "%s" "$STATUS"
+ #printf "%0d:%02d/" $((POSITION%3600/60)) $((POSITION%60))
+ #printf "%0d:%02d" $((DURATION%3600/60)) $((DURATION%60))
+ #printf "%s\n"
+fi
+}
+
+print_battery() {
+ # Find the battery level
+ hash acpi || return 0
+ onl="$(acpi -V | grep "on-line")"
+ charge="$(cat /sys/class/power_supply/BAT*/capacity)"
+ time="$(awk '{print $5}' <(acpi))"
+
+ # Determine battery glyph by percentage range
+ if [[ -z $onl && ${charge} -gt 80 ]]; then
+ echo -e "🔋 ${charge}% ${time}"
+ elif [[ -z $onl && ${charge} -le 80 && ${charge} -gt 60 ]]; then
+ echo -e "🔋 ${charge}% ${time}"
+ elif [[ -z $onl && ${charge} -le 60 && ${charge} -gt 40 ]]; then
+ echo -e "🔋 ${charge}% ${time}"
+ elif [[ -z $onl && ${charge} -le 40 && ${charge} -gt 20 ]]; then
+ echo -e "🔋 ${charge}% ${time}"
+ elif [[ -z $onl && ${charge} -le 20 ]]; then
+ echo -e "❗🔋 ${charge}% ${time}"
+ # If charging, use animated glyph
+ else
+ echo -e "🔌"
+ fi
+}
+
+print_mail(){
+ unread="$(find "${XDG_DATA_HOME:-$HOME/.local/share}"/mail/*/[Ii][Nn][Bb][Oo][Xx]/new/* -type f | wc -l 2>/dev/null)"
+ icon="$(cat "/tmp/imapsyncicon_$USER" 2>/dev/null)"
+ [ "$unread" = "1" ] && [ "$icon" = "" ] || echo "📬 $unread$icon"
+}
+
+print_cpu() {
+ cpuUse=$(top -bn1 | grep "Cpu(s)" | sed "s/.*, *\([0-9.]*\)%* id.*/\1/" | awk '{print int(100 - $1)"%"}')
+ echo -e "💻 $cpuUse"
+}
+
+while true
+do
+ xsetroot -name "$(print_music) $(print_temp) $(print_weather) $(print_mail) $(print_cpu) $(print_mem) $(print_wifi) $(print_battery) $(print_volume) $(print_date)"
+ sleep 10
+done
diff --git a/.local/bin/eix-repos-sync b/.local/bin/eix-repos-sync
new file mode 100755
index 0000000..185d16b
--- /dev/null
+++ b/.local/bin/eix-repos-sync
@@ -0,0 +1,2 @@
+#!/bin/bash
+for i in /usr/repos/*/.git; do ( echo $i; cd $i/..; sudo git pull; ); done
diff --git a/.local/bin/emerge-logs b/.local/bin/emerge-logs
new file mode 100755
index 0000000..0fac1ac
--- /dev/null
+++ b/.local/bin/emerge-logs
@@ -0,0 +1,13 @@
+#!/bin/bash
+LOGS=( $(sudo find /var/tmp/portage/ -mindepth 1 -maxdepth 5 -name "build.log") )
+LEN=${#LOGS[@]}
+
+for (( i=0; i<$LEN; i++ ));
+do
+ echo "$(( $i + 1 )) - ${LOGS[$i]}"
+done
+
+echo
+read -p 'Select number to show logs: ' NUMBER
+
+sudo tail -f ${LOGS[$NUMBER-1]}
diff --git a/.local/bin/epylint b/.local/bin/epylint
new file mode 100755
index 0000000..93a57fa
--- /dev/null
+++ b/.local/bin/epylint
@@ -0,0 +1,8 @@
+#!/usr/lib/python-exec/python3.7/python
+# -*- coding: utf-8 -*-
+import re
+import sys
+from pylint import run_epylint
+if __name__ == '__main__':
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+ sys.exit(run_epylint())
diff --git a/.local/bin/euscan b/.local/bin/euscan
new file mode 100755
index 0000000..1fe7f3b
--- /dev/null
+++ b/.local/bin/euscan
@@ -0,0 +1,4 @@
+#!/usr/lib/python-exec/python3.8/python
+# EASY-INSTALL-SCRIPT: 'euscan==9999','euscan'
+__requires__ = 'euscan==9999'
+__import__('pkg_resources').run_script('euscan==9999', 'euscan')
diff --git a/.local/bin/ext b/.local/bin/ext
new file mode 100755
index 0000000..537cd2f
--- /dev/null
+++ b/.local/bin/ext
@@ -0,0 +1,44 @@
+#!/bin/sh
+
+# A general, all-purpose extraction script. Not all extraction programs here
+# are installed by LARBS automatically.
+#
+# Default behavior: Extract archive into new directory
+# Behavior with `-c` option: Extract contents into current directory
+
+while getopts "hc" o; do case "${o}" in
+ c) extracthere="True" ;;
+ *) printf "Options:\\n -c: Extract archive into current directory rather than a new one.\\n" && exit 1 ;;
+esac done
+
+if [ -z "$extracthere" ]; then
+ archive="$(readlink -f "$*")" &&
+ directory="$(echo "$archive" | sed 's/\.[^\/.]*$//')" &&
+ mkdir -p "$directory" &&
+ cd "$directory" || exit 1
+else
+ archive="$(readlink -f "$(echo "$*" | cut -d' ' -f2)" 2>/dev/null)"
+fi
+
+[ -z "$archive" ] && printf "Give archive to extract as argument.\\n" && exit 1
+
+if [ -f "$archive" ] ; then
+ case "$archive" in
+ *.tar.bz2|*.tbz2) tar xvjf "$archive" ;;
+ *.tar.xz) tar -vxf "$archive" ;;
+ *.tar.gz|*.tgz) tar xvzf "$archive" ;;
+ *.lzma) unlzma "$archive" ;;
+ *.bz2) bunzip2 "$archive" ;;
+ *.rar) unrar x -ad "$archive" ;;
+ *.gz) gunzip "$archive" ;;
+ *.tar) tar xvf "$archive" ;;
+ *.zip) unzip "$archive" ;;
+ *.Z) uncompress "$archive" ;;
+ *.7z) 7z x "$archive" ;;
+ *.xz) unxz "$archive" ;;
+ *.exe) cabextract "$archive" ;;
+ *) printf "extract: '%s' - unknown archive method\\n" "$archive" ;;
+ esac
+else
+ printf "File \"%s\" not found.\\n" "$archive"
+fi
diff --git a/.local/bin/f2py b/.local/bin/f2py
new file mode 100755
index 0000000..7d02144
--- /dev/null
+++ b/.local/bin/f2py
@@ -0,0 +1,8 @@
+#!/usr/bin/python3.8
+# -*- coding: utf-8 -*-
+import re
+import sys
+from numpy.f2py.f2py2e import main
+if __name__ == '__main__':
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+ sys.exit(main())
diff --git a/.local/bin/f2py3 b/.local/bin/f2py3
new file mode 100755
index 0000000..7d02144
--- /dev/null
+++ b/.local/bin/f2py3
@@ -0,0 +1,8 @@
+#!/usr/bin/python3.8
+# -*- coding: utf-8 -*-
+import re
+import sys
+from numpy.f2py.f2py2e import main
+if __name__ == '__main__':
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+ sys.exit(main())
diff --git a/.local/bin/f2py3.6 b/.local/bin/f2py3.6
new file mode 100755
index 0000000..bafb207
--- /dev/null
+++ b/.local/bin/f2py3.6
@@ -0,0 +1,8 @@
+#!/usr/bin/python3.6
+# -*- coding: utf-8 -*-
+import re
+import sys
+from numpy.f2py.f2py2e import main
+if __name__ == '__main__':
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+ sys.exit(main())
diff --git a/.local/bin/f2py3.7 b/.local/bin/f2py3.7
new file mode 100755
index 0000000..b06de45
--- /dev/null
+++ b/.local/bin/f2py3.7
@@ -0,0 +1,8 @@
+#!/usr/bin/python3.7
+# -*- coding: utf-8 -*-
+import re
+import sys
+from numpy.f2py.f2py2e import main
+if __name__ == '__main__':
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+ sys.exit(main())
diff --git a/.local/bin/f2py3.8 b/.local/bin/f2py3.8
new file mode 100755
index 0000000..7d02144
--- /dev/null
+++ b/.local/bin/f2py3.8
@@ -0,0 +1,8 @@
+#!/usr/bin/python3.8
+# -*- coding: utf-8 -*-
+import re
+import sys
+from numpy.f2py.f2py2e import main
+if __name__ == '__main__':
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+ sys.exit(main())
diff --git a/.local/bin/flask b/.local/bin/flask
new file mode 100755
index 0000000..128df4e
--- /dev/null
+++ b/.local/bin/flask
@@ -0,0 +1,8 @@
+#!/usr/bin/python3.8
+# -*- coding: utf-8 -*-
+import re
+import sys
+from flask.cli import main
+if __name__ == '__main__':
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+ sys.exit(main())
diff --git a/.local/bin/futurize b/.local/bin/futurize
new file mode 100755
index 0000000..2d9c672
--- /dev/null
+++ b/.local/bin/futurize
@@ -0,0 +1,8 @@
+#!/usr/bin/python3.8
+# -*- coding: utf-8 -*-
+import re
+import sys
+from libfuturize.main import main
+if __name__ == '__main__':
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+ sys.exit(main())
diff --git a/.local/bin/gentoo-test b/.local/bin/gentoo-test
new file mode 100755
index 0000000..785d64d
--- /dev/null
+++ b/.local/bin/gentoo-test
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+sudo mount /dev/disk/by-uuid/c61f258f-5fd1-47f2-a270-bb8aa63b0020 /mnt/gentoo
+
+sleep 5
+sync
+
+if [ -e /dev/disk/by-uuid/c61f258f-5fd1-47f2-a270-bb8aa63b0020 ]
+then
+ sudo mkdir -p /mnt/gentoo/proc
+ sudo mkdir -p /mnt/gentoo/dev
+ sudo mkdir -p /mnt/gentoo/sys
+ sudo mkdir -p /mnt/gentoo/mnt/backup/ccache
+ sudo cp -r $CCACHE_DIR/ccache.conf /mnt/gentoo/mnt/backup/ccache
+ sudo rsync -az -H /etc/portage/ /mnt/gentoo/gentoo/etc/portage/
+ #sudo rsync -az -H /usr/portage/ /mnt/gentoo/gentoo/usr/portage/
+ #sudo rsync -az -H /usr/repos/ /mnt/gentoo/gentoo/usr/repos/
+ sudo mount --types proc /proc /mnt/gentoo/gentoo/proc
+ sudo mount --rbind /sys /mnt/gentoo/gentoo/sys
+ sudo mount --make-rslave /mnt/gentoo/gentoo/sys
+ sudo mount --rbind /dev /mnt/gentoo/gentoo/dev
+ sudo mount --make-rslave /mnt/gentoo/gentoo/dev
+ sleep 5
+ sudo chroot /mnt/gentoo/gentoo /bin/bash
+ # echo "Europe/Warsaw" > /etc/timezone
+ # emerge --config sys-libs/timezone-data
+ # emerge dev-util/ccache app-shells/oh-my-zsh app-admin/sudo app-editors/vim
+fi
diff --git a/.local/bin/geoip b/.local/bin/geoip
new file mode 100755
index 0000000..885fb84
--- /dev/null
+++ b/.local/bin/geoip
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+curl -s ipinfo.io/$1
diff --git a/.local/bin/getforecast b/.local/bin/getforecast
new file mode 100755
index 0000000..03e531b
--- /dev/null
+++ b/.local/bin/getforecast
@@ -0,0 +1,16 @@
+#!/bin/sh
+# Updates weather forecast
+
+FILE="$HOME/.config/.weatherreport.tmp"
+FILER="$HOME/.config/weatherreport"
+location="Kleczew"
+
+curl -s --max-time 10 "wttr.in/$location" > $FILE
+
+if [ -s "$FILE" ]
+then
+ mv $FILE $FILER
+# notify-send " Weather" "The weather forecast has been updated."
+else
+ rm $FILE
+fi
diff --git a/.local/bin/gfetch b/.local/bin/gfetch
new file mode 100755
index 0000000..ed7039c
--- /dev/null
+++ b/.local/bin/gfetch
@@ -0,0 +1,133 @@
+#!/bin/sh
+
+
+# Copyright (c) 2020, XGQT
+# Licensed under the ISC License
+
+# .vir.
+# ,d$$$$$$b.
+# &&&&( )&&&b
+# Q$$$$$$$$$$B
+# "$$$$$$$P
+# ,d$$$$$$P"
+# $$$$$$P
+# `Q$$P"
+
+# gfetch - tiny system info for gentoo
+
+# based on:
+# https://github.com/jschx/ufetch/
+
+
+# INFO
+
+host="$(hostname 2>/dev/null)"
+cpu="$(uname -p 2>/dev/null)"
+kernel="$(uname -sr 2>/dev/null)"
+uptime="$(uptime -p 2>/dev/null | sed 's/up //')"
+shell="$(basename "${SHELL}" 2>/dev/null)"
+
+if [ -f /etc/lsb-release ]
+then
+ os="$(cut -d \" -f 2 < /etc/lsb-release) $(uname -m)"
+elif [ -f /etc/os-release ]
+then
+ os="$(cut -d = -f 2 < /etc/os-release | sed 1q) $(uname -m)"
+else
+ os="Gentoo $(uname -m)"
+fi
+
+if [ -d "${EPREFIX}"/var/db/pkg ]
+then
+ packages="All: $(find "${EPREFIX}"/var/db/pkg/*/* -type d | wc -l)"
+ real="Real: $(find "${EPREFIX}"/var/db/pkg/*/* -type d | grep -c -v -E 'acct-group|acct-user|app-eselect|java-virtuals|media-fonts|virtual')"
+else
+ packages="n/a"
+ real=""
+fi
+
+if [ -f "${EPREFIX}"/var/lib/portage/world ]
+then
+ world="World: $(wc -l < "${EPREFIX}"/var/lib/portage/world)"
+else
+ world=""
+fi
+
+if [ -n "${DE}" ]
+then
+ ui="${DE}"
+ uitype='DE'
+elif [ -n "${WM}" ]
+then
+ ui="${WM}"
+ uitype='WM'
+elif [ -n "${XDG_CURRENT_DESKTOP}" ]
+then
+ ui="${XDG_CURRENT_DESKTOP}"
+ uitype='DE'
+elif [ -n "${DESKTOP_SESSION}" ]
+then
+ ui="${DESKTOP_SESSION}"
+ uitype='DE'
+elif [ -f "${HOME}/.xinitrc" ]
+then
+ ui="$(tail -n 1 "${HOME}/.xinitrc" | cut -d ' ' -f 2)"
+ uitype='WM'
+elif [ -f "${HOME}/.xsession" ]
+then
+ ui="$(tail -n 1 "${HOME}/.xsession" | cut -d ' ' -f 2)"
+ uitype='WM'
+else
+ ui='unknown'
+ uitype='UI'
+fi
+
+
+# Color Definitions
+
+if [ -x "$(command -v tput)" ]; then
+ bold="$(tput bold)"
+ # black="$(tput setaf 0)"
+ # red="$(tput setaf 1)"
+ # green="$(tput setaf 2)"
+ # yellow="$(tput setaf 3)"
+ # blue="$(tput setaf 4)"
+ magenta="$(tput setaf 5)"
+ # cyan="$(tput setaf 6)"
+ white="$(tput setaf 7)"
+ reset="$(tput sgr0)"
+fi
+
+
+# Output color
+
+# labels
+lc="${reset}${bold}${magenta}"
+
+# user and hostname
+nc="${reset}${bold}${magenta}"
+
+# info
+ic="${reset}${bold}${white}"
+
+# first color
+c0="${reset}${bold}${magenta}"
+
+# second color
+c1="${reset}${magenta}"
+
+
+# OUTPUT
+
+cat < beggining.txt
+sed -n '/# END CONFIGURATION:/,/# END OF CONFIGURATION/p' $FINAL > end.txt
+
+for blockedCountries in "${BLOCKEDCOUNTRIES[@]}"
+do
+ mv allzones/$blockedCountries.zone blocked/
+done
+
+for f in blocked/*.zone;
+do
+ echo "Adding $f to iptables.."
+ echo "" >> $TEMP
+ echo "# Adding zone $f" >> $TEMP
+
+ while read line; do
+ echo "iptables -A INPUT -s $line -j DROP" >> $TEMP
+ #echo "-A INPUT -s $line -j DROP" >> $TEMP
+ done < $f
+done
+
+cat beggining.txt > $FINAL
+cat $TEMP >> $FINAL
+echo "" >> $FINAL
+cat end.txt >> $FINAL
+
+rm -rf blocked allzones $TEMP beggining.txt end.txt
+sh ~/.local/bin/iptables-restart
+#systemctl restart iptables
diff --git a/.local/bin/isort b/.local/bin/isort
new file mode 100755
index 0000000..348d6c4
--- /dev/null
+++ b/.local/bin/isort
@@ -0,0 +1,8 @@
+#!/usr/lib/python-exec/python3.7/python
+# -*- coding: utf-8 -*-
+import re
+import sys
+from isort.main import main
+if __name__ == '__main__':
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+ sys.exit(main())
diff --git a/.local/bin/mailsync b/.local/bin/mailsync
new file mode 100755
index 0000000..6f06c05
--- /dev/null
+++ b/.local/bin/mailsync
@@ -0,0 +1,73 @@
+#!/bin/bash
+
+# Sync mail and give notification if there is new mail.
+# Run only if user logged in (prevent cron errors)
+export DISPLAY=:0
+
+pgrep -u "${USER:=$LOGNAME}" >/dev/null || { echo "$USER not logged in; sync will not run."; exit ;}
+# Run only if not already running in other instance
+pgrep -x mbsync >/dev/null && { echo "mbsync is already running." ; exit ;}
+
+# Checks for internet connection and set notification script.
+ping -q -c 1 1.1.1.1 > /dev/null || { echo "No internet connection detected."; exit ;}
+command -v notify-send >/dev/null || echo "Note that \`libnotify\` or \`libnotify-send\` should be installed for pop-up mail notifications with this script."
+
+# Required to display notifications if run as a cronjob:
+DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u)/bus
+
+# For individual configurations:
+[ -d "$HOME/.local/share/password-store" ] && export PASSWORD_STORE_DIR="$HOME/.local/share/password-store"
+
+# Settings are different for MacOS (Darwin) systems.
+if [ "$(uname)" = "Darwin" ]; then
+ notify() { osascript -e "display notification \"$2 in $1\" with title \"You've got Mail\" subtitle \"Account: $account\"" && sleep 2 ;}
+ messageinfo() { osascript "display notification with title \"📧 $from\" subtitle \"$subject\"" ;}
+else
+ notify() { notify-send --app-name="mutt-wizard" "mutt-wizard" "📬 $2 new mail(s) in \`$1\` account." ;}
+ messageinfo() { notify-send --app-name="mutt-wizard" "📧$from:" "$subject" ;}
+fi
+
+# Check account for new mail. Notify if there is new content.
+syncandnotify() {
+ acc="$(echo "$account" | sed "s/.*\///")"
+ mbsync $opts "$acc"
+ new=$(find $(ls $HOME/.local/share/mail/$acc/* | grep -i inbox | sed 's/\://')/new -type f -newer "$HOME/.config/mutt/.mailsynclastrun" 2> /dev/null)
+ newcount=$(echo "$new" | sed '/^\s*$/d' | wc -l)
+ if [ "$newcount" -gt "0" ]; then
+ notify "$acc" "$newcount" &
+ for file in $new; do
+ # Extract subject and sender from mail.
+ from=$(awk '/^From: / && ++n ==1,/^\<.*\>:/' "$file" | perl -CS -MEncode -ne 'print decode("MIME-Header", $_)' | awk '{ $1=""; if (NF>=3)$NF=""; print $0 }' | sed 's/^[[:blank:]]*[\"'\''\<]*//;s/[\"'\''\>]*[[:blank:]]*$//')
+ subject=$(awk '/^Subject: / && ++n == 1,/^\<.*\>: / && ++i == 2' "$file" | head -n-1 | perl -CS -MEncode -ne 'print decode("MIME-Header", $_)' | sed 's/^Subject: //' | sed 's/^{[[:blank:]]*[\"'\''\<]*//;s/[\"'\''\>]*[[:blank:]]*$//' | tr -d '\n')
+ messageinfo &
+ done
+ fi
+}
+
+# Sync accounts passed as argument or all.
+if [ "$#" -eq "0" ]; then
+ accounts="$(awk '/^Channel/ {print $2}' "$HOME/.mbsyncrc")"
+else
+ for arg in "$@"; do
+ [ "${arg%${arg#?}}" = '-' ] && opts="${opts:+${opts} }${arg}" && shift 1
+ done
+ accounts=$*
+fi
+
+echo " 🔃" > /tmp/imapsyncicon_"$USER"
+( pkill -RTMIN+12 "${STATUSBAR:-blocks}" >/dev/null 2>&1 ) 2>/dev/null
+
+# Parallelize multiple accounts
+for account in $accounts
+do
+ syncandnotify &
+done
+
+wait
+rm -f /tmp/imapsyncicon_"$USER"
+( pkill -RTMIN+12 "${STATUSBAR:-blocks}" >/dev/null 2>&1 ) 2>/dev/null
+
+notmuch new 2>/dev/null
+
+#Create a touch file that indicates the time of the last run of mailsync
+touch "$HOME/.config/mutt/.mailsynclastrun"
diff --git a/.local/bin/minecraft-launcher b/.local/bin/minecraft-launcher
new file mode 100755
index 0000000..6829f8f
--- /dev/null
+++ b/.local/bin/minecraft-launcher
@@ -0,0 +1,2 @@
+#!/bin/bash
+__NV_PRIME_RENDER_OFFLOAD_PROVIDER=NVIDIA-G0 __GLX_VENDOR_LIBRARY_NAME=nvidia __GL_SYNC_TO_VBLANK=0 java -jar ~/Downloads/TLauncher-2.75/TLauncher-2.75.jar
diff --git a/.local/bin/mouse-set b/.local/bin/mouse-set
new file mode 100755
index 0000000..8940eba
--- /dev/null
+++ b/.local/bin/mouse-set
@@ -0,0 +1,10 @@
+#!/bin/bash
+choices="Off\nCycle"
+chosen=$(echo -e "$choices" | dmenu -i)
+
+mouse=$(sudo ratbag-command list | awk '{print $1}' | sed 's/://')
+
+case "$chosen" in
+ Off) sudo ratbag-command led 0 set mode off $mouse ;;
+ Cycle) sudo ratbag-command led 0 set mode cycle $mouse && sudo ratbag-command led 0 set rate 25000 $mouse ;;
+esac
diff --git a/.local/bin/night b/.local/bin/night
new file mode 100755
index 0000000..8814e36
--- /dev/null
+++ b/.local/bin/night
@@ -0,0 +1,2 @@
+#!/bin/bash
+redshift -l 52.2327:18.3036 -t 6500:3200&
diff --git a/.local/bin/notify-program b/.local/bin/notify-program
new file mode 100755
index 0000000..5621842
--- /dev/null
+++ b/.local/bin/notify-program
@@ -0,0 +1,4 @@
+#!/bin/bash
+KERNEL=$(uname -sr)
+[[ $KERNEL =~ "icrosoft" ]] && $HOME/.local/bin/wsl-notify "$@"
+[[ $KERNEL =~ "gentoo" ]] && /usr/bin/notify-send "$@"
diff --git a/.local/bin/password-manager b/.local/bin/password-manager
new file mode 100755
index 0000000..5484b7a
--- /dev/null
+++ b/.local/bin/password-manager
@@ -0,0 +1,2 @@
+#!/bin/bash
+QT_SCALE_FACTOR=1.5 keepassxc
diff --git a/.local/bin/pasteurize b/.local/bin/pasteurize
new file mode 100755
index 0000000..fc74807
--- /dev/null
+++ b/.local/bin/pasteurize
@@ -0,0 +1,8 @@
+#!/usr/bin/python3.8
+# -*- coding: utf-8 -*-
+import re
+import sys
+from libpasteurize.main import main
+if __name__ == '__main__':
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+ sys.exit(main())
diff --git a/.local/bin/pwiz.py b/.local/bin/pwiz.py
new file mode 100755
index 0000000..5cdebdb
--- /dev/null
+++ b/.local/bin/pwiz.py
@@ -0,0 +1,223 @@
+#!/usr/bin/python3.8
+
+import datetime
+import sys
+from getpass import getpass
+from optparse import OptionParser
+
+from peewee import *
+from peewee import print_
+from peewee import __version__ as peewee_version
+from playhouse.cockroachdb import CockroachDatabase
+from playhouse.reflection import *
+
+
+HEADER = """from peewee import *%s
+
+database = %s('%s'%s)
+"""
+
+BASE_MODEL = """\
+class BaseModel(Model):
+ class Meta:
+ database = database
+"""
+
+UNKNOWN_FIELD = """\
+class UnknownField(object):
+ def __init__(self, *_, **__): pass
+"""
+
+DATABASE_ALIASES = {
+ CockroachDatabase: ['cockroach', 'cockroachdb', 'crdb'],
+ MySQLDatabase: ['mysql', 'mysqldb'],
+ PostgresqlDatabase: ['postgres', 'postgresql'],
+ SqliteDatabase: ['sqlite', 'sqlite3'],
+}
+
+DATABASE_MAP = dict((value, key)
+ for key in DATABASE_ALIASES
+ for value in DATABASE_ALIASES[key])
+
+def make_introspector(database_type, database_name, **kwargs):
+ if database_type not in DATABASE_MAP:
+ err('Unrecognized database, must be one of: %s' %
+ ', '.join(DATABASE_MAP.keys()))
+ sys.exit(1)
+
+ schema = kwargs.pop('schema', None)
+ DatabaseClass = DATABASE_MAP[database_type]
+ db = DatabaseClass(database_name, **kwargs)
+ return Introspector.from_database(db, schema=schema)
+
+def print_models(introspector, tables=None, preserve_order=False,
+ include_views=False, ignore_unknown=False, snake_case=True):
+ database = introspector.introspect(table_names=tables,
+ include_views=include_views,
+ snake_case=snake_case)
+
+ db_kwargs = introspector.get_database_kwargs()
+ header = HEADER % (
+ introspector.get_additional_imports(),
+ introspector.get_database_class().__name__,
+ introspector.get_database_name(),
+ ', **%s' % repr(db_kwargs) if db_kwargs else '')
+ print_(header)
+
+ if not ignore_unknown:
+ print_(UNKNOWN_FIELD)
+
+ print_(BASE_MODEL)
+
+ def _print_table(table, seen, accum=None):
+ accum = accum or []
+ foreign_keys = database.foreign_keys[table]
+ for foreign_key in foreign_keys:
+ dest = foreign_key.dest_table
+
+ # In the event the destination table has already been pushed
+ # for printing, then we have a reference cycle.
+ if dest in accum and table not in accum:
+ print_('# Possible reference cycle: %s' % dest)
+
+ # If this is not a self-referential foreign key, and we have
+ # not already processed the destination table, do so now.
+ if dest not in seen and dest not in accum:
+ seen.add(dest)
+ if dest != table:
+ _print_table(dest, seen, accum + [table])
+
+ print_('class %s(BaseModel):' % database.model_names[table])
+ columns = database.columns[table].items()
+ if not preserve_order:
+ columns = sorted(columns)
+ primary_keys = database.primary_keys[table]
+ for name, column in columns:
+ skip = all([
+ name in primary_keys,
+ name == 'id',
+ len(primary_keys) == 1,
+ column.field_class in introspector.pk_classes])
+ if skip:
+ continue
+ if column.primary_key and len(primary_keys) > 1:
+ # If we have a CompositeKey, then we do not want to explicitly
+ # mark the columns as being primary keys.
+ column.primary_key = False
+
+ is_unknown = column.field_class is UnknownField
+ if is_unknown and ignore_unknown:
+ disp = '%s - %s' % (column.name, column.raw_column_type or '?')
+ print_(' # %s' % disp)
+ else:
+ print_(' %s' % column.get_field())
+
+ print_('')
+ print_(' class Meta:')
+ print_(' table_name = \'%s\'' % table)
+ multi_column_indexes = database.multi_column_indexes(table)
+ if multi_column_indexes:
+ print_(' indexes = (')
+ for fields, unique in sorted(multi_column_indexes):
+ print_(' ((%s), %s),' % (
+ ', '.join("'%s'" % field for field in fields),
+ unique,
+ ))
+ print_(' )')
+
+ if introspector.schema:
+ print_(' schema = \'%s\'' % introspector.schema)
+ if len(primary_keys) > 1:
+ pk_field_names = sorted([
+ field.name for col, field in columns
+ if col in primary_keys])
+ pk_list = ', '.join("'%s'" % pk for pk in pk_field_names)
+ print_(' primary_key = CompositeKey(%s)' % pk_list)
+ elif not primary_keys:
+ print_(' primary_key = False')
+ print_('')
+
+ seen.add(table)
+
+ seen = set()
+ for table in sorted(database.model_names.keys()):
+ if table not in seen:
+ if not tables or table in tables:
+ _print_table(table, seen)
+
+def print_header(cmd_line, introspector):
+ timestamp = datetime.datetime.now()
+ print_('# Code generated by:')
+ print_('# python -m pwiz %s' % cmd_line)
+ print_('# Date: %s' % timestamp.strftime('%B %d, %Y %I:%M%p'))
+ print_('# Database: %s' % introspector.get_database_name())
+ print_('# Peewee version: %s' % peewee_version)
+ print_('')
+
+
+def err(msg):
+ sys.stderr.write('\033[91m%s\033[0m\n' % msg)
+ sys.stderr.flush()
+
+def get_option_parser():
+ parser = OptionParser(usage='usage: %prog [options] database_name')
+ ao = parser.add_option
+ ao('-H', '--host', dest='host')
+ ao('-p', '--port', dest='port', type='int')
+ ao('-u', '--user', dest='user')
+ ao('-P', '--password', dest='password', action='store_true')
+ engines = sorted(DATABASE_MAP)
+ ao('-e', '--engine', dest='engine', default='postgresql', choices=engines,
+ help=('Database type, e.g. sqlite, mysql, postgresql or cockroachdb. '
+ 'Default is "postgresql".'))
+ ao('-s', '--schema', dest='schema')
+ ao('-t', '--tables', dest='tables',
+ help=('Only generate the specified tables. Multiple table names should '
+ 'be separated by commas.'))
+ ao('-v', '--views', dest='views', action='store_true',
+ help='Generate model classes for VIEWs in addition to tables.')
+ ao('-i', '--info', dest='info', action='store_true',
+ help=('Add database information and other metadata to top of the '
+ 'generated file.'))
+ ao('-o', '--preserve-order', action='store_true', dest='preserve_order',
+ help='Model definition column ordering matches source table.')
+ ao('-I', '--ignore-unknown', action='store_true', dest='ignore_unknown',
+ help='Ignore fields whose type cannot be determined.')
+ ao('-L', '--legacy-naming', action='store_true', dest='legacy_naming',
+ help='Use legacy table- and column-name generation.')
+ return parser
+
+def get_connect_kwargs(options):
+ ops = ('host', 'port', 'user', 'schema')
+ kwargs = dict((o, getattr(options, o)) for o in ops if getattr(options, o))
+ if options.password:
+ kwargs['password'] = getpass()
+ return kwargs
+
+
+if __name__ == '__main__':
+ raw_argv = sys.argv
+
+ parser = get_option_parser()
+ options, args = parser.parse_args()
+
+ if len(args) < 1:
+ err('Missing required parameter "database"')
+ parser.print_help()
+ sys.exit(1)
+
+ connect = get_connect_kwargs(options)
+ database = args[-1]
+
+ tables = None
+ if options.tables:
+ tables = [table.strip() for table in options.tables.split(',')
+ if table.strip()]
+
+ introspector = make_introspector(options.engine, database, **connect)
+ if options.info:
+ cmd_line = ' '.join(raw_argv[1:])
+ print_header(cmd_line, introspector)
+
+ print_models(introspector, tables, options.preserve_order, options.views,
+ options.ignore_unknown, not options.legacy_naming)
diff --git a/.local/bin/pylint b/.local/bin/pylint
new file mode 100755
index 0000000..3ec7b9e
--- /dev/null
+++ b/.local/bin/pylint
@@ -0,0 +1,8 @@
+#!/usr/lib/python-exec/python3.7/python
+# -*- coding: utf-8 -*-
+import re
+import sys
+from pylint import run_pylint
+if __name__ == '__main__':
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+ sys.exit(run_pylint())
diff --git a/.local/bin/pyreverse b/.local/bin/pyreverse
new file mode 100755
index 0000000..e498454
--- /dev/null
+++ b/.local/bin/pyreverse
@@ -0,0 +1,8 @@
+#!/usr/lib/python-exec/python3.7/python
+# -*- coding: utf-8 -*-
+import re
+import sys
+from pylint import run_pyreverse
+if __name__ == '__main__':
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+ sys.exit(run_pyreverse())
diff --git a/.local/bin/rasp b/.local/bin/rasp
new file mode 100755
index 0000000..412a215
--- /dev/null
+++ b/.local/bin/rasp
@@ -0,0 +1,5 @@
+#!/bin/bash
+sudo chown yorune: /mnt/rasp
+sshfs root@192.168.0.222:/mnt/ /mnt/rasp/
+sleep 10
+cd /mnt/rasp
diff --git a/.local/bin/rcon b/.local/bin/rcon
new file mode 100755
index 0000000..7cb5dc5
Binary files /dev/null and b/.local/bin/rcon differ
diff --git a/.local/bin/record b/.local/bin/record
new file mode 100755
index 0000000..47080ee
--- /dev/null
+++ b/.local/bin/record
@@ -0,0 +1,19 @@
+#!/bin/bash
+AUDIO=$(pactl list sources | awk '/Name: alsa_out/{print $2}' | head -n1)
+
+pkill -9 xautolock; xset s 10800
+
+ffmpeg \
+-f pulse \
+-i "$AUDIO" \
+-f x11grab \
+-framerate 60 \
+-r 30 \
+-s 1920x1080 \
+-i :0 \
+-b:v 5500k \
+-c:v h264 -preset ultrafast -c:a aac \
+"$HOME/screencast-$(date '+%y%m%d-%H%M-%S').mp4"
+
+xset s 600&
+xautolock -time 15 -locker slock&
diff --git a/.local/bin/remove-kernel b/.local/bin/remove-kernel
new file mode 100755
index 0000000..95bf885
--- /dev/null
+++ b/.local/bin/remove-kernel
@@ -0,0 +1,6 @@
+#!/bin/bash
+sudo emerge -C "=sys-kernel/gentoo-sources-$1"
+sudo rm -rf /usr/src/linux-$1-gentoo
+sudo rm -rf /lib/modules/$1-gentoo-x86_64
+sudo rm -rf /boot/*$1*
+sudo grub-mkconfig -o /boot/grub/grub.cfg
diff --git a/.local/bin/runJava b/.local/bin/runJava
new file mode 100755
index 0000000..a6fefc5
--- /dev/null
+++ b/.local/bin/runJava
@@ -0,0 +1,2 @@
+#!/bin/sh
+ __NV_PRIME_RENDER_OFFLOAD_PROVIDER=NVIDIA-G0 __GLX_VENDOR_LIBRARY_NAME=nvidia __GL_SYNC_TO_VBLANK=0 DRI_PRIME=1 exec java "$@"
diff --git a/.local/bin/saver-off b/.local/bin/saver-off
new file mode 100755
index 0000000..2e719df
--- /dev/null
+++ b/.local/bin/saver-off
@@ -0,0 +1,4 @@
+#!/bin/bash
+xset s off
+xset -dpms
+pkill -9 xautolock
diff --git a/.local/bin/sb-battery b/.local/bin/sb-battery
new file mode 100755
index 0000000..33e08a9
--- /dev/null
+++ b/.local/bin/sb-battery
@@ -0,0 +1,25 @@
+#!/bin/bash
+print_battery() {
+ # Find the battery level
+ hash acpi || return 0
+ onl="$(acpi -V | grep "on-line")"
+ charge="$(cat /sys/class/power_supply/BAT*/capacity)"
+ time="$(awk '{print $5}' <(acpi))"
+
+ # Determine battery glyph by percentage range
+ if [[ -z $onl && ${charge} -gt 80 ]]; then
+ echo -e "🔋 ${charge}% ${time}"
+ elif [[ -z $onl && ${charge} -le 80 && ${charge} -gt 60 ]]; then
+ echo -e "🔋 ${charge}% ${time}"
+ elif [[ -z $onl && ${charge} -le 60 && ${charge} -gt 40 ]]; then
+ echo -e "🔋 ${charge}% ${time}"
+ elif [[ -z $onl && ${charge} -le 40 && ${charge} -gt 20 ]]; then
+ echo -e "🔋 ${charge}% ${time}"
+ elif [[ -z $onl && ${charge} -le 20 ]]; then
+ echo -e "❗🔋 ${charge}% ${time}"
+ # If charging, use animated glyph
+ else
+ echo -e "🔌"
+ fi
+}
+print_battery
\ No newline at end of file
diff --git a/.local/bin/sb-clock b/.local/bin/sb-clock
new file mode 100755
index 0000000..52e77dc
--- /dev/null
+++ b/.local/bin/sb-clock
@@ -0,0 +1,5 @@
+#!/bin/bash
+print_date(){
+ echo -e "🕛 $(date +"%d/%m %H:%M")"
+}
+print_date
diff --git a/.local/bin/sb-cpu b/.local/bin/sb-cpu
new file mode 100755
index 0000000..917f1ab
--- /dev/null
+++ b/.local/bin/sb-cpu
@@ -0,0 +1,6 @@
+#!/bin/bash
+print_cpu() {
+ cpuUse=$(top -bn1 | grep "Cpu(s)" | sed "s/.*, *\([0-9.]*\)%* id.*/\1/" | awk '{print int(100 - $1)"%"}')
+ echo -e "💻 $cpuUse"
+}
+print_cpu
\ No newline at end of file
diff --git a/.local/bin/sb-mail b/.local/bin/sb-mail
new file mode 100755
index 0000000..0d9d744
--- /dev/null
+++ b/.local/bin/sb-mail
@@ -0,0 +1,7 @@
+#!/bin/bash
+print_mail(){
+ unread="$(find "${XDG_DATA_HOME:-$HOME/.local/share}"/mail/*/[Ii][Nn][Bb][Oo][Xx]/new/* -type f | wc -l 2>/dev/null)"
+ icon="$(cat "/tmp/imapsyncicon_$USER" 2>/dev/null)"
+ [ "$unread" = "1" ] && [ "$icon" = "" ] || echo "📬 $unread$icon"
+}
+print_mail
\ No newline at end of file
diff --git a/.local/bin/sb-mem b/.local/bin/sb-mem
new file mode 100755
index 0000000..9d435ed
--- /dev/null
+++ b/.local/bin/sb-mem
@@ -0,0 +1,5 @@
+#!/bin/bash
+print_mem(){
+ free --mebi | sed -n '2{p;q}' | awk '{printf ("🧠 %2.2fGiB", ( $3 / 1024) )}'
+}
+print_mem
\ No newline at end of file
diff --git a/.local/bin/sb-music b/.local/bin/sb-music
new file mode 100755
index 0000000..8cf5f82
--- /dev/null
+++ b/.local/bin/sb-music
@@ -0,0 +1,30 @@
+#!/bin/bash
+print_music(){
+# Source: https://github.com/joestandring/dwm-bar
+# A dwm_bar function that shows the current artist, track, position, duration, and status from cmus
+# Joe Standring
+# GNU GPLv3
+# Dependencies: cmus
+
+if ps -C cmus > /dev/null; then
+ ARTIST=$(cmus-remote -Q | grep -a '^tag artist' | awk '{gsub("tag artist ", "");print}')
+ TRACK=$(cmus-remote -Q | grep -a '^tag title' | awk '{gsub("tag title ", "");print}')
+ POSITION=$(cmus-remote -Q | grep -a '^position' | awk '{gsub("position ", "");print}')
+ DURATION=$(cmus-remote -Q | grep -a '^duration' | awk '{gsub("duration ", "");print}')
+ STATUS=$(cmus-remote -Q | grep -a '^status' | awk '{gsub("status ", "");print}')
+ SHUFFLE=$(cmus-remote -Q | grep -a '^set shuffle' | awk '{gsub("set shuffle ", "");print}')
+
+ if [ "$STATUS" = "playing" ]; then
+ STATUS="▶"
+ else
+ STATUS="⏸"
+ fi
+
+ #printf "%s%s %s - %s " "$STATUS" "$ARTIST" "$TRACK"
+ printf "%s" "$STATUS"
+ #printf "%0d:%02d/" $((POSITION%3600/60)) $((POSITION%60))
+ #printf "%0d:%02d" $((DURATION%3600/60)) $((DURATION%60))
+ #printf "%s\n"
+fi
+}
+print_music
\ No newline at end of file
diff --git a/.local/bin/sb-network b/.local/bin/sb-network
new file mode 100755
index 0000000..9dca64c
--- /dev/null
+++ b/.local/bin/sb-network
@@ -0,0 +1,5 @@
+#!/bin/bash
+print_wifi(){
+ echo -e "$(cat /sys/class/net/w*/operstate | sed "s/down/❌/;s/up/📶/") $(cat /sys/class/net/e*/operstate | sed "s/down/❌/;s/up/🌐/")"
+}
+print_wifi
\ No newline at end of file
diff --git a/.local/bin/sb-temp b/.local/bin/sb-temp
new file mode 100755
index 0000000..65e2498
--- /dev/null
+++ b/.local/bin/sb-temp
@@ -0,0 +1,5 @@
+#!/bin/bash
+print_temp(){
+ echo -e "🔥 $(sensors | awk '/Core 0/ {print int($3)"°C"}') $(sudo nvidia-smi -q -d temperature | grep --color=no -i "GPU Current" |egrep --color=no -o '[0-9]*')°C"
+}
+print_temp
\ No newline at end of file
diff --git a/.local/bin/sb-volume b/.local/bin/sb-volume
new file mode 100755
index 0000000..c51bd2b
--- /dev/null
+++ b/.local/bin/sb-volume
@@ -0,0 +1,7 @@
+#!/bin/bash
+print_volume() {
+ [ "$(pulsemixer --get-mute)" = "1" ] && printf "🔇" && exit
+ vol=$(pulsemixer --get-volume | awk '{print $1}')
+ printf "%s%%\\n" "🔊 $vol"
+}
+print_volume
diff --git a/.local/bin/sb-weather b/.local/bin/sb-weather
new file mode 100755
index 0000000..a33804b
--- /dev/null
+++ b/.local/bin/sb-weather
@@ -0,0 +1,7 @@
+#!/bin/bash
+print_weather() {
+[ "$(stat -c %y "/home/yorune/.config/weatherreport" 2>/dev/null | cut -d' ' -f1)" != "$(date '+%Y-%m-%d')" ] && curl -s "wttr.in/$location" > "/home/yorune/.config/weatherreport"
+
+printf "%s" "$(sed '16q;d' "/home/yorune/.config/weatherreport" | grep -wo "[0-9]*%" | sort -n | sed -e '$!d' | sed -e "s/^/☔ /g" | tr -d '\n')" && sed '13q;d' "/home/yorune/.config/weatherreport" | grep -o "m\\(-\\)*[0-9]\\+" | sort -n -t 'm' -k 2n | sed -e 1b -e '$!d' | tr '\n|m' ' ' | awk '{print" ❄",$1"°","☀",$2"°"}'
+}
+print_weather
diff --git a/.local/bin/screen-switcher b/.local/bin/screen-switcher
new file mode 100755
index 0000000..d4b7596
--- /dev/null
+++ b/.local/bin/screen-switcher
@@ -0,0 +1,13 @@
+#!/bin/bash
+choices="Left\nRight\nDuplicated\nHDMI\nMonitor\nTV\n"
+
+chosen=$(echo -e "$choices" | dmenu -i)
+
+case "$chosen" in
+ Monitor) mons -o ;;
+ Duplicated) mons -d ;;
+ Left) mons -e left ;;
+ Right) mons -e right ;;
+ HDMI) mons -s ;;
+ TV) xrandr --output HDMI-1 --left-of eDP-1 --mode 1366x768 --rate 60
+esac
diff --git a/.local/bin/screenshot b/.local/bin/screenshot
new file mode 100755
index 0000000..ca085ef
--- /dev/null
+++ b/.local/bin/screenshot
@@ -0,0 +1,7 @@
+FILE="/tmp/`date +%Y%m%d-%H.%M.%S.png`"
+NAME=$(date +%s | sha256sum | base64 | head -c 20 ; echo)
+
+gnome-screenshot -f "$FILE"
+rsync -avz --delete $FILE -e "ssh -p 22" root@209.250.255.224:/var/www/yorune.pl/u/$NAME.png &&
+
+echo -e "https://yorune.pl/u/$NAME.png" | xclip -selection clipboard && notify-send "Screenshot has been updated"
diff --git a/.local/bin/screenshot-area b/.local/bin/screenshot-area
new file mode 100755
index 0000000..50277a3
--- /dev/null
+++ b/.local/bin/screenshot-area
@@ -0,0 +1,7 @@
+FILE="/tmp/`date +%Y%m%d-%H.%M.%S.png`"
+NAME=$(date +%s | sha256sum | base64 | head -c 20 ; echo)
+
+gnome-screenshot --area -f "$FILE"
+rsync -avz --delete $FILE -e "ssh -p 22" root@209.250.255.224:/var/www/yorune.pl/u/$NAME.png &&
+
+echo -e "https://yorune.pl/u/$NAME.png" | xclip -selection clipboard && notify-send "Screenshot has been updated"
diff --git a/.local/bin/set-wallpaper b/.local/bin/set-wallpaper
new file mode 100755
index 0000000..daddc39
--- /dev/null
+++ b/.local/bin/set-wallpaper
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+cp $1 ~/.wall.jpg
+#feh --bg-scale /home/yorune/.wall.jpg
+feh --bg-fill /home/yorune/.wall.jpg
+#wal -i $1 -o wal-set
+notify-send "Wallpaper changed"
diff --git a/.local/bin/shut-sup-rest b/.local/bin/shut-sup-rest
new file mode 100755
index 0000000..6384f9d
--- /dev/null
+++ b/.local/bin/shut-sup-rest
@@ -0,0 +1,11 @@
+#!/bin/bash
+choices="Lock\nSuspend\nReboot\nShutdown"
+
+chosen=$(echo -e "$choices" | dmenu -i)
+
+case "$chosen" in
+ Lock) slock ;;
+ Suspend) sudo s2ram && slock ;;
+ Reboot) sudo reboot ;;
+ Shutdown) sudo shutdown -h now ;;
+esac
diff --git a/.local/bin/speedtest-cli b/.local/bin/speedtest-cli
new file mode 100755
index 0000000..92a2be0
--- /dev/null
+++ b/.local/bin/speedtest-cli
@@ -0,0 +1,2000 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# Copyright 2012 Matt Martz
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import os
+import re
+import csv
+import sys
+import math
+import errno
+import signal
+import socket
+import timeit
+import datetime
+import platform
+import threading
+import xml.parsers.expat
+
+try:
+ import gzip
+ GZIP_BASE = gzip.GzipFile
+except ImportError:
+ gzip = None
+ GZIP_BASE = object
+
+__version__ = '2.1.2'
+
+
+class FakeShutdownEvent(object):
+ """Class to fake a threading.Event.isSet so that users of this module
+ are not required to register their own threading.Event()
+ """
+
+ @staticmethod
+ def isSet():
+ "Dummy method to always return false"""
+ return False
+
+
+# Some global variables we use
+DEBUG = False
+_GLOBAL_DEFAULT_TIMEOUT = object()
+PY25PLUS = sys.version_info[:2] >= (2, 5)
+PY26PLUS = sys.version_info[:2] >= (2, 6)
+PY32PLUS = sys.version_info[:2] >= (3, 2)
+
+# Begin import game to handle Python 2 and Python 3
+try:
+ import json
+except ImportError:
+ try:
+ import simplejson as json
+ except ImportError:
+ json = None
+
+try:
+ import xml.etree.ElementTree as ET
+ try:
+ from xml.etree.ElementTree import _Element as ET_Element
+ except ImportError:
+ pass
+except ImportError:
+ from xml.dom import minidom as DOM
+ from xml.parsers.expat import ExpatError
+ ET = None
+
+try:
+ from urllib2 import (urlopen, Request, HTTPError, URLError,
+ AbstractHTTPHandler, ProxyHandler,
+ HTTPDefaultErrorHandler, HTTPRedirectHandler,
+ HTTPErrorProcessor, OpenerDirector)
+except ImportError:
+ from urllib.request import (urlopen, Request, HTTPError, URLError,
+ AbstractHTTPHandler, ProxyHandler,
+ HTTPDefaultErrorHandler, HTTPRedirectHandler,
+ HTTPErrorProcessor, OpenerDirector)
+
+try:
+ from httplib import HTTPConnection, BadStatusLine
+except ImportError:
+ from http.client import HTTPConnection, BadStatusLine
+
+try:
+ from httplib import HTTPSConnection
+except ImportError:
+ try:
+ from http.client import HTTPSConnection
+ except ImportError:
+ HTTPSConnection = None
+
+try:
+ from httplib import FakeSocket
+except ImportError:
+ FakeSocket = None
+
+try:
+ from Queue import Queue
+except ImportError:
+ from queue import Queue
+
+try:
+ from urlparse import urlparse
+except ImportError:
+ from urllib.parse import urlparse
+
+try:
+ from urlparse import parse_qs
+except ImportError:
+ try:
+ from urllib.parse import parse_qs
+ except ImportError:
+ from cgi import parse_qs
+
+try:
+ from hashlib import md5
+except ImportError:
+ from md5 import md5
+
+try:
+ from argparse import ArgumentParser as ArgParser
+ from argparse import SUPPRESS as ARG_SUPPRESS
+ PARSER_TYPE_INT = int
+ PARSER_TYPE_STR = str
+ PARSER_TYPE_FLOAT = float
+except ImportError:
+ from optparse import OptionParser as ArgParser
+ from optparse import SUPPRESS_HELP as ARG_SUPPRESS
+ PARSER_TYPE_INT = 'int'
+ PARSER_TYPE_STR = 'string'
+ PARSER_TYPE_FLOAT = 'float'
+
+try:
+ from cStringIO import StringIO
+ BytesIO = None
+except ImportError:
+ try:
+ from StringIO import StringIO
+ BytesIO = None
+ except ImportError:
+ from io import StringIO, BytesIO
+
+try:
+ import __builtin__
+except ImportError:
+ import builtins
+ from io import TextIOWrapper, FileIO
+
+ class _Py3Utf8Output(TextIOWrapper):
+ """UTF-8 encoded wrapper around stdout for py3, to override
+ ASCII stdout
+ """
+ def __init__(self, f, **kwargs):
+ buf = FileIO(f.fileno(), 'w')
+ super(_Py3Utf8Output, self).__init__(
+ buf,
+ encoding='utf8',
+ errors='strict'
+ )
+
+ def write(self, s):
+ super(_Py3Utf8Output, self).write(s)
+ self.flush()
+
+ _py3_print = getattr(builtins, 'print')
+ try:
+ _py3_utf8_stdout = _Py3Utf8Output(sys.stdout)
+ _py3_utf8_stderr = _Py3Utf8Output(sys.stderr)
+ except OSError:
+ # sys.stdout/sys.stderr is not a compatible stdout/stderr object
+ # just use it and hope things go ok
+ _py3_utf8_stdout = sys.stdout
+ _py3_utf8_stderr = sys.stderr
+
+ def to_utf8(v):
+ """No-op encode to utf-8 for py3"""
+ return v
+
+ def print_(*args, **kwargs):
+ """Wrapper function for py3 to print, with a utf-8 encoded stdout"""
+ if kwargs.get('file') == sys.stderr:
+ kwargs['file'] = _py3_utf8_stderr
+ else:
+ kwargs['file'] = kwargs.get('file', _py3_utf8_stdout)
+ _py3_print(*args, **kwargs)
+else:
+ del __builtin__
+
+ def to_utf8(v):
+ """Encode value to utf-8 if possible for py2"""
+ try:
+ return v.encode('utf8', 'strict')
+ except AttributeError:
+ return v
+
+ def print_(*args, **kwargs):
+ """The new-style print function for Python 2.4 and 2.5.
+
+ Taken from https://pypi.python.org/pypi/six/
+
+ Modified to set encoding to UTF-8 always, and to flush after write
+ """
+ fp = kwargs.pop("file", sys.stdout)
+ if fp is None:
+ return
+
+ def write(data):
+ if not isinstance(data, basestring):
+ data = str(data)
+ # If the file has an encoding, encode unicode with it.
+ encoding = 'utf8' # Always trust UTF-8 for output
+ if (isinstance(fp, file) and
+ isinstance(data, unicode) and
+ encoding is not None):
+ errors = getattr(fp, "errors", None)
+ if errors is None:
+ errors = "strict"
+ data = data.encode(encoding, errors)
+ fp.write(data)
+ fp.flush()
+ want_unicode = False
+ sep = kwargs.pop("sep", None)
+ if sep is not None:
+ if isinstance(sep, unicode):
+ want_unicode = True
+ elif not isinstance(sep, str):
+ raise TypeError("sep must be None or a string")
+ end = kwargs.pop("end", None)
+ if end is not None:
+ if isinstance(end, unicode):
+ want_unicode = True
+ elif not isinstance(end, str):
+ raise TypeError("end must be None or a string")
+ if kwargs:
+ raise TypeError("invalid keyword arguments to print()")
+ if not want_unicode:
+ for arg in args:
+ if isinstance(arg, unicode):
+ want_unicode = True
+ break
+ if want_unicode:
+ newline = unicode("\n")
+ space = unicode(" ")
+ else:
+ newline = "\n"
+ space = " "
+ if sep is None:
+ sep = space
+ if end is None:
+ end = newline
+ for i, arg in enumerate(args):
+ if i:
+ write(sep)
+ write(arg)
+ write(end)
+
+if PY32PLUS:
+ etree_iter = ET.Element.iter
+elif PY25PLUS:
+ etree_iter = ET_Element.getiterator
+
+if PY26PLUS:
+ thread_is_alive = threading.Thread.is_alive
+else:
+ thread_is_alive = threading.Thread.isAlive
+
+
+# Exception "constants" to support Python 2 through Python 3
+try:
+ import ssl
+ try:
+ CERT_ERROR = (ssl.CertificateError,)
+ except AttributeError:
+ CERT_ERROR = tuple()
+
+ HTTP_ERRORS = (
+ (HTTPError, URLError, socket.error, ssl.SSLError, BadStatusLine) +
+ CERT_ERROR
+ )
+except ImportError:
+ ssl = None
+ HTTP_ERRORS = (HTTPError, URLError, socket.error, BadStatusLine)
+
+
+class SpeedtestException(Exception):
+ """Base exception for this module"""
+
+
+class SpeedtestCLIError(SpeedtestException):
+ """Generic exception for raising errors during CLI operation"""
+
+
+class SpeedtestHTTPError(SpeedtestException):
+ """Base HTTP exception for this module"""
+
+
+class SpeedtestConfigError(SpeedtestException):
+ """Configuration XML is invalid"""
+
+
+class SpeedtestServersError(SpeedtestException):
+ """Servers XML is invalid"""
+
+
+class ConfigRetrievalError(SpeedtestHTTPError):
+ """Could not retrieve config.php"""
+
+
+class ServersRetrievalError(SpeedtestHTTPError):
+ """Could not retrieve speedtest-servers.php"""
+
+
+class InvalidServerIDType(SpeedtestException):
+ """Server ID used for filtering was not an integer"""
+
+
+class NoMatchedServers(SpeedtestException):
+ """No servers matched when filtering"""
+
+
+class SpeedtestMiniConnectFailure(SpeedtestException):
+ """Could not connect to the provided speedtest mini server"""
+
+
+class InvalidSpeedtestMiniServer(SpeedtestException):
+ """Server provided as a speedtest mini server does not actually appear
+ to be a speedtest mini server
+ """
+
+
+class ShareResultsConnectFailure(SpeedtestException):
+ """Could not connect to speedtest.net API to POST results"""
+
+
+class ShareResultsSubmitFailure(SpeedtestException):
+ """Unable to successfully POST results to speedtest.net API after
+ connection
+ """
+
+
+class SpeedtestUploadTimeout(SpeedtestException):
+ """testlength configuration reached during upload
+ Used to ensure the upload halts when no additional data should be sent
+ """
+
+
+class SpeedtestBestServerFailure(SpeedtestException):
+ """Unable to determine best server"""
+
+
+class SpeedtestMissingBestServer(SpeedtestException):
+ """get_best_server not called or not able to determine best server"""
+
+
+def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT,
+ source_address=None):
+ """Connect to *address* and return the socket object.
+
+ Convenience function. Connect to *address* (a 2-tuple ``(host,
+ port)``) and return the socket object. Passing the optional
+ *timeout* parameter will set the timeout on the socket instance
+ before attempting to connect. If no *timeout* is supplied, the
+ global default timeout setting returned by :func:`getdefaulttimeout`
+ is used. If *source_address* is set it must be a tuple of (host, port)
+ for the socket to bind as a source address before making the connection.
+ An host of '' or port 0 tells the OS to use the default.
+
+ Largely vendored from Python 2.7, modified to work with Python 2.4
+ """
+
+ host, port = address
+ err = None
+ for res in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM):
+ af, socktype, proto, canonname, sa = res
+ sock = None
+ try:
+ sock = socket.socket(af, socktype, proto)
+ if timeout is not _GLOBAL_DEFAULT_TIMEOUT:
+ sock.settimeout(float(timeout))
+ if source_address:
+ sock.bind(source_address)
+ sock.connect(sa)
+ return sock
+
+ except socket.error:
+ err = get_exception()
+ if sock is not None:
+ sock.close()
+
+ if err is not None:
+ raise err
+ else:
+ raise socket.error("getaddrinfo returns an empty list")
+
+
+class SpeedtestHTTPConnection(HTTPConnection):
+ """Custom HTTPConnection to support source_address across
+ Python 2.4 - Python 3
+ """
+ def __init__(self, *args, **kwargs):
+ source_address = kwargs.pop('source_address', None)
+ timeout = kwargs.pop('timeout', 10)
+
+ self._tunnel_host = None
+
+ HTTPConnection.__init__(self, *args, **kwargs)
+
+ self.source_address = source_address
+ self.timeout = timeout
+
+ def connect(self):
+ """Connect to the host and port specified in __init__."""
+ try:
+ self.sock = socket.create_connection(
+ (self.host, self.port),
+ self.timeout,
+ self.source_address
+ )
+ except (AttributeError, TypeError):
+ self.sock = create_connection(
+ (self.host, self.port),
+ self.timeout,
+ self.source_address
+ )
+
+ if self._tunnel_host:
+ self._tunnel()
+
+
+if HTTPSConnection:
+ class SpeedtestHTTPSConnection(HTTPSConnection):
+ """Custom HTTPSConnection to support source_address across
+ Python 2.4 - Python 3
+ """
+ default_port = 443
+
+ def __init__(self, *args, **kwargs):
+ source_address = kwargs.pop('source_address', None)
+ timeout = kwargs.pop('timeout', 10)
+
+ self._tunnel_host = None
+
+ HTTPSConnection.__init__(self, *args, **kwargs)
+
+ self.timeout = timeout
+ self.source_address = source_address
+
+ def connect(self):
+ "Connect to a host on a given (SSL) port."
+ try:
+ self.sock = socket.create_connection(
+ (self.host, self.port),
+ self.timeout,
+ self.source_address
+ )
+ except (AttributeError, TypeError):
+ self.sock = create_connection(
+ (self.host, self.port),
+ self.timeout,
+ self.source_address
+ )
+
+ if self._tunnel_host:
+ self._tunnel()
+
+ if ssl:
+ try:
+ kwargs = {}
+ if hasattr(ssl, 'SSLContext'):
+ if self._tunnel_host:
+ kwargs['server_hostname'] = self._tunnel_host
+ else:
+ kwargs['server_hostname'] = self.host
+ self.sock = self._context.wrap_socket(self.sock, **kwargs)
+ except AttributeError:
+ self.sock = ssl.wrap_socket(self.sock)
+ try:
+ self.sock.server_hostname = self.host
+ except AttributeError:
+ pass
+ elif FakeSocket:
+ # Python 2.4/2.5 support
+ try:
+ self.sock = FakeSocket(self.sock, socket.ssl(self.sock))
+ except AttributeError:
+ raise SpeedtestException(
+ 'This version of Python does not support HTTPS/SSL '
+ 'functionality'
+ )
+ else:
+ raise SpeedtestException(
+ 'This version of Python does not support HTTPS/SSL '
+ 'functionality'
+ )
+
+
+def _build_connection(connection, source_address, timeout, context=None):
+ """Cross Python 2.4 - Python 3 callable to build an ``HTTPConnection`` or
+ ``HTTPSConnection`` with the args we need
+
+ Called from ``http(s)_open`` methods of ``SpeedtestHTTPHandler`` or
+ ``SpeedtestHTTPSHandler``
+ """
+ def inner(host, **kwargs):
+ kwargs.update({
+ 'source_address': source_address,
+ 'timeout': timeout
+ })
+ if context:
+ kwargs['context'] = context
+ return connection(host, **kwargs)
+ return inner
+
+
+class SpeedtestHTTPHandler(AbstractHTTPHandler):
+ """Custom ``HTTPHandler`` that can build a ``HTTPConnection`` with the
+ args we need for ``source_address`` and ``timeout``
+ """
+ def __init__(self, debuglevel=0, source_address=None, timeout=10):
+ AbstractHTTPHandler.__init__(self, debuglevel)
+ self.source_address = source_address
+ self.timeout = timeout
+
+ def http_open(self, req):
+ return self.do_open(
+ _build_connection(
+ SpeedtestHTTPConnection,
+ self.source_address,
+ self.timeout
+ ),
+ req
+ )
+
+ http_request = AbstractHTTPHandler.do_request_
+
+
+class SpeedtestHTTPSHandler(AbstractHTTPHandler):
+ """Custom ``HTTPSHandler`` that can build a ``HTTPSConnection`` with the
+ args we need for ``source_address`` and ``timeout``
+ """
+ def __init__(self, debuglevel=0, context=None, source_address=None,
+ timeout=10):
+ AbstractHTTPHandler.__init__(self, debuglevel)
+ self._context = context
+ self.source_address = source_address
+ self.timeout = timeout
+
+ def https_open(self, req):
+ return self.do_open(
+ _build_connection(
+ SpeedtestHTTPSConnection,
+ self.source_address,
+ self.timeout,
+ context=self._context,
+ ),
+ req
+ )
+
+ https_request = AbstractHTTPHandler.do_request_
+
+
+def build_opener(source_address=None, timeout=10):
+ """Function similar to ``urllib2.build_opener`` that will build
+ an ``OpenerDirector`` with the explicit handlers we want,
+ ``source_address`` for binding, ``timeout`` and our custom
+ `User-Agent`
+ """
+
+ printer('Timeout set to %d' % timeout, debug=True)
+
+ if source_address:
+ source_address_tuple = (source_address, 0)
+ printer('Binding to source address: %r' % (source_address_tuple,),
+ debug=True)
+ else:
+ source_address_tuple = None
+
+ handlers = [
+ ProxyHandler(),
+ SpeedtestHTTPHandler(source_address=source_address_tuple,
+ timeout=timeout),
+ SpeedtestHTTPSHandler(source_address=source_address_tuple,
+ timeout=timeout),
+ HTTPDefaultErrorHandler(),
+ HTTPRedirectHandler(),
+ HTTPErrorProcessor()
+ ]
+
+ opener = OpenerDirector()
+ opener.addheaders = [('User-agent', build_user_agent())]
+
+ for handler in handlers:
+ opener.add_handler(handler)
+
+ return opener
+
+
+class GzipDecodedResponse(GZIP_BASE):
+ """A file-like object to decode a response encoded with the gzip
+ method, as described in RFC 1952.
+
+ Largely copied from ``xmlrpclib``/``xmlrpc.client`` and modified
+ to work for py2.4-py3
+ """
+ def __init__(self, response):
+ # response doesn't support tell() and read(), required by
+ # GzipFile
+ if not gzip:
+ raise SpeedtestHTTPError('HTTP response body is gzip encoded, '
+ 'but gzip support is not available')
+ IO = BytesIO or StringIO
+ self.io = IO()
+ while 1:
+ chunk = response.read(1024)
+ if len(chunk) == 0:
+ break
+ self.io.write(chunk)
+ self.io.seek(0)
+ gzip.GzipFile.__init__(self, mode='rb', fileobj=self.io)
+
+ def close(self):
+ try:
+ gzip.GzipFile.close(self)
+ finally:
+ self.io.close()
+
+
+def get_exception():
+ """Helper function to work with py2.4-py3 for getting the current
+ exception in a try/except block
+ """
+ return sys.exc_info()[1]
+
+
+def distance(origin, destination):
+ """Determine distance between 2 sets of [lat,lon] in km"""
+
+ lat1, lon1 = origin
+ lat2, lon2 = destination
+ radius = 6371 # km
+
+ dlat = math.radians(lat2 - lat1)
+ dlon = math.radians(lon2 - lon1)
+ a = (math.sin(dlat / 2) * math.sin(dlat / 2) +
+ math.cos(math.radians(lat1)) *
+ math.cos(math.radians(lat2)) * math.sin(dlon / 2) *
+ math.sin(dlon / 2))
+ c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
+ d = radius * c
+
+ return d
+
+
+def build_user_agent():
+ """Build a Mozilla/5.0 compatible User-Agent string"""
+
+ ua_tuple = (
+ 'Mozilla/5.0',
+ '(%s; U; %s; en-us)' % (platform.platform(),
+ platform.architecture()[0]),
+ 'Python/%s' % platform.python_version(),
+ '(KHTML, like Gecko)',
+ 'speedtest-cli/%s' % __version__
+ )
+ user_agent = ' '.join(ua_tuple)
+ printer('User-Agent: %s' % user_agent, debug=True)
+ return user_agent
+
+
+def build_request(url, data=None, headers=None, bump='0', secure=False):
+ """Build a urllib2 request object
+
+ This function automatically adds a User-Agent header to all requests
+
+ """
+
+ if not headers:
+ headers = {}
+
+ if url[0] == ':':
+ scheme = ('http', 'https')[bool(secure)]
+ schemed_url = '%s%s' % (scheme, url)
+ else:
+ schemed_url = url
+
+ if '?' in url:
+ delim = '&'
+ else:
+ delim = '?'
+
+ # WHO YOU GONNA CALL? CACHE BUSTERS!
+ final_url = '%s%sx=%s.%s' % (schemed_url, delim,
+ int(timeit.time.time() * 1000),
+ bump)
+
+ headers.update({
+ 'Cache-Control': 'no-cache',
+ })
+
+ printer('%s %s' % (('GET', 'POST')[bool(data)], final_url),
+ debug=True)
+
+ return Request(final_url, data=data, headers=headers)
+
+
+def catch_request(request, opener=None):
+ """Helper function to catch common exceptions encountered when
+ establishing a connection with a HTTP/HTTPS request
+
+ """
+
+ if opener:
+ _open = opener.open
+ else:
+ _open = urlopen
+
+ try:
+ uh = _open(request)
+ if request.get_full_url() != uh.geturl():
+ printer('Redirected to %s' % uh.geturl(), debug=True)
+ return uh, False
+ except HTTP_ERRORS:
+ e = get_exception()
+ return None, e
+
+
+def get_response_stream(response):
+ """Helper function to return either a Gzip reader if
+ ``Content-Encoding`` is ``gzip`` otherwise the response itself
+
+ """
+
+ try:
+ getheader = response.headers.getheader
+ except AttributeError:
+ getheader = response.getheader
+
+ if getheader('content-encoding') == 'gzip':
+ return GzipDecodedResponse(response)
+
+ return response
+
+
+def get_attributes_by_tag_name(dom, tag_name):
+ """Retrieve an attribute from an XML document and return it in a
+ consistent format
+
+ Only used with xml.dom.minidom, which is likely only to be used
+ with python versions older than 2.5
+ """
+ elem = dom.getElementsByTagName(tag_name)[0]
+ return dict(list(elem.attributes.items()))
+
+
+def print_dots(shutdown_event):
+ """Built in callback function used by Thread classes for printing
+ status
+ """
+ def inner(current, total, start=False, end=False):
+ if shutdown_event.isSet():
+ return
+
+ sys.stdout.write('.')
+ if current + 1 == total and end is True:
+ sys.stdout.write('\n')
+ sys.stdout.flush()
+ return inner
+
+
+def do_nothing(*args, **kwargs):
+ pass
+
+
+class HTTPDownloader(threading.Thread):
+ """Thread class for retrieving a URL"""
+
+ def __init__(self, i, request, start, timeout, opener=None,
+ shutdown_event=None):
+ threading.Thread.__init__(self)
+ self.request = request
+ self.result = [0]
+ self.starttime = start
+ self.timeout = timeout
+ self.i = i
+ if opener:
+ self._opener = opener.open
+ else:
+ self._opener = urlopen
+
+ if shutdown_event:
+ self._shutdown_event = shutdown_event
+ else:
+ self._shutdown_event = FakeShutdownEvent()
+
+ def run(self):
+ try:
+ if (timeit.default_timer() - self.starttime) <= self.timeout:
+ f = self._opener(self.request)
+ while (not self._shutdown_event.isSet() and
+ (timeit.default_timer() - self.starttime) <=
+ self.timeout):
+ self.result.append(len(f.read(10240)))
+ if self.result[-1] == 0:
+ break
+ f.close()
+ except IOError:
+ pass
+
+
+class HTTPUploaderData(object):
+ """File like object to improve cutting off the upload once the timeout
+ has been reached
+ """
+
+ def __init__(self, length, start, timeout, shutdown_event=None):
+ self.length = length
+ self.start = start
+ self.timeout = timeout
+
+ if shutdown_event:
+ self._shutdown_event = shutdown_event
+ else:
+ self._shutdown_event = FakeShutdownEvent()
+
+ self._data = None
+
+ self.total = [0]
+
+ def pre_allocate(self):
+ chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+ multiplier = int(round(int(self.length) / 36.0))
+ IO = BytesIO or StringIO
+ try:
+ self._data = IO(
+ ('content1=%s' %
+ (chars * multiplier)[0:int(self.length) - 9]
+ ).encode()
+ )
+ except MemoryError:
+ raise SpeedtestCLIError(
+ 'Insufficient memory to pre-allocate upload data. Please '
+ 'use --no-pre-allocate'
+ )
+
+ @property
+ def data(self):
+ if not self._data:
+ self.pre_allocate()
+ return self._data
+
+ def read(self, n=10240):
+ if ((timeit.default_timer() - self.start) <= self.timeout and
+ not self._shutdown_event.isSet()):
+ chunk = self.data.read(n)
+ self.total.append(len(chunk))
+ return chunk
+ else:
+ raise SpeedtestUploadTimeout()
+
+ def __len__(self):
+ return self.length
+
+
+class HTTPUploader(threading.Thread):
+ """Thread class for putting a URL"""
+
+ def __init__(self, i, request, start, size, timeout, opener=None,
+ shutdown_event=None):
+ threading.Thread.__init__(self)
+ self.request = request
+ self.request.data.start = self.starttime = start
+ self.size = size
+ self.result = None
+ self.timeout = timeout
+ self.i = i
+
+ if opener:
+ self._opener = opener.open
+ else:
+ self._opener = urlopen
+
+ if shutdown_event:
+ self._shutdown_event = shutdown_event
+ else:
+ self._shutdown_event = FakeShutdownEvent()
+
+ def run(self):
+ request = self.request
+ try:
+ if ((timeit.default_timer() - self.starttime) <= self.timeout and
+ not self._shutdown_event.isSet()):
+ try:
+ f = self._opener(request)
+ except TypeError:
+ # PY24 expects a string or buffer
+ # This also causes issues with Ctrl-C, but we will concede
+ # for the moment that Ctrl-C on PY24 isn't immediate
+ request = build_request(self.request.get_full_url(),
+ data=request.data.read(self.size))
+ f = self._opener(request)
+ f.read(11)
+ f.close()
+ self.result = sum(self.request.data.total)
+ else:
+ self.result = 0
+ except (IOError, SpeedtestUploadTimeout):
+ self.result = sum(self.request.data.total)
+
+
+class SpeedtestResults(object):
+ """Class for holding the results of a speedtest, including:
+
+ Download speed
+ Upload speed
+ Ping/Latency to test server
+ Data about server that the test was run against
+
+ Additionally this class can return a result data as a dictionary or CSV,
+ as well as submit a POST of the result data to the speedtest.net API
+ to get a share results image link.
+ """
+
+ def __init__(self, download=0, upload=0, ping=0, server=None, client=None,
+ opener=None, secure=False):
+ self.download = download
+ self.upload = upload
+ self.ping = ping
+ if server is None:
+ self.server = {}
+ else:
+ self.server = server
+ self.client = client or {}
+
+ self._share = None
+ self.timestamp = '%sZ' % datetime.datetime.utcnow().isoformat()
+ self.bytes_received = 0
+ self.bytes_sent = 0
+
+ if opener:
+ self._opener = opener
+ else:
+ self._opener = build_opener()
+
+ self._secure = secure
+
+ def __repr__(self):
+ return repr(self.dict())
+
+ def share(self):
+ """POST data to the speedtest.net API to obtain a share results
+ link
+ """
+
+ if self._share:
+ return self._share
+
+ download = int(round(self.download / 1000.0, 0))
+ ping = int(round(self.ping, 0))
+ upload = int(round(self.upload / 1000.0, 0))
+
+ # Build the request to send results back to speedtest.net
+ # We use a list instead of a dict because the API expects parameters
+ # in a certain order
+ api_data = [
+ 'recommendedserverid=%s' % self.server['id'],
+ 'ping=%s' % ping,
+ 'screenresolution=',
+ 'promo=',
+ 'download=%s' % download,
+ 'screendpi=',
+ 'upload=%s' % upload,
+ 'testmethod=http',
+ 'hash=%s' % md5(('%s-%s-%s-%s' %
+ (ping, upload, download, '297aae72'))
+ .encode()).hexdigest(),
+ 'touchscreen=none',
+ 'startmode=pingselect',
+ 'accuracy=1',
+ 'bytesreceived=%s' % self.bytes_received,
+ 'bytessent=%s' % self.bytes_sent,
+ 'serverid=%s' % self.server['id'],
+ ]
+
+ headers = {'Referer': 'http://c.speedtest.net/flash/speedtest.swf'}
+ request = build_request('://www.speedtest.net/api/api.php',
+ data='&'.join(api_data).encode(),
+ headers=headers, secure=self._secure)
+ f, e = catch_request(request, opener=self._opener)
+ if e:
+ raise ShareResultsConnectFailure(e)
+
+ response = f.read()
+ code = f.code
+ f.close()
+
+ if int(code) != 200:
+ raise ShareResultsSubmitFailure('Could not submit results to '
+ 'speedtest.net')
+
+ qsargs = parse_qs(response.decode())
+ resultid = qsargs.get('resultid')
+ if not resultid or len(resultid) != 1:
+ raise ShareResultsSubmitFailure('Could not submit results to '
+ 'speedtest.net')
+
+ self._share = 'http://www.speedtest.net/result/%s.png' % resultid[0]
+
+ return self._share
+
+ def dict(self):
+ """Return dictionary of result data"""
+
+ return {
+ 'download': self.download,
+ 'upload': self.upload,
+ 'ping': self.ping,
+ 'server': self.server,
+ 'timestamp': self.timestamp,
+ 'bytes_sent': self.bytes_sent,
+ 'bytes_received': self.bytes_received,
+ 'share': self._share,
+ 'client': self.client,
+ }
+
+ @staticmethod
+ def csv_header(delimiter=','):
+ """Return CSV Headers"""
+
+ row = ['Server ID', 'Sponsor', 'Server Name', 'Timestamp', 'Distance',
+ 'Ping', 'Download', 'Upload', 'Share', 'IP Address']
+ out = StringIO()
+ writer = csv.writer(out, delimiter=delimiter, lineterminator='')
+ writer.writerow([to_utf8(v) for v in row])
+ return out.getvalue()
+
+ def csv(self, delimiter=','):
+ """Return data in CSV format"""
+
+ data = self.dict()
+ out = StringIO()
+ writer = csv.writer(out, delimiter=delimiter, lineterminator='')
+ row = [data['server']['id'], data['server']['sponsor'],
+ data['server']['name'], data['timestamp'],
+ data['server']['d'], data['ping'], data['download'],
+ data['upload'], self._share or '', self.client['ip']]
+ writer.writerow([to_utf8(v) for v in row])
+ return out.getvalue()
+
+ def json(self, pretty=False):
+ """Return data in JSON format"""
+
+ kwargs = {}
+ if pretty:
+ kwargs.update({
+ 'indent': 4,
+ 'sort_keys': True
+ })
+ return json.dumps(self.dict(), **kwargs)
+
+
+class Speedtest(object):
+ """Class for performing standard speedtest.net testing operations"""
+
+ def __init__(self, config=None, source_address=None, timeout=10,
+ secure=False, shutdown_event=None):
+ self.config = {}
+
+ self._source_address = source_address
+ self._timeout = timeout
+ self._opener = build_opener(source_address, timeout)
+
+ self._secure = secure
+
+ if shutdown_event:
+ self._shutdown_event = shutdown_event
+ else:
+ self._shutdown_event = FakeShutdownEvent()
+
+ self.get_config()
+ if config is not None:
+ self.config.update(config)
+
+ self.servers = {}
+ self.closest = []
+ self._best = {}
+
+ self.results = SpeedtestResults(
+ client=self.config['client'],
+ opener=self._opener,
+ secure=secure,
+ )
+
+ @property
+ def best(self):
+ if not self._best:
+ self.get_best_server()
+ return self._best
+
+ def get_config(self):
+ """Download the speedtest.net configuration and return only the data
+ we are interested in
+ """
+
+ headers = {}
+ if gzip:
+ headers['Accept-Encoding'] = 'gzip'
+ request = build_request('://www.speedtest.net/speedtest-config.php',
+ headers=headers, secure=self._secure)
+ uh, e = catch_request(request, opener=self._opener)
+ if e:
+ raise ConfigRetrievalError(e)
+ configxml_list = []
+
+ stream = get_response_stream(uh)
+
+ while 1:
+ try:
+ configxml_list.append(stream.read(1024))
+ except (OSError, EOFError):
+ raise ConfigRetrievalError(get_exception())
+ if len(configxml_list[-1]) == 0:
+ break
+ stream.close()
+ uh.close()
+
+ if int(uh.code) != 200:
+ return None
+
+ configxml = ''.encode().join(configxml_list)
+
+ printer('Config XML:\n%s' % configxml, debug=True)
+
+ try:
+ try:
+ root = ET.fromstring(configxml)
+ except ET.ParseError:
+ e = get_exception()
+ raise SpeedtestConfigError(
+ 'Malformed speedtest.net configuration: %s' % e
+ )
+ server_config = root.find('server-config').attrib
+ download = root.find('download').attrib
+ upload = root.find('upload').attrib
+ # times = root.find('times').attrib
+ client = root.find('client').attrib
+
+ except AttributeError:
+ try:
+ root = DOM.parseString(configxml)
+ except ExpatError:
+ e = get_exception()
+ raise SpeedtestConfigError(
+ 'Malformed speedtest.net configuration: %s' % e
+ )
+ server_config = get_attributes_by_tag_name(root, 'server-config')
+ download = get_attributes_by_tag_name(root, 'download')
+ upload = get_attributes_by_tag_name(root, 'upload')
+ # times = get_attributes_by_tag_name(root, 'times')
+ client = get_attributes_by_tag_name(root, 'client')
+
+ ignore_servers = list(
+ map(int, server_config['ignoreids'].split(','))
+ )
+
+ ratio = int(upload['ratio'])
+ upload_max = int(upload['maxchunkcount'])
+ up_sizes = [32768, 65536, 131072, 262144, 524288, 1048576, 7340032]
+ sizes = {
+ 'upload': up_sizes[ratio - 1:],
+ 'download': [350, 500, 750, 1000, 1500, 2000, 2500,
+ 3000, 3500, 4000]
+ }
+
+ size_count = len(sizes['upload'])
+
+ upload_count = int(math.ceil(upload_max / size_count))
+
+ counts = {
+ 'upload': upload_count,
+ 'download': int(download['threadsperurl'])
+ }
+
+ threads = {
+ 'upload': int(upload['threads']),
+ 'download': int(server_config['threadcount']) * 2
+ }
+
+ length = {
+ 'upload': int(upload['testlength']),
+ 'download': int(download['testlength'])
+ }
+
+ self.config.update({
+ 'client': client,
+ 'ignore_servers': ignore_servers,
+ 'sizes': sizes,
+ 'counts': counts,
+ 'threads': threads,
+ 'length': length,
+ 'upload_max': upload_count * size_count
+ })
+
+ try:
+ self.lat_lon = (float(client['lat']), float(client['lon']))
+ except ValueError:
+ raise SpeedtestConfigError(
+ 'Unknown location: lat=%r lon=%r' %
+ (client.get('lat'), client.get('lon'))
+ )
+
+ printer('Config:\n%r' % self.config, debug=True)
+
+ return self.config
+
+ def get_servers(self, servers=None, exclude=None):
+ """Retrieve a the list of speedtest.net servers, optionally filtered
+ to servers matching those specified in the ``servers`` argument
+ """
+ if servers is None:
+ servers = []
+
+ if exclude is None:
+ exclude = []
+
+ self.servers.clear()
+
+ for server_list in (servers, exclude):
+ for i, s in enumerate(server_list):
+ try:
+ server_list[i] = int(s)
+ except ValueError:
+ raise InvalidServerIDType(
+ '%s is an invalid server type, must be int' % s
+ )
+
+ urls = [
+ '://www.speedtest.net/speedtest-servers-static.php',
+ 'http://c.speedtest.net/speedtest-servers-static.php',
+ '://www.speedtest.net/speedtest-servers.php',
+ 'http://c.speedtest.net/speedtest-servers.php',
+ ]
+
+ headers = {}
+ if gzip:
+ headers['Accept-Encoding'] = 'gzip'
+
+ errors = []
+ for url in urls:
+ try:
+ request = build_request(
+ '%s?threads=%s' % (url,
+ self.config['threads']['download']),
+ headers=headers,
+ secure=self._secure
+ )
+ uh, e = catch_request(request, opener=self._opener)
+ if e:
+ errors.append('%s' % e)
+ raise ServersRetrievalError()
+
+ stream = get_response_stream(uh)
+
+ serversxml_list = []
+ while 1:
+ try:
+ serversxml_list.append(stream.read(1024))
+ except (OSError, EOFError):
+ raise ServersRetrievalError(get_exception())
+ if len(serversxml_list[-1]) == 0:
+ break
+
+ stream.close()
+ uh.close()
+
+ if int(uh.code) != 200:
+ raise ServersRetrievalError()
+
+ serversxml = ''.encode().join(serversxml_list)
+
+ printer('Servers XML:\n%s' % serversxml, debug=True)
+
+ try:
+ try:
+ try:
+ root = ET.fromstring(serversxml)
+ except ET.ParseError:
+ e = get_exception()
+ raise SpeedtestServersError(
+ 'Malformed speedtest.net server list: %s' % e
+ )
+ elements = etree_iter(root, 'server')
+ except AttributeError:
+ try:
+ root = DOM.parseString(serversxml)
+ except ExpatError:
+ e = get_exception()
+ raise SpeedtestServersError(
+ 'Malformed speedtest.net server list: %s' % e
+ )
+ elements = root.getElementsByTagName('server')
+ except (SyntaxError, xml.parsers.expat.ExpatError):
+ raise ServersRetrievalError()
+
+ for server in elements:
+ try:
+ attrib = server.attrib
+ except AttributeError:
+ attrib = dict(list(server.attributes.items()))
+
+ if servers and int(attrib.get('id')) not in servers:
+ continue
+
+ if (int(attrib.get('id')) in self.config['ignore_servers']
+ or int(attrib.get('id')) in exclude):
+ continue
+
+ try:
+ d = distance(self.lat_lon,
+ (float(attrib.get('lat')),
+ float(attrib.get('lon'))))
+ except Exception:
+ continue
+
+ attrib['d'] = d
+
+ try:
+ self.servers[d].append(attrib)
+ except KeyError:
+ self.servers[d] = [attrib]
+
+ break
+
+ except ServersRetrievalError:
+ continue
+
+ if (servers or exclude) and not self.servers:
+ raise NoMatchedServers()
+
+ return self.servers
+
+ def set_mini_server(self, server):
+ """Instead of querying for a list of servers, set a link to a
+ speedtest mini server
+ """
+
+ urlparts = urlparse(server)
+
+ name, ext = os.path.splitext(urlparts[2])
+ if ext:
+ url = os.path.dirname(server)
+ else:
+ url = server
+
+ request = build_request(url)
+ uh, e = catch_request(request, opener=self._opener)
+ if e:
+ raise SpeedtestMiniConnectFailure('Failed to connect to %s' %
+ server)
+ else:
+ text = uh.read()
+ uh.close()
+
+ extension = re.findall('upload_?[Ee]xtension: "([^"]+)"',
+ text.decode())
+ if not extension:
+ for ext in ['php', 'asp', 'aspx', 'jsp']:
+ try:
+ f = self._opener.open(
+ '%s/speedtest/upload.%s' % (url, ext)
+ )
+ except Exception:
+ pass
+ else:
+ data = f.read().strip().decode()
+ if (f.code == 200 and
+ len(data.splitlines()) == 1 and
+ re.match('size=[0-9]', data)):
+ extension = [ext]
+ break
+ if not urlparts or not extension:
+ raise InvalidSpeedtestMiniServer('Invalid Speedtest Mini Server: '
+ '%s' % server)
+
+ self.servers = [{
+ 'sponsor': 'Speedtest Mini',
+ 'name': urlparts[1],
+ 'd': 0,
+ 'url': '%s/speedtest/upload.%s' % (url.rstrip('/'), extension[0]),
+ 'latency': 0,
+ 'id': 0
+ }]
+
+ return self.servers
+
+ def get_closest_servers(self, limit=5):
+ """Limit servers to the closest speedtest.net servers based on
+ geographic distance
+ """
+
+ if not self.servers:
+ self.get_servers()
+
+ for d in sorted(self.servers.keys()):
+ for s in self.servers[d]:
+ self.closest.append(s)
+ if len(self.closest) == limit:
+ break
+ else:
+ continue
+ break
+
+ printer('Closest Servers:\n%r' % self.closest, debug=True)
+ return self.closest
+
+ def get_best_server(self, servers=None):
+ """Perform a speedtest.net "ping" to determine which speedtest.net
+ server has the lowest latency
+ """
+
+ if not servers:
+ if not self.closest:
+ servers = self.get_closest_servers()
+ servers = self.closest
+
+ if self._source_address:
+ source_address_tuple = (self._source_address, 0)
+ else:
+ source_address_tuple = None
+
+ user_agent = build_user_agent()
+
+ results = {}
+ for server in servers:
+ cum = []
+ url = os.path.dirname(server['url'])
+ stamp = int(timeit.time.time() * 1000)
+ latency_url = '%s/latency.txt?x=%s' % (url, stamp)
+ for i in range(0, 3):
+ this_latency_url = '%s.%s' % (latency_url, i)
+ printer('%s %s' % ('GET', this_latency_url),
+ debug=True)
+ urlparts = urlparse(latency_url)
+ try:
+ if urlparts[0] == 'https':
+ h = SpeedtestHTTPSConnection(
+ urlparts[1],
+ source_address=source_address_tuple
+ )
+ else:
+ h = SpeedtestHTTPConnection(
+ urlparts[1],
+ source_address=source_address_tuple
+ )
+ headers = {'User-Agent': user_agent}
+ path = '%s?%s' % (urlparts[2], urlparts[4])
+ start = timeit.default_timer()
+ h.request("GET", path, headers=headers)
+ r = h.getresponse()
+ total = (timeit.default_timer() - start)
+ except HTTP_ERRORS:
+ e = get_exception()
+ printer('ERROR: %r' % e, debug=True)
+ cum.append(3600)
+ continue
+
+ text = r.read(9)
+ if int(r.status) == 200 and text == 'test=test'.encode():
+ cum.append(total)
+ else:
+ cum.append(3600)
+ h.close()
+
+ avg = round((sum(cum) / 6) * 1000.0, 3)
+ results[avg] = server
+
+ try:
+ fastest = sorted(results.keys())[0]
+ except IndexError:
+ raise SpeedtestBestServerFailure('Unable to connect to servers to '
+ 'test latency.')
+ best = results[fastest]
+ best['latency'] = fastest
+
+ self.results.ping = fastest
+ self.results.server = best
+
+ self._best.update(best)
+ printer('Best Server:\n%r' % best, debug=True)
+ return best
+
+ def download(self, callback=do_nothing, threads=None):
+ """Test download speed against speedtest.net
+
+ A ``threads`` value of ``None`` will fall back to those dictated
+ by the speedtest.net configuration
+ """
+
+ urls = []
+ for size in self.config['sizes']['download']:
+ for _ in range(0, self.config['counts']['download']):
+ urls.append('%s/random%sx%s.jpg' %
+ (os.path.dirname(self.best['url']), size, size))
+
+ request_count = len(urls)
+ requests = []
+ for i, url in enumerate(urls):
+ requests.append(
+ build_request(url, bump=i, secure=self._secure)
+ )
+
+ max_threads = threads or self.config['threads']['download']
+ in_flight = {'threads': 0}
+
+ def producer(q, requests, request_count):
+ for i, request in enumerate(requests):
+ thread = HTTPDownloader(
+ i,
+ request,
+ start,
+ self.config['length']['download'],
+ opener=self._opener,
+ shutdown_event=self._shutdown_event
+ )
+ while in_flight['threads'] >= max_threads:
+ timeit.time.sleep(0.001)
+ thread.start()
+ q.put(thread, True)
+ in_flight['threads'] += 1
+ callback(i, request_count, start=True)
+
+ finished = []
+
+ def consumer(q, request_count):
+ _is_alive = thread_is_alive
+ while len(finished) < request_count:
+ thread = q.get(True)
+ while _is_alive(thread):
+ thread.join(timeout=0.001)
+ in_flight['threads'] -= 1
+ finished.append(sum(thread.result))
+ callback(thread.i, request_count, end=True)
+
+ q = Queue(max_threads)
+ prod_thread = threading.Thread(target=producer,
+ args=(q, requests, request_count))
+ cons_thread = threading.Thread(target=consumer,
+ args=(q, request_count))
+ start = timeit.default_timer()
+ prod_thread.start()
+ cons_thread.start()
+ _is_alive = thread_is_alive
+ while _is_alive(prod_thread):
+ prod_thread.join(timeout=0.001)
+ while _is_alive(cons_thread):
+ cons_thread.join(timeout=0.001)
+
+ stop = timeit.default_timer()
+ self.results.bytes_received = sum(finished)
+ self.results.download = (
+ (self.results.bytes_received / (stop - start)) * 8.0
+ )
+ if self.results.download > 100000:
+ self.config['threads']['upload'] = 8
+ return self.results.download
+
+ def upload(self, callback=do_nothing, pre_allocate=True, threads=None):
+ """Test upload speed against speedtest.net
+
+ A ``threads`` value of ``None`` will fall back to those dictated
+ by the speedtest.net configuration
+ """
+
+ sizes = []
+
+ for size in self.config['sizes']['upload']:
+ for _ in range(0, self.config['counts']['upload']):
+ sizes.append(size)
+
+ # request_count = len(sizes)
+ request_count = self.config['upload_max']
+
+ requests = []
+ for i, size in enumerate(sizes):
+ # We set ``0`` for ``start`` and handle setting the actual
+ # ``start`` in ``HTTPUploader`` to get better measurements
+ data = HTTPUploaderData(
+ size,
+ 0,
+ self.config['length']['upload'],
+ shutdown_event=self._shutdown_event
+ )
+ if pre_allocate:
+ data.pre_allocate()
+
+ headers = {'Content-length': size}
+ requests.append(
+ (
+ build_request(self.best['url'], data, secure=self._secure,
+ headers=headers),
+ size
+ )
+ )
+
+ max_threads = threads or self.config['threads']['upload']
+ in_flight = {'threads': 0}
+
+ def producer(q, requests, request_count):
+ for i, request in enumerate(requests[:request_count]):
+ thread = HTTPUploader(
+ i,
+ request[0],
+ start,
+ request[1],
+ self.config['length']['upload'],
+ opener=self._opener,
+ shutdown_event=self._shutdown_event
+ )
+ while in_flight['threads'] >= max_threads:
+ timeit.time.sleep(0.001)
+ thread.start()
+ q.put(thread, True)
+ in_flight['threads'] += 1
+ callback(i, request_count, start=True)
+
+ finished = []
+
+ def consumer(q, request_count):
+ _is_alive = thread_is_alive
+ while len(finished) < request_count:
+ thread = q.get(True)
+ while _is_alive(thread):
+ thread.join(timeout=0.001)
+ in_flight['threads'] -= 1
+ finished.append(thread.result)
+ callback(thread.i, request_count, end=True)
+
+ q = Queue(threads or self.config['threads']['upload'])
+ prod_thread = threading.Thread(target=producer,
+ args=(q, requests, request_count))
+ cons_thread = threading.Thread(target=consumer,
+ args=(q, request_count))
+ start = timeit.default_timer()
+ prod_thread.start()
+ cons_thread.start()
+ _is_alive = thread_is_alive
+ while _is_alive(prod_thread):
+ prod_thread.join(timeout=0.1)
+ while _is_alive(cons_thread):
+ cons_thread.join(timeout=0.1)
+
+ stop = timeit.default_timer()
+ self.results.bytes_sent = sum(finished)
+ self.results.upload = (
+ (self.results.bytes_sent / (stop - start)) * 8.0
+ )
+ return self.results.upload
+
+
+def ctrl_c(shutdown_event):
+ """Catch Ctrl-C key sequence and set a SHUTDOWN_EVENT for our threaded
+ operations
+ """
+ def inner(signum, frame):
+ shutdown_event.set()
+ printer('\nCancelling...', error=True)
+ sys.exit(0)
+ return inner
+
+
+def version():
+ """Print the version"""
+
+ printer('speedtest-cli %s' % __version__)
+ printer('Python %s' % sys.version.replace('\n', ''))
+ sys.exit(0)
+
+
+def csv_header(delimiter=','):
+ """Print the CSV Headers"""
+
+ printer(SpeedtestResults.csv_header(delimiter=delimiter))
+ sys.exit(0)
+
+
+def parse_args():
+ """Function to handle building and parsing of command line arguments"""
+ description = (
+ 'Command line interface for testing internet bandwidth using '
+ 'speedtest.net.\n'
+ '------------------------------------------------------------'
+ '--------------\n'
+ 'https://github.com/sivel/speedtest-cli')
+
+ parser = ArgParser(description=description)
+ # Give optparse.OptionParser an `add_argument` method for
+ # compatibility with argparse.ArgumentParser
+ try:
+ parser.add_argument = parser.add_option
+ except AttributeError:
+ pass
+ parser.add_argument('--no-download', dest='download', default=True,
+ action='store_const', const=False,
+ help='Do not perform download test')
+ parser.add_argument('--no-upload', dest='upload', default=True,
+ action='store_const', const=False,
+ help='Do not perform upload test')
+ parser.add_argument('--single', default=False, action='store_true',
+ help='Only use a single connection instead of '
+ 'multiple. This simulates a typical file '
+ 'transfer.')
+ parser.add_argument('--bytes', dest='units', action='store_const',
+ const=('byte', 8), default=('bit', 1),
+ help='Display values in bytes instead of bits. Does '
+ 'not affect the image generated by --share, nor '
+ 'output from --json or --csv')
+ parser.add_argument('--share', action='store_true',
+ help='Generate and provide a URL to the speedtest.net '
+ 'share results image, not displayed with --csv')
+ parser.add_argument('--simple', action='store_true', default=False,
+ help='Suppress verbose output, only show basic '
+ 'information')
+ parser.add_argument('--csv', action='store_true', default=False,
+ help='Suppress verbose output, only show basic '
+ 'information in CSV format. Speeds listed in '
+ 'bit/s and not affected by --bytes')
+ parser.add_argument('--csv-delimiter', default=',', type=PARSER_TYPE_STR,
+ help='Single character delimiter to use in CSV '
+ 'output. Default ","')
+ parser.add_argument('--csv-header', action='store_true', default=False,
+ help='Print CSV headers')
+ parser.add_argument('--json', action='store_true', default=False,
+ help='Suppress verbose output, only show basic '
+ 'information in JSON format. Speeds listed in '
+ 'bit/s and not affected by --bytes')
+ parser.add_argument('--list', action='store_true',
+ help='Display a list of speedtest.net servers '
+ 'sorted by distance')
+ parser.add_argument('--server', type=PARSER_TYPE_INT, action='append',
+ help='Specify a server ID to test against. Can be '
+ 'supplied multiple times')
+ parser.add_argument('--exclude', type=PARSER_TYPE_INT, action='append',
+ help='Exclude a server from selection. Can be '
+ 'supplied multiple times')
+ parser.add_argument('--mini', help='URL of the Speedtest Mini server')
+ parser.add_argument('--source', help='Source IP address to bind to')
+ parser.add_argument('--timeout', default=10, type=PARSER_TYPE_FLOAT,
+ help='HTTP timeout in seconds. Default 10')
+ parser.add_argument('--secure', action='store_true',
+ help='Use HTTPS instead of HTTP when communicating '
+ 'with speedtest.net operated servers')
+ parser.add_argument('--no-pre-allocate', dest='pre_allocate',
+ action='store_const', default=True, const=False,
+ help='Do not pre allocate upload data. Pre allocation '
+ 'is enabled by default to improve upload '
+ 'performance. To support systems with '
+ 'insufficient memory, use this option to avoid a '
+ 'MemoryError')
+ parser.add_argument('--version', action='store_true',
+ help='Show the version number and exit')
+ parser.add_argument('--debug', action='store_true',
+ help=ARG_SUPPRESS, default=ARG_SUPPRESS)
+
+ options = parser.parse_args()
+ if isinstance(options, tuple):
+ args = options[0]
+ else:
+ args = options
+ return args
+
+
+def validate_optional_args(args):
+ """Check if an argument was provided that depends on a module that may
+ not be part of the Python standard library.
+
+ If such an argument is supplied, and the module does not exist, exit
+ with an error stating which module is missing.
+ """
+ optional_args = {
+ 'json': ('json/simplejson python module', json),
+ 'secure': ('SSL support', HTTPSConnection),
+ }
+
+ for arg, info in optional_args.items():
+ if getattr(args, arg, False) and info[1] is None:
+ raise SystemExit('%s is not installed. --%s is '
+ 'unavailable' % (info[0], arg))
+
+
+def printer(string, quiet=False, debug=False, error=False, **kwargs):
+ """Helper function print a string with various features"""
+
+ if debug and not DEBUG:
+ return
+
+ if debug:
+ if sys.stdout.isatty():
+ out = '\033[1;30mDEBUG: %s\033[0m' % string
+ else:
+ out = 'DEBUG: %s' % string
+ else:
+ out = string
+
+ if error:
+ kwargs['file'] = sys.stderr
+
+ if not quiet:
+ print_(out, **kwargs)
+
+
+def shell():
+ """Run the full speedtest.net test"""
+
+ global DEBUG
+ shutdown_event = threading.Event()
+
+ signal.signal(signal.SIGINT, ctrl_c(shutdown_event))
+
+ args = parse_args()
+
+ # Print the version and exit
+ if args.version:
+ version()
+
+ if not args.download and not args.upload:
+ raise SpeedtestCLIError('Cannot supply both --no-download and '
+ '--no-upload')
+
+ if len(args.csv_delimiter) != 1:
+ raise SpeedtestCLIError('--csv-delimiter must be a single character')
+
+ if args.csv_header:
+ csv_header(args.csv_delimiter)
+
+ validate_optional_args(args)
+
+ debug = getattr(args, 'debug', False)
+ if debug == 'SUPPRESSHELP':
+ debug = False
+ if debug:
+ DEBUG = True
+
+ if args.simple or args.csv or args.json:
+ quiet = True
+ else:
+ quiet = False
+
+ if args.csv or args.json:
+ machine_format = True
+ else:
+ machine_format = False
+
+ # Don't set a callback if we are running quietly
+ if quiet or debug:
+ callback = do_nothing
+ else:
+ callback = print_dots(shutdown_event)
+
+ printer('Retrieving speedtest.net configuration...', quiet)
+ try:
+ speedtest = Speedtest(
+ source_address=args.source,
+ timeout=args.timeout,
+ secure=args.secure
+ )
+ except (ConfigRetrievalError,) + HTTP_ERRORS:
+ printer('Cannot retrieve speedtest configuration', error=True)
+ raise SpeedtestCLIError(get_exception())
+
+ if args.list:
+ try:
+ speedtest.get_servers()
+ except (ServersRetrievalError,) + HTTP_ERRORS:
+ printer('Cannot retrieve speedtest server list', error=True)
+ raise SpeedtestCLIError(get_exception())
+
+ for _, servers in sorted(speedtest.servers.items()):
+ for server in servers:
+ line = ('%(id)5s) %(sponsor)s (%(name)s, %(country)s) '
+ '[%(d)0.2f km]' % server)
+ try:
+ printer(line)
+ except IOError:
+ e = get_exception()
+ if e.errno != errno.EPIPE:
+ raise
+ sys.exit(0)
+
+ printer('Testing from %(isp)s (%(ip)s)...' % speedtest.config['client'],
+ quiet)
+
+ if not args.mini:
+ printer('Retrieving speedtest.net server list...', quiet)
+ try:
+ speedtest.get_servers(servers=args.server, exclude=args.exclude)
+ except NoMatchedServers:
+ raise SpeedtestCLIError(
+ 'No matched servers: %s' %
+ ', '.join('%s' % s for s in args.server)
+ )
+ except (ServersRetrievalError,) + HTTP_ERRORS:
+ printer('Cannot retrieve speedtest server list', error=True)
+ raise SpeedtestCLIError(get_exception())
+ except InvalidServerIDType:
+ raise SpeedtestCLIError(
+ '%s is an invalid server type, must '
+ 'be an int' % ', '.join('%s' % s for s in args.server)
+ )
+
+ if args.server and len(args.server) == 1:
+ printer('Retrieving information for the selected server...', quiet)
+ else:
+ printer('Selecting best server based on ping...', quiet)
+ speedtest.get_best_server()
+ elif args.mini:
+ speedtest.get_best_server(speedtest.set_mini_server(args.mini))
+
+ results = speedtest.results
+
+ printer('Hosted by %(sponsor)s (%(name)s) [%(d)0.2f km]: '
+ '%(latency)s ms' % results.server, quiet)
+
+ if args.download:
+ printer('Testing download speed', quiet,
+ end=('', '\n')[bool(debug)])
+ speedtest.download(
+ callback=callback,
+ threads=(None, 1)[args.single]
+ )
+ printer('Download: %0.2f M%s/s' %
+ ((results.download / 1000.0 / 1000.0) / args.units[1],
+ args.units[0]),
+ quiet)
+ else:
+ printer('Skipping download test', quiet)
+
+ if args.upload:
+ printer('Testing upload speed', quiet,
+ end=('', '\n')[bool(debug)])
+ speedtest.upload(
+ callback=callback,
+ pre_allocate=args.pre_allocate,
+ threads=(None, 1)[args.single]
+ )
+ printer('Upload: %0.2f M%s/s' %
+ ((results.upload / 1000.0 / 1000.0) / args.units[1],
+ args.units[0]),
+ quiet)
+ else:
+ printer('Skipping upload test', quiet)
+
+ printer('Results:\n%r' % results.dict(), debug=True)
+
+ if not args.simple and args.share:
+ results.share()
+
+ if args.simple:
+ printer('Ping: %s ms\nDownload: %0.2f M%s/s\nUpload: %0.2f M%s/s' %
+ (results.ping,
+ (results.download / 1000.0 / 1000.0) / args.units[1],
+ args.units[0],
+ (results.upload / 1000.0 / 1000.0) / args.units[1],
+ args.units[0]))
+ elif args.csv:
+ printer(results.csv(delimiter=args.csv_delimiter))
+ elif args.json:
+ printer(results.json())
+
+ if args.share and not machine_format:
+ printer('Share results: %s' % results.share())
+
+
+def main():
+ try:
+ shell()
+ except KeyboardInterrupt:
+ printer('\nCancelling...', error=True)
+ except (SpeedtestException, SystemExit):
+ e = get_exception()
+ # Ignore a successful exit, or argparse exit
+ if getattr(e, 'code', 1) not in (0, 2):
+ msg = '%s' % e
+ if not msg:
+ msg = '%r' % e
+ raise SystemExit('ERROR: %s' % msg)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/.local/bin/ssh-permissions b/.local/bin/ssh-permissions
new file mode 100755
index 0000000..5fb6cde
--- /dev/null
+++ b/.local/bin/ssh-permissions
@@ -0,0 +1,6 @@
+chmod 700 ~/.ssh
+chmod 644 ~/.ssh/authorized_keys
+chmod 644 ~/.ssh/known_hosts
+chmod 644 ~/.ssh/config
+chmod 400 ~/.ssh/id_rsa
+chmod 400 ~/.ssh/id_rsa.pub
diff --git a/.local/bin/stream b/.local/bin/stream
new file mode 100755
index 0000000..7662969
--- /dev/null
+++ b/.local/bin/stream
@@ -0,0 +1,5 @@
+#!/bin/bash
+KERNEL=$(uname -sr)
+
+[[ $KERNEL =~ "icrosoft" ]] && nohup streamlink -p "https://www.twitch.tv/riotgames best" $1 best > /dev/null 2>&1 &
+[[ $KERNEL =~ "gentoo" ]] && nohup streamlink -p mpv $1 best > /dev/null 2>&1 &
diff --git a/.local/bin/suspend-at-time b/.local/bin/suspend-at-time
new file mode 100755
index 0000000..b3ce40a
--- /dev/null
+++ b/.local/bin/suspend-at-time
@@ -0,0 +1,15 @@
+#!/bin/bash
+#./suspend-at-time 0:00
+
+RUNAT="$1"
+
+while [ 1 ]
+do
+ DATE=`/bin/date +%H:%M`
+ if [ $DATE. = $RUNAT. ]
+ then
+ sudo systemctl suspend
+ echo ""DONE" at "$(date)""
+ pkill suspend-at-time
+ fi
+done
diff --git a/.local/bin/symilar b/.local/bin/symilar
new file mode 100755
index 0000000..400edba
--- /dev/null
+++ b/.local/bin/symilar
@@ -0,0 +1,8 @@
+#!/usr/lib/python-exec/python3.7/python
+# -*- coding: utf-8 -*-
+import re
+import sys
+from pylint import run_symilar
+if __name__ == '__main__':
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+ sys.exit(run_symilar())
diff --git a/.local/bin/tea b/.local/bin/tea
new file mode 100755
index 0000000..d5c7f0d
Binary files /dev/null and b/.local/bin/tea differ
diff --git a/.local/bin/temp b/.local/bin/temp
new file mode 100755
index 0000000..95fe391
--- /dev/null
+++ b/.local/bin/temp
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+echo -e "CPU: $(sensors | awk '/Core 0/ {print $3}'| egrep --color=no -o '[0-9]+\.' | sed 's/\.//')"; echo -e "GPU: $(sudo nvidia-smi -q -d temperature | grep --color=no -i "GPU Current" |egrep --color=no -o '[0-9]*')"
diff --git a/.local/bin/term-wmi b/.local/bin/term-wmi
new file mode 100755
index 0000000..194fb05
--- /dev/null
+++ b/.local/bin/term-wmi
@@ -0,0 +1,3 @@
+#!/bin/bash
+xfreerdp /size:1920:1080 /u:s434812 /d:LABS /v:term.wmi.amu.edu.pl /cert:deny /sound /dynamic-resolution
+#rdesktop -u "\LABS\s434812" -g 90% term.wmi.amu.edu.pl
diff --git a/.local/bin/trans b/.local/bin/trans
new file mode 100755
index 0000000..1d66db2
--- /dev/null
+++ b/.local/bin/trans
@@ -0,0 +1,5713 @@
+#!/usr/bin/env bash
+export TRANS_ENTRY="$0"
+if [[ ! $LANG =~ (UTF|utf)-?8$ ]]; then export LANG=en_US.UTF-8; fi
+read -r -d '' TRANS_PROGRAM << 'EOF'
+BEGIN {
+Name = "Translate Shell"
+Description = "Command-line translator using Google Translate, Bing Translator, Yandex.Translate, etc."
+Version = "0.9.6.12"
+ReleaseDate = "2020-05-11"
+Command = "trans"
+EntryPoint = "translate.awk"
+EntryScript = "translate"
+}
+function initConst() {
+NULLSTR = ""
+NONE = "\0"
+TRUE = 1
+STDIN = "/dev/stdin"
+STDOUT = "/dev/stdout"
+STDERR = "/dev/stderr"
+SUPOUT = " > /dev/null "
+SUPERR = " 2> /dev/null "
+PIPE = " | "
+}
+function anything(array,
+i) {
+for (i in array)
+if (array[i]) return 1
+return 0
+}
+function exists(value) {
+if (isarray(value))
+return anything(value)
+else
+return value ? 1 : 0
+}
+function belongsTo(element, array,
+i) {
+for (i in array)
+if (element == array[i]) return element
+return NULLSTR
+}
+function identical(x, y,
+i) {
+if (!isarray(x) && !isarray(y))
+return x == y
+else if (isarray(x) && isarray(y)) {
+if (length(x) != length(y)) return 0
+for (i in x)
+if (!identical(x[i], y[i])) return 0
+return 1
+} else
+return 0
+}
+function append(array, element) {
+array[anything(array) ? length(array) : 0] = element
+}
+function compareByIndexFields(i1, v1, i2, v2,
+t1, t2, tl, j) {
+split(i1, t1, SUBSEP)
+split(i2, t2, SUBSEP)
+tl = length(t1) < length(t2) ? length(t1) : length(t2)
+for (j = 1; j <= tl; j++) {
+if (t1[j] < t2[j])
+return -1
+else if (t1[j] > t2[j])
+return 1
+}
+return 0
+}
+function isnum(string) {
+return string == string + 0
+}
+function startsWithAny(string, substrings,
+i) {
+for (i in substrings)
+if (index(string, substrings[i]) == 1) return substrings[i]
+return NULLSTR
+}
+function matchesAny(string, patterns,
+i) {
+for (i in patterns)
+if (string ~ "^" patterns[i]) return patterns[i]
+return NULLSTR
+}
+function replicate(string, len,
+i, temp) {
+temp = NULLSTR
+for (i = 0; i < len; i++)
+temp = temp string
+return temp
+}
+function reverse(string,
+i, temp) {
+temp = NULLSTR
+for (i = length(string); i > 0; i--)
+temp = temp substr(string, i, 1);
+return temp
+}
+function join(array, separator, sortedIn, preserveNull,
+i, j, saveSortedIn, temp) {
+if (!sortedIn)
+sortedIn = "compareByIndexFields"
+temp = NULLSTR
+j = 0
+if (isarray(array)) {
+saveSortedIn = PROCINFO["sorted_in"]
+PROCINFO["sorted_in"] = sortedIn
+for (i in array)
+if (preserveNull || array[i] != NULLSTR)
+temp = j++ ? temp separator array[i] : array[i]
+PROCINFO["sorted_in"] = saveSortedIn
+} else
+temp = array
+return temp
+}
+function explode(string, array) {
+split(string, array, NULLSTR)
+}
+function escapeChar(char) {
+switch (char) {
+case "b":
+return "\b"
+case "f":
+return "\f"
+case "n":
+return "\n"
+case "r":
+return "\r"
+case "t":
+return "\t"
+case "v":
+return "\v"
+case "u0026":
+return "&"
+case "u003c":
+return "<"
+case "u003e":
+return ">"
+case "u200b":
+return ""
+default:
+return char
+}
+}
+function literal(string,
+c, cc, escaping, i, s) {
+if (string !~ /^".*"$/)
+return string
+explode(string, s)
+string = NULLSTR
+escaping = 0
+for (i = 2; i < length(s); i++) {
+c = s[i]
+if (escaping) {
+if (cc) {
+cc = cc c
+if (length(cc) == 5) {
+string = string escapeChar(cc)
+escaping = 0
+cc = NULLSTR
+}
+} else if (c == "u") {
+cc = c
+} else {
+string = string escapeChar(c)
+escaping = 0
+}
+} else {
+if (c == "\\")
+escaping = 1
+else
+string = string c
+}
+}
+return string
+}
+function escape(string) {
+gsub(/\\/, "\\\\", string)
+gsub(/"/, "\\\"", string)
+return string
+}
+function unescape(string) {
+gsub(/\\"/, "\"", string)
+gsub(/\\\\/, "\\", string)
+return string
+}
+function parameterize(string, quotationMark) {
+if (!quotationMark)
+quotationMark = "'"
+if (quotationMark == "'") {
+gsub(/'/, "'\\''", string)
+return "'" string "'"
+} else {
+return "\"" escape(string) "\""
+}
+}
+function unparameterize(string, temp) {
+match(string, /^'(.*)'$/, temp)
+if (temp[0]) {
+string = temp[1]
+gsub(/'\\''/, "'", string)
+return string
+}
+match(string, /^"(.*)"$/, temp)
+if (temp[0]) {
+string = temp[1]
+return unescape(string)
+}
+return string
+}
+function toString(value, inline, heredoc, valOnly, numSub, level, sortedIn,
+i, items, j, k, p, saveSortedIn, temp, v) {
+if (!level) level = 0
+if (!sortedIn)
+sortedIn = "compareByIndexFields"
+if (isarray(value)) {
+saveSortedIn = PROCINFO["sorted_in"]
+PROCINFO["sorted_in"] = sortedIn
+p = 0
+for (i in value) {
+split(i, j, SUBSEP); k = join(j, ",")
+if (!numSub || !isnum(k)) k = parameterize(k, "\"")
+v = toString(value[i], inline, heredoc, valOnly, numSub, level + 1, sortedIn)
+if (!isarray(value[i])) v = parameterize(v, "\"")
+if (valOnly)
+items[p++] = inline ? v : (replicate("\t", level) v)
+else
+items[p++] = inline ? (k ": " v) :
+(replicate("\t", level) k "\t" v)
+}
+PROCINFO["sorted_in"] = saveSortedIn
+temp = inline ? join(items, ", ") :
+("\n" join(items, "\n") "\n" replicate("\t", level))
+temp = valOnly ? ("[" temp "]") : ("{" temp "}")
+return temp
+} else {
+if (heredoc)
+return "'''\n" value "\n'''"
+else
+return value
+}
+}
+function squeeze(line, preserveIndent) {
+if (!preserveIndent)
+gsub(/^[[:space:]]+/, NULLSTR, line)
+gsub(/^[[:space:]]*#.*$/, NULLSTR, line)
+gsub(/#[^"/]*$/, NULLSTR, line)
+gsub(/[[:space:]]+$/, NULLSTR, line)
+gsub(/[[:space:]]+\\$/, "\\", line)
+return line
+}
+function yn(string) {
+return (tolower(string) ~ /^([0fn]|off)/) ? 0 : 1
+}
+function initAnsiCode() {
+if (ENVIRON["TERM"] == "dumb") return
+AnsiCode["reset"] = AnsiCode[0] = "\33[0m"
+AnsiCode["bold"] = "\33[1m"
+AnsiCode["underline"] = "\33[4m"
+AnsiCode["negative"] = "\33[7m"
+AnsiCode["no bold"] = "\33[22m"
+AnsiCode["no underline"] = "\33[24m"
+AnsiCode["positive"] = "\33[27m"
+AnsiCode["black"] = "\33[30m"
+AnsiCode["red"] = "\33[31m"
+AnsiCode["green"] = "\33[32m"
+AnsiCode["yellow"] = "\33[33m"
+AnsiCode["blue"] = "\33[34m"
+AnsiCode["magenta"] = "\33[35m"
+AnsiCode["cyan"] = "\33[36m"
+AnsiCode["gray"] = "\33[37m"
+AnsiCode["default"] = "\33[39m"
+AnsiCode["dark gray"] = "\33[90m"
+AnsiCode["light red"] = "\33[91m"
+AnsiCode["light green"] = "\33[92m"
+AnsiCode["light yellow"] = "\33[93m"
+AnsiCode["light blue"] = "\33[94m"
+AnsiCode["light magenta"] = "\33[95m"
+AnsiCode["light cyan"] = "\33[96m"
+AnsiCode["white"] = "\33[97m"
+}
+function ansi(code, text) {
+switch (code) {
+case "bold":
+return AnsiCode[code] text AnsiCode["no bold"]
+case "underline":
+return AnsiCode[code] text AnsiCode["no underline"]
+case "negative":
+return AnsiCode[code] text AnsiCode["positive"]
+default:
+return AnsiCode[code] text AnsiCode[0]
+}
+}
+function w(text) {
+print ansi("yellow", text) > STDERR
+}
+function e(text) {
+print ansi("bold", ansi("yellow", text)) > STDERR
+}
+function wtf(text) {
+print ansi("bold", ansi("red", text)) > STDERR
+}
+function d(text) {
+print ansi("gray", text) > STDERR
+}
+function da(value, name, inline, heredoc, valOnly, numSub, sortedIn,
+i, j, saveSortedIn) {
+if (!name)
+name = "_"
+if (!sortedIn)
+sortedIn = "compareByIndexFields"
+d(name " = " toString(value, inline, heredoc, valOnly, numSub, 0, sortedIn))
+}
+function assert(x, message) {
+if (!message)
+message = "[ERROR] Assertion failed."
+if (x)
+return x
+else
+e(message)
+}
+function initUrlEncoding() {
+UrlEncoding["\t"] = "%09"
+UrlEncoding["\n"] = "%0A"
+UrlEncoding[" "] = "%20"
+UrlEncoding["!"] = "%21"
+UrlEncoding["\""] = "%22"
+UrlEncoding["#"] = "%23"
+UrlEncoding["$"] = "%24"
+UrlEncoding["%"] = "%25"
+UrlEncoding["&"] = "%26"
+UrlEncoding["'"] = "%27"
+UrlEncoding["("] = "%28"
+UrlEncoding[")"] = "%29"
+UrlEncoding["*"] = "%2A"
+UrlEncoding["+"] = "%2B"
+UrlEncoding[","] = "%2C"
+UrlEncoding["-"] = "%2D"
+UrlEncoding["."] = "%2E"
+UrlEncoding["/"] = "%2F"
+UrlEncoding[":"] = "%3A"
+UrlEncoding[";"] = "%3B"
+UrlEncoding["<"] = "%3C"
+UrlEncoding["="] = "%3D"
+UrlEncoding[">"] = "%3E"
+UrlEncoding["?"] = "%3F"
+UrlEncoding["@"] = "%40"
+UrlEncoding["["] = "%5B"
+UrlEncoding["\\"] = "%5C"
+UrlEncoding["]"] = "%5D"
+UrlEncoding["^"] = "%5E"
+UrlEncoding["_"] = "%5F"
+UrlEncoding["`"] = "%60"
+UrlEncoding["{"] = "%7B"
+UrlEncoding["|"] = "%7C"
+UrlEncoding["}"] = "%7D"
+UrlEncoding["~"] = "%7E"
+}
+function quote(string, i, r, s) {
+r = NULLSTR
+explode(string, s)
+for (i = 1; i <= length(s); i++)
+r = r (s[i] in UrlEncoding ? UrlEncoding[s[i]] : s[i])
+return r
+}
+function unquote(string, i, k, r, s, temp) {
+r = NULLSTR
+explode(string, s)
+temp = NULLSTR
+for (i = 1; i <= length(s); i++)
+if (temp) {
+temp = temp s[i]
+if (length(temp) > 2) {
+for (k in UrlEncoding)
+if (temp == UrlEncoding[k]) {
+r = r k
+temp = NULLSTR
+break
+}
+if (temp) {
+r = r temp
+temp = NULLSTR
+}
+}
+} else {
+if (s[i] != "%")
+r = r s[i]
+else
+temp = s[i]
+}
+if (temp)
+r = r temp
+return r
+}
+function initUriSchemes() {
+UriSchemes[0] = "file://"
+UriSchemes[1] = "http://"
+UriSchemes[2] = "https://"
+}
+function readFrom(file, line, text) {
+if (!file) file = "/dev/stdin"
+text = NULLSTR
+while (getline line < file)
+text = (text ? text "\n" : NULLSTR) line
+return text
+}
+function writeTo(text, file) {
+if (!file) file = "/dev/stdout"
+print text > file
+}
+function getOutput(command, content, line) {
+content = NULLSTR
+while ((command |& getline line) > 0)
+content = (content ? content "\n" : NULLSTR) line
+close(command)
+return content
+}
+function fileExists(file) {
+return !system("test -f " parameterize(file))
+}
+function dirExists(file) {
+return !system("test -d " parameterize(file))
+}
+function detectProgram(prog, arg, returnOutput, command, temp) {
+command = prog " " arg SUPERR
+command | getline temp
+close(command)
+if (returnOutput)
+return temp
+if (temp)
+return prog
+return NULLSTR
+}
+function getGitHead( line, group) {
+if (fileExists(".git/HEAD")) {
+getline line < ".git/HEAD"
+match(line, /^ref: (.*)$/, group)
+if (fileExists(".git/" group[1])) {
+getline line < (".git/" group[1])
+return substr(line, 1, 7)
+} else
+return NULLSTR
+} else
+return NULLSTR
+}
+BEGIN {
+initConst()
+initAnsiCode()
+initUrlEncoding()
+initUriSchemes()
+}
+function initGawk( group) {
+Gawk = "gawk"
+GawkVersion = PROCINFO["version"]
+split(PROCINFO["version"], group, ".")
+if (group[1] < 4) {
+e("[ERROR] Oops! Your gawk (version " GawkVersion ") "\
+"appears to be too old.\n"\
+" You need at least gawk 4.0.0 to run this program.")
+exit 1
+}
+}
+function initBiDiTerm() {
+if (ENVIRON["MLTERM"])
+BiDiTerm = "mlterm"
+else if (ENVIRON["KONSOLE_VERSION"])
+BiDiTerm = "konsole"
+}
+function initBiDi() {
+FriBidi = detectProgram("fribidi", "--version", 1)
+BiDiNoPad = FriBidi ? "fribidi --nopad" : "rev" SUPERR
+BiDi = FriBidi ? "fribidi --width %s" :
+"rev" SUPERR "| sed \"s/'/\\\\\\'/\" | xargs -0 printf '%%%ss'"
+}
+function initRlwrap() {
+Rlwrap = detectProgram("rlwrap", "--version")
+}
+function initEmacs() {
+Emacs = detectProgram("emacs", "--version")
+}
+function initCurl() {
+Curl = detectProgram("curl", "--version")
+}
+function l(value, name, inline, heredoc, valOnly, numSub, sortedIn) {
+if (Option["debug"]) {
+if (name)
+da(value, name, inline, heredoc, valOnly, numSub, sortedIn)
+else
+d(value)
+}
+}
+function m(string) {
+if (Option["debug"])
+return ansi("cyan", string) RS
+}
+function newerVersion(ver1, ver2, i, group1, group2) {
+split(ver1, group1, ".")
+split(ver2, group2, ".")
+for (i = 1; i <= 4; i++) {
+if (group1[i] + 0 > group2[i] + 0)
+return 1
+else if (group1[i] + 0 < group2[i] + 0)
+return 0
+}
+return 0
+}
+function rlwrapMe( i, command) {
+initRlwrap()
+if (!Rlwrap) {
+l(">> not found: rlwrap")
+return 1
+}
+if (ENVIRON["TRANS_ENTRY"]) {
+command = Rlwrap " " ENVIRON["TRANS_ENTRY"] " "\
+parameterize("-no-rlwrap")
+} else if (fileExists(ENVIRON["TRANS_DIR"] "/" EntryScript)) {
+command = Rlwrap " sh "\
+parameterize(ENVIRON["TRANS_DIR"] "/" EntryScript)\
+" - " parameterize("-no-rlwrap")
+} else {
+l(">> not found: $TRANS_ENTRY or EntryPoint")
+return 1
+}
+for (i = 1; i < length(ARGV); i++)
+if (ARGV[i])
+command = command " " parameterize(ARGV[i])
+l(">> forking: " command)
+if (!system(command)) {
+l(">> process exited with code 0")
+exit ExitCode
+} else {
+l(">> process exited with non-zero return code")
+return 1
+}
+}
+function emacsMe( i, params, el, command) {
+initEmacs()
+if (!Emacs) {
+l(">> not found: emacs")
+return 1
+}
+params = ""
+for (i = 1; i < length(ARGV); i++)
+if (ARGV[i])
+params = params " " parameterize(ARGV[i], "\"")
+if (ENVIRON["TRANS_ENTRY"]) {
+el = "(progn (setq explicit-shell-file-name \"" ENVIRON["TRANS_ENTRY"] "\") "\
+"(setq explicit-" Command "-args '(\"-I\" \"-no-rlwrap\"" params ")) "\
+"(command-execute 'shell) (rename-buffer \"" Name "\"))"
+} else if (fileExists(ENVIRON["TRANS_DIR"] "/" EntryScript)) {
+el = "(progn (setq explicit-shell-file-name \"" "sh" "\") "\
+"(setq explicit-" "sh" "-args '(\"" ENVIRON["TRANS_DIR"] "/" EntryScript "\" \"-I\" \"-no-rlwrap\"" params ")) "\
+"(command-execute 'shell) (rename-buffer \"" Name "\"))"
+} else {
+l(">> not found: $TRANS_ENTRY or EntryPoint")
+return 1
+}
+command = Emacs " --eval " parameterize(el)
+l(">> forking: " command)
+if (!system(command)) {
+l(">> process exited with code 0")
+exit ExitCode
+} else {
+l(">> process exited with non-zero return code")
+return 1
+}
+}
+function curl(url, output, command, content, line) {
+initCurl()
+if (!Curl) {
+l(">> not found: curl")
+w("[WARNING] curl is not found.")
+return NULLSTR
+}
+command = Curl " --location --silent"
+if (Option["proxy"])
+command = command " --proxy " parameterize(Option["proxy"])
+if (Option["user-agent"])
+command = command " --user-agent " parameterize(Option["user-agent"])
+command = command " " parameterize(url)
+if (output) {
+command = command " --output " parameterize(output)
+system(command)
+return NULLSTR
+}
+content = NULLSTR
+while ((command |& getline line) > 0)
+content = (content ? content "\n" : NULLSTR) line
+close(command)
+return content
+}
+function curlPost(url, data, output, command, content, line) {
+initCurl()
+if (!Curl) {
+l(">> not found: curl")
+w("[WARNING] curl is not found.")
+return NULLSTR
+}
+command = Curl " --location --silent"
+if (Option["proxy"])
+command = command " --proxy " parameterize(Option["proxy"])
+if (Option["user-agent"])
+command = command " --user-agent " parameterize(Option["user-agent"])
+command = command " --request POST --data " parameterize(data)
+command = command " " parameterize(url)
+if (output) {
+command = command " --output " parameterize(output)
+system(command)
+return NULLSTR
+}
+content = NULLSTR
+while ((command |& getline line) > 0)
+content = (content ? content "\n" : NULLSTR) line
+close(command)
+return content
+}
+function dump(text, group, command, temp) {
+command = "hexdump" " -v -e'1/1 \"%03u\" \" \"'"
+command = "echo " parameterize(text) PIPE command
+command | getline temp
+split(temp, group, " ")
+close(command)
+return length(group) - 1
+}
+function dumpX(text, group, command, temp) {
+command = "hexdump" " -v -e'1/1 \"%02X\" \" \"'"
+command = "echo " parameterize(text) PIPE command
+command | getline temp
+split(temp, group, " ")
+close(command)
+return length(group) - 1
+}
+function base64(text, command, temp) {
+if (detectProgram("uname", "-s", 1) == "Linux")
+command = "echo -n " parameterize(text) PIPE "base64 -w0"
+else
+command = "echo -n " parameterize(text) PIPE "base64"
+command = "bash -c " parameterize(command, "\"")
+command | getline temp
+close(command)
+return temp
+}
+function uprintf(text, command, temp) {
+command = "echo -en " parameterize(text)
+command = "bash -c " parameterize(command, "\"")
+command | getline temp
+close(command)
+return temp
+}
+function initLocale() {
+Locale["af"]["name"] = "Afrikaans"
+Locale["af"]["endonym"] = "Afrikaans"
+Locale["af"]["translations-of"] = "Vertalings van %s"
+Locale["af"]["definitions-of"] = "Definisies van %s"
+Locale["af"]["synonyms"] = "Sinonieme"
+Locale["af"]["examples"] = "Voorbeelde"
+Locale["af"]["see-also"] = "Sien ook"
+Locale["af"]["family"] = "Indo-European"
+Locale["af"]["iso"] = "afr"
+Locale["af"]["glotto"] = "afri1274"
+Locale["af"]["script"] = "Latn"
+Locale["sq"]["name"] = "Albanian"
+Locale["sq"]["endonym"] = "Shqip"
+Locale["sq"]["translations-of"] = "Përkthimet e %s"
+Locale["sq"]["definitions-of"] = "Përkufizime të %s"
+Locale["sq"]["synonyms"] = "Sinonime"
+Locale["sq"]["examples"] = "Shembuj"
+Locale["sq"]["see-also"] = "Shihni gjithashtu"
+Locale["sq"]["family"] = "Indo-European"
+Locale["sq"]["iso"] = "sqi"
+Locale["sq"]["glotto"] = "alba1267"
+Locale["sq"]["script"] = "Latn"
+Locale["am"]["name"] = "Amharic"
+Locale["am"]["endonym"] = "አማርኛ"
+Locale["am"]["translations-of"] = "የ %s ትርጉሞች"
+Locale["am"]["definitions-of"] = "የ %s ቃላት ፍችዎች"
+Locale["am"]["synonyms"] = "ተመሳሳይ ቃላት"
+Locale["am"]["examples"] = "ምሳሌዎች"
+Locale["am"]["see-also"] = "የሚከተለውንም ይመልከቱ"
+Locale["am"]["family"] = "Afro-Asiatic"
+Locale["am"]["iso"] = "amh"
+Locale["am"]["glotto"] = "amha1245"
+Locale["am"]["script"] = "Ethi"
+Locale["ar"]["name"] = "Arabic"
+Locale["ar"]["endonym"] = "العربية"
+Locale["ar"]["translations-of"] = "ترجمات %s"
+Locale["ar"]["definitions-of"] = "تعريفات %s"
+Locale["ar"]["synonyms"] = "مرادفات"
+Locale["ar"]["examples"] = "أمثلة"
+Locale["ar"]["see-also"] = "انظر أيضًا"
+Locale["ar"]["family"] = "Afro-Asiatic"
+Locale["ar"]["iso"] = "ara"
+Locale["ar"]["glotto"] = "stan1318"
+Locale["ar"]["script"] = "Arab"
+Locale["ar"]["rtl"] = "true"
+Locale["hy"]["name"] = "Armenian"
+Locale["hy"]["endonym"] = "Հայերեն"
+Locale["hy"]["translations-of"] = "%s-ի թարգմանությունները"
+Locale["hy"]["definitions-of"] = "%s-ի սահմանումները"
+Locale["hy"]["synonyms"] = "Հոմանիշներ"
+Locale["hy"]["examples"] = "Օրինակներ"
+Locale["hy"]["see-also"] = "Տես նաև"
+Locale["hy"]["family"] = "Indo-European"
+Locale["hy"]["iso"] = "hye"
+Locale["hy"]["glotto"] = "nucl1235"
+Locale["hy"]["script"] = "Armn"
+Locale["az"]["name"] = "Azerbaijani"
+Locale["az"]["endonym"] = "Azərbaycanca"
+Locale["az"]["translations-of"] = "%s sözünün tərcüməsi"
+Locale["az"]["definitions-of"] = "%s sözünün tərifləri"
+Locale["az"]["synonyms"] = "Sinonimlər"
+Locale["az"]["examples"] = "Nümunələr"
+Locale["az"]["see-also"] = "Həmçinin, baxın:"
+Locale["az"]["family"] = "Turkic"
+Locale["az"]["iso"] = "aze"
+Locale["az"]["glotto"] = "nort2697"
+Locale["az"]["script"] = "Latn"
+Locale["eu"]["name"] = "Basque"
+Locale["eu"]["endonym"] = "Euskara"
+Locale["eu"]["translations-of"] = "%s esapidearen itzulpena"
+Locale["eu"]["definitions-of"] = "Honen definizioak: %s"
+Locale["eu"]["synonyms"] = "Sinonimoak"
+Locale["eu"]["examples"] = "Adibideak"
+Locale["eu"]["see-also"] = "Ikusi hauek ere"
+Locale["eu"]["family"] = "Language Isolate"
+Locale["eu"]["iso"] = "eus"
+Locale["eu"]["glotto"] = "basq1248"
+Locale["eu"]["script"] = "Latn"
+Locale["be"]["name"] = "Belarusian"
+Locale["be"]["endonym"] = "беларуская"
+Locale["be"]["translations-of"] = "Пераклады %s"
+Locale["be"]["definitions-of"] = "Вызначэннi %s"
+Locale["be"]["synonyms"] = "Сінонімы"
+Locale["be"]["examples"] = "Прыклады"
+Locale["be"]["see-also"] = "Гл. таксама"
+Locale["be"]["family"] = "Indo-European"
+Locale["be"]["iso"] = "bel"
+Locale["be"]["glotto"] = "bela1254"
+Locale["be"]["script"] = "Cyrl"
+Locale["bn"]["name"] = "Bengali"
+Locale["bn"]["endonym"] = "বাংলা"
+Locale["bn"]["translations-of"] = "%s এর অনুবাদ"
+Locale["bn"]["definitions-of"] = "%s এর সংজ্ঞা"
+Locale["bn"]["synonyms"] = "প্রতিশব্দ"
+Locale["bn"]["examples"] = "উদাহরণ"
+Locale["bn"]["see-also"] = "আরো দেখুন"
+Locale["bn"]["family"] = "Indo-European"
+Locale["bn"]["iso"] = "ben"
+Locale["bn"]["glotto"] = "beng1280"
+Locale["bn"]["script"] = "Beng"
+Locale["bs"]["name"] = "Bosnian"
+Locale["bs"]["endonym"] = "Bosanski"
+Locale["bs"]["translations-of"] = "Prijevod za: %s"
+Locale["bs"]["definitions-of"] = "Definicije za %s"
+Locale["bs"]["synonyms"] = "Sinonimi"
+Locale["bs"]["examples"] = "Primjeri"
+Locale["bs"]["see-also"] = "Pogledajte i"
+Locale["bs"]["family"] = "Indo-European"
+Locale["bs"]["iso"] = "bos"
+Locale["bs"]["glotto"] = "bosn1245"
+Locale["bs"]["script"] = "Latn"
+Locale["bg"]["name"] = "Bulgarian"
+Locale["bg"]["endonym"] = "български"
+Locale["bg"]["translations-of"] = "Преводи на %s"
+Locale["bg"]["definitions-of"] = "Дефиниции за %s"
+Locale["bg"]["synonyms"] = "Синоними"
+Locale["bg"]["examples"] = "Примери"
+Locale["bg"]["see-also"] = "Вижте също"
+Locale["bg"]["family"] = "Indo-European"
+Locale["bg"]["iso"] = "bul"
+Locale["bg"]["glotto"] = "bulg1262"
+Locale["bg"]["script"] = "Cyrl"
+Locale["ca"]["name"] = "Catalan"
+Locale["ca"]["endonym"] = "Català"
+Locale["ca"]["translations-of"] = "Traduccions per a %s"
+Locale["ca"]["definitions-of"] = "Definicions de: %s"
+Locale["ca"]["synonyms"] = "Sinònims"
+Locale["ca"]["examples"] = "Exemples"
+Locale["ca"]["see-also"] = "Vegeu també"
+Locale["ca"]["family"] = "Indo-European"
+Locale["ca"]["iso"] = "cat"
+Locale["ca"]["glotto"] = "stan1289"
+Locale["ca"]["script"] = "Latn"
+Locale["ceb"]["name"] = "Cebuano"
+Locale["ceb"]["endonym"] = "Cebuano"
+Locale["ceb"]["translations-of"] = "%s Mga Paghubad sa PULONG_O_HUGPONG SA PAMULONG"
+Locale["ceb"]["definitions-of"] = "Mga kahulugan sa %s"
+Locale["ceb"]["synonyms"] = "Mga Kapulong"
+Locale["ceb"]["examples"] = "Mga pananglitan:"
+Locale["ceb"]["see-also"] = "Kitaa pag-usab"
+Locale["ceb"]["family"] = "Austronesian"
+Locale["ceb"]["iso"] = "ceb"
+Locale["ceb"]["glotto"] = "cebu1242"
+Locale["ceb"]["script"] = "Latn"
+Locale["ny"]["name"] = "Chichewa"
+Locale["ny"]["endonym"] = "Nyanja"
+Locale["ny"]["translations-of"] = "Matanthauzidwe a %s"
+Locale["ny"]["definitions-of"] = "Mamasulidwe a %s"
+Locale["ny"]["synonyms"] = "Mau ofanana"
+Locale["ny"]["examples"] = "Zitsanzo"
+Locale["ny"]["see-also"] = "Onaninso"
+Locale["ny"]["family"] = "Atlantic-Congo"
+Locale["ny"]["iso"] = "nya"
+Locale["ny"]["glotto"] = "nyan1308"
+Locale["ny"]["script"] = "Latn"
+Locale["zh-CN"]["name"] = "Chinese Simplified"
+Locale["zh-CN"]["endonym"] = "简体中文"
+Locale["zh-CN"]["translations-of"] = "%s 的翻译"
+Locale["zh-CN"]["definitions-of"] = "%s的定义"
+Locale["zh-CN"]["synonyms"] = "同义词"
+Locale["zh-CN"]["examples"] = "示例"
+Locale["zh-CN"]["see-also"] = "另请参阅"
+Locale["zh-CN"]["family"] = "Sino-Tibetan"
+Locale["zh-CN"]["iso"] = "zho-CN"
+Locale["zh-CN"]["glotto"] = "mand1415"
+Locale["zh-CN"]["script"] = "Hans"
+Locale["zh-CN"]["dictionary"] = "true"
+Locale["zh-TW"]["name"] = "Chinese Traditional"
+Locale["zh-TW"]["endonym"] = "正體中文"
+Locale["zh-TW"]["translations-of"] = "「%s」的翻譯"
+Locale["zh-TW"]["definitions-of"] = "「%s」的定義"
+Locale["zh-TW"]["synonyms"] = "同義詞"
+Locale["zh-TW"]["examples"] = "例句"
+Locale["zh-TW"]["see-also"] = "另請參閱"
+Locale["zh-TW"]["family"] = "Sino-Tibetan"
+Locale["zh-TW"]["iso"] = "zho-TW"
+Locale["zh-TW"]["glotto"] = "mand1415"
+Locale["zh-TW"]["script"] = "Hant"
+Locale["zh-TW"]["dictionary"] = "true"
+Locale["co"]["name"] = "Corsican"
+Locale["co"]["endonym"] = "Corsu"
+Locale["co"]["translations-of"] = "Traductions de %s"
+Locale["co"]["definitions-of"] = "Définitions de %s"
+Locale["co"]["synonyms"] = "Synonymes"
+Locale["co"]["examples"] = "Exemples"
+Locale["co"]["see-also"] = "Voir aussi"
+Locale["co"]["family"] = "Indo-European"
+Locale["co"]["iso"] = "cos"
+Locale["co"]["glotto"] = "cors1242"
+Locale["co"]["script"] = "Latn"
+Locale["hr"]["name"] = "Croatian"
+Locale["hr"]["endonym"] = "Hrvatski"
+Locale["hr"]["translations-of"] = "Prijevodi riječi ili izraza %s"
+Locale["hr"]["definitions-of"] = "Definicije riječi ili izraza %s"
+Locale["hr"]["synonyms"] = "Sinonimi"
+Locale["hr"]["examples"] = "Primjeri"
+Locale["hr"]["see-also"] = "Također pogledajte"
+Locale["hr"]["family"] = "Indo-European"
+Locale["hr"]["iso"] = "hrv"
+Locale["hr"]["glotto"] = "croa1245"
+Locale["hr"]["script"] = "Latn"
+Locale["cs"]["name"] = "Czech"
+Locale["cs"]["endonym"] = "Čeština"
+Locale["cs"]["translations-of"] = "Překlad výrazu %s"
+Locale["cs"]["definitions-of"] = "Definice výrazu %s"
+Locale["cs"]["synonyms"] = "Synonyma"
+Locale["cs"]["examples"] = "Příklady"
+Locale["cs"]["see-also"] = "Viz také"
+Locale["cs"]["family"] = "Indo-European"
+Locale["cs"]["iso"] = "ces"
+Locale["cs"]["glotto"] = "czec1258"
+Locale["cs"]["script"] = "Latn"
+Locale["da"]["name"] = "Danish"
+Locale["da"]["endonym"] = "Dansk"
+Locale["da"]["translations-of"] = "Oversættelser af %s"
+Locale["da"]["definitions-of"] = "Definitioner af %s"
+Locale["da"]["synonyms"] = "Synonymer"
+Locale["da"]["examples"] = "Eksempler"
+Locale["da"]["see-also"] = "Se også"
+Locale["da"]["family"] = "Indo-European"
+Locale["da"]["iso"] = "dan"
+Locale["da"]["glotto"] = "dani1285"
+Locale["da"]["script"] = "Latn"
+Locale["nl"]["name"] = "Dutch"
+Locale["nl"]["endonym"] = "Nederlands"
+Locale["nl"]["translations-of"] = "Vertalingen van %s"
+Locale["nl"]["definitions-of"] = "Definities van %s"
+Locale["nl"]["synonyms"] = "Synoniemen"
+Locale["nl"]["examples"] = "Voorbeelden"
+Locale["nl"]["see-also"] = "Zie ook"
+Locale["nl"]["family"] = "Indo-European"
+Locale["nl"]["iso"] = "nld"
+Locale["nl"]["glotto"] = "dutc1256"
+Locale["nl"]["script"] = "Latn"
+Locale["nl"]["dictionary"] = "true"
+Locale["en"]["name"] = "English"
+Locale["en"]["endonym"] = "English"
+Locale["en"]["translations-of"] = "Translations of %s"
+Locale["en"]["definitions-of"] = "Definitions of %s"
+Locale["en"]["synonyms"] = "Synonyms"
+Locale["en"]["examples"] = "Examples"
+Locale["en"]["see-also"] = "See also"
+Locale["en"]["family"] = "Indo-European"
+Locale["en"]["iso"] = "eng"
+Locale["en"]["glotto"] = "stan1293"
+Locale["en"]["script"] = "Latn"
+Locale["en"]["dictionary"] = "true"
+Locale["eo"]["name"] = "Esperanto"
+Locale["eo"]["endonym"] = "Esperanto"
+Locale["eo"]["translations-of"] = "Tradukoj de %s"
+Locale["eo"]["definitions-of"] = "Difinoj de %s"
+Locale["eo"]["synonyms"] = "Sinonimoj"
+Locale["eo"]["examples"] = "Ekzemploj"
+Locale["eo"]["see-also"] = "Vidu ankaŭ"
+Locale["eo"]["family"] = "Artificial Language"
+Locale["eo"]["iso"] = "epo"
+Locale["eo"]["glotto"] = "espe1235"
+Locale["eo"]["script"] = "Latn"
+Locale["et"]["name"] = "Estonian"
+Locale["et"]["endonym"] = "Eesti"
+Locale["et"]["translations-of"] = "Sõna(de) %s tõlked"
+Locale["et"]["definitions-of"] = "Sõna(de) %s definitsioonid"
+Locale["et"]["synonyms"] = "Sünonüümid"
+Locale["et"]["examples"] = "Näited"
+Locale["et"]["see-also"] = "Vt ka"
+Locale["et"]["family"] = "Uralic"
+Locale["et"]["iso"] = "est"
+Locale["et"]["glotto"] = "esto1258"
+Locale["et"]["script"] = "Latn"
+Locale["tl"]["name"] = "Filipino"
+Locale["tl"]["endonym"] = "Tagalog"
+Locale["tl"]["translations-of"] = "Mga pagsasalin ng %s"
+Locale["tl"]["definitions-of"] = "Mga kahulugan ng %s"
+Locale["tl"]["synonyms"] = "Mga Kasingkahulugan"
+Locale["tl"]["examples"] = "Mga Halimbawa"
+Locale["tl"]["see-also"] = "Tingnan rin ang"
+Locale["tl"]["family"] = "Austronesian"
+Locale["tl"]["iso"] = "tgl"
+Locale["tl"]["glotto"] = "taga1270"
+Locale["tl"]["script"] = "Latn"
+Locale["fi"]["name"] = "Finnish"
+Locale["fi"]["endonym"] = "Suomi"
+Locale["fi"]["translations-of"] = "Käännökset tekstille %s"
+Locale["fi"]["definitions-of"] = "Määritelmät kohteelle %s"
+Locale["fi"]["synonyms"] = "Synonyymit"
+Locale["fi"]["examples"] = "Esimerkkejä"
+Locale["fi"]["see-also"] = "Katso myös"
+Locale["fi"]["family"] = "Uralic"
+Locale["fi"]["iso"] = "fin"
+Locale["fi"]["glotto"] = "finn1318"
+Locale["fi"]["script"] = "Latn"
+Locale["fr"]["name"] = "French"
+Locale["fr"]["endonym"] = "Français"
+Locale["fr"]["translations-of"] = "Traductions de %s"
+Locale["fr"]["definitions-of"] = "Définitions de %s"
+Locale["fr"]["synonyms"] = "Synonymes"
+Locale["fr"]["examples"] = "Exemples"
+Locale["fr"]["see-also"] = "Voir aussi"
+Locale["fr"]["family"] = "Indo-European"
+Locale["fr"]["iso"] = "fra"
+Locale["fr"]["glotto"] = "stan1290"
+Locale["fr"]["script"] = "Latn"
+Locale["fr"]["dictionary"] = "true"
+Locale["gl"]["name"] = "Galician"
+Locale["gl"]["endonym"] = "Galego"
+Locale["gl"]["translations-of"] = "Traducións de %s"
+Locale["gl"]["definitions-of"] = "Definicións de %s"
+Locale["gl"]["synonyms"] = "Sinónimos"
+Locale["gl"]["examples"] = "Exemplos"
+Locale["gl"]["see-also"] = "Ver tamén"
+Locale["gl"]["family"] = "Indo-European"
+Locale["gl"]["iso"] = "glg"
+Locale["gl"]["glotto"] = "gali1258"
+Locale["gl"]["script"] = "Latn"
+Locale["ka"]["name"] = "Georgian"
+Locale["ka"]["endonym"] = "ქართული"
+Locale["ka"]["translations-of"] = "%s-ის თარგმანები"
+Locale["ka"]["definitions-of"] = "%s-ის განსაზღვრებები"
+Locale["ka"]["synonyms"] = "სინონიმები"
+Locale["ka"]["examples"] = "მაგალითები"
+Locale["ka"]["see-also"] = "ასევე იხილეთ"
+Locale["ka"]["family"] = "Kartvelian"
+Locale["ka"]["iso"] = "kat"
+Locale["ka"]["glotto"] = "nucl1302"
+Locale["ka"]["script"] = "Geor"
+Locale["de"]["name"] = "German"
+Locale["de"]["endonym"] = "Deutsch"
+Locale["de"]["translations-of"] = "Übersetzungen für %s"
+Locale["de"]["definitions-of"] = "Definitionen von %s"
+Locale["de"]["synonyms"] = "Synonyme"
+Locale["de"]["examples"] = "Beispiele"
+Locale["de"]["see-also"] = "Siehe auch"
+Locale["de"]["family"] = "Indo-European"
+Locale["de"]["iso"] = "deu"
+Locale["de"]["glotto"] = "stan1295"
+Locale["de"]["script"] = "Latn"
+Locale["de"]["dictionary"] = "true"
+Locale["el"]["name"] = "Greek"
+Locale["el"]["endonym"] = "Ελληνικά"
+Locale["el"]["translations-of"] = "Μεταφράσεις του %s"
+Locale["el"]["definitions-of"] = "Όρισμοί %s"
+Locale["el"]["synonyms"] = "Συνώνυμα"
+Locale["el"]["examples"] = "Παραδείγματα"
+Locale["el"]["see-also"] = "Δείτε επίσης"
+Locale["el"]["family"] = "Indo-European"
+Locale["el"]["iso"] = "ell"
+Locale["el"]["glotto"] = "mode1248"
+Locale["el"]["script"] = "Grek"
+Locale["gu"]["name"] = "Gujarati"
+Locale["gu"]["endonym"] = "ગુજરાતી"
+Locale["gu"]["translations-of"] = "%s ના અનુવાદ"
+Locale["gu"]["definitions-of"] = "%s ની વ્યાખ્યાઓ"
+Locale["gu"]["synonyms"] = "સમાનાર્થી"
+Locale["gu"]["examples"] = "ઉદાહરણો"
+Locale["gu"]["see-also"] = "આ પણ જુઓ"
+Locale["gu"]["family"] = "Indo-European"
+Locale["gu"]["iso"] = "guj"
+Locale["gu"]["glotto"] = "guja1252"
+Locale["gu"]["script"] = "Gujr"
+Locale["ht"]["name"] = "Haitian Creole"
+Locale["ht"]["endonym"] = "Kreyòl Ayisyen"
+Locale["ht"]["translations-of"] = "Tradiksyon %s"
+Locale["ht"]["definitions-of"] = "Definisyon nan %s"
+Locale["ht"]["synonyms"] = "Sinonim"
+Locale["ht"]["examples"] = "Egzanp:"
+Locale["ht"]["see-also"] = "Wè tou"
+Locale["ht"]["family"] = "Indo-European"
+Locale["ht"]["iso"] = "hat"
+Locale["ht"]["glotto"] = "hait1244"
+Locale["ht"]["script"] = "Latn"
+Locale["haw"]["name"] = "Hawaiian"
+Locale["haw"]["endonym"] = "ʻŌlelo Hawaiʻi"
+Locale["haw"]["family"] = "Austronesian"
+Locale["haw"]["iso"] = "haw"
+Locale["haw"]["glotto"] = "hawa1245"
+Locale["haw"]["script"] = "Latn"
+Locale["ha"]["name"] = "Hausa"
+Locale["ha"]["endonym"] = "Hausa"
+Locale["ha"]["translations-of"] = "Fassarar %s"
+Locale["ha"]["definitions-of"] = "Ma'anoni na %s"
+Locale["ha"]["synonyms"] = "Masu kamancin ma'ana"
+Locale["ha"]["examples"] = "Misalai"
+Locale["ha"]["see-also"] = "Duba kuma"
+Locale["ha"]["family"] = "Afro-Asiatic"
+Locale["ha"]["iso"] = "hau"
+Locale["ha"]["glotto"] = "haus1257"
+Locale["ha"]["script"] = "Latn"
+Locale["he"]["name"] = "Hebrew"
+Locale["he"]["endonym"] = "עִבְרִית"
+Locale["he"]["translations-of"] = "תרגומים של %s"
+Locale["he"]["definitions-of"] = "הגדרות של %s"
+Locale["he"]["synonyms"] = "מילים נרדפות"
+Locale["he"]["examples"] = "דוגמאות"
+Locale["he"]["see-also"] = "ראה גם"
+Locale["he"]["family"] = "Afro-Asiatic"
+Locale["he"]["iso"] = "heb"
+Locale["he"]["glotto"] = "hebr1245"
+Locale["he"]["script"] = "Hebr"
+Locale["he"]["rtl"] = "true"
+Locale["hi"]["name"] = "Hindi"
+Locale["hi"]["endonym"] = "हिन्दी"
+Locale["hi"]["translations-of"] = "%s के अनुवाद"
+Locale["hi"]["definitions-of"] = "%s की परिभाषाएं"
+Locale["hi"]["synonyms"] = "समानार्थी"
+Locale["hi"]["examples"] = "उदाहरण"
+Locale["hi"]["see-also"] = "यह भी देखें"
+Locale["hi"]["family"] = "Indo-European"
+Locale["hi"]["iso"] = "hin"
+Locale["hi"]["glotto"] = "hind1269"
+Locale["hi"]["script"] = "Deva"
+Locale["hmn"]["name"] = "Hmong"
+Locale["hmn"]["endonym"] = "Hmoob"
+Locale["hmn"]["translations-of"] = "Lus txhais: %s"
+Locale["hmn"]["family"] = "Hmong-Mien"
+Locale["hmn"]["iso"] = "hmn"
+Locale["hmn"]["glotto"] = "firs1234"
+Locale["hmn"]["script"] = "Latn"
+Locale["hu"]["name"] = "Hungarian"
+Locale["hu"]["endonym"] = "Magyar"
+Locale["hu"]["translations-of"] = "%s fordításai"
+Locale["hu"]["definitions-of"] = "%s jelentései"
+Locale["hu"]["synonyms"] = "Szinonimák"
+Locale["hu"]["examples"] = "Példák"
+Locale["hu"]["see-also"] = "Lásd még"
+Locale["hu"]["family"] = "Uralic"
+Locale["hu"]["iso"] = "hun"
+Locale["hu"]["glotto"] = "hung1274"
+Locale["hu"]["script"] = "Latn"
+Locale["is"]["name"] = "Icelandic"
+Locale["is"]["endonym"] = "Íslenska"
+Locale["is"]["translations-of"] = "Þýðingar á %s"
+Locale["is"]["definitions-of"] = "Skilgreiningar á"
+Locale["is"]["synonyms"] = "Samheiti"
+Locale["is"]["examples"] = "Dæmi"
+Locale["is"]["see-also"] = "Sjá einnig"
+Locale["is"]["family"] = "Indo-European"
+Locale["is"]["iso"] = "isl"
+Locale["is"]["glotto"] = "icel1247"
+Locale["is"]["script"] = "Latn"
+Locale["ig"]["name"] = "Igbo"
+Locale["ig"]["endonym"] = "Igbo"
+Locale["ig"]["translations-of"] = "Ntụgharị asụsụ nke %s"
+Locale["ig"]["definitions-of"] = "Nkọwapụta nke %s"
+Locale["ig"]["synonyms"] = "Okwu oyiri"
+Locale["ig"]["examples"] = "Ọmụmaatụ"
+Locale["ig"]["see-also"] = "Hụkwuo"
+Locale["ig"]["family"] = "Atlantic-Congo"
+Locale["ig"]["iso"] = "ibo"
+Locale["ig"]["glotto"] = "nucl1417"
+Locale["ig"]["script"] = "Latn"
+Locale["id"]["name"] = "Indonesian"
+Locale["id"]["endonym"] = "Bahasa Indonesia"
+Locale["id"]["translations-of"] = "Terjemahan dari %s"
+Locale["id"]["definitions-of"] = "Definisi %s"
+Locale["id"]["synonyms"] = "Sinonim"
+Locale["id"]["examples"] = "Contoh"
+Locale["id"]["see-also"] = "Lihat juga"
+Locale["id"]["family"] = "Austronesian"
+Locale["id"]["iso"] = "ind"
+Locale["id"]["glotto"] = "indo1316"
+Locale["id"]["script"] = "Latn"
+Locale["ga"]["name"] = "Irish"
+Locale["ga"]["endonym"] = "Gaeilge"
+Locale["ga"]["translations-of"] = "Aistriúcháin ar %s"
+Locale["ga"]["definitions-of"] = "Sainmhínithe ar %s"
+Locale["ga"]["synonyms"] = "Comhchiallaigh"
+Locale["ga"]["examples"] = "Samplaí"
+Locale["ga"]["see-also"] = "féach freisin"
+Locale["ga"]["family"] = "Indo-European"
+Locale["ga"]["iso"] = "gle"
+Locale["ga"]["glotto"] = "iris1253"
+Locale["ga"]["script"] = "Latn"
+Locale["it"]["name"] = "Italian"
+Locale["it"]["endonym"] = "Italiano"
+Locale["it"]["translations-of"] = "Traduzioni di %s"
+Locale["it"]["definitions-of"] = "Definizioni di %s"
+Locale["it"]["synonyms"] = "Sinonimi"
+Locale["it"]["examples"] = "Esempi"
+Locale["it"]["see-also"] = "Vedi anche"
+Locale["it"]["family"] = "Indo-European"
+Locale["it"]["iso"] = "ita"
+Locale["it"]["glotto"] = "ital1282"
+Locale["it"]["script"] = "Latn"
+Locale["it"]["dictionary"] = "true"
+Locale["ja"]["name"] = "Japanese"
+Locale["ja"]["endonym"] = "日本語"
+Locale["ja"]["translations-of"] = "「%s」の翻訳"
+Locale["ja"]["definitions-of"] = "%s の定義"
+Locale["ja"]["synonyms"] = "同義語"
+Locale["ja"]["examples"] = "例"
+Locale["ja"]["see-also"] = "関連項目"
+Locale["ja"]["family"] = "Japonic"
+Locale["ja"]["iso"] = "jpn"
+Locale["ja"]["glotto"] = "nucl1643"
+Locale["ja"]["script"] = "Jpan"
+Locale["ja"]["dictionary"] = "true"
+Locale["jv"]["name"] = "Javanese"
+Locale["jv"]["endonym"] = "Basa Jawa"
+Locale["jv"]["translations-of"] = "Terjemahan %s"
+Locale["jv"]["definitions-of"] = "Arti %s"
+Locale["jv"]["synonyms"] = "Sinonim"
+Locale["jv"]["examples"] = "Conto"
+Locale["jv"]["see-also"] = "Deleng uga"
+Locale["jv"]["family"] = "Austronesian"
+Locale["jv"]["iso"] = "jav"
+Locale["jv"]["glotto"] = "java1254"
+Locale["jv"]["script"] = "Latn"
+Locale["kn"]["name"] = "Kannada"
+Locale["kn"]["endonym"] = "ಕನ್ನಡ"
+Locale["kn"]["translations-of"] = "%s ನ ಅನುವಾದಗಳು"
+Locale["kn"]["definitions-of"] = "%s ನ ವ್ಯಾಖ್ಯಾನಗಳು"
+Locale["kn"]["synonyms"] = "ಸಮಾನಾರ್ಥಕಗಳು"
+Locale["kn"]["examples"] = "ಉದಾಹರಣೆಗಳು"
+Locale["kn"]["see-also"] = "ಇದನ್ನೂ ಗಮನಿಸಿ"
+Locale["kn"]["family"] = "Dravidian"
+Locale["kn"]["iso"] = "kan"
+Locale["kn"]["glotto"] = "nucl1305"
+Locale["kn"]["script"] = "Knda"
+Locale["kk"]["name"] = "Kazakh"
+Locale["kk"]["endonym"] = "Қазақ тілі"
+Locale["kk"]["translations-of"] = "%s аудармалары"
+Locale["kk"]["definitions-of"] = "%s анықтамалары"
+Locale["kk"]["synonyms"] = "Синонимдер"
+Locale["kk"]["examples"] = "Мысалдар"
+Locale["kk"]["see-also"] = "Келесі тізімді де көріңіз:"
+Locale["kk"]["family"] = "Turkic"
+Locale["kk"]["iso"] = "kaz"
+Locale["kk"]["glotto"] = "kaza1248"
+Locale["kk"]["script"] = "Cyrl"
+Locale["km"]["name"] = "Khmer"
+Locale["km"]["endonym"] = "ភាសាខ្មែរ"
+Locale["km"]["translations-of"] = "ការបកប្រែនៃ %s"
+Locale["km"]["definitions-of"] = "និយមន័យនៃ %s"
+Locale["km"]["synonyms"] = "សទិសន័យ"
+Locale["km"]["examples"] = "ឧទាហរណ៍"
+Locale["km"]["see-also"] = "មើលផងដែរ"
+Locale["km"]["family"] = "Austroasiatic"
+Locale["km"]["iso"] = "khm"
+Locale["km"]["glotto"] = "cent1989"
+Locale["km"]["script"] = "Khmr"
+Locale["ko"]["name"] = "Korean"
+Locale["ko"]["endonym"] = "한국어"
+Locale["ko"]["translations-of"] = "%s의 번역"
+Locale["ko"]["definitions-of"] = "%s의 정의"
+Locale["ko"]["synonyms"] = "동의어"
+Locale["ko"]["examples"] = "예문"
+Locale["ko"]["see-also"] = "참조"
+Locale["ko"]["family"] = "Koreanic"
+Locale["ko"]["iso"] = "kor"
+Locale["ko"]["glotto"] = "kore1280"
+Locale["ko"]["script"] = "Kore"
+Locale["ko"]["dictionary"] = "true"
+Locale["ku"]["name"] = "Kurdish"
+Locale["ku"]["endonym"] = "Kurdî"
+Locale["ku"]["family"] = "Indo-European"
+Locale["ku"]["iso"] = "kur"
+Locale["ku"]["glotto"] = "kurd1259"
+Locale["ku"]["script"] = "Latn"
+Locale["ky"]["name"] = "Kyrgyz"
+Locale["ky"]["endonym"] = "Кыргызча"
+Locale["ky"]["translations-of"] = "%s котормосу"
+Locale["ky"]["definitions-of"] = "%s аныктамасы"
+Locale["ky"]["synonyms"] = "Синонимдер"
+Locale["ky"]["examples"] = "Мисалдар"
+Locale["ky"]["see-also"] = "Дагы караңыз"
+Locale["ky"]["family"] = "Turkic"
+Locale["ky"]["iso"] = "kir"
+Locale["ky"]["glotto"] = "kirg1245"
+Locale["ky"]["script"] = "Cyrl"
+Locale["lo"]["name"] = "Lao"
+Locale["lo"]["endonym"] = "ລາວ"
+Locale["lo"]["translations-of"] = "ຄຳແປສຳລັບ %s"
+Locale["lo"]["definitions-of"] = "ຄວາມໝາຍຂອງ %s"
+Locale["lo"]["synonyms"] = "ຄຳທີ່ຄ້າຍກັນ %s"
+Locale["lo"]["examples"] = "ຕົວຢ່າງ"
+Locale["lo"]["see-also"] = "ເບິ່ງເພີ່ມເຕີມ"
+Locale["lo"]["family"] = "Tai-Kadai"
+Locale["lo"]["iso"] = "lao"
+Locale["lo"]["glotto"] = "laoo1244"
+Locale["lo"]["script"] = "Laoo"
+Locale["la"]["name"] = "Latin"
+Locale["la"]["endonym"] = "Latina"
+Locale["la"]["translations-of"] = "Versio de %s"
+Locale["la"]["family"] = "Indo-European"
+Locale["la"]["iso"] = "lat"
+Locale["la"]["glotto"] = "lati1261"
+Locale["la"]["script"] = "Latn"
+Locale["lv"]["name"] = "Latvian"
+Locale["lv"]["endonym"] = "Latviešu"
+Locale["lv"]["translations-of"] = "%s tulkojumi"
+Locale["lv"]["definitions-of"] = "%s definīcijas"
+Locale["lv"]["synonyms"] = "Sinonīmi"
+Locale["lv"]["examples"] = "Piemēri"
+Locale["lv"]["see-also"] = "Skatiet arī"
+Locale["lv"]["family"] = "Indo-European"
+Locale["lv"]["iso"] = "lav"
+Locale["lv"]["glotto"] = "latv1249"
+Locale["lv"]["script"] = "Latn"
+Locale["lt"]["name"] = "Lithuanian"
+Locale["lt"]["endonym"] = "Lietuvių"
+Locale["lt"]["translations-of"] = "„%s“ vertimai"
+Locale["lt"]["definitions-of"] = "„%s“ apibrėžimai"
+Locale["lt"]["synonyms"] = "Sinonimai"
+Locale["lt"]["examples"] = "Pavyzdžiai"
+Locale["lt"]["see-also"] = "Taip pat žiūrėkite"
+Locale["lt"]["family"] = "Indo-European"
+Locale["lt"]["iso"] = "lit"
+Locale["lt"]["glotto"] = "lith1251"
+Locale["lt"]["script"] = "Latn"
+Locale["lb"]["name"] = "Luxembourgish"
+Locale["lb"]["endonym"] = "Lëtzebuergesch"
+Locale["lb"]["family"] = "Indo-European"
+Locale["lb"]["iso"] = "ltz"
+Locale["lb"]["glotto"] = "luxe1241"
+Locale["lb"]["script"] = "Latn"
+Locale["mk"]["name"] = "Macedonian"
+Locale["mk"]["endonym"] = "Македонски"
+Locale["mk"]["translations-of"] = "Преводи на %s"
+Locale["mk"]["definitions-of"] = "Дефиниции на %s"
+Locale["mk"]["synonyms"] = "Синоними"
+Locale["mk"]["examples"] = "Примери"
+Locale["mk"]["see-also"] = "Види и"
+Locale["mk"]["family"] = "Indo-European"
+Locale["mk"]["iso"] = "mkd"
+Locale["mk"]["glotto"] = "mace1250"
+Locale["mk"]["script"] = "Cyrl"
+Locale["mg"]["name"] = "Malagasy"
+Locale["mg"]["endonym"] = "Malagasy"
+Locale["mg"]["translations-of"] = "Dikan'ny %s"
+Locale["mg"]["definitions-of"] = "Famaritana ny %s"
+Locale["mg"]["synonyms"] = "Mitovy hevitra"
+Locale["mg"]["examples"] = "Ohatra"
+Locale["mg"]["see-also"] = "Jereo ihany koa"
+Locale["mg"]["family"] = "Austronesian"
+Locale["mg"]["iso"] = "mlg"
+Locale["mg"]["glotto"] = "plat1254"
+Locale["mg"]["script"] = "Latn"
+Locale["ms"]["name"] = "Malay"
+Locale["ms"]["endonym"] = "Bahasa Melayu"
+Locale["ms"]["translations-of"] = "Terjemahan %s"
+Locale["ms"]["definitions-of"] = "Takrif %s"
+Locale["ms"]["synonyms"] = "Sinonim"
+Locale["ms"]["examples"] = "Contoh"
+Locale["ms"]["see-also"] = "Lihat juga"
+Locale["ms"]["family"] = "Austronesian"
+Locale["ms"]["iso"] = "msa"
+Locale["ms"]["glotto"] = "stan1306"
+Locale["ms"]["script"] = "Latn"
+Locale["ml"]["name"] = "Malayalam"
+Locale["ml"]["endonym"] = "മലയാളം"
+Locale["ml"]["translations-of"] = "%s എന്നതിന്റെ വിവർത്തനങ്ങൾ"
+Locale["ml"]["definitions-of"] = "%s എന്നതിന്റെ നിർവ്വചനങ്ങൾ"
+Locale["ml"]["synonyms"] = "പര്യായങ്ങള്"
+Locale["ml"]["examples"] = "ഉദാഹരണങ്ങള്"
+Locale["ml"]["see-also"] = "ഇതും കാണുക"
+Locale["ml"]["family"] = "Dravidian"
+Locale["ml"]["iso"] = "mal"
+Locale["ml"]["glotto"] = "mala1464"
+Locale["ml"]["script"] = "Mlym"
+Locale["mt"]["name"] = "Maltese"
+Locale["mt"]["endonym"] = "Malti"
+Locale["mt"]["translations-of"] = "Traduzzjonijiet ta' %s"
+Locale["mt"]["definitions-of"] = "Definizzjonijiet ta' %s"
+Locale["mt"]["synonyms"] = "Sinonimi"
+Locale["mt"]["examples"] = "Eżempji"
+Locale["mt"]["see-also"] = "Ara wkoll"
+Locale["mt"]["family"] = "Afro-Asiatic"
+Locale["mt"]["iso"] = "mlt"
+Locale["mt"]["glotto"] = "malt1254"
+Locale["mt"]["script"] = "Latn"
+Locale["mi"]["name"] = "Maori"
+Locale["mi"]["endonym"] = "Māori"
+Locale["mi"]["translations-of"] = "Ngā whakamāoritanga o %s"
+Locale["mi"]["definitions-of"] = "Ngā whakamārama o %s"
+Locale["mi"]["synonyms"] = "Ngā Kupu Taurite"
+Locale["mi"]["examples"] = "Ngā Tauira:"
+Locale["mi"]["see-also"] = "Tiro hoki:"
+Locale["mi"]["family"] = "Austronesian"
+Locale["mi"]["iso"] = "mri"
+Locale["mi"]["glotto"] = "maor1246"
+Locale["mi"]["script"] = "Latn"
+Locale["mr"]["name"] = "Marathi"
+Locale["mr"]["endonym"] = "मराठी"
+Locale["mr"]["translations-of"] = "%s ची भाषांतरे"
+Locale["mr"]["definitions-of"] = "%s च्या व्याख्या"
+Locale["mr"]["synonyms"] = "समानार्थी शब्द"
+Locale["mr"]["examples"] = "उदाहरणे"
+Locale["mr"]["see-also"] = "हे देखील पहा"
+Locale["mr"]["family"] = "Indo-European"
+Locale["mr"]["iso"] = "mar"
+Locale["mr"]["glotto"] = "mara1378"
+Locale["mr"]["script"] = "Deva"
+Locale["mn"]["name"] = "Mongolian"
+Locale["mn"]["endonym"] = "Монгол"
+Locale["mn"]["translations-of"] = "%s-н орчуулга"
+Locale["mn"]["definitions-of"] = "%s үгийн тодорхойлолт"
+Locale["mn"]["synonyms"] = "Ойролцоо утгатай"
+Locale["mn"]["examples"] = "Жишээнүүд"
+Locale["mn"]["see-also"] = "Мөн харах"
+Locale["mn"]["family"] = "Mongolic"
+Locale["mn"]["iso"] = "mon"
+Locale["mn"]["glotto"] = "mong1331"
+Locale["mn"]["script"] = "Cyrl"
+Locale["my"]["name"] = "Myanmar"
+Locale["my"]["endonym"] = "မြန်မာစာ"
+Locale["my"]["translations-of"] = "%s၏ ဘာသာပြန်ဆိုချက်များ"
+Locale["my"]["definitions-of"] = "%s၏ အနက်ဖွင့်ဆိုချက်များ"
+Locale["my"]["synonyms"] = "ကြောင်းတူသံကွဲများ"
+Locale["my"]["examples"] = "ဥပမာ"
+Locale["my"]["see-also"] = "ဖော်ပြပါများကိုလဲ ကြည့်ပါ"
+Locale["my"]["family"] = "Sino-Tibetan"
+Locale["my"]["iso"] = "mya"
+Locale["my"]["glotto"] = "nucl1310"
+Locale["my"]["script"] = "Mymr"
+Locale["ne"]["name"] = "Nepali"
+Locale["ne"]["endonym"] = "नेपाली"
+Locale["ne"]["translations-of"] = "%sका अनुवाद"
+Locale["ne"]["definitions-of"] = "%sको परिभाषा"
+Locale["ne"]["synonyms"] = "समानार्थीहरू"
+Locale["ne"]["examples"] = "उदाहरणहरु"
+Locale["ne"]["see-also"] = "यो पनि हेर्नुहोस्"
+Locale["ne"]["family"] = "Indo-European"
+Locale["ne"]["iso"] = "nep"
+Locale["ne"]["glotto"] = "nepa1254"
+Locale["ne"]["script"] = "Deva"
+Locale["no"]["name"] = "Norwegian"
+Locale["no"]["endonym"] = "Norsk"
+Locale["no"]["translations-of"] = "Oversettelser av %s"
+Locale["no"]["definitions-of"] = "Definisjoner av %s"
+Locale["no"]["synonyms"] = "Synonymer"
+Locale["no"]["examples"] = "Eksempler"
+Locale["no"]["see-also"] = "Se også"
+Locale["no"]["family"] = "Indo-European"
+Locale["no"]["iso"] = "nor"
+Locale["no"]["glotto"] = "norw1258"
+Locale["no"]["script"] = "Latn"
+Locale["ps"]["name"] = "Pashto"
+Locale["ps"]["endonym"] = "پښتو"
+Locale["ps"]["translations-of"] = "د %sژباړې"
+Locale["ps"]["definitions-of"] = "د%s تعریفونه"
+Locale["ps"]["synonyms"] = "مترادف لغتونه"
+Locale["ps"]["examples"] = "بېلګې"
+Locale["ps"]["see-also"] = "دا هم ووینئ"
+Locale["ps"]["family"] = "Indo-European"
+Locale["ps"]["iso"] = "pus"
+Locale["ps"]["glotto"] = "pash1269"
+Locale["ps"]["script"] = "Arab"
+Locale["ps"]["rtl"] = "true"
+Locale["fa"]["name"] = "Persian"
+Locale["fa"]["endonym"] = "فارسی"
+Locale["fa"]["translations-of"] = "ترجمههای %s"
+Locale["fa"]["definitions-of"] = "تعریفهای %s"
+Locale["fa"]["synonyms"] = "مترادفها"
+Locale["fa"]["examples"] = "مثالها"
+Locale["fa"]["see-also"] = "همچنین مراجعه کنید به"
+Locale["fa"]["family"] = "Indo-European"
+Locale["fa"]["iso"] = "fas"
+Locale["fa"]["glotto"] = "west2369"
+Locale["fa"]["script"] = "Arab"
+Locale["fa"]["rtl"] = "true"
+Locale["pl"]["name"] = "Polish"
+Locale["pl"]["endonym"] = "Polski"
+Locale["pl"]["translations-of"] = "Tłumaczenia %s"
+Locale["pl"]["definitions-of"] = "%s – definicje"
+Locale["pl"]["synonyms"] = "Synonimy"
+Locale["pl"]["examples"] = "Przykłady"
+Locale["pl"]["see-also"] = "Zobacz też"
+Locale["pl"]["family"] = "Indo-European"
+Locale["pl"]["iso"] = "pol"
+Locale["pl"]["glotto"] = "poli1260"
+Locale["pl"]["script"] = "Latn"
+Locale["pt"]["name"] = "Portuguese"
+Locale["pt"]["endonym"] = "Português"
+Locale["pt"]["translations-of"] = "Traduções de %s"
+Locale["pt"]["definitions-of"] = "Definições de %s"
+Locale["pt"]["synonyms"] = "Sinônimos"
+Locale["pt"]["examples"] = "Exemplos"
+Locale["pt"]["see-also"] = "Veja também"
+Locale["pt"]["family"] = "Indo-European"
+Locale["pt"]["iso"] = "por"
+Locale["pt"]["glotto"] = "port1283"
+Locale["pt"]["script"] = "Latn"
+Locale["pt"]["dictionary"] = "true"
+Locale["pa"]["name"] = "Punjabi"
+Locale["pa"]["endonym"] = "ਪੰਜਾਬੀ"
+Locale["pa"]["translations-of"] = "ਦੇ ਅਨੁਵਾਦ%s"
+Locale["pa"]["definitions-of"] = "ਦੀਆਂ ਪਰਿਭਾਸ਼ਾ %s"
+Locale["pa"]["synonyms"] = "ਸਮਾਨਾਰਥਕ ਸ਼ਬਦ"
+Locale["pa"]["examples"] = "ਉਦਾਹਰਣਾਂ"
+Locale["pa"]["see-also"] = "ਇਹ ਵੀ ਵੇਖੋ"
+Locale["pa"]["family"] = "Indo-European"
+Locale["pa"]["iso"] = "pan"
+Locale["pa"]["glotto"] = "panj1256"
+Locale["pa"]["script"] = "Guru"
+Locale["ro"]["name"] = "Romanian"
+Locale["ro"]["endonym"] = "Română"
+Locale["ro"]["translations-of"] = "Traduceri pentru %s"
+Locale["ro"]["definitions-of"] = "Definiții pentru %s"
+Locale["ro"]["synonyms"] = "Sinonime"
+Locale["ro"]["examples"] = "Exemple"
+Locale["ro"]["see-also"] = "Vedeți și"
+Locale["ro"]["family"] = "Indo-European"
+Locale["ro"]["iso"] = "ron"
+Locale["ro"]["glotto"] = "roma1327"
+Locale["ro"]["script"] = "Latn"
+Locale["ru"]["name"] = "Russian"
+Locale["ru"]["endonym"] = "Русский"
+Locale["ru"]["translations-of"] = "%s: варианты перевода"
+Locale["ru"]["definitions-of"] = "%s – определения"
+Locale["ru"]["synonyms"] = "Синонимы"
+Locale["ru"]["examples"] = "Примеры"
+Locale["ru"]["see-also"] = "Похожие слова"
+Locale["ru"]["family"] = "Indo-European"
+Locale["ru"]["iso"] = "rus"
+Locale["ru"]["glotto"] = "russ1263"
+Locale["ru"]["script"] = "Cyrl"
+Locale["ru"]["dictionary"] = "true"
+Locale["sm"]["name"] = "Samoan"
+Locale["sm"]["endonym"] = "Gagana Sāmoa"
+Locale["sm"]["family"] = "Austronesian"
+Locale["sm"]["iso"] = "smo"
+Locale["sm"]["glotto"] = "samo1305"
+Locale["sm"]["script"] = "Latn"
+Locale["gd"]["name"] = "Scots Gaelic"
+Locale["gd"]["endonym"] = "Gàidhlig"
+Locale["gd"]["translations-of"] = "Eadar-theangachadh airson %s"
+Locale["gd"]["definitions-of"] = "Deifiniseanan airson %s"
+Locale["gd"]["synonyms"] = "Co-fhaclan"
+Locale["gd"]["examples"] = "Buill-eisimpleir"
+Locale["gd"]["see-also"] = "Faic na leanas cuideachd"
+Locale["gd"]["family"] = "Indo-European"
+Locale["gd"]["iso"] = "gla"
+Locale["gd"]["glotto"] = "scot1245"
+Locale["gd"]["script"] = "Latn"
+Locale["sr-Cyrl"]["name"] = "Serbian (Cyrillic)"
+Locale["sr-Cyrl"]["endonym"] = "српски"
+Locale["sr-Cyrl"]["translations-of"] = "Преводи за „%s“"
+Locale["sr-Cyrl"]["definitions-of"] = "Дефиниције за %s"
+Locale["sr-Cyrl"]["synonyms"] = "Синоними"
+Locale["sr-Cyrl"]["examples"] = "Примери"
+Locale["sr-Cyrl"]["see-also"] = "Погледајте такође"
+Locale["sr-Cyrl"]["family"] = "Indo-European"
+Locale["sr-Cyrl"]["iso"] = "srp-Cyrl"
+Locale["sr-Cyrl"]["glotto"] = "serb1264"
+Locale["sr-Cyrl"]["script"] = "Cyrl"
+Locale["sr-Latn"]["name"] = "Serbian (Latin)"
+Locale["sr-Latn"]["endonym"] = "srpski"
+Locale["sr-Latn"]["translations-of"] = "Prevodi za „%s“"
+Locale["sr-Latn"]["definitions-of"] = "Definicije za %s"
+Locale["sr-Latn"]["synonyms"] = "Sinonimi"
+Locale["sr-Latn"]["examples"] = "Primeri"
+Locale["sr-Latn"]["see-also"] = "Pogledajte takođe"
+Locale["sr-Latn"]["family"] = "Indo-European"
+Locale["sr-Latn"]["iso"] = "srp-Latn"
+Locale["sr-Latn"]["glotto"] = "serb1264"
+Locale["sr-Latn"]["script"] = "Latn"
+Locale["st"]["name"] = "Sesotho"
+Locale["st"]["endonym"] = "Sesotho"
+Locale["st"]["translations-of"] = "Liphetolelo tsa %s"
+Locale["st"]["definitions-of"] = "Meelelo ea %s"
+Locale["st"]["synonyms"] = "Mantsoe a tšoanang ka moelelo"
+Locale["st"]["examples"] = "Mehlala"
+Locale["st"]["see-also"] = "Bona hape"
+Locale["st"]["family"] = "Atlantic-Congo"
+Locale["st"]["iso"] = "sot"
+Locale["st"]["glotto"] = "sout2807"
+Locale["st"]["script"] = "Latn"
+Locale["sn"]["name"] = "Shona"
+Locale["sn"]["endonym"] = "chiShona"
+Locale["sn"]["translations-of"] = "Shanduro dze %s"
+Locale["sn"]["definitions-of"] = "Zvinoreva %s"
+Locale["sn"]["synonyms"] = "Mashoko anoreva zvakafana nemamwe"
+Locale["sn"]["examples"] = "Mienzaniso"
+Locale["sn"]["see-also"] = "Onawo"
+Locale["sn"]["family"] = "Atlantic-Congo"
+Locale["sn"]["iso"] = "sna"
+Locale["sn"]["glotto"] = "core1255"
+Locale["sn"]["script"] = "Latn"
+Locale["sd"]["name"] = "Sindhi"
+Locale["sd"]["endonym"] = "سنڌي"
+Locale["sd"]["translations-of"] = "%s جو ترجمو"
+Locale["sd"]["definitions-of"] = "%s جون وصفون"
+Locale["sd"]["synonyms"] = "هم معني"
+Locale["sd"]["examples"] = "مثالون"
+Locale["sd"]["see-also"] = "به ڏسو"
+Locale["sd"]["family"] = "Indo-European"
+Locale["sd"]["iso"] = "snd"
+Locale["sd"]["glotto"] = "sind1272"
+Locale["sd"]["script"] = "Arab"
+Locale["sd"]["rtl"] = "true"
+Locale["si"]["name"] = "Sinhala"
+Locale["si"]["endonym"] = "සිංහල"
+Locale["si"]["translations-of"] = "%s හි පරිවර්තන"
+Locale["si"]["definitions-of"] = "%s හි නිර්වචන"
+Locale["si"]["synonyms"] = "සමානාර්ථ පද"
+Locale["si"]["examples"] = "උදාහරණ"
+Locale["si"]["see-also"] = "මෙයත් බලන්න"
+Locale["si"]["family"] = "Indo-European"
+Locale["si"]["iso"] = "sin"
+Locale["si"]["glotto"] = "sinh1246"
+Locale["si"]["script"] = "Sinh"
+Locale["sk"]["name"] = "Slovak"
+Locale["sk"]["endonym"] = "Slovenčina"
+Locale["sk"]["translations-of"] = "Preklady výrazu: %s"
+Locale["sk"]["definitions-of"] = "Definície výrazu %s"
+Locale["sk"]["synonyms"] = "Synonymá"
+Locale["sk"]["examples"] = "Príklady"
+Locale["sk"]["see-also"] = "Pozrite tiež"
+Locale["sk"]["family"] = "Indo-European"
+Locale["sk"]["iso"] = "slk"
+Locale["sk"]["glotto"] = "slov1269"
+Locale["sk"]["script"] = "Latn"
+Locale["sl"]["name"] = "Slovenian"
+Locale["sl"]["endonym"] = "Slovenščina"
+Locale["sl"]["translations-of"] = "Prevodi za %s"
+Locale["sl"]["definitions-of"] = "Razlage za %s"
+Locale["sl"]["synonyms"] = "Sopomenke"
+Locale["sl"]["examples"] = "Primeri"
+Locale["sl"]["see-also"] = "Glejte tudi"
+Locale["sl"]["family"] = "Indo-European"
+Locale["sl"]["iso"] = "slv"
+Locale["sl"]["glotto"] = "slov1268"
+Locale["sl"]["script"] = "Latn"
+Locale["so"]["name"] = "Somali"
+Locale["so"]["endonym"] = "Soomaali"
+Locale["so"]["translations-of"] = "Turjumaada %s"
+Locale["so"]["definitions-of"] = "Qeexitaannada %s"
+Locale["so"]["synonyms"] = "La micne ah"
+Locale["so"]["examples"] = "Tusaalooyin"
+Locale["so"]["see-also"] = "Sidoo kale eeg"
+Locale["so"]["family"] = "Afro-Asiatic"
+Locale["so"]["iso"] = "som"
+Locale["so"]["glotto"] = "soma1255"
+Locale["so"]["script"] = "Latn"
+Locale["es"]["name"] = "Spanish"
+Locale["es"]["endonym"] = "Español"
+Locale["es"]["translations-of"] = "Traducciones de %s"
+Locale["es"]["definitions-of"] = "Definiciones de %s"
+Locale["es"]["synonyms"] = "Sinónimos"
+Locale["es"]["examples"] = "Ejemplos"
+Locale["es"]["see-also"] = "Ver también"
+Locale["es"]["family"] = "Indo-European"
+Locale["es"]["iso"] = "spa"
+Locale["es"]["glotto"] = "stan1288"
+Locale["es"]["script"] = "Latn"
+Locale["es"]["dictionary"] = "true"
+Locale["su"]["name"] = "Sundanese"
+Locale["su"]["endonym"] = "Basa Sunda"
+Locale["su"]["translations-of"] = "Tarjamahan tina %s"
+Locale["su"]["definitions-of"] = "Panjelasan tina %s"
+Locale["su"]["synonyms"] = "Sinonim"
+Locale["su"]["examples"] = "Conto"
+Locale["su"]["see-also"] = "Tingali ogé"
+Locale["su"]["family"] = "Austronesian"
+Locale["su"]["iso"] = "sun"
+Locale["su"]["glotto"] = "sund1252"
+Locale["su"]["script"] = "Latn"
+Locale["sw"]["name"] = "Swahili"
+Locale["sw"]["endonym"] = "Kiswahili"
+Locale["sw"]["translations-of"] = "Tafsiri ya %s"
+Locale["sw"]["definitions-of"] = "Ufafanuzi wa %s"
+Locale["sw"]["synonyms"] = "Visawe"
+Locale["sw"]["examples"] = "Mifano"
+Locale["sw"]["see-also"] = "Angalia pia"
+Locale["sw"]["family"] = "Atlantic-Congo"
+Locale["sw"]["iso"] = "swa"
+Locale["sw"]["glotto"] = "swah1253"
+Locale["sw"]["script"] = "Latn"
+Locale["sv"]["name"] = "Swedish"
+Locale["sv"]["endonym"] = "Svenska"
+Locale["sv"]["translations-of"] = "Översättningar av %s"
+Locale["sv"]["definitions-of"] = "Definitioner av %s"
+Locale["sv"]["synonyms"] = "Synonymer"
+Locale["sv"]["examples"] = "Exempel"
+Locale["sv"]["see-also"] = "Se även"
+Locale["sv"]["family"] = "Indo-European"
+Locale["sv"]["iso"] = "swe"
+Locale["sv"]["glotto"] = "swed1254"
+Locale["sv"]["script"] = "Latn"
+Locale["tg"]["name"] = "Tajik"
+Locale["tg"]["endonym"] = "Тоҷикӣ"
+Locale["tg"]["translations-of"] = "Тарҷумаҳои %s"
+Locale["tg"]["definitions-of"] = "Таърифҳои %s"
+Locale["tg"]["synonyms"] = "Муродифҳо"
+Locale["tg"]["examples"] = "Намунаҳо:"
+Locale["tg"]["see-also"] = "Ҳамчунин Бинед"
+Locale["tg"]["family"] = "Indo-European"
+Locale["tg"]["iso"] = "tgk"
+Locale["tg"]["glotto"] = "taji1245"
+Locale["tg"]["script"] = "Cyrl"
+Locale["ta"]["name"] = "Tamil"
+Locale["ta"]["endonym"] = "தமிழ்"
+Locale["ta"]["translations-of"] = "%s இன் மொழிபெயர்ப்புகள்"
+Locale["ta"]["definitions-of"] = "%s இன் வரையறைகள்"
+Locale["ta"]["synonyms"] = "இணைச்சொற்கள்"
+Locale["ta"]["examples"] = "எடுத்துக்காட்டுகள்"
+Locale["ta"]["see-also"] = "இதையும் காண்க"
+Locale["ta"]["family"] = "Dravidian"
+Locale["ta"]["iso"] = "tam"
+Locale["ta"]["glotto"] = "tami1289"
+Locale["ta"]["script"] = "Taml"
+Locale["te"]["name"] = "Telugu"
+Locale["te"]["endonym"] = "తెలుగు"
+Locale["te"]["translations-of"] = "%s యొక్క అనువాదాలు"
+Locale["te"]["definitions-of"] = "%s యొక్క నిర్వచనాలు"
+Locale["te"]["synonyms"] = "పర్యాయపదాలు"
+Locale["te"]["examples"] = "ఉదాహరణలు"
+Locale["te"]["see-also"] = "వీటిని కూడా చూడండి"
+Locale["te"]["family"] = "Dravidian"
+Locale["te"]["iso"] = "tel"
+Locale["te"]["glotto"] = "telu1262"
+Locale["te"]["script"] = "Telu"
+Locale["th"]["name"] = "Thai"
+Locale["th"]["endonym"] = "ไทย"
+Locale["th"]["translations-of"] = "คำแปลของ %s"
+Locale["th"]["definitions-of"] = "คำจำกัดความของ %s"
+Locale["th"]["synonyms"] = "คำพ้องความหมาย"
+Locale["th"]["examples"] = "ตัวอย่าง"
+Locale["th"]["see-also"] = "ดูเพิ่มเติม"
+Locale["th"]["family"] = "Tai-Kadai"
+Locale["th"]["iso"] = "tha"
+Locale["th"]["glotto"] = "thai1261"
+Locale["th"]["script"] = "Thai"
+Locale["tr"]["name"] = "Turkish"
+Locale["tr"]["endonym"] = "Türkçe"
+Locale["tr"]["translations-of"] = "%s çevirileri"
+Locale["tr"]["definitions-of"] = "%s için tanımlar"
+Locale["tr"]["synonyms"] = "Eş anlamlılar"
+Locale["tr"]["examples"] = "Örnekler"
+Locale["tr"]["see-also"] = "Ayrıca bkz."
+Locale["tr"]["family"] = "Turkic"
+Locale["tr"]["iso"] = "tur"
+Locale["tr"]["glotto"] = "nucl1301"
+Locale["tr"]["script"] = "Latn"
+Locale["uk"]["name"] = "Ukrainian"
+Locale["uk"]["endonym"] = "Українська"
+Locale["uk"]["translations-of"] = "Переклади слова або виразу \"%s\""
+Locale["uk"]["definitions-of"] = "\"%s\" – визначення"
+Locale["uk"]["synonyms"] = "Синоніми"
+Locale["uk"]["examples"] = "Приклади"
+Locale["uk"]["see-also"] = "Дивіться також"
+Locale["uk"]["family"] = "Indo-European"
+Locale["uk"]["iso"] = "ukr"
+Locale["uk"]["glotto"] = "ukra1253"
+Locale["uk"]["script"] = "Cyrl"
+Locale["ur"]["name"] = "Urdu"
+Locale["ur"]["endonym"] = "اُردُو"
+Locale["ur"]["translations-of"] = "کے ترجمے %s"
+Locale["ur"]["definitions-of"] = "کی تعریفات %s"
+Locale["ur"]["synonyms"] = "مترادفات"
+Locale["ur"]["examples"] = "مثالیں"
+Locale["ur"]["see-also"] = "نیز دیکھیں"
+Locale["ur"]["family"] = "Indo-European"
+Locale["ur"]["iso"] = "urd"
+Locale["ur"]["glotto"] = "urdu1245"
+Locale["ur"]["script"] = "Arab"
+Locale["ur"]["rtl"] = "true"
+Locale["uz"]["name"] = "Uzbek"
+Locale["uz"]["endonym"] = "Oʻzbek tili"
+Locale["uz"]["translations-of"] = "%s: tarjima variantlari"
+Locale["uz"]["definitions-of"] = "%s – ta’riflar"
+Locale["uz"]["synonyms"] = "Sinonimlar"
+Locale["uz"]["examples"] = "Namunalar"
+Locale["uz"]["see-also"] = "O‘xshash so‘zlar"
+Locale["uz"]["family"] = "Turkic"
+Locale["uz"]["iso"] = "uzb"
+Locale["uz"]["glotto"] = "uzbe1247"
+Locale["uz"]["script"] = "Latn"
+Locale["vi"]["name"] = "Vietnamese"
+Locale["vi"]["endonym"] = "Tiếng Việt"
+Locale["vi"]["translations-of"] = "Bản dịch của %s"
+Locale["vi"]["definitions-of"] = "Nghĩa của %s"
+Locale["vi"]["synonyms"] = "Từ đồng nghĩa"
+Locale["vi"]["examples"] = "Ví dụ"
+Locale["vi"]["see-also"] = "Xem thêm"
+Locale["vi"]["family"] = "Austroasiatic"
+Locale["vi"]["iso"] = "vie"
+Locale["vi"]["glotto"] = "viet1252"
+Locale["vi"]["script"] = "Latn"
+Locale["cy"]["name"] = "Welsh"
+Locale["cy"]["endonym"] = "Cymraeg"
+Locale["cy"]["translations-of"] = "Cyfieithiadau %s"
+Locale["cy"]["definitions-of"] = "Diffiniadau %s"
+Locale["cy"]["synonyms"] = "Cyfystyron"
+Locale["cy"]["examples"] = "Enghreifftiau"
+Locale["cy"]["see-also"] = "Gweler hefyd"
+Locale["cy"]["family"] = "Indo-European"
+Locale["cy"]["iso"] = "cym"
+Locale["cy"]["glotto"] = "wels1247"
+Locale["cy"]["script"] = "Latn"
+Locale["fy"]["name"] = "Frisian"
+Locale["fy"]["endonym"] = "Frysk"
+Locale["fy"]["translations-of"] = "Oersettings fan %s"
+Locale["fy"]["definitions-of"] = "Definysjes fan %s"
+Locale["fy"]["synonyms"] = "Synonimen"
+Locale["fy"]["examples"] = "Foarbylden"
+Locale["fy"]["see-also"] = "Sjoch ek"
+Locale["fy"]["family"] = "Indo-European"
+Locale["fy"]["iso"] = "fry"
+Locale["fy"]["glotto"] = "west2354"
+Locale["fy"]["script"] = "Latn"
+Locale["xh"]["name"] = "Xhosa"
+Locale["xh"]["endonym"] = "isiXhosa"
+Locale["xh"]["translations-of"] = "Iinguqulelo zika-%s"
+Locale["xh"]["definitions-of"] = "Iingcaciso zika-%s"
+Locale["xh"]["synonyms"] = "Izithethantonye"
+Locale["xh"]["examples"] = "Imizekelo"
+Locale["xh"]["see-also"] = "Kwakhona bona"
+Locale["xh"]["family"] = "Atlantic-Congo"
+Locale["xh"]["iso"] = "xho"
+Locale["xh"]["glotto"] = "xhos1239"
+Locale["xh"]["script"] = "Latn"
+Locale["yi"]["name"] = "Yiddish"
+Locale["yi"]["endonym"] = "ייִדיש"
+Locale["yi"]["translations-of"] = "איבערזעצונגען פון %s"
+Locale["yi"]["definitions-of"] = "דפיניציונען %s"
+Locale["yi"]["synonyms"] = "סינאָנימען"
+Locale["yi"]["examples"] = "ביישפילע"
+Locale["yi"]["see-also"] = "זייען אויך"
+Locale["yi"]["family"] = "Indo-European"
+Locale["yi"]["iso"] = "yid"
+Locale["yi"]["glotto"] = "yidd1255"
+Locale["yi"]["script"] = "Hebr"
+Locale["yi"]["rtl"] = "true"
+Locale["yo"]["name"] = "Yoruba"
+Locale["yo"]["endonym"] = "Yorùbá"
+Locale["yo"]["translations-of"] = "Awọn itumọ ti %s"
+Locale["yo"]["definitions-of"] = "Awọn itumọ ti %s"
+Locale["yo"]["synonyms"] = "Awọn ọrọ onitumọ"
+Locale["yo"]["examples"] = "Awọn apẹrẹ"
+Locale["yo"]["see-also"] = "Tun wo"
+Locale["yo"]["family"] = "Atlantic-Congo"
+Locale["yo"]["iso"] = "yor"
+Locale["yo"]["glotto"] = "yoru1245"
+Locale["yo"]["script"] = "Latn"
+Locale["zu"]["name"] = "Zulu"
+Locale["zu"]["endonym"] = "isiZulu"
+Locale["zu"]["translations-of"] = "Ukuhumusha i-%s"
+Locale["zu"]["definitions-of"] = "Izincazelo ze-%s"
+Locale["zu"]["synonyms"] = "Amagama afanayo"
+Locale["zu"]["examples"] = "Izibonelo"
+Locale["zu"]["see-also"] = "Bheka futhi"
+Locale["zu"]["family"] = "Atlantic-Congo"
+Locale["zu"]["iso"] = "zul"
+Locale["zu"]["glotto"] = "zulu1248"
+Locale["zu"]["script"] = "Latn"
+Locale["yue"]["support"] = "bing-only"
+Locale["yue"]["name"] = "Cantonese"
+Locale["yue"]["endonym"] = "粵語"
+Locale["yue"]["family"] = "Sino-Tibetan"
+Locale["yue"]["iso"] = "yue"
+Locale["yue"]["glotto"] = "cant1236"
+Locale["yue"]["script"] = "Hant"
+Locale["fj"]["support"] = "bing-only"
+Locale["fj"]["name"] = "Fijian"
+Locale["fj"]["endonym"] = "Vosa Vakaviti"
+Locale["fj"]["family"] = "Austronesian"
+Locale["fj"]["iso"] = "fij"
+Locale["fj"]["glotto"] = "fiji1243"
+Locale["fj"]["script"] = "Latn"
+Locale["mww"]["support"] = "bing-only"
+Locale["mww"]["name"] = "Hmong Daw"
+Locale["mww"]["endonym"] = "Hmoob Daw"
+Locale["mww"]["family"] = "Hmong-Mien"
+Locale["mww"]["iso"] = "mww"
+Locale["mww"]["glotto"] = "hmon1333"
+Locale["mww"]["script"] = "Latn"
+Locale["otq"]["support"] = "bing-only"
+Locale["otq"]["name"] = "Querétaro Otomi"
+Locale["otq"]["endonym"] = "Hñąñho"
+Locale["otq"]["family"] = "Oto-Manguean"
+Locale["otq"]["iso"] = "otq"
+Locale["otq"]["glotto"] = "quer1236"
+Locale["otq"]["script"] = "Latn"
+Locale["ty"]["support"] = "bing-only"
+Locale["ty"]["name"] = "Tahitian"
+Locale["ty"]["endonym"] = "Reo Tahiti"
+Locale["ty"]["family"] = "Austronesian"
+Locale["ty"]["iso"] = "tah"
+Locale["ty"]["glotto"] = "tahi1242"
+Locale["ty"]["script"] = "Latn"
+Locale["to"]["support"] = "bing-only"
+Locale["to"]["name"] = "Tongan"
+Locale["to"]["endonym"] = "Lea faka-Tonga"
+Locale["to"]["family"] = "Austronesian"
+Locale["to"]["iso"] = "ton"
+Locale["to"]["glotto"] = "tong1325"
+Locale["to"]["script"] = "Latn"
+Locale["yua"]["support"] = "bing-only"
+Locale["yua"]["name"] = "Yucatec Maya"
+Locale["yua"]["endonym"] = "Màaya T'àan"
+Locale["yua"]["family"] = "Mayan"
+Locale["yua"]["iso"] = "yua"
+Locale["yua"]["glotto"] = "yuca1254"
+Locale["yua"]["script"] = "Latn"
+Locale["tlh"]["support"] = "bing-only"
+Locale["tlh"]["name"] = "Klingon"
+Locale["tlh"]["endonym"] = "tlhIngan Hol"
+Locale["tlh"]["family"] = "Artificial Language"
+Locale["tlh"]["iso"] = "tlh"
+Locale["tlh"]["script"] = "Latn"
+Locale["tlh-Qaak"]["support"] = "bing-only"
+Locale["tlh-Qaak"]["name"] = "Klingon (pIqaD)"
+Locale["tlh-Qaak"]["endonym"] = " "
+Locale["tlh-Qaak"]["family"] = "Artificial Language"
+Locale["tlh-Qaak"]["iso"] = "tlh"
+Locale["tlh-Qaak"]["script"] = "Piqd"
+Locale["as"]["support"] = "unstable"
+Locale["as"]["name"] = "Assamese"
+Locale["as"]["endonym"] = "অসমীয়া"
+Locale["as"]["family"] = "Indo-European"
+Locale["as"]["iso"] = "asm"
+Locale["as"]["glotto"] = "assa1263"
+Locale["as"]["script"] = "Beng"
+Locale["ba"]["support"] = "yandex-only"
+Locale["ba"]["name"] = "Bashkir"
+Locale["ba"]["endonym"] = "башҡорт теле"
+Locale["ba"]["family"] = "Turkic"
+Locale["ba"]["iso"] = "bak"
+Locale["ba"]["glotto"] = "bash1264"
+Locale["ba"]["script"] = "Cyrl"
+Locale["br"]["support"] = "unstable"
+Locale["br"]["name"] = "Breton"
+Locale["br"]["endonym"] = "Brezhoneg"
+Locale["br"]["family"] = "Indo-European"
+Locale["br"]["iso"] = "bre"
+Locale["br"]["glotto"] = "bret1244"
+Locale["br"]["script"] = "Latn"
+Locale["dz"]["support"] = "unstable"
+Locale["dz"]["name"] = "Dzongkha"
+Locale["dz"]["endonym"] = "རྫོང་ཁ"
+Locale["dz"]["family"] = "Sino-Tibetan"
+Locale["dz"]["iso"] = "dzo"
+Locale["dz"]["glotto"] = "nucl1307"
+Locale["dz"]["script"] = "Tibt"
+Locale["mhr"]["support"] = "yandex-only"
+Locale["mhr"]["name"] = "Eastern Mari"
+Locale["mhr"]["endonym"] = "Олык марий"
+Locale["mhr"]["family"] = "Uralic"
+Locale["mhr"]["iso"] = "mhr"
+Locale["mhr"]["glotto"] = "east2328"
+Locale["mhr"]["script"] = "Cyrl"
+Locale["fo"]["support"] = "unstable"
+Locale["fo"]["name"] = "Faroese"
+Locale["fo"]["endonym"] = "Føroyskt"
+Locale["fo"]["family"] = "Indo-European"
+Locale["fo"]["iso"] = "fao"
+Locale["fo"]["glotto"] = "faro1244"
+Locale["fo"]["script"] = "Latn"
+Locale["gn"]["support"] = "unstable"
+Locale["gn"]["name"] = "Guarani"
+Locale["gn"]["endonym"] = "Avañe'ẽ"
+Locale["gn"]["family"] = "Tupian"
+Locale["gn"]["iso"] = "grn"
+Locale["gn"]["glotto"] = "para1311"
+Locale["gn"]["script"] = "Latn"
+Locale["mrj"]["support"] = "yandex-only"
+Locale["mrj"]["name"] = "Hill Mari"
+Locale["mrj"]["endonym"] = "Кырык мары"
+Locale["mrj"]["family"] = "Uralic"
+Locale["mrj"]["iso"] = "mrj"
+Locale["mrj"]["glotto"] = "west2392"
+Locale["mrj"]["script"] = "Cyrl"
+Locale["ie"]["support"] = "unstable"
+Locale["ie"]["name"] = "Interlingue"
+Locale["ie"]["endonym"] = "Interlingue"
+Locale["ie"]["family"] = "Artificial Language"
+Locale["ie"]["iso"] = "ile"
+Locale["ie"]["glotto"] = "occi1241"
+Locale["ie"]["script"] = "Latn"
+Locale["rw"]["support"] = "unstable"
+Locale["rw"]["name"] = "Kinyarwanda"
+Locale["rw"]["endonym"] = "Ikinyarwanda"
+Locale["rw"]["family"] = "Atlantic-Congo"
+Locale["rw"]["iso"] = "kin"
+Locale["rw"]["glotto"] = "kiny1244"
+Locale["rw"]["script"] = "Latn"
+Locale["oc"]["support"] = "unstable"
+Locale["oc"]["name"] = "Occitan"
+Locale["oc"]["endonym"] = "Occitan"
+Locale["oc"]["family"] = "Indo-European"
+Locale["oc"]["iso"] = "oci"
+Locale["oc"]["glotto"] = "occi1239"
+Locale["oc"]["script"] = "Latn"
+Locale["om"]["support"] = "unstable"
+Locale["om"]["name"] = "Oromo"
+Locale["om"]["endonym"] = "Afaan Oromoo"
+Locale["om"]["family"] = "Afro-Asiatic"
+Locale["om"]["iso"] = "orm"
+Locale["om"]["glotto"] = "nucl1736"
+Locale["om"]["script"] = "Latn"
+Locale["or"]["support"] = "unstable"
+Locale["or"]["name"] = "Oriya"
+Locale["or"]["endonym"] = "ଓଡ଼ିଆ"
+Locale["or"]["family"] = "Indo-European"
+Locale["or"]["iso"] = "ori"
+Locale["or"]["glotto"] = "macr1269"
+Locale["or"]["script"] = "Orya"
+Locale["pap"]["support"] = "yandex-only"
+Locale["pap"]["name"] = "Papiamento"
+Locale["pap"]["endonym"] = "Papiamentu"
+Locale["pap"]["family"] = "Indo-European"
+Locale["pap"]["iso"] = "pap"
+Locale["pap"]["glotto"] = "papi1253"
+Locale["pap"]["script"] = "Latn"
+Locale["rm"]["support"] = "unstable"
+Locale["rm"]["name"] = "Romansh"
+Locale["rm"]["endonym"] = "Rumantsch"
+Locale["rm"]["family"] = "Indo-European"
+Locale["rm"]["iso"] = "roh"
+Locale["rm"]["glotto"] = "roma1326"
+Locale["rm"]["script"] = "Latn"
+Locale["ti"]["support"] = "unstable"
+Locale["ti"]["name"] = "Tigrinya"
+Locale["ti"]["endonym"] = "ትግርኛ"
+Locale["ti"]["family"] = "Afro-Asiatic"
+Locale["ti"]["iso"] = "tir"
+Locale["ti"]["glotto"] = "tigr1271"
+Locale["ti"]["script"] = "Ethi"
+Locale["bo"]["support"] = "unstable"
+Locale["bo"]["name"] = "Tibetan"
+Locale["bo"]["endonym"] = "བོད་ཡིག"
+Locale["bo"]["family"] = "Sino-Tibetan"
+Locale["bo"]["iso"] = "bod"
+Locale["bo"]["glotto"] = "tibe1272"
+Locale["bo"]["script"] = "Tibt"
+Locale["tk"]["support"] = "unstable"
+Locale["tk"]["name"] = "Turkmen"
+Locale["tk"]["endonym"] = "Türkmen"
+Locale["tk"]["family"] = "Turkic"
+Locale["tk"]["iso"] = "tuk"
+Locale["tk"]["glotto"] = "turk1304"
+Locale["tk"]["script"] = "Latn"
+Locale["tt"]["support"] = "yandex-only"
+Locale["tt"]["name"] = "Tatar"
+Locale["tt"]["endonym"] = "татарча"
+Locale["tt"]["family"] = "Turkic"
+Locale["tt"]["iso"] = "tat"
+Locale["tt"]["glotto"] = "tata1255"
+Locale["tt"]["script"] = "Cyrl"
+Locale["udm"]["support"] = "yandex-only"
+Locale["udm"]["name"] = "Udmurt"
+Locale["udm"]["endonym"] = "удмурт"
+Locale["udm"]["family"] = "Uralic"
+Locale["udm"]["iso"] = "udm"
+Locale["udm"]["glotto"] = "udmu1245"
+Locale["udm"]["script"] = "Cyrl"
+Locale["ug"]["support"] = "unstable"
+Locale["ug"]["name"] = "Uyghur"
+Locale["ug"]["endonym"] = "ئۇيغۇر تىلى"
+Locale["ug"]["family"] = "Turkic"
+Locale["ug"]["iso"] = "uig"
+Locale["ug"]["glotto"] = "uigh1240"
+Locale["ug"]["script"] = "Arab"
+Locale["ug"]["rtl"] = "true"
+Locale["vo"]["support"] = "unstable"
+Locale["vo"]["name"] = "Volapük"
+Locale["vo"]["endonym"] = "Volapük"
+Locale["vo"]["family"] = "Artificial Language"
+Locale["vo"]["iso"] = "vol"
+Locale["vo"]["script"] = "Latn"
+Locale["wo"]["support"] = "unstable"
+Locale["wo"]["name"] = "Wolof"
+Locale["wo"]["endonym"] = "Wollof"
+Locale["wo"]["family"] = "Atlantic-Congo"
+Locale["wo"]["iso"] = "wol"
+Locale["wo"]["glotto"] = "wolo1247"
+Locale["wo"]["script"] = "Latn"
+Locale["chr"]["support"] = "unstable"
+Locale["chr"]["name"] = "Cherokee"
+Locale["chr"]["endonym"] = "ᏣᎳᎩ"
+Locale["chr"]["family"] = "Iroquoian"
+Locale["chr"]["iso"] = "chr"
+Locale["chr"]["glotto"] = "cher1273"
+Locale["chr"]["script"] = "Cher"
+Locale["emj"]["support"] = "yandex-only"
+Locale["emj"]["name"] = "Emoji"
+Locale["emj"]["endonym"] = "Emoji"
+}
+function initLocaleAlias( i) {
+for (i in Locale) {
+LocaleAlias[Locale[i]["iso"]] = i
+LocaleAlias[tolower(Locale[i]["name"])] = i
+LocaleAlias[tolower(Locale[i]["endonym"])] = i
+}
+LocaleAlias["in"] = "id"
+LocaleAlias["iw"] = "he"
+LocaleAlias["ji"] = "yi"
+LocaleAlias["jw"] = "jv"
+LocaleAlias["mo"] = "ro"
+LocaleAlias["nb"] = "no"
+LocaleAlias["nn"] = "no"
+LocaleAlias["sh"] = "sr-Cyrl"
+LocaleAlias["sr"] = "sr-Cyrl"
+LocaleAlias["srp"] = "sr-Cyrl"
+LocaleAlias["serbian"] = "sr-Cyrl"
+LocaleAlias["zh"] = "zh-CN"
+LocaleAlias["zh-CHS"] = "zh-CN"
+LocaleAlias["zh-CHT"] = "zh-TW"
+LocaleAlias["zh-Hans"] = "zh-CN"
+LocaleAlias["zh-Hant"] = "zh-TW"
+LocaleAlias["zho"] = "zh-CN"
+LocaleAlias["chinese"] = "zh-CN"
+LocaleAlias["tlh-Latn"] = "tlh"
+LocaleAlias["tlh-Piqd"] = "tlh-Qaak"
+}
+function initLocaleDisplay( i) {
+for (i in Locale) {
+Locale[i]["display"] = show(Locale[i]["endonym"], i)
+}
+}
+function getCode(code, group) {
+if (code == "auto" || code in Locale)
+return code
+else if (code in LocaleAlias)
+return LocaleAlias[code]
+else if (tolower(code) in LocaleAlias)
+return LocaleAlias[tolower(code)]
+match(code, /^([[:alpha:]][[:alpha:]][[:alpha:]]?)-(.*)$/, group)
+if (group[1])
+return group[1]
+return
+}
+function getName(code) {
+return Locale[getCode(code)]["name"]
+}
+function getEndonym(code) {
+return Locale[getCode(code)]["endonym"]
+}
+function getDisplay(code) {
+return Locale[getCode(code)]["display"]
+}
+function showTranslationsOf(code, text, fmt) {
+fmt = Locale[getCode(code)]["translations-of"]
+if (!fmt) fmt = Locale["en"]["translations-of"]
+return sprintf(fmt, text)
+}
+function showDefinitionsOf(code, text, fmt) {
+fmt = Locale[getCode(code)]["definitions-of"]
+if (!fmt) fmt = Locale["en"]["definitions-of"]
+return sprintf(fmt, text)
+}
+function showSynonyms(code, tmp) {
+tmp = Locale[getCode(code)]["synonyms"]
+if (!tmp) tmp = Locale["en"]["synonyms"]
+return tmp
+}
+function showExamples(code, tmp) {
+tmp = Locale[getCode(code)]["examples"]
+if (!tmp) tmp = Locale["en"]["examples"]
+return tmp
+}
+function showSeeAlso(code, tmp) {
+tmp = Locale[getCode(code)]["see-also"]
+if (!tmp) tmp = Locale["en"]["see-also"]
+return tmp
+}
+function getFamily(code) {
+return Locale[getCode(code)]["family"]
+}
+function getISO(code) {
+return Locale[getCode(code)]["iso"]
+}
+function getGlotto(code) {
+return Locale[getCode(code)]["glotto"]
+}
+function getScript(code) {
+return Locale[getCode(code)]["script"]
+}
+function isRTL(code) {
+return Locale[getCode(code)]["rtl"] ? 1 : 0
+}
+function hasDictionary(code) {
+return Locale[getCode(code)]["dictionary"] ? 1 : 0
+}
+function compName(i1, v1, i2, v2) {
+if (getName(i1) < getName(i2))
+return -1
+else
+return (getName(i1) != getName(i2))
+}
+function scriptName(code) {
+switch (code) {
+case "Arab": return "Arabic"
+case "Armn": return "Armenian"
+case "Beng": return "Bengali"
+case "Cher": return "Cherokee"
+case "Cyrl": return "Cyrillic"
+case "Deva": return "Devanagari"
+case "Ethi": return "Ethiopic (Geʻez)"
+case "Geor": return "Georgian (Mkhedruli)"
+case "Grek": return "Greek"
+case "Gujr": return "Gujarati"
+case "Guru": return "Gurmukhi"
+case "Hani": return "Han"
+case "Hans": return "Han (Simplified)"
+case "Hant": return "Han (Traditional)"
+case "Hebr": return "Hebrew"
+case "Jpan": return "Japanese (Han + Hiragana + Katakana)"
+case "Khmr": return "Khmer"
+case "Knda": return "Kannada"
+case "Kore": return "Korean (Hangul + Han)"
+case "Laoo": return "Lao"
+case "Latn": return "Latin"
+case "Mlym": return "Malayalam"
+case "Mymr": return "Myanmar"
+case "Orya": return "Oriya"
+case "Piqd": return "Klingon (pIqaD)"
+case "Sinh": return "Sinhala"
+case "Taml": return "Tamil"
+case "Telu": return "Telugu"
+case "Thai": return "Thai"
+case "Tibt": return "Tibetan"
+default: return "Unknown"
+}
+}
+function getDetails(code, group, iso, language, script) {
+if (code == "auto" || !getCode(code)) {
+e("[ERROR] Language not found: " code "\n"\
+" Run '-reference / -R' to see a list of available languages.")
+exit 1
+}
+script = scriptName(getScript(code))
+if (isRTL(code)) script = script " (R-to-L)"
+split(getISO(code), group, "-")
+iso = group[1]
+split(getName(code), group, " ")
+language = length(group) == 1 ? group[1] "_language" :
+group[2] ~ /^\(.*\)$/ ? group[1] "_language" : join(group, "_")
+return ansi("bold", sprintf("%s\n", getDisplay(code)))\
+sprintf("%-22s%s\n", "Name", ansi("bold", getName(code)))\
+sprintf("%-22s%s\n", "Family", ansi("bold", getFamily(code)))\
+sprintf("%-22s%s\n", "Writing system", ansi("bold", script))\
+sprintf("%-22s%s\n", "Code", ansi("bold", getCode(code)))\
+sprintf("%-22s%s\n", "ISO 639-3", ansi("bold", iso))\
+sprintf("%-22s%s\n", "SIL", ansi("bold", "http://www-01.sil.org/iso639-3/documentation.asp?id=" iso))\
+sprintf("%-22s%s\n", "Glottolog", getGlotto(code) ?
+ansi("bold", "http://glottolog.org/resource/languoid/id/" getGlotto(code)) : "")\
+sprintf("%-22s%s", "Wikipedia", ansi("bold", "http://en.wikipedia.org/wiki/" language))
+}
+function showPhonetics(phonetics, code) {
+if (code && getCode(code) == "en")
+return "/" phonetics "/"
+else
+return "(" phonetics ")"
+}
+function show(text, code, command, temp) {
+if (!code || isRTL(code)) {
+if (Cache[text][0])
+return Cache[text][0]
+else {
+if ((FriBidi || (code && isRTL(code))) && BiDiNoPad) {
+command = "echo " parameterize(text) PIPE BiDiNoPad
+command | getline temp
+close(command)
+} else
+temp = text
+return Cache[text][0] = temp
+}
+} else
+return text
+}
+function s(text, code, width, command, temp) {
+if (!code || isRTL(code)) {
+if (!width) width = Option["width"]
+if (Cache[text][width])
+return Cache[text][width]
+else {
+if ((FriBidi || (code && isRTL(code))) && BiDi) {
+command = "echo " parameterize(text) PIPE sprintf(BiDi, width)
+command | getline temp
+close(command)
+} else
+temp = text
+return Cache[text][width] = temp
+}
+} else
+return text
+}
+function ins(level, text, code, width, i, temp) {
+if (code && isRTL(code)) {
+if (!width) width = Option["width"]
+return s(text, code, width - Option["indent"] * level)
+} else
+return replicate(" ", Option["indent"] * level) text
+}
+function parseLang(lang, code, group) {
+match(lang, /^([a-z][a-z][a-z]?)(_|$)/, group)
+code = getCode(group[1])
+if (lang ~ /^zh_(CN|SG)/) code = "zh-CN"
+else if (lang ~ /^zh_(TW|HK)/) code = "zh-TW"
+if (!code) code = "en"
+return code
+}
+function initUserLang( lang, utf) {
+if (lang = ENVIRON["LC_ALL"]) {
+if (!UserLocale) UserLocale = lang
+utf = utf || tolower(lang) ~ /utf-?8$/
+}
+if (lang = ENVIRON["LANG"]) {
+if (!UserLocale) UserLocale = lang
+utf = utf || tolower(lang) ~ /utf-?8$/
+}
+if (!UserLocale) {
+UserLocale = "en_US.UTF-8"
+utf = 1
+}
+if (!utf)
+w("[WARNING] Your locale codeset (" UserLocale ") is not UTF-8.")
+UserLang = parseLang(UserLocale)
+}
+function getVersion( build, gitHead) {
+initAudioPlayer()
+initPager()
+Platform = Platform ? Platform : detectProgram("uname", "-s", 1)
+if (ENVIRON["TRANS_BUILD"])
+build = "-" ENVIRON["TRANS_BUILD"]
+else {
+gitHead = getGitHead()
+build = gitHead ? "-git:" gitHead : ""
+}
+return ansi("bold", sprintf("%-22s%s%s\n\n", Name, Version, build))\
+sprintf("%-22s%s\n", "platform", Platform)\
+sprintf("%-22s%s\n", "terminal type", ENVIRON["TERM"])\
+sprintf("%-22s%s\n", "bi-di emulator", BiDiTerm ? BiDiTerm :
+"[N/A]")\
+sprintf("%-22s%s\n", "gawk (GNU Awk)", PROCINFO["version"])\
+sprintf("%s\n", FriBidi ? FriBidi :
+"fribidi (GNU FriBidi) [NOT INSTALLED]")\
+sprintf("%-22s%s\n", "audio player", AudioPlayer ? AudioPlayer :
+"[NOT INSTALLED]")\
+sprintf("%-22s%s\n", "terminal pager", Pager ? Pager :
+"[NOT INSTALLED]")\
+sprintf("%-22s%s\n", "web browser", Option["browser"] != NONE ?
+Option["browser"] :"[NONE]")\
+sprintf("%-22s%s (%s)\n", "user locale", UserLocale, getName(UserLang))\
+sprintf("%-22s%s\n", "home language", Option["hl"])\
+sprintf("%-22s%s\n", "source language", join(Option["sls"], "+"))\
+sprintf("%-22s%s\n", "target language", join(Option["tl"], "+"))\
+sprintf("%-22s%s\n", "translation engine", Option["engine"])\
+sprintf("%-22s%s\n", "proxy", Option["proxy"] ? Option["proxy"] :
+"[NONE]")\
+sprintf("%-22s%s\n", "user-agent", Option["user-agent"] ? Option["user-agent"] :
+"[NONE]")\
+sprintf("%-22s%s\n", "ip version", Option["ip-version"] ? Option["ip-version"] :
+"[DEFAULT]")\
+sprintf("%-22s%s\n", "theme", Option["theme"])\
+sprintf("%-22s%s\n", "init file", InitScript ? InitScript : "[NONE]")\
+sprintf("\n%-22s%s", "Report bugs to:", "https://github.com/soimort/translate-shell/issues")
+}
+function getHelp() {
+return "Usage: " ansi("bold", Command)\
+" [" ansi("underline", "OPTIONS") "]"\
+" [" ansi("underline", "SOURCES") "]"\
+":[" ansi("underline", "TARGETS") "]"\
+" [" ansi("underline", "TEXT") "]..." RS\
+RS "Information options:" RS\
+ins(1, ansi("bold", "-V") ", " ansi("bold", "-version")) RS\
+ins(2, "Print version and exit.") RS\
+ins(1, ansi("bold", "-H") ", " ansi("bold", "-help")) RS\
+ins(2, "Print help message and exit.") RS\
+ins(1, ansi("bold", "-M") ", " ansi("bold", "-man")) RS\
+ins(2, "Show man page and exit.") RS\
+ins(1, ansi("bold", "-T") ", " ansi("bold", "-reference")) RS\
+ins(2, "Print reference table of languages and exit.") RS\
+ins(1, ansi("bold", "-R") ", " ansi("bold", "-reference-english")) RS\
+ins(2, "Print reference table of languages (in English names) and exit.") RS\
+ins(1, ansi("bold", "-L ") ansi("underline", "CODES")\
+", " ansi("bold", "-list ") ansi("underline", "CODES")) RS\
+ins(2, "Print details of languages and exit.") RS\
+ins(1, ansi("bold", "-S") ", " ansi("bold", "-list-engines")) RS\
+ins(2, "List available translation engines and exit.") RS\
+ins(1, ansi("bold", "-U") ", " ansi("bold", "-upgrade")) RS\
+ins(2, "Check for upgrade of this program.") RS\
+RS "Translator options:" RS\
+ins(1, ansi("bold", "-e ") ansi("underline", "ENGINE")\
+", " ansi("bold", "-engine ") ansi("underline", "ENGINE")) RS\
+ins(2, "Specify the translation engine to use.") RS\
+RS "Display options:" RS\
+ins(1, ansi("bold", "-verbose")) RS\
+ins(2, "Verbose mode. (default)") RS\
+ins(1, ansi("bold", "-b") ", " ansi("bold", "-brief")) RS\
+ins(2, "Brief mode.") RS\
+ins(1, ansi("bold", "-d") ", " ansi("bold", "-dictionary")) RS\
+ins(2, "Dictionary mode.") RS\
+ins(1, ansi("bold", "-identify")) RS\
+ins(2, "Language identification.") RS\
+ins(1, ansi("bold", "-show-original ") ansi("underline", "Y/n")) RS\
+ins(2, "Show original text or not.") RS\
+ins(1, ansi("bold", "-show-original-phonetics ") ansi("underline", "Y/n")) RS\
+ins(2, "Show phonetic notation of original text or not.") RS\
+ins(1, ansi("bold", "-show-translation ") ansi("underline", "Y/n")) RS\
+ins(2, "Show translation or not.") RS\
+ins(1, ansi("bold", "-show-translation-phonetics ") ansi("underline", "Y/n")) RS\
+ins(2, "Show phonetic notation of translation or not.") RS\
+ins(1, ansi("bold", "-show-prompt-message ") ansi("underline", "Y/n")) RS\
+ins(2, "Show prompt message or not.") RS\
+ins(1, ansi("bold", "-show-languages ") ansi("underline", "Y/n")) RS\
+ins(2, "Show source and target languages or not.") RS\
+ins(1, ansi("bold", "-show-original-dictionary ") ansi("underline", "y/N")) RS\
+ins(2, "Show dictionary entry of original text or not.") RS\
+ins(1, ansi("bold", "-show-dictionary ") ansi("underline", "Y/n")) RS\
+ins(2, "Show dictionary entry of translation or not.") RS\
+ins(1, ansi("bold", "-show-alternatives ") ansi("underline", "Y/n")) RS\
+ins(2, "Show alternative translations or not.") RS\
+ins(1, ansi("bold", "-w ") ansi("underline", "NUM")\
+", " ansi("bold", "-width ") ansi("underline", "NUM")) RS\
+ins(2, "Specify the screen width for padding.") RS\
+ins(1, ansi("bold", "-indent ") ansi("underline", "NUM")) RS\
+ins(2, "Specify the size of indent (number of spaces).") RS\
+ins(1, ansi("bold", "-theme ") ansi("underline", "FILENAME")) RS\
+ins(2, "Specify the theme to use.") RS\
+ins(1, ansi("bold", "-no-theme")) RS\
+ins(2, "Do not use any other theme than default.") RS\
+ins(1, ansi("bold", "-no-ansi")) RS\
+ins(2, "Do not use ANSI escape codes.") RS\
+ins(1, ansi("bold", "-no-autocorrect")) RS\
+ins(2, "Do not autocorrect. (if defaulted by the translation engine)") RS\
+ins(1, ansi("bold", "-no-bidi")) RS\
+ins(2, "Do not convert bidirectional texts.") RS\
+ins(1, ansi("bold", "-bidi")) RS\
+ins(2, "Always convert bidirectional texts.") RS\
+ins(1, ansi("bold", "-no-warn")) RS\
+ins(2, "Do not write warning messages to stderr.") RS\
+ins(1, ansi("bold", "-dump")) RS\
+ins(2, "Print raw API response instead.") RS\
+RS "Audio options:" RS\
+ins(1, ansi("bold", "-p, -play")) RS\
+ins(2, "Listen to the translation.") RS\
+ins(1, ansi("bold", "-speak")) RS\
+ins(2, "Listen to the original text.") RS\
+ins(1, ansi("bold", "-n ") ansi("underline", "VOICE")\
+", " ansi("bold", "-narrator ") ansi("underline", "VOICE")) RS\
+ins(2, "Specify the narrator, and listen to the translation.") RS\
+ins(1, ansi("bold", "-player ") ansi("underline", "PROGRAM")) RS\
+ins(2, "Specify the audio player to use, and listen to the translation.") RS\
+ins(1, ansi("bold", "-no-play")) RS\
+ins(2, "Do not listen to the translation.") RS\
+ins(1, ansi("bold", "-no-translate")) RS\
+ins(2, "Do not translate anything when using -speak.") RS\
+ins(1, ansi("bold", "-download-audio")) RS\
+ins(2, "Download the audio to the current directory.") RS\
+ins(1, ansi("bold", "-download-audio-as ") ansi("underline", "FILENAME")) RS\
+ins(2, "Download the audio to the specified file.") RS\
+RS "Terminal paging and browsing options:" RS\
+ins(1, ansi("bold", "-v") ", " ansi("bold", "-view")) RS\
+ins(2, "View the translation in a terminal pager.") RS\
+ins(1, ansi("bold", "-pager ") ansi("underline", "PROGRAM")) RS\
+ins(2, "Specify the terminal pager to use, and view the translation.") RS\
+ins(1, ansi("bold", "-no-view") ", " ansi("bold", "-no-pager")) RS\
+ins(2, "Do not view the translation in a terminal pager.") RS\
+ins(1, ansi("bold", "-browser ") ansi("underline", "PROGRAM")) RS\
+ins(2, "Specify the web browser to use.") RS\
+ins(1, ansi("bold", "-no-browser")) RS\
+ins(2, "Do not open the web browser.") RS\
+RS "Networking options:" RS\
+ins(1, ansi("bold", "-x ") ansi("underline", "HOST:PORT")\
+", " ansi("bold", "-proxy ") ansi("underline", "HOST:PORT")) RS\
+ins(2, "Use HTTP proxy on given port.") RS\
+ins(1, ansi("bold", "-u ") ansi("underline", "STRING")\
+", " ansi("bold", "-user-agent ") ansi("underline", "STRING")) RS\
+ins(2, "Specify the User-Agent to identify as.") RS\
+ins(1, ansi("bold", "-4") ", " ansi("bold", "-ipv4")\
+", " ansi("bold", "-inet4-only")) RS\
+ins(2, "Connect only to IPv4 addresses.") RS\
+ins(1, ansi("bold", "-6") ", " ansi("bold", "-ipv6")\
+", " ansi("bold", "-inet6-only")) RS\
+ins(2, "Connect only to IPv6 addresses.") RS\
+RS "Interactive shell options:" RS\
+ins(1, ansi("bold", "-I") ", " ansi("bold", "-interactive") ", " ansi("bold", "-shell")) RS\
+ins(2, "Start an interactive shell.") RS\
+ins(1, ansi("bold", "-E") ", " ansi("bold", "-emacs")) RS\
+ins(2, "Start the GNU Emacs front-end for an interactive shell.") RS\
+ins(1, ansi("bold", "-no-rlwrap")) RS\
+ins(2, "Do not invoke rlwrap when starting an interactive shell.") RS\
+RS "I/O options:" RS\
+ins(1, ansi("bold", "-i ") ansi("underline", "FILENAME")\
+", " ansi("bold", "-input ") ansi("underline", "FILENAME")) RS\
+ins(2, "Specify the input file.") RS\
+ins(1, ansi("bold", "-o ") ansi("underline", "FILENAME")\
+", " ansi("bold", "-output ") ansi("underline", "FILENAME")) RS\
+ins(2, "Specify the output file.") RS\
+RS "Language preference options:" RS\
+ins(1, ansi("bold", "-l ") ansi("underline", "CODE")\
+", " ansi("bold", "-hl ") ansi("underline", "CODE")\
+", " ansi("bold", "-lang ") ansi("underline", "CODE")) RS\
+ins(2, "Specify your home language.") RS\
+ins(1, ansi("bold", "-s ") ansi("underline", "CODES")\
+", " ansi("bold", "-sl ") ansi("underline", "CODES")\
+", " ansi("bold", "-source ") ansi("underline", "CODES")\
+", " ansi("bold", "-from ") ansi("underline", "CODES")) RS\
+ins(2, "Specify the source language(s), joined by '+'.") RS\
+ins(1, ansi("bold", "-t ") ansi("underline", "CODES")\
+", " ansi("bold", "-tl ") ansi("underline", "CODES")\
+", " ansi("bold", "-target ") ansi("underline", "CODES")\
+", " ansi("bold", "-to ") ansi("underline", "CODES")) RS\
+ins(2, "Specify the target language(s), joined by '+'.") RS\
+RS "Text preprocessing options:" RS\
+ins(1, ansi("bold", "-j") ", " ansi("bold", "-join-sentence")) RS\
+ins(2, "Treat all arguments as one single sentence.") RS\
+RS "Other options:" RS\
+ins(1, ansi("bold", "-no-init")) RS\
+ins(2, "Do not load any initialization script.") RS\
+RS "See the man page " Command "(1) for more information."
+}
+function showMan( temp) {
+if (ENVIRON["TRANS_MANPAGE"]) {
+initPager()
+Groff = detectProgram("groff", "--version")
+if (Pager && Groff) {
+temp = "echo -E \"${TRANS_MANPAGE}\""
+temp = temp PIPE\
+Groff " -Wall -mtty-char -mandoc -Tutf8 "\
+"-rLL=" Option["width"] "n -rLT=" Option["width"] "n"
+switch (Pager) {
+case "less":
+temp = temp PIPE\
+Pager " -s -P\"\\ \\Manual page " Command "(1) line %lt (press h for help or q to quit)\""
+break
+case "most":
+temp = temp PIPE Pager " -Cs"
+break
+default:
+temp = temp PIPE Pager
+}
+system(temp)
+return
+}
+}
+if (fileExists(ENVIRON["TRANS_DIR"] "/man/" Command ".1"))
+system("man " parameterize(ENVIRON["TRANS_DIR"] "/man/" Command ".1") SUPERR)
+else if (system("man " Command SUPERR))
+print getHelp()
+}
+function getReference(displayName,
+code, col, cols, i, j, name, num, r, rows, saveSortedIn,
+t1, t2) {
+num = 0
+for (code in Locale)
+if (Locale[code]["support"] != "unstable")
+num++
+rows = int(num / 3) + (num % 3 ? 1 : 0)
+cols[0][0] = cols[1][0] = cols[2][0] = NULLSTR
+i = 0
+saveSortedIn = PROCINFO["sorted_in"]
+PROCINFO["sorted_in"] = displayName == "endonym" ? "@ind_num_asc" :
+"compName"
+for (code in Locale) {
+if (Locale[code]["support"] != "unstable") {
+col = int(i / rows)
+append(cols[col], code)
+i++
+}
+}
+PROCINFO["sorted_in"] = saveSortedIn
+if (displayName == "endonym") {
+r = "┌" replicate("─", 23) "┬" replicate("─", 23) "┬" replicate("─", 23) "┐" RS
+for (i = 0; i < rows; i++) {
+r = r "│"
+for (j = 0; j < 3; j++) {
+if (cols[j][i]) {
+t1 = getDisplay(cols[j][i])
+switch (cols[j][i]) {
+case "he":
+t1 = sprintf(" %-18s", t1)
+break
+case "ur":
+t1 = sprintf(" %-17s", t1)
+break
+case "hi": case "gu": case "km": case "kn":
+case "my": case "ne": case "pa": case "si":
+case "ta": case "te": case "yi":
+t1 = sprintf(" %-16s", t1)
+break
+case "yue":
+t1 = sprintf(" %-13s", t1)
+break
+case "ja": case "ko":
+t1 = sprintf(" %-12s", t1)
+break
+case "zh-CN": case "zh-TW":
+t1 = sprintf(" %-11s", t1)
+break
+default:
+if (length(t1) <= 15)
+t1 = sprintf(" %-15s", t1)
+}
+switch (length(cols[j][i])) {
+case 1: case 2: case 3: case 4:
+t2 = sprintf("- %s │", ansi("bold", sprintf("%4s", cols[j][i])))
+break
+case 5:
+t2 = sprintf("- %s│", ansi("bold", cols[j][i]))
+break
+case 6:
+t2 = sprintf("-%s│", ansi("bold", cols[j][i]))
+break
+case 7:
+t2 = sprintf("-%s", ansi("bold", cols[j][i]))
+break
+default:
+t2 = ansi("bold", cols[j][i])
+}
+r = r t1 t2
+} else
+r = r sprintf("%23s│", NULLSTR)
+}
+r = r RS
+}
+r = r "└" replicate("─", 23) "┴" replicate("─", 23) "┴" replicate("─", 23) "┘"
+} else {
+r = "┌" replicate("─", 23) "┬" replicate("─", 23) "┬" replicate("─", 23) "┐" RS
+for (i = 0; i < rows; i++) {
+r = r "│"
+for (j = 0; j < 3; j++) {
+if (cols[j][i]) {
+t1 = getName(cols[j][i])
+if (length(t1) > 15)
+t1 = substr(t1, 1, 12) "..."
+t1 = sprintf(" %-15s", t1)
+switch (length(cols[j][i])) {
+case 1: case 2: case 3: case 4:
+t2 = sprintf("- %s │", ansi("bold", sprintf("%4s", cols[j][i])))
+break
+case 5:
+t2 = sprintf("- %s│", ansi("bold", cols[j][i]))
+break
+case 6:
+t2 = sprintf("-%s│", ansi("bold", cols[j][i]))
+break
+case 7:
+t2 = sprintf("-%s", ansi("bold", cols[j][i]))
+break
+default:
+t2 = ansi("bold", cols[j][i])
+}
+r = r t1 t2
+} else
+r = r sprintf("%23s│", NULLSTR)
+}
+r = r RS
+}
+r = r "└" replicate("─", 23) "┴" replicate("─", 23) "┴" replicate("─", 23) "┘"
+}
+return r
+}
+function getList(codes, code, i, r, saveSortedIn) {
+r = NULLSTR
+if (!isarray(codes))
+r = getDetails(codes)
+else if (anything(codes)) {
+saveSortedIn = PROCINFO["sorted_in"]
+PROCINFO["sorted_in"] = "@ind_num_asc"
+for (i in codes)
+r = (r ? r RS prettify("target-seperator", replicate(Option["chr-target-seperator"], Option["width"])) RS\
+: r) getDetails(codes[i])
+PROCINFO["sorted_in"] = saveSortedIn
+} else
+r = getDetails(Option["hl"])
+return r
+}
+function tokenize(returnTokens, string,
+delimiters,
+newlines,
+quotes,
+escapeChars,
+leftBlockComments,
+rightBlockComments,
+lineComments,
+reservedOperators,
+reservedPatterns,
+blockCommenting,
+c,
+currentToken,
+escaping,
+i,
+lineCommenting,
+p,
+quoting,
+r,
+s,
+tempGroup,
+tempPattern,
+tempString) {
+if (!delimiters[0]) {
+delimiters[0] = " "
+delimiters[1] = "\t"
+delimiters[2] = "\v"
+}
+if (!newlines[0]) {
+newlines[0] = "\n"
+newlines[1] = "\r"
+}
+if (!quotes[0]) {
+quotes[0] = "\""
+}
+if (!escapeChars[0]) {
+escapeChars[0] = "\\"
+}
+if (!leftBlockComments[0]) {
+leftBlockComments[0] = "#|"
+leftBlockComments[1] = "/*"
+leftBlockComments[2] = "(*"
+}
+if (!rightBlockComments[0]) {
+rightBlockComments[0] = "|#"
+rightBlockComments[1] = "*/"
+rightBlockComments[2] = "*)"
+}
+if (!lineComments[0]) {
+lineComments[0] = ";"
+lineComments[1] = "//"
+lineComments[2] = "#"
+}
+if (!reservedOperators[0]) {
+reservedOperators[0] = "("
+reservedOperators[1] = ")"
+reservedOperators[2] = "["
+reservedOperators[3] = "]"
+reservedOperators[4] = "{"
+reservedOperators[5] = "}"
+reservedOperators[6] = ","
+}
+if (!reservedPatterns[0]) {
+reservedPatterns[0] = "[+-]?((0|[1-9][0-9]*)|[.][0-9]*|(0|[1-9][0-9]*)[.][0-9]*)([Ee][+-]?[0-9]+)?"
+reservedPatterns[1] = "[+-]?0[0-7]+([.][0-7]*)?"
+reservedPatterns[2] = "[+-]?0[Xx][0-9A-Fa-f]+([.][0-9A-Fa-f]*)?"
+}
+split(string, s, "")
+currentToken = ""
+quoting = escaping = blockCommenting = lineCommenting = 0
+p = 0
+i = 1
+while (i <= length(s)) {
+c = s[i]
+r = substr(string, i)
+if (blockCommenting) {
+if (tempString = startsWithAny(r, rightBlockComments))
+blockCommenting = 0
+i++
+} else if (lineCommenting) {
+if (belongsTo(c, newlines))
+lineCommenting = 0
+i++
+} else if (quoting) {
+currentToken = currentToken c
+if (escaping) {
+escaping = 0
+} else {
+if (belongsTo(c, quotes)) {
+if (currentToken) {
+returnTokens[p++] = currentToken
+currentToken = ""
+}
+quoting = 0
+} else if (belongsTo(c, escapeChars)) {
+escaping = 1
+} else {
+}
+}
+i++
+} else {
+if (belongsTo(c, delimiters) || belongsTo(c, newlines)) {
+if (currentToken) {
+returnTokens[p++] = currentToken
+currentToken = ""
+}
+i++
+} else if (belongsTo(c, quotes)) {
+if (currentToken) {
+returnTokens[p++] = currentToken
+}
+currentToken = c
+quoting = 1
+i++
+} else if (tempString = startsWithAny(r, leftBlockComments)) {
+if (currentToken) {
+returnTokens[p++] = currentToken
+currentToken = ""
+}
+blockCommenting = 1
+i += length(tempString)
+} else if (tempString = startsWithAny(r, lineComments)) {
+if (currentToken) {
+returnTokens[p++] = currentToken
+currentToken = ""
+}
+lineCommenting = 1
+i += length(tempString)
+} else if (tempString = startsWithAny(r, reservedOperators)) {
+if (currentToken) {
+returnTokens[p++] = currentToken
+currentToken = ""
+}
+returnTokens[p++] = tempString
+i += length(tempString)
+} else if (tempPattern = matchesAny(r, reservedPatterns)) {
+if (currentToken) {
+returnTokens[p++] = currentToken
+currentToken = ""
+}
+match(r, "^" tempPattern, tempGroup)
+returnTokens[p++] = tempGroup[0]
+i += length(tempGroup[0])
+} else {
+currentToken = currentToken c
+i++
+}
+}
+}
+if (currentToken)
+returnTokens[p++] = currentToken
+}
+function parseJsonArray(returnAST, tokens,
+leftBrackets,
+rightBrackets,
+separators,
+i, j, key, p, stack, token) {
+if (!leftBrackets[0]) {
+leftBrackets[0] = "("
+leftBrackets[1] = "["
+leftBrackets[2] = "{"
+}
+if (!rightBrackets[0]) {
+rightBrackets[0] = ")"
+rightBrackets[1] = "]"
+rightBrackets[2] = "}"
+}
+if (!separators[0]) {
+separators[0] = ","
+}
+stack[p = 0] = 0
+for (i = 0; i < length(tokens); i++) {
+token = tokens[i]
+if (belongsTo(token, leftBrackets))
+stack[++p] = 0
+else if (belongsTo(token, rightBrackets))
+--p
+else if (belongsTo(token, separators))
+stack[p]++
+else {
+key = stack[0]
+for (j = 1; j <= p; j++)
+key = key SUBSEP stack[j]
+returnAST[key] = token
+}
+}
+}
+function parseJson(returnAST, tokens,
+arrayStartTokens, arrayEndTokens,
+objectStartTokens, objectEndTokens,
+commas, colons,
+flag, i, j, key, name, p, stack, token) {
+if (!arrayStartTokens[0]) arrayStartTokens[0] = "["
+if (!arrayEndTokens[0]) arrayEndTokens[0] = "]"
+if (!objectStartTokens[0]) objectStartTokens[0] = "{"
+if (!objectEndTokens[0]) objectEndTokens[0] = "}"
+if (!commas[0]) commas[0] = ","
+if (!colons[0]) colons[0] = ":"
+stack[p = 0] = 0
+flag = 0
+for (i = 0; i < length(tokens); i++) {
+token = tokens[i]
+if (belongsTo(token, arrayStartTokens)) {
+stack[++p] = 0
+} else if (belongsTo(token, objectStartTokens)) {
+stack[++p] = NULLSTR
+flag = 0
+} else if (belongsTo(token, objectEndTokens) ||
+belongsTo(token, arrayEndTokens)) {
+--p
+} else if (belongsTo(token, commas)) {
+if (isnum(stack[p]))
+stack[p]++
+else
+flag = 0
+} else if (belongsTo(token, colons)) {
+flag = 1
+} else if (isnum(stack[p]) || flag) {
+key = stack[0]
+for (j = 1; j <= p; j++)
+key = key SUBSEP stack[j]
+returnAST[key] = token
+flag = 0
+} else {
+stack[p] = unparameterize(token)
+}
+}
+}
+function parseList(returnAST, tokens,
+leftBrackets,
+rightBrackets,
+separators,
+i, j, key, p, stack, token) {
+if (!leftBrackets[0]) {
+leftBrackets[0] = "("
+leftBrackets[1] = "["
+leftBrackets[2] = "{"
+}
+if (!rightBrackets[0]) {
+rightBrackets[0] = ")"
+rightBrackets[1] = "]"
+rightBrackets[2] = "}"
+}
+if (!separators[0]) {
+separators[0] = ","
+}
+stack[p = 0] = 0
+for (i = 0; i < length(tokens); i++) {
+token = tokens[i]
+if (belongsTo(token, leftBrackets)) {
+stack[++p] = 0
+} else if (belongsTo(token, rightBrackets)) {
+stack[--p]++
+} else if (belongsTo(token, separators)) {
+} else {
+key = NULLSTR
+if (p > 0) {
+for (j = 0; j < p - 1; j++)
+key = key SUBSEP stack[j]
+returnAST[key][stack[p - 1]] = NULLSTR
+key = key SUBSEP stack[p - 1]
+}
+returnAST[key][stack[p]] = token
+stack[p]++
+}
+}
+}
+function prettify(name, string, i, temp) {
+temp = string
+if ("sgr-" name in Option)
+if (isarray(Option["sgr-" name]))
+for (i in Option["sgr-" name])
+temp = ansi(Option["sgr-" name][i], temp)
+else
+temp = ansi(Option["sgr-" name], temp)
+return temp
+}
+function randomColor( i) {
+i = int(5 * rand())
+switch (i) {
+case 0: return "green"
+case 1: return "yellow"
+case 2: return "blue"
+case 3: return "magenta"
+case 4: return "cyan"
+default: return "default"
+}
+}
+function setRandomTheme( i, n, temp) {
+srand(systime())
+for (i = 0; i < 3; i++) {
+do temp = randomColor(); while (belongsTo(temp, n))
+n[i] = temp
+}
+Option["sgr-prompt-message"] = Option["sgr-languages"] = n[0]
+Option["sgr-original-dictionary-detailed-word-class"][1] = n[0]
+Option["sgr-original-dictionary-detailed-word-class"][2] = "bold"
+Option["sgr-original-dictionary-synonyms"] = n[0]
+Option["sgr-original-dictionary-synonyms-word-class"][1] = n[0]
+Option["sgr-original-dictionary-synonyms-word-class"][2] = "bold"
+Option["sgr-original-dictionary-examples"] = n[0]
+Option["sgr-original-dictionary-see-also"] = n[0]
+Option["sgr-dictionary-word-class"][1] = n[0]
+Option["sgr-dictionary-word-class"][2] = "bold"
+Option["sgr-original"][1] = Option["sgr-original-phonetics"][1] = n[1]
+Option["sgr-original"][2] = Option["sgr-original-phonetics"][2] = "bold"
+Option["sgr-prompt-message-original"][1] = n[1]
+Option["sgr-prompt-message-original"][2] = "bold"
+Option["sgr-languages-sl"] = n[1]
+Option["sgr-original-dictionary-detailed-explanation"][1] = n[1]
+Option["sgr-original-dictionary-detailed-explanation"][2] = "bold"
+Option["sgr-original-dictionary-detailed-example"] = n[1]
+Option["sgr-original-dictionary-detailed-synonyms"] = n[1]
+Option["sgr-original-dictionary-detailed-synonyms-item"][1] = n[1]
+Option["sgr-original-dictionary-detailed-synonyms-item"][2] = "bold"
+Option["sgr-original-dictionary-synonyms-synonyms"] = n[1]
+Option["sgr-original-dictionary-synonyms-synonyms-item"][1] = n[1]
+Option["sgr-original-dictionary-synonyms-synonyms-item"][2] = "bold"
+Option["sgr-original-dictionary-examples-example"] = n[1]
+Option["sgr-original-dictionary-examples-original"][1] = n[1]
+Option["sgr-original-dictionary-examples-original"][2] = "bold"
+Option["sgr-original-dictionary-examples-original"][3] = "underline"
+Option["sgr-original-dictionary-see-also-phrases"] = n[1]
+Option["sgr-original-dictionary-see-also-phrases-item"][1] = n[1]
+Option["sgr-original-dictionary-see-also-phrases-item"][2] = "bold"
+Option["sgr-dictionary-explanation"] = n[1]
+Option["sgr-dictionary-explanation-item"][1] = n[1]
+Option["sgr-dictionary-explanation-item"][2] = "bold"
+Option["sgr-alternatives-original"][1] = n[1]
+Option["sgr-alternatives-original"][2] = "bold"
+Option["sgr-translation"][1] = Option["sgr-translation-phonetics"][1] = n[2]
+Option["sgr-translation"][2] = Option["sgr-translation-phonetics"][2] = "bold"
+Option["sgr-languages-tl"] = n[2]
+Option["sgr-dictionary-word"][1] = n[2]
+Option["sgr-dictionary-word"][2] = "bold"
+Option["sgr-alternatives-translations"] = n[2]
+Option["sgr-alternatives-translations-item"][1] = n[2]
+Option["sgr-alternatives-translations-item"][2] = "bold"
+Option["sgr-brief-translation"][1] = Option["sgr-brief-translation-phonetics"][1] = n[2]
+Option["sgr-brief-translation"][2] = Option["sgr-brief-translation-phonetics"][2] = "bold"
+Option["fmt-welcome-message"] = Name
+Option["sgr-welcome-message"][1] = n[0]
+Option["sgr-welcome-message"][2] = "bold"
+Option["fmt-welcome-submessage"] = "(:q to quit)"
+Option["sgr-welcome-submessage"] = n[0]
+Option["fmt-prompt"] = "%s> "
+Option["sgr-prompt"][1] = n[1]
+Option["sgr-prompt"][2] = "bold"
+}
+function setDefaultTheme() {
+Option["sgr-translation"] = Option["sgr-translation-phonetics"] = "bold"
+Option["sgr-prompt-message-original"] = "underline"
+Option["sgr-languages-sl"] = "underline"
+Option["sgr-languages-tl"] = "bold"
+Option["sgr-original-dictionary-detailed-explanation"] = "bold"
+Option["sgr-original-dictionary-detailed-synonyms-item"] = "bold"
+Option["sgr-original-dictionary-synonyms-synonyms-item"] = "bold"
+Option["sgr-original-dictionary-examples-original"][1] = "bold"
+Option["sgr-original-dictionary-examples-original"][2] = "underline"
+Option["sgr-original-dictionary-see-also-phrases-item"] = "bold"
+Option["sgr-dictionary-word"] = "bold"
+Option["sgr-alternatives-original"] = "underline"
+Option["sgr-alternatives-translations-item"] = "bold"
+Option["fmt-welcome-message"] = Name
+Option["sgr-welcome-message"] = "bold"
+Option["fmt-welcome-submessage"] = "(:q to quit)"
+Option["fmt-prompt"] = "%s> "
+Option["sgr-prompt"] = "bold"
+}
+function setTheme( file, line, script) {
+if (Option["theme"] && Option["theme"] != "default"\
+&& Option["theme"] != "none" && Option["theme"] != "random") {
+file = Option["theme"]
+if (!fileExists(file)) {
+file = ENVIRON["HOME"] "/.translate-shell/" Option["theme"]
+if (!fileExists(file)) {
+file = ENVIRON["HOME"] "/.config/translate-shell/" Option["theme"]
+if (!fileExists(file)) return
+}
+}
+}
+if (file && fileExists(file)) {
+script = NULLSTR
+while (getline line < file)
+script = script "\n" line
+loadOptions(script)
+} else if (Option["theme"] == "none")
+;
+else if (Option["theme"] == "random")
+setRandomTheme()
+else
+setDefaultTheme()
+}
+function provides(engineName) {
+Translator[tolower(engineName)] = TRUE
+}
+function engineMethod(methodName, engine, translator) {
+if (!Translator[Option["engine"]]) {
+engine = tolower(Option["engine"])
+if (!Translator[engine])
+for (translator in Translator)
+if (Translator[translator] &&
+translator ~ "^"engine) {
+engine = translator
+break
+}
+if (!Translator[engine]) {
+e("[ERROR] Translator not found: " Option["engine"] "\n"\
+" Run '-list-engines / -S' to see a list of available engines.")
+exit 1
+}
+Option["engine"] = engine
+}
+return Option["engine"] methodName
+}
+function initAudioPlayer() {
+AudioPlayer = !system("mpv" SUPOUT SUPERR) ?
+"mpv --no-config" :
+(!system("mplayer" SUPOUT SUPERR) ?
+"mplayer" :
+(!system("mpg123 --version" SUPOUT SUPERR) ?
+"mpg123" :
+""))
+}
+function initSpeechSynthesizer() {
+SpeechSynthesizer = !system("say ''" SUPOUT SUPERR) ?
+"say" :
+(!system("espeak ''" SUPOUT SUPERR) ?
+"espeak" :
+"")
+}
+function initPager() {
+Pager = !system("less -V" SUPOUT SUPERR) ?
+"less" :
+(!system("more -V" SUPOUT SUPERR) ?
+"more" :
+(!system("most" SUPOUT SUPERR) ?
+"most" :
+""))
+}
+function initHttpService( inet) {
+_Init()
+inet = "inet"
+if (Option["ip-version"])
+inet = inet Option["ip-version"]
+if (Option["proxy"]) {
+match(Option["proxy"], /^(http:\/*)?(([^:]+):([^@]+)@)?([^\/]*):([^\/:]*)/, HttpProxySpec)
+HttpAuthUser = HttpProxySpec[3]
+HttpAuthPass = HttpProxySpec[4]
+HttpAuthCredentials = base64(unquote(HttpAuthUser) ":" HttpAuthPass)
+HttpService = "/" inet "/tcp/0/" HttpProxySpec[5] "/" HttpProxySpec[6]
+HttpPathPrefix = HttpProtocol HttpHost
+} else {
+HttpService = "/" inet "/tcp/0/" HttpHost "/" HttpPort
+HttpPathPrefix = ""
+}
+PROCINFO[HttpService, "READ_TIMEOUT"] = 2000
+}
+function preprocess(text) {
+return quote(text)
+}
+function preprocessByDump(text, arr, len, temp) {
+len = dumpX(text, arr)
+temp = ""
+for (i = 1; i <= len; i++)
+temp = temp "%" arr[i]
+return temp
+}
+function postprocess(text) {
+text = gensub(/ ([.,;:?!"])/, "\\1", "g", text)
+text = gensub(/(["]) /, "\\1", "g", text)
+return text
+}
+function getResponse(text, sl, tl, hl,
+content, header, isBody, url, group, status, location) {
+url = _RequestUrl(text, sl, tl, hl)
+header = "GET " url " HTTP/1.1\r\n"\
+"Host: " HttpHost "\r\n"\
+"Connection: close\r\n"
+if (Option["user-agent"])
+header = header "User-Agent: " Option["user-agent"] "\r\n"
+if (Cookie)
+header = header "Cookie: " Cookie "\r\n"
+if (HttpAuthUser && HttpAuthPass)
+header = header "Proxy-Authorization: Basic " HttpAuthCredentials "\r\n"
+content = NULLSTR; isBody = 0
+while (1) {
+print header |& HttpService
+while ((HttpService |& getline) > 0) {
+if (isBody)
+content = content ? content "\n" $0 : $0
+else if (length($0) <= 1)
+isBody = 1
+else {
+match($0, /^HTTP[^ ]* ([^ ]*)/, group)
+if (RSTART) status = group[1]
+match($0, /^Location: (.*)/, group)
+if (RSTART) location = squeeze(group[1])
+}
+l(sprintf("%4s bytes > %s", length($0), $0))
+}
+close(HttpService)
+if (ERRNO == "Connection timed out") {
+w("[WARNING] " ERRNO ". Retrying IPv4 connection.")
+Option["ip-version"] = 4
+initHttpService()
+PROCINFO[HttpService, "READ_TIMEOUT"] = 0
+ERRNO = ""
+} else
+break
+}
+if ((status == "301" || status == "302") && location)
+content = curl(location)
+return assert(content, "[ERROR] Null response.")
+}
+function postResponse(text, sl, tl, hl, type,
+content, contentLength, contentType, group,
+header, isBody, reqBody, url, status, location, userAgent) {
+url = _PostRequestUrl(text, sl, tl, hl, type)
+contentType = _PostRequestContentType(text, sl, tl, hl, type)
+userAgent = _PostRequestUserAgent(text, sl, tl, hl, type)
+reqBody = _PostRequestBody(text, sl, tl, hl, type)
+if (DumpContentengths[reqBody])
+contentLength = DumpContentengths[reqBody]
+else
+contentLength = DumpContentengths[reqBody] = dump(reqBody, group)
+header = "POST " url " HTTP/1.1\r\n"\
+"Host: " HttpHost "\r\n"\
+"Connection: close\r\n"\
+"Content-Length: " contentLength "\r\n"\
+"Content-Type: " contentType "\r\n"
+if (Option["user-agent"] && !userAgent)
+header = header "User-Agent: " Option["user-agent"] "\r\n"
+if (userAgent)
+header = header "User-Agent: " userAgent "\r\n"
+if (Cookie)
+header = header "Cookie: " Cookie "\r\n"
+if (HttpAuthUser && HttpAuthPass)
+header = header "Proxy-Authorization: Basic " HttpAuthCredentials "\r\n"
+content = NULLSTR; isBody = 0
+while (1) {
+print (header "\r\n" reqBody) |& HttpService
+while ((HttpService |& getline) > 0) {
+if (isBody)
+content = content ? content "\n" $0 : $0
+else if (length($0) <= 1)
+isBody = 1
+else {
+match($0, /^HTTP[^ ]* ([^ ]*)/, group)
+if (RSTART) status = group[1]
+match($0, /^Location: (.*)/, group)
+if (RSTART) location = squeeze(group[1])
+}
+l(sprintf("%4s bytes > %s", length($0), $0))
+}
+close(HttpService)
+if (ERRNO == "Connection timed out") {
+w("[WARNING] " ERRNO ". Retrying IPv4 connection.")
+Option["ip-version"] = 4
+initHttpService()
+PROCINFO[HttpService, "READ_TIMEOUT"] = 0
+ERRNO = ""
+} else
+break
+}
+if (status == "404") {
+e("[ERROR] 404 Not Found")
+exit 1
+}
+if ((status == "301" || status == "302") && location) {
+url = "https" substr(url, 5)
+content = curlPost(url, reqBody)
+}
+return content
+}
+function p(string) {
+if (Option["view"]) {
+print string | Option["pager"]
+close(Option["pager"])
+} else
+print string > Option["output"]
+}
+function play(text, tl, url, status) {
+url = _TTSUrl(text, tl)
+status = system(Option["player"] " " parameterize(url) SUPOUT SUPERR)
+if (status)
+w("Voice output isn't available for " getName(tl))
+return status
+}
+function download_audio(text, tl, url, output) {
+url = _TTSUrl(text, tl)
+if (Option["download-audio-as"])
+output = Option["download-audio-as"]
+else
+output = text " [" Option["engine"] "] (" Option["narrator"] ").ts"
+if (url ~ /^\//)
+system("mv -- " parameterize(url) " " parameterize(output))
+else
+curl(url, output)
+}
+function getTranslation(text, sl, tl, hl,
+isVerbose, toSpeech, returnPlaylist, returnIl) {
+return _Translate(text, sl, tl, hl,
+isVerbose, toSpeech, returnPlaylist, returnIl)
+}
+function fileTranslation(uri, group, temp1, temp2) {
+temp1 = Option["input"]
+temp2 = Option["verbose"]
+match(uri, /^file:\/\/(.*)/, group)
+Option["input"] = group[1]
+Option["verbose"] = 0
+translateMain()
+Option["input"] = temp1
+Option["verbose"] = temp2
+}
+function webTranslation(uri, sl, tl, hl, temp) {
+temp = _WebTranslateUrl(uri, sl, tl, hl)
+if (temp) {
+p(temp)
+if (Option["browser"] != NONE)
+system(Option["browser"] " " parameterize(temp) SUPOUT SUPERR)
+}
+}
+function translate(text, inline,
+i, j, playlist, il, saveSortedIn) {
+if (!getCode(Option["hl"])) {
+w("[WARNING] Unknown language code: " Option["hl"] ", fallback to English: en")
+Option["hl"] = "en"
+} else if (isRTL(Option["hl"])) {
+if (!FriBidi)
+w("[WARNING] " getName(Option["hl"]) " is a right-to-left language, but FriBidi is not found.")
+}
+if (!getCode(Option["sl"])) {
+w("[WARNING] Unknown source language code: " Option["sl"])
+} else if (isRTL(Option["sl"])) {
+if (!FriBidi)
+w("[WARNING] " getName(Option["sl"]) " is a right-to-left language, but FriBidi is not found.")
+}
+saveSortedIn = PROCINFO["sorted_in"]
+PROCINFO["sorted_in"] = "@ind_num_asc"
+for (i in Option["tl"]) {
+if (!Option["interactive"])
+if (Option["verbose"] && i > 1)
+p(prettify("target-seperator", replicate(Option["chr-target-seperator"], Option["width"])))
+if (inline &&
+startsWithAny(text, UriSchemes) == "file://") {
+fileTranslation(text)
+} else if (inline &&
+startsWithAny(text, UriSchemes) == "http://" ||
+startsWithAny(text, UriSchemes) == "https://") {
+webTranslation(text, Option["sl"], Option["tl"][i], Option["hl"])
+} else {
+if (!Option["no-translate"])
+p(getTranslation(text, Option["sl"], Option["tl"][i], Option["hl"], Option["verbose"], Option["play"] || Option["download-audio"], playlist, il))
+else
+il[0] = Option["sl"] == "auto" ? "en" : Option["sl"]
+if (Option["play"] == 1) {
+if (Option["player"])
+for (j in playlist)
+play(playlist[j]["text"], playlist[j]["tl"])
+else if (SpeechSynthesizer)
+for (j in playlist)
+print playlist[j]["text"] | SpeechSynthesizer
+} else if (Option["play"] == 2) {
+if (Option["player"])
+play(text, il[0])
+else if (SpeechSynthesizer)
+print text | SpeechSynthesizer
+}
+if (Option["download-audio"] == 1) {
+if (Option["play"] != 2 && !Option["no-translate"])
+download_audio(playlist[length(playlist) - 1]["text"],\
+playlist[length(playlist) - 1]["tl"])
+else
+download_audio(text, il[0])
+}
+}
+}
+PROCINFO["sorted_in"] = saveSortedIn
+}
+function translates(text, inline,
+i) {
+saveSortedIn = PROCINFO["sorted_in"]
+PROCINFO["sorted_in"] = "@ind_num_asc"
+for (i in Option["sls"]) {
+if (!Option["interactive"])
+if (Option["verbose"] && i > 1)
+p(prettify("target-seperator", replicate(Option["chr-target-seperator"], Option["width"])))
+Option["sl"] = Option["sls"][i]
+translate(text, inline)
+}
+PROCINFO["sorted_in"] = saveSortedIn
+}
+function translateMain( i, line) {
+if (Option["interactive"])
+prompt()
+if (Option["input"] == STDIN || fileExists(Option["input"])) {
+i = 0
+while (getline line < Option["input"])
+if (line) {
+if (!Option["interactive"])
+if (Option["verbose"] && i++ > 0)
+p(prettify("source-seperator",
+replicate(Option["chr-source-seperator"],
+Option["width"])))
+if (Option["interactive"])
+repl(line)
+else
+translates(line)
+} else {
+if (!Option["interactive"])
+if (!Option["verbose"])
+p(line)
+}
+} else
+e("[ERROR] File not found: " Option["input"])
+}
+function _Init( vm) {
+vm = engineMethod("Init")
+return @vm()
+}
+function _RequestUrl(text, sl, tl, hl, vm) {
+vm = engineMethod("RequestUrl")
+return @vm(text, sl, tl, hl)
+}
+function _PostRequestUrl(text, sl, tl, hl, type, vm) {
+vm = engineMethod("PostRequestUrl")
+return @vm(text, sl, tl, hl, type)
+}
+function _PostRequestContentType(text, sl, tl, hl, type, vm) {
+vm = engineMethod("PostRequestContentType")
+return @vm(text, sl, tl, hl, type)
+}
+function _PostRequestUserAgent(text, sl, tl, hl, type, vm) {
+vm = engineMethod("PostRequestUserAgent")
+return @vm(text, sl, tl, hl, type)
+}
+function _PostRequestBody(text, sl, tl, hl, type, vm) {
+vm = engineMethod("PostRequestBody")
+return @vm(text, sl, tl, hl, type)
+}
+function _TTSUrl(text, tl, vm) {
+vm = engineMethod("TTSUrl")
+return @vm(text, tl)
+}
+function _WebTranslateUrl(uri, sl, tl, hl, vm) {
+vm = engineMethod("WebTranslateUrl")
+return @vm(uri, sl, tl, hl)
+}
+function _Translate(text, sl, tl, hl,
+isVerbose, toSpeech, returnPlaylist, returnIl,
+vm) {
+vm = engineMethod("Translate")
+return @vm(text, sl, tl, hl,
+isVerbose, toSpeech, returnPlaylist, returnIl)
+}
+BEGIN { provides("google") }
+function genRL(a, x,
+b, c, d, i, y) {
+tokenize(y, x)
+parseList(b, y)
+i = SUBSEP 0
+for (c = 0; c < length(b[i]) - 2; c += 3) {
+d = b[i][c + 2]
+d = d >= 97 ? d - 87 :
+d - 48
+d = b[i][c + 1] == 43 ? rshift(a, d) : lshift(a, d)
+a = b[i][c] == 43 ? and(a + d, 4294967295) : xor(a, d)
+}
+return a
+}
+function genTK(text,
+a, d, dLen, e, tkk, ub, vb) {
+if (TK[text]) return TK[text]
+tkk = systime() / 3600
+ub = "[43,45,51,94,43,98,43,45,102]"
+vb = "[43,45,97,94,43,54]"
+dLen = dump(text, d)
+a = tkk
+for (e = 1; e <= dLen; e++)
+a = genRL(a + d[e], vb)
+a = genRL(a, ub)
+0 > a && (a = and(a, 2147483647) + 2147483648)
+a %= 1e6
+TK[text] = a "." xor(a, tkk)
+l(text, "text")
+l(tkk, "tkk")
+l(TK[text], "tk")
+return TK[text]
+}
+function googleInit() {
+HttpProtocol = "http://"
+HttpHost = "translate.googleapis.com"
+HttpPort = 80
+}
+function googleRequestUrl(text, sl, tl, hl, qc) {
+qc = Option["no-autocorrect"] ? "qc" : "qca";
+return HttpPathPrefix "/translate_a/single?client=gtx"\
+"&ie=UTF-8&oe=UTF-8"\
+"&dt=bd&dt=ex&dt=ld&dt=md&dt=rw&dt=rm&dt=ss&dt=t&dt=at&dt=gt"\
+"&dt=" qc "&sl=" sl "&tl=" tl "&hl=" hl\
+"&q=" preprocessByDump(text)
+}
+function googleTTSUrl(text, tl) {
+return HttpProtocol HttpHost "/translate_tts?ie=UTF-8&client=gtx"\
+"&tl=" tl "&q=" preprocessByDump(text)
+}
+function googleWebTranslateUrl(uri, sl, tl, hl) {
+return "https://translate.google.com/translate?"\
+"hl=" hl "&sl=" sl "&tl=" tl "&u=" uri
+}
+function googleTranslate(text, sl, tl, hl,
+isVerbose, toSpeech, returnPlaylist, returnIl,
+r,
+content, tokens, ast,
+_sl, _tl, _hl, il, ils, isPhonetic,
+article, example, explanation, ref, word,
+translation, translations, phonetics,
+wordClasses, words, segments, altTranslations,
+original, oPhonetics, oWordClasses, oWords,
+oRefs, oSynonymClasses, oSynonyms,
+oExamples, oSeeAlso,
+wShowOriginal, wShowOriginalPhonetics,
+wShowTranslation, wShowTranslationPhonetics,
+wShowPromptMessage, wShowLanguages,
+wShowOriginalDictionary, wShowDictionary,
+wShowAlternatives,
+genderedTrans, hasWordClasses, hasAltTranslations,
+i, j, k, group, temp, saveSortedIn) {
+isPhonetic = match(tl, /^@/)
+tl = substr(tl, 1 + isPhonetic)
+if (!getCode(tl)) {
+w("[WARNING] Unknown target language code: " tl)
+} else if (isRTL(tl)) {
+if (!FriBidi)
+w("[WARNING] " getName(tl) " is a right-to-left language, but FriBidi is not found.")
+}
+_sl = getCode(sl); if (!_sl) _sl = sl
+_tl = getCode(tl); if (!_tl) _tl = tl
+_hl = getCode(hl); if (!_hl) _hl = hl
+content = getResponse(text, _sl, _tl, _hl)
+if (Option["dump"])
+return content
+tokenize(tokens, content)
+parseJsonArray(ast, tokens)
+l(content, "content", 1, 1)
+l(tokens, "tokens", 1, 0, 1)
+l(ast, "ast")
+if (!isarray(ast) || !anything(ast)) {
+e("[ERROR] Oops! Something went wrong and I can't translate it for you :(")
+ExitCode = 1
+return
+}
+saveSortedIn = PROCINFO["sorted_in"]
+PROCINFO["sorted_in"] = "compareByIndexFields"
+for (i in ast) {
+if (ast[i] == "null") continue
+if (i ~ "^0" SUBSEP "0" SUBSEP "[[:digit:]]+" SUBSEP "0$")
+append(translations, literal(ast[i]))
+if (i ~ "^0" SUBSEP "0" SUBSEP "[[:digit:]]+" SUBSEP "1$")
+append(original, literal(ast[i]))
+if (i ~ "^0" SUBSEP "0" SUBSEP "[[:digit:]]+" SUBSEP "2$")
+append(phonetics, literal(ast[i]))
+if (i ~ "^0" SUBSEP "0" SUBSEP "[[:digit:]]+" SUBSEP "3$")
+append(oPhonetics, literal(ast[i]))
+if (match(i, "^0" SUBSEP "1" SUBSEP "([[:digit:]]+)" SUBSEP "0$", group))
+wordClasses[group[1]] = literal(ast[i])
+if (match(i, "^0" SUBSEP "1" SUBSEP "([[:digit:]]+)" SUBSEP "2" SUBSEP "([[:digit:]]+)" SUBSEP "([[:digit:]]+)$", group))
+words[group[1]][group[2]][group[3]] = literal(ast[i])
+if (match(i, "^0" SUBSEP "1" SUBSEP "([[:digit:]]+)" SUBSEP "2" SUBSEP "([[:digit:]]+)" SUBSEP "1" SUBSEP "([[:digit:]]+)$", group))
+words[group[1]][group[2]]["1"][group[3]] = literal(ast[i])
+if (match(i, "^0" SUBSEP "5" SUBSEP "([[:digit:]]+)" SUBSEP "0$", group)) {
+segments[group[1]] = literal(ast[i])
+altTranslations[group[1]][0] = ""
+}
+if (match(i, "^0" SUBSEP "5" SUBSEP "([[:digit:]]+)" SUBSEP "2" SUBSEP "([[:digit:]]+)" SUBSEP "0$", group))
+altTranslations[group[1]][group[2]] = literal(ast[i])
+if (i ~ "^0" SUBSEP "7" SUBSEP "5$") {
+if (ast[i] == "true")
+w("Showing translation for: (use -no-auto to disable autocorrect)")
+else
+w("Did you mean: "\
+ansi("bold", unparameterize(ast["0" SUBSEP "7" SUBSEP "1"])))
+}
+if (i ~ "^0" SUBSEP "8" SUBSEP "0" SUBSEP "[[:digit:]]+$" ||
+i ~ "^0" SUBSEP "2$")
+append(ils, literal(ast[i]))
+if (match(i, "^0" SUBSEP "11" SUBSEP "([[:digit:]]+)" SUBSEP "0$", group))
+oSynonymClasses[group[1]] = literal(ast[i])
+if (match(i, "^0" SUBSEP "11" SUBSEP "([[:digit:]]+)" SUBSEP "1" SUBSEP "([[:digit:]]+)" SUBSEP "1$", group))
+if (ast[i]) {
+oRefs[literal(ast[i])][1] = group[1]
+oRefs[literal(ast[i])][2] = group[2]
+}
+if (match(i, "^0" SUBSEP "11" SUBSEP "([[:digit:]]+)" SUBSEP "1" SUBSEP "([[:digit:]]+)" SUBSEP "0" SUBSEP "([[:digit:]]+)$", group))
+oSynonyms[group[1]][group[2]][group[3]] = literal(ast[i])
+if (match(i, "^0" SUBSEP "12" SUBSEP "([[:digit:]]+)" SUBSEP "0$", group))
+oWordClasses[group[1]] = literal(ast[i])
+if (match(i, "^0" SUBSEP "12" SUBSEP "([[:digit:]]+)" SUBSEP "1" SUBSEP "([[:digit:]]+)" SUBSEP "0$", group))
+oWords[group[1]][group[2]][0] = literal(ast[i])
+if (match(i, "^0" SUBSEP "12" SUBSEP "([[:digit:]]+)" SUBSEP "1" SUBSEP "([[:digit:]]+)" SUBSEP "1$", group))
+oWords[group[1]][group[2]][1] = literal(ast[i])
+if (match(i, "^0" SUBSEP "12" SUBSEP "([[:digit:]]+)" SUBSEP "1" SUBSEP "([[:digit:]]+)" SUBSEP "2$", group))
+oWords[group[1]][group[2]][2] = literal(ast[i])
+if (match(i, "^0" SUBSEP "13" SUBSEP "0" SUBSEP "([[:digit:]]+)" SUBSEP "0$", group))
+oExamples[group[1]] = literal(ast[i])
+if (match(i, "^0" SUBSEP "14" SUBSEP "0" SUBSEP "([[:digit:]]+)$", group))
+oSeeAlso[group[1]] = literal(ast[i])
+if (match(i, "^0" SUBSEP "18" SUBSEP "0" SUBSEP "([[:digit:]]+)" SUBSEP "1$", group))
+genderedTrans[group[1]] = literal(ast[i])
+}
+PROCINFO["sorted_in"] = saveSortedIn
+translation = join(translations)
+returnIl[0] = il = !anything(ils) || belongsTo(sl, ils) ? sl : ils[0]
+if (Option["verbose"] < -1)
+return il
+else if (Option["verbose"] < 0)
+return getList(il)
+if (!isVerbose) {
+r = isPhonetic && anything(phonetics) ?
+prettify("brief-translation-phonetics", join(phonetics, " ")) :
+prettify("brief-translation", s(translation, tl))
+if (toSpeech) {
+returnPlaylist[0]["text"] = translation
+returnPlaylist[0]["tl"] = _tl
+}
+} else {
+wShowOriginal = Option["show-original"]
+wShowOriginalPhonetics = Option["show-original-phonetics"]
+wShowTranslation = Option["show-translation"]
+wShowTranslationPhonetics = Option["show-translation-phonetics"]
+wShowPromptMessage = Option["show-prompt-message"]
+wShowLanguages = Option["show-languages"]
+wShowOriginalDictionary = Option["show-original-dictionary"]
+wShowDictionary = Option["show-dictionary"]
+wShowAlternatives = Option["show-alternatives"]
+if (!anything(oPhonetics)) wShowOriginalPhonetics = 0
+if (!anything(phonetics)) wShowTranslationPhonetics = 0
+if (getCode(il) == getCode(tl) &&\
+(isarray(oWordClasses) || isarray(oSynonymClasses) ||\
+isarray(oExamples) || isarray(oSeeAlso))) {
+wShowOriginalDictionary = 1
+wShowTranslation = 0
+}
+hasWordClasses = exists(wordClasses)
+hasAltTranslations = exists(altTranslations[0])
+if (!hasWordClasses && !hasAltTranslations)
+wShowPromptMessage = wShowLanguages = 0
+if (!hasWordClasses) wShowDictionary = 0
+if (!hasAltTranslations) wShowAlternatives = 0
+if (wShowOriginal) {
+if (r) r = r RS RS
+r = r m("-- display original text & phonetics")
+r = r prettify("original", s(join(original, " "), il))
+if (wShowOriginalPhonetics)
+r = r RS prettify("original-phonetics", showPhonetics(join(oPhonetics, " "), il))
+}
+if (wShowTranslation) {
+if (r) r = r RS RS
+r = r m("-- display major translation & phonetics")
+if (!exists(genderedTrans))
+r = r prettify("translation", s(translation, tl))
+else {
+r = r prettify("prompt-message", s("(♂) ", hl))
+r = r prettify("translation", s(genderedTrans[0], tl)) RS
+r = r prettify("prompt-message", s("(♀) ", hl))
+r = r prettify("translation", s(genderedTrans[1], tl))
+}
+if (wShowTranslationPhonetics)
+r = r RS prettify("translation-phonetics", showPhonetics(join(phonetics, " "), tl))
+}
+if (wShowPromptMessage || wShowLanguages)
+if (r) r = r RS
+if (wShowPromptMessage) {
+if (hasWordClasses) {
+if (r) r = r RS
+r = r m("-- display prompt message (Definitions of ...)")
+if (isRTL(hl))
+r = r prettify("prompt-message", s(showDefinitionsOf(hl, join(original, " "))))
+else {
+split(showDefinitionsOf(hl, "\0%s\0"), group, "\0")
+for (i = 1; i <= length(group); i++) {
+if (group[i] == "%s")
+r = r prettify("prompt-message-original", show(join(original, " "), il))
+else
+r = r prettify("prompt-message", group[i])
+}
+}
+} else if (hasAltTranslations) {
+if (r) r = r RS
+r = r m("-- display prompt message (Translations of ...)")
+if (isRTL(hl))
+r = r prettify("prompt-message", s(showTranslationsOf(hl, join(original, " "))))
+else {
+split(showTranslationsOf(hl, "\0%s\0"), group, "\0")
+for (i = 1; i <= length(group); i++) {
+if (group[i] == "%s")
+r = r prettify("prompt-message-original", show(join(original, " "), il))
+else
+r = r prettify("prompt-message", group[i])
+}
+}
+}
+}
+if (wShowLanguages) {
+if (r) r = r RS
+r = r m("-- display source language -> target language")
+temp = Option["fmt-languages"]
+if (!temp) temp = "[ %s -> %t ]"
+split(temp, group, /(%s|%S|%t|%T)/)
+r = r prettify("languages", group[1])
+if (temp ~ /%s/)
+r = r prettify("languages-sl", getDisplay(il))
+if (temp ~ /%S/)
+r = r prettify("languages-sl", getName(il))
+r = r prettify("languages", group[2])
+if (temp ~ /%t/)
+r = r prettify("languages-tl", getDisplay(tl))
+if (temp ~ /%T/)
+r = r prettify("languages-tl", getName(tl))
+r = r prettify("languages", group[3])
+}
+if (wShowOriginalDictionary) {
+if (exists(oWordClasses)) {
+if (r) r = r RS
+r = r m("-- display original dictionary (detailed explanations)")
+for (i = 0; i < length(oWordClasses); i++) {
+r = (i > 0 ? r RS : r) RS prettify("original-dictionary-detailed-word-class", s(oWordClasses[i], hl))
+for (j = 0; j < length(oWords[i]); j++) {
+explanation = oWords[i][j][0]
+ref = oWords[i][j][1]
+example = oWords[i][j][2]
+r = (j > 0 ? r RS : r) RS prettify("original-dictionary-detailed-explanation", ins(1, explanation, il))
+if (example)
+r = r RS prettify("original-dictionary-detailed-example", ins(2, "- \"" example "\"", il))
+if (ref && isarray(oRefs[ref])) {
+temp = prettify("original-dictionary-detailed-synonyms", ins(1, show(showSynonyms(hl), hl) ": "))
+temp = temp prettify("original-dictionary-detailed-synonyms-item", show(oSynonyms[oRefs[ref][1]][oRefs[ref][2]][0], il))
+for (k = 1; k < length(oSynonyms[oRefs[ref][1]][oRefs[ref][2]]); k++)
+temp = temp prettify("original-dictionary-detailed-synonyms", ", ")\
+prettify("original-dictionary-detailed-synonyms-item", show(oSynonyms[oRefs[ref][1]][oRefs[ref][2]][k], il))
+r = r RS temp
+}
+}
+}
+}
+if (exists(oSynonymClasses)) {
+r = r RS RS
+r = r m("-- display original dictionary (synonyms)")
+r = r prettify("original-dictionary-synonyms", s(showSynonyms(hl), hl))
+for (i = 0; i < length(oSynonymClasses); i++) {
+r = (i > 0 ? r RS : r) RS prettify("original-dictionary-synonyms-word-class", ins(1, oSynonymClasses[i], hl))
+for (j = 0; j < length(oSynonyms[i]); j++) {
+temp = prettify("original-dictionary-synonyms-synonyms", ins(2, "- "))
+temp = temp prettify("original-dictionary-synonyms-synonyms-item", show(oSynonyms[i][j][0], il))
+for (k = 1; k < length(oSynonyms[i][j]); k++)
+temp = temp prettify("original-dictionary-synonyms-synonyms", ", ")\
+prettify("original-dictionary-synonyms-synonyms-item", show(oSynonyms[i][j][k], il))
+r = r RS temp
+}
+}
+}
+if (exists(oExamples)) {
+r = r RS RS
+r = r m("-- display original dictionary (examples)")
+r = r prettify("original-dictionary-examples", s(showExamples(hl), hl))
+for (i = 0; i < length(oExamples); i++) {
+example = oExamples[i]
+temp = prettify("original-dictionary-examples-example", ins(1, "- "))
+split(example, group, /(|<\/b>)/)
+if (group[3] ~ / [[:punct:].]/)
+group[3] = substr(group[3], 2)
+if (isRTL(il))
+temp = temp show(group[1] group[2] group[3], il)
+else
+temp = temp prettify("original-dictionary-examples-example", group[1])\
+prettify("original-dictionary-examples-original", group[2])\
+prettify("original-dictionary-examples-example", group[3])
+r = (i > 0 ? r RS : r) RS temp
+}
+}
+if (exists(oSeeAlso)) {
+r = r RS RS
+r = r m("-- display original dictionary (see also)")
+r = r prettify("original-dictionary-see-also", s(showSeeAlso(hl), hl))
+temp = ins(1, prettify("original-dictionary-see-also-phrases-item", show(oSeeAlso[0], il)))
+for (k = 1; k < length(oSeeAlso); k++)
+temp = temp prettify("original-dictionary-see-also-phrases", ", ")\
+prettify("original-dictionary-see-also-phrases-item", show(oSeeAlso[k], il))
+r = r RS temp
+}
+}
+if (wShowDictionary) {
+if (r) r = r RS
+r = r m("-- display dictionary entries")
+for (i = 0; i < length(wordClasses); i++) {
+r = (i > 0 ? r RS : r) RS prettify("dictionary-word-class", s(wordClasses[i], hl))
+for (j = 0; j < length(words[i]); j++) {
+word = words[i][j][0]
+article = words[i][j][4]
+if (isRTL(il))
+explanation = join(words[i][j][1], ", ")
+else {
+explanation = prettify("dictionary-explanation-item", words[i][j][1][0])
+for (k = 1; k < length(words[i][j][1]); k++)
+explanation = explanation prettify("dictionary-explanation", ", ")\
+prettify("dictionary-explanation-item", words[i][j][1][k])
+}
+r = r RS prettify("dictionary-word", ins(1, (article ? "(" article ") " : "") word, tl))
+if (isRTL(il))
+r = r RS prettify("dictionary-explanation-item", ins(2, explanation, il))
+else
+r = r RS ins(2, explanation)
+}
+}
+}
+if (wShowAlternatives) {
+if (r) r = r RS RS
+r = r m("-- display alternative translations")
+for (i = 0; i < length(altTranslations); i++) {
+r = (i > 0 ? r RS : r) prettify("alternatives-original", show(segments[i], il))
+if (isRTL(tl)) {
+temp = join(altTranslations[i], ", ")
+r = r RS prettify("alternatives-translations-item", ins(1, temp, tl))
+} else {
+temp = prettify("alternatives-translations-item", altTranslations[i][0])
+for (j = 1; j < length(altTranslations[i]); j++)
+temp = temp prettify("alternatives-translations", ", ")\
+prettify("alternatives-translations-item", altTranslations[i][j])
+r = r RS ins(1, temp)
+}
+}
+}
+if (toSpeech) {
+if (index(showTranslationsOf(hl, "%s"), "%s") > 2) {
+returnPlaylist[0]["text"] = showTranslationsOf(hl)
+returnPlaylist[0]["tl"] = _hl
+returnPlaylist[1]["text"] = join(original)
+returnPlaylist[1]["tl"] = il
+} else {
+returnPlaylist[0]["text"] = join(original)
+returnPlaylist[0]["tl"] = il
+returnPlaylist[1]["text"] = showTranslationsOf(hl)
+returnPlaylist[1]["tl"] = _hl
+}
+returnPlaylist[2]["text"] = translation
+returnPlaylist[2]["tl"] = _tl
+}
+}
+return r
+}
+BEGIN { provides("bing") }
+function bingInit() {
+HttpProtocol = "http://"
+HttpHost = "www.bing.com"
+HttpPort = 80
+}
+function bingSetIG( content, cookie, group, header, isBody,
+url, status, location) {
+url = HttpPathPrefix "/translator"
+header = "GET " url " HTTP/1.1\r\n"\
+"Host: " HttpHost "\r\n"\
+"Connection: close\r\n"
+if (Option["user-agent"])
+header = header "User-Agent: " Option["user-agent"] "\r\n"
+cookie = NULLSTR
+print header |& HttpService
+while ((HttpService |& getline) > 0) {
+match($0, /Set-Cookie: ([^;]*);/, group)
+if (group[1]) {
+cookie = cookie (cookie ? "; " : NULLSTR) group[1]
+}
+if (isBody)
+content = content ? content "\n" $0 : $0
+else if (length($0) <= 1)
+isBody = 1
+else {
+match($0, /^HTTP[^ ]* ([^ ]*)/, group)
+if (RSTART) status = group[1]
+match($0, /^Location: (.*)/, group)
+if (RSTART) location = squeeze(group[1])
+}
+l(sprintf("%4s bytes > %s", length($0), length($0) < 1024 ? $0 : "..."))
+}
+close(HttpService)
+if ((status == "301" || status == "302") && location)
+content = curl(location)
+Cookie = cookie
+match(content, /IG:"([^"]+)"/, group)
+if (group[1]) {
+IG = group[1]
+} else {
+e("[ERROR] Oops! Something went wrong and I can't translate it for you :(")
+exit 1
+}
+}
+function bingTTSUrl(text, tl,
+country, gender, i, group,
+header, content, isBody) {
+gender = "female"
+country = NULLSTR
+split(Option["narrator"], group, ",")
+for (i in group) {
+if (group[i] ~ /^(f(emale)?|w(oman)?)$/)
+gender = "female"
+else if (group[i] ~ /^m(ale|an)?$/)
+gender = "male"
+else
+country = group[i]
+}
+if (country) tl = tl "-" country
+else if (tl == "ar") tl = tl "-EG"
+else if (tl == "da") tl = tl "-DK"
+else if (tl == "de") tl = tl "-DE"
+else if (tl == "en") tl = tl "-US"
+else if (tl == "es") tl = tl "-ES"
+else if (tl == "fi") tl = tl "-FI"
+else if (tl == "fr") tl = tl "-FR"
+else if (tl == "it") tl = tl "-IT"
+else if (tl == "ja") tl = tl "-JP"
+else if (tl == "ko") tl = tl "-KR"
+else if (tl == "nl") tl = tl "-NL"
+else if (tl == "nb") tl = tl "-NO"
+else if (tl == "pl") tl = tl "-PL"
+else if (tl == "pt") tl = tl "-PT"
+else if (tl == "ru") tl = tl "-RU"
+else if (tl == "sv") tl = tl "-SE"
+else if (tl == "yue") ;
+else if (tl == "zh") tl = tl "-CN"
+header = "GET " "/tspeak?"\
+"&language=" tl "&text=" preprocess(text)\
+"&options=" gender "&format=audio%2Fmp3" " HTTP/1.1\r\n"\
+"Host: " HttpHost "\r\n"\
+"Connection: close\r\n"
+if (Option["user-agent"])
+header = header "User-Agent: " Option["user-agent"] "\r\n"
+if (Cookie)
+header = header "Cookie: " Cookie "\r\n"
+content = NULLSTR; isBody = 0
+print header |& HttpService
+while ((HttpService |& getline) > 0) {
+if (isBody)
+content = content ? content "\n" $0 : $0
+else if (length($0) <= 1)
+isBody = 1
+}
+close(HttpService)
+if (!TempFile)
+TempFile = getOutput("mktemp")
+printf("%s", content) > TempFile
+close(TempFile)
+return TempFile
+}
+function bingWebTranslateUrl(uri, sl, tl, hl, _sl, _tl) {
+_sl = sl; _tl = tl
+if (_sl == "bs") _sl = "bs-Latn"
+if (_sl == "zh") _sl = "zh-CHS"
+if (_sl == "zh-CN") _sl = "zh-CHS"
+if (_sl == "zh-TW") _sl = "zh-CHT"
+if (_tl == "bs") _tl = "bs-Latn"
+if (_tl == "zh") _tl = "zh-CHS"
+if (_tl == "zh-CN") _tl = "zh-CHS"
+if (_tl == "zh-TW") _tl = "zh-CHT"
+return "https://www.translatetheweb.com/?" "from=" _sl "&to=" _tl "&a=" uri
+}
+function bingRequestUrl(text, sl, tl, hl) {
+return HttpPathPrefix "/translator/api/Dictionary/Lookup?"\
+"from=" sl "&to=" tl "&text=" preprocess(text)
+}
+function bingPostRequestUrl(text, sl, tl, hl, type) {
+if (type == "lookup")
+return HttpPathPrefix "/tlookupv3"
+else # type == "translate"
+return HttpPathPrefix "/ttranslatev3"
+}
+function bingPostRequestContentType(text, sl, tl, hl, type) {
+return "application/x-www-form-urlencoded"
+}
+function bingPostRequestUserAgent(text, sl, tl, hl, type) {
+return ""
+}
+function bingPostRequestBody(text, sl, tl, hl, type) {
+if (type == "lookup")
+return "&text=" quote(text) "&from=" sl "&to=" tl
+else # type == "translate"
+return "&text=" quote(text) "&fromLang=" sl "&to=" tl
+}
+function bingTranslate(text, sl, tl, hl,
+isVerbose, toSpeech, returnPlaylist, returnIl,
+r,
+content, tokens, ast, dicContent, dicTokens, dicAst,
+_sl, _tl, _hl, il, isPhonetic,
+translation, phonetics, oPhonetics,
+wordClasses, words, wordBackTranslations,
+wShowOriginal, wShowOriginalPhonetics,
+wShowTranslation, wShowTranslationPhonetics,
+wShowLanguages, wShowDictionary,
+i, j, k, group, temp, saveSortedIn) {
+isPhonetic = match(tl, /^@/)
+tl = substr(tl, 1 + isPhonetic)
+if (!getCode(tl)) {
+w("[WARNING] Unknown target language code: " tl)
+} else if (isRTL(tl)) {
+if (!FriBidi)
+w("[WARNING] " getName(tl) " is a right-to-left language, but FriBidi is not found.")
+}
+_sl = getCode(sl); if (!_sl) _sl = sl
+_tl = getCode(tl); if (!_tl) _tl = tl
+_hl = getCode(hl); if (!_hl) _hl = hl
+if (_sl == "auto") _sl = "auto-detect"
+if (_sl == "bs") _sl = "bs-Latn"
+if (_sl == "no") _sl = "nb"
+if (_sl == "pt") _sl = "pt-pt"
+if (_sl == "zh-CN") _sl = "zh-Hans"
+if (_sl == "zh-TW") _sl = "zh-Hant"
+if (_tl == "bs") _tl = "bs-Latn"
+if (_tl == "no") _tl = "nb"
+if (_tl == "pt") _tl = "pt-pt"
+if (_tl == "zh-CN") _tl = "zh-Hans"
+if (_tl == "zh-TW") _tl = "zh-Hant"
+content = postResponse(text, _sl, _tl, _hl, "translate")
+if (content == "") {
+HttpHost = "cn.bing.com"
+if (Option["proxy"]) {
+HttpPathPrefix = HttpProtocol HttpHost
+} else {
+HttpService = "/" "inet" "/tcp/0/" HttpHost "/" HttpPort
+}
+content = postResponse(text, _sl, _tl, _hl, "translate")
+}
+if (Option["dump"])
+return content
+tokenize(tokens, content)
+parseJson(ast, tokens)
+l(content, "content", 1, 1)
+l(tokens, "tokens", 1, 0, 1)
+l(ast, "ast")
+if (!isarray(ast) || !anything(ast)) {
+e("[ERROR] Oops! Something went wrong and I can't translate it for you :(")
+ExitCode = 1
+return
+}
+translation = unparameterize(ast[0 SUBSEP 0 SUBSEP "translations" SUBSEP 0 SUBSEP "text"])
+returnIl[0] = il = _sl == "auto-detect" ?
+unparameterize(ast[0 SUBSEP 0 SUBSEP "detectedLanguage" SUBSEP "language"]) : _sl
+if (Option["verbose"] < -1)
+return il
+if (Option["verbose"] < 0)
+return getList(il)
+wShowTranslationPhonetics = Option["show-translation-phonetics"]
+if (wShowTranslationPhonetics) {
+split(_tl, group, "-")
+phonetics = unparameterize(ast[0 SUBSEP 0 SUBSEP "translations" SUBSEP 0 SUBSEP "transliteration"\
+SUBSEP "text"])
+if (phonetics == translation) phonetics = ""
+}
+if (!isVerbose) {
+r = isPhonetic && phonetics ?
+prettify("brief-translation-phonetics", join(phonetics, " ")) :
+prettify("brief-translation", s(translation, tl))
+} else {
+wShowOriginal = Option["show-original"]
+wShowTranslation = Option["show-translation"]
+wShowLanguages = Option["show-languages"]
+wShowDictionary = Option["show-dictionary"]
+wShowOriginalPhonetics = Option["show-original-phonetics"]
+if (wShowOriginalPhonetics) {
+split(_sl, group, "-")
+delete ast
+content = postResponse(text, il, il, _hl, "translate")
+tokenize(tokens, content)
+parseJson(ast, tokens)
+oPhonetics = unparameterize(ast[0 SUBSEP 0 SUBSEP "translations" SUBSEP 0\
+SUBSEP "transliteration" SUBSEP "text"])
+if (oPhonetics == text) oPhonetics = ""
+}
+if (!oPhonetics) wShowOriginalPhonetics = 0
+if (!phonetics) wShowTranslationPhonetics = 0
+if (wShowOriginal) {
+if (r) r = r RS RS
+r = r m("-- display original text")
+r = r prettify("original", s(text, _sl))
+if (wShowOriginalPhonetics)
+r = r RS prettify("original-phonetics", showPhonetics(join(oPhonetics, " "), _sl))
+}
+if (wShowTranslation) {
+if (r) r = r RS RS
+r = r m("-- display major translation")
+r = r prettify("translation", s(translation, tl))
+if (wShowTranslationPhonetics)
+r = r RS prettify("translation-phonetics", showPhonetics(join(phonetics, " "), tl))
+}
+if (wShowLanguages) {
+if (r) r = r RS RS
+r = r m("-- display source language -> target language")
+temp = Option["fmt-languages"]
+if (!temp) temp = "[ %s -> %t ]"
+split(temp, group, /(%s|%S|%t|%T)/)
+r = r prettify("languages", group[1])
+if (temp ~ /%s/)
+r = r prettify("languages-sl", getDisplay(il))
+if (temp ~ /%S/)
+r = r prettify("languages-sl", getName(il))
+r = r prettify("languages", group[2])
+if (temp ~ /%t/)
+r = r prettify("languages-tl", getDisplay(tl))
+if (temp ~ /%T/)
+r = r prettify("languages-tl", getName(tl))
+r = r prettify("languages", group[3])
+}
+if (wShowDictionary) {
+dicContent = postResponse(text, il, _tl, _hl, "lookup")
+if (dicContent != "") {
+tokenize(dicTokens, dicContent)
+parseJson(dicAst, dicTokens)
+l(dicContent, "dicContent", 1, 1)
+l(dicTokens, "dicTokens", 1, 0, 1)
+l(dicAst, "dicAst")
+saveSortedIn = PROCINFO["sorted_in"]
+PROCINFO["sorted_in"] = "compareByIndexFields"
+for (i in dicAst) {
+if (match(i, "^0" SUBSEP "0" SUBSEP "translations" SUBSEP "([[:digit:]]+)" SUBSEP\
+"posTag$", group))
+wordClasses[group[1]] = tolower(literal(dicAst[i]))
+}
+for (i in dicAst) {
+if (match(i, "^0" SUBSEP "0" SUBSEP "translations" SUBSEP "([[:digit:]]+)" SUBSEP\
+"displayTarget$", group))
+words[wordClasses[group[1]]][group[1]] = literal(dicAst[i])
+if (match(i, "^0" SUBSEP "0" SUBSEP "translations" SUBSEP "([[:digit:]]+)" SUBSEP\
+"backTranslations" SUBSEP "([[:digit:]]+)" SUBSEP "displayText$", group))
+wordBackTranslations[wordClasses[group[1]]][group[1]][group[2]] = literal(dicAst[i])
+}
+PROCINFO["sorted_in"] = saveSortedIn
+if (r) r = r RS
+r = r m("-- display dictionary entries")
+for (i = 0; i < length(words); i++) {
+r = (i > 0 ? r RS : r) RS prettify("dictionary-word-class", s(wordClasses[i], hl))
+for (j in words[wordClasses[i]]) {
+r = r RS prettify("dictionary-word", ins(1, words[wordClasses[i]][j], tl))
+if (isRTL(il))
+explanation = join(wordBackTranslations[wordClasses[i]][j], ", ")
+else {
+explanation = prettify("dictionary-explanation-item",
+wordBackTranslations[wordClasses[i]][j][0])
+for (k = 1; k < length(wordBackTranslations[wordClasses[i]][j]); k++)
+explanation = explanation prettify("dictionary-explanation", ", ")\
+prettify("dictionary-explanation-item",
+wordBackTranslations[wordClasses[i]][j][k])
+}
+if (isRTL(il))
+r = r RS prettify("dictionary-explanation-item", ins(2, explanation, il))
+else
+r = r RS ins(2, explanation)
+}
+}
+}
+}
+}
+if (toSpeech) {
+returnPlaylist[0]["text"] = translation
+returnPlaylist[0]["tl"] = _tl
+}
+return r
+}
+BEGIN { provides("yandex") }
+function genSID( content, group, temp) {
+content = curl("http://translate.yandex.com")
+match(content, /SID:[[:space:]]*'([^']+)'/, group)
+if (group[1]) {
+split(group[1], temp, ".")
+SID = reverse(temp[1]) "." reverse(temp[2]) "." reverse(temp[3])
+} else {
+e("[ERROR] Oops! Something went wrong and I can't translate it for you :(")
+exit 1
+}
+}
+function yandexInit() {
+genSID()
+YandexWebTranslate = "z5h64q92x9.net"
+HttpProtocol = "http://"
+HttpHost = "translate.yandex.net"
+HttpPort = 80
+}
+function yandexRequestUrl(text, sl, tl, hl, group) {
+split(sl, group, "-"); sl = group[1]
+split(tl, group, "-"); tl = group[1]
+return HttpPathPrefix "/api/v1/tr.json/translate?"\
+"id=" SID "-0-0&srv=tr-text"\
+"&text=" preprocess(text) "&lang=" (sl == "auto" ? tl : sl "-" tl)
+}
+function yandexPostRequestBody(text, sl, tl, hl, type) {
+return "text=" quote(text) "&lang=" sl
+}
+function yandexGetDictionaryResponse(text, sl, tl, hl, content, header, isBody, url) {
+split(sl, group, "-"); sl = group[1]
+split(tl, group, "-"); tl = group[1]
+url = "http://dictionary.yandex.net/dicservice.json/lookupMultiple?"\
+"&text=" preprocess(text) "&dict=" sl "-" tl
+content = curl(url)
+return assert(content, "[ERROR] Null response.")
+}
+function yandexTTSUrl(text, tl,
+speaker, emotion, i, group) {
+speaker = NULLSTR
+emotion = NULLSTR
+split(Option["narrator"], group, ",")
+for (i in group) {
+if (group[i] ~ /^(g(ood)?|n(eutral)?|e(vil)?)$/)
+emotion = group[i]
+else if (group[i] ~ /^(f(emale)?|w(oman)?)$/)
+speaker = "alyss"
+else if (group[i] ~ /^m(ale|an)?$/)
+speaker = "zahar"
+else
+speaker = group[i]
+}
+switch (tl) {
+case "ar": tl = "ar_AE"; break
+case "cs": tl = "cs_CZ"; break
+case "da": tl = "da_DK"; break
+case "de": tl = "de_DE"; break
+case "el": tl = "el_GR"; break
+case "en": tl = "en_GB"; break
+case "es": tl = "es_ES"; break
+case "fi": tl = "fi_FI"; break
+case "fr": tl = "fr_FR"; break
+case "it": tl = "it_IT"; break
+case "nl": tl = "nl_NL"; break
+case "no": tl = "no_NO"; break
+case "pl": tl = "pl_PL"; break
+case "pt": tl = "pt_PT"; break
+case "ru": tl = "ru_RU"; break
+case "sv": tl = "sv_SE"; break
+case "tr": tl = "tr_TR"; break
+default: tl = NULLSTR
+}
+return HttpProtocol "tts.voicetech.yandex.net" "/tts?"\
+"text=" preprocess(text) (tl ? "&lang=" tl : tl)\
+(speaker ? "&speaker=" speaker : speaker)\
+(emotion ? "&emotion=" emotion : emotion)\
+"&format=mp3" "&quality=hi"
+}
+function yandexWebTranslateUrl(uri, sl, tl, hl) {
+gsub(/:\/\//, "/", uri)
+return HttpProtocol YandexWebTranslate "/proxy_u/"\
+(sl == "auto" ? tl : sl "-" tl)"/" uri
+}
+function yandexTranslate(text, sl, tl, hl,
+isVerbose, toSpeech, returnPlaylist, returnIl,
+r,
+content, tokens, ast,
+_sl, _tl, _hl, il, isPhonetic,
+translation,
+wShowOriginal, wShowTranslation, wShowLanguages,
+wShowDictionary, dicContent, dicTokens, dicAst,
+i, syn, mean,
+group, temp) {
+isPhonetic = match(tl, /^@/)
+tl = substr(tl, 1 + isPhonetic)
+if (!getCode(tl)) {
+w("[WARNING] Unknown target language code: " tl)
+} else if (isRTL(tl)) {
+if (!FriBidi)
+w("[WARNING] " getName(tl) " is a right-to-left language, but FriBidi is not found.")
+}
+_sl = getCode(sl); if (!_sl) _sl = sl
+_tl = getCode(tl); if (!_tl) _tl = tl
+_hl = getCode(hl); if (!_hl) _hl = hl
+content = getResponse(text, _sl, _tl, _hl)
+if (Option["dump"])
+return content
+tokenize(tokens, content)
+parseJson(ast, tokens)
+l(content, "content", 1, 1)
+l(tokens, "tokens", 1, 0, 1)
+l(ast, "ast")
+if (!isarray(ast) || !anything(ast)) {
+e("[ERROR] Oops! Something went wrong and I can't translate it for you :(")
+ExitCode = 1
+return
+}
+if (ast[0 SUBSEP "code"] != "200") {
+e("[ERROR] " unparameterize(ast[0 SUBSEP "message"]))
+ExitCode = 1
+return
+}
+translation = unparameterize(ast[0 SUBSEP "text" SUBSEP 0])
+wShowTranslationPhonetics = Option["show-translation-phonetics"]
+if (wShowTranslationPhonetics && _tl != "emj") {
+split(_tl, group, "-")
+data = yandexPostRequestBody(translation, group[1], group[1], _hl, "translit")
+content = curlPost("https://translate.yandex.net/translit/translit", data)
+phonetics = (content ~ /not supported$/) ? "" : unparameterize(content)
+}
+split(unparameterize(ast[0 SUBSEP "lang"]), group, "-")
+returnIl[0] = il = group[1]
+if (Option["verbose"] < -1)
+return il
+else if (Option["verbose"] < 0)
+return getList(il)
+if (!isVerbose) {
+r = isPhonetic && phonetics ?
+prettify("brief-translation-phonetics", join(phonetics, " ")) :
+prettify("brief-translation", s(translation, tl))
+} else {
+wShowOriginal = Option["show-original"]
+wShowTranslation = Option["show-translation"]
+wShowLanguages = Option["show-languages"]
+wShowDictionary = Option["show-dictionary"]
+wShowOriginalPhonetics = Option["show-original-phonetics"]
+if (wShowTranslationPhonetics && il != "emj") {
+split(il, group, "-")
+data = yandexPostRequestBody(text, group[1], group[1], _hl, "translit")
+content = curlPost("https://translate.yandex.net/translit/translit", data)
+oPhonetics = (content ~ /not supported$/) ? "" : unparameterize(content)
+}
+if (!oPhonetics) wShowOriginalPhonetics = 0
+if (!phonetics) wShowTranslationPhonetics = 0
+if (wShowOriginal) {
+if (r) r = r RS RS
+r = r m("-- display original text & phonetics")
+r = r prettify("original", s(text, il))
+if (wShowOriginalPhonetics)
+r = r RS prettify("original-phonetics", showPhonetics(join(oPhonetics, " "), il))
+}
+if (wShowTranslation) {
+if (r) r = r RS RS
+r = r m("-- display major translation")
+r = r prettify("translation", s(translation, tl))
+if (wShowTranslationPhonetics)
+r = r RS prettify("translation-phonetics", showPhonetics(join(phonetics, " "), tl))
+}
+if (wShowLanguages) {
+if (r) r = r RS RS
+r = r m("-- display source language -> target language")
+temp = Option["fmt-languages"]
+if (!temp) temp = "[ %s -> %t ]"
+split(temp, group, /(%s|%S|%t|%T)/)
+r = r prettify("languages", group[1])
+if (temp ~ /%s/)
+r = r prettify("languages-sl", getDisplay(il))
+if (temp ~ /%S/)
+r = r prettify("languages-sl", getName(il))
+r = r prettify("languages", group[2])
+if (temp ~ /%t/)
+r = r prettify("languages-tl", getDisplay(tl))
+if (temp ~ /%T/)
+r = r prettify("languages-tl", getName(tl))
+r = r prettify("languages", group[3])
+}
+if (wShowDictionary && false) {
+dicContent = yandexGetDictionaryResponse(text, il, _tl, _hl)
+tokenize(dicTokens, dicContent)
+parseJson(dicAst, dicTokens)
+if (anything(dicAst)) {
+if (r) r = r RS
+r = r m("-- display dictionary entries")
+saveSortedIn = PROCINFO["sorted_in"]
+PROCINFO["sorted_in"] = "@ind_num_asc"
+for (i in dicAst) {
+if (i ~ "^0" SUBSEP "def" SUBSEP "[[:digit:]]+" SUBSEP\
+"pos$") {
+r = r RS prettify("dictionary-word-class", s((literal(dicAst[i])), hl))
+syn = mean = ""
+}
+if (i ~ "^0" SUBSEP "def" SUBSEP "[[:digit:]]+" SUBSEP\
+"tr" SUBSEP "[[:digit:]]+" SUBSEP\
+"mean" SUBSEP "[[:digit:]]+" SUBSEP "text") {
+if (mean) {
+mean = mean prettify("dictionary-explanation", ", ")\
+prettify("dictionary-explanation-item", s((literal(dicAst[i])), sl))
+} else {
+mean = prettify("dictionary-explanation-item", s((literal(dicAst[i])), sl))
+}
+}
+if (i ~ "^0" SUBSEP "def" SUBSEP "[[:digit:]]+" SUBSEP\
+"tr" SUBSEP "[[:digit:]]+" SUBSEP\
+"syn" SUBSEP "[[:digit:]]+" SUBSEP "text") {
+if (syn) {
+syn = syn prettify("dictionary-explanation", ", ")\
+prettify("dictionary-word", s((literal(dicAst[i])), il))
+} else {
+syn = prettify("dictionary-word", s((literal(dicAst[i])), il))
+}
+}
+if (i ~ "^0" SUBSEP "def" SUBSEP "[[:digit:]]+" SUBSEP\
+"tr" SUBSEP "[[:digit:]]+" SUBSEP "text$") {
+text = prettify("dictionary-word", s((literal(dicAst[i])), il))
+if (syn) {
+r = r RS ins(1, text prettify("dictionary-explanation", ", ") syn)
+} else {
+r = r RS ins(1, text)
+}
+r = r RS ins(2, mean)
+syn = mean = ""
+}
+}
+PROCINFO["sorted_in"] = saveSortedIn
+}
+}
+}
+if (toSpeech) {
+returnPlaylist[0]["text"] = translation
+returnPlaylist[0]["tl"] = _tl
+}
+return r
+}
+BEGIN { provides("apertium") }
+function apertiumInit() {
+HttpProtocol = "http://"
+HttpHost = "www.apertium.org"
+HttpPort = 80
+}
+function apertiumRequestUrl(text, sl, tl, hl) {
+return HttpPathPrefix "/apy/translate?"\
+"langpair=" preprocess(sl) "|" preprocess(tl)\
+"&q=" preprocess(text)
+}
+function apertiumTTSUrl(text, tl, narrator) {
+}
+function apertiumWebTranslateUrl(uri, sl, tl, hl) {
+}
+function apertiumTranslate(text, sl, tl, hl,
+isVerbose, toSpeech, returnPlaylist, returnIl,
+r,
+content, tokens, ast,
+_sl, _tl, _hl, il,
+translation,
+wShowOriginal, wShowTranslation, wShowLanguages,
+group, temp) {
+if (!getCode(tl)) {
+w("[WARNING] Unknown target language code: " tl)
+} else if (isRTL(tl)) {
+if (!FriBidi)
+w("[WARNING] " getName(tl) " is a right-to-left language, but FriBidi is not found.")
+}
+_sl = getCode(sl); if (!_sl) _sl = sl
+_tl = getCode(tl); if (!_tl) _tl = tl
+_hl = getCode(hl); if (!_hl) _hl = hl
+_sl = "auto" == _sl ? "en" : _sl
+content = getResponse(text, _sl, _tl, _hl)
+if (Option["dump"])
+return content
+tokenize(tokens, content)
+parseJson(ast, tokens)
+l(content, "content", 1, 1)
+l(tokens, "tokens", 1, 0, 1)
+l(ast, "ast")
+if (!isarray(ast) || !anything(ast)) {
+e("[ERROR] Oops! Something went wrong and I can't translate it for you :(")
+ExitCode = 1
+return
+}
+translation = uprintf(unquote(unparameterize(ast[0 SUBSEP "responseData" SUBSEP "translatedText"])))
+returnIl[0] = il = _sl
+if (Option["verbose"] < -1)
+return il
+else if (Option["verbose"] < 0)
+return getList(il)
+if (!isVerbose) {
+r = translation
+} else {
+wShowOriginal = Option["show-original"]
+wShowTranslation = Option["show-translation"]
+wShowLanguages = Option["show-languages"]
+if (wShowOriginal) {
+if (r) r = r RS RS
+r = r m("-- display original text")
+r = r prettify("original", s(text, il))
+}
+if (wShowTranslation) {
+if (r) r = r RS RS
+r = r m("-- display major translation")
+r = r prettify("translation", s(translation, tl))
+}
+if (wShowLanguages) {
+if (r) r = r RS RS
+r = r m("-- display source language -> target language")
+temp = Option["fmt-languages"]
+if (!temp) temp = "[ %s -> %t ]"
+split(temp, group, /(%s|%S|%t|%T)/)
+r = r prettify("languages", group[1])
+if (temp ~ /%s/)
+r = r prettify("languages-sl", getDisplay(il))
+if (temp ~ /%S/)
+r = r prettify("languages-sl", getName(il))
+r = r prettify("languages", group[2])
+if (temp ~ /%t/)
+r = r prettify("languages-tl", getDisplay(tl))
+if (temp ~ /%T/)
+r = r prettify("languages-tl", getName(tl))
+r = r prettify("languages", group[3])
+}
+}
+if (toSpeech) {
+returnPlaylist[0]["text"] = translation
+returnPlaylist[0]["tl"] = _tl
+}
+return r
+}
+BEGIN {
+provides("spell")
+provides("aspell")
+provides("hunspell")
+}
+function spellInit() {
+Ispell = detectProgram("aspell", "--version") ? "aspell" :
+(detectProgram("hunspell", "--version") ? "hunspell" : "")
+if (!Ispell) {
+e("[ERROR] Spell checker (aspell or hunspell) not found.")
+exit 1
+}
+}
+function aspellInit() {
+if (!(Ispell = detectProgram("aspell", "--version") ? "aspell" : "")) {
+e("[ERROR] Spell checker (aspell) not found.")
+exit 1
+}
+}
+function hunspellInit() {
+if (!(Ispell = detectProgram("hunspell", "--version") ? "hunspell" : "")) {
+e("[ERROR] Spell checker (hunspell) not found.")
+exit 1
+}
+}
+function spellTranslate(text, sl, tl, hl,
+isVerbose, toSpeech, returnPlaylist, returnIl,
+args, i, j, r, line, group, word, sug) {
+args = " -a" (sl != "auto" ? " -d " sl : "")
+if (system("echo" PIPE Ispell args SUPOUT SUPERR)) {
+e("[ERROR] No dictionary for language: " sl)
+exit 1
+}
+i = 1
+r = ""
+while ((("echo " parameterize(text) PIPE Ispell args SUPERR) |& getline line) > 0) {
+match(line,
+/^& (.*) [[:digit:]]+ [[:digit:]]+: ([^,]+)(, ([^,]+))?(, ([^,]+))?/,
+group)
+if (RSTART) {
+ExitCode = 1
+word = group[1]
+sug = "[" group[2]
+if (group[4]) sug = sug "|" group[4]
+if (group[6]) sug = sug "|" group[6]
+sug = sug "]"
+j = i + index(substr(text, i), word) - 1
+r = r substr(text, i, j - i)
+r = r ansi("bold", ansi("red", word)) ansi("yellow", sug)
+i = j + length(word)
+}
+}
+r = r substr(text, i)
+return r
+}
+function aspellTranslate(text, sl, tl, hl,
+isVerbose, toSpeech, returnPlaylist, returnIl) {
+return spellTranslate(text, sl, tl, hl)
+}
+function hunspellTranslate(text, sl, tl, hl,
+isVerbose, toSpeech, returnPlaylist, returnIl) {
+return spellTranslate(text, sl, tl, hl)
+}
+function spellTTSUrl(text, tl, narrator) {
+e("[ERROR] Spell checker does not support TTS.")
+ExitCode = 1
+return
+}
+function aspellTTSUrl(text, tl, narrator) {
+return spellTTSUrl(text, tl)
+}
+function hunspellTTSUrl(text, tl, narrator) {
+return spellTTSUrl(text, tl)
+}
+function spellWebTranslateUrl(uri, sl, tl, hl) {
+e("[ERROR] Spell checker does not support web translation.")
+ExitCode = 1
+return
+}
+function aspellWebTranslateUrl(uri, sl, tl, hl) {
+return spellWebTranslateUrl(uri, sl, tl, hl)
+}
+function hunspellWebTranslateUrl(uri, sl, tl, hl) {
+return spellWebTranslateUrl(uri, sl, tl, hl)
+}
+function loadOptions(script, i, j, tokens, name, value) {
+tokenize(tokens, script)
+for (i in tokens) {
+if (tokens[i] ~ /^:/) {
+name = substr(tokens[i], 2)
+value = tokens[i + 1]
+if (value ~ /^[+-]?((0|[1-9][0-9]*)|[.][0-9]*|(0|[1-9][0-9]*)[.][0-9]*)([Ee][+-]?[0-9]+)?$/) {
+delete Option[name]
+Option[name] = value
+} else if (value == "false" || value == "true") {
+delete Option[name]
+Option[name] = yn(value)
+} else if (value ~ /^".*"$/) {
+delete Option[name]
+Option[name] = literal(value)
+} else if (value == "[") {
+delete Option[name]
+for (j = 1; tokens[i + j + 1] && tokens[i + j + 1] != "]"; j++) {
+if (tokens[i + j + 1] ~ /^".*"$/)
+Option[name][j] = literal(tokens[i + j + 1])
+else {
+e("[ERROR] Malformed configuration.")
+return
+}
+}
+} else {
+e("[ERROR] Malformed configuration.")
+return
+}
+}
+}
+}
+function upgrade( i, newVersion, registry, tokens) {
+RegistryIndex = "https://raw.githubusercontent.com/soimort/translate-shell/registry/index.trans"
+registry = curl(RegistryIndex)
+if (!registry) {
+e("[ERROR] Failed to check for upgrade.")
+ExitCode = 1
+return
+}
+tokenize(tokens, registry)
+for (i in tokens)
+if (tokens[i] == ":translate-shell")
+newVersion = literal(tokens[i + 1])
+if (newerVersion(newVersion, Version)) {
+w("Current version: \t" Version)
+w("New version available: \t" newVersion)
+w("Download from: \t" "https://www.soimort.org/translate-shell/trans")
+} else {
+w("Current version: \t" Version)
+w("Already up-to-date.")
+}
+}
+function welcome() {
+if (Option["fmt-welcome-message"])
+print prettify("welcome-message", Option["fmt-welcome-message"]) > STDERR
+if (Option["fmt-welcome-submessage"])
+print prettify("welcome-submessage", Option["fmt-welcome-submessage"]) > STDERR
+}
+function prompt( i, p, temp) {
+p = Option["fmt-prompt"]
+if (p ~ /%a/) gsub(/%a/, strftime("%a"), p)
+if (p ~ /%A/) gsub(/%A/, strftime("%A"), p)
+if (p ~ /%b/) gsub(/%b/, strftime("%b"), p)
+if (p ~ /%B/) gsub(/%B/, strftime("%B"), p)
+if (p ~ /%c/) gsub(/%c/, strftime("%c"), p)
+if (p ~ /%C/) gsub(/%C/, strftime("%C"), p)
+if (p ~ /%d/) gsub(/%d/, strftime("%d"), p)
+if (p ~ /%D/) gsub(/%D/, strftime("%D"), p)
+if (p ~ /%e/) gsub(/%e/, strftime("%e"), p)
+if (p ~ /%F/) gsub(/%F/, strftime("%F"), p)
+if (p ~ /%g/) gsub(/%g/, strftime("%g"), p)
+if (p ~ /%G/) gsub(/%G/, strftime("%G"), p)
+if (p ~ /%h/) gsub(/%h/, strftime("%h"), p)
+if (p ~ /%H/) gsub(/%H/, strftime("%H"), p)
+if (p ~ /%I/) gsub(/%I/, strftime("%I"), p)
+if (p ~ /%j/) gsub(/%j/, strftime("%j"), p)
+if (p ~ /%m/) gsub(/%m/, strftime("%m"), p)
+if (p ~ /%M/) gsub(/%M/, strftime("%M"), p)
+if (p ~ /%n/) gsub(/%n/, strftime("%n"), p)
+if (p ~ /%p/) gsub(/%p/, strftime("%p"), p)
+if (p ~ /%r/) gsub(/%r/, strftime("%r"), p)
+if (p ~ /%R/) gsub(/%R/, strftime("%R"), p)
+if (p ~ /%u/) gsub(/%u/, strftime("%u"), p)
+if (p ~ /%U/) gsub(/%U/, strftime("%U"), p)
+if (p ~ /%V/) gsub(/%V/, strftime("%V"), p)
+if (p ~ /%w/) gsub(/%w/, strftime("%w"), p)
+if (p ~ /%W/) gsub(/%W/, strftime("%W"), p)
+if (p ~ /%x/) gsub(/%x/, strftime("%x"), p)
+if (p ~ /%X/) gsub(/%X/, strftime("%X"), p)
+if (p ~ /%y/) gsub(/%y/, strftime("%y"), p)
+if (p ~ /%Y/) gsub(/%Y/, strftime("%Y"), p)
+if (p ~ /%z/) gsub(/%z/, strftime("%z"), p)
+if (p ~ /%Z/) gsub(/%Z/, strftime("%Z"), p)
+if (p ~ /%_/)
+gsub(/%_/, showTranslationsOf(Option["hl"]), p)
+if (p ~ /%l/)
+gsub(/%l/, getDisplay(Option["hl"]), p)
+if (p ~ /%L/)
+gsub(/%L/, getName(Option["hl"]), p)
+if (p ~ /%S/) {
+temp = getName(Option["sls"][1])
+for (i = 2; i <= length(Option["sls"]); i++)
+temp = temp "+" getName(Option["sls"][i])
+gsub(/%S/, temp, p)
+}
+if (p ~ /%t/) {
+temp = getDisplay(Option["tl"][1])
+for (i = 2; i <= length(Option["tl"]); i++)
+temp = temp "+" getDisplay(Option["tl"][i])
+gsub(/%t/, temp, p)
+}
+if (p ~ /%T/) {
+temp = getName(Option["tl"][1])
+for (i = 2; i <= length(Option["tl"]); i++)
+temp = temp "+" getName(Option["tl"][i])
+gsub(/%T/, temp, p)
+}
+if (p ~ /%,/) {
+temp = getDisplay(Option["tl"][1])
+for (i = 2; i <= length(Option["tl"]); i++)
+temp = temp "," getDisplay(Option["tl"][i])
+gsub(/%,/, temp, p)
+}
+if (p ~ /%) {
+temp = getName(Option["tl"][1])
+for (i = 2; i <= length(Option["tl"]); i++)
+temp = temp "," getName(Option["tl"][i])
+gsub(/%, temp, p)
+}
+if (p ~ /%\//) {
+temp = getDisplay(Option["tl"][1])
+for (i = 2; i <= length(Option["tl"]); i++)
+temp = temp "/" getDisplay(Option["tl"][i])
+gsub(/%\//, temp, p)
+}
+if (p ~ /%\?/) {
+temp = getName(Option["tl"][1])
+for (i = 2; i <= length(Option["tl"]); i++)
+temp = temp "/" getName(Option["tl"][i])
+gsub(/%\?/, temp, p)
+}
+temp = getDisplay(Option["sls"][1])
+for (i = 2; i <= length(Option["sls"]); i++)
+temp = temp "+" getDisplay(Option["sls"][i])
+printf(prettify("prompt", p), temp) > STDERR
+}
+function repl(line, command, group, name, i, value, words) {
+split(line, words, " ")
+command = words[1]
+if (command ~ /^:(q|quit)$/) {
+exit
+} else if (command ~ /^:set$/) {
+name = words[2]
+value = words[3]
+Option[name] = value
+} else if (command ~ /^:show$/) {
+name = words[2]
+print prettify("welcome-submessage", toString(Option[name], 1, 0, 1))
+} else if (command ~ /^:engine$/) {
+value = words[2]
+Option["engine"] = value
+initHttpService()
+} else {
+match(command, /^[{(\[]?((@?[[:alpha:]][[:alpha:]][[:alpha:]]?(-[[:alpha:]][[:alpha:]][[:alpha:]]?[[:alpha:]]?)?\+)*(@?[[:alpha:]][[:alpha:]][[:alpha:]]?(-[[:alpha:]][[:alpha:]][[:alpha:]]?[[:alpha:]]?)?)?)?(:|=)((@?[[:alpha:]][[:alpha:]][[:alpha:]]?(-[[:alpha:]][[:alpha:]][[:alpha:]]?[[:alpha:]]?)?\+)*(@?[[:alpha:]][[:alpha:]][[:alpha:]]?(-[[:alpha:]][[:alpha:]][[:alpha:]]?[[:alpha:]]?)?)?)[})\]]?$/, group)
+if (RSTART) {
+if (group[1]) {
+split(group[1], Option["sls"], "+")
+Option["sl"] = Option["sls"][1]
+}
+if (group[7]) split(group[7], Option["tl"], "+")
+line = words[2]
+for (i = 3; i <= length(words); i++)
+line = line " " words[i]
+}
+if (line) {
+translates(line)
+if (Option["verbose"]) printf RS
+}
+}
+prompt()
+}
+function init() {
+initGawk()
+initBiDiTerm()
+initBiDi()
+initLocale()
+initLocaleAlias()
+initUserLang()
+RS = "\n"
+ExitCode = 0
+Option["debug"] = 0
+Option["engine"] = "google"
+Option["verbose"] = 1
+Option["show-original"] = 1
+Option["show-original-phonetics"] = 1
+Option["show-translation"] = 1
+Option["show-translation-phonetics"] = 1
+Option["show-prompt-message"] = 1
+Option["show-languages"] = 1
+Option["show-original-dictionary"] = 0
+Option["show-dictionary"] = 1
+Option["show-alternatives"] = 1
+Option["width"] = ENVIRON["COLUMNS"] ? ENVIRON["COLUMNS"] - 2 : 0
+Option["indent"] = 4
+Option["no-ansi"] = 0
+Option["no-autocorrect"] = 0
+Option["no-bidi"] = 0
+Option["force-bidi"] = 0
+Option["no-warn"] = 0
+Option["theme"] = "default"
+Option["dump"] = 0
+Option["play"] = 0
+Option["narrator"] = "female"
+Option["player"] = ENVIRON["PLAYER"]
+Option["no-translate"] = 0
+Option["download-audio"] = 0
+Option["download-audio-as"] = NULLSTR
+Option["view"] = 0
+Option["pager"] = ENVIRON["PAGER"]
+Option["browser"] = ENVIRON["BROWSER"]
+Option["proxy"] = ENVIRON["HTTP_PROXY"] ? ENVIRON["HTTP_PROXY"] : ENVIRON["http_proxy"]
+Option["user-agent"] = ENVIRON["USER_AGENT"] ? ENVIRON["USER_AGENT"] :
+"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) "\
+"AppleWebKit/537.36 (KHTML, like Gecko) "\
+"Chrome/81.0.4044.138 "\
+"Safari/537.36"
+Option["ip-version"] = 0
+Option["no-rlwrap"] = 0
+Option["interactive"] = 0
+Option["emacs"] = 0
+Option["input"] = NULLSTR
+Option["output"] = STDOUT
+Option["hl"] = ENVIRON["HOME_LANG"] ? ENVIRON["HOME_LANG"] : UserLang
+Option["sl"] = ENVIRON["SOURCE_LANG"] ? ENVIRON["SOURCE_LANG"] : "auto"
+Option["sls"][1] = Option["sl"]
+Option["tl"][1] = ENVIRON["TARGET_LANG"] ? ENVIRON["TARGET_LANG"] : UserLang
+Option["join-sentence"] = 0
+}
+function initScript( file, line, script, temp) {
+file = ".trans"
+if (!fileExists(file)) {
+file = ENVIRON["HOME"] "/.translate-shell/init.trans"
+if (!fileExists(file)) {
+file = ENVIRON["XDG_CONFIG_HOME"] "/translate-shell/init.trans"
+if (!fileExists(file)) {
+file = ENVIRON["HOME"] "/.config/translate-shell/init.trans"
+if (!fileExists(file)) {
+file = "/etc/translate-shell"
+if (!fileExists(file)) return
+}
+}
+}
+}
+InitScript = file
+script = NULLSTR
+while (getline line < InitScript)
+script = script "\n" line
+loadOptions(script)
+if (!isarray(Option["tl"])) {
+temp = Option["tl"]
+delete Option["tl"]
+Option["tl"][1] = temp
+}
+}
+function initMisc( command, group, temp) {
+initHttpService()
+if (!Option["width"] && detectProgram("tput", "-V")) {
+command = "tput cols" SUPERR
+command | getline temp
+close(command)
+Option["width"] = temp > 5 ? temp - 2 : 64
+}
+if (Option["no-ansi"])
+delete AnsiCode
+if (Option["no-bidi"] || BiDiTerm == "mlterm")
+BiDi = BiDiNoPad = NULLSTR
+else if (!Option["force-bidi"] && BiDiTerm == "konsole") {
+BiDiNoPad = NULLSTR
+BiDi = "sed \"s/'/\\\\\\'/\" | xargs -0 printf '%%%ss'"
+}
+initLocaleDisplay()
+if (Option["no-warn"])
+STDERR = "/dev/null"
+if (Option["play"]) {
+if (!Option["player"]) {
+initAudioPlayer()
+Option["player"] = AudioPlayer ? AudioPlayer : Option["player"]
+if (!Option["player"])
+initSpeechSynthesizer()
+}
+if (!Option["player"] && !SpeechSynthesizer) {
+w("[WARNING] No available audio player or speech synthesizer.")
+Option["play"] = 0
+}
+}
+if (Option["view"]) {
+if (!Option["pager"]) {
+initPager()
+Option["pager"] = Pager
+}
+if (!Option["pager"]) {
+w("[WARNING] No available terminal pager.")
+Option["view"] = 0
+}
+}
+if (!Option["browser"]) {
+Platform = detectProgram("uname", "-s", 1)
+Option["browser"] = Platform == "Darwin" ? "open" : "xdg-open"
+}
+}
+BEGIN {
+init()
+if (!(belongsTo("-no-init", ARGV) || belongsTo("--no-init", ARGV)))
+initScript()
+pos = 0
+noargc = 0
+while (ARGV[++pos]) {
+match(ARGV[pos], /^--?(V|vers(i(on?)?)?)$/)
+if (RSTART) {
+InfoOnly = "version"
+continue
+}
+match(ARGV[pos], /^--?(H|h(e(lp?)?)?)$/)
+if (RSTART) {
+InfoOnly = "help"
+continue
+}
+match(ARGV[pos], /^--?(M|m(a(n(u(al?)?)?)?)?)$/)
+if (RSTART) {
+InfoOnly = "manual"
+continue
+}
+match(ARGV[pos], /^--?(T|ref(e(r(e(n(ce?)?)?)?)?)?)$/)
+if (RSTART) {
+InfoOnly = "reference"
+continue
+}
+match(ARGV[pos], /^--?r$/)
+if (RSTART) {
+w("[WARNING] Option '-r' has been deprecated since version 0.9.\n"\
+" Use option '-T' or '-reference' instead.")
+exit 1
+}
+match(ARGV[pos], /^--?(R|reference-e(n(g(l(i(sh?)?)?)?)?)?)$/)
+if (RSTART) {
+InfoOnly = "reference-english"
+continue
+}
+match(ARGV[pos], /^--?(L|list)(=(.*)?)?$/, group)
+if (RSTART) {
+InfoOnly = "list"
+if (group[2]) {
+if (group[3]) split(group[3], Option["tl"], "+")
+} else
+split(ARGV[++pos], Option["tl"], "+")
+continue
+}
+match(ARGV[pos], /^--?(S|list-e(n(g(i(n(es?)?)?)?)?)?)$/)
+if (RSTART) {
+InfoOnly = "list-engines"
+continue
+}
+match(ARGV[pos], /^--?(U|upgrade)$/)
+if (RSTART) {
+InfoOnly = "upgrade"
+continue
+}
+match(ARGV[pos], /^--?(N|nothing)$/)
+if (RSTART) {
+InfoOnly = "nothing"
+continue
+}
+match(ARGV[pos], /^--?(e|engine)(=(.*)?)?$/, group)
+if (RSTART) {
+Option["engine"] = group[2] ?
+(group[3] ? group[3] : Option["engine"]) :
+ARGV[++pos]
+continue
+}
+match(ARGV[pos], /^\/(.*)$/, group)
+if (RSTART) {
+Option["engine"] = group[1]
+continue
+}
+match(ARGV[pos], /^--?verbose$/)
+if (RSTART) {
+Option["verbose"] = 1
+continue
+}
+match(ARGV[pos], /^--?b(r(i(ef?)?)?)?$/)
+if (RSTART) {
+Option["verbose"] = 0
+continue
+}
+match(ARGV[pos], /^--?d(i(c(t(i(o(n(a(ry?)?)?)?)?)?)?)?)?$/)
+if (RSTART) {
+Option["show-original-dictionary"] = 1
+Option["show-dictionary"] = 0
+Option["show-alternatives"] = 0
+continue
+}
+match(ARGV[pos], /^--?id(e(n(t(i(fy?)?)?)?)?)?$/)
+if (RSTART) {
+Option["verbose"] = Option["verbose"] - 2
+continue
+}
+match(ARGV[pos], /^--?show-original(=(.*)?)?$/, group)
+if (RSTART) {
+Option["show-original"] = yn(group[1] ? group[2] : ARGV[++pos])
+continue
+}
+match(ARGV[pos], /^--?show-original-phonetics(=(.*)?)?$/, group)
+if (RSTART) {
+Option["show-original-phonetics"] = yn(group[1] ? group[2] : ARGV[++pos])
+continue
+}
+match(ARGV[pos], /^--?show-translation(=(.*)?)?$/, group)
+if (RSTART) {
+Option["show-translation"] = yn(group[1] ? group[2] : ARGV[++pos])
+continue
+}
+match(ARGV[pos], /^--?show-translation-phonetics(=(.*)?)?$/, group)
+if (RSTART) {
+Option["show-translation-phonetics"] = yn(group[1] ? group[2] : ARGV[++pos])
+continue
+}
+match(ARGV[pos], /^--?show-prompt-message(=(.*)?)?$/, group)
+if (RSTART) {
+Option["show-prompt-message"] = yn(group[1] ? group[2] : ARGV[++pos])
+continue
+}
+match(ARGV[pos], /^--?show-languages(=(.*)?)?$/, group)
+if (RSTART) {
+Option["show-languages"] = yn(group[1] ? group[2] : ARGV[++pos])
+continue
+}
+match(ARGV[pos], /^--?show-original-dictionary(=(.*)?)?$/, group)
+if (RSTART) {
+Option["show-original-dictionary"] = yn(group[1] ? group[2] : ARGV[++pos])
+continue
+}
+match(ARGV[pos], /^--?show-dictionary(=(.*)?)?$/, group)
+if (RSTART) {
+Option["show-dictionary"] = yn(group[1] ? group[2] : ARGV[++pos])
+continue
+}
+match(ARGV[pos], /^--?show-alternatives(=(.*)?)?$/, group)
+if (RSTART) {
+Option["show-alternatives"] = yn(group[1] ? group[2] : ARGV[++pos])
+continue
+}
+match(ARGV[pos], /^--?w(i(d(th?)?)?)?(=(.*)?)?$/, group)
+if (RSTART) {
+Option["width"] = group[4] ?
+(group[5] ? group[5] : Option["width"]) :
+ARGV[++pos]
+continue
+}
+match(ARGV[pos], /^--?indent(=(.*)?)?$/, group)
+if (RSTART) {
+Option["indent"] = group[1] ?
+(group[2] ? group[2] : Option["indent"]) :
+ARGV[++pos]
+continue
+}
+match(ARGV[pos], /^--?theme(=(.*)?)?$/, group)
+if (RSTART) {
+Option["theme"] = group[1] ?
+(group[2] ? group[2] : Option["theme"]) :
+ARGV[++pos]
+continue
+}
+match(ARGV[pos], /^--?no-theme$/)
+if (RSTART) {
+Option["theme"] = NULLSTR
+continue
+}
+match(ARGV[pos], /^--?no-ansi$/)
+if (RSTART) {
+Option["no-ansi"] = 1
+continue
+}
+match(ARGV[pos], /^--?no-auto(correct)?$/)
+if (RSTART) {
+Option["no-autocorrect"] = 1
+continue
+}
+match(ARGV[pos], /^--?no-bidi/)
+if (RSTART) {
+Option["no-bidi"] = 1
+continue
+}
+match(ARGV[pos], /^--?bidi/)
+if (RSTART) {
+Option["force-bidi"] = 1
+continue
+}
+match(ARGV[pos], /^--?no-warn/)
+if (RSTART) {
+Option["no-warn"] = 1
+continue
+}
+match(ARGV[pos], /^--?dump/)
+if (RSTART) {
+Option["dump"] = 1
+continue
+}
+match(ARGV[pos], /^--?p(l(ay?)?)?$/)
+if (RSTART) {
+Option["play"] = 1
+continue
+}
+match(ARGV[pos], /^--?sp(e(ak?)?)?$/)
+if (RSTART) {
+Option["play"] = 2
+continue
+}
+match(ARGV[pos], /^--?(n|narrator)(=(.*)?)?$/, group)
+if (RSTART) {
+if (!Option["play"]) Option["play"] = 1
+Option["narrator"] = group[2] ?
+(group[3] ? group[3] : Option["narrator"]) :
+ARGV[++pos]
+continue
+}
+match(ARGV[pos], /^--?player(=(.*)?)?$/, group)
+if (RSTART) {
+if (!Option["play"]) Option["play"] = 1
+Option["player"] = group[1] ?
+(group[2] ? group[2] : Option["player"]) :
+ARGV[++pos]
+continue
+}
+match(ARGV[pos], /^--?no-play$/)
+if (RSTART) {
+Option["play"] = 0
+continue
+}
+match(ARGV[pos], /^--?no-tran(s(l(a(te?)?)?)?)?$/)
+if (RSTART) {
+Option["no-translate"] = 1
+continue
+}
+match(ARGV[pos], /^--?download-a(u(d(io?)?)?)?$/)
+if (RSTART) {
+Option["download-audio"] = 1
+continue
+}
+match(ARGV[pos], /^--?download-audio-as(=(.*)?)?$/, group)
+if (RSTART) {
+if (!Option["download-audio"]) Option["download-audio"] = 1
+Option["download-audio-as"] = group[1] ?
+(group[2] ? group[2] : Option["download-audio-as"]) :
+ARGV[++pos]
+continue
+}
+match(ARGV[pos], /^--?v(i(ew?)?)?$/)
+if (RSTART) {
+Option["view"] = 1
+continue
+}
+match(ARGV[pos], /^--?pager(=(.*)?)?$/, group)
+if (RSTART) {
+Option["view"] = 1
+Option["pager"] = group[1] ?
+(group[2] ? group[2] : Option["pager"]) :
+ARGV[++pos]
+continue
+}
+match(ARGV[pos], /^--?no-(view|pager)$/)
+if (RSTART) {
+Option["view"] = 0
+continue
+}
+match(ARGV[pos], /^--?browser(=(.*)?)?$/, group)
+if (RSTART) {
+Option["browser"] = group[1] ?
+(group[2] ? group[2] : Option["browser"]) :
+ARGV[++pos]
+continue
+}
+match(ARGV[pos], /^--?no-browser$/)
+if (RSTART) {
+Option["browser"] = NONE
+continue
+}
+match(ARGV[pos], /^--?(x|proxy)(=(.*)?)?$/, group)
+if (RSTART) {
+Option["proxy"] = group[2] ?
+(group[3] ? group[3] : Option["proxy"]) :
+ARGV[++pos]
+continue
+}
+match(ARGV[pos], /^--?(u|user-agent)(=(.*)?)?$/, group)
+if (RSTART) {
+Option["user-agent"] = group[2] ?
+(group[3] ? group[3] : Option["user-agent"]) :
+ARGV[++pos]
+continue
+}
+match(ARGV[pos], /^--?(4|ipv4|inet4-only)$/)
+if (RSTART) {
+Option["ip-version"] = 4
+continue
+}
+match(ARGV[pos], /^--?(6|ipv6|inet6-only)$/)
+if (RSTART) {
+Option["ip-version"] = 6
+continue
+}
+match(ARGV[pos], /^--?(I|int(e(r(a(c(t(i(ve?)?)?)?)?)?)?)?|shell)$/)
+if (RSTART) {
+Option["interactive"] = 1
+continue
+}
+match(ARGV[pos], /^--?(E|emacs)$/)
+if (RSTART) {
+Option["emacs"] = 1
+continue
+}
+match(ARGV[pos], /^--?no-rlwrap$/)
+if (RSTART) {
+Option["no-rlwrap"] = 1
+continue
+}
+match(ARGV[pos], /^--?prompt(=(.*)?)?$/, group)
+if (RSTART) {
+w("[ERROR] Option '-prompt' has been deprecated since version 0.9.\n"\
+" Use configuration variable 'fmt-prompt' instead.")
+exit 1
+}
+match(ARGV[pos], /^--?prompt-color(=(.*)?)?$/, group)
+if (RSTART) {
+w("[ERROR] Option '-prompt-color' has been deprecated since version 0.9.\n"\
+" Use configuration variable 'sgr-prompt' instead.")
+exit 1
+}
+match(ARGV[pos], /^--?i(n(p(ut?)?)?)?(=(.*)?)?$/, group)
+if (RSTART) {
+Option["input"] = group[4] ?
+(group[5] ? group[5] : Option["input"]) :
+ARGV[++pos]
+continue
+}
+match(ARGV[pos], /^--?o(u(t(p(ut?)?)?)?)?(=(.*)?)?$/, group)
+if (RSTART) {
+Option["output"] = group[5] ?
+(group[6] ? group[6] : Option["output"]) :
+ARGV[++pos]
+continue
+}
+match(ARGV[pos], /^--?(l(a(ng?)?)?|hl)(=(.*)?)?$/, group)
+if (RSTART) {
+Option["hl"] = group[4] ?
+(group[5] ? group[5] : Option["hl"]) :
+ARGV[++pos]
+continue
+}
+match(ARGV[pos], /^--?(s(o(u(r(ce?)?)?)?|l)?|f|from)(=(.*)?)?$/, group)
+if (RSTART) {
+if (group[6]) {
+if (group[7]) split(group[7], Option["sls"], "+")
+} else
+split(ARGV[++pos], Option["sls"], "+")
+Option["sl"] = Option["sls"][1]
+continue
+}
+match(ARGV[pos], /^--?t(a(r(g(et?)?)?)?|l|o)?(=(.*)?)?$/, group)
+if (RSTART) {
+if (group[5]) {
+if (group[6]) split(group[6], Option["tl"], "+")
+} else
+split(ARGV[++pos], Option["tl"], "+")
+continue
+}
+match(ARGV[pos], /^[{(\[]?((@?[[:alpha:]][[:alpha:]][[:alpha:]]?(-[[:alpha:]][[:alpha:]][[:alpha:]]?[[:alpha:]]?)?\+)*(@?[[:alpha:]][[:alpha:]][[:alpha:]]?(-[[:alpha:]][[:alpha:]][[:alpha:]]?[[:alpha:]]?)?)?)?(:|=)((@?[[:alpha:]][[:alpha:]][[:alpha:]]?(-[[:alpha:]][[:alpha:]][[:alpha:]]?[[:alpha:]]?)?\+)*(@?[[:alpha:]][[:alpha:]][[:alpha:]]?(-[[:alpha:]][[:alpha:]][[:alpha:]]?[[:alpha:]]?)?)?)[})\]]?$/, group)
+if (RSTART) {
+if (group[1]) {
+split(group[1], Option["sls"], "+")
+Option["sl"] = Option["sls"][1]
+}
+if (group[7]) split(group[7], Option["tl"], "+")
+continue
+}
+match(ARGV[pos], /^--?j(o(i(n(-(s(e(n(t(e(n(ce?)?)?)?)?)?)?)?)?)?)?)?$/)
+if (RSTART) {
+Option["join-sentence"] = 1
+continue
+}
+match(ARGV[pos], /^--?(D|debug)$/)
+if (RSTART) {
+Option["debug"] = 1
+continue
+}
+match(ARGV[pos], /^--?no-init/)
+if (RSTART) continue
+match(ARGV[pos], /^-(-?no-op)?$/)
+if (RSTART) continue
+match(ARGV[pos], /^--$/)
+if (RSTART) {
+++pos
+break
+}
+noargv[noargc++] = ARGV[pos]
+}
+if (Option["interactive"] && !Option["no-rlwrap"])
+rlwrapMe()
+else if (Option["emacs"] && !Option["interactive"] && !Option["no-rlwrap"])
+if (emacsMe())
+Option["interactive"] = 1
+initMisc()
+switch (InfoOnly) {
+case "version":
+print getVersion()
+exit ExitCode
+case "help":
+print getHelp()
+exit ExitCode
+case "manual":
+showMan()
+exit ExitCode
+case "reference":
+print getReference("endonym")
+exit ExitCode
+case "reference-english":
+print getReference("name")
+exit ExitCode
+case "list":
+print getList(Option["tl"])
+exit ExitCode
+case "list-engines":
+for (translator in Translator)
+print (Option["engine"] == translator ? "* " : " ") translator
+exit ExitCode
+case "upgrade":
+upgrade()
+exit ExitCode
+case "nothing":
+exit ExitCode
+}
+setTheme()
+if (Option["interactive"])
+welcome()
+if (pos < ARGC)
+for (i = pos; i < ARGC; i++)
+noargv[noargc++] = ARGV[i]
+if (noargc > 1 && Option["join-sentence"]) {
+noargv[0] = join(noargv, " ")
+noargc = 1
+}
+if (noargc) {
+for (i = 0; i < noargc; i++) {
+if (Option["verbose"] && i > pos)
+p(prettify("source-seperator", replicate(Option["chr-source-seperator"], Option["width"])))
+translates(noargv[i], 1)
+}
+} else {
+if (!Option["input"]) Option["input"] = STDIN
+}
+if (Option["input"])
+translateMain()
+exit ExitCode
+}
+EOF
+read -r -d '' TRANS_MANPAGE << 'EOF'
+.\" Automatically generated by Pandoc 2.5
+.\"
+.TH "TRANS" "1" "2020\-05\-11" "0.9.6.12" ""
+.hy
+.SH NAME
+.PP
+trans \- Command\-line translator using Google Translate, Bing
+Translator, Yandex.Translate, etc.
+.SH SYNOPSIS
+.PP
+\f[B]trans\f[R] [\f[I]OPTIONS\f[R]]
+[\f[I]SOURCE\f[R]]:[\f[I]TARGETS\f[R]] [\f[I]TEXT\f[R]]...
+.SH DESCRIPTION
+.PP
+This tool translates text into any language from the command\-line,
+using a translation engine such as Google Translate, Bing Translator and
+Yandex.Translate.
+.PP
+Each argument which is not a valid option is treated as \f[I]TEXT\f[R]
+to be translated.
+.PP
+If neither \f[I]TEXT\f[R] nor the input file is specified by
+command\-line arguments, the program will read and translate from
+standard input.
+.SH OPTIONS
+.SS Information options
+.TP
+.B \f[B]\-V\f[R], \f[B]\-version\f[R]
+Print version and exit.
+.TP
+.B \f[B]\-H\f[R], \f[B]\-help\f[R]
+Print help message and exit.
+\f[B]\-M\f[R], \f[B]\-man\f[R]
+Show man page and exit.
+.TP
+.B \f[B]\-T\f[R], \f[B]\-reference\f[R]
+Print reference table of all supported languages and codes, and exit.
+Names of languages are displayed in their endonyms (language name in the
+language itself).
+.TP
+.B \f[B]\-R\f[R], \f[B]\-reference\-english\f[R]
+Print reference table of all supported languages and codes, and exit.
+Names of languages are displayed in English.
+.TP
+.B \f[B]\-L\f[R] \f[I]CODES\f[R], \f[B]\-list\f[R] \f[I]CODES\f[R]
+Print details of languages and exit.
+When specifying two or more language codes, concatenate them by plus
+sign \[dq]+\[dq].
+.TP
+.B \f[B]\-S\f[R], \f[B]\-list\-engines\f[R]
+List available translation engines and exit.
+.TP
+.B \f[B]\-U\f[R], \f[B]\-upgrade\f[R]
+Check for upgrade of this program.
+.SS Translator options
+.TP
+.B \f[B]\-e\f[R] \f[I]ENGINE\f[R], \f[B]\-engine\f[R] \f[I]ENGINE\f[R]
+Specify the translation engine to use.
+(default: google)
+.SS Display options
+.TP
+.B \f[B]\-verbose\f[R]
+Verbose mode.
+.RS
+.PP
+Show the original text and its most relevant translation, then its
+phonetic notation (if any), then its alternative translations (if any)
+or its definition in the dictionary (if it is a word).
+.PP
+This option is unnecessary in most cases since verbose mode is enabled
+by default.
+.RE
+.TP
+.B \f[B]\-b\f[R], \f[B]\-brief\f[R]
+Brief mode.
+.RS
+.PP
+Show the most relevant translation or its phonetic notation only.
+.RE
+.TP
+.B \f[B]\-d\f[R], \f[B]\-dictionary\f[R]
+Dictionary mode.
+.RS
+.PP
+Show the definition of the original word in the dictionary.
+.RE
+.TP
+.B \f[B]\-identify\f[R]
+Language identification.
+.RS
+.PP
+Show the identified language of the original text.
+.RE
+.TP
+.B \f[B]\-show\-original\f[R] \f[I]Y/n\f[R]
+Show original text or not.
+(default: yes)
+.TP
+.B \f[B]\-show\-original\-phonetics\f[R] \f[I]Y/n\f[R]
+Show phonetic notation of original text or not.
+(default: yes)
+.TP
+.B \f[B]\-show\-translation\f[R] \f[I]Y/n\f[R]
+Show translation or not.
+(default: yes)
+.TP
+.B \f[B]\-show\-translation\-phonetics\f[R] \f[I]Y/n\f[R]
+Show phonetic notation of translation or not.
+(default: yes)
+.TP
+.B \f[B]\-show\-prompt\-message\f[R] \f[I]Y/n\f[R]
+Show prompt message or not.
+(default: yes)
+.TP
+.B \f[B]\-show\-languages\f[R] \f[I]Y/n\f[R]
+Show source and target languages or not.
+(default: yes)
+.TP
+.B \f[B]\-show\-original\-dictionary\f[R] \f[I]y/N\f[R]
+Show dictionary entry of original text or not.
+(default: no)
+.RS
+.PP
+This option is enabled in dictionary mode.
+.RE
+.TP
+.B \f[B]\-show\-dictionary\f[R] \f[I]Y/n\f[R]
+Show dictionary entry of translation or not.
+(default: yes)
+.TP
+.B \f[B]\-show\-alternatives\f[R] \f[I]Y/n\f[R]
+Show alternative translations or not.
+(default: yes)
+.TP
+.B \f[B]\-w\f[R] \f[I]NUM\f[R], \f[B]\-width\f[R] \f[I]NUM\f[R]
+Specify the screen width for padding.
+.RS
+.PP
+This option overrides the setting of environment variable
+$\f[B]COLUMNS\f[R].
+.RE
+.TP
+.B \f[B]\-indent\f[R] \f[I]NUM\f[R]
+Specify the size of indent (number of spaces).
+(default: 4)
+.TP
+.B \f[B]\-theme\f[R] \f[I]FILENAME\f[R]
+Specify the theme to use.
+(default: default)
+.TP
+.B \f[B]\-no\-theme\f[R]
+Do not use any other theme than default.
+.TP
+.B \f[B]\-no\-ansi\f[R]
+Do not use ANSI escape codes.
+.TP
+.B \f[B]\-no\-autocorrect\f[R]
+Do not autocorrect.
+(if defaulted by the translation engine)
+.TP
+.B \f[B]\-no\-bidi\f[R]
+Do not convert bidirectional texts.
+.TP
+.B \f[B]\-bidi\f[R]
+Always convert bidirectional texts.
+.TP
+.B \f[B]\-no\-warn\f[R]
+Do not write warning messages to stderr.
+.TP
+.B \f[B]\-dump\f[R]
+Print raw API response instead.
+.SS Audio options
+.TP
+.B \f[B]\-p\f[R], \f[B]\-play\f[R]
+Listen to the translation.
+.RS
+.PP
+You must have at least one of the supported audio players
+(\f[B]mplayer\f[R], \f[B]mpv\f[R] or \f[B]mpg123\f[R]) installed to
+stream from Google Text\-to\-Speech engine.
+Otherwise, a local speech synthesizer may be used instead (\f[B]say\f[R]
+on macOS, \f[B]espeak\f[R] on Linux or other platforms).
+.RE
+.TP
+.B \f[B]\-speak\f[R]
+Listen to the original text.
+.TP
+.B \f[B]\-n\f[R] \f[I]VOICE\f[R], \f[B]\-narrator\f[R] \f[I]VOICE\f[R]
+Specify the narrator, and listen to the translation.
+.RS
+.PP
+Common values for this option are \f[B]male\f[R] and \f[B]female\f[R].
+.RE
+.TP
+.B \f[B]\-player\f[R] \f[I]PROGRAM\f[R]
+Specify the audio player to use, and listen to the translation.
+.RS
+.PP
+Option \f[B]\-play\f[R] will try to use \f[B]mplayer\f[R], \f[B]mpv\f[R]
+or \f[B]mpg123\f[R] by default, since these players are known to work
+for streaming URLs.
+Not all command\-line audio players can work this way.
+Use this option only when you have your own preference.
+.PP
+This option overrides the setting of environment variable
+$\f[B]PLAYER\f[R].
+.RE
+.TP
+.B \f[B]\-no\-play\f[R]
+Do not listen to the translation.
+.TP
+.B \f[B]\-no\-translate\f[R]
+Do not translate anything when using \-speak.
+.TP
+.B \f[B]\-download\-audio\f[R]
+Download the audio to the current directory.
+.TP
+.B \f[B]\-download\-audio\-as\f[R] \f[I]FILENAME\f[R]
+Download the audio to the specified file.
+.SS Terminal paging and browsing options
+.TP
+.B \f[B]\-v\f[R], \f[B]\-view\f[R]
+View the translation in a terminal pager (\f[B]less\f[R], \f[B]more\f[R]
+or \f[B]most\f[R]).
+.TP
+.B \f[B]\-pager\f[R] \f[I]PROGRAM\f[R]
+Specify the terminal pager to use, and view the translation.
+.RS
+.PP
+This option overrides the setting of environment variable
+$\f[B]PAGER\f[R].
+.RE
+.TP
+.B \f[B]\-no\-view\f[R], \f[B]\-no\-pager\f[R]
+Do not view the translation in a terminal pager.
+.TP
+.B \f[B]\-browser\f[R] \f[I]PROGRAM\f[R]
+Specify the web browser to use.
+.RS
+.PP
+This option overrides the setting of environment variable
+$\f[B]BROWSER\f[R].
+.RE
+.TP
+.B \f[B]\-no\-browser\f[R]
+Do not open the web browser.
+.SS Networking options
+.TP
+.B \f[B]\-x\f[R] \f[I]HOST:PORT\f[R], \f[B]\-proxy\f[R] \f[I]HOST:PORT\f[R]
+Use HTTP proxy on given port.
+.RS
+.PP
+This option overrides the setting of environment variables
+$\f[B]HTTP_PROXY\f[R] and $\f[B]http_proxy\f[R].
+.RE
+.TP
+.B \f[B]\-u\f[R] \f[I]STRING\f[R], \f[B]\-user\-agent\f[R] \f[I]STRING\f[R]
+Specify the User\-Agent to identify as.
+.RS
+.PP
+This option overrides the setting of environment variables
+$\f[B]USER_AGENT\f[R].
+.RE
+.TP
+.B \f[B]\-4\f[R], \f[B]\-ipv4\f[R], \f[B]\-inet4\-only\f[R]
+Connect only to IPv4 addresses.
+.TP
+.B \f[B]\-6\f[R], \f[B]\-ipv6\f[R], \f[B]\-inet6\-only\f[R]
+Connect only to IPv6 addresses.
+.SS Interactive shell options
+.TP
+.B \f[B]\-I\f[R], \f[B]\-interactive\f[R], \f[B]\-shell\f[R]
+Start an interactive shell, invoking \f[B]rlwrap\f[R] whenever possible
+(unless \f[B]\-no\-rlwrap\f[R] is specified).
+.TP
+.B \f[B]\-E\f[R], \f[B]\-emacs\f[R]
+Start the GNU Emacs front\-end for an interactive shell.
+.RS
+.PP
+This option does not need to, and cannot be used along with
+\f[B]\-I\f[R] or \f[B]\-no\-rlwrap\f[R].
+.RE
+.TP
+.B \f[B]\-no\-rlwrap\f[R]
+Do not invoke \f[B]rlwrap\f[R] when starting an interactive shell.
+.RS
+.PP
+This option is useful when your terminal type is not supported by
+\f[B]rlwrap\f[R] (e.g.
+\f[B]emacs\f[R]).
+.RE
+.SS I/O options
+.TP
+.B \f[B]\-i\f[R] \f[I]FILENAME\f[R], \f[B]\-input\f[R] \f[I]FILENAME\f[R]
+Specify the input file.
+.RS
+.PP
+Source text to be translated will be read from the input file, instead
+of standard input.
+.RE
+.TP
+.B \f[B]\-o\f[R] \f[I]FILENAME\f[R], \f[B]\-output\f[R] \f[I]FILENAME\f[R]
+Specify the output file.
+.RS
+.PP
+Translations will be written to the output file, instead of standard
+output.
+.RE
+.SS Language preference options
+.TP
+.B \f[B]\-l\f[R] \f[I]CODE\f[R], \f[B]\-hl\f[R] \f[I]CODE\f[R], \f[B]\-lang\f[R] \f[I]CODE\f[R]
+Specify your home language (the language you would like to see for
+displaying prompt messages in the translation).
+.RS
+.PP
+This option affects only the display in verbose mode (anything other
+than source language and target language will be displayed in your home
+language).
+This option has no effect in brief mode.
+.PP
+This option is optional.
+When its setting is omitted, English will be used.
+.PP
+This option overrides the setting of environment variables
+$\f[B]LC_ALL\f[R], $\f[B]LANG\f[R], and $\f[B]HOME_LANG\f[R].
+.RE
+.TP
+.B \f[B]\-s\f[R] \f[I]CODES\f[R], \f[B]\-sl\f[R] \f[I]CODES\f[R], \f[B]\-source\f[R] \f[I]CODES\f[R], \f[B]\-from\f[R] \f[I]CODES\f[R]
+Specify the source language(s) (the language(s) of original text).
+When specifying two or more language codes, concatenate them by plus
+sign \[dq]+\[dq].
+.RS
+.PP
+This option is optional.
+When its setting is omitted, the language of original text will be
+identified automatically (with a possibility of misidentification).
+.PP
+This option overrides the setting of environment variable
+$\f[B]SOURCE_LANG\f[R].
+.RE
+.TP
+.B \f[B]\-t\f[R] \f[I]CODES\f[R], \f[B]\-tl\f[R] \f[I]CODES\f[R], \f[B]\-target\f[R] \f[I]CODES\f[R], \f[B]\-to\f[R] \f[I]CODES\f[R]
+Specify the target language(s) (the language(s) of translated text).
+When specifying two or more language codes, concatenate them by plus
+sign \[dq]+\[dq].
+.RS
+.PP
+This option is optional.
+When its setting is omitted, everything will be translated into English.
+.PP
+This option overrides the setting of environment variables
+$\f[B]LC_ALL\f[R], $\f[B]LANG\f[R], and $\f[B]TARGET_LANG\f[R].
+.RE
+.TP
+.B [\f[I]SOURCE\f[R]]:[\f[I]TARGETS\f[R]]
+A simpler, alternative way to specify the source language and target
+language(s) is to use a shortcut formatted string:
+.RS
+.IP \[bu] 2
+\f[I]SOURCE\-CODE\f[R]:\f[I]TARGET\-CODE\f[R]
+.IP \[bu] 2
+\f[I]SOURCE\-CODE\f[R]:\f[I]TARGET\-CODE1\f[R]+\f[I]TARGET\-CODE2\f[R]+...
+.IP \[bu] 2
+\f[I]SOURCE\-CODE\f[R]=\f[I]TARGET\-CODE\f[R]
+.IP \[bu] 2
+\f[I]SOURCE\-CODE\f[R]=\f[I]TARGET\-CODE1\f[R]+\f[I]TARGET\-CODE2\f[R]+...
+.PP
+Delimiter \[dq]:\[dq] and \[dq]=\[dq] can be used interchangeably.
+.PP
+Either \f[I]SOURCE\f[R] or \f[I]TARGETS\f[R] may be omitted, but the
+delimiter character must be kept.
+.RE
+.SS Text preprocessing options
+.TP
+.B \f[B]\-j\f[R], \f[B]\-join\-sentence\f[R]
+Treat all arguments as one single sentence.
+.SS Other options
+.TP
+.B \f[B]\-no\-init\f[R]
+Do not load any initialization script.
+.TP
+.B \f[B]\-\-\f[R]
+End\-of\-options.
+.RS
+.PP
+All arguments after this option are treated as \f[I]TEXT\f[R] to be
+translated.
+.RE
+.SH EXIT STATUS
+.TP
+.B \f[B]0\f[R]
+Successful translation.
+.TP
+.B \f[B]1\f[R]
+Error.
+.SH ENVIRONMENT
+.TP
+.B \f[B]PAGER\f[R]
+Equivalent to option setting \f[B]\-pager\f[R].
+.TP
+.B \f[B]BROWSER\f[R]
+Equivalent to option setting \f[B]\-browser\f[R].
+.TP
+.B \f[B]PLAYER\f[R]
+Equivalent to option setting \f[B]\-player\f[R].
+.TP
+.B \f[B]HTTP_PROXY\f[R]
+Equivalent to option setting \f[B]\-proxy\f[R].
+.TP
+.B \f[B]USER_AGENT\f[R]
+Equivalent to option setting \f[B]\-user\-agent\f[R].
+.TP
+.B \f[B]HOME_LANG\f[R]
+Equivalent to option setting \f[B]\-lang\f[R].
+.TP
+.B \f[B]SOURCE_LANG\f[R]
+Equivalent to option setting \f[B]\-source\f[R].
+.TP
+.B \f[B]TARGET_LANG\f[R]
+Equivalent to option setting \f[B]\-target\f[R].
+.SH FILES
+.TP
+.B \f[I]/etc/translate\-shell\f[R]
+Initialization script.
+(system\-wide)
+.TP
+.B \f[I]$HOME/.translate\-shell/init.trans\f[R]
+Initialization script.
+(user\-specific)
+.TP
+.B \f[I]$XDG_CONFIG_HOME/translate\-shell/init.trans\f[R]
+Initialization script.
+(user\-specific)
+.TP
+.B \f[I]./.trans\f[R]
+Initialization script.
+(current directory)
+.SH FURTHER DOCUMENTATION
+.PP
+
+.SH REPORTING BUGS
+.PP
+
+.SH AUTHORS
+Mort Yao .
+EOF
+export TRANS_MANPAGE
+export TRANS_BUILD=release
+gawk -f <(echo -E "$TRANS_PROGRAM") - "$@"
diff --git a/.local/bin/twitch b/.local/bin/twitch
new file mode 100755
index 0000000..688f3b8
--- /dev/null
+++ b/.local/bin/twitch
@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+
+CLIENT_ID=SET_CLIENT_ID_HERE
+OAUTH_TOKEN=SET_OAUTH_TOKEN_HERE
+
+if [ -z "$1" ]; then
+ curl -s \
+ -H 'Accept: application/vnd.twitchtv.v5+json' \
+ -H "Client-ID: ${TWITCH_CLIENT_ID:-$CLIENT_ID}" \
+ -H "Authorization: OAuth ${TWITCH_OAUTH_TOKEN:-$OAUTH_TOKEN}" \
+ -X GET 'https://api.twitch.tv/kraken/streams/followed' | \
+ ramda \
+ 'tap (res) -> if res.error then console.error(res); process.exit(1)' \
+ '.streams' \
+ 'map pick-dot-paths [\channel.name, \channel.status, \game, \viewers]' \
+ 'map flat' \
+ 'map (rename-keys-by split(".") >> last)' \
+ '-> if is-empty(it) then "No one you are following is streaming right now." else it' \
+ -o table
+else
+ streamlink -p mpv twitch.tv/$1 ${2:-best}
+fi
diff --git a/.local/bin/update b/.local/bin/update
new file mode 100755
index 0000000..57e9873
--- /dev/null
+++ b/.local/bin/update
@@ -0,0 +1,18 @@
+#!/bin/bash
+echo
+echo
+echo -e $(date)
+echo
+echo
+sudo /home/yorune/.local/bin/eix-repos-sync
+sudo eix-sync -a
+sudo eix-update
+#sudo emerge-webrsync
+#sudo emerge --sync
+#sudo emaint sync -a
+sudo emerge -auDN @world
+echo
+echo
+echo -e $(date)
+echo
+echo
diff --git a/.local/bin/update-kernel b/.local/bin/update-kernel
new file mode 100755
index 0000000..2c2fc2b
--- /dev/null
+++ b/.local/bin/update-kernel
@@ -0,0 +1,118 @@
+#!/bin/bash
+
+BACKUP="/home/yorune/Linux/portage"
+LOG_FILE="/tmp/update-kernel.log"
+TMP_KERNEL="/tmp/kernel-config-`uname -r`"
+DEFAULT_KERNEL="/usr/src/linux/.config"
+
+function starting() {
+echo -e "\e[93m----------------------COMPILING------------------------------\e[0m"
+sudo cp -rv $DEFAULT_KERNEL $TMP_KERNEL
+cp -r $TMP_KERNEL $BACKUP/kernel-config
+cp -r /etc/portage/* $BACKUP
+qlist -I | sort -u > $BACKUP/list-of-programs
+}
+
+function selection() {
+echo -e "\e[93m----------------------SELECTION-----------------------------\e[0m"
+sudo eselect kernel list
+echo
+echo
+read -p "New kernel is: " KERVER
+echo
+echo -e "Your kernel now is \e[91m$(uname -sr)\e[0m"
+echo -e "Your selected kernel is \e[91m"$KERVER"\e[0m"
+sudo eselect kernel set $KERVER
+sudo eselect kernel list | grep "*"
+echo
+}
+
+function compilation() {
+read -p "Do you want to accept and compile (Y/N): " agreed
+echo
+if [ "$agreed" == "y" ] || [ "$agreed" == "Y" ]
+ then
+ echo -e "\e[91m----------------------\e[5mSTARTING\e[0m\e[91m------------------------------\e[0m" && sleep 10
+ $NOW > /tmp/compiling-starting
+ NEW_KERNEL="/tmp/new-kernel-config"
+ sudo cp -r $TMP_KERNEL $DEFAULT_KERNEL
+ cd /usr/src/linux; sudo make menuconfig; sleep 2; sudo cp -r $DEFAULT_KERNEL $NEW_KERNEL
+ sudo genkernel all --makeopts=-j$(nproc --all) --kernel-config=$NEW_KERNEL --callback="emerge nvidia-drivers::gentoo" --lvm --btrfs --luks
+ elif [ "$agreed" == "N" ] || [ "$agreed" == "n" ]
+ then
+ exit
+fi
+}
+
+function ending() {
+echo
+echo
+echo -e "\e[93m----------------------CHECKING-----------------------------\e[0m"
+checking
+echo
+echo
+echo -e "\e[93m----------------------REMEMBER-----------------------------\e[0m"
+echo "You can remove:"
+echo "* /lib/modules/OLD_KERNEL"
+echo "* /boot/initramfs-genkernel-OLD_KERNEL"
+echo "* /boot/vmlinuz-OLD_KERNEL"
+echo "* /boot/System.map-OLD_KERNEL"
+echo "* /boot/initramfs-OLD_KERNEL"
+echo "* /usr/src/linux-OLD_KERNEL"
+echo
+echo -e "AFTER EVERYTHING YOU MUST WRITE COMMAND \e[91m"sudo grub-mkconfig -o /boot/grub/grub.cfg"\e[0m"
+}
+
+function checking() {
+KERNEL=`eselect kernel list | awk '{print $2}' | egrep -o '[0-9]+.[0-9]+.[0-9]+' | tail -n1`
+
+INITRANFS="initramfs-$KERNEL-gentoo-x86_64.img"
+SYSTEMMAP="System.map-$KERNEL-gentoo-x86_64"
+VMLINUZ="vmlinuz-$KERNEL-gentoo-x86_64"
+
+ifchecking $INITRANFS
+ifchecking $SYSTEMMAP
+ifchecking $VMLINUZ
+sudo grub-mkconfig -o /boot/grub/grub.cfg
+}
+
+function ifchecking () {
+FILE=/boot/$1
+if test -f "$FILE"; then
+ echo "$FILE exist in the /boot folder ;)"
+else
+ echo "$FILE NOT exist in the /boot folder ;)"
+fi
+}
+
+function addgit() {
+ cd ~/Linux/portage || exit
+ git add .
+ git commit -m "Updated: $(date)"
+ git push
+ cd ~ || exit
+}
+
+function main() {
+clear
+
+BEGIN=$(date +"%s")
+
+starting
+addgit
+selection
+compilation
+
+echo -e "\e[31mI am leaving! Thank You!\e[0m" && sleep 3
+
+TERMIN=$(date +"%s")
+DIFFTLPS=$(($TERMIN-$BEGIN))
+
+echo -e "\e[93m------------------TIME COMPILATION-------------------------\e[0m"
+echo -e "\e[93m$(($DIFFTLPS / 60)) minutes and $(($DIFFTLPS % 60)) seconds \e[0m elapsed for Script Execution." && sleep 3
+ending
+
+exit
+}
+
+main
diff --git a/.local/bin/video-convert b/.local/bin/video-convert
new file mode 100755
index 0000000..6f44717
--- /dev/null
+++ b/.local/bin/video-convert
@@ -0,0 +1,3 @@
+#!/bin/bash
+FILE=$(echo $1 | cut -d. -f1)
+ffmpeg -i $1 -b:a 128k -codec:v libx265 -b:v 1500k -vf scale=1280:720 $FILE-convered.mp4
diff --git a/.local/bin/volume b/.local/bin/volume
new file mode 100755
index 0000000..29ab7f1
--- /dev/null
+++ b/.local/bin/volume
@@ -0,0 +1,5 @@
+#!/bin/bash
+pkill -RTMIN+10 dwmblocks
+[ "$1" = "up" ] && ponymix increase 5 > /dev/null && notify-send "🔊 Volume $(ponymix get-volume)"
+[ "$1" = "down" ] && ponymix decrease 5 > /dev/null && notify-send "🔉 Volume $(ponymix get-volume)"
+[ "$1" = "toggle" ] && ponymix toggle> /dev/null && notify-send "🔇 Volume (un)mute "
diff --git a/.local/bin/welcomer b/.local/bin/welcomer
new file mode 100755
index 0000000..97125d6
--- /dev/null
+++ b/.local/bin/welcomer
@@ -0,0 +1,54 @@
+#!/bin/bash
+
+MEMTOTAL=`awk '$3=="kB"{$2=$2/1024**2;$3="GB";} 1' /proc/meminfo | column -t | grep "MemTotal" | awk '{print $2}'`
+MEMFREE=`awk '$3=="kB"{$2=$2/1024**2;$3="GB";} 1' /proc/meminfo | column -t | grep "MemAvailable" | awk '{print $2}'`
+NETE=`ip a | awk {'print $2'} | grep -i en | sed 's/://g'`
+NETW=`ip a | awk {'print $2'} | grep -i wl | sed 's/://g'`
+BACKUP_SDA8=`df -h | grep -i sda8 | awk '{print $5", " $2 " (Total), " $3" (Usage)"}'`
+BACKUP_EL=`df -h | grep /run/media/jaqu3/ELEMENTS | awk '{print $5", " $2 " (Total), " $3" (Usage)"}'`
+BACKUP_EV=`df -h | grep /run/media/jaqu3/BACKUP_EVERYTHING | awk '{print $5", " $2 " (Total), " $3" (Usage)"}'`
+
+
+if [ -z "$NETW" ]; then
+ NETD=$NETE
+else
+ NETD=$NETW
+fi
+
+if [ -z "$BACKUP_SDA8" ]; then
+ BACKUPSDA8=`echo NOT MOUNTED`
+else
+ BACKUPSDA8=$BACKUP_SDA8
+fi
+
+if [ -z "$BACKUP_EL" ]; then
+ BACKUPEL=`echo NOT MOUNTED`
+else
+ BACKUPEL=$BACKUP_EL
+fi
+
+if [ -z "$BACKUP_EV" ]; then
+ BACKUPEV=`echo NOT MOUNTED`
+else
+ BACKUPEV=$BACKUP_EV
+fi
+
+ echo -e "
+ \033[0;35m+++++++++++++++++++: \033[0;37mInfo\033[0;35m :+++++++++++++++++++++++++++++++++++++++++++++++++++++
+ +
+ + \033[0;37mHostname \033[0;35m= \033[1;32m `hostname`
+ \033[0;35m+ \033[0;37mAddress \033[0;35m= \033[1;32m `/sbin/ip addr show $NETD | grep "inet " | awk '{print $2}' | cut -d'/' -f1`
+ \033[0;35m+ \033[0;37mKernel \033[0;35m= \033[1;32m `uname -r`
+ \033[0;35m+ \033[0;37mUptime \033[0;35m= \033[1;32m`uptime | sed 's/.*up ([^,]*), .*/1/'`
+ \033[0;35m+ \033[0;37mMemory \033[0;35m= \033[1;32m $MEMTOTAL GB (free $MEMFREE GB)\033[0;35m
+ \033[0;35m+ \033[0;37mUsername \033[0;35m= \033[1;32m `whoami`\033[0;35m
+ +
+ \033[0;35m++++++++++++++++++: \033[0;37mStorage\033[0;35m :+++++++++++++++++++++++++++++++++++++++++++++++++++
+ +
+ + \033[0;37m/ \033[0;35m= \033[1;32m `df -h / | sed '1,1d' | awk '{print $5\", \"$2\" (Total), \"$3\" (Usage)\"}'`\033[0;35m
+ + \033[0;37mSDA8 \033[0;35m= \033[1;32m $BACKUPSDA8 \033[0;35m
+ + \033[0;37mELEMENTS \033[0;35m= \033[1;32m $BACKUPEL \033[0;35m
+ + \033[0;37mBACKUP_EVERYTHING \033[0;35m= \033[1;32m $BACKUPEV \033[0;35m
+ +
+ \033[0;35m++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ \033[0m"
+ echo ""
diff --git a/.local/bin/welcomer-serwer b/.local/bin/welcomer-serwer
new file mode 100755
index 0000000..ef61673
--- /dev/null
+++ b/.local/bin/welcomer-serwer
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+# Copy this file to /bin/welcomer and change permisson +x
+# sudo bash -c $'echo "/bin/welcomer" >> /etc/profile.d/mymotd.sh && chmod +x /etc/profile.d/mymotd.sh'
+
+
+MEMTOTAL=`awk '$3=="kB"{$2=$2/1024**2;$3="GB";} 1' /proc/meminfo | column -t | grep "MemTotal" | awk '{print $2}'`
+MEMFREE=`awk '$3=="kB"{$2=$2/1024**2;$3="GB";} 1' /proc/meminfo | column -t | grep "MemAvailable" | awk '{print $2}'`
+NETE=`ip a | awk {'print $2'} | grep -i en | sed 's/://g'`
+NETW=`ip a | awk {'print $2'} | grep -i wl | sed 's/://g'`
+
+if [ -z "$NETW" ]; then
+ NETD=$NETE
+else
+ NETD=$NETW
+fi
+
+ echo -e "
+ \033[0;35m+++++++++++++++++++: \033[0;37mInfo\033[0;35m :+++++++++++++++++++++++++++++++++++++++++++++++++++++
+ +
+ + \033[0;37mHostname \033[0;35m= \033[1;32m `hostname`
+ \033[0;35m+ \033[0;37mAddress \033[0;35m= \033[1;32m `/sbin/ip addr show $NETD | grep "inet " | awk '{print $2}' | cut -d'/' -f1`
+ \033[0;35m+ \033[0;37mKernel \033[0;35m= \033[1;32m `uname -r`
+ \033[0;35m+ \033[0;37mUptime \033[0;35m= \033[1;32m`uptime | sed 's/.*up ([^,]*), .*/1/'`
+ \033[0;35m+ \033[0;37mMemory \033[0;35m= \033[1;32m $MEMTOTAL GB (free $MEMFREE GB)\033[0;35m
+ \033[0;35m+ \033[0;37mUsername \033[0;35m= \033[1;32m `whoami`\033[0;35m
+ +
+ \033[0;35m++++++++++++++++++: \033[0;37mStorage\033[0;35m :+++++++++++++++++++++++++++++++++++++++++++++++++++
+ +
+ + \033[0;37m/ \033[0;35m= \033[1;32m `df -h / | sed '1,1d' | awk '{print $5\", \"$2\" (Total), \"$3\" (Usage)\"}'`\033[0;35m
+ +
+ \033[0;35m++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ \033[0m"
+ echo ""
diff --git a/.local/bin/wheel b/.local/bin/wheel
new file mode 100755
index 0000000..67a4232
--- /dev/null
+++ b/.local/bin/wheel
@@ -0,0 +1,8 @@
+#!/usr/bin/python3.8
+# -*- coding: utf-8 -*-
+import re
+import sys
+from wheel.cli import main
+if __name__ == '__main__':
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+ sys.exit(main())
diff --git a/.local/bin/wsl-notify b/.local/bin/wsl-notify
new file mode 100755
index 0000000..7e9f7bb
--- /dev/null
+++ b/.local/bin/wsl-notify
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+
+/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe "New-BurntToastNotification -Text \"$1\""
diff --git a/.local/bin/wylaczoff b/.local/bin/wylaczoff
new file mode 100755
index 0000000..bfb72eb
--- /dev/null
+++ b/.local/bin/wylaczoff
@@ -0,0 +1,5 @@
+xset b off
+xset s off && xset -dpms
+set bell-style none
+#/usr/bin/amixer -c 0 sset "Auto-Mute Mode" Disabled
+
diff --git a/.local/bin/yatqa b/.local/bin/yatqa
new file mode 100755
index 0000000..b1df29b
--- /dev/null
+++ b/.local/bin/yatqa
@@ -0,0 +1 @@
+wine "C:/Program Files (x86)/YaTQA/yatqa.exe"
diff --git a/.local/bin/yt-mp3 b/.local/bin/yt-mp3
new file mode 100755
index 0000000..6827cdd
--- /dev/null
+++ b/.local/bin/yt-mp3
@@ -0,0 +1,2 @@
+#!/bin/bash
+youtube-dl --extract-audio --audio-format mp3 $1
diff --git a/.local/bin/yt-video b/.local/bin/yt-video
new file mode 100755
index 0000000..a3053b0
--- /dev/null
+++ b/.local/bin/yt-video
@@ -0,0 +1,2 @@
+#!/bin/bash
+youtube-dl $1