Home

Script: shell.py

Run shell commands inside WeeChat. (for WeeChat ≥ 0.3.0)
Author: kolter, version 0.8, GPL2 — added: 2006-03-13, updated: 2013-07-27
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
# -*- coding: utf-8 -*-
# =============================================================================
#  shell.py (c) March 2006, 2009 by Kolter <kolter@openics.org>
#
#  Licence     : GPL v2
#  Description : running shell commands in WeeChat
#  Syntax      : try /help shell to get some help on this script
#  Precond     : needs weechat >= 0.3.0 to run
#
#
# ### changelog ###
#
#  * version 0.8, 2013-07-27, Sebastien Helleu <flashcode@flashtux.org>:
#      - don't remove empty lines in output of command
#  * version 0.7, 2012-11-26, Sebastien Helleu <flashcode@flashtux.org>:
#      - use hashtable for command arguments (for WeeChat >= 0.4.0)
#  * version 0.6, 2012-11-21, Sebastien Helleu <flashcode@flashtux.org>:
#      - call shell in hook_process (WeeChat >= 0.3.9.2 does not call shell any more)
#  * version 0.5, 2011-10-01, Sebastien Helleu <flashcode@flashtux.org>:
#      - add shell buffer
#  * version 0.4, 2009-05-02, Sebastien Helleu <flashcode@flashtux.org>:
#      - sync with last API changes
#  * version 0.3, 2009-03-06, Sebastien Helleu <flashcode@flashtux.org>:
#      - use of hook_process to run background process
#      - add option -t <timeout> to kill process after <timeout> seconds
#      - show process running, kill it with -kill
#  * version 0.2, 2009-01-31, Sebastien Helleu <flashcode@flashtux.org>:
#      - conversion to WeeChat 0.3.0+
#  * version 0.1, 2006-03-13, Kolter <kolter@openics.org>:
#      - first release
#
# =============================================================================

import weechat, os, datetime

SCRIPT_NAME    = 'shell'
SCRIPT_AUTHOR  = 'Kolter'
SCRIPT_VERSION = '0.8'
SCRIPT_LICENSE = 'GPL2'
SCRIPT_DESC    = 'Run shell commands in WeeChat'

SHELL_CMD      = 'shell'
SHELL_PREFIX   = '[shell] '

cmd_hook_process   = ''
cmd_command        = ''
cmd_start_time     = None
cmd_buffer         = ''
cmd_shell_buffer   = ''
cmd_stdout         = ''
cmd_stderr         = ''
cmd_send_to_buffer = ''
cmd_timeout        = 0

if weechat.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION, SCRIPT_LICENSE,
                    SCRIPT_DESC, '', ''):
    weechat.hook_command(
        SHELL_CMD,
        'Running shell commands in WeeChat',
        '[-o|-n] [-t seconds] <command> || -show || -kill',
        '        -o: send output to current buffer (simulate user entry '
        'with command output - dangerous, be careful when using this option)\n'
        '        -n: display output in a new empty buffer\n'
        '-t seconds: auto-kill process after timeout (seconds) if process '
        'is still running\n'
        '   command: shell command or builtin like cd, getenv, setenv, unsetenv\n'
        '     -show: show running process\n'
        '     -kill: kill running process',
        '-o|-n|-t|cd|getenv|setenv|unsetenv|-show||-kill -o|-n|-t|cd|getenv|setenv|unsetenv',
        'shell_cmd', '')

def shell_init():
    """Initialize some variables."""
    global cmd_hook_process, cmd_command, cmd_start_time, cmd_buffer, cmd_stdout, cmd_stderr
    cmd_hook_process = ''
    cmd_command      = ''
    cmd_start_time   = None
    cmd_buffer       = ''
    cmd_stdout       = ''
    cmd_stderr       = ''

def shell_set_title():
    """Set title on shell buffer (with working directory)."""
    global cmd_shell_buffer
    if cmd_shell_buffer:
        weechat.buffer_set(cmd_shell_buffer, 'title',
                           '%s.py %s | "q": close buffer | Working dir: %s' % (SCRIPT_NAME, SCRIPT_VERSION, os.getcwd()))

def shell_process_cb(data, command, rc, stdout, stderr):
    """Callback for hook_process()."""
    global cmd_hook_process, cmd_buffer, cmd_stdout, cmd_stderr, cmd_send_to_buffer
    cmd_stdout += stdout
    cmd_stderr += stderr
    if int(rc) >= 0:
        if cmd_stdout:
            lines = cmd_stdout.rstrip().split('\n')
            if cmd_send_to_buffer == 'current':
                for line in lines:
                    weechat.command(cmd_buffer, '%s' % line)
            else:
                weechat.prnt(cmd_buffer, '')
                if cmd_send_to_buffer != 'new':
                    weechat.prnt(cmd_buffer, '%sCommand "%s" (rc %d), stdout:'
                                 % (SHELL_PREFIX, data, int(rc)))
                for line in lines:
                    weechat.prnt(cmd_buffer, ' \t%s' % line)
        if cmd_stderr:
            lines = cmd_stderr.rstrip().split('\n')
            if cmd_send_to_buffer == 'current':
                for line in lines:
                    weechat.command(cmd_buffer, '%s' % line)
            else:
                weechat.prnt(cmd_buffer, '')
                if cmd_send_to_buffer != 'new':
                    weechat.prnt(cmd_buffer, '%s%sCommand "%s" (rc %d), stderr:'
                                 % (weechat.prefix('error'), SHELL_PREFIX, data, int(rc)))
                for line in lines:
                    weechat.prnt(cmd_buffer, '%s%s' % (weechat.prefix('error'), line))
        cmd_hook_process = ''
        shell_set_title()
    return weechat.WEECHAT_RC_OK

def shell_show_process(buffer):
    """Show running process."""
    global cmd_command, cmd_start_time
    if cmd_hook_process:
        weechat.prnt(buffer, '%sprocess running: "%s" (started on %s)'
                     % (SHELL_PREFIX, cmd_command, cmd_start_time.ctime()))
    else:
        weechat.prnt(buffer, '%sno process running' % SHELL_PREFIX)

def shell_kill_process(buffer):
    """Kill running process."""
    global cmd_hook_process, cmd_command
    if cmd_hook_process:
        weechat.unhook(cmd_hook_process)
        weechat.prnt(buffer, '%sprocess killed (command "%s")' % (SHELL_PREFIX, cmd_command))
        shell_init()
    else:
        weechat.prnt(buffer, '%sno process running' % SHELL_PREFIX)

def shell_chdir(buffer, directory):
    """Change working directory."""
    if not directory:
        if os.environ.has_key('HOME'):
            directory = os.environ['HOME']
    try:
        os.chdir(directory)
    except:
        weechat.prnt(buffer, '%san error occured while running command "cd %s"' % (SHELL_PREFIX, directory))
    else:
        weechat.prnt(buffer, '%schdir to "%s" ok, new path: %s' % (SHELL_PREFIX, directory, os.getcwd()))
    shell_set_title()

def shell_getenv(buffer, var):
    """Get environment variable."""
    global cmd_send_to_buffer
    var = var.strip()
    if not var:
        weechat.prnt(buffer, '%swrong syntax, try "getenv VAR"' % (SHELL_PREFIX))
        return

    value = os.getenv(var)
    if value == None:
        weechat.prnt(buffer, '%s$%s is not set' % (SHELL_PREFIX, var))
    else:
        if cmd_send_to_buffer == 'current':
            weechat.command(buffer, '$%s=%s' % (var, os.getenv(var)))
        else:
            weechat.prnt(buffer, '%s$%s=%s' % (SHELL_PREFIX, var, os.getenv(var)))

def shell_setenv(buffer, expr):
    """Set an environment variable."""
    global cmd_send_to_buffer
    expr = expr.strip()
    lexpr = expr.split('=')

    if (len(lexpr) < 2):
        weechat.prnt(buffer, '%swrong syntax, try "setenv VAR=VALUE"' % (SHELL_PREFIX))
        return

    os.environ[lexpr[0].strip()] = '='.join(lexpr[1:])
    if cmd_send_to_buffer != 'current':
        weechat.prnt(buffer, '%s$%s is now set to "%s"' % (SHELL_PREFIX, lexpr[0], '='.join(lexpr[1:])))

def shell_unsetenv(buffer, var):
    """Remove environment variable."""
    var = var.strip()
    if not var:
        weechat.prnt(buffer, '%swrong syntax, try "unsetenv VAR"' % (SHELL_PREFIX))
        return

    if os.environ.has_key(var):
        del os.environ[var]
        weechat.prnt(buffer, '%s$%s is now unset' % (SHELL_PREFIX, var))
    else:
        weechat.prnt(buffer, '%s$%s is not set' % (SHELL_PREFIX, var))

