xcodeのplaygroundでrunningが永遠に終わらない

前書き

iPhoneアプリでも作ろうとmacを新調して、swiftの勉強を始めたのはいいけど
なぜか、永遠に終わらなくて、勉強が進まないので、CUIでswift でやってたけど、これじゃUIKit使えなくて詰んだ

本文

https://stackoverflow.com/questions/39978092/xcode-playground-gets-stuck-on-running-playground-or-launching-simulator-and

Buggy Simulator's main process is still working even after Xcode closing. So here're the steps:

Quit Xcode;
Kill com.apple.CoreSimulator.CoreSimulatorService process in Activity Monitor;
Launch Xcode and run your Playground.

まず、xcodeを終了しておく

シェル上でPIDを確認

$ ps -xa|grep com.apple.Core
22361 ??         0:04.56 /Library/Developer/PrivateFrameworks/CoreSimulator.framework/Versions/A/XPCServices/com.apple.CoreSimulator.CoreSimulatorService.xpc/Contents/MacOS/com.apple.CoreSimulator.CoreSimulatorService
22790 ttys002    0:00.00 grep com.apple.Core

普通のkillじゃ死なないので、-9をつける

$ kill -9 22361

死んだのを確認

$ ps -xa|grep com.apple.Core
22790 ttys002    0:00.00 grep com.apple.Core

これでおk、あとはxcodeを起動すればできる

JSONを読み書きできるWebAPI serverを書いた

前書き

最近、BTCのシステムトレードを始めて、プログラムに拡張性を持たせるために必要性を感じたから書いた
今までは、tickerを取得するプログラム -> 分析するプログラム -> 売買するAPI飛ばすプログラム
っていう動きにしてたけど、全部、API serverを通してやった方が、新しいプログラム書いた時に修正しやすいと思った
pythonはろくに触ったことないし、プログラミングも久々だったので、書くまでにすごい時間かかった

本文

https://github.com/silenvx/api_server_string_io
ソースはここ
APIはRESTとWebSocketどっちも使える。詳しくはREADME.mdを見るか、ソースを読んでください

書くとき、苦労したとこ

threadingが何故か使えないので、ws.send()をするとき、処理が止まってしまうが、ws.receive()で入力待ちの時は
なぜか、threadな動きをしていたので、どうにかしたらできると信じて丸1日かけて調べたら、gevent.sleep()使うといいことがわかった
あとは、websocketは更新された場合に送るようにしたかったので、更新されたか比較する部分を作ったが、同時にwebsocketで取得されるとそれぞれに
更新された情報を送ったかのフラグが必要だったので、id(ws)でオブジェクトを識別させて管理した

tmuxで~/.tmux.confに書けるオプションを足す方法

前書き

以前色々とpatchを書いたが、環境によって使い分けられるように~/.tmux.confで制御しようと思った
のでoption関連のコードリーディングした
したのはいいけどソースコードからしか情報とってないのでtmuxを書いてる人たちがどういった規格でコードを書いてるのか知りません

本文

改変する必要があるのはoptions-table.c
まず追加する先の選択肢として3種類ある
ServerとSessionとWindowといった構造体
それぞれの使い分けはServerとSessionはset-option
Windowはset-window-optionで設定できる
Serverは常にグローバル変数を使っている
SessionとWindowは設定で引数に-gを使ったらグローバル変数を使うようになってるが指定しなくても挙動に変化でないしどうしたいのかわからん(俺のtmuxがおかしいのか?)

次にその構造体にどう書くのか例を書く

const char *options_table_status_keys_list[] = {    //のちに登場するtypeがOPTIONS_TABLE_CHOICEのための選択肢
    "emacs", "vi", NULL
};

//ここではsessionを例に使う
const struct options_table_entry session_options_table[] = {
    { .name = "default-command",            //set-option ここの部分
      .type = OPTIONS_TABLE_STRING,         //オプションの値を文字列として扱う
      .default_str = ""                     //初期のこのオプションに対する値
    },

    { .name = "assume-paste-time",
      .type = OPTIONS_TABLE_NUMBER,         //オプションの値を数値として扱う
      .minimum = 0,                         //数値の最低値
      .maximum = INT_MAX,                   //数値の最大値
      .default_num = 1,                     //初期のこのオプションに対する値
    },

    { .name = "prefix",
      .type = OPTIONS_TABLE_KEY,            //オプションの値をキー入力として扱う
      .default_num = '\002',                //初期の値、指定できるやつはkey-string.c見たらわかる
    },

    { .name = "status-bg",                  //backgroundとして扱うなら.nameの後ろに-bgを入れないといけない
      .type = OPTIONS_TABLE_COLOUR,         //オプションの値をblackとかredみたいな色として扱う
      .default_num = 2,                     //初期の値、この場合green、指定できるやつはcolour.cを見たらわかる
      .style = "status-style"               //この色を適用したいstyleを指定する
    },

    { .name = "status-attr",                //attributesとして扱うなら.nameの後ろに-attrを入れないといけない
      .type = OPTIONS_TABLE_ATTRIBUTES,     //オプションの値をbrightとかboldみたいな文字の表現として扱う
      .default_num = 0,                     //初期の値、この場合none、指定できるやつはattributes.cを見たらわかる
      .style = "status-style"               //この表現を適用したいstyleを指定する
    },

    { .name = "status",
      .type = OPTIONS_TABLE_FLAG,           //オプションの値を0か1のみ使用してフラグとして使う
      .default_num = 1                      //初期の値、この場合、on、指定できるやつは0 off no 1 on yes cmd-set-option.cを見たらわかる
    },

    { .name = "status-keys",
      .type = OPTIONS_TABLE_CHOICE,         //オプションの値を定義したリストの中から選べる
      .choices = options_table_status_keys_list, //定義した文字列構造体の変数名を指定する
      .default_num = MODEKEY_EMACS          //初期の値、例に書いてるのはtmux.hで定義されてる、0
    },

    { .name = "status-style",
      .type = OPTIONS_TABLE_STYLE,          //オプションの値としてstatus-bgとかstatus-attrをまとめて書ける
      .default_str = "bg=green,fg=black"    //初期の値、style.cで式が評価される
    }

    { .name = NULL }                        //終わりを示すためにNULL
};

こんな感じのをかけたら後は
typeがOPTIONS_TABLE_STRINGの場合

char * options_get_string(struct options *oo, const char *name)

typeがOPTIONS_TABLE_STYLEの場合

struct grid_cell * options_get_style(struct options *oo, const char *name)

typeがそれ以外の場合

long long options_get_number(struct options *oo, const char *name)

で取得が可能です

書くためにやった手順(tmux-1.9a)

ソースコードを展開してそのディレクトリ内を~/.tmux.confに書くオプション名で検索する
ここではdefault-terminalを採用

% find ./tmux-1.9a -type f -exec grep -nH "default-terminal" {} +
./tmux-1.9a/examples/tmux.vim:177:      \ default-terminal
./tmux-1.9a/tmux.1:2227:.It Ic default-terminal Ar terminal
./tmux-1.9a/options-table.c:133:        { .name = "default-terminal",
./tmux-1.9a/server-fn.c:39:             term = options_get_string(&s->options, "default-terminal");
./tmux-1.9a/FAQ:183:    set -g default-terminal "screen-256color"
./tmux-1.9a/FAQ:378:    set -g default-terminal "screen-it"

この中で関係がありそうなものは

./tmux-1.9a/options-table.c:133:        { .name = "default-terminal",
./tmux-1.9a/server-fn.c:39:             term = options_get_string(&s->options, "default-terminal");

の2つになります。ファイル名から推測するに

./tmux-1.9a/options-table.c:133:        { .name = "default-terminal",

これでオプションを定義して

./tmux-1.9a/server-fn.c:39:             term = options_get_string(&s->options, "default-terminal");

これで定義しているように見えるのでoptions-table.cから先に見てみることにする

するとこのファイルの中ではオプションがいくつか分けられていて

/* Server options. */
const struct options_table_entry server_options_table[] = {

/* Session options. */
const struct options_table_entry session_options_table[] = {

/* Window options. */
const struct options_table_entry window_options_table[] = {

の3種類のオプションに分かれていることがわかる。この中から追加したいオプションがこのカテゴリに含まれるのかを考えて足す必要がありそう
とりあえずdefault-terminalを見ることにする

    { .name = "default-terminal",
        .type = OPTIONS_TABLE_STRING,
        .default_str = "screen"
    },

なるほど・・・わからんということでこの型を定義しているであろうincludeしているtmux.hを見る

extern const struct options_table_entry session_options_table[];

なのでoptions_table_entry型

struct options_table_entry {
    const char              *name;
    enum options_table_type type;

    u_int                   minimum;
    u_int                   maximum;
    const char              **choices;

    const char              *default_str;
    long long               default_num;

    const char              *style;
};

に新たに出てきたoptions_table_typeの定義を見る

enum options_table_type {
    OPTIONS_TABLE_STRING,
    OPTIONS_TABLE_NUMBER,
    OPTIONS_TABLE_KEY,
    OPTIONS_TABLE_COLOUR,
    OPTIONS_TABLE_ATTRIBUTES,
    OPTIONS_TABLE_FLAG,
    OPTIONS_TABLE_CHOICE,
    OPTIONS_TABLE_STYLE
};

なるほど・・・だいたい定義の仕方はわかったので次に読み取る関数の options_get_string(); を使っているところを調べる

% find ./tmux-1.9a -type f -exec grep -nH "options_get_string" {} +
./tmux-1.9a/window-copy.c:580:              options_get_string(&sess->options, "word-separators");
./tmux-1.9a/window-copy.c:586:              options_get_string(&sess->options, "word-separators");
./tmux-1.9a/window-copy.c:596:              options_get_string(&sess->options, "word-separators");
./tmux-1.9a/session.c:247:      shell = options_get_string(&s->options, "default-shell");
./tmux-1.9a/server-window.c:208:        ptr = options_get_string(&w->options, "monitor-content");
./tmux-1.9a/cmd-attach-session.c:170:           update = options_get_string(&s->options, "update-environment");
./tmux-1.9a/cmd-new-session.c:190:              cmd = options_get_string(&global_s_options, "default-command");
./tmux-1.9a/cmd-new-session.c:194:      update = options_get_string(&global_s_options, "update-environment");
./tmux-1.9a/cmd-split-window.c:83:              cmd = options_get_string(&s->options, "default-command");
./tmux-1.9a/cmd-split-window.c:141:     shell = options_get_string(&s->options, "default-shell");
./tmux-1.9a/server-client.c:129:        overrides = options_get_string(oo, "terminal-overrides");
./tmux-1.9a/server-client.c:777:        template = options_get_string(&s->options, "set-titles-string");
./tmux-1.9a/server-client.c:1054:       shell = options_get_string(&global_s_options, "default-shell");
./tmux-1.9a/status.c:88:            NULL, NULL, options_get_string(&s->options, "status-left"), t, 1);
./tmux-1.9a/status.c:109:           NULL, NULL, options_get_string(&s->options, "status-right"), t, 1);
./tmux-1.9a/status.c:211:               sep = options_get_string(oo, "window-status-separator");
./tmux-1.9a/status.c:226:               sep = options_get_string(oo, "window-status-separator");
./tmux-1.9a/status.c:634:       fmt = options_get_string(oo, "window-status-format");
./tmux-1.9a/status.c:637:               fmt = options_get_string(oo, "window-status-current-format");
./tmux-1.9a/status.c:1045:              wsep = options_get_string(oo, "word-separators");
./tmux-1.9a/status.c:1078:                      wsep = options_get_string(oo, "word-separators");
./tmux-1.9a/status.c:1101:                      wsep = options_get_string(oo, "word-separators");
./tmux-1.9a/status.c:1129:                      wsep = options_get_string(oo, "word-separators");
./tmux-1.9a/cmd-set-option.c:347:               oldval = options_get_string(oo, oe->name);
./tmux-1.9a/options.c:124:options_get_string(struct options *oo, const char *name)
./tmux-1.9a/tmux.h:1579:char   *options_get_string(struct options *, const char *);
./tmux-1.9a/cmd-new-window.c:86:                cmd = options_get_string(&s->options, "default-command");
./tmux-1.9a/names.c:86: fmt = options_get_string(&w->options, "automatic-rename-format");
./tmux-1.9a/server-fn.c:39:             term = options_get_string(&s->options, "default-terminal");
./tmux-1.9a/server-fn.c:246:    cmd = options_get_string(&c->session->options, "lock-command");

オプションの数に対して関数使っているところが少ないので別の関数もある予感
定義しているっぽい

./tmux-1.9a/options.c:124:options_get_string(struct options *oo, const char *name)

を見に行く

    char *
options_get_string(struct options *oo, const char *name)
{
    struct options_entry    o;

    if ((o = options_find(oo, name)) == NULL)
        fatalx("missing option");
    if (o->type != OPTIONS_STRING)
        fatalx("option not a string");
    return (o->str);
}

こんな定義になってる
関数名だけで判断するなら最初のif文でそのオプションが存在するのか
次のif文でそのオプションの値は文字列なのかどうかを見ているっぽい
他にも似たやつで

    long long
options_get_number(struct options *oo, const char *name)
{
    struct options_entry    *o;

    if ((o = options_find(oo, name)) == NULL)
        fatalx("missing option");
    if (o->type != OPTIONS_NUMBER)
        fatalx("option not a number");
    return (o->num);
}
    struct grid_cell *
options_get_style(struct options *oo, const char *name)
{
    struct options_entry    *o;

    if ((o = options_find(oo, name)) == NULL)
        fatalx("missing option");
    if (o->type != OPTIONS_STYLE)
        fatalx("option not a style");
    return (&o->style);
}

こんなのがある

色々ソースコードを追ってみたけど、変更する箇所は
options-table.cだけで良さげ
この中で追加するために考える必要がありそうなのは

/* Server options. */
const struct options_table_entry server_options_table[] = {

/* Session options. */
const struct options_table_entry session_options_table[] = {

/* Window options. */
const struct options_table_entry window_options_table[] = {

この中のどのジャンルに含まれるかを考える
中のオプションを見たとき
~/.tmux.confに書くとき
Server optionsはset-optionで
Session optionsはset-optionで
Window optionsはset-window-optionで書くことがわかる
じゃあServerとSessionの違いは?ということで調べたらそれぞれ取得するときの第1引数に使うものが異なってくる
Server optionsの場合

options_get_number(&global_options, "buffer-limit");

Session optionsの場合

options_get_string(&s->options, "default-terminal");

というように&global_optionsと&s->optionsで使い分けている
同じようにwindowは&w->options
global_optionsはtmux.cでoptions型のglobal変数として定義されている
s->optionsは

void
server_fill_environ(struct session *s, struct environ *env)
{
    char    var[MAXPATHLEN], *term;
    u_int   idx;
    long    pid;

    if (s != NULL) {
        term = options_get_string(&s->options, "default-terminal");
        environ_set(env, "TERM", term);

        idx = s->id;
    } else
        idx = -1;
    pid = getpid();
    xsnprintf(var, sizeof var, "%s,%ld,%d", socket_path, pid, idx);
    environ_set(env, "TMUX", var);
}

で書いてる通りsession型、定義はtmux.h

struct session {
    u_int        id;

    char        *name;
    int      cwd;

    struct timeval   creation_time;
    struct timeval   activity_time;
    struct timeval   last_activity_time;

    u_int        sx;
    u_int        sy;

    struct winlink  *curw;
    struct winlink_stack lastw;
    struct winlinks  windows;

    struct options   options;

#define SESSION_UNATTACHED 0x1  /* not attached to any clients */
    int      flags;

    struct termios  *tio;

    struct environ   environ;

    int      references;

    TAILQ_ENTRY(session) gentry;
    RB_ENTRY(session)    entry;
};

s->optionsがどうやって作られてるのか調べるために関数を呼び出した関数を呼び出した…と遡っていくと
session.cにて

options_init(&s->options, &global_s_options);

global_s_options変数から生成されているっぽい
これもtmux.cでoptions型のglobal変数として定義されている
同じようにwindow用のglobal_w_options変数も定義されている
これの初期化はtmux.cの

    options_init(&global_options, NULL);
    options_table_populate_tree(server_options_table, &global_options);
    options_set_number(&global_options, "quiet", quiet);

    options_init(&global_s_options, NULL);
    options_table_populate_tree(session_options_table, &global_s_options);
    options_set_string(&global_s_options, "default-shell", "%s", getshell());

    options_init(&global_w_options, NULL);
    options_table_populate_tree(window_options_table, &global_w_options);

そしてsession.cやwindow.cで新しく作られたときに

    options_init(&s->options, &global_s_options);

    options_init(&w->options, &global_w_options);

のようにそれぞれのセッションやウィンドウのoptionの設定に反映させているのがわかる

では初期化以外でoptionの設定をいじる場合どこでいじるのかということで
options_set_*を使用している初期化以外の関数を見たらcmd-set-option.cに
cmd_set_option_*という法則で関数名が定義されていた
さらにそれを追ったらcmd_set_option_exec関数にたどり着いた

enum cmd_retval
cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq)
{
    struct args             *args = self->args;
    const struct options_table_entry    *table, *oe;
    struct session              *s;
    struct winlink              *wl;
    struct client               *c;
    struct options              *oo;
    struct window               *w;
    const char              *optstr, *valstr;
    u_int                    i;

    /* Get the option name and value. */
    optstr = args->argv[0];
    if (*optstr == '\0') {
        cmdq_error(cmdq, "invalid option");
        return (CMD_RETURN_ERROR);
    }
    if (args->argc < 2)
        valstr = NULL;
    else
        valstr = args->argv[1];

    /* Is this a user option? */
    if (*optstr == '@')
        return (cmd_set_option_user(self, cmdq, optstr, valstr));

    /* Find the option entry, try each table. */
    table = oe = NULL;
    if (options_table_find(optstr, &table, &oe) != 0) {
        cmdq_error(cmdq, "ambiguous option: %s", optstr);
        return (CMD_RETURN_ERROR);
    }
    if (oe == NULL) {
        cmdq_error(cmdq, "unknown option: %s", optstr);
        return (CMD_RETURN_ERROR);
    }

    /* Work out the tree from the table. */
    if (table == server_options_table)
        oo = &global_options;
    else if (table == window_options_table) {
        if (args_has(self->args, 'g'))
            oo = &global_w_options;
        else {
            wl = cmd_find_window(cmdq, args_get(args, 't'), NULL);
            if (wl == NULL) {
                cmdq_error(cmdq,
                    "couldn't set '%s'%s", optstr,
                    (!args_has(args, 't') && !args_has(args,
                    'g')) ? " need target window or -g" : "");
                return (CMD_RETURN_ERROR);
            }
            oo = &wl->window->options;
        }
    } else if (table == session_options_table) {
        if (args_has(self->args, 'g'))
            oo = &global_s_options;
        else {
            s = cmd_find_session(cmdq, args_get(args, 't'), 0);
            if (s == NULL) {
                cmdq_error(cmdq,
                    "couldn't set '%s'%s", optstr,
                    (!args_has(args, 't') && !args_has(args,
                    'g')) ? " need target session or -g" : "");
                return (CMD_RETURN_ERROR);
            }
            oo = &s->options;
        }
    } else {
        cmdq_error(cmdq, "unknown table");
        return (CMD_RETURN_ERROR);
    }

    /* Unset or set the option. */
    if (args_has(args, 'u')) {
        if (cmd_set_option_unset(self, cmdq, oe, oo, valstr) != 0)
            return (CMD_RETURN_ERROR);
    } else {
        if (args_has(args, 'o') && options_find1(oo, optstr) != NULL) {
            if (!args_has(args, 'q'))
                cmdq_print(cmdq, "already set: %s", optstr);
            return (CMD_RETURN_NORMAL);
        }
        if (cmd_set_option_set(self, cmdq, oe, oo, valstr) != 0)
            return (CMD_RETURN_ERROR);
    }

    /* Start or stop timers when automatic-rename changed. */
    if (strcmp(oe->name, "automatic-rename") == 0) {
        for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
            if ((w = ARRAY_ITEM(&windows, i)) == NULL)
                continue;
            if (options_get_number(&w->options, "automatic-rename"))
                queue_window_name(w);
            else if (event_initialized(&w->name_timer))
                evtimer_del(&w->name_timer);
        }
    }

    /* Update sizes and redraw. May not need it but meh. */
    recalculate_sizes();
    for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
        c = ARRAY_ITEM(&clients, i);
        if (c != NULL && c->session != NULL)
            server_redraw_client(c);
    }

    return (CMD_RETURN_NORMAL);
}

引数に使われるoption型はオプション名でServerかWindowかSessionのオプションかを判別して
WindowまたはSessionの場合は引数に-tがあればその指定した奴だけ変更するようになっている
そこから考えるに

/* Server options. */
const struct options_table_entry server_options_table[] = {

/* Session options. */
const struct options_table_entry session_options_table[] = {

/* Window options. */
const struct options_table_entry window_options_table[] = {

Serverはtmux全体でSessionとかWindowの概念とかそんなもの使わない奴に使って
Sessionはセッションごと
Windowはウィンドウごとの定義に使うみたい (正直最初から薄々気づいてた)

次にこれの使い分けについて調べる

enum options_table_type {
    OPTIONS_TABLE_STRING,
    OPTIONS_TABLE_NUMBER,
    OPTIONS_TABLE_KEY,
    OPTIONS_TABLE_COLOUR,
    OPTIONS_TABLE_ATTRIBUTES,
    OPTIONS_TABLE_FLAG,
    OPTIONS_TABLE_CHOICE,
    OPTIONS_TABLE_STYLE
};
>||
% find ./tmux-1.9a -type f -exec grep -nH "OPTIONS_TABLE_STRING" {} +
./tmux-1.9a/cmd-set-option.c:299:       case OPTIONS_TABLE_STRING:
(中略)
./tmux-1.9a/options-table.c:854:                case OPTIONS_TABLE_STRING:
./tmux-1.9a/options-table.c:877:        case OPTIONS_TABLE_STRING:

この中で1個ずつ確認して一番怪しい初期化の時に呼び出している

void
options_table_populate_tree(
    const struct options_table_entry *table, struct options *oo)
{
    const struct options_table_entry    *oe;

    for (oe = table; oe->name != NULL; oe++) {
        switch (oe->type) {
        case OPTIONS_TABLE_STRING:
            options_set_string(oo, oe->name, "%s", oe->default_str);
            break;
        case OPTIONS_TABLE_STYLE:
            options_set_style(oo, oe->name, oe->default_str, 0);
            break;
        default:
            options_set_number(oo, oe->name, oe->default_num);
            break;
        }
    }
}

を見るに設定を書き込む方法が3種類で実現されている
それぞれの関数はoptions.cで定義されているので見ると

struct options_entry *printflike3
options_set_string(struct options *oo, const char *name, const char *fmt, ...)

がnameの文字列を代入

struct options_entry *
options_set_number(struct options *oo, const char *name, long long value)

valueの値を代入

struct options_entry *
options_set_style(struct options *oo, const char *name, const char *value,
    int append)

style_parse関数で式を評価して正しければvalueの指定されている色を設定している

次にnameやvalueの数値が入力可能な数値か判定している関数があるので見る

/* Set an option. */
int
cmd_set_option_set(struct cmd *self, struct cmd_q *cmdq,
    const struct options_table_entry *oe, struct options *oo, const char *value)

この関数から
cmd_set_option_( typle ) (self, cmdq, oe, oo, value);
みたいな形式の関数を呼び出している

やってることはだいたいこんな感じ

type 処理
string 引数にaがあれば前の値の後ろに付け足す
number 最低値と最大値の値が超えてないか
key キーを指定できてるか
colour blackとかredとか色を指定できているか
attributes brightとかboldとか文字の表現を指定できてるか
flag 空エンターならflagを逆に,onかyesかoffかnoになってるか
choice choices変数の構造体にある文字列かどうか
style 引数にaがあるか

colourは評価が終えた後styleに指定している所に色の設定を加えます
style_update_new関数の仕様上name変数は最後が-bgか-fgかじゃないと動きません
同じようにattributesは-attrじゃないと動きません

choiceに入る構造体は

const char *options_table_mode_keys_list[] = {
    "emacs", "vi", NULL
};

こんな感じで最後にNULLがあればおk

これで必要な情報が揃ったので実際に実装するとしたら
options-table.cにある

/* Server options. */
const struct options_table_entry server_options_table[] = {

/* Session options. */
const struct options_table_entry session_options_table[] = {

/* Window options. */
const struct options_table_entry window_options_table[] = {

この構造体の中から好きなものを選び
typeの場合によっては.choiceに入れる構造体や.styleを付け足せばオプションが追加できる
処理の中で取得するには

    char * options_get_string(struct options *oo, const char *name)
    long long options_get_number(struct options *oo, const char *name)
    struct grid_cell * options_get_style(struct options *oo, const char *name)

この関数を使って取得できる

後書き

http://mojavy.com/blog/2011/12/06/tmux_advent_calendar_2011/
書いてる途中で先駆者を見つけてしまったがまあいいか

UTF-8-CJKのPKGBUILDを作った

前書き

http://d.hatena.ne.jp/silenvx/20120929/1348930210
以前こんな記事を書いてcharmapsのUTF-8.gzを改造してUTF-8-CJK.gzを生成した
のはいいけど新しい環境に行くたびにやるのがめんどくさいんでPKGBUILD書いた

本文

https://github.com/silenvx/PKGBUILD.git
ここのutf-8-cjkにおいた

書くためにやったこと

まずUTF-8.gzがどんな方法で追加されてるのか調べる

% pkgfile UTF-8.gz
core/glibc

glibcでインストールされていることがわかったので生成方法を調べるためにソースコードをダウンロード

% yaourt -G glibc
% cd glibc
% makepkg -o
% cd src

次に生成に関連してそうなキーワードとして生成元のデータが書かれているEastAsianWidth.txtで検索

% find . -type f -exec grep -nH "EastAsianWidth.txt" {} +
./src/glibc/localedata/unicode-gen/utf8_gen.py:25:Usage: python3 utf8_gen.py UnicodeData.txt EastAsianWidth.txt
./src/glibc/localedata/unicode-gen/utf8_gen.py:205:    outfile.write('%        "grep \'^[^;]*;[WF]\' EastAsianWidth.txt"\n')
./src/glibc/localedata/unicode-gen/utf8_gen.py:220:    EastAsianWidth.txt
./src/glibc/localedata/unicode-gen/utf8_gen.py:231:        # If an entry in EastAsianWidth.txt is found, it overrides entries in
./src/glibc/localedata/unicode-gen/utf8_gen.py:252:        print("USAGE: python3 utf8_gen.py UnicodeData.txt EastAsianWidth.txt")
./src/glibc/localedata/unicode-gen/utf8_gen.py:267:                # the EastAsianWidth.txt file.
./src/glibc/localedata/unicode-gen/utf8_gen.py:277:            # Processing EastAsianWidth.txt and write WIDTH to UTF-8 file
./src/glibc/localedata/unicode-gen/utf8_compatibility.py:240:        help='The EastAsianWidth.txt file to read.')
./src/glibc/localedata/unicode-gen/Makefile:43:DOWNLOADS = UnicodeData.txt DerivedCoreProperties.txt EastAsianWidth.txt
./src/glibc/localedata/unicode-gen/Makefile:93:UTF-8: UnicodeData.txt EastAsianWidth.txt
./src/glibc/localedata/unicode-gen/Makefile:95: $(PYTHON3) utf8_gen.py UnicodeData.txt EastAsianWidth.txt
./src/glibc/localedata/unicode-gen/Makefile:100:        -e EastAsianWidth.txt -o ../charmaps/UTF-8 \
./src/glibc/localedata/charmaps/GB18030:88685:%        "grep '^[^;]*;[AWF]' EastAsianWidth.txt"
./src/glibc/localedata/charmaps/GB18030:88686:%   and  "grep '^[^;]*;[^AWF]' EastAsianWidth.txt"
./src/glibc/localedata/charmaps/UTF-8:43789:%        "grep '^[^;]*;[WF]' EastAsianWidth.txt"
./src/glibc/localedata/ChangeLog:69:    * unicode-gen/EastAsianWidth.txt: Likewise.
./src/glibc/localedata/ChangeLog:102:   (UTF-8-report): Reference UnicodeData.txt and EastAsianWidth.txt.
./src/glibc/localedata/ChangeLog:237:   * unicode-gen/EastAsianWidth.txt: New, from Unicode.
Binary file ./src/glibc/.git/index matches
./src/glibc/benchtests/strcoll-inputs/filelist#en_US.UTF-8:10925:EastAsianWidth.txt

ChangeLogは関係なさそうなので
./src/glibc/localedata/unicode-gen/のMakefileからutf8-gen.pyを呼び出してるのがわかる
Makefileを見たら

UTF-8: UnicodeData.txt EastAsianWidth.txt
UTF-8: utf8_gen.py
    $(PYTHON3) utf8_gen.py UnicodeData.txt EastAsianWidth.txt

これしかしてない
なのでutf8_gen.pyをmain関数から追って行くと

if re.match(r'^[^;]*;[WF]', LINE):

この部分で2文字分の幅の文字をhitさせていたのでAを追加して

if re.match(r'^[^;]*;[WFA]', LINE):

こんな感じにして実行したらうまい具合に動いたのでこれでおk
後はPKGBUILDを書くだけだけどglibcにpatch当てる形式じゃ毎回コンパイル糞みたいにかかる上に
システムのUTF-8.gzをいじるのは嫌だったので新たにUTF-8-CJK.gzを追加するようにした

お名前.comで取得した独自ドメインをさくらvpsに当てている時にサブドメインを自宅に当てたい

前書き

ついに固定回線引いたのはいいが可変IPなので外からでも自宅に繋げられるように自宅にサブドメイン割り当てようと思った
環境はVPSがarchlinux
自宅がRaspberry pi上でRaspbianだけどssh打てたらなんでもいい
これからやるのはVPSddns鯖立てる

本文

1.vps上にdns鯖立てるためにbindを入れる
    # pacman -S bind

設定ファイル/etc/named.confにこれを追加する、noter.jpの箇所は各自の独自ドメイン

    zone "noter.jp" IN {                                                        
        type master;                                                            
        file "noter.jp.zone";                                                   
        allow-transfer { 210.172.129.81; };                                     
        allow-update { 127.0.0.1; };                                            
    };               

次にさっきのファイルのdirectoryで指定しているディレクトリにさっき書いたfileの場所に以下の内容のファイルを作る
コメント書きたかったら「;」
/var/named/noter.jp.zone

    $TTL 3600
    *       IN      SOA     noter.jp. root.noter.jp. (
                            2016040301
                            3600
                            900
                            604800
                            86400
                            )
            NS      *
            NS      ns1.noter.jp.
            NS      2nd.dnsv.jp.
            A       自分のIP
    ns1     A       自分のIP
    自宅    A       家のIP

設定は終わったのでbind使うようにする

    # systemctl enable named.service
    # systemctl start named.service

まだ公開してない状態で正引きが設定した家のIPであることを確認

    # nslookup 自宅.noter.jp localhost
2.お名前.comでプライマリネームサーバ IPアドレスの設定

お名前.comにログインしたら
Navi TOP -> ネームサーバーの変更する -> DNS関連機能の設定 -> 設定するドメイン選んで次へ
セカンダリDNS(Slave)を利用する -> プライマリネームサーバ IPアドレスに自分のVPSのIPを入力

3.お名前.comでホスト設定

Navi TOP -> ネームサーバーの変更する -> DNS関連機能の設定 -> 設定するドメイン選んで次へ
ネームサーバー名としてのホストを設定する -> 作成を選択 -> DNS鯖として使うドメインとIPを入力する。例ではns1.noter.jp 自分のIP

4.お名前.comでネームサーバーの変更

Navi TOP -> ネームサーバーの変更する -> ネームサーバーの変更 -> 他のネームサーバーを利用
プライマリネームサーバーに先ほどホスト設定したドメイン 例: ns1.noter.jp
セカンダリネームサーバーに2nd.dnsv.jpで登録する

とりあえずこれで時間経てばnslookupで普通に正引きできます

5.IPを書き換える

色んな方法があるだろうけど今回はsshddns鯖にあるスクリプト実行するのをcronで走らせることにした
まず、こんなスクリプトを用意
https://gist.githubusercontent.com/silenvx/15d58473c201f1b7a2563cb2a09a3059/raw/ee826c7b0f1eb0a5caa02a219b6f08633d7835b1/hoge.sh

#!/bin/sh
GLOBAL_IP=` echo $SSH_CONNECTION|cut -f1 -d " "`
HOST_NAME="sub.noter.jp"
TTL="3600"

echo $GLOBAL_IP|grep -E '^((([0-1]?[0-9]|2[0-4])?[0-9]|25[0-5])\.){3}(([0-1]?[0-9]|2[0-4])?[0-9]|25[0-5])$' > /dev/null
if [ $? -eq 0 ];then
    echo "
    server 127.0.0.1
    update delete $HOST_NAME
    update add $HOST_NAME $TTL A $GLOBAL_IP
    send
    "|nsupdate
fi

これでやってるのはsshで接続してきた奴のglobal ipを取得してnsupdateで情報書き換えてる
自分用にいじるのはHOST_NAMEとTTLの値で、HOST_NAMEは自分の割り当てたいサブドメイン
TTLはzonefileに書いてるアレ
後はssh経由でこのスクリプトを実行するだけ

    # ssh ユーザー名@鯖名 hoge.sh

これでできたら後はcron書いたり更にセキュアにするためにchrootにしたりとかするだけ

Acer Aspire One 533にarchlinuxインスコした

前書き

新しい環境作ったときによく忘れるので書いておく、ただの個人用のメモ

本文

wifi設定 ドライバは最初から認識しているので

    # ip link
    (これで[device name]を確認)
    # ip link set [device name] up
    # iw dev [device name] scan
    (接続できるssidを探す)
    # wpa_supplicant -B -i [device name] -c <(wpa_passphrase [ssid] [password])

fbtermで端末の色を指定できるようにする

前書き

xtermでいうとこの~/.Xresourcesに書くような

    XTerm*background: #000000
    XTerm*foreground: #ffffff
    XTerm*color0: #242424

こんな感じの部分を~/.fbtermrcで設定できるようにする
以前からこんな機能ほしかったけどfbterm自体あんまり使わないからかかなかった
でもまたお世話になりそうなんで書く
というか書かないとデフォルトの色は見にくい、今まで誰もやらなかったのが不思議なほどだよ

やり方

patchを書いたので
https://raw.github.com/silenvx/PKGBUILD/master/fbterm/config_colors.patch
ここから各自落として当てたら使えます
archlinux系の人は

    % git clone https://github.com/silenvx/PKGBUILD.git
    % cd PKGBUILD/fbterm
    % makepkg -i

で入る

~/.fbtermrcには

    color-0=000000
    color-1=952519
    color-2=69821b
    color-3=af8700
    color-4=3387cc
    color-5=a757a8
    color-6=418179
    color-7=a0a8b0
    color-8=444444
    color-9=ff5f55
    color-10=73d144
    color-11=ffd348
    color-12=89bdff
    color-13=d787ff
    color-14=71bebe
    color-15=c3c6ca

こんな感じで24bitの16進数で色を書いて0から15までの色を指定できる
どんな感じかはどっかに落ちてるであろう256colors2.plを使って確認するといい
但しtmux上でないと画面がバグるので注意
たぶんhttps://joeldotfiles.googlecode.com/svn/trunk/256colors2.pl このあたりに落ちてると思う

patchを書くためにしたソースコードとの戯れ方 (ここから下は読む必要ない)

既にbgとfgならば~/.fbtermrcにて

    color-background=0

こんな書き方が可能なのでこの変数名を使ってソースコード全体に検索をかけることにする

ソースコードを展開させて、そこをカレントディレクトリにしたのちfindする

    % find . -type f -exec grep -nH 'color-background' {} +
    ./src/screen_render.cpp:80:             Config::instance()->getOption("color-background", color);
    ./src/fbconfig.cpp:184:         "color-background=0\n"
    ./src/fbconfig.cpp:231:         { "color-background", required_argument, 0, 'b' },
    ./src/fbconfig.cpp:267:                         "  -b, --color-background=NUM      specify background color\n"
    ./src/fbshell.cpp:328:          Config::instance()->getOption("color-background", color);
    ./doc/fbterm.1.in:52:\fB-b, --color-background=\fR\fInum\fR

実際に値を使って何かしているのは

    ./src/screen_render.cpp:80:             Config::instance()->getOption("color-background", color);
    ./src/fbshell.cpp:328:          Config::instance()->getOption("color-background", color);

この2つなのでまずは上から見ていく

    void Screen::initFillDraw()
    (略)
        Config::instance()->getOption("color-background", color);
        if (color > 7) color = 0;
        bgcolor = color;

initFillDraw()関数内の処理のようだ、関数名にiniとdrawが使われていることから描写に関する初期化処理だろう
color-backgroundの変数には0から7の値のうちどれかを入れることになっているので
if文で判定して、範囲外なら0にしていることから察するに
検索でヒットした行はcolor-backgroundの値を取得しているようだ

この時に気づいたんだが、今見てるのは背景に関する処理っぽいし
文字と背景は処理違う気がしてきたのでcolor-foregroundで検索し直した

    % find . -type f -exec grep -nH 'color-foreground' {} +
    ./src/fbconfig.cpp:183:         "color-foreground=7\n"
    ./src/fbconfig.cpp:230:         { "color-foreground", required_argument, 0, 'f' },
    ./src/fbconfig.cpp:266:                         "  -f, --color-foreground=NUM      specify foreground color\n"
    ./src/fbshell.cpp:324:          Config::instance()->getOption("color-foreground", color);
    ./doc/fbterm.1.in:49:\fB-f, --color-foreground=\fR\fInum\fR

fbconfig.cppは見るからに設定ファイルに関する処理なのでで残った選択肢からfbshell.cppの方を見ると
このファイルの

    static const Color defaultPalette[NR_COLORS] = {

ここに256色の パレットカラーがあることに気づいたのでとりあえず~/.Xresourcesに書いてるのと同じ値を
0番から15番に記入してコンパイルして試しに実行してみると予想通り色が変更された
ちなみにxtermの場合は./256colres.hと./charproc.cで定義されているみたいだ
ソースコードまでは確認してないがxtermの場合はbgと256色の0番は違うのでfbtermもそれに倣ってpatchを書いていく
ひとまずは0番から15番までの色を~/.fbtermrcで指定できるように改変していく
仕様としては~/.fbtermrcに

color-0=000000
color-1=FF0000

みたいな感じで書いていけるようにする

まずは設定の追加方法を知るためにtermを指定できるようにするpatchが既にあるのでそれを参考にする
http://code.google.com/p/fbterm/issues/attachmentText?id=48&aid=480002000&name=fbterm-1.7-term.patch
それで見た結果、

    Config::instance()->getOption("term", term, sizeof(term));

こんな感じで呼び出すだけで設定が抜き取れそうだったのでさっそく改変して

    s8 hoge[32] = "hoge"
    Config::instance()->getOption("hoge", hoge, sizeof(hoge));
    setenv("HOGE", hoge, 1);

こんな感じのをsrc/lib/shell.cppのvoid Shell::createShellProcess(s8 **command)に埋め込んでみて
~/.fbtermrcにhoge=foobar的なのを書いて、% echo $HOGE でfoobarが出力されるのを確認した
けど最初にhogeを代入する必要があったのか疑問だな…~/.fbtermrcから設定を取ってくるときに未定義だとNULLっぽいし
そういうわけで後は

    static const Color defaultPalette[NR_COLORS] = {

からconstとって勝手に代入するようにするだけ
このColor型は

    % find . -type f -exec grep -nH 'Color' {} +|grep struct
    ./src/screen.h:29:struct Color {
    ./src/fbshell.h:82:     struct Color *mPalette;

src/screen.hで定義されていて

    struct Color {
        u8 red, green, blue;
    };

unsigned char型が3つ並んでるだけです
ここまで分かれば後はアルゴリズムは思いつきますが、どこに書けばいいのかわかりません
とりあえずdefaultPalette変数が使われてる前で書き換えさえすれば問題なさそうなので検索する

    % find . -type f -exec grep -nH 'defaultPalette' {} +
    ./src/fbshell.cpp:42:static const Color defaultPalette[NR_COLORS] = {
    ./src/fbshell.cpp:522:                  memcpy(mPalette, defaultPalette, sizeof(defaultPalette));
    ./src/fbshell.cpp:539:                  screen->setPalette(defaultPalette);
    ./src/fbshell.cpp:572:          screen->setPalette(mPaletteChanged ? mPalette : defaultPalette);
    ./src/fbshell.cpp:582:          screen->setPalette(defaultPalette);

それぞれの処理は

    void FbShell::request(RequestType type,  u32 val)
    void FbShell::switchVt(bool enter, FbShell *peer)

このどちらかの関数かで行われてる
それぞれの関数の最初の処理に青と緑だけ色をつける処理を書いてみたところ

    void FbShell::switchVt(bool enter, FbShell *peer)

こっちの方で書いた緑色が表示されたのでとりあえずこの関数の先頭に書き換える処理を書く

そうと決まればどんな風に書けばいいか想像する
まず取得する値は文字列になるので6byte
1byteの数字の範囲は0~F
2byteを16進数の1byteに変換する
それが3組できて、それぞれの対応した1byteをred,green,blueに代入する
そして試しにcolor-0だけいじるようにしたpatchがこれ

diff -Narup fbterm-1.7.orig/src/fbshell.cpp fbterm-1.7/src/fbshell.cpp
--- fbterm-1.7.orig/src/fbshell.cpp 2014-03-19 20:26:38.615617000 +0900
+++ fbterm-1.7/src/fbshell.cpp  2014-03-19 20:27:41.665617000 +0900
@@ -39,7 +39,7 @@
 #define screen (Screen::instance())
 #define manager (FbShellManager::instance())
 
-static const Color defaultPalette[NR_COLORS] = {
+static Color defaultPalette[NR_COLORS] = {
    {0x00, 0x00, 0x00}, /* 0 */
    {0xaa, 0x00, 0x00}, /* 1 */
    {0x00, 0xaa, 0x00}, /* 2 */
@@ -552,6 +552,31 @@ static s32 tty0_fd = -1;
 
 void FbShell::switchVt(bool enter, FbShell *peer)
 {
+    s8 color[7]={0}, rgb[3]={0};
+    u32 i,j,x;
+    Config::instance()->getOption("color-0", color, sizeof(color));
+    for(i=0;i<3;i++){
+        for(j=0;j<2;j++){
+            x=i*2+j;
+            if(('0' <= color[x]) && (color[x] <= '9'))
+                rgb[i]|=(color[x]-48);
+            else if(('A' <= color[x]) && (color[x] <= 'F'))
+                rgb[i]|=(color[x]-55);
+            else if(('a' <= color[x]) && (color[x] <= 'f'))
+                rgb[i]|=(color[x]-87);
+            else
+                goto NoTouch;
+            if(!j)
+                rgb[i]<<=4;
+        }
+        if(i==2){
+            defaultPalette[0].red=rgb[0];
+            defaultPalette[0].green=rgb[1];
+            defaultPalette[0].blue=rgb[2];
+        }
+    }
+NoTouch:
+
    if (tty0_fd == -1) tty0_fd = open("/dev/tty0", O_RDWR);
    if (tty0_fd != -1) {
        seteuid(0);

後はcolor-15までループさせればいいだけなのでループさせてから
処理をオブジェクト化して終了

後書き

backgroundとforegroundの色を独立して書くのは何かもう面倒臭いからまたやりたくなったら書きます

これで使いやすいブラウザがあれば最強なんだが
w3mは最高に使いにくい
vimperatorライクで使えたら最高なんだがな…