プロフィール
| |
- Author:あろは (alohakun)
- 若槻俊宏 (WAKATSUKI toshihiro)
連絡先 : alohakun ___at___ gmail.com mixi : http://mixi.jp/show_friend.pl?id=182927 twitter : http://twitter.com/alohakun
abstract
プログラミングという人間の知的行為を体系化し,単なる職人芸ではなく,サイエンスにするための研究をしています.
具体的には,等価変換計算モデルに基づいた,仕様記述からのプログラム合成の研究をしています.
もっと噛み砕くと,プログラムの正しさをどのように定式化し,どのような枠組みで,どのように変換を進めていけば,正しさを保証したまま,効率的なプログラムを手に入れることができるのか,ということについて研究しています.
キーワード : equivalent transformation, computation model, programming paradigm, formal specification, program synthesis





|
FC2カウンター
| |
|
ブロとも申請フォーム
| |
この人とブロともになる
|
|
こんなことしてる場合じゃ無いんだけど
| 2007/01/11(木) 02:37:20
|
treelang 4.1.1
う〜ん,treelang が読めそうでよくわからんのだよなぁ.
なんとなーく,どうやって木を組み立てれば良いのかはわかりつつあるんだけど,致命的なのが,わしがさっぱり yacc わかんない子ってこと.
ここらへんを見ていると,parsec が本当に非常に素晴らしい (もう見た感じ非効率なんだけど,Haskeller がそんなことを気にしていてはいけない !!) ものに思えてくるんだけど,Haskell から C 関数を呼び出す方法がさっぱりわからんのだよなぁ.
parsec で構文解析して,gcc のミドルエンドにつなげるようなことができたら,いろいろ素晴らしい世界が広がるような気がするんだけど.
とりあえず,テキトーな S 式を,gcc の tree に変換するようなのを書いてみようかねぇ.
S 式のパーサを書くのも面倒だから,またいつものように gauche の拡張ライブラリとして実装するか.
これがうまく行けば,gauche から gcc のミドルエンドを操れるという不思議な世界が到来するかもしれない.ていうか,ilog スクリプトのパクリだけど.
まぁ,gcc のインタフェースが謎過ぎるし,関数も構造も膨大だから,俺程度の腕力じゃ絵に書いた餅に終わりそうだけど.
んー,まてよ.C++ Boost Spirit からなら,直接 gcc の関数を呼び出せるのではないか ? spirit で構文解析をして,tree をミドルエンドに渡すことができたらいろいろ素晴らしいじゃないか.
まぁ,こっちはこっちで,C++ と Boost という yacc 以上の強敵を撃破しないといけないんで,先が長すぎだけど.
あと,yacc を使わないと,行番号とかみたいなデバッグ情報をうまく取得できないかもしれない (できないってことは無いだろうけど,yacc よりもはるかに情報が少なそうだ)
てか,明日 2 限スタートなのに,なにやってんだ,俺の葛.たまに GCC 熱が急激に高まって躯が疼く夜もある (キモい)
一番の難敵は GGC なのかもしれない.未だに何やってるのかさっぱりわからない.
あと,treelang に,tree_rest_of_compilation とか出てきてるんだけど,ここらへんって obsolete になったんじゃなかったっけ ?
追記 : 廃止になったのは,rest_of_compilation() だよ !!
一度師 (を さん) にいろいろ聞いてみたいなぁ.ここらへんのブログ界隈では,一番 GCC の奥底まで潜られた御方だと思うので.ilog スクリプトは,僕程度の力量ではあんまり理解できなかった (駄目)
誰か 「ふつうの GCC プログラミング」 とか 「GHG」 とか書いて ! (どこまでも他力本願)
GCC の API とかって,どっかに情報無いのかな…
|
codeReading|TB:0|CM:2|
|

