シェルスクリプトのファイル内未定義変数を調べる

どれが外部ファイルで宣言されているのかわからない

シェルスクリプトでかなりの数の変数を外部ファイルで定義をしているものに出会った時に、どれが外部宣言なのかを調べるために作りました。check.py < sample.shって感じで使います。




evalで変数展開して組み合わせて使っている変数には対応していません。
hoge=foo
fuga=bar
foobar=yes

eval echo \$$hoge$fuga
# yes

NoLockScreen2 Public beta test

NoLockScreen

NoLockScreenご存知ですか?そこそこ古くからあるtweak(CydiaUpdates.netを見る限りは2010/10/05)でアラートがなく、パスコードロックもされていないようであればiPhoneの特徴的なslide to unlockをスキップしてくれる代物で愛用しています。

が、初版の公開から一切のアップデートが行われておらずiOS5での新しいバナーシステムへの対応は行われていませんでした。さらにiOS6ではアプリが最前面にある状態(SpringBoardではない)でロック後にロックボタンでロックを解除すると、ロック解除後にすぐさまロックされてしまうバグがあります。

この状態で放置されており、自分的にも欲しいtweakでしたので対応版をつくりました(こんな時オープンソースならフォークするのが楽なのにと思います、なので将来的にはgithubで公開予定です)。

NoLockScreen2

上述の問題点である

  • iOS 5+でロックスクリーンにバナー表示があるのに自動アンロックしてしまう対応不足
  • iOS 6でロックされてしまうバグ

を解消したNoLockScreen2をパブリックベータテストします。iFileでインストールするのが一番楽だと思います。問題なければBigBoss行き。アイコンは四苦八苦してたら@renz0ne23さんが作ってくれました!多謝!

jp.r-plus.nolockscreen2_0.0.1-11_iphoneos-arm.deb

依存関係は以下です。

Depends: mobilesubstrate, preferenceloader, firmware(>= 4.0)
Conflicts: com.openbrew.nolockscreen

DicActivity


ActionMenuで選択文字列を各種辞書やGoogle検索(Safari)に投げ込むDaijirin ActionMenu PluginをiOS 6から使えるようになったUIActivity+UIActivityViewControllerで作り直しました。以下課題というか問題点

  • 表示時に一気に各ActivityClassをalloc initするコストがやはり高いのか、iPhone 4では動作速度にちょっと不満がでるレベル(といっても0.1sぐらいの差ですが)
  • オプション追加する度にアイコンで四苦八苦する
  • アルファ画像しか使えない仕様なので視認性も期待したほど向上していない

お蔵入りの可能性が高そうですかね

execリダイレクト

exec redirect-out
exec使ってますか?bashの組み込みコマンドのexecはコマンドの変更の他にリダイレクトの動的変更ができます。こんなシェルスクリプトを実行すると、何もしなければファイルディスクリプタ1番の/dev/stdoutに出力されます。
#!/bin/sh
echo "first"

これを以下のようにすると、途中で全体のファイルディスクリプタをexecコマンドで変更できます。この場合、firstはstdoutにsecondはfileに出力されます。

#!/bin/sh
echo "first"
exec > file
echo "second"
exec redirect-in
リダイレクトインも同様に動的変更ができますが、おもな使い所はwhile readだと思います。普通ファイルから1行ずつ読み込んで処理を行う場合は
while read LINE; do
  # do something...
done < file
としますが、標準入力を変更する事で以下のように書くことができます。
exec < file
while read LINE; do
  # do something...
done

doneのところまで読み進まずとも何のファイルを入力とするのかわかる点がメリットでしょうか。デメリットは他人が読んだ時にわからない可能性が高い点です!調べざるを得なかったよ!他人が読みやすいプログラムを書いたほうが絶対に良いので、使わない方が良いでしょう!

blogger.vimでvimからpost

install pandoc
  1. brew install haskell-platform
  2. cabal update
  3. cabal install cabal-install
  4. cabal install pandoc
install vim-plugins
  1. metarw.vim
  2. blogger.vim
install ruby 1.9.2+(blogger.vim required)
  1. brew install ruby
prefer brew and cabal commands
  1. sudo vim /etc/paths

先頭に/Users/r-plus/.cabal/bin, /usr/local/binを追加

config blogger.vim

自分のbloggerの設定と1.9.2+のRubyへのPATHを指定。


install blogger.vim required gems
  1. gem install nokogiri
  2. gem install net-https-wrapper
Post From Vim!

これでmarkdown形式で書いたバッファから:w blogger:createでBloggerに投稿されます。
が、なにやら自動で改行タグが入れられてしまったのでxmlを圧縮するパッチを書きました。https://github.com/r-plus/blogger.vim/commit/844e1cfc387c49e858daefbb943ab62dbfd4dbdd しかしこれだとpreタグの中の改行も削除してしまう。しょうがないので複数行のはgistにでもなげようかと思ったがscriptタグの閉じタグが内包表記になってしまう。なんとままならない事か。

テキストはtextileで書こう!

■textileを使おう!

いえmarkdownの方が好みなのですが会社でRedmineを使っているので、ドキュメントはtextileで書いておけばそれをコピペするだけでチケットに綺麗に書けるわけです。この書きためたtextileをローカル閲覧する際にHTMLで見れる環境を構築しました。

vim-quickrun+openbrowser.vimで開くとかApache使わないでinvisible.js使ったり色々模索してますがこれはそのうちの1つです。ちなみにこのBloggerはmarkdownで書いてます。

■Apacheを導入

とりあえずWindows向けのApacheをインストールします。

■PHPをインストール

PHPを使うのでインストールしておきます。

■httpd.confの編集

textileのTypeとActionを追加しておきます。(要mod_action)

AddType text/textile textile
Action text/textile /lib/tt.php

conf読み直しのためにApacheを再起動。

■Parserをlibにつっこみます

これで拡張子が.textileのtextile記法のファイルをtextileディレクトリにでも突っ込んでブラウザでlocalhost/textile/hoge.textileとかアクセスすればHTMLで表示されるよ!

cssなしの生htmlもなんなので、会社ではtt.phpで生成しているhtmlにcssをリンクさせて表示させてます。 丸々参考にさせてもらったFenrir川端さんのGitHub のように、Markdown ファイルを HTML として表示したいに感謝。

Python辞書型の最初と最後のキーを取得する

日付をキーにした辞書の最初と最後を拾うのにちょっと詰まったのでメモ。Python 3ではkeysメソッドが返すのはリストじゃなくてビューに変わるけど、sorted関数にすぐ渡しているので3でもそのまま動く。ちなみにiterkeyメソッドは廃止なので積極的に使わない方が良い。

iOS 6におけるSleipnizerフルスクリーン機能

■Portraitモードにおけるoverlayボタン

v3.1からPortraitモードでもOS標準のoverlayボタンが表示されるように変更します。下記の表はそれを踏まえての遷移表です。

■Sleipnizerフルスクリーン遷移表

Sleipnizer (>= 3.1)のフルスクリーン管理が非常に複雑になってきましたので、まとめておきます。左の状態からアクション後の状態が右側です。on/offの状態はoverlayボタンが出なくて不便なだけなのでならないように作ってます。

SleipnizeriOS 6 nativeactionSleipnizeriOS 6 native
offofftweakonon
nativeoff (on : Relate to native)
offontweak (Portrait)onon
tweak (Landscape)offoff
native (Landscape)
onontweakoffoff
native

■Relate to native

Relate to nativeを有効にすると上記の表で1箇所だけ動作が変わります。また、iOS6からのiOS標準のフルスクリーン状態は/var/mobile/Library/Preferences/com.apple.mobilesafari.plistに保存され、Safariの起動時(Suspendから復帰ではない)にplist設定値から状態が復元されるのですが、この際にフルスクリーンがONで保存されていると起動時からフルスクリーンになります。Startup FullScreenが半分入る感じですね。

■Keep statusbar

諸々の整合性からiOS6+ですとKeep statusbarが有効なのはPortraitモードのみになりました。FullScreen for Safariは完全になくしてしまったみたいですが。

■iPad

iPadは標準が入ってこなかったようなので、これまで通りに使えるはずです。はず。

libinstabanner

libinstabannerをリリースしました。iOS5から実装されたUIAlertViewによる通知にかわる方法として上部にバナーがでるおなじみのアレですが、tweakから任意に呼び出すには結構な手間がかかる(特にSpringBoardの外からは)のでライブラリを作りました。

使い方は1通りしかないので難しい事は何もなく、ヘッダーのUsageがもうほんとすべてです。bundleIdentifierの引数はnilチェックしてるのでそこは最低限なにか入れる必要があります。titleはnilにすると自動でDisplayNameが入ります。com.apple.Preferencesを指定して日本語なら「設定」てな感じです。

bundleIdentifierを自由に指定できるのでこんな感じにすると・・

[InstaBanner showBannerWithBundleIdentifier:@"eu.heinelt.ifile" title:nil message:@"libinstabanner test"];
こうなります。タップするとそのアプリを起動できます。
面倒なコードが1行ですむね!やったね!

CleanActivity

iOS 6からUIActivityViewControllerを使用したメニュー導入されました。従来使われていたUIActionSheetより生成にパワーが必要でUIActionSheetよりも起動は遅いですが、表示できるメニュー数が多くて便利ですね。便利なんですが人によっては絶対に使わないようなメニューもあるかと思います。Weiboとか、メールとか、メッセージとか。そこでOS標準提供のActivityをオン・オフ設定できるCleanActivityを作りました。これで私のBylineは2段にまとまってスッキリ!

