?? 17.htm
字號:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<meta name="GENERATOR" content="Microsoft FrontPage 3.0">
<title>C:\WINDOWS\Desktop\UnixProg\7.htm</title>
</head>
<body>
<font SIZE="2">
<h1 align="center">第十七章 與PostScript打印機通信 </h1>
<p>17.1 引言 </p>
<p>我們現(xiàn)在開發(fā)一個可以與Postscript打印機通信的程序。PostScript打印機目
</p>
<p>前使用很廣,它一般通過RS-232端口與主機相連。這樣就使得我們有可能使用第十
</p>
<p>一章中的終端I/O函數(shù)。同樣,與PostScript打印機通信是全雙工的,我們發(fā)送數(shù)
</p>
<p>據(jù)給打印機時也要準備好從打印機讀取狀態(tài)消息。這樣,我們又有可能使用12.5節(jié)
</p>
<p>中的I/O多路轉接函數(shù):select 和poll。我們所開發(fā)的這個程序是基于James
Cla </p>
<p>rk 所寫的lprps程序。這個程序和其他一些程序共同組成lprps軟件包,可以在co
</p>
<p>mp.sources.misc新聞組中找到(Volume 21,1991年7月)。 </p>
<p>17.2 PostScript 通信機制 </p>
<p>關于PostScript打印機所需要知道的第一件事就是我們并不是發(fā)送一個文件給
</p>
<p>打印機去打印-我們只是發(fā)送一個PostScript程序給打印機讓它去執(zhí)行。在PostSc
</p>
<p>ript打印機中通常有一個PostScript解釋器來執(zhí)行這個程序,生成輸出的頁面。如
</p>
<p>果PostScript程序有錯誤,PostScript打印機(實際上是PostScript解釋器)返回
</p>
<p>一個錯誤消息,或許還會產生其他輸出。 </p>
<p>下面的PostScript程序在輸出頁面上生成一個熟悉的字符串hello, world。(
</p>
<p>我們這里并不敘述PostScript編程,詳細情況請參見Adobe Systems[1985和1986]
</p>
<p>。我們著重在與PostScript打印機的通信上)。 </p>
<p>%! </p>
<p>/Times-Roman findfont </p>
<p>15 scalefont % point size of 15 </p>
<p>setfont % establish current font </p>
<p>300 350 moveto % x=300 y=350 (position on page) </p>
<p>(hello, world) show % output the string to current page </p>
<p>showpage % and output page to output device </p>
<p>如果我們在PostScript程序中改變setfont 為ssetfont,再把它發(fā)送到PostS </p>
<p>cript打印機,結果是什么也沒有被打印。相反的,我們從打印機得到以下消息:
</p>
<p>%% [ Error: undefined; OffendingCommand: ssetfont ]%% </p>
<p>%% [ Flushing: rest of job (to end-of-file) will be ignored ]%% </p>
<p>這些錯誤消息隨時都可能產生,這也是處理PostScript打印機復雜的地方。我
</p>
<p>們不能只是將整個PostScript程序發(fā)送給打印機后就不管了-我們必須處理這些潛
</p>
<p>在的錯誤消息。(在這一章中,我們所說的"打印機",就是指PostScript解釋器。
</p>
<p>) </p>
<p>PostScript打印機通常通過RS-232串口與主機相連。這就如同終端的連接一樣
</p>
<p>,所以我們在第十一章中的終端I/O函數(shù)在這里也適用。(PostScript打印機也可
</p>
<p>以通過其它方式連接到主機上,例如網(wǎng)絡接口逐漸流行。但目前占主導地位的還是
</p>
<p>串口相連。)圖17.2 顯示了典型的工作過程。一個PostScript程序可以產生兩種
</p>
<p>形式的輸出:通過showpage操作輸出到打印機頁面上,或者通過print操作輸出到
</p>
<p>它的標準輸出(在這里是與主機的串口連接)。 </p>
<p>PostScript解釋器發(fā)送和接受的是七位ASCII字符。PostScript程序可包含所
</p>
<p>有可打印的ASCII字符。一些不可以打印的字符有著特殊的含義(參見圖17.1)。
</p>
<p>圖17.1 從主機發(fā)送到PostScript打印機的特殊字符 </p>
<p>圖17.2 用串口連接與Postscript打印機通信 </p>
<p>PostScript的文件終止符(Control-D)用來同步打印機和主機。我們發(fā)送一
</p>
<p>個PostScript程序到打印機,然后再發(fā)送一個EOF到打印機。當打印機執(zhí)行完Post
</p>
<p>Script程序后,它就發(fā)回一個EOF。 </p>
<p>當PostScript解釋器正在執(zhí)行一個PostScript程序時,我們可以向它發(fā)送一個
</p>
<p>中斷(Control-C)。這通常使正在被打印機執(zhí)行的程序終止。 </p>
<p>狀態(tài)查詢消息(Control-T)會使得打印機返回一個一行的狀態(tài)消息。所有的打印
</p>
<p>機消息都是如下格式: </p>
<p>%% [ key : val ] %% </p>
<p>所有可能出現(xiàn)在狀態(tài)消息中的key: val對,被分號分開。回憶前面例子的返回
</p>
<p>消息: </p>
<p>%% [ Error: undefined; OffendingCommand: ssetfont ]%% </p>
<p>%% [ Flushing: rest of job (to end-of-file) will be ignored ]%% </p>
<p>這個狀態(tài)消息具有這個格式: </p>
<p>%% [status : idle ]%% </p>
<p>除了idle(沒有作業(yè))外,其它狀態(tài)指示還有busy(正在執(zhí)行一個PostScrip
</p>
<p>t程序)、 waiting(正在等待執(zhí)行PostScript程序)、 printing(打印中)、i
</p>
<p>nitializing(初始化)和printing test page(正打印測試頁)。 </p>
<p>現(xiàn)在來考慮PostScript解釋器自己產生的狀態(tài)消息。我們看到以下的消息
</p>
<p>%% [ Error: error ; OffendingCommand : operator ]%% </p>
<p>總共大約會發(fā)生25種不同的錯誤。通常的錯誤有dictstackunderflow,
invalidac </p>
<p>cess, typecheck, 和undefined。這里的operator是產生錯誤的PostScript操作。 </p>
<p>一個打印機的錯誤用以下形式來指示。 </p>
<p>%% [ PrinterError: reason ]%% </p>
<p>這兒的reason一般是Out Of Paper(缺紙)、Cover Open(蓋打開)等錯誤。
</p>
<p>當錯誤發(fā)生時,PostScript解釋器經(jīng)常會發(fā)出另外一個消息 </p>
<p>%% [ Flushing : rest of job (to end-of-file) will be ignored ] %% </p>
<p>我們看一下在特殊字符序列%%[和]%%中的字符串,為了處理這個消息,我們必
</p>
<p>須分析該字符串。一個PostScript程序也可以通過PostScript的print操作來產生
</p>
<p>輸出。這個輸出應當傳給發(fā)送程序給打印機的用戶(雖然這并不是我們的打印程序
</p>
<p>所期望解釋的)。 </p>
<p>圖17.3 列出了PostScript解釋器傳送給主機的特殊字符。 </p>
<p>圖17.3 PostScript解釋器傳送給主機的特殊字符 </p>
<p>17.3 假脫機打印 </p>
<p>我們在本章所開發(fā)的程序通過兩種方式發(fā)送PostScript程序給PostScript打印
</p>
<p>機,單獨的方式或者通過BSD行式打印機假脫機系統(tǒng)。通常使用假脫機系統(tǒng),但提
</p>
<p>供一個獨立的方式也是很有用的,如用于測試等。 </p>
<p>Unix SVR4同樣提供了一個假脫機打印系統(tǒng),在AT&T手冊[1991]
第一部分以l </p>
<p>p開頭的手冊頁中可以找到假脫機系統(tǒng)的詳細資料。Stevens[1990]的第13章詳細說
</p>
<p>明了BSD和pre-SVR4的假脫機系統(tǒng)。我們在這一章中并不著重在假脫機系統(tǒng)上,而
</p>
<p>在于與PostScript打印機的通信。 </p>
<p>在BSD的假脫機系統(tǒng)中,我們以如下形式打印一個文件 </p>
<p>lpr -pps main.c </p>
<p>這個命令發(fā)送文件main.c到名為ps的打印機。如果我們沒有指定-pps的選項,
</p>
<p>那么或者輸出到PRINTER環(huán)境變量對應的打印機上,或者輸出到缺省的lp打印機上
</p>
<p>。所用的打印機參數(shù)可以在/etc/printcap文件中查到。圖17.4是對應一個PostSc
</p>
<p>ript打印機的一項。 </p>
<p>lp|ps:\ </p>
<p>:br#19200:lp=/dev/ttyb:\ </p>
<p>:sf:sh:rw:\ </p>
<p>:fc#0000374:fs#0000003:xc#0:xs#0040040:\ </p>
<p>:af=/var/adm/psacct:lf=/var/adm/pslog:sd=/var/spool/pslpd:\ </p>
<p>:if=/usr/local/lib/psif: </p>
<p>圖17.4 一個PostScript打印機對應的printcap項 </p>
<p>第一行給出了這個項的名稱,ps或者lp。br的值指定了波特率是19200。lp指
</p>
<p>定了該打印機的特殊設備文件路徑名。sf是格式送紙,sh是指打印作業(yè)的開始加入
</p>
<p>一個打印頁頭,rw是指定打印機以讀寫方式打開。如17.2節(jié)中所述,這一項是Pos
</p>
<p>tScript打印機所必須的。 </p>
<p>下面的四個域指定了在舊版本BSD風格的stty結構中需要打開和關閉的位。(
</p>
<p>我們在這里對此進行敘述是因為大多數(shù)使用printcap文件的BSD系統(tǒng)都支持這種老
</p>
<p>式的設置終端方式的方法。在這一章的源程序文件中,我們可以看到如何使用第十
</p>
<p>一章中所述的POSIX.1函數(shù)來設置所有的終端參數(shù)。)首先,fc掩碼清除sg_flags
</p>
<p>元素中的下列值:EVENP和ODDP(關閉了奇偶校驗)、RAW(關閉raw模式)、CRMO
</p>
<p>D(關閉了在輸入輸出中的CR/LF映射)、ECHO(關閉回顯)和LCASE(關閉輸入輸
</p>
<p>出中的大小寫映射)。然后,fs掩碼打開了以下位: CBREAK(一次輸入一個字符
</p>
<p>)和TANDEM(主機產生Control-S,Control-Q流控制)。接著,xc掩碼清除了本地
</p>
<p>模式字中各位。最后,xs掩碼設置了本地模式字中的下列兩位:LDECCTQ(Contro
</p>
<p>l-Q重新開始輸出,Control-S則停止輸出)和LLITOUT(壓縮輸出轉換)。
</p>
<p>af和lf字符串分別指定了記帳文件和日志文件。sd指定了假脫機的目錄,而i
</p>
<p>f指定了輸入過濾器。 </p>
<p>輸入過濾器可被所有的打印文件所激活,格式如下: </p>
<p>filter -n loginname -h hostname acctfile </p>
<p>這里還有幾個可選的參數(shù)(這些參數(shù)有可能被PostScript打印機所忽略)。要
</p>
<p>打印的文件在標準輸入中,打印機(printcap文件中的lp項)設在標準輸出。標準
</p>
<p>輸入也可以是一個管道。 </p>
<p>使用一個PostScript打印機,輸入過濾器首先查詢輸入文件的開始兩個字符,
</p>
<p>確定這個文件是一個ASCII文本文件還是一個PostScript程序。通常的慣例是前兩
</p>
<p>個字符為%!表示這是一個PostScript程序。如果這個文件是PostScript程序,lpr
</p>
<p>ps程序(在下面會詳細討論)就把它發(fā)送到打印機。如果這個文件是文本文件,就
</p>
<p>使用其他程序將它轉換成PostScript程序。 </p>
<p>在printcap文件中提到的psif過濾器是lprps軟件包提供的。在這個包中的te
</p>
<p>xtps可以將文本文件轉換成PostScript程序。圖17.5概略表示了這些程序。
</p>
<p>圖17.5 lprps系統(tǒng)示意圖 </p>
<p>圖中有一個程序psrev沒有表示出來,該程序將PostScript程序生成的頁面反
</p>
<p>轉過來,當PostScript程序打印機在正面而不是在反面打印輸出時,就可以使用此
</p>
<p>程序。 </p>
<p>概述以后,我們就來看一下lprps程序的設計和編碼。 </p>
<p>17.4 源程序 </p>
<p>我們先看main()調用的函數(shù),以及它們是如何與打印機交互作用的。圖17.
</p>
<p>6詳細表明了這種相互作用。圖中第二列標注為"int?",它指定該函數(shù)是否可以通
</p>
<p>過接受SIGINT信號而中斷。第三欄指定了各個函數(shù)的超時設置(以秒為單位)。注
</p>
<p>意,當我們發(fā)送用戶的PostScript程序到打印機時,沒有超時設置。這是因為一個
</p>
<p>PostScript程序可能用任意長的時間來執(zhí)行。函數(shù)get_page行中的"我們的PostSc
</p>
<p>ript程序"是指程序17.9,這個程序是用來記錄當前頁碼的。 </p>
<p>程序17.1列出了頭文件lprps.h。該文件包含在所有的源文件中。該頭文件包
</p>
<p>含了各個源程序所需的系統(tǒng)頭文件,定義了全局變量和全局函數(shù)的原型。
</p>
<p>圖17.6 被主程序調用的函數(shù) </p>
<p>_______________________________________________________________________ </p>
<p>________ </p>
<p>#include <sys/types.h> </p>
<p>#include <sys/time.h> </p>
<p>#include <errno.h> </p>
<p>#include <signal.h> </p>
<p>#include <syslog.h> /* since we're a daemon */ </p>
<p>#include "ourhdr.h" </p>
<p>#define EXIT_SUCCESS 0 /* defined by BSD spooling system */ </p>
<p>#define EXIT_REPRINT 1 </p>
<p>#define EXIT_THROW_AWAY 2 </p>
<p>#define DEF_DEVICE "/dev/ttyb" /* defaults for debug mode */ </p>
<p>#define DEF_BAUD B19200 </p>
<p>/* modify following as a </p>
<p>propriate */ </p>
<p>#define MAILCMD "mail -s \"printer job\" %s@%s < %s" </p>
<p>#define OBSIZE 1024 /* output buffer */ </p>
<p>#define IBSIZE 1024 /* input buffer */ </p>
<p>#define MBSIZE 1024 /* message buffer */ </p>
<p>/* declare global variables */ </p>
<p>extern char *loginname; </p>
<p>extern char *hostname; </p>
<p>extern char *acct_file; </p>
<p>extern char eofc; /* PS end-of-file (004) */ </p>
<p>extern int debug; /* true if interactive (not a daemon) */ </p>
<p>extern int in_job; /* true if sending user's PS job to printer */ </p>
<p>extern int psfd; /* file descriptor for PostScript printer */ </p>
<p>extern int start_page;/* starting page# */ </p>
<p>#include <syslog.h> /* since we're a daemon */ </p>
<p>#include "ourhdr.h" </p>
<p>#define EXIT_SUCCESS 0 /* defined by BSD spooling system */ </p>
<p>#define EXIT_REPRINT 1 </p>
<p>#define EXIT_THROW_AWAY 2 </p>
<p>#define DEF_DEVICE "/dev/ttyb" /* defaults for debug mode */ </p>
<p>#define DEF_BAUD B19200 </p>
<p>/* modify following as a </p>
<p>propriate */ </p>
<p>#define MAILCMD "mail -s \"printer job\" %s@%s < %s" </p>
<p>#define OBSIZE 1024 /* output buffer */ </p>
<p>#define IBSIZE 1024 /* input buffer */ </p>
<p>#define MBSIZE 1024 /* message buffer */ </p>
<p>/* declare global variables */ </p>
<p>extern char *loginname; </p>
<p>extern char *hostname; </p>
<p>extern char *acct_file; </p>
<p>extern char eofc; /* PS end-of-file (004) */ </p>
<p>extern int debug; /* true if interactive (not a daemon) */ </p>
<p>extern int in_job; /* true if sending user's PS job to printer */ </p>
<p>extern int psfd; /* file descriptor for PostScript printer */ </p>
<p>extern int start_page;/* starting page# */ </p>
<p>void msg_init(void); /* message.c */ </p>
<p>void msg_char(int); </p>
<p>void proc_msg(void); </p>
<p>void out_char(int); /* output.c */ </p>
<p>void get_page(int *); /* pagecount.c */ </p>
<p>void send_file(void); /* sendfile.c */ </p>
<p>void block_write(const char *, int); /* tty.c */ </p>
<p>void tty_flush(void); </p>
<p>void set_block(void); </p>
<p>void set_nonblock(void); </p>
<p>void tty_open(void); </p>
<p>_______________________________________________________________________ </p>
<p>________ </p>
<p>程序17.1 lprps.h頭文件 </p>
<p>文件vars.c(程序17.2)定義了全局變量。 </p>
<p>程序17.3是main函數(shù)。因為此程序一般是作為精靈進程運行的,所以main函數(shù)調用
</p>
<p>log_open函數(shù)(見附錄B)。我們不能將錯誤消息寫到標準錯誤上-為此我們使用了
</p>
<p>13.4.2小節(jié)中描述的syslog。 </p>
<p>_______________________________________________________________________ </p>
<p>________ </p>
<p>#include "lprps.h" </p>
<p>char *loginname; </p>
<p>char *hostname; </p>
<p>char *acct_file; </p>
<p>char eofc = '\004'; /* Control-D = PostScript EOF */ </p>
<p>int psfd = STDOUT_FILENO; </p>
<p>int start_page = -1; </p>
<p>int end_page = -1; </p>
<p>int debug; </p>
<p>int in_job; </p>
<p>volatile sig_atomic_t intr_flag; </p>
<p>volatile sig_atomic_t alrm_flag; </p>
<p>enum status status = INVALID; </p>
<p>_______________________________________________________________________ </p>
<p>________ </p>
<p>程序17.2 聲明全局變量 </p>
<p>_______________________________________________________________________ </p>
<p>________ </p>
<p>#include "lprps.h" </p>
<p>static void usage(void); </p>
<p>int </p>
<p>main(int argc, char *argv[]) </p>
<p>{ </p>
<p>int c; </p>
<p>log_open("lprps", LOG_PID, LOG_LPR); </p>
<p>opterr = 0; /* don't want getopt() writing to stderr */ </p>
<p>while ( (c = getopt(argc, argv, "cdh:i:l:n:x:y:w:")) != EOF) { </p>
<p>switch (c) { </p>
<p>case 'c': /* control chars to be passed */ </p>
<p>case 'x': /* horizontal page size */ </p>
<p>case 'y': /* vertical page size */ </p>
<p>case 'w': /* width */ </p>
<p>case 'l': /* length */ </p>
<p>case 'i': /* indent */ </p>
<p>break; /* not interested in these */ </p>
<p>case 'd': /* debug (interactive) */ </p>
<p>debug = 1; </p>
<p>break; </p>
<p>case 'n': /* login name of user */ </p>
<p>loginname = optarg; </p>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -