DISCLAIMER. English language used here only for compatibility (ASCII only), so any suggestions about my bad grammar (and not only it) will be greatly appreciated.

четверг, 30 сентября 2010 г.

[draft][part] Shell I/O redirection

DISCLAIMER. English language used here only for compatibility (ASCII only), so any suggestions about my bad grammar (and not only it) will be greatly appreciated.

1. Open file descriptors table maps number (file descriptor - FD) into actual
   file to which output will be written.

    +----+------------+
    | FD | File       |
    +----+------------+
    | 0  |  /dev/vc/1 |
    +----+------------+
    | 1  |  /dev/vc/1 |
    +----+------------+
    | 2  |  /dev/vc/1 |
    +----+------------+
    | ...             |

2. Redirection operators (shell) affect (change) only 'File' column of the
   table. So, if program writes to FD=5 (write(5,..); call) you can _not_
   force it to write to another FD with shell redirection - you can only
   change the content of 'File' column in the FD=5 row (i.e to what file FD=5
   is mapped) and through that change to where output arrives at the end (to
   which file).

3. '/dev/stdout' and '/dev/stderr' are _not_ an actual files, they're _links_
   to what is _now_ opened at FD=1 and FD=2 correspondingly. I.e links to
   file specified in 'File' column of FD=1 and FD=2 rows.

# ls -l /dev/stdout /dev/stderr
lrwxrwxrwx 1 root root 15 Sep 30 07:56 /dev/stderr -> /proc/self/fd/2
lrwxrwxrwx 1 root root 15 Sep 30 07:56 /dev/stdout -> /proc/self/fd/1

4. In construction like 

        f() {
            echo "abc"
            return 0
        }
        res=$(f)

   function f() result are sent through pipe to the caller. I think, this is
   not clear to say, that result are sent through "stdout", though it's
   correct, since "stdout" is _not_ an actual file - it's a link to what is
   now opened at file descriptor 1, and now there will be pipe. By default,
   when function invoked through command substitution (function will be
   executed in a subshell), pipe is opened for writing at FD=1 for child
   process (function) and for reading at FD=3 for caller process.  So, if you
   use something like

        echo "abc"

   "abc" will be written into pipe (because `echo` always writes to FD=1). But
   if you want, that the way how we send result does not affect function's
   code, we should move 'pipe' from FD=1 into some unused FD and restore
   original FD=1 content. 

        exec 7>&1 1>&2
   
   When result will be ready, we move 'pipe' back to FD=1 and `echo` result
   into it.

        exec 1>&7-
        echo "result"

   Note, that for all child subshells pipe will be opened as well.


Example. Illustrates complete implementation, with several stacked functions
returning result through pipe.


#!/bin/bash

log='./t.log'
read_f='/dev/null'
rm -f $log

f2() {
    echo "f2(): SUBSH=$BASH_SUBSHELL" >>$log
    echo "f2(): BASHPID[$BASHPID]:" >>$log
    lsof -a -p $BASHPID -d '^mem,^cwd,^rtd,^txt' >>$log

    echo "f2(): stdout-1: ghi"
    echo "f2(): stderr-1: klm" >/dev/stderr

    echo "f2(): Before moving pipe somewhere" >>$log && read -n1 < $read_f
    eval "exec $save_pipe>&1 1>&$save_stdout-"
    echo "f2(): BASHPID[$BASHPID]:" >>$log
    lsof -a -p $BASHPID -d '^mem,^cwd,^rtd,^txt' >>$log

    echo "f2(): stdout-2: ghi"
    echo "f2(): stderr-2: klm" >/dev/stderr
    echo "f2(): Before exit f2()" >>$log && read -n1 < $read_f
    return 0
}
f() {
    echo "f(): SUBSH=$BASH_SUBSHELL" >>$log
    echo "f(): \$\$[$$]:" >>$log
    lsof -a -p $$ -d '^mem,^cwd,^rtd,^txt' >>$log
    echo "f(): BASHPID[$BASHPID]:" >>$log
    lsof -a -p $BASHPID -d '^mem,^cwd,^rtd,^txt' >>$log

    echo 'f(): stdout-1: abc'
    echo 'f(): stderr-1: def' >/dev/stderr

    echo "f(): Before moving pipe somewhere" >>$log && read -n1 < $read_f
    eval "exec $save_pipe>&1 1>&$save_stdout-"
    echo "f(): BASHPID[$BASHPID]:" >>$log
    lsof -a -p $BASHPID -d '^mem,^cwd,^rtd,^txt' >>$log

    echo 'f(): stdout-2: abc'
    echo 'f(): stderr-2: def' >/dev/stderr

    echo "f(): Before calling f2()" >>$log && read -n1 < $read_f
    eval "exec $save_stdout>&1"
    v=$(f2)
    echo "-$v-"
    eval "exec $save_stdout>&-"

    echo "f(): BASHPID[$BASHPID]:" >>$log
    lsof -a -p $BASHPID -d '^mem,^cwd,^rtd,^txt' >>$log

    echo "f(): Before restoring pipe" >>$log && read -n1 < $read_f
    exec >&$save_pipe-

    echo 'f(): stdout-3: abc'
    echo 'f(): stderr-3: def' >/dev/stderr

    echo "f(): BASHPID[$BASHPID]:" >>$log
    lsof -a -p $BASHPID -d '^mem,^cwd,^rtd,^txt' >>$log
    echo "f(): Before exit f()" >>$log && read -n1 < $read_f
    return 0
}
declare -r -i save_stdout=9
declare -r -i save_pipe=7
eval "exec $save_stdout>&1"
v=$(f)
eval "exec $save_stdout>&-"
echo "in main()"
echo "\$\$[$$]:" >>$log
lsof -a -p $$ -d '^mem,^cwd,^rtd,^txt' >>$log
echo "-$v-"