設定できるのは標準提供されている以下9種類のActivityです。

  • Twitter
  • Facebook
  • Weibo
  • Message
  • Mail
  • Pasteboard
  • Print
  • Assign to Contact
  • Save to CameraRoll
  • 色々とオフにするとスッキリしたメニューにできます。

    CleanActivityはgithubで公開しています。Licensed under the GPL v3です。アイコンのほうきがGPLだったので。CleanStatusのほうきと同じ画像だと思うのですがアレはちゃんとGPL守ってるのでしょうか。

    built new Mac.

    MacBook Air (Late 2010)からMacBook Pro 13" Retinaに移行した際のメモ。

    ■Boot

  • 起動は普通に日本語で適当に。

  • ■AppStore

    AppStoreから必要なものを再DL
  • Xcode
  • Evernote
  • Twitter for Mac
  • Alfred
  • Go2Shell
  • Cloud
  • Skitch
  • The Unarchiver
  • CCleaner
  • Dash
  • ■Document for Dash

    Xcodeを入れたら、DashのためにiOSのドキュメントをDL。ついでにCommand Line toolsも。

    システム環境設定

    ■Dock

  • サイズと拡大率を適当に調整して自動非表示ON
  • ■Mission Control

  • ホットコーナーで右上でディスプレイのスリープ
  • ■Firewall

  • とりあえずON
  • ■Spotlight

  • 色々はずしておく
  • ■Keyboard

  • 「すべてのコントロール」に変更
  • キーリピート最速、最短認識
  • 標準ファンクションキーにチェック
  • ■Trackpad

  • 軌道最速
  • Mission ControlとExposeをチェックオフ(BTTと干渉するので)
  • フルスクリーンアプリ間移動は4本指に変更
  • ■共有

  • host名変えとく
  • ■アクセシビリティ->マウスとトラックパッド

  • トラックパッドオプション->ドラッグ有効、ドラッグロックあり、最速スクロール

  • App from web

  • Firefox
  • Dropbox
  • SugarSync
  • 1Password
  • VLC
  • Insomnia
  • BetterTouchTool
  • GoogleChrome
  • Google日本語入力
  • TuneTEXT
  • Witch

  • Music

  • ~/Musicを固めてAirDropで持ってくるか、iTunesホームシェアリングでコピー

  • 開発環境

  • githubから自分のdotfilesをclone. vim-plugin、symlink等設定
  • homebrewからdpkg入れる
  • theos installシェルスクリプトでtheosインストール
  • tweakのtheosへのsymlinkが外れているのを修正

  • 設定

    ■BetterTouchTool

    Global
  • 4FingerSwipeUp/Down : ShowDesktop
  • 3FingerSwipeUp/Down : MissionControl
  • CornerClickBottomRight : RightClick
  • Terminal
  • 3FingerSwipeLeft : Cmd+Shift+[
  • 3FingerSwipeRight : Cmd+Shift+]
  • ■Witch

  • [Behavior] Show list : right away
  • [Behavior] Show←のチェックは外す
  • [Appearance] 42pt, Show : applicationIcons

  • これで大体完了。

    FakeClockUp v0.7.1-1

    ここ最近tsSpeed Aをオープンソース実装する事を目指してFakeClockUpをバージョンアップしてきてv0.7.1-1で達成できました。バイナリ・ソースコードともにgithubにあります。ライセンスは踏襲してApache License 2.0です。

    ■実装の雑感

    スライダーの値はfloat型でしか得られないので、スライダーの値をListの値に完全に合わせてあげなければ設定が同期しているようにならないのですが、ここで他のSpecifierの値を設定するにはobjectを引数とするメソッドを使用するので、floatもしくはdoubleからオブジェクトへ変換する際にNSNumberを使って0.100000000の値を[NSNumber numberWithDouble:0.100000000];してみると実際の値は0.099999998753みたいな値になってしまい同期がとれませんでした。なので、最終的にはNSStringとして保存する形になりました。詳しくはソースで。

    Sleipnizerで使っているAllAroundPullViewを公開しました。

    タイトル通り、Sleipnizerで使っているPull-To-Gestureの部分のコードをgithubで公開しました。特徴はSleipnizerで使えるように4方向に対応している事です。branchのsleipnizerがまさに使ってる状態です。

    https://github.com/r-plus/AllAroundPullView

    使い方の詳細はgithubのREADMEを参照。

    chpwnのPullToRefreshViewを元に以下な感じの代物です。

  • 色々と微妙な実装だったので修正(大分不親切な実装だった)
  • 4方向に対応
  • コード実行部分をdelegateからBlocksに変更
  • LabelとLastRefreshDateを削除
  • XcodeのDemoプロジェクトの追加
  • tweakで使えるようにARC非対応!非対応!
  • kCFCoreFoundationVersionNumberのススメ

    iOSのバージョンを判定するのには二通り程あって、UIDeviceから取得する方法

    float iOSVersion = [[[UIDevice currentDevice] systemVersion] floatValue];
    と、kCFCoreFoundationVersionNumberを使うやり方があります。
    kCFCoreFoundationVersionNumber >= 550.52 //550.52はiOS 4.2の値

    ちなみにfloatValue使うと小数点2個は取れないので5.1.1は5.100000になります。kCFCoreFoundationVersionNumberも3項目のバージョンでの値に変更はないため、3項目までちゃんと取る場合はNSNumericSearchできちんと比較する必要があります。

    で、tweakのバージョン判定では2項目までで十分な事が多いのでkCF~の方がオススメです。うっかりspringboardが対象のtweakのconstructorでUIDevice使うと危険なので。UIDeviceを使ってはいませんが、iOS5が出た当初に発現していた画面1/4問題は、例えばLastAppでは以下のバージョン判定の1行が原因で起こっていました。

    isFirmware3x_ = (class_getInstanceMethod(objc_getClass("SBApplication"), @selector(pid)) != NULL);
    これを以下に書き換えることで対処したと思われます。
    isFormware3x_ = (kCFCoreFoundationVersionNumber < 550.32) ? YES : NO; // 550.32はiOS 4.0の数字

    新OSが出た際の確認方法は/System/Library/Frameworks/CoreFoundation.framework/Info.plistのCFBundleVersionから確認できます。iOS5.1.xは690.10ですね。

    2.0からの番号はこのあたりを参考に。chpwn / IconSupport - Firmware.h

    What is Tweak?



    ■What is Tweak?

    「CydiaでよくダウンロードするTweakとは何なのか?」について割りと詳しく解説してみようと思う。

    様々な便利な機能の追加や、細かい変更をOSに施すTweakは単語であるTweakの意味「ちょっとした調整」から来ている。ソフトウェアの既存動作にちょっとした調整を加える目的のソフトウェアをさす。

    では好みの動作をさせるにはどうしたらいいか。
    端的に言えばある処理をフックして好みの処理に置き換える事で実現出来る。

    フックを考えるにあたってまず、そのソフトウェアの動作を考えよう。
    iPhoneひいてはOS Xのソフトウェアは専らObjective-Cで記述されている。
    Objective-Cは非常に動的な言語で、コンパイル時点であまり警告・エラーを出さず実行時にうまいこと動けばOKって感じの言語だ。

    オブジェクト指向言語であり、クラスとメソッドとインスタンス変数があってカプセル化されているように見えるが、C言語が皮をかぶっているだけで中でランタイムがゴリゴリ動いてる印象だ。

    メソッドもC関数に皮を数枚かぶせた感じの実装で、実態はIMP型という関数ポインタだ。

    更にランタイムが非常に強力で、カプセル化されていながら、見ようと思えば何でも見れるようになっていて実にフックしやすい言語なんだ。

    フックの仕方には何種類か方法があって、木下さんの連載を参考にすると
    あたりが挙げられる。詳しくは連載の方が面白いのでそちらを見ていただくとして、簡単で便利なのはIMPの入れ替えだ。
    IMP変数を取得するための関数もランタイムで提供されているため、実に簡単にフックが行える。

    ■例を見てみよう!

    - (BOOL)shouldShowDictationKey;というインスタンスメソッドがUIKeyboardLayoutStarクラスに存在する。これは音声入力キーを表示すべきかどうかYESかNO(BOOL値)で返すメソッドだ。
    iOSの音声入力はSiriが可能な言語と今のところ同じであるので、使用中のキーボードがそれに該当するかどうかを判定してYESかNOを返していると想像がつくと思う。

    このメソッドを書き換えて常にYESやNOを返す処理に変えてしまえば、あのマイクキーが常に出るようになったり、常に表示されなくなる事も想像がつくだろう。 このメソッドをフックするには以下のようなコードで実現できる。
    IMP old, new;
    old = class_getMethodImplementation([UIKeyboardLayoutStar class], @selector(shouldShowDictationKey));
    new = class_getMethodImplementation([UIKeyboardLayoutStar class], @selector(new_shouldShowDictationKey));
    
    IMP tmp;
    tmp = old;
    old = new;
    new = tmp;
    
    オリジナルのメソッドを使う気がなければ最後のところは
    old = new;
    だけで事足りてしまう。 また、method_exchangeImplementationsなんて素敵な関数もあるのでこうも書ける。
    Method old, new;
    old = class_getInstanceMethod([UIKeyboardLayoutStar class], @selector(shouldShowDictationKey));
    new = class_getInstanceMethod([UIKeyboardLayoutStar class], @selector(new_shouldShowDictationKey));
    
    method_exchangeImplementations(old, new);
    いずれにせよ、これらの処理を行えば以後OSやアプリケーションがshouldShowDIctationKeyメソッドを呼び出すと、実際に処理が走るコードは自らが書いたnew_shouldShowDictationKeyメソッドのもととなる。 ちなみにnew_shouldShowDictationKeyは追加しなければならないので、class_addMethod関数で追加できる。
    static BOOL replaced_UIKeyboardLayoutStar_shouldShowDictationKey(UIKeyboardLayoutStar *self, SEL _cmd) { return NO; }
    
    class_addMethod([UIKeyboardLayoutStar class],
        @selector(new_shouldShowDictationKey),
        &replaced_UIKeyboardLayoutStar_shouldShowDictationKey,
        "c@:");
    
    メソッドの実装であるIMPを上書きするだけでも書き方は色々ある。
    Method method = class_getMethodImplementation([UIKeyboardLayoutStar class], @selector(shouldShowDictationKey));
    static BOOL replaced_UIKeyboardLayoutStar_shouldShowDictationKey(UIKeyboardLayoutStar *self, SEL _cmd) { return NO; }
    
    method_setImplementation(method, &replaced_UIKeyboardLayoutStar_shouldShowDictationKey);
    
    さて、これらの処理を適当な関数にまとめて、その関数を呼び出せば晴れてフックが完了するが、元々は存在しない関数だ。一体どうやって呼べばいいのか。
    そもそもこのコードを任意のタイミングで読みこませる(ロード)にはどうしたらいいのか。

    まず、動的なコードのロードはライブラリが同様の形態を持っているため、これを読み込むための環境が配備されている。OS XのサブセットであるiOSではdyld(Linuxならld-linux.so)によって読み込まれる.dylib形式(Linuxで言えば.so、Windowsなら.dll)にコンパイルしてdyldに読み込んでもらえばいい。

    次に関数の呼び出しだがアプリケーション側が呼んでくれるはずもないので、ロードされたタイミングで実行する関数をdyldに指定するオプションがある。これはgccの-initオプションの引数に関数名を記述すればいい。このオプションはDarwin linker(dyldのことだ)に引き渡される。man dyldでマニュアルを読むとこのオプションで指定された関数はdyldによって実行される事が書いてある。
    そこには__attribute__((constructor))が指定されたものも実行する事が記述されている。

    これらをまとめると、このTweakは以下のようなコードになる。

    このコードを動的にロードできるようdylib形式でコンパイルしたファイルがTweakそのものだ。

    この例だとメソッドの置換ではなく上書きにしたため、わりと短いコードですんでいるが、置換するとなると行うべき処理は多くなっていき、フックするメソッドが増えていくととても長いコードになっていく。そこで、このランタイムによる実装の置き換えを便利な関数でまとめてくれたライブラリを提供してくれるパッケージがCydiaにはある。そう、MobileSubstrateだ。

    ■MobileSubstrate

    MobileSubstrateの提供する関数はメソッドの置換や関数の置換、インスタンス変数の取得を行える関数を提供してくれている。
    では上記のコードをMobileSubstrateの一般的な関数を使用した形にすると、以下の様になる。

    種々のTweakが一様にMobileSubstrateをパッケージの依存関係に指定しているのは、MobileSubstrateが提供する関数を使用しているのもあるが、他にも重要な機能をMobileSubstrateは担っている。 ざっと以下のような構成だ。
    • MSHook系関数を提供するライブラリlibsubstrate.dylib
    • TweakをロードするSubstrateLoader.dylib
    • plistによるTweakのロードを制御(SubstrateLoader)
    • DYLD_INSERT_LIBRARIES環境変数へのMobileLoader.dylib(MobileSubstrate->SubstrateInjection->SubstrateBootstrap->SubstrateLoader)の追加
    • DYLD_INSERT_LIBRARIESを元に戻すMobileSafety
    この中で、TweakをロードするSubstrateLoaderが最重要機能といってもいいだろう。
    これはDynamicLibrariesディレクトリ以下のdylibを同名のplistの内容からロードの可否を判定してロードしてくれる。特定のアプリケーションにのみフックを適応させたい場合、このplistに条件を記述しておくことでコード本体にそのif文をかかずにすむようになっている。これがないと各Tweakは自分で自分のコードをロードする為のコードをかかなくてはならなくなってしまう。

    TweakをロードするのがSubstrateLoaderなら、SubstrateLoaderはどのようにロードされているのだろうか。SubstrateLoaderだって勝手に書かれた関数でしかないのだ。これにはDYLD_INSERT_LIBRARIESという環境変数へdylibを指定する事でdyldがロードするdylibの一つに加える事で実現している。

    DYLD_INSERT_LIBRARIESはLinuxでいうところのLD_PRELOADに非常によく似ていて、テストのために一時的に使用するライブラリのバージョンを違うものを指定したい時などに使われるものだ。LD_PRELOADを使ったフックについてはこちらのページ(LD_PRELOADを使ったテスト(C言語編))が非常にわかりやすく、行なっている内容もiOSのTweakと非常に似ている。そんなに長くないので是非読んでみてほしい。

    現在のMobileSubstrateはMobileSubstrate.dylibをDYLD_INSERT_LIBRARIESに追加している。/Library/MobileSubstrate/MobileSubstrate.dylibは/Library/Framework/CydiaSubstrate.framework/Libraries/SubstrateInjection.dylibへのシンボリックリンクとなっていて、SubstrateInjection.dylibは同ディレクトリのSubstrateBootstrap.dylibへのシンボリックリンクとなっている。
    SubstrateBootstrapからSubstrateLoader.dylibが読み込まれ、各種Tweakが読み込まれる流れだ。

    最後にMobileSafetyについて。SpringboardをクラッシュさせるTweakをインストールしてしまった場合、無限にRespringする事態におちいるのを防ぐためにTweakの導火線であるSubstrateLoader.dylibをロードするDYLD_INSERT_LIBRARIES環境変数を元の状態に戻した上でSpringboardを起動する役目を担っている。これが俗にいうSafe Modeだ。Tweakのロードを行わないだけなので、Cydiaからインストールしたフックを行わないアプリケーション(iFileとか)は普通に起動できる。SpringboardのクラッシュまでならMobileSafetyで対応できますが、Rebootループになるようなものには無力なので、最新のMobileSubstrateでは音量アップボタンを押しながら起動する事でTweakはおろかMobileSafetyもロードせずに起動できるようになりました。この場合もやっぱりiFileとかは動かせます。

    ちなみにOS XではSIMBLというLoaderが有名だ。MSHook系の関数を使用する前のようなコードを書く事でOS X向けのTweakも作成できる。

    ■theos

    最後にtheosについて。theosはiOS向けのMakefileプラットフォームで、長ったらしいMakefileを非常に簡素化できます。また、詳細は過去の記事を参照として割愛しますが、Tweak向けにマクロを用意してあり、直感的に記述できるようになります。
    上記の例示コードでいくとtheosを使用するとたった3行で記述できてしまいます。簡単だね!:)

    %hook UIKeyboardLayoutStar
    - (BOOL)shouldShowDictationKey { return NO; }
    %end

    ■終わりに、Jailbreak developerが増える事を願って。

    iOSのTweakのソースコードの多くはgithubで公開されています。
    記事上部で長々と書いたコードもtheosを使えば簡単に書けますので、「ここの動きが気に食わない」と感じるところがあれば、是非チャレンジしてみてはどうでしょうか。私も最初は大学でのC言語の授業(ポインタぐらいまで)程度の知識からスタートしましたので、そんなに難しくはないと思います。
    何より自分の作ったものが動くというのは楽しいものですよ?

    フリックキーボードの音声入力キーを無効にするNoDictation

    iOS5.1からSiriが日本語に対応した事で、日本語キーボードでも音声入力が使えるようになりましたが
    あまりの邪魔っぷりにこのキーを無効にするtweakを作りました。Require: iOS 5.1+です。

    日本語フリックキーボードの場合だけ無効にするバージョンと全てのキーボードで無効にするバージョンを作りました。
     全てのキーボードで無効にするバージョンはBigBoss提出予定です。

    https://github.com/r-plus/NoDictation/downloads

    ちなみにSiriが要らないのならSiriをOFFにすれば純正でこのボタンは無効です。
    iPadは設定から音声入力のON/OFFが純正で可能です。

    メモリの初期不良を交換した!

    先日UMAXの4GBx2メモリをSofmap-Onlineで買ったのですが、初期不良を掴んでしまった時のメモ。

    とりあえずここから症状を報告しました。https://www.sofmap.com/inquiry_mail_ec/exec/
    たしかこんな感じ。

    Windows使用中に突然落ちてしまった為、Memtest86+を走らせるとErrorの数値が変化せず落ちるため初期不良と思われます。

    1日後あたりにこんな返信、

    この度、ご注文を頂いておりました上記商品でございますが、
    ご連絡頂きました状況から、初期不良の可能性が考慮されます
    為、弊社にて交換のお手続きをさせて頂きたく存じます。
    
    なお、商品の交換方法につきましては、弊社から交換商品と
    着払いの返送用伝票をお客様のご注文時の配送先ご住所へ
    お送り致します。
    交換商品をお受け取り頂く際に、お届けにあがった佐川急便
    の配送担当者へお手元の不具合のある商品を、梱包頂いた状態
    で返送用伝票とともにお渡し頂く事により、商品の交換と
    させて頂きます。
    
    また、商品交換の際には、不具合品とともに、下記一式を
    梱包箱へお戻しくださいますようお願い致します。
    
    ○御買上票/納品書(商品に同梱)のコピー
    (原本はお手元にお取り置き下さい。)
     もしくはお名前・ご注文番号、不具合の症状を記載したメモ書き
    ○マニュアル、メーカー保証書、付属品など購入時の
     製品に同梱されていた物全て。
    
    梱包頂く箱や緩衝材がお手元に無い場合は、交換品をお届け
    致します梱包材をそのままご利用頂いても結構でございます。
    
    上記方法による交換にご了承頂けます場合は、大変お手数を
    お掛け致しますが商品の交換につきまして、ご希望の配送時間を
    下記にご記入頂き、本メールをご返信頂けますようお願い
    申し上げます。
    
    時間帯指定(【】内に○をお願い致します。)
    【 】午前中
    【 】12:00-14:00
    【 】14:00-16:00
    【 】16:00-18:00
    【 】18:00-21:00
    【 】指定無し

    ってことなので、○をつけて返信。
    メモリをパッケージに戻して、納品書は捨てたので指示通りに名前・注文番号、不具合の症状を記載したメモを書いて佐川を待ちます。

    佐川が来た際にダンボール開けて不良メモリとメモ書きを入れて佐川の人に渡して完了!

    返送料もかからなかったし、初期不良なら思ったよりも素晴らしい対応でした。

    DocSetsを試そうとしてダメだった

    DocSetsがAppStoreに並びました。
    買う前に試してみたかったので、コンパイルしてみましたメモ。

    まずはcloneします。

    Air:git_clone hyde$ git clone git://github.com/omz/DocSets-for-iOS.git
    Cloning into DocSets-for-iOS...
    remote: Counting objects: 358, done.
    remote: Compressing objects: 100% (206/206), done.
    remote: Total 358 (delta 158), reused 343 (delta 145)
    Receiving objects: 100% (358/358), 8.96 MiB | 107 KiB/s, done.
    Resolving deltas: 100% (158/158), done.
    で、そのままコンパイルしてみる。
    何かエラーがでました。
    そんなメソッド知らんと宣ってるので.hに宣言を追加してあげましょう。
    -[RootViewController addDocSet:]の宣言をぴろっとな。
    @interface RootViewController : UITableViewController  {
     
     DetailViewController *detailViewController;
    }
    
    @property (nonatomic, strong) DetailViewController *detailViewController;
    
    - (void)addDocSet:(id)sender; ←これ
    @end
    今度は通りました。
    が、ARM向けにコンパイルしようとすると認証があるらしい。

    大人しく買えというオチになったのであった。

    キャンペーン開始通知スクリプト

    先日の#pyfesでjsonファイルを操作したことないなー、と思ったのでこんなのを作ってました。

    @search_accountの最新3ツイートを取得し、それぞれ5分以内のものか判定した上でsearch wordが含まれているかを判定します。
    最後の部分でメール送信なり、自分にmentionなり、boxcarにプッシュなりを設定し、
    数分間隔で定期実行させればキャンペーン開始通知として使えそうです。

    ところでPythonの変数って推奨の銘々則ってあるんですかね?PEPとかに。
    ObjCみたくキャメルケースで書いてしまってるけど。

    Twitter botを定期実行させるPaaS先選定

    Twitter上で早い者勝ちのキャンペーン等がされる事が最近多いため、定期的にそのTwitterアカウントのツイートを取得してキャンペーン関連のツイートが行われたら通知を受け取りたいと思います。
    流石にこのためだけにPCを立ち上げっぱなしにするのも嫌なので、デプロイ先PaaSをどれにするか調べたメモ。

    ■heroku

    RubyもPythonも使えていい感じのherokuさんですが、
    Cronが有料($3)でHourlyと今回の目的にそぐわない感じです。
    出来れば数分以内に通知を受け取りたいわけなので、
    分間隔で使えるCronが欲しいところです。

    と、思っていたらclockwork使えば秒単位で実行できそうでした。
    Schedulerは10分間隔で可能みたいです。後でやってみます。

    ■fluxflex

    国産のfluxflexさん。
    #!/usr/bin/env python
    
    import datetime
    
    print 'Content-type: text/html\n\n'
    print '<html><body>'
    nowtime = datetime.datetime.now()
    print 'Now time is %s' % nowtime
    print '</body></html>'
    などという軽量極まりないコードを1回実行させるとCPU時間が
    0.265増えたので、3分間隔で実行させると、
    0.265 * 480 = 127.2というわけで結構余裕ありそうですが、定期実行がしにくい感じです。

    デプロイがgitだったりgithubからimportできたらtwitterアカウントでログインできたり、好印象。



    ■GoogleAppEngine

    Cronが最短1分間隔で使えます。無料のQuotaは色々とややこしいのですが、
    結論から言えばBotを動かす程度だとこんな感じで収まります。

    Gmailからのメール送信がすごく簡単なのが良い。

    ■で、どれ使うの

    Cloud FoundryとかDotCloudとか名前だけ知ってるやつも試したい。
    DotCloudはSSHで入ってcrontabいじるみたいで楽しそう。

    とりあえずGAEで作成済みなので次回はそれで。

    Bylineのキャッシュが終わったらLocal Notificationを発行する

    ■BylineTweetFormatter v0.0.3

    BylineTweetFormatterを修正して、Sylfeedのように同期 キャッシュが終わったタイミングでLocal Notificationを出すようにしてみました。
    Local Notificationなのでフォアグラウンドの場合は出ません。
    加えてキャッシュ完了数で判定しているため、1つもキャッシュしない設定や同期の場合通知されません。


    https://github.com/r-plus/BylineTweetFormatter

    DictGoogle v0.3 スワイプで辞書画面を終了

    普段左手持ちで左の親指で全て操作してるのですが、この持ち方だと画面右上がちょっと遠い。
    そこそこの頻度で利用するDefineメニューのDoneボタンを押すのがそろそろ面倒になったので、
    左右どちらかのスワイプで破棄できるようにDictGoogleに機能追加しました。

    モーダルビューの破棄に限定したので、iPadではスワイプしても意味はないです。

    PreferenceBundleで同じリストの管理をしやすく

    ■PreferenceLoaderでのリスト選択について

    PreferenceLoaderのplistでリスト選択させたい場合、大抵は下記のようにして
    PSListItemsControllerクラスにvalidValuesとvalidTitlesのArrayを書いてあげれば良いので非常に楽です。
    detail
    PSListItemsController
    validValues
    
        80
        100
        120
        160
        200
    
    validTitles
    
        80(default)
        100
        120
        160
        200(insensitive)
    
    が、このリストへのリンクが1つ2つならまだ平気ですが、10個とかになると億劫になってきて、
    追加や削除が面倒なことうけあいです。

    PreferenceBundleにするとこの辺りは非常に楽になります。

    ■まずはBundleへ誘導するPreferenceLoader用のplistを作成

    entry.plistを作成します。detailが自作するクラス名になります。
    {
     entry = {
      bundle = "SleipnizerSettings";
      cell = PSLinkCell;
      detail = "RootPreferenceController";
      icon = "SleipnizerforSafari.png";
      isController = 1;
      label = "Sleipnizer for Safari";
     };
    }

    このentry.plistはMakefileの記述でビルド時に適切にリネーム・移動します。

    BUNDLE_NAME = SleipnizerSettings
    SleipnizerSettings_FILES = Preference.m
    SleipnizerSettings_INSTALL_PATH = /Library/PreferenceBundles
    SleipnizerSettings_FRAMEWORKS = UIKit
    SleipnizerSettings_PRIVATE_FRAMEWORKS = Preferences
    
    include $(THEOS_MAKE_PATH)/bundle.mk
    
    internal-stage::
     $(ECHO_NOTHING)mkdir -p $(THEOS_STAGING_DIR)/Library/PreferenceLoader/Preferences$(ECHO_END)
     $(ECHO_NOTHING)cp entry.plist $(THEOS_STAGING_DIR)/Library/PreferenceLoader/Preferences/SleipnizerforSafari.plist$(ECHO_END)

    ■今までPreferenceLoader用に記述していたplistを読み込む

    今まで記述していたPreferenceLoader用のplistがそのまま使えますので、
    Bundle用の格納ディレクトリであるResourcesの中に放り込みます。

    放り込んだplistを読むために以下のメソッドを書いておきます。ここでのクラス名がentry.plistのdetailに記述したものと同一になるようにします。loadSpecifiersFromPlistNameの引数はplistの名前を書きます。

    #import <Preferences/Preferences.h>
    
    __attribute__((visibility("hidden")))
    @interface RootPreferenceController : PSListController
    @end
    
    @implementation RootPreferenceController
    - (id)specifiers {
      if(_specifiers == nil) {
        _specifiers = [[self loadSpecifiersFromPlistName:@"SleipnizerforSafari" target:self] retain];
      }
      return _specifiers;
    }
    @end

    これでこれまでと同じ状態に出来ました。

    ■リスト用のメソッドを指定する

    PreferenceLoader用のkeyの中に、そのためのものがあります。valuesDataSourceとtitlesDataSourceです。
    冒頭のこれまで長々と書いていた部分を置き換えます。

    detail
    PSListItemsController
    valuesDataSource
    valuesSource:
    titlesDataSource
    titlesSource:

    ■メソッドを記述する

    NSArray *を返すように記述してあげます。

    - (NSArray *)valuesSource:(id)target {
      return [NSArray arrayWithObjects:@"80", @"100", @"120", @"160", @"200", nil];
    }
    
    - (NSArray *)titlesSource:(id)target {
      return [NSArray arrayWithObjects:@"80(default)", @"100", @"120", @"160", @"200(insensitive)", nil];
    }
    

    これでリストを編集したければ、こちらのコードを弄れば参照箇所全てに反映されます。