T プログラミング SPARC List 6 Li st 6 109 : 1 10 : 1 12 : 113 : 第 1 14 : 115 : 116 : 118 : 119 : 120 : 121 : 122 : } ・ 123 : 124 : barcode 8(num, moji) 125 : char n 諞冂 [MOJILEN] : char moj i ロ [ MO 引し EN 」 : 126 : 127 : 128 : char x [ MO 引し EN] : 129 : char xx [ MO 引し EN] : 130 : char kai [MOJILEN] : 131 : char *pt ・ 132 : WORD i; 133 : char num2 [- し ABE し NUM] [MOJ ILEN] : 134 : swi tch (Gbark i (d) { 135 : 136 : case 5 : 137 : case 15 : 138 : Gketa = 12 : 139 : break; 140 : case 6 : 141 : case 16 : 142 : Gketa ニ 7 : 143 : break : 144 : 145 : 146 : 147 : 148 : 149 : strcpy(num[i], number ) : strcpy(moji [i], samen 十十 if( samen % Gpsamen) { : Gkasan; 150 : 151 : 152 : 153 : barcode_0 (num2, moj i ) : 154 : return ( の : 155 : 156 : } 157 : 158 : barcode_O(num,moji charr 00j i [MOJ ILENj : 160 : 161 : switch(Gbarkind) { 162 : 163 : case 1 : barl nw7 (num, moj i , NONHEX) : 164 : 165 : break : 166 : case 2 : 167 : barInw7(num,moji, HEX) : 168 : break : 169 : case 3 : 170 : barl cd39 (num, moj i ) : 171 : break : 172 : case 4 : 173 : barl cd39c (num. moj i ) : 174 : break ; 175 : case 5 : barl jan (num, moj i ) : 176 : 177 : break : 178 : case 6 : 179 : barl jans (num, moj i) : 180 : break : 181 : case 7 : 182 : bar1205(num, moji) : 183 : break; 184 : case 8 : 185 : barl 205C (num, moj i ) : 186 : break : 187 : 188 : return ( 0 ) : 189 : } right(kai, pt, Gketa strcpy(num2[i], kai barcode_8 (num, moj i) : form_feed ( ) : fcl ose (stdprn) : return ( 0 ) : fortom(), 0 , Gnppg) { strcpy(), num[i]) : strcpy (xx, " 000000000000000 " pt=strcat(xx, x) : 置にタイトルおよび文字列を表示する。 ・ DBOXSTR. SUN(List 5 ) ポックス型の罫線を引き , 指定の位置 に文字列 strl を表示する関数 pw text は , 文字列を表示する SunView 独 自の関数て、ある。 ・ BARLASE6. SUN(List 6 ) バーコードのプリント関連の関数 この関数は , デバイスファイルをオープ ンし , プリンタを直接制御している。本関 数の、、ミソ〃は叩 en と fdopen を使用している 点にある。まず , open 関数よりファイルハ ンドルを得 , それを用いて , ioctl 関数によ る制御を行う。これだけて、は printf などの関 数が使用て、きないのて、 , fdopen を使用して オープンしなおしている。 marknws の操作方法 marknws は , SPARC LT て開発したもの て、あるが , 基本的には SunView のみを使用 しているのて、 , 多くの SUN ワークステーシ ョン上て、稼働可能なはずてある ( 実際にソフ トバンクの SUN ワークステーションにイン ストールして操作したところ問題なく動い た。 SUN ワークステーションて注意すべき 点は , 以下に述べるプリンタデバイスを正 SPARC LT プログラミング 87 ・ SELBOXDP. SUN(List 4 ) タイトルを表示しつつ , ポックスを表 示する関数 Gbox から x 座標および y 座標を得 , その位 Fig. 3 ma 「 knws のメニュー画面 日 、 p “い : 1 ー CASSETTE— d v tt Etl T TA 0
実力養成講座 3 位置についてひと言。本誌 ' 91 年 4 月号の特集 「最新デバッグ学入門」て三田典玄さんも述 べられていますが , K & R などては main ( ) が ソースの頭に出てきて , その下に main ( ) か ら直接的間接的に呼び出される関数の定義 が続く , というトップダウン型の記述がさ れていました。 古い C コンパイラ ( UNIX のポータブル C コ ンパイラ ) は型チェックがないに等しい代物 てあったため , トップダウン的な手法が行 いやすく , また自然て、した。 しかし , C 十十てはプロトタイプ宣言が必 須てす。プロトタイプ宣言という事項その ものは ANSI C にも採用されていますが , 古 い C との完全な互換性を保っためにいわば推 奨にとどまっているのに対して , C 十十て、は プロトタイプ宣言のない関数を呼び出すと 工ラーになります。 全関数の宣言をへッダファイルにまとめ ておくなどの処置を施しておけば , 古い C の ようなソースの配置もてきます。しかし , 苦労してまてトップダウンにこだわる必要 性はありません。強いていえば , ポトムア ップ型の書き方のほうが , C 十十てはより自 然なコーディングスタイルなのてす。 三田氏はこれを「デバッグのしやすいスタ イル」としています。 C 言語てはデバッグレ ベルの話になってしまうわけてす。それに 対して , C 十十ては「コンパイラに通るか」と いうレベルてあることに注意してください これまてに出てきた新しい機能としては , ・どこでも宣 ・ const 型 ・参照型 ・デフォルト引数 ・インライン関数 ・ for 文の拡張 ( for 初期化文 ) まず main ( ) から見ていきましよう omain mainO ン関数」にスポットが当てられています。 くに「デフォルト引数」「参照引数」「インライ などがおもだったところてす。今回は , と (int argc, char* * argv) というような ( プ ロトタイプ宣言を兼ねた ) 記述法は , ANSI C てすてにおなじみてすね。 ここ Cchar* * としているのは , なにも * 〃という新しい型が導入されたわ けてはありません。ただ , C 十十てはポイン タを宣言するときに , char * p と書くより は char * p というように , * を型名につけ る書き方のほうが好まれる傾向があるのて、 す。 ところが , このスタイルには問題があっ て , 複数の変数をまとめて宣言するときに char * p, q ; などとうつかり書きかねませ ん。これは , char * p, q ; と同じことなの て、 , q はポインタて、はなく char 型になってし まいます。 もっとも , C 十十の基本方針として , 変数 は宣言と同時に初期化することが推奨され ます ( または要求される ) 。たとえばこのプ ログラム全体を通じて , あらゆる変数は宣 言と同時に初期化されています。したがっ て , C 十十プログラマにとっては 1 行 1 宣言が 普通てあり , それを守っているかぎり , の問題はまず顕在化しません。 そういう問題を抱えこんてまて、ヾ char * 式の書き方が好まれるのにはいくつかの根 拠があるのてすが , 最終的には趣味の領域 にすぎません。たとえば Turbo C 十十のマ ニュアルては , char * p や int &a といっ た , 変数名に * や & をつけるスタイルが採用 されています。 Tab ( ) の定義がありますが , 引数のすべてに 出しには引数がありません。少し上に Get 文がありますが , この GetTab() 関数の呼び int tabst 叩 = GetTab( ) ; という初期化 GetTab( ) うだけのことてす。 trap がサンプルをそういうふうに書いたとい とりたてて根拠があるわけてはなく ,Strous もまかりとおっています。しかし , これは NULL という値を直接〃と書くスタイル この手の細かいスタイルの違いていえば , スタートアップ C 十十 デフォルト値が与えられています。つまり GetTab( ) ; とだけ書くと GetTab(), 1 , 8 ) という規定値を与えて呼び出したのと同じ ことになります。 また , 少し下の行ては , GetTab(*argv) という形て , 最初の引数だけを指定して呼 び出しています。ユーザから与えられたコ マンドライン引数を使いますが , 範囲チェ ックに用いられる値や , 無効な値が与えら れた場合のデフォルト値などは規定値を使 うというわけて、す。 こて、は少々オーバにデフォルト引数を 使ってみましたが , 定数の管理を一か所に 閉じ込めたい場合や , 柔軟に範囲チェック などを行いたい場合に有効かもしれません。 また , インライン関数が使われているのて , その場て範囲チェックを行うのと同等の効 率が得られます。 DeTab( ),PutTab(')ÅVne» その上の DeTab ( ) 関数がタブ展開の本体 てす。ファイルから 1 文字ずつ読みこんて , 読みこんだ文字がタプてあれば PutTab( ) 関 数によって展開されます。 タブをスペースに置き換えるには , ヾ現在 のカラム位置〃とヾタブストップが何文字 ごとか〃という情報が必要てす。 PutTab() はこのふたつを引数にとりますが , タブが 展開されると必然的にカラム位置が変化し ます。このため , PutTab ( ) はカラム位置を 参照引数とし , 変化した値を呼び出し元の 関数に通知するようにしました。 C 言語ては ポインタを引数にとることによって同様の 処理をしていましたが , 参照引数のほうが はるかにスマートに解決てきます。 タブによるカラム位置の変化は PutTab ( ) が一手にめんどうをみてくれるのて , カラ ム位置に関して DeTab ( ) 関数が気にしなく てはいけないのは , 改行が出てきたらリセ ットすることくらいてす。 SwitchState ( ) 以上だけてもタブ展開プログラムとして スタートアップ C 十十 123
新い - ト プログラ、グ人門 C プログラマ のための 中島信行 第 1 0 回子プロセスの起動 今回は子プロセスの起動をちょっと変わった形で 取り上けます。プログラム例として , 386 マシン で動作する DOS ー Extender 用のプログラムの起動 を手助けするユーティリティを作成します。機種 依存しないユーティリティなので , 当然 FM-TO WNS でも動作します。 ターンコードを取得しています。 というような使い方をします ( system ( ) 関数 7 spawn ( ) 関数 , spawn. c て、は , コマンド名にフルバス名を の指定においてコマンドの位置て、分けて , system ( ) 関数の作成 指定する必要があるため , PATH を参照し コマンドをフルバス名て、指定する感じて、 て起動する関数を作成すると spawnp. c( 付録 MS-DOS 上の C コンパイラには , 子プロセ す ) 。この関数のポイントは DOS ファンクシ ョン 4B00h(TabIe 1 ) のプログラムのロード ディスク収録 ) のようになります。 spawnp. スを起動するために spawn ( ) 関数が用意され c て、は searchenv( ) 関数 ()S ー C), sear と実行て、す。 Fig. 1 の 8 種類の spawn ( ) 関数 ています ( ちなみに spawn( ) 関数は DOS 専用 chpath( ) 関数 (Turbo C/C 十十 ) を使用して , の関数 <ANSI 仕様て、はありません ) 。 も DOS ファンクション 4B00h を使用して , 子 PATH のリストを参照した検索を行ってい spawn ( ) 関数は引数や PATH を検索するか プロセスを起動しています。 spawn. c て、は DOS ファンクション 4B00h の ます (TabIe 3 ) 。 どうかなどの要因により , 通常 , 8 種類の関 引数をセットアップして , DOS ファンクシ 数 (Fig. 1 ) からなります。 spawnp. c は , spawnpath("chkdsk. exe", 'e: /v") : ョン 4B00h を実行し , 終了後に DOS フアノク これらの関数は機能が豊富なため , かな というような使い方になります。 ション 4Dh(Table 2 ) て、子プロセスからのリ りサイズが大きくなっています。そこて、 , spawn ( ) 関数の中身の解明もかねて簡単な Fig. 1 8 種類の spawn 関数 spawn( ) 関数 (spawn. c, 付録ディスク収録 ) を作成します。 int spawnl(int mode, char * path, char * a 「 gO, int spawnle(int mode, char * path, char * a 「 gO int spawn(cha 「 *path, cha 「 *cmd line) ; int spawnlp(int mode, char * path, char * argO, path は起動するコマンドのフルバス名 int spawnlpe(int mode, char * path, char * argO, int spawnv(int mode, char * path, cha 「 *argv [ ] ) ・ て、 , cmd line はそのプログラムに渡すコマ int spawnve(int mode, char * path, char * argv [ ] , char * *env int spawnvp(int mode, char * path, char * argv [ ] ) ・ ンドラインてす。たとえば , int spawnvpe(int mode, char * path, char * argv [ ] Cha 「 * *env spawn("a:**bin%%chkdsk. exe", "e. / (") : こまて、 76 C MAGAZINE 1991 7
Li st 3 LISt 6 5 : #include く fcntl.h> 6 : #include く string. h> 7 : 8 : # i fdef MSC 9 : #include く stdlib. h 〉 10 : #include く præess. h> 11 : #endif 13 : #ifdef SYSV 14 : #include く sys/ioctl.h> 15 : 日し E *stdprn : 16 : void myerror() : 17 : char *mids(); 18 : #endif 20 : #include "prepmini. h" 21 : #include ” dbarvar5. h ” 22 : #include "barcode2. h ” 23 : #include "pldwn. h" 24 : #include ” lips. h ” 25 : #include "lipsvar. h" 26 : #include ” barvar. h" 28 : #ifdef PROTOTYPE 29 : #include "pldwnvar. h 30 : # i nc lude "barprot. h" 31 : #include ” lipsprot. h ” 32 : #end i f 33 : 34 : 35 : EXTERN short xhaba : 36 : EXTERN short yhaba : 37 : EXTERN short llhaba ; 38 : 39 : #def i ne _LABE し NUM 50 40 : 41 : bar Iase6 (I j i kan, lformat, prtdev) 42 : long 凵 i kan ・ 43 : long lformat : 44 : char *prtdev : 45 : { 46 : char num [ し ABE し NUM] [MOJ ILEN) : 47 : char IIIOJ it-LABE し NUM] CMOJILEN] : 48 : int handle, i : 49 : char jikan[10]; 50 : char format 10 ・ 51 : char number 20 52 : char pnum[20] : 53 : long ⅱ : 54 : POINT p; 55 : uns i gned long i nt samen=O : 56 : if(Gbarkind くニ 0 Ⅱ Gbarkind > 8 ) { 58 : Gbarkind ニ 1 59 : if(Gbai くニ 10 Ⅱ Gbai > 40 ) { 60 : Gba i 62 : 63 : xhaba ニ 40 64 : llhaba : 4 ・ 65 : Gdpi ニ 240 : 66 : 67 : #ifdef SPARCLT if((handle ニ open(prtdev, 0 WRON し Y ) ト 68 : myerror("can ・ t open printer Yn") : 69 : ioctl(handle, RAW) : if((stdprn ニ fdopen(handle, "w") ) = NU しい { myerror("can ・ t fopen printer Yn") : 73 : 75 : #endif 76 : 77 : #ifdef PCUX V if((handle : open("/dev/lp", 0 WRON し Y ) ) = myerror("can ・ t open printer Yn") : 80 : ioctl(handle, し円 OCTHR) : if((stdprn : fdopen(handle, "w") ) = NUL し ) { 82 : myerror("can ・ t fopen printer Yn") : 83 : 84 : 85 : #endif 86 : 87 : 88 : 89 : 90 : 92 : 93 : 94 : 95 : 96 : 98 : 99 : 100 : 101 : 102 : 103 : 104 : 105 : 106 : 107 : 108 : 10 : #include ” dbarvar5. h" 12 : VOid 13 : f i Ⅱ t 叩 2 (canvas_name) 14 : Canvas canvas_name : 16 : PiXWin *pw; 17 : char *homeptr; 18 : char bairitsu[50]; 19 : char barkind[50] : 20 : pw = canvas_pixwin(canvas_name) : 22 : selboxdp(pw, &Gbox[0 , Gsprtdev, " プリンタデパイス ". Gprtdev_tbl) : 23 : 24 : selboxdp(pw, &Gbox[l . Gsprtkind, " プリンタの種類 " , Gprtkind_tbl) : 25 : seIboxdp(pw,&Gbox[2 ,Gsbarkind,"JS—コードの種 M",Gbarkind_tbl); 26 : selboxdp(pw, &Gbox 3 , Gslabel " ラベルの種類 ",Glabelkind_tbl); 27 : selboxdp(pw, &Gbox 4 , Gspaper , ”用紙の種類 ",Gpaper tbl); ・ 28 : selboxdp(pw, &Gbox 5 , GsfiIe " ファイル指定 ",Gfile_€bl); 29 : selboxdp(pw. &Gbox 6 , Gscolumn " 列数 ",Gcolumn_tbl) : 30 : se lboxdp (pw. &GbOX 7 , " 行数 ",Grow tbl); , Gsrow " 横幅 ",Gbai:tbl); selboxdp(pw, &Gbox 8 . Gsbai 32 : selboxdp(pw, &GbOX 9 , Gsbheight " 縦幅” ,Gbheight_tbl) : 33 : selboxdp(pw, &GbOX 10 , GsIeft ," 左余白 ",Gleft_tbl); 34 : selboxdp(pw, &GbOX 11 , Gskaigyo , ”行間 ". Gkaigyo_tbl); 35 : selboxdp(pw, &GbOX 12 , Gsnumrow " 数字行 " , Gnumrow tbl) : , " 桁数 ",Gketa_tbl) : 36 : selboxdp(pw, &Gbox 13 , Gsketa selboxdp(pw. &Gbox 14 . Gsup ," 上余白 ",Gup-tbI) : 38 : selboxdp(pw, &Gbox 15 ,Gscolint ,"パー間 ",GcoIint_tbl); 39 : selboxdp(pw, &GbOX 16 , Gsmoj i row ”文字行 ". Gmojirow tbl); ," 字間隔 ". Gjikan_t61); 40 : selboxdp(pw, &GbOX 17 , Gsj ikan selboxdp(pw. &Gbox 18 , Gsrenpr . " 連続番号印刷 " . Grenpr tb l) : 42 : selboxdp(pw, &GbOX 19 . Gsfilepr " ファイル印刷 ". Gf ⅱ ep 己 tbl) : 43 : se lboxdp (pw, &Gbox 20 ] , Gsd i rectpr, ”問合せ ",Gdirectpr_tbl) : 44 : dbutton(pw, Gbox [ 21 ]. xl , Gtm [ 21 ]. yl, " 終了 " ) ; 45 : 46 : List SELBOXDP. SUN . #define EXTN extern 2 : 3 : #include く suntool/sunview. h> 4 : #include く suntool/canvas. h> 5 : 6 : #include "vartype. h" 7 : #include ” smarknva. h ” 8 : 9 : selboxdp(pw, boxn, selnum, title, str) 10 : Pixwin *pw; 11 : struct GbOXY *boxn; 12 : SHORT selnum; 14 = UCHAR 0 ロ ESMOJILEN ・ 16 : SHORT if( strlen(str[selnum]) ) { dboxstr(pw, boxn->xl, boxn->yl, title, str[selnum] 20 : } LiSt DBOXSTR. SUN 1 : #def i ne EX'I'N extern 2 : 3 : #include く suntool/sunview. h> 4 : #inglude く suntool/canvas. h> 5 : 6 : #include "smarknva. h ” 7 : 8 : dboxstr(pw, x, y, strl, str2) 9 : PlXWln *pw; 10 : int x; int y: 12 : Char *strl : 13 : Char *Str2 : 15 : int boxlength; drawbox(pw, x, y + 5 , str2) : 17 : pw-text(pw, x, y, 円 X_NOT( 円 X_SRC), N 乢し . strl) : 18 : } 0 ・ー O tn 0 E , 0 編し 1 ー E く 0 《 0 X C.21 よ 一十しい 0 一 : ー C 十し NC 0 0 0 ・ー cd ・し + ) 本し 0 よー List BARLASE6. SUN . #def i ne EXTERN extern 2 : 3 : #include "config. h ” 4 : #include く stdio. h> 86 C MAGAZINE 1991 7
00 プロクラミング研究 List 86 : POP bp 88 : 89 : 90 : 92 : 93 : 94 : 96 : 1 : 2 : 3 : 5 : 6 : 8 : 9 : 10 : 13 : 14 : 15 : 16 : } : 17 : 18 : 19 : 20 : 22 : 24 : { 25 : 26 : 27 : 28 : 29 : 30 : 31 : ーコード ト * / 32 : 33 : } 34 : 35 : 36 : { 37 : 38 : 39 : segread( &segs ) : 40 : 41 : 42 : 43 : 44 : } List 3 : 5 : POP POP POP POP POP ret 0 S ー dx CX bx i nterrupt endp 95 : _TEXT endS end start テパイスドライバサンプルプログラム ( devsmp. c ) #include く dos. h> #ifndef MK FP 4 : #define MKZFP(s,o) ((void far *)(((unsigned long) (s) くく 16 ) ー C (unsigned) ( 0 ) ) ) #endif 7 : struct SREQHEAD { unsigned Char unitnum; unsigned char dmy[ 8 ] : status : unsigned unsigned char cmdcode; unsigned Char unitcode; unsigned char cmdlen; unsigned char far *pbrkadr; / * unsigned char far *poption; / * struct SREQHEAD far *preqhead; / * extern unsigned brksiz, / * リクエストヘッダの定義 * / ステータス / * コマンド番号 / * ユニット番号 / * コマンド長 リクエストヘッダアドレス * / / * フリーメモリ開始アドレスオフセット * / オプションスイッチ フリーメモリ開始アドレス * / / * サポートするユニット数 * / 21 : void init(void); 23 : void inthdl( void ) / * 割り込みルーチン / * コマンド番号 switch ( preqhead->cmdcode ) { case 0 / * 初期設定 i n i t ( ) : break; preqhead->status ニ 0X8103 : default / * コマンドエラー * / break; vo i d i n i t ( ) / * 工ラービットを DONE ビット , / * 初期化処理 * / エラ セッ struct SREGS segs; / * セグメントレジスタの取得 * / preqhead->pbrkadr = MK_PP( segs. cs, brksiz ) : / * フリーメモリ開始アドレス / * ユニット数 = 1 preqhead —>unitnum preqhead->status ニ 0X0100 : ストラテジルーチンその 1 preqhead = MK-FP( ES, VOid far strategy() 1 : struct SREQHEAD far *preqhead : / * DONE ビットのセット / * リクエストヘッダアドレス * / の本体は interrupt 関数として ES:BX レジス タからリクエストヘッダのアドレスを取得 します。最初は , ストラテジルーチンを far 関数として記述し , Turbo C の疑似変数を 使用して List 4 , List 5 のように挑戦してみ ましたが , ワークレジスタを使用するコー ドが生成されるため , 最終的にこのような 2 段構成になりました。もっとも , インライ ンアセンプリ言語を使用すれば List 6 のよう に簡単に記述て、きます ( とくに理由はありま せんが , TurboC / C 十十て、はインラインア センプリ言語を使わないようにしました ) 。 割込みルーチン (far 」 nthd い nthdl: List 7 ) Turbo C/C 十十 割り込みルーチンも far リターンする必要 があるため , far 関数て、記述し , 割り込みル ーチンの本体を呼び出します。 割り込みルーチンの本体 ( inthdl) は全レジ スタを保存する必要があるため , interrupt 関数としています。割り込みルーチンの本 体てはローカルスタックを設定してコマン ド番号に対応した各コマンドの関数を呼び 出しています。 ローカルスタックの設定はタイニィモデ ルて、前提としている , SS=DS 特集 TSR プログラミング研究 53 誤動作してしまいます。幸い Turb 。 C / C 十十 を指していることを前提にしていますから , スタックポインタが Fig. 10 のスタック領域 はいけないことてす。スタックチェックは スタックチェックを行うコードを生成して るため , ひとつ制限が生じます。それは , static 領域をローカルスタックに割り当て ありませんが ) 。 しなければ SS 手 DS のままてもとくに問題は まいます ( もっとも auto 変数をまったく使用 List 8 のような関数呼び出しは誤動作してし 関数が使用て、きなくなります。たとえば , まて、あれば near ポインタを引数に指定する タを切り換えているわけてす。 SS DS のま ーカルスタックに割り当てて , SS : SP レジス るため , DGROUP に属する static 領域を口 の MS ー DOS の内部変数領域を指し示してい ます。しかし , SS : SP レジスタは呼び出し元 した後に DS レジスタを DGROUP にセットし ラがこの関数の入り口て、全レジスタを保存 ます。 interrupt 関数て、あるため , コンパイ という条件を満足させるために必要となり
す。 ROM BIOS や I/O ポートのアクセスを使 用すると機種依存を避けることがて、きませ んが , デバイスドライバを経由すれば機種 依存を回避てきます。 こて紹介する ESC / P プリンタドライバ は , 説明を簡単にするため , PC ー 9801 シリー ズ用とさせていただきます ( 申し訳ありませ 0 escptc. c( 付録ディスク収録 ) は参考文献 [ 1 ] のアセンプラ版から , ・旧 M - PC 互換機への対応 ・ PR201H への対応 ・ ANK 文字を半角漢字に変換するモード などの一部の機能を省略して , かわりに IOCTL 機能を追加した PC ー 9801 版 ESC / P プ リンタドライバて、す。機能の省略はドライ バとしての機能よりも , C 言語て、の記述方法 に焦点を当てるためと筆者の手元には PC- 9801 しかないため確認に手間取るためて、す。 上記の機能を追加することは簡単なのて、 , 気に入らない方は参考文献 [ 1 ] , [ 2 ] を参 照して追加してください 0 また , ESC / P プリンタ以外に対応させる こともさほど難しくありません。 ESC/P プ リンタ以外のプリンタを所有している方は , 挑戦してみてください PJ9801 版日 SC / P リンタドライバの使用法 使用法 : escptc [-En] C-R] スイッチは次のような意味を持ちます。 ① -E1ev-E8 ESC/P プリンタのモード (TabIe 13 ) を指 定します。デフォルトはもっとも印字桁 数の多いモード 6 にしています 常駐を解除する 簡単なヘルプメッセージを表示する テパイスドライバサンプルプログラム (devsmpt. c) 1 : #include く stdio. h> 2 : #include く stdlib. h> 3 : #include く dos. h> 4 : 5 : struct SDEVHEAD { / * キャラクタデバイスデバイスへッダ struct SDEVHEAD far *pnextdev; / * 次のデバイスへッダを指すポインタ 6 : / * 属性 7 : unsigned int uattr; VOid near *strategy ) ( ) : / * ストラテジェントリポインタのポインタ 0 " igned char de " 000 8 3 / * 割り込み工ントリポインタのポインタ 9 : / * キャラクタデバイスファイル名 10 : 12 : 13 : struct SREQHEAD { / * リクエストヘッダの定義 14 : unsigned Char コマンド長 cmdlen; 15 : / * ユニット番号 unsigned Char unitcode; 16 : / * コマンド番号 uns igned Char cmdcode : 17 : uns igned ステータス status: dmy [ 8 ] : 18 : uns igned Char 19 : / * サポートするユニット数 uns igned Char unitnum; 20 : / * 転送アドレス uns igned far *ptrans; char / * 転送バイト数 uns igned count; 22 : uns igned start : 23 : } ; 24 : 25 : void far strategy(void), far inthdl(void), done(void) , cmderr(void) , init(void); 26 : 28 : void sdevhead( void ) / * デバイスへッダ 29 : { emit__( / * 次のデバイスへッダを指すポインタ 30 : unsigned ) 0X8000 , 31 : / * 属性 void ( 0000 , } ) 、 nthdl, ) strategy, / * ストラテジェントリポインタのポインタ 32 : 33 : / * 割り込み工ントリポインタのポインタ 34 : ・ E'/* キャラクタデパイスファイル名 35 : 37 : 38 : struct SREQHEAD far *preqhead 咢 NU しし : / * リクエストヘッダアドレス 39 : 40 : VOid far strategy() / * ストラテジルーチン 42 : asm mov word ptr cs:preqhead, bx: 43 : asm mov word ptr cs:preqhead 十 2, es; 44 : } 45 : 46 : void far inthdl ( ) static void ( *functbl[] ) ( void ) 48 : 49 : 0 初期設定 init, 50 : done, 1 媒体検査 done, BPB 作成 2 52 : cmderr, IOCT し入力 3 53 : 4 入力 ( 入力待ち ) done, 54 : 5 連続非破壊読み込み done, 55 : done, 6 入力ステータス 56 : 7 入力フラッシュ done, 8 出力 done, 58 : 9 出力 & べリファイ done, 59 : / * 10 出力ステータス done, 60 : 1 1 出力バッフアフラッシュ done, / * 1 2 IOCTL 出力 cmderr 62 : 63 : static char newstkC 0X100 ] : / * 常駐時のスタック領域 / * 旧 SS static unsigned oldss; 65 : / * 旧 SP static unsigned oldsp; 66 : push asm 68 : push bx asm 69 : push asm CX push dx asm push asm S ー push di asm push bp asm push ds asm push asm push asm CS 77 : ds asm POP oldss _SS; OIdsp disable(); 80 : SS DS : / * ローカルスタック設定 82 : SP = unsigned ) ( newstk + sizeof( newstk ) ) : enable ) : 84 : if ( preqhead->cmdcode く sizeof( functbl ) / sizeof( functbl [ 0 ] ) ) functbl [ preqhead->cmdcode ] ( ) : / * 各コマンド実行 86 : List A / * 割り込みルーチン 0 ② -R ③ -H 50 C MAGAZINE 1991 7
語具 1 導 て、 , コンパイルスイッチを以下に示すよう に設定し , コンノヾイル後オプジェクトファ イルを読者のライプラリに追加し , 最後に メインプログラムにリンクする。 urbo C Turbo C て、は , コンパイルスイッチを以 下のように設定するとともに , TurboC コ ンパイラの INCLUDE ディレクトリに ,con fig. h として以下の内容のファイルを入れて おく必要がある。また統合環境てはなく , コマンドライン上てコンパイルする。 tcc -v -K -J -w -ml -c ( デバッガを使用 /char を unsigned char にする / 漢字を使用する / 警告あり / ラー ジメモリモード / リンクは行わずコンパ イルのみ行う ) (Turbo C 用 config. h) #define TURBOC #ifndef MSDOS #define MSDOS #endif #ifndef PROTOTYPE #define PROTOTYPE 示 0 足す 用 0 、 16 ℃ 09 0 0 マっ 06-00 っ 011 ・く・ ア マ 、 ,. 0 ド - っ 01 、ノっ 3 , 98 ー 0 E O , 1 っ 0 肥Øコ皿付刻印肥靆 2 1 1 1 0 るつっ朝 っ 0 っ 0 っ 0 4 -4 -4 O 静 0 Ø 111 1111111 っ 0 っ 0 っ 0 っ 0 つっ 0 つなつなっ 0 っ 0 っ 0 っ 0 っ 0 000000 っ 0 っ 0 -4 -44 4 4 -4 4 -4 -4 - -0 -0 -0 【 0 -0 -- 0 【 0 【 05 【 066666666660 ー々ー 7 行ーー々ー々ー 0 ー行ー 7 888 List 2 #endif S-C MS ー C ては , 従来と同様にコンパイルスイ ッチを設定するとともに , MS ー C コンパイラ の INCLUDE ディレクトリに , config. h とし て以下の内容のファイルを入れておく必要 がある。 CL /AL /Od /W3 /Zi /J /c 関数 . c ( ラージメモリモード /CodeView を使用 / 警告レベルは 3/char を unsigned char に する / リンクは行わずコンパイルのみ行 (MS-C 用 config. h) #define MSC #ifndef MSDOS #define MSDOS #endif #ifndef PROTOTYPE 応用 C 言語 99
List SMARKN5. SUN 63 : { 71 : * * / 91 : * * / 84 List 1 209 : 208 : 207 : 201 : 200 : { 199 ・ 198 : 197 : 196 : } 195 : 194 : 193 : 192 : 191 : 190 : 189 : 188 : 187 : 186 : 185 : 184 : 183 : 182 : 181 : 180 : 179 : 178 : 177 : 176 : 174 : 173 : 172 : 170 : 169 : 168 : 167 : 166 : 165 : 164 : 163 : 162 : 161 : 160 : 159 : 158 : 157 : 156 : 155 : 154 : 153 : 152 : 150 : 149 : 148 : 146 : 145 : } 144 : 143 : 142 : 141 : 140 : 139 : 138 : 137 : 136 : 135 : 134 : 133 : 132 : 131 : 130 : 129 : 128 : 127 : 126 : 125 : 124 : 123 : 122 : 121 : 120 : 118 : 117 : 116 : 115 : 1 13 : 112 : 110 : 109 : 108 : 107 : 106 : 105 : = ⅵ ndow-create (pframe, PANEL, 0 ) : panel 3 : 8 : 12 : 22 : 23 : 27 : 29 : 33 : 34 : 35 : 36 : 37 : 38 : 40 : 43 : 44 : 47 : 48 : 49 : 50 : 59 : 60 : 62 : 64 : 65 : 68 : 69 : 74 : 75 : 80 : 82 : 84 : 85 : 86 : 87 : 88 : 89 : 90 : 92 : 93 : 94 : 95 : 96 : 99 : Wind0H ttysw; 46 : PaneI pane12: 45 : Panel pane l, Frame pframe2 : Frame pframe : 42 : static void done_proc2() : 41 : stat i c VO i d done_proc ( ) : 39 : # i ncl ude ” smarknva. h" #include く suntool/tty. h> #include く suntool/panel. h> # i ncl ude く suntoo l/canvas. h> #include く suntool/sunview. h> 32 : EXTERN short Ⅱ haba : 31 : EXT ERN short yhaba : 30 : EXTERN short xhaba : 28 : #include ” barvar. h ” #include "lipsvar. h" 26 : #include "lips. h" 25 : #include "pldwn. h" 24 : # i nc lude ” barcode2. ド #include ” dbarvar5. h ” #include "prepmini. h" 1 : #define EXTN 2 : #define EXTERN 4 : #include "config. h" 5 : #include く stdio. h> 6 : #include く fcntl.h> 7 : #include く string. h> #include く setjmp. h> 9 : #include く signal.h> 10 : #include く sys/ioctl.h> 11 : 日し E *stdprn ・ VO i d myerror ( ) : 13 : char *mids() : 15 : void , 。 t 。に 3 16 : VOid 叩 _proc() : 17 : void home_proc() : 18 : void do command() : 19 : void do-cmd2() ; 20 : void dO cmd3(); ー snum 日 num lpnum ー panel create_ i tem (pane l, PANE し _TEXT, PANEWITEM_X, ATTÜROW(OB: PANE し」 TEM Y, PANE し LABEC STRING, " 開始番号 PANE し :VALUCDI SP し AY_ し ENGTH, 55 , - pane l_create- i tem (pane l, PANE し _TEXT, ATTR_CO し (の . PANE し _ITEM_X, ATTR ROW ( 1 ), PANEL ITEM Y, PANE し :LABEC_STRING, " 終了番号 PANEL_VALUE_DI SPLAY_ し ENGTH, 55 を lkasan - panel_create-item(panel, PANEL TEXT, ATTR CO し ( の . PANE し _ITEM X, PANEL_ITEM-y, ATTÜROW ( 2 ) , PANEL_LABEC_STRING, " 増分 PANE し _VA し UE_DI SP し AY_ し ENGTH, 55 , lpsamen= pane l_create_ i tem (pane l, PANE し _TEXT, ATTÜROW ( 3 : PANE し ITEM X, PANE し -ITEM Y, PANE し :LABEC_STRING, " 同番号回数 PANE し _VALUE_DI SP し AY_ し ENGTH, 55 , : panel_create item(panel, PANEL_TEXT, PANE し _ITEM X, ATTR_CO し ( 0 ) . ATTR ROW ( 4 ) , PANE し ITEM Y, PANELZLABEC_STRING, " 印刷回数 PANE し _VA し UE_DI SP し AY_ し ENGTH, 55 , 147 : pitems2() pane l-create_ i tem (pane l. PANEL_BUTTON, PANEL_LABE し _IMAGE, panel_button_image(panel, "DONE", 5 , の . PAN E し _NOT IFY_PROC , done_proc, window fit height(panel) : window-set(pframe, WIN-SHOW. TRUE, 0 ) : Panel Panel Panel 51 : Panel 52 : Panel 53 : Panel 54 : Panel 55 : Panel 56 : Panel 57 : Panel 58 : Panel i tem i tem i tem i tem i tem i tem i tem item i tem i tem main(argc, Char int ー snum 第 Ⅱ num : lkasan; lprtdev 1 : Iprtdev2 : Iprtdev3 : Iprtdev4 : ldbfnam : lpmfnam : lpsamen : lpnum; argv) argc : **argv : Canvas contro ー i ndOW : PiXWin *pw; setjmp (Gretback の : signaI(SIGINT, sctrlc) : signal (SIGHUP. sctrlc) : signal (SIGTSTP, sctrlc) : initvars() : strcpy ( 引 prtdev, "/dev/ttya" ) : base_frame ニ window_create()U しし FRAME, FRAME_LABE し "markn WS*** MARKN WS verl . 0 * * * " , の : control w i ndow = ⅵ ndow create (base_frame, CANVAS. 引 N_EV ENT PROC, iyproc, N 』 GH ↑ . 200 , pitems() : pitems2() : ttysw = window_create(base_frame, TTY, pframe2= w i ndow-create (base_frame, FRAME, の ; panel 2 = ⅵ ndow-create (pframe2, PANE しの : lprtdev 1 ニ pane l_create_ i tem (pane , PANEL TEXT, PANE し」 TEM X, ATTR_CO し (0) . ATTR ROW( の , PANEL_ITEM Y, PANE し _LABEC_STRING, " ラリンタデバイス 1 PANE しーVAし肥ー田 SP し AY_ し ENGTH. 55 を Iprtdev2= pane l_c reate i tem (panel 2 , PANEL TEXT, ATTR_CO し (0), PANE し _ITEM_X, ATTR ROW(I), PANE し _ITEM Y, PANE し一し ABEC_STRING, " ラ・リンタデバイス 2 PANE し _VA し UE_DISP し AY_ し ENGTH. 55 , Iprtdev3= pane l-create- i ten (pane . PANE し _TEXT. ATTR_CO し (0), PANE し _ITEM_X, PANEL_ITEM Y, ATTR_ROW ( 2 ). PANE しし ABEC STRING," プリンタデパイス 3 PANEIZVALUCDISP し AY_ し ENGTH. 55. Iprtdev4= pane l_create_ i tem (pane . PANE し _TEXT, ATTR_COL ( 0 ) . PANE し _ITEM_X, ATTR ROW (3), PANEL_ITEM_Y, PANE し一し ABEL STRING. " プリンタデバイス 4 PANE し _VALUE_DISP し AY_LENGTH, 55 , ldbfnam = pane l_create_ i tem (pane , PANE し _TEXT, ATTR_CO し (0), PANE し _ITEM_X, ATTR_ROW(4), PANE し _ITEM Y, PANE し - し ABEC_STRING, " データファイル名 PANEL_VA し UE_DI SP し AY_ し ENGTH, 55 , lpmfnam = paneI_create_item(pane12. PANE し _TEXT, PANE し _ITEM_X, ATTR CO し ( 0 ). ATTÜROW(5), PANE し _ITEM Y, PANE し _LABE し STR G , " 六ラメータファイル名 : " PANE し _VALUE_-DI SP し AY_LENGTH. 55 , pane l-create i tem (panel 2 , PANEL BUTTON. PANEC_ し ABEL_IMAGE, panei_button_image(panel, "tnIE". 5.0 ). PANE し _NOTIFY_PRW, done_proc2, window fit height(pane12) : window:set(pframe2, WIN-SHOW. TRUE. の : Ⅲ N_ROWS, 20 第 Ⅲ N_VERTI CA し _SCRO しし BAR. scro Ⅱ bar_create ( の , ⅵ ndow-set (contro l_wi ndow, N_I GNORE_ 円 CK_EVENT. Ⅵ N_UP_EVENTS, の : ⅵ ndow_ma i n_100P (base_frame) : fi llt 叩 2(control_window) : ・ pitems() exit(0); 1 圓・ 101 102 : 103 : 104 : pframe= w i ndow_create (base_frame. FRAME. の : static void ・ done_proc ( ) int status; 202 : char snum 256 203 : char lnum 256 204 : char kasan[256] : 205 : char psamen [ 256 ] : 206 : char pnum [ 256 ] : strcpy(snum . (char *)panel_get_value(lsnum) strcpy(lnum . (char *)panel get_value(llnum) C MAGAZINE 1991 7
各項目について見ていきます。 func,c のリンケージは変更されません。 bar もう少し詳しく見ていきましよう。名前 のリンケージは TabIe 3 のようにして決定さ 規則①は次の場合てす。 は初めて現れたのて , external リンケージに れます。 List 2 の foo. c 内ては , まず printf の なります。 static int i ・ 宣言があります。これには static は指定され 規則④は関数について規定しています。 static void f( ) : ていないため , 規則①はあてはまりません。 己憶クラス指定子 static を明示したファイ List 5 の f00 は関数定義てすが , 記憶クラス 規則② , ③もあてはまらないのて規則④の ルスコープの宣言は , internal リンケージに 指定子がないのて , 規則④によって extern なります。 が指定されたとみなされます。この定義以 規定によって , この宣言は , 規則②はいわゆる通常の変数宣言てす。 前に foo の宣言はありませんから , 規則③ー extern int printf(char * 2 によって f00 は external リンケージを持っこ と解釈されます。すると , 規則③ー2 から printf は external リンケージとなることがわかりま こてすてに f00 が , Char C ; とになります。さて , これらが関数の外て宣言されているとき す。関数 f00 と printf. c の printf の定義もこれ static VOid f00 ( ) : にはファイルスコープとなり , 記憶クラス と同様にして external リンケージとなりま として宣言されている場合にはどうなるて 指定子がありませんから external リンケージ しようか。このときには , まず規則①によ す。 となります って f00 は internal リンケージになります。 また , 関数の宣言 , 定義て、は明示的に exter 規則③は己憶クラスが extern のときのリ そして f00 が定義されるとき , 規則③ー 1 によ n を指定することがてきます。したがって , ンケージを規定しています。 List 5 の例を見 って internal リンケージのままになります。 List 2 を List 3 のように書き換えることがて てください。まず i を宣言しています。 extern 続く static を指定した c の宣言を見てくだ きます。 を指定した i の宣言時には , すてに i は宣言さ 、。ここて、 c は internal リンケージとして 次に internal リンケージてす。 List 4 を見 れていますのて , リンケージは変わらず exter 定義されます。しかし , c はすてに external てください。 foo と bar の宣言にはどちらに nal てす。 リンケージとして宣言されています。した も記憶クラス static がついています。したが 次の func の宣言は , まず static が指定され がって , リンケージが不一致てすから , ANSI って ,TabIe 3 の規則①から , これらは inter ているのて , internal リンケージになりま nal リンケージてあり , そのファイル内ての の規定てはエラーになるはずてす。 す。次の extern 指定された宣言ては i のとき み同じオプジェクトや関数をアクセスする しかし , これまてのコンパイラては , と同様に , リンケージは変更されずに internal れを許しているものが多くあります。これ ことになります。このファイルが他のファ のままてす。次の c の宣言に注意してくださ らのコンパイラては , extern を指定した宣 イルやライプラリとリンクされ , 他のファ いきなり extern を指定して宣言してい 言て、はリンケージを決定せず , 先送りにし イルに external リンケージの bar という名前 ます。このときには規則③ー 2 によって external リンケージを決定てきる宣 ます。その後 , が存在していたとしても , 関数 f00 て、の bar リンケージになります。関数 foo の中の extern 言を認識したときに , 初めて名前にそのリ へのアクセスは , static intbar : への参照 のついた宣言も上の例と同じてす。 i, ンケージを持たせるようになっています。 になります。また , この bar は他のファイル からはアクセスてきません。 リンケージの決定の例 external リンケージても internal リンケー 1 : int i; によって external リンケージになる 2 : extern i nt i : ー 1 で , これも external リンケージ ジてもない名前には , リンケージがありま 8 3 : static void func によって internal リンケージになる 4 : extern VO i d func , せん。これは「リンケージが none てある」と 5 : extern Char C : ー 2 によって external リンケージになる 6 : void f80 , ③ー 2 によって external リンケージになる * / もいいます。リンケージがない名前には , 8 : extern int i : ー 1 で external リンケージ 9 : extern VO i d func ( ) : / * ー 1 で internal リンケージ typedef 名 , ラベル , 関数のパラメータ , プ 10 : extern Char C : -1 で external リンケージ extern i nt bar : -2 で external リンケージになる ロックスコープて extern が指定されていな いオプジェクトの名前などがあります。 14 : static char c; : リンケージが一致しない 工ラー れらは 1 回しか宣言て、きません。 同じファイル内て , 同じ名前に対して異 なるリンケージが指定されたときには「動作 は不定てある」と規定されています。 さらに , リンケージ さらに詳しく説明しましよう 0Table 3 の 94 C MAGAZINE 1991 7 1 三ロ 0 一三ロ List 5 関数の相互参照 1 : extern void fOO 2 : static void bar foo() : 4 : 6 : static VOid fOO() 8 : bar() : List 6 / * bar から呼び出すために宣言しておく * / / * foo を呼び出す / * bar を呼び出す
学 五ロ スは①て宣言された i を扱うことになりま Table 2 リンケージの種類 す。 種類 関数プロトタイプ宣言は ANSI 規格て導入 external された記法てす。この宣言のパラメータは , internal 特別なスコープ ( 関数プロトタイプスコープ ) none を持ちます。これはパラメータの宣言から , 関数のパラメータ宣言の終了を示す〃ま TabIe 3 リンケージの決定 て、の間だけのスコープて、す。 List 1 て、は関数 ファイルスコープのオプジェクトや関数の宣言に記憶クラス指定子 static が指定されている 規則① とき , 名前は internal リンケージになる bar のパラメータ i と c が関数プロトタイプス ファイルスコープのオプジェクトの宣言に記憶クラス指定子がないとき , 名前は external リ コープの例て、す。この中にも i の宣言 ( ② ) が 規則② ンケージになる あります。この場合もプロックスコープと 規則③ オプジェクトや関数の宣言に extern が指定されているとき 同じく , 新たなスコープて、の i の宣言が外側 ③ -1 すでに同名のファイルスコープのオプジェクトや関数が宣言されているなら , リンケ ージは変わらない のスコープの宣言を隠します。したがって , ③ー 2 ③ー 1 にあてはまらないとき , 名前は external リンケージになる ②の i は①とは別のものを表すことになりま 関数宣言に記憶クラス指定子がないとき , extern が指定されたものとしてリンケージを決定 規則④ する す。 規則⑤ それ以外のものはリンケージを持たない ( none リンケージである ) 最後は関数スコープて、す。このスコープ の名前には goto 文のラベルしかありませ 分割コンバイル ん。ラベルは関数内ならばどこに存在して ー f00. c ー もよく , 関数内のどこからて、も goto によっ int printf(char * , void foo() て制御を移すことがてきます。関数 z 。 d には printf("hello world%n") : labell がありますが , これは内側のプロック ー printf. c ー こて、外側のプロック に存在しています。 i nt pr i ntf (char *format, から gotolabell ; によって内側のプロック に飛び込むことがて、きるのに注意してくだ さい。ラベルがプロックスコープてあると , 内側のプロックのラベルは外側のプロック ては参照することがて、きません。 また , List 1 の例ては labell よりも先に goto 文が現れています。つまり前方参照してい るわけて、 , これが許されているのもラベル だけの特徴てす。 リンケージ リンケージは複数回宣言されたオプジェ クト ( 変数 ) や関数の実体を特定するために 使われます。リンケージは Table 2 に示すよ うに 3 種類あります。リンケージというとこ ろからもわかるように , これはプログラム と複数のファイルやライプラリとのリンク と密接な関係があります。 と printf. c というふたつのファイルがあり , リンケージとなり , リンク時にこれらふた これらをリンクする場合てす。関数 printf は external リンケージは , 分割コンパイルや つが同一のものと認識されます。したがって , 関数 f00ての printf の呼び出しは , printf. c< printf. c の中て定義されていますが , f00. c て ライプラリ関数とのリンクなどの複数のフ 定義されている関数 printf に制御を移すこと は宣言されているだけてす。この printf とい ァイル間て , オプジェクトや関数の実体を になります。 う関数名は , f00. c と printf. c の中 <external 特定します。つまり , List 2 のように foo. c 意味 複数のファイルで参照される ひとつのファイル内でのみ参照される プロックスコープの変数や typedef 名など List 2 / * 関数宣言 * / / * 関数呼び出し * / / * 関数定義 関数定義に exte 「 n を明示する extern i nt pr i ntf (char * , extern vo i d foo ( ) printf("hello worldYn") : ー printf. c ーー * / extern i nt pr i ntf (char *format List 3 / * 関数宣言 * / / * 関数呼び出し * / / * 関数定義 * / internal リンケージ List 4 1 : static int bar; 2 : static void foo() 4 : bar 5 : / * internal リンケージ * / / * internal リンケージ * / / * このファイル内の bar * / C 言語雑学講座 93