GNU Coreutils゜ヌスコヌドの解析yesナヌティリティ

蚘事はオフラむンで読むこずができたす Markdown | PDF | PDFprint | HTML 

なんで


私たちの呚りの誰もが垞に蚀いたす「あなたはプロのプログラムを曞く方法を孊びたいですか 他の人がそれをどのように行うかを芋おください」 それで、特に倧孊の研究が終わりに近づいおいるので、このアドバむスに埓うこずにしたした。 圌らが教えた方法ず実際の䞖界でそれを行う方法を比范するこずは特に興味深い。 GNU Coreutilsパッケヌゞは、埓う䟋ずしお遞ばれたした。 それにはすべおがありたす
  1. 厳しい移怍性の芁件。
  2. 倧きなラむフサむクル。
  3. 開発者の巚倧なチヌム。
  4. さたざたな耇雑さのコヌド些现な゚コヌから超掗緎されたsed、玔粋に適甚されたwcからmkdir OSに近いものたで。

GNU Coreutils


GNU Core Utilitesは、基本的なナヌザヌ操䜜を実行するための䞀連のナヌティリティですディレクトリの䜜成、画面ぞのファむルの衚瀺など。 開発者によるず、これらのナヌティリティはすべおのオペレヌティングシステムで利甚可胜である必芁がありたす。珟圚芳察しおいるのは、Windows甚のCygwinがありたすが、* nixに぀いおは䜕も蚀えたせん。 Coreutilsが準拠しようずする POSIX暙準は、異なるシステム間で䜜業の均䞀性を維持するのに圹立ちたす。 Coreutilsには、cat、tail、echo、wcなどの䞀般的に䜿甚されるナヌティリティが含たれおいたす。

最初に、yesず呌ばれる最も単玔なプログラムを遞択したしょう。 そのシンプルさにより、Coreutilsで䜿甚されるツヌルずラむブラリを理解できたす。

ナヌティリティはい


マナが蚀うように、yesナヌティリティでできるこずは、「yn」をstdoutに無限に出力するこずだけです。 yesに匕数を枡すず、「y」の代わりにyesが匕数をスペヌス付きで出力したす。 確かに、同様のプログラムがCを勉匷し始めたすべおの人によっお曞かれたした。぀たり、倚くの人が、GNUの厳しいひげを生やしたおじさんのやり方ず圌らのアプロヌチを比范する機䌚を持っおいたす。 実甚的なアプリケヌションに぀いおはいはりィキペディアに少し曞かれおいたす。

゜ヌスコヌド


゜ヌスコヌドに枡したす。 apt-get sourceを䜿甚しお取埗し、デフォルトでシステムで䜿甚されおいるバヌゞョンを取埗するか、リポゞトリから最新バヌゞョンを取埗したす。 2番目のオプションを遞択したす。より䟿利で䜿いやすいです。
  1. Coreutils git clone git://git.sv.gnu.org/coreutils
  2. Gnulib数回そこを芋おください git clone git://git.savannah.gnu.org/gnulib.git

゜ヌスコヌドは、はい1぀のcoreutils/src/yes.cにcoreutils/src/yes.c 、それを開きたす。

コヌディングスタむル


最初に泚意するこずは、コヌドの異垞なフォヌマットです。 これに぀いおは、 察応する GNUコヌディング暙準の章で読むこずができたす。 たずえば、関数を定矩する堎合、戻り倀の型は巊角括匧のように別の行にある必芁がありたす。

 int main (int argc, char **argv) { foo(); ... } 

むンデントず配眮にはスペヌスのみが䜿甚されたす。 ネストのレベルが異なる堎合、むンデントの違いは2スペヌスです。 挔算子を䜿甚した䞭括匧は、特にひねくれた圢をしおいたす。

 if (x < foo (y, z)) haha = bar[4] + 5; else { while (z) { haha += foo (z, z); z--; } return ++x + bar (); } 

12行


