📄 17.htm
字号:
<p>break; </p>
<p>case 'h': /* host name of user */ </p>
<p>hostname = optarg; </p>
<p>break; </p>
<p>case '?': </p>
<p>log_msg("unrecognized option: -%c", optopt); </p>
<p>usage(); </p>
<p>} </p>
<p>} </p>
<p>if (hostname == NULL || loginname == NULL) </p>
<p>usage(); /* require both hostname and loginname */ </p>
<p>if (optind < argc) </p>
<p>acct_file = argv[optind]; /* remaining arg = acct file */ </p>
<p>if (debug) </p>
<p>tty_open(); </p>
<p>if (atexit(close_mailfp) < 0) /* register func for exit() */ </p>
<p>log_sys("main: atexit error"); </p>
<p>get_status(); </p>
<p>get_page(&start_page); </p>
<p>send_file(); /* copies stdin to printer */ </p>
<p>get_page(&end_page); </p>
<p>do_acct(); </p>
<p>exit(EXIT_SUCCESS); </p>
<p>} </p>
<p>static void </p>
<p>usage(void) </p>
<p>{ </p>
<p>log_msg("lprps: invalid arguments"); </p>
<p>exit(EXIT_THROW_AWAY); </p>
<p>} </p>
<p>_______________________________________________________________________ </p>
<p>________ </p>
<p>程序17.3 main函数 </p>
<p>然后处理命令行参数,很多参数会被PostScript打印机所忽略。我们使用-d标
</p>
<p>志指示这个程序是交互运行,而不是作为精灵进程。如果设置了这个标志,我们需
</p>
<p>要初始化终端模式(tty_open)。然后我们将函数close_mailfp指定为退出处理程
</p>
<p>序。 </p>
<p>我们就可以调用在图17.6中提到的函数:取得打印机状态保证它是就绪的(ge
</p>
<p>t_status),得到打印机的起始页码(get_page),发送文件(PostScript程序)到
</p>
<p>打印机(send_file),得到打印机的结束页码(get_page),写记帐记录(do_acct),
</p>
<p>然后终止。 </p>
<p>文件acct.c定义了函数do_acct(程序17。4)。它在main函数的结尾处被调用,
</p>
<p>用来写下记帐记录。记帐文件的路径和名字从printcap文件中的相应记录项(图1
</p>
<p>7.4)获得,并作为命令行的最后一个参数。 </p>
<p>_______________________________________________________________________ </p>
<p>________ </p>
<p>#include "lprps.h" </p>
<p>/* Write the number of pages, hostname, and loginname to the </p>
<p>* accounting file. This function is called by main() at the end </p>
<p>* (if all was OK, by printer_flushing(), and by handle_intr() if </p>
<p>* an interrupt is received. */ </p>
<p>void </p>
<p>do_acct(void) </p>
<p>{ </p>
<p>FILE *fp; </p>
<p>if (end_page > start_page && </p>
<p>acct_file != NULL && </p>
<p>(fp = fopen(acct_file, "a")) != NULL) { </p>
<p>fprintf(fp, "%7.2f %s:%s\n", </p>
<p>(double)(end_page - start_page), </p>
<p>hostname, loginname); </p>
<p>if (fclose(fp) == EOF) </p>
<p>log_sys("do_acct: fclose error"); </p>
<p>} </p>
<p>} </p>
<p>_______________________________________________________________________ </p>
<p>________ </p>
<p>程序17.4 do_acct函数 </p>
<p>从历史上看,所有的BSD打印过滤器都使用%7.2f的printf格式,把输出的页数
</p>
<p>写到记帐文件中。这样就允许光栅设备不使用页数,而以英尺为单位报告输出长度
</p>
<p>。 </p>
<p>下面一个文件是tty.c(程序17.5),它包含了所有的终端I/O函数。它们调用
</p>
<p>我们在第三章中提到的函数(fcntl, write和open)和第11章中的POSIX..1终端函
</p>
<p>数(tcflush, tcgetattr, tcsetattr, cfsetispeed 和cfsetospeed)。如果我们 </p>
<p>允许发生写阻塞,那么要调用block_write函数。如果我们不希望发生阻塞,则调
</p>
<p>用set_nonblock函数,然后再调用read或者write函数。因为PostScript是一个全
</p>
<p>双工的设备,打印机有可能发送数据回来(例如出错消息等),所以我们不希望阻
</p>
<p>塞一个方向的写操作。如果打印机发送错误消息时,我们正因向其发送数据而处于
</p>
<p>阻塞状态,则会出现死锁。 </p>
<p>一般是核心为终端输入和输出进行缓冲,所以如果发生错误,我们可以调用t
</p>
<p>ty_flush来刷清输入和输出队列。 </p>
<p>如果以交互方式运行该程序,那么main函数将调用函数tty_open。我们需要把
</p>
<p>终端设为非规范模式,设置波特率和其他一些终端标志。注意各种PostScript打印
</p>
<p>机的这些设置并不都一样。检查你的打印机手册确定它的设置。(数据的位数可能
</p>
<p>是7位或8位,起始位、停止位的数目以及奇偶校验等都可能因打印机而异。)
</p>
<p>_______________________________________________________________________ </p>
<p>_______ </p>
<p>#include "lprps.h" </p>
<p>#include <fcntl.h> </p>
<p>#include <termios.h> </p>
<p>static int block_flag = 1; /* default is blocking I/O */ </p>
<p>void </p>
<p>set_block(void) /* turn off nonblocking flag */ </p>
<p>{ /* called only by block_write() below */ </p>
<p>int val; </p>
<p>if (block_flag == 0) { </p>
<p>if ( (val = fcntl(psfd, F_GETFL, 0)) < 0) </p>
<p>log_sys("set_block: fcntl F_GETFL error"); </p>
<p>val &= ~O_NONBLOCK; </p>
<p>if (fcntl(psfd, F_SETFL, val) < 0) </p>
<p>log_sys("set_block: fcntl F_SETFL error"); </p>
<p>block_flag = 1; </p>
<p>} </p>
<p>} </p>
<p>void </p>
<p>set_nonblock(void) /* set descriptor nonblocking */ </p>
<p>{ </p>
<p>int val; </p>
<p>if (block_flag) { </p>
<p>if ( (val = fcntl(psfd, F_GETFL, 0)) < 0) </p>
<p>log_sys("set_nonblock: fcntl F_GETFL error"); </p>
<p>val |= O_NONBLOCK; </p>
<p>if (fcntl(psfd, F_SETFL, val) < 0) </p>
<p>log_sys("set_nonblock: fcntl F_SETFL error"); </p>
<p>block_flag = 0; </p>
<p>} </p>
<p>} </p>
<p>void </p>
<p>block_write(const char *buf, int n) </p>
<p>{ </p>
<p>set_block(); </p>
<p>if (write(psfd, buf, n) != n) </p>
<p>log_sys("block_write: write error"); </p>
<p>} </p>
<p>void </p>
<p>tty_flush(void) /* flush (empty) tty input and output queues */ </p>
<p>{ </p>
<p>if (tcflush(psfd, TCIOFLUSH) < 0) </p>
<p>log_sys("tty_flush: tcflush error"); </p>
<p>} </p>
<p>void </p>
<p>tty_open(void) </p>
<p>{ </p>
<p>struct termios term; </p>
<p>if ( (psfd = open(DEF_DEVICE, O_RDWR)) < 0) </p>
<p>log_sys("tty_open: open error"); </p>
<p>if (tcgetattr(psfd, &term) < 0) /* fetch attributes */ </p>
<p>log_sys("tty_open: tcgetattr error"); </p>
<p>term.c_cflag = CS8 | /* 8-bit data */ </p>
<p>CREAD | /* enable receiv </p>
<p>r */ </p>
<p>CLOCAL; /* ignore modem </p>
<p>tatus lines */ </p>
<p>/* no pa </p>
<p>ity, 1 stop bit */ </p>
<p>term.c_oflag &= ~OPOST; /* turn off post processing */ </p>
<p>term.c_iflag = IXON | IXOFF | /* Xon/Xoff flow control */ </p>
<p>IGNBRK | /* ignore breaks </p>
<p>*/ </p>
<p>ISTRIP | /* strip input t </p>
<p>7 bits */ </p>
<p>IGNCR; /* ignore receiv </p>
<p>d CR */ </p>
<p>term.c_lflag = 0; /* everything off in local flag: </p>
<p>disables canonical mo </p>
<p>e, disables </p>
<p>signal generation, di </p>
<p>ables echo */ </p>
<p>term.c_cc[VMIN] = 1; /* 1 byte at a time, no timer */ </p>
<p>term.c_cc[VTIME] = 0; </p>
<p>cfsetispeed(&term, DEF_BAUD); </p>
<p>cfsetospeed(&term, DEF_BAUD); </p>
<p>if (tcsetattr(psfd, TCSANOW, &term) < 0) /* set attributes */ </p>
<p>log_sys("tty_open: tcsetattr error"); </p>
<p>} </p>
<p>_______________________________________________________________________ </p>
<p>_______ </p>
<p>程序17.5 终端函数 </p>
<p>这个程序处理两个信号:SIGINT和SIGALRM。处理SIGINT是对BSD假脱机系统调
</p>
<p>用的任何一种过滤器的要求。如果打印机作业被lprm(1)命令删除,那么这个信号
</p>
<p>被发送给过滤器。我们使用SIGALRM来设置超时,对这两个信号用类似的方式处理
</p>
<p>:我们提供了set_XXX函数来建立信号处理器,clear_XXX函数来取消这个信号处理
</p>
<p>器。如果有信号传送给这个进程,信号处理器在设置一个全局的标记intr_flag和
</p>
<p>alrm_flag后返回。程序的其它部分可在适当的时间来检测这些标记。有一个明显
</p>
<p>的时间是在I/O函数返回错误EINTR时,该程序然后调用handle_intr或者handle_a
</p>
<p>lrm来处理这种情况,调用signal_intr函数(程序10.13)来中断一个慢速的系统
</p>
<p>调用。程序17.6是处理SIGINT信号的interrupt.c文件。 </p>
<p>当一个中断发生时,我们必须发送PostScript的中断字符(Control-C)给打
</p>
<p>印机,接着发送一个文件终止符(EOF)。这通常引起PostScript解释器终止它正在
</p>
<p>解释的程序。然后我们等待从打印机返回的EOF(我们稍后将描述proc_upto_eof函
</p>
<p>数)。我们读取最后的页码,写下记帐记录,然后就可以终止了。
</p>
<p>_______________________________________________________________________ </p>
<p>______ </p>
<p>#include "lprps.h" </p>
<p>static void </p>
<p>sig_int(int signo) /* SIGINT handler */ </p>
<p>{ </p>
<p>intr_flag = 1; </p>
<p>return; </p>
<p>} </p>
<p>/* This function is called after SIGINT has been delivered, </p>
<p>* and the main loop has recognized it. (It not called as </p>
<p>* a signal handler, set_intr() above is the handler.) */ </p>
<p>void </p>
<p>handle_intr(void) </p>
<p>{ </p>
<p>char c; </p>
<p>intr_flag = 0; </p>
<p>clear_intr(); /* turn signal off */ </p>
<p>set_alrm(30); /* 30 second timeout to interrupt printer */ </p>
<p>tty_flush(); /* discard any queued output */ </p>
<p>c = '\003'; </p>
<p>block_write(&c, 1); /* Control-C interrupts the PS job */ </p>
<p>block_write(&eofc, 1); /* followed by EOF */ </p>
<p>proc_upto_eof(1); /* read & ignore up through EOF */ </p>
<p>clear_alrm(); </p>
<p>get_page(&end_page); </p>
<p>do_acct(); </p>
<p>exit(EXIT_SUCCESS); /* success since user lprm'ed the job */ </p>
<p>} </p>
<p>void </p>
<p>set_intr(void) /* enable signal handler */ </p>
<p>{ </p>
<p>if (signal_intr(SIGINT, sig_int) == SIG_ERR) </p>
<p>log_sys("set_intr: signal_intr error"); </p>
<p>} </p>
<p>void </p>
<p>clear_intr(void) /* ignore signal */ </p>
<p>{ </p>
<p>if (signal(SIGINT, SIG_IGN) == SIG_ERR) </p>
<p>log_sys("clear_intr: signal error"); </p>
<p>} </p>
<p>_______________________________________________________________________ </p>
<p>______ </p>
<p>程序17.6 处理中断信号的interrupt.c文件 </p>
<p>图17.6写明了哪些函数设置超时时间。我们只是在以下情况下设置超时:查询
</p>
<p>打印机状态(get_status)、读取打印机的页码(get_page)或者当我们正中断打印机
</p>
<p>时(handle_intr)。如果发生了超时,我们只需要记录下错误,过一段时间后终
</p>
<p>止。程序17.7是alarm.c文件。 </p>
<p>_______________________________________________________________________ </p>
<p>______ </p>
<p>#include "lprps.h" </p>
<p>static void </p>
<p>sig_alrm(int signo) /* SIGALRM handler */ </p>
<p>{ </p>
<p>alrm_flag = 1; </p>
<p>return; </p>
<p>} </p>
<p>void </p>
<p>handle_alrm(void) </p>
<p>{ </p>
<p>log_ret("printer not responding"); </p>
<p>sleep(60); /* it will take at least this long to warm up */ </p>
<p>exit(EXIT_REPRINT); </p>
<p>} </p>
<p>void /* Establish the signal handler and set the alarm. */ </p>
<p>set_alrm(unsigned int nsec) </p>
<p>{ </p>
<p>alrm_flag = 0; </p>
<p>if (signal_intr(SIGALRM, sig_alrm) == SIG_ERR) </p>
<p>log_sys("set_alrm: signal_intr error"); </p>
<p>alarm(nsec); </p>
<p>} </p>
<p>void </p>
<p>clear_alrm(void) </p>
<p>{ </p>
<p>alarm(0); </p>
<p>if (signal(SIGALRM, SIG_IGN) == SIG_ERR) </p>
<p>log_sys("clear_alrm: signal error"); </p>
<p>alrm_flag = 0; </p>
<p>} </p>
<p>_______________________________________________________________________ </p>
<p>______ </p>
<p>程序17.7 处理超时的alarm.c文件 </p>
<p>程序17.8是函数get_status,这个函数由main函数调用。它发送一个Control
</p>
<p>-T到打印机以获取打印机的状态消息。打印机回送一行消息。如果我们接到的消息
</p>
<p>是: </p>
<p>%%[ status : idle]%% </p>
<p>这意味着打印机准备好执行一个新的作业。这个消息被函数proc_some_input
</p>
<p>读取和处理(下面我们会讨论这个函数)。 </p>
<p>_______________________________________________________________________ </p>
<p>______ </p>
<p>#include "lprps.h" </p>
<p>/* Called by main() before printing job. </p>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -