2009年03月21日

File::Find風味にftsを使ってみる。

File::Find風味のftwと比べるとftsは前後処理が結構面倒くさいのだが
関数型変数などを使って使いやすくしてみた。
今回は多少ライブラリー関数臭い物も使っているが原則libcオンリーだと思う。
typedefなどの理解も若干深まった。
今回はsortは時間と名前固定でselect関数を自分で設定する形の
ディレクトリ一括処理としてjpegファイルをリストするものを作った。
まず関数型変数について
fts_openをするときには関数ではなくてint型変数を渡すことが出来たので
delphiとは違ってint型でもキャストされると思ったのだが


int sort_cmp(const FTSENT **a, const FTSENT **b){
//
//何か皮革
//

}

として

int sort;
sort=&sort_comp;
fts_open( path,FTS_LOGICAL
, sort_cmp)

と一般的な変数に関数ポインタを代入できるが
ここで

sort_cmp(a,b);

とできるところを

sort(a,b);

とはできない。

結局delphi同様関数型として定義する方が無難なようで
最初は

typedef int (*Tselect)(const FTSENT * const *, const FTS * const *);

等としたのだが思い直して

typedef int (*TProc2)(const void * const *, const void * const *);
typedef int (*TProc1)(const void * const *);

と汎用性を持たせた。タイプキャスト関連でwarningがでがち(だらけ)だが使える。

次にFile:Findやftwの様に開放のいらないコールバック指定型のつかいっきりの関数を作る。


int find_Ln(char **path,TProc1 select,int lv){
//var
FTS *ftsp;
FTSENT *p;
TProc1 selfn;
int l;
//begen
if ((ftsp = fts_open( path,FTS_LOGICAL //
,cmp_tf)) == NULL)return(1);
selfn= (select==NULL)?&dummy1:select;
fts_read(ftsp);
while((p = fts_read(ftsp)) != NULL) {
if (lv &&(p->fts_info==FTS_D)){if (p->fts_level>=lv)
fts_set(ftsp,p,FTS_SKIP);
}
if(selfn(p))break;
}
fts_close(ftsp);
return(0);
}


ソートはファイル優先・時間順で固定、レベルは0だと全部探る仕様
cmp_tfは後述するがls -trの様なディレクトリーを後回しにする仕様

fts_openでエラーがでていないなら少なくとも最初のfts_readは確実に成功するから
「.」ないしは開始ディレクトリーをスキップする。
つまりselectで$_->fts_info==FTS_Dであれば終了するようにすれば
lvが0でもディレクトリそのものも選択せずに終了する。

fts_openの第二パラメータにロジカルを選んでいるからリンクはリンク先を読む。
selectがnullだとコアを吐いて落ちる。そのためにダミーの関数を作っておいた。
ループの中でselectのnullチェックをしてdummyを代入する。
dummy1は

int dummy1(void *$_){return(0);};

と返すだけ、

使うときは


int main (int argc, char *argv[])
{
//var
char *path[2]={(argc == 1)? "." :argv[1],NULL} ;
TProc1 select=NULL;

select=select_J;
if (find_Ln(path,select,0)){
puts("fail");return(1);
}


という感じ文字列配列の最後にnullを入れるのも定石の様だ。

当然sortを選ぶバージョンのものも作れるが引数が多くなるので構造体化を検討している。
しかし今回のものをinclude化すれば相当すっきりしそうだ。

今回のsort

int cmp_tf(const FTSENT **a, const FTSENT **b)
//ディレクトリー後回し 時間昇順 名前昇順
{
//var
int t;
//begen
if (S_ISDIR((*a)->fts_statp->st_mode)){//dir
if (!(S_ISDIR((*b)->fts_statp->st_mode))) return(1);
} else
if (S_ISDIR((*b)->fts_statp->st_mode))return(-1);

t =(*a)->fts_statp->st_ctime - (*b)->fts_statp->st_ctime;
if (t)return(t);
return (strcasecmp((*a)->fts_name, (*b)->fts_name));
}


ここ数回の内容を多少反映させたjpg選択

int select_J(FTSENT *$_){
//var
char *c;
c=strrchr($_->fts_name,'.');
if (c)if (!strcasecmp(".jpg",c)){

puts($_->fts_path);
}
return(0);
}


予約語で引っかかるかと思ったが
perl風に引数を「$_」にしてみた。「_」や「$」でもいいようだ。
相対pathを書くだけだが実際はここで作業をする。
ソート済みなのでこれを配列にもっていってもいいし直接処理でもいいだろう。
strcasecmpはnullだと落ちるみたいなのでstrrchrがnullを返した時に備えた。

jpgにしたのは現実問題としてのデジカメ処理のため
ftwやsortと違ってパラメータは自由に渡せるので拡張子配列を渡す手もあると思う。
拡張子配列に関してはグローバル変数にしてもいいかもしれない。

拡張子判別は頭でreturnしてしまっても良い。
perlだと

return if ( c !~ /jpg$/i );

というところか。そういうのはpcreでやればよい。

今回のfind作成でいろいろと楽になった気がする。
stat済みである点がperlのFile::Findと違うところ。


ラベル:ディレクトリ FTS
posted by Xo_ox at 19:52| Comment(0) | C | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
×

この広告は180日以上新しい記事の投稿がないブログに表示されております。