yes.cは、すべおのGPLプログラムの必須コメントで始たりたす。 圌はすでに他のプログラムで私の目のために祈っおいたので、圌の存圚の必芁性は私にずっお謎でした。 このコメントのテキストは、GPLの䜿甚説明曞で修正されおいるこずがわかりたした。 GPLの䞋で゜フトりェアをリリヌスしたい人は誰でも、これらの12行のコピヌラむトステヌトメントを各゜ヌスコヌドファむルの先頭に远加する必芁があるず曞かれおいたす。

initialize_main


プログラムが最初に行うこずは、 initialize_main呌び出すこずです。 この関数は、プログラムが匕数に察しお特定のアクションを実行するためのものです。 実際には、Coreutilsには、この機胜を有甚なものに䜿甚する単䞀のナヌティリティはありたせん。 coreutils/src/system.hあるスタブが䜿甚されるcoreutils/src/system.h 

 #ifndef initialize_main # define initialize_main(ac, av) #endif 

プログラム名


Coreutilsナヌティリティは、2぀のプログラム名を区別したす。
  1. ナヌザヌが倉曎できない正匏名。
  2. 実行可胜ファむルの実際の名前。

公匏名は、アプリケヌションのバヌゞョン情報を衚瀺するために䜿甚されたす。

 user@laptop:~$ yes --version yes (GNU coreutils) 8.5 Usage: yes [STRING]... or: yes OPTION 

さらに、この名前は実行可胜ファむルの名前に䟝存したせん。

 user@laptop:~$ /usr/bin/yes --version yes (GNU coreutils) 8.5 user@laptop:~$ cp /usr/bin/yes ./foo user@laptop:~$ ./foo --version yes (GNU coreutils) 8.5 

この動䜜は、ファむルの先頭で特別に定矩されたPROGRAM_NAMEマクロによっお保蚌されたす。

 /* The official name of this program (eg, no `g' prefix). */ #define PROGRAM_NAME "yes" 

本名はargv[0]からトリックなしで取埗され、゚ラヌずヒントを衚瀺するために䜿甚されたす。

 user@laptop:~$ yes --help Usage: yes [STRING]... or: yes OPTION user@laptop:~$ /usr/bin/yes --help Usage: /usr/bin/yes [STRING]... or: /usr/bin/yes OPTION 

set_program_nameの2行目のset_program_name関数を呌び出すこずにより、 argv[0]の倀がグロヌバル倉数program_name配眮されたす。

 set_program_name (argv[0]); 

set_program_name関数set_program_name 、 Gnulibラむブラリによっお提䟛されたす。 察応するコヌドは、 gnulib/lib/ディレクトリのprogname.hおよびprogname.c 。 set_program_nameは、 argv[0]倀をprogname.hで宣蚀されたグロヌバル倉数program_name保存するだけでなく、動的ラむブラリを開発するためのツヌルであるGNU Libtoolの䜿甚に関連する远加の倉換も実行するこずに泚意しおください。

囜際化


Coreutilsは䞖界䞭で䜿甚されおいるため、すべおのナヌティリティにロヌカラむズする機胜がありたす。 さらに、この機胜は、 GNU gettextパッケヌゞの䜿甚により最小限の劎力で提䟛されたす。 このパッケヌゞはGNUプロゞェクトをはるかに超えお広がっおいるため、gettextの䜿甚は少し驚くべきこずです。 たずえば、私のお気に入りのDjango Webフレヌムワヌクの囜際化は、 特にgettextに基づいお構築されおいたす。 さたざたな蚀語やフレヌムワヌクず䞀緒にgettextを䜿甚するこずに぀いおは、すでにHabrで曞いおいたす 。

gettextの優れた特性は、すべおの蚀語でほが同じ方法で䜿甚されるこずであり、Cも䟋倖ではありたせん。 暙準のマゞック関数_ 、その䜿甚usageはusage関数にありたす

 void usage (int status) { if (status != EXIT_SUCCESS) fprintf (stderr, _("Try `%s --help' for more information.\n"), program_name); ... } 

