エラーの発生を捕らえるには、if文を使わざるをえません。では、エラーはどう処理すればよい でしょうか。このプログラムでは、if文のelseブロックがエラー処理になっていました。
つまり、原作者のやり方で、if文のネストが12−2段の場合は次のようになります。
if( 正常条件 ) { if( 正常条件 ) { 正常処理 } else { エラー処理 } } else { エラー処理 }この方法で段数が増えれば、条件とエラー処理との距離は、どんどん伸びていきます。これは条 件が複雑になったのだからしかたがないと思ってはいけません。こんな方法で書いているからいけ ないのです。では、どうすればよいでしょうか。
もう分かりますよね。ifの条件が「真」のとき、エラーになるようにすれば、ifの直後のブロッ クが対応するエラー処理となります。したがって、上の例は次のようになります。
if( エラー条件 ) { エラー処理 } if( エラー条件 ) { エラー処理 } 正常処理この方法では、条件判定がいくら増えてもネストは深くなりません。エラー処理の部分は、その 場で処理してから return するか、関数の終りに用意した共通エラー処理部にgoto文で飛ばすなど の方法を取ります。
処理内容には一切変更を加えず、エラー判定直後にエラー処理するように変更したものがリスト 12−2です。ネストが深くなっていないことが分かるでしょう。コメントを加えましたが、正常 処理の流れが、上から下へ、淡々と流れていることが分かるでしょう。ネストが深くならないので、 「つまらない」感じを与えますが、平易なことが一番肝心なのです。
リスト12−2 修正版(その1) エラー処理を先にする |
1 int file_copyfile( filed, files, job, page, comment ) 2 char *filed; /* 複写先ファイル名 */ 3 char *files; /* 複写元ファイル名 */ 4 char *job; /* 複写先JOB名 */ 5 char *page; /* 複写先頁番号 */ 6 char *comment; /* 複写先コメント */ 7 { 8 int ret; 9 int fhs, fhd; 10 int size, rsize, wsize; 11 char *buff = NULL; 12 int pos; 13 SYS_PAGE_HEAD head; 14 BASE_INFO destbase; 15 /* 複写先ファイルの存在確認 */ 16 fhd = open( filed, O_RDONLY ); 17 if( fhd != -1 ) { 18 close( fhd ); 19 ret = notice_prompt( master_menu_panel, NULL, 20 NOTICE_MESSAGE_STRINGS, 21 SYS_MESSAGE_SAVE_OWRITE1, 22 SYS_MESSAGE_SAVE_OWRITE2, 23 NULL, 24 NOTICE_BUTTON_YES, SYS_TEXT_YES, 25 NOTICE_BUTTON_NO, SYS_TEXT_NO, 26 NULL ); 27 if ( ret != NOTICE_YES ) 28 return -2; 29 } 30 /* 複写元ファイルのオープン */ 31 ret = NORMAL; 32 fhs = open( files, O_RDONLY ); 33 if( fhs == -1 ) { 34 notice_prompt( master_menu_panel, NULL, 35 NOTICE_MESSAGE_STRINGS, 36 SYS_MESSAGE_FILEOPENS, 37 SYS_MESSAGE_FILECOPYE, 38 NULL, 39 NOTICE_BUTTON_YES, SYS_TEXT_CHECK, 40 NULL ); 41 return ERROR; 42 } 43 /* 複写先ファイルのオープン */ 44 fhd = open( filed, O_CREAT | O_TRUNC | O_WRONLY, 00660 ); 45 if( fhd == -1 ) { 46 close( fhs ); 47 notice_prompt( master_menu_panel, NULL, 48 NOTICE_MESSAGE_STRINGS, 49 SYS_MESSAGE_FILEOPEND, 50 SYS_MESSAGE_FILECOPYE, 51 NULL, 52 NOTICE_BUTTON_YES, SYS_TEXT_CHECK, 53 NULL ); 54 return ERROR; 55 } 56 /* ファイルコピーバッファの確保 */ 57 size = KB_SIZE; 58 buff = malloc( size ); 59 if ( buff == NULL ) { 60 notice_prompt( master_menu_panel, NULL, 61 NOTICE_MESSAGE_STRINGS, 62 SYS_MESSAGE_FILEMEM, 63 SYS_MESSAGE_FILECOPYE, 64 NULL, 65 NOTICE_BUTTON_YES, SYS_TEXT_CHECK, 66 NULL ); 67 ret = ERROR; 68 goto finish; 69 } 70 71 for(;;) { 72 rsize = read( fhs, buff, size ); /* ファイル読み込み */ 73 if( rsize == 0 ) /* ファイル終端 */ 74 break; 75 if( rsize == -1 ) { /* 読み込みエラー */ 76 notice_prompt( master_menu_panel, NULL, 77 NOTICE_MESSAGE_STRINGS, 78 SYS_MESSAGE_FILEREAD, 79 SYS_MESSAGE_FILECOPYE, 80 NULL, 81 NOTICE_BUTTON_YES, SYS_TEXT_CHECK, 82 NULL ); 83 ret = ERROR; 84 break; 85 } 86 wsize = write( fhd, buff, rsize ); /* ファイル書き込み */ 87 if ( wsize != rsize ) { /* 書き込みエラー */ 88 notice_prompt( master_menu_panel, NULL, 89 NOTICE_MESSAGE_STRINGS, 90 SYS_MESSAGE_FILEWRITE, 91 SYS_MESSAGE_FILECOPYE, 92 NULL, 93 NOTICE_BUTTON_YES, SYS_TEXT_CHECK, 94 NULL ); 95 ret = ERROR; 96 break; 97 } 98 } 99 free( buff ); 100 /* 複写元ファイルの先頭へ */ 101 pos = lseek( fhs, 0, SEEK_SET ); 102 if ( pos != 0 ) { 103 notice_prompt( master_menu_panel, NULL, 104 NOTICE_MESSAGE_STRINGS, 105 SYS_MESSAGE_FILEREAD, 106 SYS_MESSAGE_FILECOPYE, 107 NULL, 108 NOTICE_BUTTON_YES, SYS_TEXT_CHECK, 109 NULL ); 110 ret = ERROR; 111 goto finish; 112 } 113 /* ヘッダー部読み込み */ 114 size = read( fhs, &head, sizeof( SYS_PAGE_HEAD ) ); 115 if ( size != sizeof( SYS_PAGE_HEAD ) ) { 116 notice_prompt( master_menu_panel, NULL, 117 NOTICE_MESSAGE_STRINGS, 118 SYS_MESSAGE_FILEREAD, 119 SYS_MESSAGE_FILECOPYE, 120 NULL, 121 NOTICE_BUTTON_YES, SYS_TEXT_CHECK, 122 NULL ); 123 ret = ERROR; 124 goto finish; 125 } 126 /* 変更部分に位置合わせ */ 127 if ( lseek( fhs, head.base_pos, SEEK_SET ) != head.base_pos || 128 lseek( fhd, head.base_pos, SEEK_SET ) != head.base_pos ) { 129 notice_prompt( master_menu_panel, NULL, 130 NOTICE_MESSAGE_STRINGS, 131 SYS_MESSAGE_FILEWRITE, 132 SYS_MESSAGE_FILECOPYE, 133 NULL, 134 NOTICE_BUTTON_YES, SYS_TEXT_CHECK, 135 NULL ); 136 ret = ERROR; 137 goto finish; 138 } 139 /* 変更部分の読み込み */ 140 size = read( fhs, &destbase, head.base_size ); 141 if ( size != head.base_size ) { 142 notice_prompt( master_menu_panel, NULL, 143 NOTICE_MESSAGE_STRINGS, 144 SYS_MESSAGE_FILEREAD, 145 SYS_MESSAGE_FILECOPYE, 146 NULL, 147 NOTICE_BUTTON_YES, SYS_TEXT_CHECK, 148 NULL ); 149 ret = ERROR; 150 goto finish; 151 } 152 /* 変更 */ 153 strncpy( destbase.job_name, job, JOBNAME_LEN ); 154 strncpy( destbase.page_no, page, PAGENO_LEN ); 155 strncpy( destbase.comment, comment,COMMENT_LEN); 156 /* 変更部分の書き込み */ 157 size = write( fhd, &destbase,head.base_size); 158 if ( size != head.base_size ) { 159 notice_prompt( master_menu_panel, NULL, 160 NOTICE_MESSAGE_STRINGS, 161 SYS_MESSAGE_FILEWRITE, 162 SYS_MESSAGE_FILECOPYE, 163 NULL, 164 NOTICE_BUTTON_YES, SYS_TEXT_CHECK, 165 NULL ); 166 ret = ERROR; 167 } 168 169 finish: /* 後始末 */ 170 close( fhs ); 171 if ( close( fhd ) == -1 ) { 172 notice_prompt( master_menu_panel, NULL, 173 NOTICE_MESSAGE_STRINGS, 174 SYS_MESSAGE_FILEWRITE, 175 SYS_MESSAGE_FILECOPYE, 176 NULL, 177 NOTICE_BUTTON_YES, SYS_TEXT_CHECK, 178 NULL ); 179 ret = ERROR; 180 } 181 if ( ret == ERROR ) 182 unlink( filed ); 183 184 return ret; 185 } |
goto文を禁止する人がいますが、「アホなgotoの使い方」をするからいけないのです。エラー処 理など、適正な個所に使うのならどんどん利用し、プログラムを読みやすくできます。
ここまで変更して、やっと人に説明できるプログラムになってきました。リスト12−2のコメ ントを集めると、この関数が何をするかが分かるようになります。
図12−1 コメント(関数仕様書) |
複写先ファイルの存在確認 複写元ファイルのオープン 複写先ファイルのオープン ファイルコピーバッファの確保 for(;;) ファイル読み込み ファイル終端 読み込みエラー ファイル書き込み 書き込みエラー 複写元ファイルの先頭へ ヘッダー部読み込み 変更部分に位置合わせ 変更部分の読み込み 変更 変更部分の書き込み 後始末 |