Manjaro 0.8.13 セットアップ作業 part2

パッケージの順次インストール

  • umplayer-svn
  • kuickshow
  • gqview
  • chromium-pepper-flash
  • smtube
  • youtube-dl
  • atomicparsley
  • gnome-terminal
  • cutemarked
  • haroopad
  • screen
  • at
  • nemo-share
  • unbound

screenは、.zshrcでSSH接続に対してscreenを呼んでいるので必要。

atはrsoftmirrorが使っているので必要。

Gnome Terminalは、Nemoが必要とするため(Gnome Terminalがないと、Nemoで「端末で開く」とした時に開くことができない)に導入した。

ファイアウォール

とりあえず、

# touch /etc/iptables/iptables.rules

した上で

# iptables -P OUTPUT ACCEPT
# iptables -P FORWARD DROP
# iptables -N INCOMING
# iptables -A INCOMING -m state --state ESTABLISHED,RELATED -j ACCEPT
# iptables -A OUTPUT -o lo -j ACCEPT
# iptables -A INPUT -j INCOMING
# iptables -A INCOMING -p udp --dport 53 -j ACCEPT
# iptables -A INCOMING -p udp --dport 5353 -j ACCEPT
# iptables -A INPUT -p tcp --dport 22 -j ACCEPT
# iptables -P INPUT DROP
# iptables-save > /etc/iptables/iptables-rules

して

# systemctl start iptables

だが、ルールがどうにも気に入らなかったので、結局gufwを使ってしまった。 なんとか早くnftをマスターしたいところではある。

SpamAssasinのham学習

Spamはだいたい固めてあるが、Hamは非常に広い階層にあるため、次のスクリプトで処理した。

setopt EXTENDED_GLOB
integer ind