Here is illustration:

                             (subshell)
  main()                   ....................
 +--------------+          .   f()            .
 | 3r  pipe     | call f() . +--------------+ .
 | 9u  "stdout" |----------->| 1w  pipe(f)  | .
 +--------------+          . | 9u  "stdout" | .
                           . +--------------+ .
                           .     |            .
                           .     | move pipe(f)
                           .     |            .
                           .     v            .           (subshell)
                           . +--------------+ .         ....................
                           . | 1w  "stdout" | .         .   f2()           .
                           . | 7w  pipe(f)  | call f2() . +--------------+ .
                           . | 9-  (closed) |------------>| 1w  pipe(f2) | .
                           . +--------------+ .         . | 7w  pipe(f)  | .
                           .                  .         . | 9u  "stdout" | .
                           .                  .         . +--------------+ .
                           .                  .         .     |            .
                           .                  .         .     | move pipe(f2)
                           .                  .         .     | over pipe(f)
                           .                  .         .     |            .
                           .                  .         .     v            .
                           .                  .         . +--------------+ .
                           . +--------------+ . return  . | 1w  "stdout" | .
                           . | 1w  "stdout" |<------------| 7w  pipe(f2) | .
                           . | 7w  pipe(f)  | .         . | 9-  (closed) | .
                           . +--------------+ .         . +--------------+ .
                           .     |            .         ....................
                           .     | restore pipe(f)
                           .     |            .
                           .     v            .
                           . +--------------+ .
 +--------------+  return  . | 1w  pipe(f)  | .
 | 1u  "stdout" |------------| 7-  (closed) | .
 +--------------+          . +--------------+ .
                           ....................


And here is log (slightly edited)

# ./t.sh >|./1.tmp

f(): SUBSH=1
f(): $$[4794]:
COMMAND  PID USER   FD   TYPE DEVICE SIZE    NODE NAME
t.sh    4794 root    0u   CHR    4,1         5466 /dev/vc/1
t.sh    4794 root    1w   REG    8,7    0 1121662 .../1.tmp
t.sh    4794 root    2u   CHR    4,1         5466 /dev/vc/1
t.sh    4794 root    3r  FIFO    0,6        14132 pipe
t.sh    4794 root    9w   REG    8,7    0 1121662 .../1.tmp
t.sh    4794 root  255r   REG    8,7 2075 1121653 .../t.sh
f(): BASHPID[4796]:
COMMAND  PID USER   FD   TYPE DEVICE SIZE    NODE NAME
t.sh    4796 root    0u   CHR    4,1         5466 /dev/vc/1
t.sh    4796 root    1w  FIFO    0,6        14132 pipe
t.sh    4796 root    2u   CHR    4,1         5466 /dev/vc/1
t.sh    4796 root    9w   REG    8,7    0 1121662 .../1.tmp
f(): Before moving pipe somewhere
f(): BASHPID[4796]:
COMMAND  PID USER   FD   TYPE DEVICE SIZE    NODE NAME
t.sh    4796 root    0u   CHR    4,1         5466 /dev/vc/1
t.sh    4796 root    1w   REG    8,7    0 1121662 .../1.tmp
t.sh    4796 root    2u   CHR    4,1         5466 /dev/vc/1
t.sh    4796 root    7w  FIFO    0,6        14132 pipe
f(): Before calling f2()
f2(): SUBSH=2
f2(): BASHPID[4803]:
COMMAND  PID USER   FD   TYPE DEVICE SIZE    NODE NAME
t.sh    4803 root    0u   CHR    4,1         5466 /dev/vc/1
t.sh    4803 root    1w  FIFO    0,6        14194 pipe
t.sh    4803 root    2u   CHR    4,1         5466 /dev/vc/1
t.sh    4803 root    7w  FIFO    0,6        14132 pipe
t.sh    4803 root    9w   REG    8,7   19 1121662 .../1.tmp
f2(): Before moving pipe somewhere
f2(): BASHPID[4803]:
COMMAND  PID USER   FD   TYPE DEVICE SIZE    NODE NAME
t.sh    4803 root    0u   CHR    4,1         5466 /dev/vc/1
t.sh    4803 root    1w   REG    8,7   19 1121662 .../1.tmp
t.sh    4803 root    2u   CHR    4,1         5466 /dev/vc/1
t.sh    4803 root    7w  FIFO    0,6        14194 pipe
f2(): Before exit f2()
f(): BASHPID[4796]:
COMMAND  PID USER   FD   TYPE DEVICE SIZE    NODE NAME
t.sh    4796 root    0u   CHR    4,1         5466 /dev/vc/1
t.sh    4796 root    1w   REG    8,7   61 1121662 .../1.tmp
t.sh    4796 root    2u   CHR    4,1         5466 /dev/vc/1
t.sh    4796 root    7w  FIFO    0,6        14132 pipe
f(): Before restoring pipe
f(): BASHPID[4796]:
COMMAND  PID USER   FD   TYPE DEVICE SIZE  NODE NAME
t.sh    4796 root    0u   CHR    4,1       5466 /dev/vc/1
t.sh    4796 root    1w  FIFO    0,6      14132 pipe
t.sh    4796 root    2u   CHR    4,1       5466 /dev/vc/1
f(): Before exit f()
$$[4794]:
COMMAND  PID USER   FD   TYPE DEVICE SIZE    NODE NAME
t.sh    4794 root    0u   CHR    4,1         5466 /dev/vc/1
t.sh    4794 root    1w   REG    8,7   71 1121662 .../1.tmp
t.sh    4794 root    2u   CHR    4,1         5466 /dev/vc/1
t.sh    4794 root  255r   REG    8,7 2075 1121653 .../t.sh

четверг, 20 мая 2010 г.

[summary][draft][part] Tabs in vim

DISCLAIMER. English language used here only for compatibility (ASCII only), so any suggestions about my bad grammar (and not only it) will be greatly appreciated.

Status: summary.
State: draft,part.
Detailed description: Some tables for illustrate <Tab>'s handling in vim. See vim help for details.

Tabs in vim {{{

    'sts' and 'sta' options {{{

        Following options affect <Tab>s and indents:

            'tabstop'       'ts'
            'shiftwidth'    'sw'
            'softtabstop'   'sts'
            'smarttab'      'sta'
            'expandtab'     'et'

        'sw' and 'ts' define standard vim operations behavior, they are not
        switches for some features (hence they're always set and always used).
        But 'sts' and 'sta' enables additional features, which affect (change)
        some vim operations behavior (hence, they can be unset to turn feature
        off).

        Table below shows what option will be used to determine how many
        positions insert or delete during some editing operations depending on
        activated modes: both 'sts' and 'sta' are off (default), 'sts' set,
        'sta' set and both 'sts' and 'sta' set. When editing operation insert
        less (or more) positions, than real <Tab> counts for, mix from spaces
        and real <Tab>s are used.


    +--------------------------+-------------------------------------------+
    |    Operation             |   Will be inserted .. positions           |
    |                          +------+-----------+-----------+------------+
    |                          |      | +sts      | +sta      | +sts +sta  |
    +--------------------------+------+-----------+-----------+------------+
    | Use >> , etc             |  sw  | sw        |  sw       |  sw        |
    +--------------------------+------+-----------+-----------+------------+
    | Type <Tab> or <BS>       |  ts  | sts (mix) |           |            |
    |                          +      +           +-----------+------------+
    |     at the start of line |      |           |  sw (mix) |  sw (mix)  |
    |                          +      +           +-----------+------------+
    |     in other places      |      |           |  ts       |  sts (mix) |
    +--------------------------+------+-----------+-----------+------------+
    | Real <Tab> length        |  ts  | ts        |  ts       |  ts        |
    +--------------------------+------+-----------+-----------+------------+

    }}}
    ':retab' and changing 'ts' option value {{{

        'ts' option changes real tabstop, but does not change text. Hence,
        indents, which made according to old tabstop, probably will be messed
        (i.e there will be visible changes in text), but actual file remains
        untouched. So, using 'undo' after changing 'ts' has no sense.

        :retab command changes both 'ts' option and text according to new 'ts'
        value in such way, that all indents remain the same (there will be no
        visible changes), though actual file will be changed (to preserve
        visible indents :retab pads them, if necessary, with spaces). So,
        using 'undo' after :retab recover text to previous state, but not
        recover 'ts' to previous value, hence indents may be messed (like
        after you change only 'ts') and to recover visible text state you need
        set 'ts' to previous value. Table below summarizes that.


        +-------------------+---------------+-----------+
        |                   | Change 'ts'   | :retab    |
        +-------------------+---------------+-----------+
        | 'ts' changed?     | yes           | yes       |
        +-------------------+---------------+-----------+
        | File changed?     | no            | yes       |
        +-------------------+---------------+-----------+
        | Visible changes?  | yes           | no        |
        +-------------------+---------------+-----------+

    }}}

}}}


вторник, 4 мая 2010 г.

[method][draft][part][need_check] (bash) passing arguments to function

DISCLAIMER. English language used here only for compatibility (ASCII only), so any suggestions about my bad grammar (and not only it) will be greatly appreciated.

Status: method.
State: suggestion,draft,part,need_check.
Detailed description: One more method for passing arguments to bash function.

Outline: below under 'passing argument as pointer' i mean passing argument by name:
function f() {
}

declare var='abc'
f var  # not f "$var"
This may be considered as analogy for pointers (in C), i think :-)