def shell_exec(buffer, command):
    """Execute a command."""
    global cmd_hook_process, cmd_command, cmd_start_time, cmd_buffer
    global cmd_stdout, cmd_stderr, cmd_send_to_buffer, cmd_timeout
    if cmd_hook_process:
        weechat.prnt(buffer,
                     '%sanother process is running! (use "/%s -kill" to kill it)'
                     % (SHELL_PREFIX, SHELL_CMD))
        return
    if cmd_send_to_buffer == 'new':
        weechat.prnt(buffer, '-->\t%s%s$ %s%s'
                     % (weechat.color('chat_buffer'), os.getcwd(), weechat.color('reset'), command))
        weechat.prnt(buffer, '')
    args = command.split(' ')
    if args[0] == 'cd':
        shell_chdir(buffer, ' '.join(args[1:]))
    elif args[0] == 'getenv':
        shell_getenv (buffer, ' '.join(args[1:]))
    elif args[0] == 'setenv':
        shell_setenv (buffer, ' '.join(args[1:]))
    elif args[0] == 'unsetenv':
        shell_unsetenv (buffer, ' '.join(args[1:]))
    else:
        shell_init()
        cmd_command = command
        cmd_start_time = datetime.datetime.now()
        cmd_buffer = buffer
        version = weechat.info_get("version_number", "") or 0
        if int(version) >= 0x00040000:
            cmd_hook_process = weechat.hook_process_hashtable('sh', { 'arg1': '-c', 'arg2': command },
                                                              cmd_timeout * 1000, 'shell_process_cb', command)
        else:
            cmd_hook_process = weechat.hook_process("sh -c '%s'" % command, cmd_timeout * 1000, 'shell_process_cb', command)

def shell_input_buffer(data, buffer, input):
    """Input callback on shell buffer."""
    global cmd_send_to_buffer
    if input in ('q', 'Q'):
        weechat.buffer_close(buffer)
        return weechat.WEECHAT_RC_OK
    cmd_send_to_buffer = 'new'
    weechat.prnt(buffer, '')
    command = weechat.string_input_for_buffer(input)
    shell_exec(buffer, command)
    return weechat.WEECHAT_RC_OK

def shell_close_buffer(data, buffer):
    """Close callback on shell buffer."""
    global cmd_shell_buffer
    cmd_shell_buffer = ''
    return weechat.WEECHAT_RC_OK

def shell_new_buffer():
    """Create shell buffer."""
    global cmd_shell_buffer
    cmd_shell_buffer = weechat.buffer_search('python', 'shell')
    if not cmd_shell_buffer:
        cmd_shell_buffer = weechat.buffer_new('shell', 'shell_input_buffer', '', 'shell_close_buffer', '')
    if cmd_shell_buffer:
        shell_set_title()
        weechat.buffer_set(cmd_shell_buffer, 'localvar_set_no_log', '1')
        weechat.buffer_set(cmd_shell_buffer, 'time_for_each_line', '0')
        weechat.buffer_set(cmd_shell_buffer, 'input_get_unknown_commands', '1')
        weechat.buffer_set(cmd_shell_buffer, 'display', '1')
    return cmd_shell_buffer

def shell_cmd(data, buffer, args):
    """Callback for /shell command."""
    global cmd_send_to_buffer, cmd_timeout
    largs = args.split(' ')

    # strip spaces
    while '' in largs:
        largs.remove('')
    while ' ' in largs:
        largs.remove(' ')

    cmdbuf = buffer

    if len(largs) ==  0:
        shell_new_buffer()
    else:
        if largs[0] == '-show':
            shell_show_process(cmdbuf)
        elif largs[0] == '-kill':
            shell_kill_process(cmdbuf)
        else:
            cmd_send_to_buffer = ''
            cmd_timeout = 0
            while largs:
                if largs[0] == '-o':
                    cmd_send_to_buffer = 'current'
                    largs = largs[1:]
                    continue
                if largs[0] == '-n':
                    cmd_send_to_buffer = 'new'
                    cmdbuf = shell_new_buffer()
                    largs = largs[1:]
                    continue
                if largs[0] == '-t' and len(largs) > 2:
                    cmd_timeout = int(largs[1])
                    largs = largs[2:]
                    continue
                break
            if len(largs) > 0:
                shell_exec(cmdbuf, ' '.join(largs))
    return weechat.WEECHAT_RC_OK