MOSA Multi-OS Software Artists

MOSAはソフトウェア開発者を支援します

プログラマーに興味がある方なら誰でも入会いただけます。
MOSA Multi-OS Software Artists
===SINCE 1995===

  • iPhone/iPod touch アプリ紹介
  • MOSA掲示板
  • 活動履歴
  • About MOSA(English)

MOSADenバックナンバー 2007年5月発行分

  • MOSA Developer News[MOSADeN=モサ伝]第253号

    2007-05-29

    目次

    • 「りんご味Ruby」       第4回  藤本 尚邦
    • 藤本裕之のプログラミング夜話   #115
    • 高橋真人の「プログラミング指南」  第113回
    • 書籍紹デザイニング・インターフェース 第2版

    りんご味Ruby   第4回  藤本 尚邦

    前回は、REPLの基本的な仕組みについて説明しましたが、今回は「RubyのことはRubyに聞け」の続きとして、irbとCocoaRepl.appを紹介します。

    ■ RubyのことはRubyに聞けその2 – irb

    irbは、前回紹介した自作repl.rb、あるいはRubyのサンプルeval.rbに似たタイプのREPLコマンドを大幅に発展させた、Rubyに標準で付属するREPLコマンドです。基本的な使い方は、repl.rbと同様に

    ・標準入力の終端(コントロール+D)に達しないかぎり、以下を繰り返す:
    ・プロンプトを表示して
    ・標準入力から文字列を読み込み
    ・それを(Rubyプログラムとみなし)評価して
    ・結果を表示

    という流れになりますが、irbの場合には、構文的に正しい(と推定される)Rubyプログラムが完成するまで何行でも入力を受け付け、完成したところで入力されたプログラムが評価されます。そのため、メソッドやクラスの定義、if構文やブロック構文の入力に何行でも費やすことが可能になります。

     $ /usr/bin/irb
     irb(main):001:0> 1 + 2 + 3
     => 6
     irb(main):002:0> puts "hello,world"
     hello,world
     => nil
     irb(main):003:0> Time.now
     => Thu May 24 15:03:17 JST 2007
     irb(main):004:0> def factorial(n)
     irb(main):005:1>   if n == 0 then
     irb(main):006:2*     1
     irb(main):007:2>   else
     irb(main):008:2*     n * factorial(n-1)
     irb(main):009:2>   end
     irb(main):010:1> end
     => nil
     irb(main):011:0> factorial 10
     => 3628800
    


    irbには、この他にもいろいろと隠れた機能が盛り沢山に含まれているようなのですが、私も説明できるほどにはわかっていません(汗)

    ■ RubyのことはRubyに聞けその3 – CocoaRepl.app

    irbはターミナルから使ういわゆるコマンドラインツールなのですが、Macの開発者の中にはコマンドラインツールが苦手な方もいらっしゃるかもしれないということで、Ruby用の簡易的なREPLとしてCocoaRepl.appというアプリケーション(OSX 10.4用ユニバーサルバイナリ)を用意しました。

    http://rubycocoa.sourceforge.net/hisa/files/CocoaRepl.app.zip

    ここでは、CocoaRepl.appの使い方について簡単に説明します。

    CocoaRepl.appを起動すると、ウィンドウが表示されます。このウィンドウの中で上側の部分が、Rubyプログラムを落書きするためのテキストビュー、通称「落書きビュー」になります。下側の部分は、状況に応じて、評価結果が表示されたり、標準出力・エラー出力の内容が表示されたり、Rubyに付属しているリファレンスマニュアルやメソッド名のリストが表示されたりします。

    ◇ Rubyプログラムの入力

    落書きビューの好きなところに、Rubyプログラムを入力します。もちろん複数行でも構いません。メソッドの入力中に、コマンドキーを押しながらピリオドを入れると、メソッド名の候補一覧が表示されます。また、入力中にリファレンスマニュアルを参照し、マッチするエントリーがあれば、それを下側のビューに表示します。

    ◇ Rubyプログラムの評価・実行

    落書きビューに入力したプログラムは、Rubyメニューの中にある3つのコマンド(それぞれショートカットが割り当ててあります)のいずれかを使って評価・実行することができます。評価・実行の結果は、ウィンドウの下側に表示されます。

    ・Eval — 落書きビュー全体を評価
    ・Eval Selection — 選択箇所を評価
    ・Eval Line — 挿入ポイントのある行を評価

    (注) Rubyメニューには、挿入ポイントを含むブロックを評価するための
    Eval Block というコマンドもありますが、このコマンドは、今のところま
    ともに実装されていません。

    CocoaRepl.appには、RubyCocoa.frameworkが含まれているので、Cocoaのオブジェクトにアクセスすることも可能です。例えば:

    OSX::NSApp.mainWindow.setTitle(Time.now.to_s)

    と入力して評価・実行するとどうなるでしょう?

    CocoaRepl.appは、落書きをファイルに保存できない・標準入出力の扱いが不完全など、いろいろと機能が足りないのですが、Rubyプログラムのかけらを試してみるくらいの用途にはそれなりに使えます。

    ■ (宣伝) RubyCocoa 0.11.0

    先日、RubyCocoa 0.11.0をリリースいたしました。よろしければお試しください。

    http://rubycocoa.sourceforge.net/

    上記のCocoaRepl.appも、RubyCocoaを使ってRubyで書かれています。Finderで
    CocoaRepl.appのコンテキストメニューから「パッケージの内容を表示」を選び、Contentsフォルダの中にあるResouresフォルダを開くと、Rubyで書かれたCocoaRepl.appのソースファイルがいくつかありますので、興味のある方はのぞいてみてください。

    ■ まとめ

    本連載では、これまでも今後も、随時、Rubyプログラムのかけらが登場するはずです。REPLを使わずに、Rubyプログラムをファイルに保存してrubyコマンドで実行してみるという方法もあるのですが(REPLよりもそちらの方法を好む方もいるようです)、今回紹介したようなREPLに親しんでいただき、手を動かしながらRubyプログラムを試しつつ読んでいただければと思います。

    藤本裕之のプログラミング夜話 #115

     前回の続き、予告通り、作ったMyApplication.h とMyApplication.m の中身のコーディングを行おう。まずヘッダーファイル、MyApplication.hをひらき、以下のようにコーディングする。こりゃなんだ、という疑問はあとで氷解するはずなので(たぶん)、今はだまされたと思ってやってほしい。

    #import 
    
    @interface MyApplication : NSApplication {
        AboutWindowManager*      aboutWindowController;
    }
    
    -(void)awakeFromNib;
    -(IBAction)orderFrontStandardAboutPanel:(id)sender;
    
    @end
    


     上で足した、「aboutWindowController」というインスタンス変数がMainMenu.nibではない別のnibファイルのオーナーオブジェクトになる。こいつの初期値をnilにしておくために awakeFromNib が必要。そしてorderFrontStandardAboutPanel: は前回「こいつをオーバーライドして別のnibファイルからアバウトパネルをロードする」と書いたメソッドである。あ、ここで一言、これ、このほうが分かりやすいかなと思ってオーバーライドしてるので、このメソッドの名前の意味、すなわち「標準的アバウトパネルを
    前面に持ってくる」ちうのと実際の行いが違って気分がすぐれない、というヒトはMainMenu.nibに立ち戻り、この MyApplication クラスに……例えばshowMyAboutPanel: てな名前のアクションを追加し、アプリケーションメニューの「About このアプリケーション」からそいつへラインをつなげても構わない。その場合でも単に「オーバーライド」ではなくなるだけで動作に変わりはないから。

     こんだけやったら次は MyApplication.m の番。これまた意味はあとで判明するので(たぶん)とりあえず以下のようにコーディング。

    #import "MyApplication.h"
    
    @implementation MyApplication
    
    -(void)awakeFromNib {
        aboutWindowController = nil;
    }
    
    -(IBAction)orderFrontStandardAboutPanel:(id)sender{
        if(aboutWindowController == nil){
             aboutWindowController = [[AboutWindowController alloc]
                                      initWithWindowNibName: @"MyAbout"];
        }
        [aboutWindowController showWindow:self];
    }
    
    @end
    


     awakeFromNib の中身はさっき書いた通り。問題はもうひとつのメソッドだよね。この意味するところは、「aboutWindowController オブジェクトがまだ生成されてなかったら、これをアロケートし、MyAbout.nib というnibファイルの中身を使って初期化しろ。ほんで、そのオブジェクトに showWindow:メッセージを送れ」、である。
     これでこいつのコーディングはおしまい。次回はその、MyAbout.nibを作成する。……そんな馬鹿なと思うかもしれないけどもうこれ以上のコーディングは必要ないのである。ま、モノがアバウトパネルという見せるだけのもんだからだけどね。Cocoaって簡単でしょう?
                                (2007_05_24)

    高橋真人の「プログラミング指南」第113回

    プログラマのためのオブジェクト指向再入門(21)

    〜XcodeによるPowerPlant X入門(5)〜

     こんにちは。高橋真人です。
     はじめに訂正です。前回のコードに大きなミスがありました。MyApplicationクラスの関数、HandleNew()とHandleClose()の中身が全く違うものになっていました。正しくは以下の通りです。
     前回のコードをビルドして実際に試された方が戸惑われているだろうとモサ伝の前号にも訂正を載せさせていただきましたが、私としても一体なんでこんな取り違いをしてしまったのかと反省しきりです。
     混乱させてしまった方、すみませんでした。

    OSStatus
    MyApplication::HandleNew()
    {
       PPx::Window* theWindow =
           PPx::NibDecoder::CreateWindowFromNib(
               CFSTR("main"),
               CFSTR("MainWindow"));
       if (theWindow) {
           ::RepositionWindow(
                   theWindow->GetSysWindow(),
                   nil,
                   kWindowCascadeOnMainScreen);
           theWindow->Show();
       }
    
       return noErr;
    }
    
    OSStatus
    MyApplication::HandleClose()
    {
       OSStatus status = eventNotHandledErr;
    
       WindowRef theWindow =
           ::GetFrontWindowOfClass(kDocumentWindowClass, true);
       PPx::Window* win = PPx::Window::GetWindowObject(theWindow);
       if (win) {
           win->Close();
       }
    
       ::CFShow(CFSTR("HandleClose called."));
    
       return noErr;
    }
    


     それでは気を取り直してコードの解説をいたします。できれば、以下の続きを読まれる前に上記のコードにさしかえた上で再実行してみていただけるとよいでしょう。
     ところで、プログラムを走らせてみて、おかしいと思いませんでしたか?
     MyApplication::HandleClose()という関数(上記のさしかえ部分に含まれています)が呼ばれると、”HandleClose called.”というメッセージがコンソールに出力されるのですが、この出力のタイミングがおかしいのです。メッセージがどのようなタイミングで表示されるのか(つまり、HandleClose()関数がどんなタイミングで呼び出されるのか)について注意しながらプログラムを試してみていただけると「おかしい」の意味がお分かりになると思います。
     この辺についてはのちほど触れたいと思います。

     さて、まずはmain.cpです。
     Setup()という関数ではアプリケーションの初期化をしています。
     ここではSysNibというクラスのオブジェクトを作成していますが、その際にNibファイルの名前を与えています。これは、SysNibクラスのコンストラクタにNibファイル名を与えていることなのですが、こうすることで生成されたオブジェクトが「どのNibファイルを対象にして読み込みをするのか」を記憶するようになります。従って以下に説明するように、単に”MenuBar”と、Nibファイルの中の要素の名前を指定してやるだけでよく、Nibのリファレンスをいちいち生成したりする手間が省けます。
     そんなわけで、作成したSysNibのオブジェクトのSetMenuBar関数を呼び出すと、上で指定したNibファイルの中から、引数で指定した名前を持つメニューバー項目を使って、アプリケーションのメニューバーを構築します。
     メニューに関するコードはこれだけです。

     ところで、SysNibに限らずいろんなところにPPx::というプレフィックス
    (接頭辞)が付いていますが、これはPowerPlant Xが定義している名前空間(namespace)です。名前空間に関しては、この連載の第44回で解説していますので、そちらを参照してください。
     それから余談ですが、メモリ管理の話を簡単にします。
     Carbonの経験のある方はメニューの設定などでIBNibRefの変数を使って作業をしたと思いますが、これらの末尾にRefが付く型の変数は使用後にメモリの破棄が必要になることをご存知でしょう。(IBNibRefの場合には、DisposeNibReference()を呼び出す)
     Cを長いことやっていると、こういった後始末をきちんとする習慣(もちろ
    ん、よい習慣です)が身に付いていて、コードを見ると、ついつい「メモリ管理がどうなっているのかが気になる」かもしれませんので(笑)、その懸念を払拭しておきます。
     SysNibに限らず、PPxにおいてメモリ管理(確保と破棄)を必要とするケースは、オブジェクト自体がそれをやってくれるようになっています。このようなやり方は、C++の世界ではRAIIなどと呼ばれ、必須の技術となっています。
    詳しくは連載の第25回で触れていますので、そちらを参照してください。

     話を戻して、次のRegisterCommonNibDecoders() ですが(以降、特別に強調する場合を除きPPx::は省略します)、これはWindowや各種ViewをNibから読み込むために必要な情報を登録しています。詳しい処理内容については割愛しますが、興味のある方はコードを覗いてみてください。
     ちなみに、Carbonでは、アプリケーションの初期化にはInitCursor()を呼び出すことになっていましたが、最近Appleのドキュメントを読んだら「もはや、不要」と書いてありましたので、呼び出していません。

    ニュース解説

    ★★★ 開発関連のニュースはweb掲載開始しました ★★★
    http://www.mosa.gr.jp/?page_id=1017

    ・四月のニュース
    http://www.mosa.gr.jp/?p=1076

    書籍紹介               デザイニング・インターフェース

     解説担当:高橋政明

    デザイニング・インターフェース
      Jenifer Tidwell 著
      ソシオメディア株式会社 監訳  浅野 紀予 訳
      オライリー・ジャパン ISBN978-4-87311-316-6  定価3,990円

     ユーザーインターフェース(以下UI)に関する今年出版された本で、副題は「パターンによる実践的インタラクションデザイン」です。ソフトウェアに限らず『デザイン上の問題点に対するすぐに使えるパターン集』とありましたので多いに期待して購入しました。

     パターンには連番が振られ全部で94載っています、出版社のwebで目次をご覧ください。各「パターン」は概要、利用場面、理由、用法、事例が解説されています。ヒントや注意点も書かれていて参考になります。
     全ページカラーで美しい本です。原著が2005年ですので画面等をキャプチャしているOSやソフトウェアのバージョンは当時のものです。

     二段組みの部分を除き一行の文字数が多く私には読みにくいと感じました。訳すと文章が長くなるためなのでしょうか。ページ数イコールコストでしょうから難しい判断だったのかもしれませんが、デザインを論ずる本の紙面としては残念です。出版社のwebで原著にもリンクしていて同じ部分をpdfサンプルで見る事ができますのでご自身で確かめてください。

     UIの設計が大変難しいことは皆さん実感されていると思います。それは「使い易くて美しく、それだけではなくすぐにわかる独自性も必要」と要求が高いからでしょう。Interface BuilderでAquaのUI部品をガイドに添ってレイアウトするとそこそこ使い易く美しい画面ができあがります。しかしそれだけでは個性は出せません。ひと目見てそれとわかる個性も製品の大切な要素です。個性を出すためのトレードオフがあるなら、それを最小にするためにも本書の関連するパターンそれぞれに対する理解が大切と思います。

     この本はどのようなUIにするべきか悩んだり行き詰まったりした時の簡便な解決策が書かれている訳ではありません。またUIのチェックリストでもない事は序章にも書かれています。ボリュームのある本ですし即効性は期待しないほうが良いと思います。(実のところ私もまだすべてを読み終えていません)

     UIは様々な条件を考慮しなければなりません。機能が多く大規模なUIの場合、UIのパターンを統一するべきかそれぞれに最適なものを採用するべきでしょうか? おそらく誰が使うか、どのような頻度で使うかの条件だけでも結論は様々と思います。
     本書をとおしてUIのパターンをより理解する事でUI設計時の洞察が深くなるだけでなく、客観的なUIの評価分析にも役立つことと思います。

    ▼出版社のweb(詳しい目次とpdfのサンプルページが載っています)
    http://www.oreilly.co.jp/books/9784873113166/

    ◇MOSAからのお知らせと編集後記は割愛します◇

     

     MOSA Developer News   略称[MOSADeN=モサ伝]
            配信停止 mailto:mosaden-ml@mosa.gr.jp
     記事内容に関するご意見 mailto:mosaden-toukou@mosa.gr.jp
          記事投稿受付 http://www.mosa.gr.jp/?page_id=850
    Apple、Mac OSは米国アップル社の登録商標です。またそのほかの各製品名等
    はそれぞれ各社の商標ならびに登録商標です。
    このメールの再配信、および掲載された記事の無断転載を禁じます。
    特定非営利活動法人MOSA  http://www.mosa.gr.jp/
    Copyright (C)2007 MOSA. All rights reserved.

  • MOSA Developer News[MOSADeN=モサ伝]第252号

    2007-05-22

    目次

    • 「「Wonderful Server Life」    第47回    田畑 英和
    • 小池邦人の「Carbon API 徒然草」
    • ターミナルの向こうから      第2回  海上 忍 
    • 書籍紹介             エンジニアのための時間管理術

    「Wonderful Server Life」  第47回  田畑 英和

      〜アカウント編〜

     それでは今回はアカウント編のしめくくりとして、コンピュータリストの応用を解説したいと思います。コンピュータリストには、コンピュータへのアクセスを制限する機能があり、この機能を利用すれば任意のコンピュータへのログインを一部のユーザだけに制限することができます。

    ◇コンピュータリストのアクセス制御
     アクセスを設定するには、「ワークグループマネージャ」を起動し、ツールバーから「アカウント」をクリックして、画面左側にコンピュータリストの一覧を表示します。
     次にアクセス制御を設定したいコンピュータリストを選択し、右側の画面で「アクセス」をクリックします。アクセス制御はグループ単位で行うため、あらかじめアクセスを許可したいユーザをまとめたグループを作成しておく必要があります。
     デフォルトの状態では「すべてのグループがコンピュータを使用できる」ように設定されているため、アクセス制御はなにも行われていません。アクセス制御を行うには「以下のグループに限定する」を選択し、アクセスを許可するグループを登録します。「+」ボタンをクリックすると登録済みのグループが画面端に一覧で表示されますので、ここからアクセスを許可したいグループをリストにドラッグ&ドロップします。

    ・アクセスを許可するグループの追加
    http://homepage.mac.com/htabata/MXS10.3/img/WGM_Computer/WGM_Computer_06.png

     グループを追加したら、画面右下の「保存」ボタンをクリックします。これで、アクセスを制限したコンピュータリストに含まれるコンピュータには、追加したグループのメンバーしかアクセスができなくなります。
     なお、ここで実施させるアクセス制御はログインウインドウに対するアクセス制御です。つまり、アクセスを許可したグループに所属するユーザしか、ログインウインドウからログインできなくなります。一方、AFPやSSHによる接続はここでのアクセス制御の影響を受けませんので、これまでどおりネットワーク経由でのアクセスが可能です。

    ◇サービスのアクセス制御
     コンピュータリストのアクセス制御は、ログインウインドウに対するアクセス制御に限定されます。「サーバ管理」を使ってサービスのACLを設定すれば、各サービスごとのアクセス制御を実施することもできます。アクセス制御の設定が可能なサービスは次のとおりです。

      AFP、FTP、iChat、SSH、VPN、Web、Windows、
      Xgrid、ウェブログ、メール、ログインウインドウ

     設定方法ですが、「サーバ管理」で設定を行うサーバに接続し、左側のリストでホスト名をクリックした後、「設定(右下)」>「アクセス(右上)」の順でクリックします。
     まず、デフォルトの状態ですが、デフォルトではすべてのサービスに同じアクセス権が使用されるようになっており、かつすべてのユーザとグループにアクセスが許可されています。つまりいっさいサービスレベルでのアクセス制御は行われていないということです。

    ・サービスACLのデフォルト設定
    http://homepage.mac.com/htabata/MXS10.3/img/SACL/SACL_ADMIN_01.png

     サービスごとに異なるアクセス制御を行いたい場合にはまず「すべてのサービスに同じアクセス権を使用する」のチェックを外します。次にアクセス制御を行いたいサービスを左のリストから選択し、「以下のユーザとグループのみを許可する」を選択します。
     あとは画面下の「+」ボタンでユーザとグループのリストを表示し、アクセスを許可したい、ユーザまたはグループを右側のリストに追加します。

    ・サービスACLの設定
    http://homepage.mac.com/htabata/MXS10.3/img/SACL/SACL_ADMIN_02.png

     以上の設定を行えば、ユーザまたはグループ単位で各サービスへのアクセスを制御することができます。

     これで「ワークグループマネージャ」では、グループ単位でログインウインドウのアクセス制御が、「サーバ管理」では、ユーザ/グループ単位で各サービスのアクセス制御ができるようになりました。

     ところでサービスのACLで設定可能なサービスにもログインウインドウが含まれていますが、「ワークグループマネージャ」と「サーバ管理」で異なる設定をした場合どうなるでしょうか。
     例えば、「ワークグループマネージャ」では特定のグループのみのアクセスを許可し、「サーバ管理」では「ワークグループマネージャ」でアクセスを許可したグループには含まれないユーザに対してログインウインドウへのアクセスを許可したとします。この場合は、どちらかでアクセスが許可されていなければ、もう片方で許可しても、最終的にはアクセスが許可されないということになります。
     ルールとしては分っていても、混乱のもとになりやすいところですので、ログインウインドウのアクセス制御を行いたい場合は、どちらか片方のみで設定するのがよいでしょう。

     さて、これでアカウント編の解説は完了です。次回からは環境設定の管理について解説していきたいと思います。
    つづく                               

    小池邦人のCarbon API 徒然草(2007/05/18)

    〜 Carbonモダンアプリケーションへの道(その16) 〜

    今回は、ファイルのオープン(Open)とクローズ(Close)、ファイルへのデータ書き込み(Write)とファイルからの読み込み(Read)、ファイルの削除(Delete)やファイル名の変更(Rename)など、一般的なファイル処理を調べてみます。

    最初はファイルのオープンです。Mac OS X 10.4になり、今までCarbonFrameworkで親しまれてきたFSpOpenDF()やFSpOpenRF()など、引数にFSSpec構造体を渡すAPIは、そのほとんどがDEPRECATED指定となりました。ヘッダーファイルFiles.hに記載されているコメントでも言及されていますが、現在は代わりにFSOpenFork()を利用します。

    OSErr  FSOpenFork(
    
       const FSRef *ref,              // FSRefで対象ファイルを指示
       UniCharCount forkNameLength,   // フォーク名の長さ
       const UniChar *forkName,       // フォーク名(ユニコード文字列)
       SInt8 permissions,             // パーミッション(アクセス権)
       SInt16 *forkRefNum );          // ファイル固有のリファレンス番号

    まずオープンしたいファイルをFSRefで指定します。次に対象となるフォーク名をユニコード文字列で指定します。Macintoshのファイルシステムでは、ファイル内部にデータフォークとリソースフォークの2つのデータ領域を持つことが可能です。この仕組みは、Mac OS Xでも存続しています(消えると言う噂もありましたが…)。通常のファイル処理ではデータフォークの方をオープンすることがほとんです。その場合には、ディフォルト値としてforkNameLengthにゼロを、forkNameにはNULLを渡せばOKです。もし、リソースフォークの方をオープンしたければ、FSGetResourceForkName()で得た名称を
    ユニコード文字列として渡します。同様に、データフォーク名を得るためのFSGetDataForkName()というAPIも存在しています。

    OSErr FSGetResourceForkName( HFSUniStr255 * resourceForkName );
    OSErr FSGetDataForkName( HFSUniStr255 * dataForkName );
    


    次のファイルパーミッション(アクセス権)については、以下の7種類から選択することが可能です。データの読み書きをする一般的なファイル処理では、fsRdWrPermを選択しておけば良いでしょう。ファイルオープンが成功すると、forkRefNumにはそのファイル固有のリファレンス番号が返されます。その後、このリファレンス番号を用いてファイルデータの読み書きを行うことになります。

    enum {
           fsCurPerm    = 0x00,
           fsRdPerm     = 0x01,
           fsWrPerm     = 0x02,
           fsRdWrPerm   = 0x03,
           fsRdWrShPerm = 0x04,
           fsRdDenyPerm = 0x10,
           fsWrDenyPerm = 0x20
    };
    


    実際の作業では、ファイルをFSRefで指定するのではなく、ファイル名やパス名などで指定したい場合も多々あります。例えば、親ディレクトリ(フォルダ)のFSRefとファイル名(CFStringRef)を引数で渡すことで、対象ファイルをオープンするopenNameFile()ルーチンを作成すると以下のようになります。

    OSErr openNameFile( FSRef *pfsref,CFStringRef cfstr,char perm,short *fref )
    {
       UniChar         buf[256];
       FSRef           fsref;
       OSErr           err=1;
       UniCharCount    len;
    
       if( len=(UniCharCount)CFStringGetLength( cfstr ) ) // ファイル名の長さ
       {
           CFStringGetCharacters( cfstr,CFRangeMake(0,len),buf );  // ユニコード名
           if( ! FSMakeFSRefUnicode( pfsref,len,buf,kTextEncodingUnicodeDefault,
                                                                       &fsref ) )
                                                      // 対象ファイルのFSRefを得る
               err=FSOpenFork( &fsref,0,NULL,perm,fref );  // ファイルオープン
       }
       return( err );
    }
    
    ファイルからのデータの読み込みには、以下のFSReadFork()を利用します。
    
    OSErr  FSReadFork(
    
       SInt16 forkRefNum,        // 対象ファイルのリファレンス番号
       UInt16 positionMode,      // 読み込みデータ位置を指示する方法
       SInt64 positionOffset,    // 読み込みデータ位置オフセット
       ByteCount requestCount,   // 読み込みデータバイト数
       void *buffer,             // データバッファの先頭アドレス
       ByteCount *actualCount ); // 実際に読み込まれたデータバイト数
    
    読み込みデータ位置の意味は以下の4種類から選択することが可能です。ま
    た、読み込みポジションはSInt64(64ビット整数)で指定できますが、一度に
    読み込むデータ量はByteCount(32ビット整数)指定となり4G Byteに制限され
    ています。
    
    enum {
    
        fsAtMark        = 0,    // カレントポジション
        fsFromStart     = 1,    // ファイル(フォーク)の先頭からのオフセット
        fsFromLEOF      = 2,    // ファイル(フォーク)の最後からのオフセット
        fsFromMark      = 3     // カレントポジションからのオフセット
    };
    
    ファイルからのデータの書き込みには、以下のFSWriteFork()を利用します。
    引数の内容は、FSReadFork()とほとんど同じです。
    
    OSErr  FSWriteFork(
    
       SInt16 forkRefNum,        // 対象ファイルのリファレンス番号
       UInt16 positionMode,      // 書き込みデータ位置を指示する方法
       SInt64 positionOffset,    // 書き込みデータ位置オフセット
       ByteCount requestCount,   // 書き込みデータバイト数
       const void *buffer,       // データバッファの先頭アドレス
       ByteCount *actualCount ); // 実際に書き込まれたデータバイト数
    
    アクセスが終了したファイルを閉じるのには、以下のFSCloseFork()にファイ
    ルリファレンス番号を渡します。
    
    OSErr  FSCloseFork( SInt16 forkRefNum );
    
    またファイルやフォルダ(ディレクトリ)の削除は、FSDeleteObject()で行え
    ます。対象がフォルダの場合には、その中にファイルや別フォルダが含まれた
    ままだと削除ができませんので注意してください。
    
    OSErr  FSDeleteObject( const FSRef *ref );
    
    最後に紹介するのは、ファイルやフォルダ名を変更するための
    FSRenameUnicode()です。引数のtextEncodingHintにkTextEncodingUnknownを
    代入しておけば、ファイルシステムがディフォルトテキストエンコーディング
    を利用してくれます。
    
    OSErr  FSRenameUnicode(
    
       const FSRef *ref,              // 名称変更したいファイルかフォルダ
       UniCharCount nameLength,       // ファイルかフォルダ名の長さ
       const UniChar *name,           // ファイルかフォルダ名(ユニコード文字列)
       TextEncoding textEncodingHint, // テキストエンコーディング
       FSRef *newRef );               // 新規のFSRef(不必要ならNULL指定)

    ところで先ほどリソースフォークの話題が出ましたので、ついでにリソースファイルに関する注意点をいくつか上げておきます。もし、GetResource()やGetIndResource()などでリソースファイル内部の個々のリソースデータ(PICTリソースなど)にアクセスしたい場合には、リソースファイルをオープンしてカレントリソースに設定しておく必要があります。つまり、単純にFSOpenFork()で対象ファイルのリソースフォークをオープンするだけではマズイということです。

    昔からこの働きをしていたのは、Resources.hに定義されているFSpOpenResFile()でした。これは、引数にFSSpec構造体を渡しファイル指定する旧型APIですが、何か事情があるのか、まだDEPRECATED指定になっていません(不思議?)。問題は、このAPIを用いてもリソースフォークに保存されているリソースデータにしかアクセスできないことです。Mac OS Xから、アプリケーションバンドルのResourcesフォルダに保存されるリソースファイルなどは、データフォークにそのデータが保存されています(ややこしい)。よって、それらにアクセスするためには、FSpOpenResFile()の代わりに
    FSOpenResourceFile()を使う必要があるわけです。

    FSOpenResourceFile(
    
       cconst FSRef *    ref,             // FSRefで対象ファイルを指示
       cUniCharCount     forkNameLength,  // フォーク名の長さ
       cconst UniChar *  forkName,        // フォーク名(ユニコード文字列)
       cSInt8            permissions,     // パーミッション(アクセス権)
       cSInt16 *         refNum );        // ファイル固有のリファレンス番号
    

    先ほど取り上げたFSOpenFork()と同様に、forkNameLengthにゼロを、forkName
    にNULLを渡した時にディフォルト対象はデータフォークとなります。ディフォルトはリソースフォークではないので注意してください。こうしてオープンしたファイルをクローズするには、FSCloseFork()ではなくCloseResFile()を使いますので、こちらも注意が必要です。

    次回は、色々なファイル処理の話題を集めてみたいと思います。ディレクトリ・カタログからファイルを抽出するような処理にもチャレンジしてみます。
                                    
    つづく

    ターミナルの向こうから      第2回  海上 忍

    〜Mac OS Xのディレクトリ構成(1)〜

     前回は、コマンドと聞いただけで身構えてしまう方のために、一種の心構えについて紹介させていただきました。今回はその先に横たわる”UNIXレイヤー”の理解を進める第一歩として、Mac OS Xのディレクトリ構成について解説を行います。

    ・すべては「/」から始まる
     Mac OS Xにかぎらず、UNIX系OSではファイルシステムの基点を「/」で表現します。読み方は「ルート(ディレクトリ)」、下位のディレクトリやファイルとの区切り記号としても利用されます。
     ファイルシステム上に存在するすべてのファイル/ディレクトリは、ボリュームやフォーマット種が異なっても、ディレクトリ階層が浅くても深くても、一律に「/」から始まる文字のら列(POSIXパス、単純に「パス」とも呼ばれる)で表現できます。以下に挙げる例は、日本語環境のファインダに表示されたファイルを、パスに置き換えたものです。

    1) Macintosh HD→Developer→About Xcode Tools.pdf
    = /Developer/About Xcode Tools.pdf
    2) Macintosh HD→システム→ライブラリ(フォルダ)
    = /System/Library
    3) Macintosh HD→ユーザ→shinobu→Flower.jpg
    = /Users/shinobu/Flower.jpg
    


    ・ファインダとPOSIXパスの関係
     上の例を見ればわかるように、ファインダに表示される情報とPOSIXパスには違いがあります。ファインダ上の「Macintosh HD」はPOSIXパスでは「/」に、「システム」は「System」に、「ユーザ」は「Users」に、それぞれ置き換えられていることがわかるはずです。
     なお、このパスの置換はCore Foundationの国際化により実現されました。Mac OS X 10.2(Jaguar)以降のシステムでは、特定のディレクトリを対象に「.localized」ファイルの有無を判定し、存在すれば(システム環境設定の)第一優先言語で定義されたディレクトリ名に置換し、存在しなければそのまま(英語≒POSIXパス)表示します。ユーザ権限で作成した一般のディレクトリも多言語対応させることが可能ですが、今回のテーマから大きく逸脱するため、具体的な方法はまたの機会にさせていただきます。

    - – -
    表1:システム標準で多言語対応するディレクトリの例

    本来のディレクトリ名       日本語ローカライズ時
    /Applications            アプリケーション
    /Applications/Utilities  ユーティリティ
    /Library                 ライブラリ
    /Network                 ネットワーク
    /System                  システム
    /System/Library          ライブラリ
    /Users                   ユーザ
    /Users/Shared            共有
    - – -

    ・ファインダに表示されない”隠しアイテム”
     「/」以下にはいくつかのディレクトリが設けられ、それぞれ用途に応じた使い分けが行われます。表1に挙げたディレクトリはその代表格で、Mac OS Xをインストールした時点で作成される”システム標準”です。重要な役割が割り当てられているため、安易に改変されないよう、管理権限を持たない一般ユーザによる変更は(アクセス権の設定によって)禁じられています。
     システム標準のディレクトリは、表1以外にも多数存在します。ファインダの機能により非表示に設定されているため、通常の設定では存在に気づきませんが、ターミナルから「ls」コマンドを実行(-aオプションが必要)すれば確認できます。どうしてもファインダで表示したければ、TinkerTool
    (http://www.bresink.com/osx/TinkerTool.html)などユーティリティソフト
    の力を借りる方法があります。

    - – -
    表2:ファインダに表示されないディレクトリ

    ディレクトリ名    内容
    /automount      自動マウント機能が使用する領域
    /bin            最低限必要なコマンドが保存される領域
    /cores          コアダンプが保存される領域
    /dev            デバイスファイルが作成される領域
    /etc            システム上重要な設定ファイルが保存される領域
    /private        ローカルホストのみで使用される領域
    /sbin           管理用コマンドが保存される領域
    /usr            ユーザの共用領域
    /tmp            一時使用のファイルなどが保存される領域
    /var            頻繁な書き換えが予想されるファイル用の領域
    - – -

     表2に挙げたディレクトリは、Mac OS XのUNIXとしての側面を語る際に不可欠の存在です。次回は、これらUNIX由来のディレクトリについて説明する予定です。

    書籍紹介                エンジニアのための時間管理術

     解説担当:高橋政明

    エンジニアのための時間管理術
      Thomas A. Limoncelli 著
      株式会社クイープ 訳
      オライリー・ジャパン ISBN4-87311-307-5  定価2,415円

     原書Time Management for System Administratorsの翻訳本です。冒頭に「本書の対象読者」の項があり『本書はプログラマを対象としていません。』とキッパリ書かれています。
     私がこの行を目にしたのは購入した後でしたが(だからこそ?)気にせず〔^_^;〕読み進みました。そうSystem Administrators(以下SA)向けの内容です。

     目次を見るとどの章も、あるいはいくつかは、興味をそそる項目ではないでしょうか? SA向けの内容と言っても紹介されている問題と対策がSAに最適化されているだけであって、ここに載っている様々な問題がプログラマやそのほかの技術者に無縁かと言えばまったくそのような事はありません。日本の書名を「エンジニアのための」とした事はなかなかの名訳ではないかと思います。

     私のようにSAでない方は気楽に本書を開き、斜め読みする事をおすすめします。組織の中で働くエンジニアに有用な事柄が多い印象ですが、私のような一匹狼にも役に立ちました。新製品の開発に加えて、出荷済み製品のバグ対応、ユーザー(や上司)のサポート、ウイルスチェックソフトの更新からスパムメール対策の設定などなど一手に引き受けている方にはなおさらでしょう。

     この本はとても読み易く書かれています。具体的なアドバイスもちりばめられています。各章ごとにまとめがあり、後日見直すのにも便利です。
     たくさんやらなければならない事がある場合に優先順位を付けて対処することは重要です。この本ではさらに一日の終わりに充実感を持つ事ができ、客観的に自分の仕事量を判断するためのテクニックを示してくれます。そのような良い習慣が幸せを呼ぶことになると自然に理解できます。

     SAのために最適化された本ですが、プログラマがざっと読むと時間管理のエッセンスを感じとる事ができ、じっくり読むとSAの苦労が理解できる本と言えるかも知れません。(エピソードが技術系の話であることを気にしなければどんな仕事の方にも参考になると思います)

     過労や心の病は個人にとっても企業にとっても重大な、そして厄介なことに差し迫った問題です。青い背表紙の『エンジニアのための時間管理術』が目に入ったら手を伸ばしてみてください。ちょっとした余裕を手に入れるきっかけになるかも知れません。

    ▼出版社のweb(詳しい目次とpdfのサンプルページが載っています)
    http://www.oreilly.co.jp/books/4873113075/index.html

    ◇モサ伝編集部から◇
    【ソースの訂正】
    プログラミング指南の高橋真人です。

    先週配信しましたMOSA Developer News第251号に掲載の「高橋真人のプログラミング指南」第112回に誤記がありました。詳しくは来週の第113回でフォローしますが、取り急ぎさしかえ内容を以下に掲載いたしますので、ご参照ください。

    【誤】
    OSStatus
    MyApplication::HandleNew()
    {
        OSStatus status = eventNotHandledErr;
    
        WindowRef theWindow = ::GetFrontWindowOfClass(kDocumentWindowClass, true);
        PPx::Window* win = PPx::Window::GetWindowObject(theWindow);
        if (win) {
            win->Close();
        }
    
        ::CFShow(CFSTR("HandleClose called."));
    
        return noErr;
    }
    


    【正】
    OSStatus
    MyApplication::HandleNew()
    {
       PPx::Window* theWindow = PPx::NibDecoder::CreateWindowFromNib(
           CFSTR("main"), CFSTR("MainWindow"));
       if (theWindow) {
           ::RepositionWindow(theWindow->GetSysWindow(), nil, kWindowCascadeOnMainScreen);
           theWindow->Show();
       }
    
       return noErr;
    }
    


    【誤】
    OSStatus
    MyApplication::HandleClose()
    {
        OSStatus status = eventNotHandledErr;
    
        WindowRef theWindow = ::GetFrontWindowOfClass(kDocumentWindowClass, true);
        if (theWindow) {
            PPx::EventUtils::SetMenuCommandStatus(kHICommandClose, true);
        }
    
        return noErr;
    }
    


    【正】
    OSStatus
    MyApplication::HandleClose()
    {
       OSStatus status = eventNotHandledErr;
    
       WindowRef theWindow = ::GetFrontWindowOfClass(kDocumentWindowClass, true);
       PPx::Window* win = PPx::Window::GetWindowObject(theWindow);
       if (win) {
           win->Close();
       }
    
       ::CFShow(CFSTR("HandleClose called."));
    
       return noErr;
    }
    


    ご迷惑をおかけし、申し訳ありません。
    高橋真人

    ◇MOSAからのお知らせと編集後記は割愛します◇

     

    MOSA Developer News   略称[MOSADeN=モサ伝]
            配信停止 mailto:mosaden-ml@mosa.gr.jp
     記事内容に関するご意見 mailto:mosaden-toukou@mosa.gr.jp
          記事投稿受付 http://www.mosa.gr.jp/?page_id=850
    Apple、Mac OSは米国アップル社の登録商標です。またそのほかの各製品名等はそれぞれ各社の商標ならびに登録商標です。このメールの再配信、および掲載された記事の無断転載を禁じます。
    特定非営利活動法人MOSA  http://www.mosa.gr.jp/
    Copyright (C)2007 MOSA. All rights reserved.

  • MOSA Developer News[MOSADeN=モサ伝]第251号

    2007-05-15

    目次

    • 「りんご味Ruby」       第3回  藤本 尚邦
    • 藤本裕之のプログラミング夜話   #114
    • 高橋真人の「プログラミング指南」  第112回
    • 書書籍紹介    Subversion実践入門 第2版

    りんご味Ruby   第3回  藤本 尚邦

    今回は、RSSリーダから離れて、Rubyインタープリタとのインタラクティブな対話、対話型プログラミング環境について、あれこれ書いてみたいと思います。「インタラクティブな対話」などと言うとちょっと堅いかもしれませんが、要するに

     RubyのことはRubyに聞け

    ということです。Rubyは尋ねれば気軽に応えてくれます。だったら、Ruby本人に尋ねながらRubyプログラムをあれこれ試行錯誤してみようじゃないか、そんな感じです。CやObjective-Cでのプログラミングにはない、Rubyのようなインタープリタを持つプログラミング言語処理系によるプログラミングならではの醍醐味かもしれません。

    ■ RubyのことはRubyに聞けその1 – 対話環境を自作する

    Rubyでは、evalというメソッド(およびその変種)を使って、「Rubyプログラムを解析・実行」するRubyプログラムを書くことができます。すなわち、対話的プログラミング環境を簡単にプログラムすることが可能です。対話の仕組みを知る・雰囲気を味わうためにも、まずは、実用的かどうかはともかくとして、単純な対話環境を自作してみることにしましょう。

    以下の5行のプログラムは、ごく簡単な対話環境プログラムです。

     

    $stdout.sync = true
     read_expr = proc { print "ruby> " ; gets }
     while expr = read_expr.call do
       puts eval(expr).inspect rescue puts "ERR -- #{$!}"
     end
    


    このRubyプログラムは、おおよそ以下のことを行っています。

     ・標準入力の終端(コントロール+D)に達しないかぎり、以下を繰り返す:
       ・プロンプトを表示して
       ・標準入力から文字列を1行読み込み
       ・その文字列を(正しいRubyプログラムとみなし)評価して
       ・結果を表示

    これをrepl.rbという名前でファイルに保存しておき、ターミナル上でrubyコマンドを使って実行してみてください。

     

    $ ruby repl.rb
     ruby>

    プロンプト”ruby>”が表示されるので、Rubyプログラムを入力してリターンキーを押すと、実行して結果を表示してから、再び入力待ちとなります。終了する場合は、^D(コントロールキー+D)を入力します。以下は、実行例です。

     

    $ ruby repl.rb
     ruby> 1 + 2 + 3
     6
     ruby> puts "hello,world"
     hello,world
     nil
     ruby> Time.now
     Thu May 10 22:17:00 JST 2007
     ruby> def factorial(n) n==0 ? 1 : n*factorial(n-1) end
     nil
     ruby> factorial(10)
     3628800
     ruby> (2..50).inject([]) {|a,i| a.find{|j| i%j==0 } ? a : a<
    


    というような感じで、何かしらRubyプログラムを入力すると結果が表示されます。いかかでしょうか? 前回提示したRSSリーダプログラムを1行ずつ入力してみるのもよいでしょう。

    このrepl.rbは、正しいRubyプログラムを1行に収めなければならない・エラー処理がおざなりであるなど、REPLとしての実用度には欠けるのですが、「『Ruby プログラムを解析・実行』するRubyプログラム」がどんなことをしているのか、その匂いを感じとってもらえればと思います。

    (余談)ちなみに、上の実行例で、最後に実行したプログラムは、50までの素数の配列を生成するプログラムです。たまにはRubyらしさ濃厚なプログラム片を見せたいなと思って差し挟みました。(余談終)

    ■ REPLとは?

    さて、上のプログラムにはrepl.rbというファイル名を付けましたが、REPLという用語を聞いたことはあるでしょうか?

     ・read  – ユーザが入力したプログラムを読み込んで
     ・eval  – それを実行(評価=evaluate)し
     ・print — 結果を表示する

    といったことを対話的に繰り返し行うプログラミング環境のことを、Read-eval-print loop、略してREPLといいます。Rubyを含めて、インタープリタを備えたプログラミング言語処理系の多くが、何らかの形でREPLを備えています。

    Rubyと同様にMac OS Xに付属しているインタープリタ型のプログラミング言語のPythonの場合には、pythonコマンドを引数なしで実行すると、REPLが起動するようになっています。また、大昔のパソコンでは、スイッチを入れるとBASICインタープリタの対話型プログラミング環境が初期画面になっているというのが一般的でした。

    さらに、筆者も愛用しているEmacsは、見かけ上テキストエディタに分類されがちですが、その実体は、テキストエディタを備えたインタープリタ型Lisp処理系によるアプリケーション実行環境と言えます。Emacsを起動して最初に表示されるscratchバッファは、Lispプログラムを落書きするためのREPLそのものです。

    Rubyを含めて、インタープリタを持ったプログラミング言語処理系の存在意義は、REPL・対話型プログラミング環境による試行錯誤を通して、スケッチ・落書きするようにプログラミングできるところにあるかもしれません。

    ■ まとめ

    今回は、もともと、Rubyに付属の標準的なREPLであるirbコマンドと拙作のCocoaRepl.appなどについて説明するつもりだったのですが、対話型プログラミング環境への思い入れが強過ぎて、ついつい脱線してしまいました。irbまでたどり着くことができず、あまり実践的な話もできず。次回は「RubyのことはRubyに聞け」の続きとして、irbとCocoaRepl.appについて説明して、再びRSSリーダの説明に戻りたいところなのですが、はたしてどうなることやら…

    筆者は、10年くらい前、Java(JDK 1.0あたり)で遊びつつ、主にCやC++を使ってプログラミングしており、RubyやLispのようなタイプの言語のことはあまりよく知りませんでした。Rubyのソースに昔から付いてくるサンプルとしてeval.rbというプログラムがあるのですが、それを見てRubyではREPLが簡単に実装できることを知り、「Rubyすげー!」と感動した覚えがあります。今回は、そんなところが少しでも伝わればいいなと思うのですが、いかがだったでしょうか?

    藤本裕之のプログラミング夜話 #114

     連休も終わり,来月のWWDCに向けて仕事にスパートをかけているヒトもおられることと思う。かく言うワタシはと言えば,「WWDC前のひと稼ぎ」と当てにしていた仕事が流れてしまい,現在この原稿を書くくらいしか仕事がない。暇なら暇でいい季節なんだからそこらに遊びに出ればいいではないかという意見もあろうがいろいろ事情もあってそうも行かず……いや,そういう話はまた書き出すと止らないからやめておこう。さて今回からまた新しい話。タカハシ編集長からのリクエストで「Nibファイルの分割」ちうのを説明したい。

     Nibファイルとは何かということについては説明の必要はないだろう。Xcodeを起動してファイルメニューから「新規プロジェクト」を選び,アプリケーションその他のプロジェクトを作成すればたいてい自動的に作られる(コマンドライン・ツールなどでは使えないので作られない)。前にも書いたことがあると思うが……いつだったっけと思って今調べてみたら2005年の始め頃だ,NSApplicationを解説したとき。デフォルトではこの自動的に作られるNibファイル(Cocoa アプリでは「MainMenu.nib」という名前になる)が「メインNibファイル」となりアプリケーション起動時にその中に定義されたオブジェクトが生成される。ここまではいいよね?

     Nibファイルの分割というのはつまり,必ずしも起動時に生成されなくてもいいオブジェクトを,別のNibファイルに定義しておいて,必要に応じて読み込もうということである。メインのウインドウが一個に初期設定用のダイアログが一個というようなアプリケーションならその初期設定用のダイアログだけを別にしてもたいした効果はないが,巨大なアプリケーションになればなるほどNibファイルの分割による起動時間のスピードアップは効いてくる。

     もちろんその分の時間は分割されたそれぞれのNibファイルをロードする際に分散されるだけで物理的に短縮されるわけではない。でもね,アプリケーションを起動してから終了するまでの1回のセッション中に,そのアプリケーションが用意している全てのウィンドウを表示する,なんてことは滅多にない。

     例えば今私はこの原稿を JeditXを使って書いているのだが,起動してから今までの間,さっき,前にNibファイルについて説明したのはいつかを調べるのに検索ウィンドウは表示したけど,一度もアバウトボックスを表示していない。ということは,アバウトボックスだけでも別のNibファイルに入れておけば,それをロードするための時間分だけ……まぁわずかだけども起動は速くなるわけだ。ってJeditXは既にそうなってるみたいだけどね。

     具体的な話に入る。おそらくはこれが一番簡単な例だと思うので,上のようなアバウトボックスを別のNibファイルに格納してみよう。分割されたNibファイルに格納されるこのアバウトボックスは,アプリケーションメニューの「About …」というアイテムからのメッセージを受け取って表示されることになるわけだ。

     まず最初に,Interface BuilderでMainMenu.nibを開き,File’s Ownerを選んで「Class」タブをクリック,プリンシパルクラスである NSApplicationのサブクラスを派生させてしまおう。仮にこれをMyApplicationとするが,このクラスで NSApplicationのorderFrontStandardAboutPanel: メソッドをオーバーライドし,これから作る別のNibファイルからアバウトボックスをロードするコードを書けばいい。サブクラスを選択状態にしてClassメニューから「Create Files For MyApplication」を選んでソースファイルを作る。

     おっとここで一つ注意。NSApplication の説明の時に書いたと思うが,こんな風にプリンシパルクラスをサブクラスした時には,プロジェクトのInfo.plist ファイルの NSPrincipalClass キーに対応する文字列を派生させたサブクラス名(この例でMyApplication)に変更するのを忘れないこと。これを忘れるといくらコードを書いてもそれらは金輪際実行されない。いや,笑ってるけどこれ意外と忘れがちで,ぼやぼやしてると半日くらい変だな変だなと首を傾げっぱなしになるので覚えておこう。……では次回は今作ったMyApplication.h とMyApplication.m の中身のコーディングから。お元気で。
                                (2007_05_10)

    高橋真人のプログラミング指南  第112回

    プログラマのためのオブジェクト指向再入門(20)

    〜XcodeによるPowerPlant X入門(4)〜

     こんにちは。高橋真人です。
     お待たせしました。早速続きを始めます。
     新しいプロジェクトは準備してあったので、あとはコードを書いていくだけです。とりあえず今回はメニューもウインドウもあるまともなアプリケーションが出来ますが、さほどコード量は多くありません。
     このくらいではまだ「真価を発揮」というほどまでには至りませんが、フレームワークの恩恵はところどころで味わえます。

     では、手順を説明します。まずは、Applicationクラスのサブクラシング、すなわち、Applicationクラスからサブクラスを作るわけです。

     プロジェクトウインドウの「Sourcesグループ」(連載110回参照)を選択して、メニュー「ファイル→新規ファイル…」を選択します。
     CarbonのC++ファイルを選んで「次へ」ボタンをクリック。MyApplication.cpとファイル名を付けます。「ヘッダも作成する」のチェックは付けたままです。
     ちなみに、C++のソースファイルの拡張子は.cppにしているケースも多いですが、PowerPlantは伝統的に(?).cpなのです。Panther(Mac OS X10.3.x)だと.cpをXcodeがC++ファイルと見なしてくれないこともありますが、Tigerでは問題ないようです。
     ちなみに、C++のソースの拡張子としては他に.cc、.cxx、.C(大文字)などもありますが、余り一般的ではないので、.cppか.cpのどちらかにしておいてください。(.cppがお好みであれば、それでも構いません)
     完了ボタンをクリックすると、プロジェクトに加わった形でMyApplication.hとMyApplication.cpの二つのファイルができます。
     前回から引き継いだmain.cpを含めたそれぞれのファイルを、以下のように変更します。

    ====================== main.cp =========================
    
    #include "MyApplication.h"
    
    #include 
    #include 
    
    
    void Setup();
    
    int main()
    {
        Setup();
    
        MyApplication theApp;
        theApp.Run();
    
        return 0;
    }
    
    
    void
    Setup()
    {
        PPx::SysNib menuNib(CFSTR("main"));
        menuNib.SetMenuBar(CFSTR("MenuBar"));
    
        PPx::RegisterCommonNibDecoders();
    }
    
    
    ====================== MyApplication.h =========================
    
    #include 
    
    #include 
    #include 
    
    class MyApplication : public PPx::Application,
                          public PPx::CommandProcessDoer
    {
    public:
                            MyApplication();
    
    
    protected:
        virtual OSStatus    DoCommandProcess(
                                PPx::SysCarbonEvent&    ioEvent,
                                HICommand               inCommand,
                                UInt32                  inKeyModifiers,
                                UInt32                  inMenuContext);
    
    private:
        OSStatus            HandleNew();
        OSStatus            HandleClose();
    
        virtual CFStringRef ClassName() const;
    
    };
    
    
    ====================== MyApplication.cp =========================
    
    #include "MyApplication.h"
    
    #include 
    #include 
    #include 
    
    MyApplication::MyApplication()
    {
        EventTargetRef targetRef = GetSysEventTarget();
    
        PPx::CommandProcessDoer::Install(targetRef);
    }
    
    
    OSStatus
    MyApplication::DoCommandProcess(
        PPx::SysCarbonEvent&        ioEvent,
        HICommand                   inCommand,
        UInt32                      inKeyModifiers,
        UInt32                      inMenuContext)
    {
        OSStatus status = eventNotHandledErr;
    
        switch (inCommand.commandID) {
            case kHICommandNew:
                status = HandleNew();
                break;
            case kHICommandClose:
                status = HandleClose();
                break;
        }
    
        return status;
    }
    
    
    OSStatus
    MyApplication::HandleNew()
    {
        OSStatus status = eventNotHandledErr;
    
        WindowRef theWindow = ::GetFrontWindowOfClass(kDocumentWindowClass, true);
        PPx::Window* win = PPx::Window::GetWindowObject(theWindow);
        if (win) {
            win->Close();
        }
    
        ::CFShow(CFSTR("HandleClose called."));
    
        return noErr;
    }
    
    
    OSStatus
    MyApplication::HandleClose()
    {
        OSStatus status = eventNotHandledErr;
    
        WindowRef theWindow = ::GetFrontWindowOfClass(kDocumentWindowClass, true);
        if (theWindow) {
            PPx::EventUtils::SetMenuCommandStatus(kHICommandClose, true);
        }
    
        return noErr;
    }
    
    
    CFStringRef
    MyApplication::ClassName() const
    {
        return CFSTR("MyApplication");
    }
    
    


     では早速ビルドしてみましょう。
     アプリケーションを実行させてみると、メニューもきちんとあり、メニューから新規ウインドウを作れるアプリケーションが動くはずです。
     今回のコードのポイントは、もともとプロジェクトのステーショナリに加わっていたNibファイルをPPxがちゃんと読み込んで、それを使用していることです。
     オリジナルのPowerPlantでは、GUIの構築のためにPPobリソースという独自のリソースを使用していました。PPobリソースの編集のためにConstructorというツールが付属していて、これがPowerPlantの使いやすさの一翼を担っていたのです。
     PowerPlant Xの最初のリリース時には、GUI構築のために新規にxml形式のデータ形式を使うことになったのですが、Constructorのようなビジュアル編集ツールがなかったことから、決してGUIの編集はラクではありませんでした。
     初期のPPxが提供していたのは、PPobリソースとInterface BuilderのNibファイルをこの形式のデータに変更するツールでした。しかし、PPxを作っていた人たちは早い段階でこの方式に見切りを付け、Interface Builderが生成するNibファイルを直接読み込んで利用する方式に変更しました。この仕組みが採用されたPowerPlant Xのversion1.1が結果的に商品としては最後のリリースになってしまいましたが、私はこの考え方は正しかったと思っています。
     PowerPlant時代、Mac OSが新しいウィジェット(コントロール)を作り出す度に、PowerPlantユーザーは、PPがそれに対応するのを待っていなければならなかったのです。
     しかし、Nibファイルは、xml形式のデータであり、CarbonフレームワークにはInterface Builder Servicesというというフレームワークが備わっていますから、OSのバージョンが上がって、新たなコントロール(HIView)が登場しても、ちょっとソースを書き足せば対応が可能なのです。
     いずれ、この方法についても解説したいと思います。

    書籍紹介          Subversion実践入門 第2版

     解説担当:高橋政明

    Subversion実践入門 第2版
      Mike Mason著
      でびあんぐる監訳
      株式会社オーム社 ISBN978-4-274-06680-1  本体2,600円(税別)

     ソースコード管理ツールであるSubversionの解説書です。初版もおすすめでしたが早くも第2版が出ました。
     この本の初版は Pragmatic Version Control: Using Subversion を翻訳したものです。第2版は原著が2nd Editionになりそれに対応したようです。原著のwebには2nd EditionではSubversionのバージョン1.3に対応したと明記されていますが、日本語版ではその点は明示されてはいないようです。

     ソースコード管理(バージョン管理)については食わず嫌いの方もいらっしゃるかもしれません。本書はそんな方にもおすすめです。単にツールとしてのSubversionの使い方に留まらず、「序章」と「バージョン管理とは何か」の二つの章を使い具体例を示し文字通りバージョン管理とは何かを解説しています。

     私が特にありがたかったのは「3.7競合の発生」「3.8競合の解決」でした。実際に試すことで、深刻な状況に思えた競合と競合の対処の意味を理解する事ができました。競合以外も具体的に書かれているので試しながら理解を深めて行く事ができます。
     CVSに慣れたユーザ向けのヒントも載っていてCVSからの乗り換えにも最適です。
     「付録E.2レシピ」はやりたい事を実現するためのサブコマンドがすぐにわかる構成で便利です。

     Mac OS Xにも数カ所で言及されていますが残念ながら索引には載っていません。もっともWindows特有の解説部分を除きすべてはMac OS Xでもそのまま使えますので支障はありません。

    ●第2版の変更点
     出版社のwebに詳細な目次がありますが、第2版では第7章「ファイルロックとバイナリファイル」と「付録D高度なトピック」が追加されました。ありがたいことに価格は変わっていません。
     図版も変わっていてUnixのシェルプロンプトにはAquaのウインドウ(明らかにターミナル.app)が載っています。

     日本語版独自に監訳者による注釈やコラムも追加になっていました。またWindows Vistaに言及している部分もありました。
     初版とは表紙の写真が違うので初版をお持ちの方はすぐに気がつく事と思います。既に初版をお持ちの方はあえて第2版を購入される必要は少ないとは思います。なお原著のwebで追加になった第7章がまるまるpdfで公開されています。(太っ腹!)

    ▼出版社のweb
    http://ssl.ohmsha.co.jp/cgi-bin/menu.cgi?ISBN=978-4-274-06680-1

    ▼原著 Pragmatic Version Control using Subversion, 2nd Ed. のweb
    http://www.pragmaticprogrammer.com/titles/svn/

    ▼サンプルページ(原著の第7章)2.2MB
    http://www.pragmaticprogrammer.com/titles/svn2/Locking.pdf

    ◇MOSAからのお知らせと編集後記は割愛します◇

     

     MOSA Developer News   略称[MOSADeN=モサ伝]
            配信停止 mailto:mosaden-ml@mosa.gr.jp
     記事内容に関するご意見 mailto:mosaden-toukou@mosa.gr.jp
          記事投稿受付 http://www.mosa.gr.jp/?page_id=850
    Apple、Mac OSは米国アップル社の登録商標です。またそのほかの各製品名等
    はそれぞれ各社の商標ならびに登録商標です。
    このメールの再配信、および掲載された記事の無断転載を禁じます。
    特定非営利活動法人MOSA  http://www.mosa.gr.jp/
    Copyright (C)2007 MOSA. All rights reserved.

  • MOSA Developer News[MOSADeN=モサ伝]第250号

    2007-05-08 

    目次

    • 「「Wonderful Server Life」    第46回    田畑 英和
    • 小池邦人の「Carbon API 徒然草」
    • ターミナルの向こうから      第1回  海上 忍 ★新連載★
    • 書籍紹介             GDBデバッギング入門

    「Wonderful Server Life」  第46回  田畑 英和

      〜アカウント編〜

     Leopardのリリースが10月に延期になりました。ホッとしている方もいればガッカリしている方もいらっしゃるかと思います。サーバの場合は、頻繁にOSをアップデートできなかったりもしますので、準備する期間が増えたのではないかと思います。これまでのメジャーバージョンアップでは、アップデート後は旧バージョンのOSは販売されなくなりましたので、10月以降に構築を開始するシステムでは、Leopardベースでのシステム構築になるかと思われます。
     また、4月中旬に開催されたNABでは「Final Cut Studio 2」が発表されました。これと同時にサーバアプリケーションの「Final Cut Server」も発表されています。クライアントアプリケーションだけではなく、サーバアプリケーションも充実していく傾向にあります。

    ・Final Cut Server
    http://www.apple.com/jp/finalcutserver/

    ◇コンピュータアカウント
     これまで、ユーザおよびグループのアカウント管理について説明してきました。アカウント管理の締めくくりとしてコンピュータアカウントについて説明していきます。
     ユーザやグループは、ファイルシステムのアクセス権を管理するときに使用しますので比較的なじみのあるタイプのアカウントかと思いますが、Mac OS XServerでは、コンピュータもアカウントとして管理できます。
     コンピュータアカウントを使ってなにができるかですが、次のようなことが実現できます。

    ・コンピュータ単位での環境設定の管理
    ・コンピュータごとのアクセス制御
    ・ネットワーク表示のカスタマイズ

     サーバ上で一元管理する環境設定には、コンピュータアカウントでのみ有効な設定項目も用意されています。
     コンピュータアカウントですが、1台1台のコンピュータを個別に管理するのではなく、1台以上のコンピュータをリストとして管理するようになっています。例えば機種別にコンピュータリストを作成したり、設置場所ごとにコンピュータリストを作成するなどの応用が可能になります。

    ◇標準のコンピュータアカウント
     システムをセットアップした時点で、自動的に次の3つのコンピュータリストが作成されています。

    ・ゲストコンピュータ
    ・Windowsコンピュータ
    ・すべてのコンピュータ

     まず「ゲストコンピュータ」ですが、どのリストにも含まれないコンピュータが存在した場合、自動的にゲストコンピュータとして扱われます。「ゲストコンピュータ」に手動でコンピュータを登録することはできません。
     なんらかのコンピュータリストにコンピュータを追加すれば、自動的に「すべてのコンピュータ」にも登録されます。つまり、「すべてのコンピュータ」のリストを参照すれば、登録済みコンピュータの一覧を確認することができます。「すべてのコンピュータ」に直接コンピュータを登録することもできます。
     また、これらのコンピュータリストを削除することはできません。

    ・ゲストコンピュータ
    http://homepage.mac.com/htabata/MXS10.3/img/WGM_Computer/WGM_Computer_01.png

    ◇コンピュータリストの設定方法
     コンピュータリストの作成は、グループの作成手順と似ています。「ワークグループマネージャ」を起動し、ツールバーから「アカウント」をクリックして、画面に左側にコンピュータリストの一覧を表示します。
     次にツールバーから「新規コンピュータリスト」をクリックし、名前を付けて保存します。
     これでコンピュータリストが作成できましたので、あとは任意のコンピュータをリストのメンバーとして登録していきます。コンピュータの登録方法ですが、メンバーリストの下にある「+」ボタンをクリックし、ここでコンピュータの「名前」と「Ethernet ID」(MACアドレス)を入力します。

    ・コンピュータの登録(その1)
    http://homepage.mac.com/htabata/MXS10.3/img/WGM_Computer/WGM_Computer_04.png

     あるいは、「…」ボタンをクリックするとネットワーク上のコンピュータをブラウズすることができますので、ここから任意のコンピュータを選択して自動的に登録することもできます。コンピュータをブラウズするには、対象となるコンピュータがネットワークに接続され、電源がOnになっている必要があります。
     手動でコンピュータを登録する場合、Ethernet IDを手入力する必要がありますが、打ち間違いを防ぐためにも自動的に登録するのがよいでしょう。

    ・コンピュータの登録(その2)
    http://homepage.mac.com/htabata/MXS10.3/img/WGM_Computer/WGM_Computer_05.png

     コンピュータリストは、1つのリストに最大2,000台のコンピュータを登録することができます。
     グループの場合は1つのユーザを複数のグループに登録することができましたが、コンピュータリストの場合は1台のコンピュータを複数のコンピュータリストに所属させることはできません。
     それでは、次回はコンピュータリストの応用について解説します。
    つづく
                                   

    小池邦人のCarbon API 徒然草(2007/05/04)

    〜 Carbonモダンアプリケーションへの道(その15) 〜

    今回からは、FSRefで参照したファイルやフォルダ(ディレクトリ)への基本的なアクセス(作成、書き込み、読み込み、削除など)について調べて行きます。まずはファイルを作成することから始めてみましょう。

    Carbon Frameworkでファイルを作成する場合には、FSCreateFileUnicode()APIを利用します。このAPIを含め、ファイルアクセスに関係するAPIは、すべてヘッダーファイルのFiles.hに定義されています。

    OSErr  FSCreateFileUnicode(
    
           const FSRef *parentRef,           // 親ディレクトリのFSRef
           UniCharCount nameLength,          // ファイル名の長さ(2バイト単位)
           const UniChar *name,              // ユニコードファイル名
           FSCatalogInfoBitmap whichInfo,    // 設定したいファイル情報を指示する
           const FSCatalogInfo *catalogInfo, // FSCatalogInfo構造体に情報を設定
           FSRef *newRef,                    // 作成されたファイルのFSRef
           FSSpec *newSpec )                 // 作成されたファイルのFSSpec

    最初のparentRefでファイルを作成する親ディレクトリ(フォルダ)の場所を指示し、次のnameLengthとnameでファイル名(ユニコード)を指示します。whichInfoは、作成したファイルに対して何かしらの情報を設定したい場合のみ指定します。指示は、unsigned long値の各ビットを立てることで行います。その場合には、情報格納場所としてのcatalogInfoにFSCatalogInfo構造体の先頭アドレスをセットしておきます。もし何も情報を指定しなければ、whichInfoにはkFSCatInfoNoneをセットし、catalogInfoの方はNULLでかまいません。最後のnewRefとnewSpecには、作成されたファイルのFSRefとFSSpec構造体が代入されますが、こちらも必要がなければ、それぞれNULLを代入しておきます。

    FSCreateFileUnicode()は、ファイルを作成するだけなのにやけに引数が多いですね。そこで、親ディレクトリ(FSRef)とファイル名(CFStringRef)を与えるだけの簡単なファイル作成ルーチンを別途用意してみます。ルーチン名はcreateFile()です。正しくファイルが作成できた場合には、そのファイルのFSRefを返してきます。

    OSErr createFile( FSRef *pfsref,CFStringRef cfstr,FSRef *fsref )
    {
    
       UniChar         buf[256];    // ユニコードなので一文字は2バイト
       FSRef           tfsref;
       OSErr           err=1;
       UniCharCount    len;
    
       if( len=(UniCharCount)CFStringGetLength( cfstr ) ) // ファイル名の長さを得る
       {
           CFStringGetCharacters( cfstr,CFRangeMake( 0,len ),buf ); // ユニコードに
           if( ! FSMakeFSRefUnicode( pfsref,len,buf,kTextEncodingUnicodeDefault,
                                                                     &tfsref ) )
               FSDeleteObject( &tfsref ); // すでに存在していたら削除
           err=FSCreateFileUnicode( pfsref,len,buf,kFSCatInfoNone,NULL,fsref,NULL);
       }                                  // ファイルを作成
       return( err );
    }
    


    CFStringRefで定義されたファイル名は、CFStringGetCharacters()を使いユニコード文字列へと変換されています。先んじてFSMakeFSRefUnicode()を呼び出しているのは、同じ名称のファイルが存在しているかどうかを確認するためです。もし同じ名称のファイル(もしくはディレクトリ)が存在していると、FSMakeFSRefUnicode()がnoErr(ゼロ)を返しますので、そのファイルをFSDeleteObject()で削除してしまいます。この削除処理に関しては、用途によっては実行せずエラーを返すだけの方が良いケースがあるとおもいますので、利用する場合には注意してください。ちなみに、削除せずFSCreateFileUnicode()を実行するとエラーが返ります。

    同様な引数を与えることで、ファイルではなくディレクトリ(フォルダ)を作成する場合には、以下のFSCreateDirectoryUnicode()を利用します。FSCreateFileUnicode()より引数がひとつ多いのですが、最後の引数には作成されたディレクトリのディレクトリID番号(unsigned long値)が返ります(不必要ならNULL指定)。こちらの場合には、すでに同名ディレクトリが存在していれば削除する必要はないので、エラーを返すだけの処理となっています。

    OSErr createFolderFile( FSRef *pfsref,CFStringRef cfstr,FSRef *fsref )
    {
    
       UniChar         buf[256];    // ユニコードなので一文字は2バイト
       OSErr           err=1;
       UniCharCount    len;
    
       if( len=(UniCharCount)CFStringGetLength( cfstr ) )// ファイル名の長さを得る
       {
           CFStringGetCharacters( cfstr,CFRangeMake( 0,len ),buf ); // ユニコードに
           err=FSCreateDirectoryUnicode( pfsref,len,buf,kFSCatInfoNone,NULL,fsref,
                                                                      NULL,NULL );
       }                                // ディレクトリを作成
       return( err );
    }
    


    後から作成したファイルの各種情報を得たい場合にはどうしたら良いのでしょうか? 例えば、先ほど用いたFSMakeFSRefUnicode()を単独で使うことで、指定ファイルのFSRefを得るルーチンなどは簡単に作成することができます。

    OSErr getFSRefFile( FSRef *pfsref,CFStringRef cfstr,FSRef *fsref )
    {
    
       UniChar         buf[256];
       OSErr           err=1;
       UniCharCount    len;
    
       if( len=(UniCharCount)CFStringGetLength( cfstr ) ) // ファイル名の長さを得る
       {
          CFStringGetCharacters( cfstr,CFRangeMake( 0,len ),buf ); // ユニコードに
          err=FSMakeFSRefUnicode(pfsref,len,buf,kTextEncodingUnicodeDefault,fsref);
       }                          // そのファイルがあればFSRefを返す
       return( err );
    }
    


    もっと詳しい情報を得たい場合には、次のFSGetCatalogInfo() APIを利用します。

    OSErr  FSGetCatalogInfo(
    
           const FSRef *ref,                 // 対象ファイルのFSRef
           FSCatalogInfoBitmap whichInfo,    // 欲しい情報を指示する
           FSCatalogInfo *catalogInfo,       // FSCatalogInfo構造体に情報が返る
           HFSUniStr255 *outName,            // 対象ファイルのファイル名
           FSSpec *fsSpec,                   // 対象ファイルのFSSpec
           FSRef *parentRef);                // 対象ファイルの親ディレクトリのFSRef
    


    例えば、引数で対象ファイルのFSRefを渡して、そのファイルの親ディレクトリ(ファイルが入っているフォルダ)のFSRefを得たい場合には、以下のようなルーチンを用意しておくと便利です。

    OSErr getParentFSRefFile( FSRef *fsref,FSRef *pfsref )
    {
       short    err;
    
       err=FSGetCatalogInfo( fsref,kFSCatInfoNone,NULL,NULL,NULL,pfsref );
    }
    


    FSCatalogInfoBitmapを設定することで、FSCatalogInfo構造体に様々なファイル情報が書き込まれてきます。それらは、ファイル作成日時や編集日時、ファイル容量、アクセス権(パーミッション)情報からFinder情報まで多種多様です。どんな情報を得られるのかは(作成時に設定する場合も同様)、Files.hに詳しく記載されていますので参照してみてください。上記ルーチンと同様に、目的に応じた情報を得るためだけのルーチンを用意しておくと便利かもしれません。

    今まで紹介してきたルーチンは、ファイル名をCFStringRef定義で渡していますが、代わりにHFSUniStr255構造体として渡したい場合もあります。Files.hには、CFStringRefをHFSUniStr255構造体に変換(もしくはその逆を)するためのAPIが用意されていますので、それを使えばファイル名を好みの文字列フォーマットで指示できます。HFSUniStr255構造体からCFStringRefを得るにはFSCreateStringFromHFSUniStr()を、また逆の処理にはFSGetHFSUniStrFromString()を使います。

    CFStringRef FSCreateStringFromHFSUniStr( CFAllocatorRef alloc,const
                                                            HFSUniStr255 *uniStr);
    
    OSStatus FSGetHFSUniStrFromString( CFStringRef theString,HFSUniStr255 *uniStr);
    


    一番最初のcreateFolderFile()をFSGetHFSUniStrFromString()を使うように書き直すと、以下のようになります。

    OSErr createFile( FSRef *pfsref,CFStringRef cfstr,FSRef *fsref )
    {
       FSRef           tfsref;
       OSErr           err=1;
       HFSUniStr255    ustr;
    
       if( ! FSGetHFSUniStrFromString( cfstr,&ustr ) )
       {
          if( ustr.length ) // 文字列の長さはHFSUniStr255構造体のメンバ
          {
             CFStringGetCharacters( cfstr,CFRangeMake( 0,len ),buf );
             if( ! FSMakeFSRefUnicode( pfsref,ustr.length,(UniChar *)ustr.unicode,
                                          kTextEncodingUnicodeDefault,&tfsref ) )
                 FSDeleteObject( &tfsref );
             err=FSCreateFileUnicode( pfsref,ustr.length,(UniChar *)ustr.unicode,
                                                 kFSCatInfoNone,NULL,fsref,NULL );
          }
       }
       return( err );
    }
    


    次回は、ファイルのオープン(Open)とクローズ(Close)ファイルへのデータ書き込み(Write)とファイルからの読み込み(Read)、ファイルの削除(Delete)やファイル名の変更(Rename)など、一般的なファイル処理を調べてみる予定です。
    つづく                                

    ターミナルの向こうから      第1回  海上 忍

     〜私のMacの使い方と3つの処方箋〜

     皆さん、はじめまして。湘南セミナーにご出席の皆さんはお久しぶりです。このたび御指名により、モサ伝の連載という大役を仰せつかり、恐縮している次第です。私ごときの話で皆さんにご満足いただけるか自信はありませんが、ターミナル/UNIXコマンド関連トピックのなかでも開発に役立つものを選び、できるだけ噛み砕いた解説をさせていただこうと思います。

    ・私の”Macの使い方”
     初回ということで、私の”Macの使い方”から話を始めさせていただきます。旧Mac OSの時代からMacをメインにお使いの方からすると、奇異な印象を受けるかもしれませんが、NeXTSTEPなどのUNIX系OSに慣れ親しんだ人間にはそれほど珍しいことではないと思います。
     まず、デスクトップには「ターミナル」、もしくはEmacs(Carbon Emacs)が必ず1つ起動しています。ファイル操作にはそのいずれかを利用することが多く、ファインダはそれほど使いません。ファインダでブラウジングするより、シェルの入力補完機能を利用してパスを指定したほうが迅速に作業できますし、Macには「open」という便利なコマンドがあります。ファイルの削除や圧縮にはEmacsの「Dired」機能を利用するので、原稿の画面から視線を移さずに目的を果たせます。
     きちんとレイアウトした文書をつくるときには、もっぱらTeX/LaTeXを使います。EmacsでLaTeXの文書を書き、LaTeX(日本語文書ですから正確にはpLaTeX2e)でタイプセットを実行、dvipdfmxを利用してdviファイルからPDFファイルを生成、という流れです。宛名印刷のような作業には、LaTeXで記述した雛形にPerlで住所氏名を流し込む方法で対処しています。
     システムやアプリケーションの内部を”探る”ときには、コマンドの力が欠かせません。バイナリがダイナミックリンクするフレームワーク/共有ライブラリを調べるときには、「otool」コマンドを利用しますし、設定ファイル(*.plist)の閲覧には「less」コマンドが必要です。上書きが禁止されたファイルも、「sudo」と「vi」コマンドを組み合わせれば自由に編集できます。
     とはいえ、WebブラウジングにはSafariやFirefoxを使いますし、メールの読み書きもApple Mailで済ませています。iTunesで音楽を聴かずに原稿を書くなど、考えられないことです。ファイルの検索も、遅い「find」コマンドではなくSpotlightを愛用しています。基本的な作業スタイルは、多くのMacユーザと変わらないと思います。
     前置きが長くなりましたが、コマンドなど使ったことがない、Macにおけるターミナルの存在意義を見いだせない、という”コマンド蕁麻疹”の処方箋を3つ用意しました。次回予定しているMac OS X/Darwinのディレクトリ構成をお読みいただく前に、服用をお願いします。

    ・処方箋その1:ターミナルもファインダも目的は同じターミナルは、特別な処理を行うためのツールではありません。「シェル」という文字ベースのユーザインターフェイス(CUI)を通じ、システムに命令を伝えるにすぎません。GUIを使うかCUIを使うかは、言ってしまえば”ユーザの好みの問題”で、どちらか一方を選択するのではなく平行利用可能なところが、Mac OS Xの美点なのだと思います。
     ただし、CUIはユーザフレンドリーとは言い難いことも事実です。コマンドを利用した作業は、視覚的ではなく論理的、直感的というよりは観念的になりがちです。Mac OS XにはCUIを使わないという選択肢もあるので、必要なときだけ/必要な人だけ使う、というスタンスが正解でしょう。

    ・処方箋その2:コマンドを暗記する必要はない ターミナルを使う場合でも、コマンドの書式を暗記する必要はありません。「man」というオンラインマニュアルを表示するコマンドを利用すれば、かんたんに調べることができますから。コマンド名を忘れてしまった場合も、「apropos」コマンドに続けて適当なキーワードを指定すれば、関連するコマ
    ンドを表示できます。
     コマンドを毎回入力しなければならない、という考えも誤りです。試しに、ターミナルで[control]-[p]を何度か押してみてください。以前実行したコマンドが自動的に入力されるはずです。

    ・処方箋その3:カレントディレクトリを理解しよう CUIの力を必要とするときがターミナルの出番ですが、知っておかねばならない約束事がいくつかあります。
     その1つが、現在どのフォルダ(ディレクトリ)で作業しているかを示す「カレントディレクトリ」という概念です。結果をターミナル画面に表示してお終い、というコマンドでもないかぎり、実行結果の保存先はカレントディレクトリとなることが基本です。Cocoa/Carbonアプリケーションのように、黙っていても「書類」や「デスクトップ」に保存されるということはありません。
     ターミナルを起動すると、現在ログインしているユーザのホームフォルダがカレントディレクトリに設定されます。言い換えれば、カレントディレクトリを変更するコマンドを実行しないかぎり、ファインダで”家”として表示されるフォルダが、コマンドの実行結果が保存される領域となります。

    ■プロフィール
    海上 忍(うなかみ・しのぶ)
    ITライター&コラムニスト。紙媒体ではMac Fanと日刊スポーツ、オンライン媒体ではマイコミジャーナルやITmediaなどで計6本の連載を持つほか、取材や製品レビューも手がける。これまでに上梓した単行本は約30冊、近著は「改訂版Mac OS X ターミナルコマンド ポケットリファレンス」(技術評論社刊)。

    書籍紹介                    GDBデバッギング入門

     解説担当:高橋政明

    GDBデバッギング入門
      Richard M.Stallman and Roland H. Pesch著
      コスモ・プラネット訳
      株式会社アスキー ISBN 4-7561-3016-X  本体1,900円+税

     1999年に初版発行の少し古い本です。GDBのバージョン4.17で書かれたドキュメントの翻訳ですが書籍版原著(Debugging with GDB)はバージョン5.1.1対応に更新されているようです。古い本は絶版となるのが常ですがありがたいことに本書は増刷され、私は2005年の第1版第5刷を入手しました。

     GDBはgccなどの開発ツールのひとつとしてMac OS Xに搭載されているオープンソースのソースレベルデバッガです。Xcodeのデバッガを使っているなら(間接的に)GDBを使っていることになります。
    /Developer/ADC Reference Library/documentation/DeveloperTools/gdbに英文ドキュメントがありますが、今回ご紹介した書籍はバージョンは違いますがこれの日本語訳です。

    Debugging with GDB  The GNU Source-Level Debugger
    file:///Developer/ADC%20Reference%20Library/documentation/DeveloperTools/gdb/gdb/gdb_toc.html

     なおインターネットを検索すると日本語のドキュメントもみつかります。

     GDBはグラフィカルインターフェースを持ちませんがXcodeがUIを担当しコマンドライン操作せずにデバグできています。直接GDBのコマンドを使えるようになると効率よくバグ退治ができるようになる場面も少なくないハズです。コンソール出力はコピーしてバグの報告書を作るとき等にも役に立ちます。
     そんなこともありXcodeのドキュメント(Xcode 2.3ユーザーガイド)にも
    『「Debugging with GDB」を参照してください。』と度々出てきます。

     さらにGDBの威力はXcodeによる開発中だけに留まりません。ターミナルでgdbコマンドを使いクラッシュの原因を突き止めることも可能です。GDBはいろいろなCPUで利用でき、CやC++、Objective-CやObjective-C++はもちろん様々な言語(Modula-2やAdaなんて項目があります!)にも対応しているそうです。ですのでGDBのスキルを身につけることは無駄にはなりません。
     マルチコアを生かすためのマルチスレッドプログラミングではデバグ作業も難しくなりますが、GDBのスレッドデバグ機能が役に立つでしょう。

     GDBには興味があるが英文ドキュメントは敷居が高い場合に本書が手頃な入門書となるはずです。

    ▼出版社のweb
    http://www.ascii.co.jp/books/books/detail/4-7561-3016-X.shtml

    ◇MOSAからのお知らせと編集後記は割愛します◇

     

    MOSA Developer News   略称[MOSADeN=モサ伝]
            配信停止 mailto:mosaden-ml@mosa.gr.jp
     記事内容に関するご意見 mailto:mosaden-toukou@mosa.gr.jp
          記事投稿受付 http://www.mosa.gr.jp/?page_id=850
    Apple、Mac OSは米国アップル社の登録商標です。またそのほかの各製品名等はそれぞれ各社の商標ならびに登録商標です。このメールの再配信、および掲載された記事の無断転載を禁じます。
    特定非営利活動法人MOSA  http://www.mosa.gr.jp/
    Copyright (C)2007 MOSA. All rights reserved.