for i in ~/Mail/inbox/**/*~*/SystemMessage(#q/)
do
{
#  print $i
for j in "$i"/<->(#q.[1,100])
do
	cp -v "$j" ~/tmp/ham/$(( ++ind ))
done
} always { TRY_BLOCK_ERROR=0 }
done

これでMHフォルダ内にある全てのメールが集約できる (事前にspamは排除しておく必要あり) ため、あとはディレクトリをhamで学習させれば良い。

マウスカーソルが歪む問題とCinnamon

相変わらず出現する。XFceだと、それなりの頻度ででるようだ。 KDEに関してはそこまで使い続けたことがないため、出るかどうかは分からない。 KDE4では最も出現しやすかったが。

結局、UIが改善されたXFceでもKDEでもなく、Cinnamonに戻ることとなった。 LINEのフォント問題も、なぜかCinnamonで起動すると綺麗に表示される。

ちなみに、以前から感じていたことだが、Cinnamonのフォントレンダリングが異様に綺麗だ。 サブピクセルレンダリングの設定に関わらず。 これは一体なんなんだろう。

また、以前は機能しなかったCinnamonのスクリーンショット機能もきちんと動作した。 もしかして以前はGnome-Screenshotが入っていなかったりしたのだろうか。

Gnome-ScreenshootはXDG-PICTURES以下に自動で保存するため、ガンガン撮れる代わりに、ちゃんと分類しないと後で痛い目を見る。

Cinnamonに戻したため、またウィンドウショッピング(GUIの外観の設定)に時間を費やすこととなった。 壁紙を前回から引き継ぎ、黒ベースのテーマとしてかなり綺麗に仕上がった(以前はそもそも設定していなかった)のだが、そうするとウィンドウ色などを独自に定義するものとうまく噛み合わなかったりする。 Mikutterに関してはダークテーマに設定したが、Geanyが常に白背景に白字を使うのだが、にも関わらず入力中の部分についてはGTKのテキスト背景色を使う。ダークテーマだとテキスト背景色は黒に近いため、見えない。 変換するとシステムカラーになるため問題ないが…

結局、日本語テキストが多い場合は、meditを使う、というスタイルに戻ることになりそうだ。

マウスカーソルテーマでLCDテーマに青がないため、黒を使っているがテキストエリアでロストすることがちらほらあるのも残念。

メインシステムをEncFS on BtrfsからBtrfs on dm-cryptに変更

概要

散々やるよといっていた構成変更だが、ついに実行した。

作業はかなり面倒で、1時間半ほどかかってしまった。

今までは次のようになっていた。

  • 4ディスクによるBtrfsを編成
  • Btrfsはサブボリュームを持つ
  • ひとつのサブボリュームを丸々EncFSとする
  • .zshrcでencfsコマンドを呼び、マウントする

この方法のメリットは次のようなものだ

  • 強力で柔軟な暗号化
  • ファイル単位でのバックアップが、暗号化したまま可能
  • 手軽
  • rootでもユーザーデータにアクセスできないので、かなり安全

一方デメリットは次のようなもの

  • rootでユーザーデータにアクセスできないとKVMが暗号化されたディスクにイメージを置けないなど結構不便
  • 特にバックアップで困る
  • ファイルアクセスは常にEncFSがトップ。どのプロセスがIO負荷になっているのかわからない

下準備

先日のrsoftmirrorとrsyncを使ってデータをすべてスレーブにバックアップ

dm-crypt

4kBのurandomデータをめちゃくちゃに暗号しまくって、その中から512Bを切り出す方法で鍵ファイルを作成。これを使う。

cryptsetup --hash=sha512 --key-size=512 --ciper="twofish-xts-plain" --offset=0 key-file=/etc/worldencmount/keyfile --type=plain open device name

単にopen。LUKSではluksFormatという専用のアクションがあるが、プレーンだとない。 ちなみに、twofish-xts-plain結構遅い

これをしただけだとBtrfsの存在はblkidでは残っているように見えてしまう。 そこでrebootするのだが、その前に/etc/fstabを編集してbtrfsをマウントしないようにしておかないと痛い目を見る。

Btrfs

リブートしたら暗号化を行うのと同じ手順でデバイスをオープンする。 Btrfsはそのオープンしたデバイスに対して行う。

# mkfs.btrfs -m raid1 -d raid1 -l WorldBtrfs /dev/mapper/world_enc_*

ちなみに、ストライプで高速化するのではなく、ミラーで分散してもらうことにした。 ミラーするかどうかは悩んだのだが、やはり安全をとってミラー。つまり、2ミラー、1バックアップになる。

サブボリュームを作ることを忘れていて若干ハマった。

# mount /dev/mapper/world_enc_1 /mnt/1
# cd /mnt/1
# btrfs subvolume create shared-world

設定ファイルにblkidでdm-cryptのPARTUUIDを送り込んでおく。 また、btrfsのUUIDを/etc/fstabに送り込んでおく。

# blkid | grep -F /dev/mapper/world_enc_* | tail -n 1 >> /etc/fstab

そしてvim

# vim /etc/fstab

コピペしてUUIDを書き換え、ゴミを消去し、コメントアウトを外し、noautoオプションを追加する。

修正

そういえばこの方法はdiskのパーティションを暗号化している。 これは推奨される方法だが、やはりできればwhole diskの暗号化をしたい。 LUKSも含め3TB以上のwhole diskの暗号化には対応している。

だが、そうするとかなり重大な問題が生じる。 PARTUUIDやPARTLABELが使えなくなるのだ。

これは結構困ったことで、どうしたものかと思ったが、/dev/disk/by-idを使うことにした。 これはWWIDと呼ばれる、システムに依存しないデバイスに対して永続的なIDであるという。 若干手間だが、確実だ。

ちなみにこの修正、同期がかなり進んだ段階でリセットする形で行った。 この修正に30分くらいかかってしまった。

セットアップスクリプト

#!/usr/bin/zsh

#read config file.
. /etc/worldencmount/worldrc || exit 1

#decryption
for disk in "${disks[@]}"
do
  cryptsetup --hash=sha512 --key-size=512 --cipher="twofish-xts-plain" --offset=0 --key-file=${keyfile:-/etc/worldencmount/seedfile} --type=plain open "$disk" dmcw_${disk:t}
done

.zshrc

#Check shared zshrc file.

if [[ -e $HOME/share/.zshrc ]]
then
  # Enable shared zshrc
  . $HOME/share/.zshrc
else
  # Disabled shared zshrc.
  
  print ~/share/.zshrc "is not exist." >&2
  print "Probably encrypted filesystem don't mount yet." >&2
  print "Let's mount it!" >&2
  print >&2
  
  ## Decryption ##
  if sudo btrfs filesystem show | grep -F -q HydrangeaMasterBtr
  then
    print "Btrfs filesystem already active." >&2
  else
    if sudo /usr/local/sbin/worldencmount
    then
      print "Okay, succeed to decrypt." >&2
    else
      print "Oh, failed to decrypt disks." >&2
      print "Aborting..." >&2
      exit 2
    fi
  fi
  
  ## Mount ##
  if sudo mount ~/share
  then
    print "Okay, succeed to mount the filesystem." >&2
    print "You can use the World." >&2
    exit 0
  else
    print "Gosh! failed to mount. I don't know why." >&2
    print "Please fix it." >&2
    exit 3
  fi
fi

いままでよりだいぶ複雑になった。 結局、自動マウントではなく.zshrcで処理するようにした。 このほうが合理的だ。

slave側の自動マウント

逆にslave側は常にマウントされているべきなので、systemdユニットを書く。

[Unit]
Description=Mount World filesystem.
After=network.service

[Service]
ExecStart=/usr/local/sbin/worldmount

[Install]
WantedBy=multi-user.target

そして自動起動

# systemctl enable worldmount

ものすごく手抜きで、実はまだテストしていないが、動くはずだ。 ちなみにCentOS7である。

同期

rsyncで新たに作られた~/shareに同期する。 これは復旧作業なので通常とは逆方向のsyncとなる。 結果的には約14時間かかった。gzipのためか時間はかかったが、容量は2.02TiBと約2/3に抑えられた。

鍵のバックアップ

鍵ファイルがなくなると困るのでバックアップ。 しかしそれだけでは暗号化している意味がないので、鍵ファイルも暗号化した上でCD-Rに焼く。 暗号化はopensslのencを使うのが手っ取り早く、確実。 復号化用のスクリプトも同梱した。

総論

今回の作業はそれなりにしんどかったが、それでも同期を別とすれば1時間半ほどで終了した。 (ただしそれは最初にしたようにパーティションベースで行った場合のみの話)

元々EncFSにしたのは暫定的な措置だったし、スレーブサーバー設置に伴ってこういった作業をすることは想定していた。

VHS変換用スクリプト

結局Windowsで記録するようにしたため、MPEGファイルをH.265形式に変換するためのスクリプト。

INDIR/SUBDIR/FILE.mpgファイルをOUTDIR/SUBDIR/SUBDIDR-NN.mkvに変換する。

ちょっと工夫をいれることでだいぶ楽にした。

#!/usr/bin/zsh

# Put your videos on a subdirectory under $WORKER_DIR.
WORKER_DIR=$HOME/mov/usr/vhs_converted/worker
# Videos will be put on a subdirectory under $DEST_DIR.
DEST_DIR=$HOME/mov/usr/vhs_converted/recorded/${dir:t}
# if $1 given, use as bitrate value.
bitrate={$1:512k}

for dir in $WORKER_DIR/*
do

typeset -i index=1

if [[ ! -e $DEST_DIR/${dir:t} ]]
then
mkdir $DEST_DIR/${dir:t}
fi

for i in $dir/*
do
ffmpeg -i "$i" -vcodec hevc -b:v $bitrate -aspect 720:480 $DEST_DIR/${dir:t}/${dir:t}-$(printf "%02d" $index).mkv
(( index++ ))
done
done

サイトの大幅手直し

ウェブサイトに直すべき点が大量にあったためにかなり手をいれることになった。

CSSグラデーション、デザインの修正

まずベンダープレフィクスを用いたCSS3のグラデーションを入れた。

/* headline level2 decoration */
h2 {
width: 100%;
background: -moz-linear-gradient(left -65deg, #fff, #acf);
background: -webkit-gradient(linear, left top, right bottom, from(#fff), to(#acf));
}

これはAki SI&Eのほうのもの。これを使い、サイトロゴは透過画像として(新規に作成した)、背景にグラデーションを入れた。画像はフォアグラウンドのイメージ

/* Header container (Top position of Main container) */
#Header {
background: #fff;
padding: 0 1.3em;
max-height: 125px;
}

#SITE_REASONSET #Header img.sitebanner {
margin-right : 40%;
width: 50%;
}

#SITE_REASONSET #Header {
background: -moz-linear-gradient(left -65deg, #f0f8ff, #acf);
background: -webkit-gradient(linear, left top, right bottom, from(#f0f8ff), to(#acf));
}

SI&Eのほうはメインコンテナが950pxとっているのに切り替えを750pxにしていたのでこれも修正。

Aki SI&Eのほうはキャプチャのスタイルを変更。

h1 {
border-top : double 5px;
border-bottom: double 5px;
color: #39f;
margin-left: -1em;
margin-right: -1em;
margin-top: -0.8em;
text-align: center;

}

マイナスのmarginはあまり見ない気がする。このようなデザインはほとんど見ることはないが、なかなかパッとみてすっきりと見えて目を引く良いデザインだと思う。このために、テキストインデントのマイナスは消した。テキストインデントのマイナスは一行目にのみ適用されるらしい(brで改行すればその都度適用されるのだろうが、折り返しには適用されない)。

サイトバーウィジットの変更

AKi SI&Eが先行で変更され、それがReasonSetに反映され、さらにReasonSetで追加された機能がAki SI&Eのほうに入った。

<!-- Sub column -->

<!-- Menu -->

-*- Menu -*-

<%= ENV["SUBCOLUMN_CONTENT"] || "" %>

<!-- Notes if any -->
% unless DOC.notes.empty?

-*- Notes -*-
    % DOC.notes.each do |note|

  1. <%= note %>
  2. %end

% end

<!-- Navigation links with PureDoc and PureBuilder template -->
% if DOC.meta["link"] && ( DOC.meta["link"]["next"] || DOC.meta["link"]["content"] || DOC.meta["link"]["prev"] || DOC.meta["link"]["start"] )|| ENV["reasonset_link_content"] || ENV["reasonset_link_start"]

-*- Referrances -*-

% end

<!-- / Sub column -->

このために、これまでSubCloumnコンテナ直下にメニューがあったが、それとcontent_boxクラスを分離して、同クラスのdiv要素を追加している。

/* White background */
.content_box {
background-color: #fff;
}

/* Padding for continuous content box. */
.content_box + .content_box {
margin-top: 11px;
}

Aki SI&Eのほうはもう少し手の込んだものになっている。

#SideBar .container {
padding : 1.28em;
}

#SideBar .container:last-child {
margin: 0;
}

CSS3の:last-child擬似プロパティを使うことで最後が間延びしてしまうことを防ぎながら、要素を分離してチェックをみせるようにしている。

注釈をサイドバー内にも表示するようにした他、内容的にはこれまでコンテンツ側にあったACCSのserial articleのreferranceをサイドバーに表示するように変更した。

レイアウト方法の変更

これでサイドバーが長くなったのだが、なぜかこれではメインコンテナが短縮されてしまう。あくまでメインカラムの高さに合わせるのだ。構造としては

なのでこの挙動は謎だが、MozillaでもBlinkでも同様の挙動を示す。

floatすると一見うまくいくようだが、逆にサイドカラムの高さに合わせてしまい、メインカラムが突き抜けてしまう。また、幅もきちんと規定しなくてはいけない。

結局、3者全てinline-blockにしてしまえば高さは正しく確保される。ただし、これでは2つのカラムはmarginがとられないためぴったりくっついてしまう。

そこでこれをmargin, padding, border-widthが全て0のコンテナとし、paddingは別のブロックで確保する。paddingは設定してもよかったのかもしれないが、このほうが管理しやすい。

エッセイ用のほうが先行して開発されたので、構造が甘かった。ReasonSetはもう少し安定しているが、やはり作りなおしたほうが良いと思われる。ちなみに、ReasonSetが安定しているのは、高さが規定されているためでもある。

ここまでしたのだが、inline-blockで幅を%指定だとGeckoでは見られるが、Blinkでは上下にレイアウトされてしまう。ピクセル指定にすれば大丈夫だが、relativeにした上でレイアウトし、さらにfloatすれば%指定でもBlinkで正常に表示される。

ACCSの修正

まず、TOC機能がエラーになるようになっていた。以前はRuby2.0でやっていたが、2.1になったからだろうか?どうも、クラスが違うオブジェクトを持つArrayを比較しようとしている、ということらしい。文字列しか格納しないように思うのだが、一応to_s

essaies.push [ sortitem.to_s, fp.to_s, meta["title"] ]

TOCに戻るリンクが間違っていた。これは、設定ファイルがreasonset_link_contentsとしているのに、テンプレートはreasonset_link_contentとしているという単純な理由だった。これは、HTMLのlink要素に対応するのでcontentsが正しい。ちなみに、Essayにはその機能がそもそも含まれていなかった。

そして、ACCS記事間のリンクが機能しない。Talkin’ About(Aki’s Palace)との違いが見つからず、一体…と思ったのだが、grepしてみるとその設定はrbutilに含まれており、chienomiが専用で使っている。chienomiのリンクが機能していたので、当然Talkin’ Aboutでも機能しているものと思い込んでいたが、実際はTalkin’ Aboutでは機能しないわけだ。単に記事がひとつしかないので気づかなかっただけだ。

基本的にはACCS記事内でそのスクリプトをロードすれば機能するのだが、ロードすべきスクリプトが存在しない。chienomiのものはパスがハードコーディングされている。そのため、それを修正したバージョンを用意した。

また、「最初の記事」だが、

export reasonset_link_start="si/$wd_element/${artdir:+${$(print $artdir/*.pdoc):r}.html}"

となっていた。Talkin’ Aboutでは機能していたが、これは記事がひとつしかないからだ。つまり、複数のファイルがあると、それが連なった文字列(スペース区切り)の最後だけpdocをhtmlに変えることになってしまう。

当然、先頭のファイル名のみをとり出さなくてはいけない。(#q[1])で機能しなかったので、

export reasonset_link_start="si/$wd_element/${artdir:+${$(print -l $artdir/*.pdoc | head -n 1):r}.html}"

と変更した。単純な方法だが、そこまで繰り返し呼び出すわけではないので、head(1)を呼び出す程度どうということはないだろう。

ノートインデックスの作成

ノートのインデックスを作るため、まずPureDoc側を修正。その機能をスーパークラスに追加。

### Notes ###
# Add note text to an array
def addnote(note)
@notes.push note
end

attr :notes

サブクラスで呼び出し

# Note
def n(text, ¬e)
puredoc_element(:n) do
note = note.call
addnote note
'%s</pure:note>' % [esc!(note), esc(text) ]
end
end

ちなみに、HTMLクラスタイプだけノートとテキストが逆になっていた。どちらが正しいのかよくわからなくなっている。仕様を規定すべきだろう。

そして今修正した。

def n(note, &text)
puredoc_element(:n) do
addnote esc!(note)
'%s</pure:note>' % [esc!(note), esc(text.call) ]
end
end

これでノートインデックスの作成が可能になった。だが、これに対応した表示が本文に必要だ。これはCSSに任せる。

body {
counter-reset : notes;
}

/* Note element */
.puredoc_note:after /*, puredoc|note:after */ {
vertical-align: super;
font-size: 80%;
content : "\002020" counter(notes);
counter-increment: notes;
text-decoration: none;
}

content中の文字参照はどうするのだろう、と思ったら、16進数にした上でクォート内で

FontConfig, Zsh compilation, Zsh prompt

FontConfig

うまく動かないといっていたlocal.conf及びUser configだが、どうも/etc/fonts/conf.d/のリンクをmvして戻すと有効になるようだ。理由はわからず、競合するリンクを削除しても変わらない。

また、それでもなぜかUserに対してのserifだけが適用されない。謎だ。

Zsh Compilation

以下の設定を追加してみた。

# Complation options
setopt auto_menu
setopt glob_complete
setopt auto_list
setopt list_beep
setopt list_types
setopt rec_exact
menu_complete

menu_completeは使いにくかったのでやめた。

Zsh Prompt

Momonga Linuxのzshrcサンプルを流用していて、これが結構いかしたプロンプトなのだが、Manjaroのetc版と比べてGitが有効になっていないので、有効にすると同時にちょっといじってみた。

zshのプロンプトにgitのステータスをシンプル可愛くを参考にしたのだが、これをそのまま追加するとRPROMPTと競合してしまう。どうやら、プロンプトの設定自体を動的に行うとRPROMPTの文字数が計算できなくなるようだ。

RPROMPT込みで流用できるのはきたけーさんのブログだが、これだと情報量が少ない。

そこでこれを合体させ、さらに色使いを変更し、終了ステータスで変更するのをプロンプト全体ではなくプロンプト($)のみにした。

## prompt
# USER –>
# For git. I referred http://kitak.hatenablog.jp/entry/2013/05/25/103059 and http://yoshiko.hatenablog.jp/entry/2014/04/02/zshのプロンプトにgitのステータスをシンプル可愛く. Thank you.
# VCSの情報を取得するzshの便利関数 vcs_infoを使う
autoload -Uz vcs_info

# 表示フォーマットの指定
# %b ブランチ情報
# %a アクション名(mergeなど)
zstyle ‘:vcs_info:*’ formats ‘[%b]’
zstyle ‘:vcs_info:*’ actionformats ‘[%b|%a]’
precmd () {
psvar=()
LANG=en_US.UTF-8 vcs_info
if [[ -n “$vcs_info_msg_0_” ]]
then
psvar[1]=”$vcs_info_msg_0_”
if [[ -n “$vcs_info_msg_1_” ]]
then
psvar[2]=”$vcs_info_msg_1_”
elif [[ -n “$vcs_info_msg_2_” ]]
then
psvar[2]=”$vcs_info_msg_2_”
elif [[ -n `echo “$st” | grep “^Untracked”` ]]
then
psvar[2]=”Untracked”
else
psvar[2]=”Clean”
fi
fi
}

# バージョン管理されているディレクトリにいれば表示,そうでなければ非表示
#RPROMPT=”%1(v|%F{green}%1v%f|)”
# <– USER
PROMPT=’%F{cyan}[%n@%m${WINDOW:+[$WINDOW]} %.] %1(v|%F{yellow}%1v|)%2(v|(%2v) |)%{%(?.$fg[green].$fg[red])%}%(!.#.$)%{$reset_color%} ‘
RPROMPT=[%~]
SPROMPT=’zsh: replace ‘\”%R’\” to ‘\”%r’\” ? [Yes/No/Abort/Edit] ‘

結構イケる。

ReasonSet サイト構築システム PureBuilder

PureBuilderは以前から何度か登場し、完全にやり直し、と繰り返している。今回は、「本体スクリプト」というものがない状態で登場し、ついに実用となった。

まず下地となるPureBuilderだが、かなり更新され、GitHubにも既に反映されている。まずこれがベースになる。

そして、「PureDocを使った翻訳を自動化する」というのがPureBuilderの基本的なところだ。PureBuilderのGitHubにあるpurebuild-puredoc.zshがその根幹となるスクリプトである。

はっきり言ってGitHubにある設定ファイルのサンプルとREADMEを読むのが一番早いと思うのだが、つまりこのスクリプトは.purebuild-puredoc.rc, .up_*, .rebuild-rulesファイルに従って実行するための補助ユーティリティでしかない。だが、設定ファイルを適切に書くことでほぼ全面的な自動化を実現している。サンプルをみれば分かるように、メニューを動的生成していたりする(eRubyを使っている)ため、サイトでユーザーからのアクションが自動的に反映されるような仕組みを持たないドキュメントに関してはこの方法で動的に静的ドキュメントを生成することができる。これは、著しいオーバーヘッド削減につながる。

参考までに、ReasonSetのサイトのメニューの構築を行うテンプレートは次のようなものだ。

<% menu = [
[ ["ReasonSet / Help", "//reasonset.net/", "ReasonSet全体の情報"], (ENV["SITETYPE"] == "REASONSET"), [["Profile", "//reasonset.net/profile.html", "正木はるか(柊美亜紀)のプロフィール"]] ],
[ ["HARUKA Sound", nil, "音楽プロダクション/プロジェクト HARUKA Soundの公式ページ(お仕事)"], nil, [] ],
[ ["Aki's palace", nil, "正木はるか(柊美亜紀)に関するウェブサイト。本人について、意見など"], (ENV["SITETYPE"] == "AKI"), [ ["Talkin' about", nil, "様々なテーマに対しそのフィルタを通して語る" ] ] ],
[ ["ちぇのみ-Chienomi-", nil, "コンピュータ"] , (ENV["SITETYPE"] == "CHIENOMI"), ["COLUMN", nil, "コラム" ], ["+Play programming with JavaScript", nil, "JavaScriptでプログラミングを遊ぶ"] ["+Easy step programming with Perl", nil, "Perlではじめるプログラミング入門"], ["+Easy step object oriented programming with Ruby", nil, "Rubyではじめるオブジェクト指向入門"] , ["Live with Linux", "http://reasonset.net/journal/archives/category/livewithlinux", "Linux日記" ] ],
[ ["Feel the Earth", nil, "バイク"], (ENV["SITETYPE"] == "MOTO"), [["Impression",nil,"所有車両のインプレッション"], ["Impression:HONDA VT250J SPADA", nil], ["Impression: SUZUKI SV400S", nil], ["Impression:YAMAHA MT-09", nil], ["Column", nil, "コラム"] ] ],
[ ["Blogs", "//reasonset.net/blog.html", "ブログ"], (ENV["SITETYPE"] == "BLOG") , [["journal de Aki", "http://reasonset.net/journal/", "本来のブログ"], ["Ameblo", "http://ameblo.jp/reasonset", "くだらないブログ"]] ],
[ ["SI Service", "http://reasonset.net/si/", "コンピュータの技術を供与するサービスについて(お仕事)"], nil, [] ]
]
%>

<div id="ReasonsetMenu">
% menu.each do | name, cond, items |
% if name[1]
<div class="menu_title"><a href="<%= name[1] %>" title="<%= name[2] %>"><%= name[0] %></a></div>
% else
<div class="menu_title"><span title="<%= name[2] %>"><%= name[0] %></span></div>
% end
% items.each do |i|
% if i[1]
<div class="menu_item"><a href="<%= i[1] %>" title="<%= i[2] %>"><%= i[0] %></a></div>
% else
<div class="menu_item"><span title="<%= i[2] %>"><%= i[0] %></span></div>
% end
% end if cond
% end
</div>

さらに、よりプロフィールに関してはプロフィール自体がRuby scriptとなっており、それを実行することでPureDocを拡張した形式で書かれたプロフィールを展開する。

もっとも、プロフィールを含むスクリプトはあくまで出力するだけであり、実際にアップロードする処理は.up_profileというスクリプトを書いて実行している。

全体には伝播する環境変数をうまくつかった仕組みになっていると思う。

PureBuilderでzsh functionを導入

Zshのautoloading functionは「functionを書いたファイルを大量に.する代わりにロードする手段を与える」ものだと思えばいい。基本的には1 file 1 functionであるようだ。

$fpathに含まれるディレクトリにあるファイル名がautoloadで指定する値であり、またfunction名にもなる。

これまでrcファイルに直接書いていたUpdate機能だが、Zsh functionとして提供することにした。ファンクションインストーラが勝手に専用ディレクトリ~/.yek/lib/purebuild_functionsにインストールするので($funcdirで直接指定することもできる)、それを$fpathに追加することでロードできるようになる。あとはautoload -Uzして使って欲しい。

このfunctionはかなり丁寧に情報を出力する。「Last Updateよりも新しい」の判定は、素直にfind(1)touch(1)を使っている。また、最終更新についてはstat(1)を使って表示している。

今回はfunctionを使うようになったのがメインだが、つられてpure builder及びpuredocの更新もあった。

また、purebuilderはGitHubで公開したので参考にしてほしい。

Mail Deliver 0.0.1 release!

全面的にコードを書き直す改変を行った。GitHubに反映済みだ。

従来のコードではアドレスの抽出がうまく動いておらず、アドレスに合わせてソートされていなかった。どこが問題なのか見つけ出すよりは全面的に新しいコードにしたほうがよさそうだったため、大幅に修正した。

メインとなるlocaldelivは従来を踏襲する。ただし、ヘッダーの取得・保存に使うスクリプト、アドレス抽出に使うスクリプトはあたらしくなり、またlocaldelivが何を受け取り何を渡すかという動作も変更された。

getheaderスクリプトはメールのヘッダを読み、NKFで変換して適切に結合し、Hashにして、YAMLにして出力する。NKFを使うため、日本語か、ASCIIか、UTF-8のメールでないとうまく動作しない。これはRubyスクリプトだが、Mageiaはnkfパッケージをもっていないし、恐らくRuby経由でNKFを使うほうが確実に動作する。

getaddrはそのHashからFromの値をとった上で正規表現でマッチングを行い、アドレスを取得して出力する。うまく取得できない場合はexit 1だが、それよりもむしろlocaldeliv側でif [[ -n $addr ]]しているほうが重要になる。

localdelivはソートされなかったメールを$MH/inbox/domain/addressに振り分ける。Zshのコードは今回はあまり難しいものはいれていないが、この部分については

box="inbox/address/${addr#*@}/${addr}"

という形でドメインの切りだしを行っている。

savemailはほぼ以前の通りだが、設計がおかしかったので、修正した。

従来、savemailにはフォルダのみを渡していた。この時渡されるのはdomain/addressだけだった。しかし、このためにinboxフォルダを使うようなものが$Mail/inbox/address/inboxになってしまっていた。そこで、$Mail/junkなどを使うためにも、$Mail以下を指定するようにした。これは、localdelivが渡す値の修正と、savemailの受けとった値の取り扱いが変更された。

また、localdelivが呼ぶコマンドにはmaildeliv.というプレフィクスをつけるようになった。これにあわせて名前を変更しながらコピーするインストールスクリプトを書く予定だ。

基本的に修正したのはこの4つだ。とりあえず、目的の動作はするようになったが、メール関連はいくつかユーティリティを書かないとうまく動作しない。また、設定サンプルも書いたほうがいいのだろうか?

このメールユーティリティはZshとRubyの組み合わせとなっている。まさに私らしいユーティリティスクリプトだと言えるだろう。

さて、ここまで書いてから一日がかりの大幅な加筆修正とバグフィックスを行った。コードの詳細はかなり詳しいREADMEもついているのでGitHubを参照して欲しい。ここでは裏話をしよう。

今回最もハマったのは、Rubyでexit 1が書けないことだろう。カッコが省略できない。ちなみに、Kernel.abortも省略できなかった。以前(1.8.6)はできた気がするのだが、やはり1.9以降の変更でハマっている状況だ。

設計は根本的に見直した。まず、今回再認識したのが、設計していないコードや、コメントのないコードは、手が入る時点で投げ捨てるのが正義である、ということだ。

とりあえずでやっつけで作ったこのプログラムは徐々に拡張されてきたが、その過程で入出力のフォーマットの整合性がなかったり、YAMLをつかっているのにわざわざ正規表現で抽出したりということがあった。変数名が統一されていないためにエラーになるような状況もあった。

そのことからそもそもlocaldelivが既存のutilを利用せず新たに書き起こす展開だったのだが、当然ながらlocaldelivに限らずほとんど書き直すことになった。ただ、mail-notify-countに関しては既にYAMLでカウントするようになっていたためそのままとなった。しかしNotify系のコマンドも整合性をとるための編集をしたり、動作モデルを変更したりとなかなか大変だった。

特に大掛かりな変更となったのがmdafilterだろう。なにしろ、今までZshで書いていたものをRubyで書き直し、設定ファイルの形式も変更されてしまったのだから。どれほど変更されたかはgit logで具体的に確認することができるが、結局「これは違うな」と思った時点で挙動を確認せず新たに書き起こしてしまった部分がほとんどだし、修正した部分の中にも最初からやり直すのと大して変わらない労力をかけた部分が多い。

こんな小さなプログラムに6時間もかかってしまったので、だいぶ悔しい。

ソートルールに関しては従来、第二フィールドの値をシェルコマンドとして実行して終了ステータスから判断する仕様だったが、ほとんどの場合Fromから判断するのだから、コマンドで、というのはあまり合理的でない。そこで、Rubyに変更し、ルール自体をRubyで書くことにした。Procに対して渡されるのはメールヘッダのHashオブジェクトだが、さらにアドレスについては#fromメソッドで取得できるほか、#bodyメソッドで本文も取得できる。ちなみに、メールボディについては呼ばれた時にメールを読んでインスタンス変数に格納する仕組みでロードを遅延している。これは、メールボディはSTDIN経由で渡す、という方法で実現している。最新のコードでは次のようになっている

def body
@body ||= NKF.nkf("-w -Lu -m", STDIN.read.to_s).split("

", 2).last
end

今回の作業は多岐にわたったが、なんといってもちゃんとしたREADMEにInstallスクリプトまで書いてリリースにこぎつけた。

ぜひGitHubをのぞいてみてほしい。

Firefox Latest 起動スクリプト修正

やってはいけない、と分かっていたが、突然の再起動でFirefox Latest起動中に終了してしまい、うっかりそのままFirefox Latestをスタートしてしまった。

起動スクリプトは最初にmv ~/.mozilla ~/.mozilla.origをやるため、Firefox Latest用の.mozillaが紛失してしまった。ちょっと焦ったが、これは~/.mozilla.orig/.mozilla.origになるため復元はできた。

だが、このような問題のあるコードをそのままにする気にはなれないので修正する。

#!/bin/zsh --extended-glob

# If .moziila is not for latest Firefox.
# (#q:A) means expand symbolic link
if [[ -e ~/.mozilla $( print ~/.mozilla(#q:A) ) != */.mozilla.latest ]]
then

# Abort if .mozilla is a directory
if [[ ! -h ~/.mozilla -d ~/.mozilla ]]
then
print .mozilla is a directory. 2
exit 1
fi

# Link .mozilla for latest Firefox if any.
if [[ -e ~/.mozilla.latest ]]
then
[[ -h ~/.mozilla ]] rm ~/.mozilla
ln -sf ~/.mozilla.latest ~/.mozilla
fi

fi

# Invoke latest Firefox
~/lib/firefox/firefox

# If invoked with no .mozilla for latest Firefox.
# Rename .mozilla for it.
if [[ ! -h ~/.mozilla ]]
then
mv ~/.mozilla ~/.mozilla.latest
fi

# Change link for original Firefox.
if [[ -e ~/.mozilla ]]
then
rm ~/.mozilla
fi
ln -s ~/.mozilla.orig ~/.mozilla
ln -sf

なぜかln -sfで機能しなかった。

新旧のFirefoxを使い分けるスクリプト

新旧のFirefoxを使い分けるスクリプト

MageiaのFirefoxは24ESRだ。最新のFirefox31を~/lib/firefox/firefoxとして置いた。

しかしこのまま起動すると、どちらのバージョンを使うかによって.mozillaのバージョンチェックが行われ、アドオンなどがいじられてしまう。そのため、それぞれの.mozillaを分けたい。なお、ここでは.mozillaをいじっているが、本来なら.firefoxをいじるべきなのかもしれない。

単純に起動するバージョンによって.mozillaを変えることにした。

#!/bin/zsh

mv ~/.mozilla ~/.mozilla.orig
if [[ -e ~/.mozilla.latest ]]
then
mv ~/.mozilla.latest ~/.mozilla
fi

~/lib/firefox/firefox

mv ~/.mozilla ~/.mozilla.latest
mv ~/.mozilla.orig ~/.mozilla

Firefoxはシェルスクリプトとは別プロセスであるため、Firefox起動中にシャットダウンするようなことをしない限りファイルは保たれる。また、同時起動はどのみちできない。

しかしこのままだとbookmarkが共有されないなど不便な点がある。bookmarkやhistoryなどは~/.mozilla/firefox/$profile.default/places.sqliteにあるということだ。これは通常ファイルなので、symbolic linkにしておけばいい。ただし、latest側を、起動時に作られる.origディレクトリへのリンクにする必要がある。

$ ln -sfv ~/.mozilla.orig/firefox/$profile.default/places.sqlite ~/.mozilla/firefox/$profile.default/places.sqlite

bookmarkbackupsディレクトリもリンクしておいたほうがいいかもしれない。