1. Passing argument as pointer to function sometimes may require redefinition
   it as local variable in order to preserve referred data. Though, generally
   pointer used to provide to function ability to modify caller's environment,
   in bash it may be used, to skip unnecessary copying of big argument, if we
   want to redefine all positional parameters to local variables (names
   1, 2, 3,.. is bad whenever, and also positional parameters may be used for
   some tricks, so it's better, when they do not contain useful data).

        eval "
                $(declare -p $2 | sed -e's/^[^=]\+=/local -a shortopts=/' );
                $(declare -p $3 | sed -e's/^[^=]\+=/local -a longopts=/');
        "

   Above construction is, seemingly, the only way. To correctly redefine
   pointer to local variable we should honour several contraints:
        1. we should not define any local variable before all pointers will be
           expanded, because otherwise there is a chance, that defined local
           variable name is match with some pointed to variable's name and we
           lose access to it.
        2. we should preserve all special characters inside pointed data.

   To handle first constraint, we need `eval`.

   To handle second, we should properly quote expanded pointed to variable's
   value:

        ${[@]} - split elements correctly only during first expand, but in the
        second (done in `eval`) these boundaries will be lost and bash splits
        elements by 'metacharacters', not by IFS or anything else (info bash,
        ch-2).
        ${[*]} - my be used, perhaps, with IFS=',', and then.. something
        artificial to set up quoting by brace expansion. But this rarely works,
        though (i don't know working example).
        `declare` - seems, only way to obtain correct quoting without many
        evals and artificial tricks. But its output, perhaps, should be
        processed by some program to replace variable name to desired one.


[summary][draft][part] Bash script parsing

DISCLAIMER. English language used here only for compatibility (ASCII only), so any suggestions about my bad grammar (and not only it) will be greatly appreciated.

Status: summary.
State: draft,part.
Detailed description: illustration for info bash ch-2.

Bash script parsing units:

                    token = single unit
                   /     \
                 /         \
               /             \
             /                 \
          word                operator
         / | \                /     \
       /   |   \             /       \
     /     |     \          /         \
reserved  name    ..     control     redirection
word                     operator     operator


              metacharacter - separate words (or tokens?)
              /     \   
            /         \ 
          /             \
       blank           some (why not all?)
                       control operators

пятница, 23 апреля 2010 г.

[additions][done][unmaintained] Дополнение к инструкции по рациям Joker/Kenwood TK/JK-450S.

Status: additions.
State: done,unmaintained.
Detailed description: ...

TODO: [dropped] про набор номера канала.
TODO: [dropped] что включает функция F+0 ?


Таблица настроек субтонов CTCSS/DCS.

        +-----------------------+--------------------------------------+
        | Действие              |             Настройка                |
        |                       +------------------+-------------------+
        |                       | CTCSS            | DCS               |
        +-----------------------+------------------+-------------------+
        | Вкл/выкл CTCSS/DCS    |             (20) CT.DCS              |
        +-----------------------+--------+---------+---------+---------+
        | Уст. тон приема       | (7) RC | (21) CT | (9)  RD | (22) DC |
        +-----------------------+--------+         +---------+         |
        | Уст. тон передачи     | (8) TC |         | (10) TD |         |
        +-----------------------+--------+---------+---------+---------+

        Примечания.
                1. настройки (21) CT и (22) DC устанавливают и тон приема, и
                   тон передачи в одинаковые значения. Поэтому, если тон
                   приема и передачи будут одинаковые, то ими пользоваться
                   удобнее, чем двумя разными настройками (7)/(8) или
                   (9)/(10).

Настройка разных субтонов на прием/передачу и сдвиг частот приема/передачи.

        Сдвиг (как частот, так и субтонов) будет работать только при разговоре
        двух раций: третья (R-3) рация сможет либо общаться только с первой
        (R-1), либо только со второй (R-2). Для разговора c первой надо
        настроить третью, как вторую (см. схему ниже). В этом случае, третья
        не будет слышать вторую, а вторая не будет слышать третью.  Для
        разговора со второй надо настроить третью, как первую.

                    +-----------------------+ 
                    v                       | 
                    Rx                 +-------------------->  Rx 
              [Ch=B, DCS=Ab]           |    |            [Ch=A, DCS=Ad]
                +-----+                |    |                +-----+
                | R-1 |       Tx ------+    +------ Tx       | R-2 |
                |     | [Ch=A, DCS=Ad]        [Ch=B, DCS=Ab] |     |
                +-----+                                      +-----+

        Обозначения:
                Ch      - канал (частота);
                DCS     - тон DCS;

        Последовательность настройки:

                A=433.265, Ad=025I, B=433.255, Bd=023N

                1. Настраиваем рации на частоту приема.
                   R-1: ставим частоту 433.255;
                   R-2: ставим частоту 433.265;
                2. Установить сдвиг частот.
                   R-1: (6) OFFSET ставим на 0.010;
                   R-2: (6) OFFSET ставим на 0.010;
                3. Включить сдвиг частот.
                   R-1: (5) SFT ставим на '+';
                   R-2: (5) SFT ставим на '-';

                Сдвиг частот включен. Проверяем, что связь между рациями
                работает.

                4. Настраиваем DCS тон передачи.
                   R-1: (10) Td ставим на '025I';
                   R-2: (10) Td ставим на '023N';
                5. Настраиваем DCS тон приема.
                   R-1: (9) Rd ставим на '023N';
                   R-2: (9) Rd ставим на '025I';
                6. Включаем использование DCS тонов.
                   R-1: (20) CT.DCS ставим на 'DCS';
                   R-2: (20) CT.DCS ставим на 'DCS';

                DCS тона включены. Проверяем, что связь между рациями
                работает.

Режимы сканирование:
        (1) SCAN=  TO - остановка на найденном сигнале и продолжение 
                        через 5 секунд;
                   CO - остановка на найденном сигнале и продолжение через 5
                        секунд после пропадания сигнала;
                   SE - остановка на первом найденном сигнале;

Другие настройки:
        (2) TOT - Tx timeout Timer
                время (в секундах), после которого передача отключится даже
                при нажатой PTT (на случай залипшей PTT).
        (11) APO - Automatic Power-off
                автоматическое отключение рации через заданное время (в
                минутах).

В этой заметке использованы материалы с форумов 
tucson-club.ru (tucson-club.ru/forum/...)
и
lpd.radioscanner.ru (lpd.radioscanner.ru/topic...).