▲
| |
|
treelang Hacking Guide
| 2006/12/18(月) 12:09:28
|
えーと.RHG のパクリです.
とりあえず,yacc/lex の部分は丸投げ.各自読んでおいてください.あと,『Rubyを256倍使うための本 無道編』 は,もし近所で売ってたら買おうと思いました.
第9章 速習yacc
あと,何はなくともこれ.
treelang のソースを GLOBAL で HTML 化したもの
まずは parse.y とかを眺めてみると.
575 |expression tl_PLUS expression { 576 struct prod_token_parm_item *tok = $2; 577 struct prod_token_parm_item *op1 = $1; 578 struct prod_token_parm_item *op2 = $3; 579 int type_code = get_common_type (op1, op2); 580 if (!type_code) 581 YYERROR; 582 $$ = make_plus_expression (tok, op1, op2, type_code, EXP_PLUS); 583 }
これが + の構文木を組み立てるアクションの部分です.yacc 様と lex 様が, "1 + 2" とか "hoge + foo" みたいな文字列を,$1 $2 $3 と切り出してくれて,$2 が + だったら還元,アクション発動 ! みたいなノリだと思います.
prod_token_parm_item っていうのが謎ですが.treelang.h で,こんな感じになっとります.
/* A production or a token. */ struct prod_token_parm_item GTY(()) { enum category_enum category; /* Token or production. */
unsigned int type; /* Token or production type. */ union t_or_p { struct token_part GTY((tag ("token_category"))) tok; struct production_part GTY((tag ("production_category"))) pro; struct parameter_part GTY((tag ("parameter_category"))) par; } GTY((desc ("((item *)&%1)->category"))) tp; };
というか,またもやいきなり謎だらけですが.GTY ってのは,とりあえず gcc の GC (ggc) のためのタグなので,とりあえず無視.
GCC の構造は死ぬ程込み入っているので,LANG HOOKS (フロントエンドごとの個別の処理を引っかけておく (初期化とか,フロントエンド名の表示とか,その他もろもろの関数呼び出しをフックして,差し替える) ための,関数ポインタの塊.これにより,共通のミドルエンド,バックエンドに対して,フロントエンドだけを言語ごとに別々のものを用意できるようになる),GGC,tree.(c | h | def) あたりの構造を全く知らないと,いきなりどうしようもなくなるという異常な学習曲線の高さ !! こんなイカれたプログラムよりも,現在は coins とか,もっとモダンなコンパイラインフラはいろいろありそうな気もしますが,そこはアレ.
まずは簡単なとこから.get_common_type() ってのは,単に型の大きさを揃えているだけ.まぁ,treelang は型が 3 つぐらいしか無い (int, uint, char, void) から楽勝なんだけど.型の数が増えてくると,こういう切り上げ規則とかが一気に複雑になってくると思う (型 × 型の数だけ可能性がある).
832 /* Check TYPE1 and TYPE2 which are integral types. Return the lowest 833 common type (min is signed int). */ 834 835 static int 836 get_common_type (struct prod_token_parm_item *type1, 837 struct prod_token_parm_item *type2) 838 { 839 if (NUMERIC_TYPE (type1) == UNSIGNED_INT) 840 return UNSIGNED_INT; 841 if (NUMERIC_TYPE (type2) == UNSIGNED_INT) 842 return UNSIGNED_INT; 843 844 return SIGNED_INT; 845 }
どっちかが uint だったら両方 uint にキャスト,ってのも無理矢理な気がする.
prod_token_parm_item を見る限り,なんでトークン切り出すだけのパーサで,いきなりこんな複雑な構造体が $1 とか $3 に飛び込んで来るのよ ! と謎が謎を呼ぶ展開ですが.
そこらへんをちょっとわかった気になるために,まずは整数ノードをどうやって作るかを見てみよう.
568 expression: 569 INTEGER { 570 $$ = make_integer_constant ($1); 571
# というか,FC2 は codeReading に根本的に向いてない.コードとか URL 張るのが死ぬ程面倒.自動で URL を href に変換してくれて,スーパー pre 記法が使えるはてなは素晴らしい.
まぁ,こんな感じで,トークンから整数ノードを作る.$$ ってのは YYSTYPE で, ぶっちゃけ void *.なんでも指せる.
145 /* For parser. Alternatively you can define it using %union (bison) or 146 union. */ 147 #define YYSTYPE void *
そして,構造体の先頭に,必ず型タグを含んだ共通の構造を入れておいて,それを元にエラーチェックしたりキャストして使ったり,ってのは,GCC (に限らず,C でちょっと複雑な構造を扱うときの) 常套句.
/* Parse structure type. */ enum category_enum { /* These values less likely to be there by chance unlike 0/1, make checks more meaningful */ token_category = 111, production_category = 222, parameter_category = 333 };
非常になげやりな定義ですな.
まぁ,そんなこんなで,トークンと構造体の区別は付く.
ここまでの知識があれば,以下の整数定数ノードを作る関数は読み解けるはず… っ !! (たぶん)
881 /* Make a production for an integer constant VALUE. */ 882 883 static struct prod_token_parm_item * 884 make_integer_constant (struct prod_token_parm_item* value) 885 { 886 struct prod_token_parm_item *tok; 887 struct prod_token_parm_item *prod; 888 tok = value; 889 prod = make_production (PROD_INTEGER_CONSTANT, tok); 890 if ((tok->tp.tok.chars[0] == (unsigned char)'-') 891 || (tok->tp.tok.chars[0] == (unsigned char)'+')) 892 NUMERIC_TYPE (prod) = SIGNED_INT; 893 else 894 NUMERIC_TYPE (prod) = UNSIGNED_INT; 895 prod->tp.pro.code = tree_code_get_integer_value (tok->tp.tok.chars, 896 tok->tp.tok.length); 897 return prod; 898 }
すいません,大嘘でした.全然わかりません.
まず,$1 (value) は単なるトークンだと思っていたら,実は struct prod_token_parm_item * だったという衝撃的な展開.$1 とかって,どこで作られてるんだ ???
(根本的に yacc/lex をわかってない管理人)
あ,なるほど.lex.l で,トークン切り出すと同時にノードが作られてるんだ.
%%
{ /* ??? Should really allocate only what we need. */ yylval = my_malloc (sizeof (struct prod_token_parm_item)); LINEMAP_POSITION_FOR_COLUMN (input_location, &line_table, next_tree_charno); ((struct prod_token_parm_item *)yylval)->tp.tok.location = input_location; ((struct prod_token_parm_item *)yylval)->tp.tok.charno = next_tree_charno; } ...
/* Fill in the fields of yylval - the value of the token. The token type is A. */ void update_yylval (int a) { struct prod_token_parm_item * tok; tok = yylval;
tok->category = token_category; tok->type = a; tok->tp.tok.length = yyleng; /* Have to copy yytext as it is just a ptr into the buffer at the
moment. */ tok->tp.tok.chars = (unsigned char*) get_string (yytext, yyleng); }
んで,これがマッチするたびに,こんな感じで呼ばれる.
#define SAVE_RETURN(a) {update_yylval (a); if (option_lexer_trace)\ {fprintf (stderr, "\nlexer returning"); dump_lex_value (a);} return a;} ...
"+" { update_lineno_charno (); SAVE_RETURN (tl_PLUS); } ...
[+-]?[0-9]+ { update_lineno_charno (); SAVE_RETURN (INTEGER); }
ちなみに,どこでこういう INTEGER とか tl_PLUS とかが定義されてるんだ ? って感じだけど.
それはまぁ,たぶん parse.y から何か自動生成されるんだと思う.そもそも,lex.l に include されてる parse.h とか,どこにも影も形も無いもん.
parse.y を bison すると,parse.tab.c とかが生成されて.
/* Tokens. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE /* Put the tokens into the symbol table, so that GDB and other debuggers know about them. */ enum yytokentype { RIGHT_BRACE = 258, LEFT_BRACE = 259, RIGHT_SQUARE_BRACKET = 260, LEFT_SQUARE_BRACKET = 261, RIGHT_PARENTHESIS = 262, LEFT_PARENTHESIS = 263, SEMICOLON = 264, ASTERISK = 265, COMMA = 266, EQUALS = 267, ASSIGN = 268, tl_PLUS = 269, tl_MINUS = 270, INTEGER = 271, IF = 272, ELSE = 273, tl_RETURN = 274, CHAR = 275, INT = 276, UNSIGNED = 277, VOID = 278, TYPEDEF = 279, NAME = 280, STATIC = 281, AUTOMATIC = 282, EXTERNAL_DEFINITION = 283, EXTERNAL_REFERENCE = 284, WHITESPACE = 285, COMMENT = 286, PROD_VARIABLE_NAME = 287, PROD_TYPE_NAME = 288, PROD_FUNCTION_NAME = 289, PROD_INTEGER_CONSTANT = 290, PROD_PLUS_EXPRESSION = 291, PROD_MINUS_EXPRESSION = 292, PROD_ASSIGN_EXPRESSION = 293, PROD_VARIABLE_REFERENCE_EXPRESSION = 294, PROD_PARAMETER = 295, PROD_FUNCTION_INVOCATION = 296 };
まぁ,こんな感じでトークンが定義される.しかも,これは単なる整数じゃなくて,ちゃんと GDB とかで追えるようになってるらしい.GCC と GDB の素晴らしい連携プレイが垣間見れます.
ふ〜む,ボクは基本的に,今までパーサレキサの類は全部手書きしてた (いや,というか,ほとんど S 式しか扱わないからそれでも余裕なんだけど) んだけど,やっぱパーサジェネレータは便利なんだねぇ (今さら)
# こういう yacc/lex みたいなやり方だと,ちょっと本格的に使ったら,すぐにデバグで死にそうだけど.Prolog と同じ類のややこしさを感じる (yacc/lex 使える人は,すぐに Prolog も使えると思う).どっちも What の記述を謳ってるけど,実際には How の中身を詳細に知ってないと使えないという.まぁ,Prolog の方が,マッチやバックトラックのトレースは楽そうだけど.
そんなこんなで,トークンが切り出されるたびに,行番号とかさまざまな有益な情報も同時に付加され,ノードが自動的に作られてるみたい.
あとはま,なんかテキトーに.いろいろ作ってる (なげやり)
288 /* Create a struct productions of type TYPE, main token MAIN_TOK. */ 289 290 struct prod_token_parm_item * 291 make_production (int type, struct prod_token_parm_item *main_tok) 292 { 293 struct prod_token_parm_item *prod; 294 prod = my_malloc (sizeof (struct prod_token_parm_item)); 295 prod->category = production_category; 296 prod->type = type; 297 prod->tp.pro.main_token = main_tok; 298 return prod; 299 }
疲れてきたので,続きは次回ということで.
つづく (のか ?)
何かもう,次回あたりから,いきなり gcc の tree を操る関数群に触れないといけない感じです.build_int_cst_wide() とか.ここらへんは,たぶんドキュメントとか全く無いから (いや,もともと treelang も無いんだけど),ソース読むしか無いような.まぁ,とどのつまり,tree を制するものがフロントエンドを制す !! ということです.tree は避けられない.
GCC 解読室 Wiki* : build_int_cst_wide
正直,tree 周りは,もうほとんど C 言語で Lisp を書いてるようなところなので,Lisp の知識が無いとつらいかも.命名規則とか特に.なんせ gcc を作った RMS が,筋金入りの古典的 MIT Lisp ハカーだし (emacs 見ればわかるだろうけど).
|
codeReading|TB:0|CM:4|
|

▲
| |
|
ping から始まる僕らのネットワークプログラミング
| 2006/08/29(火) 17:03:27
|
まぁ,やっぱ時代は Web 2.0 だよ.ウェブプログラミングの時代だよ.
そうだこのブログにはウェブ分が足りない !今時ネットワークも使わないようじゃ全然駄目だ.
てことで,とりあえず ping のソースコードを読んでみよう (違
元ネタは,プログラミングテクニック --- UNIXコマンドのソースコードにみる実践プログラミング手法.
この本は若干古いですし,雑誌連載記事ということで内容も軽目なんですが,そのぶん読みやすくてなかなか良いです.
しっかし,ping と一口に言っても,メチャクチャ種類があるんですね.歴史がありまくりのソフトウェアですから.
BSD とか OpenSolaris あたりから引っ張ってくるのが王道なんでしょうが,IPv6 に対応してるとか,オプションがいっぱいあって高機能とかは本質ではないと思うので,今回は一番古いベーシックなやつっぽい GNU のをチョイスしてみました.
The Ping Page
OpenSolaris のやつなんかは,ちゃんと ping.h と ping.c に別れてて,2500 行ぐらいの規模なんですが,古い方はわずか 500 行ほどです.
HTML 化したもの
しっかし,古すぎて,ヘッダとかスタイルが合わない (BSD スタイル ? のトラディショナル C で,bcopy(const void *src, void *dest, size_t n) とか使われまくりだし・・・ autotools なんてものは,もちろん使われてない) ためか,コンパイルが通らない・・・ というわけで,テキトーに読んで行くだけにします.
う〜ん,int は全て省略と,C の型推論機構をフルに活かした,実に男気溢れるソースとなっております.
# リンク先の 「argv の型は int です !」「char**p=v;」 に,全米が萌えた.

(外人素材集)
余談ですが,ping.shar (SHell ARchive) 形式 なんて,始めて知りました・・・ chmod +x して実行すると,ファイルが出てくるという.まるまるファイルの内容が,シェルスクリプトに埋めこまれているんですね.実行し終るとファイルサイズが 0 になったりと,芸が細かい.ちょっと感動.
(tar/zip 世代の管理人)
ping は,もうほとんど,socket プログラミングのサンプルですね.動作は実にシンプル.ICMP (International Conference on MetaProgramming ・・・ ではなく,Internet Control Message Protocol) でソケットを開いて,ICMP_ECHO を投げて,ICMP_ECHOREPLY を受け取るまでの時間を計ったり,アレコレしてネットワークの接続状況を確かめると.
UNIX だと,ファイルもソケットもほとんど同じような感覚で扱えます.現に,send と write システムコールは,ほとんど同じだったと思います.そしてこの Berkeley socket の気軽さこそが,UNIX がネットワークの世界で爆発的に普及した要因で, そのファイルによる抽象化の精神は,plan 9 でさらに徹底されるようになったとかナントカいう話アルよ.
# それっぽく書いてますが,管理人は BSD とか socket プログラミングとか全然わかりません (Mr. 知ったかぶり).
123 hp = gethostbyname(av[0])
153 if ((proto = getprotobyname("icmp")) == NULL) { 154 fprintf(stderr, "icmp: unknown protocol\n"); 155 exit(10); 156
158 if ((s = socket(AF_INET, SOCK_RAW, proto->p_proto)) < 0) { 159 perror("ping: socket"); 160 exit(5); 161
man page には,「PF_INET IPv4 インターネット・プロトコル ip(7) 」 とかしか載ってないので謎ですが,IF_INET もほとんど同じみたいです.
なんだかPF_*****となっている.が,ネットで調査していてよく見掛けるのはAF_*****である.どういうことかというと,/usr/include/bits/socket.hによると
#define AF_UNIX PF_UNIX #define AF_FILE PF_FILE #define AF_INET PF_INET #define AF_AX25 PF_AX25 #define AF_IPX PF_IPX ....
らしい.同ファイルコメントによると,PFはProtocol Familyで,AFはAddress Familyらしい. (ファイヤープロジェクト socket関数とプロトコル)
162 if (options & SO_DEBUG) { 163 if(pingflags & VERBOSE) 164 printf("...debug on.\n"); 165 setsockopt(s, SOL_SOCKET, SO_DEBUG, &on, sizeof(on)); 166 } 167 if (options & SO_DONTROUTE) { 168 if(pingflags & VERBOSE) 169 printf("...no routing.\n"); 170 setsockopt(s, SOL_SOCKET, SO_DONTROUTE, &on, sizeof(on)); 171
206 if ( (cc=recvfrom(s, packet, len, 0, &from, &fromlen)) < 0) { 207 if( errno == EINTR ) 208 continue; 209 perror("ping: recvfrom"); 210 continue; 211
266 icp->icmp_type = ICMP_ECHO; 267 icp->icmp_code = 0; 268 icp->icmp_cksum = 0; 269 icp->icmp_seq = ntransmitted++; 270 icp->icmp_id = ident; /* ID */ ... 283 /* cc = sendto(s, msg, len, flags, to, tolen) */ 284 i = sendto( s, outpack, cc, 0, &whereto, sizeof(struct sockaddr) );
まぁ,そんなこんなで 191 行目からの for(;;;) の中で,延々と指定された回数分 pinger して catcher して,finish という流れなのではないかと思います (面倒になってきてテキトー).
216 /*NOTREACHED*/
ってのは,lint を黙らせるためのテクニックですな.finish するから,絶対に main の最後には行かないから.現在では,lint は C コンパイラに統合されていることが多いのではないかと思います.
Super Technique 講座 longjmpと例外
ここでコメント /* NOTREACHED */ は、知らない人もいるだろうからちょっと注釈しておこう。gcc では、正確なフロー解析を行い、初期値がセットされていない自動変数を指摘することは皆さんも御承知のことだろう。つまり、プログラムの流れが複雑に分岐していても、それの流れを辿って、すべての場合に変数が正しくセットされるかどうかをチェックしているのである。この時、基本的に関数は戻って来るものとして処理される。呼び出したらそこに戻らずに、どこかに行ってしまう関数は変態であり、そういうライブラリ関数は数少ないが、しかし、そのようなライブラリ関数をラップしたユーザ関数は、任意に作れるからである(warning() なんて良い例だが...)。この時、「ユーザ関数 warning() は戻って来ない」ことをコンパイラに伝えてやれないと、フロー解析では戻って来ることを前提にするので、無意味に「初期値がセットされていない」という警告が発生することがある。だから、コメントのかたちで /* NOTREACHED */ と書いてやると、コンパイラはこのコメントを認識し、このコメントには制御が移らないことを理解する。だからlongjmp 呼び出しをするユーザ関数の呼び出しの後には、このコメントを入れておくべきであるし、また、同様な戻らない関数 exec() などにもこれは有用なわけである。
ちなみにこのようにコンパイラが認識する特殊なコメントとしては、switch 文の FALLTHROUGH を「判って書いている」ことを伝える /* FALLTHROUGH */ があることはご存知かな?
実はこまかい所が全然わかってないのですが,雰囲気はつかめたような・・・ ふん,まぁええわ.今日のところは,この辺で勘弁しといたるわ !
(吉本新喜劇な管理人)
追記
我らが GCC なら… GCC ならきっとなんとかしてくれる…
と思いきや.
やっぱり gcc は黙らないみたいです ('・c_・` ;)
更新履歴兼雑記 ■[Program][Bin] GCC の __attribute__ いろいろ
GCC-2.95.3 から GCC-4.1 まで、全て -Wall で warning が出ました。 lint がこれで黙ったというのはそうなんだろうなぁと思うんですが。はて。まぁなんにせよ今だったら GCC の拡張、 noreturn で黙らせればいいと思いますです。
...
pure も面白いと気付きました。純粋関数型の世界にようこそ。
でも,今時みんな gcc -Wall で済ませてしまうから, lint なんて,使っている人少ないですよね,きっと (怒られそうだ).
上記した UNIX プログラミングテクニック本を読み返して見ると,lint は黙るけど,コンパイラがうるさい場合は __dead2 という sys/cdefs.h で定義されているマクロを使うと良いと書いてありました.
テキトーにぐぐってヒットした sys/cdefs.h の GLOBAL によると
/* * Compiler-dependent macros to help declare dead (non-returning) and * pure (no side effects) functions, and unused variables. They are * null except for versions of gcc that are known to support the features * properly (old versions of gcc-2 supported the dead and pure features * in a different (wrong) way). */ #if __GNUC__ < 2 || __GNUC__ == 2 && __GNUC_MINOR__ < 5 #define __dead2 #define __pure2 #define __unused #endif #if __GNUC__ == 2 && __GNUC_MINOR__ >= 5 && __GNUC_MINOR__ < 7 #define __dead2 __attribute__((__noreturn__)) #define __pure2 __attribute__((__const__)) #define __unused #endif #if __GNUC__ == 2 && __GNUC_MINOR__ >= 7 || __GNUC__ == 3 #define __dead2 __attribute__((__noreturn__)) #define __pure2 __attribute__((__const__)) #define __unused __attribute__((__unused__)) #endif
と,shinh さんとほぼ同様に,noreturn attribute を使用しているようです.
ようこそ…『戻れない世界』へ…
なんとなく __attribute でいいんなら __attribute__ じゃなくて今度からそっち使うか…と思い始めたんですが、そのへん使いわけとかあるんですかね…
んで,これを見る限り,__attribute は,__attribute__ のラッパーみたいな感じになっているような.
他にも,プログラム中ではどこにも使用されない (strings や,what などの UNIX コマンドによって抽出される情報らしい) 文字列に対してコンパイラがうるさいときは,同じヘッダで定義されている __IDSTRING(name, string) マクロを使うと良いとか.
#if defined(__GNUC__) && defined(__ELF__) #define __IDSTRING(name,string) __asm__(".ident\t\"" string "\"") #else #define __IDSTRING(name,string) static const char name[] __unused = string #endif
ちなみに,ping の古いコード内の "@(#)" っていう文字列は,RCS (cvs のいっこ前.cvs さえ今は時代遅れぎみ) さえ古過ぎなのに,そのさらにいっこ昔のソースコード管理システム SCCS に由来するそうな…
この本には,こういう類の,トラディショナルな GNU のテクニックがどっさり詰め込まれていて,無駄にお得感を感じます.
彼の心にさわやかな風が吹いた… もういじけた目はしていない.彼の中に,生きるための目的が見えたのだ…
こうして管理人は,セリエ A のスター選手よりも ! UNIX 考古学者にあこがれるようになったのだ !! (地味
(いつか石仮面の謎を解きます)
|
codeReading|TB:0|CM:0|
|

▲
| |
| |
|
|
最近のコメント
| |
| リンク
| |
このブログをリンクに追加する
| 最近のトラックバック
| |
| 人生の残り日数
| |
日本人男性の平均寿命は 28700日.
| RSSフィード
| |
| カテゴリー
| |
|
|