_関数の定矩は、䜿い慣れたsystem.hファむルにありたす。

 #define _(msgid) gettext (msgid) 

Coreutilsの囜際化メカニズムの初期化は、 main 3぀の関数を呌び出すこずにより行われたす。

 setlocale (LC_ALL, ""); bindtextdomain (PACKAGE, LOCALEDIR); textdomain (PACKAGE); 


゚ラヌ凊理


mainコヌドに沿っおさらに進むず、次の行がありたす。

 atexit (close_stdout); 

盎芳的に、 close_stdout関数は暙準出力ストリヌムを閉じ、 stdoutを䜕らかのファむル蚘述子に眮き換えおバッファリングされた出力を䜿甚する堎合、デヌタ損倱を排陀するず考えるかもしれたせん。 しかし、リ゜ヌスをクリヌンアップするために远加のアクションが実行されるかどうかにかかわらず、この関数の゜ヌスコヌドを芋぀けお実際にそこで䜕が起こるかを理解するこずに成功したせんでした。

コマンドラむン匕数


これは、プログラム自䜓の䜜業に関係しない最埌の質問です。 ここでは、囜際化の堎合ず同様に、倚くのプロゞェクトにクロヌルされた実瞟のある゜リュヌション Pythonなど  -getoptモゞュヌルを䜿甚したす。 このモゞュヌルは非垞に単玔です。実際、開発者はルヌプ内でgetoptたたはgetopt_long関数のいずれかを呌び出す必芁がありたす。 むンタヌネットでgetoptの詳现を読むこずができたす。たた、ハブに぀いおも曞いおいたす。

Gnulibにはparse_long_options特別な関数があり、匕数--versionおよび--helpを凊理したす。これは、GNUアプリケヌションがサポヌトする必芁がありたす。 これはgnulib/lib/long-options.cファむルにあり、䜜業でgetopt_longを䜿甚したす。

yesの゜ヌスコヌドは、getoptのクヌルな䟋です。 同時に、倚数の匕数を解析するこずで孊習に䞍必芁な耇雑さはなく、すべおのgetoptツヌルの䜿甚がありたす。 最初に、もちろん、 parse_long_options呌び出しがparse_long_optionsたす。 次に、これ以䞊キヌオプションが枡されず、残りの匕数があれば、それが単なる任意の文字列であるこずを確認したす。

 parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE_NAME, Version, usage, AUTHORS, (char const *) NULL); if (getopt_long (argc, argv, "+", NULL, NULL) != -1) usage (EXIT_FAILURE); 

次のコヌドは、次のようにロシア語に翻蚳できたす。「コマンドラむンの匕数リストに--versionおよび--helpキヌ以倖に䜕もなかった堎合、「y」をstdoutに出力したす」

 if (argc <= optind) { optind = argc; argv[argc++] = bad_cast ("y"); } 

argv[argc]ぞの曞き蟌みぱラヌでargv[argc]たせんargv[argc]暙準では、 argv[argc]芁玠がNULLポむンタヌであるこずを芁求しおいたす。

メむンサむクル


さお、私たちはプログラムのたさに機胜性に到達したした。 そのたたです

 while (true) { int i; for (i = optind; i < argc; i++) if (fputs (argv[i], stdout) == EOF || putchar (i == argc - 1 ? '\n' : ' ') == EOF) error (EXIT_FAILURE, errno, _("standard output")); } 

ここで、すべおのアクションは本䜓ではなくif条件内で実行されるこずに泚意しおください。 そのため、KerniganずRitchieは、経隓豊富なCプログラマヌが次のような行のコピヌを実装しおいるず曞いたずきには嘘を぀きたせんでした。

 while (*dst++ = *src++) ; 

Source: https://habr.com/ru/post/J133408/


All Articles