天天看點

[apue] 一個檢視目前終端标志位設定的小工具

與終端參數相關的一個查詢指令,比系統提供的展示内容更豐富哦

話不多說,先看運作效果:

>./term
input flag 0x00006d02
    BRKINT
    ICRNL
    IMAXBEL
    IXANY
    IXON
output flag 0x00000005
    ONLCR
    OPOST
control flag 0x000004bf
    CREAD
    CSIZE
    CS6
    CS7
    CS8
    HUPCL
local flag 0x00008a3b
    ECHO
    ECHOE
    ECHOK
    ICANON
    IEXTEN
    ISIG
input control char array size 32
    cc[VDISCARD=13] = 15 (CTRL+O)
    VDSUSP not defined
    cc[VEOF=4] = 4 (CTRL+D)
    cc[VEOL=11] = 255 (CTRL+?)
    cc[VEOL2=16] = 255 (CTRL+?)
    cc[VERASE=2] = 127 (CTRL+�)
    VERASE2 not defined
    cc[VINTR=0] = 3 (CTRL+C)
    cc[VKILL=3] = 21 (CTRL+U)
    cc[VLNEXT=15] = 22 (CTRL+V)
    cc[VQUIT=1] = 28 (CTRL+\)
    cc[VREPRINT=12] = 18 (CTRL+R)
    cc[VSTART=8] = 17 (CTRL+Q)
    VSTATUS not defined
    cc[VSTOP=9] = 19 (CTRL+S)
    cc[VSUSP=10] = 26 (CTRL+Z)
    cc[VWERASE=14] = 23 (CTRL+W)
      

衆所周知,通過 tcgetattr 接口與 termios 結構體,我們可以擷取一個終端裝置的設定資訊:

struct termios
{
    tcflag_t c_iflag;       /* input mode flags */
    tcflag_t c_oflag;       /* output mode flags */
    tcflag_t c_cflag;       /* control mode flags */
    tcflag_t c_lflag;       /* local mode flags */
    cc_t c_cc[NCCS];        /* control characters */
};      

主要是各種類型的标志位,雖然你可以将它們列印出來,但是一眼望去,這些數字是什麼意思,還要查對應平台的 man 手冊。

這個工具可以将二進制的标志位,翻譯為人類可以讀懂的常量宏,例如上面的輸出中,可以看到輸入标志位打開了 ICRNL 與 IXON 兩個标志位,

對應的含義分别是“将輸入的CR轉換為NL”、“使啟動/停止輸出控制流起作用”。

看這段輸出也許你已經想到了代碼的實作,就是挨個常量宏嘗試呗,這有啥難的。

不錯,但是考慮到不同平台上定義的宏不一緻,有時增加一兩個宏可能還需要修改源代碼,這是多麼痛苦的事啊!

這個小工具就解決了這個痛點,你可以在配置檔案中指定要測試的宏名稱,然後 make 一下就可以啦~~~

iflag.sym

BRKINT
ICRNL
IGNBRK
IGNCR
IGNPAR
IMAXBEL
INLCR
INPCK
ISTRIP
IUCLC
IXANY
IXOFF
IXON
PARMRK
      

oflag.sym

BSDLY
CMSPAR
CRDLY
FFDLY
NLDLY
OCRNL
OFDEL
OFILL
OLCUC
ONLCR
ONLRET
ONOCR
ONOEOT
OPOST
OXTABS
TABDLY
VTDLY
      

cflag.sym

CBAUDEXT
CCAR_OFLOW
CCTS_OFLOW
CDSR_OFLOW
CDTR_IFLOW
CIBAUDEXT
CIGNORE
CLOCAL
CREAD
CRTSCTS
CRTS_IFLOW
CRTSXOFF
CSIZE
CSTOPB
HUPCL
MDMBUF
PARENB
PAREXT
PARODD
      

lflag.sym

ALTWERASE
ECHO
ECHOCTL
ECHOE
ECHOK
ECHOKE
ECHONL
ECHOPRT
EXTPROC
FLUSHO
ICANON
IEXTEN
ISIG
NOFLSH
NOKERNINFO
PENDIN
TOSTOP
XCASE
      

其實這裡是用 awk 讀取配置檔案自動生成 c 語言的代碼來實作的:

print_flag.awk

1 #! /bin/awk -f
 2 # usage: print_flag.awk -v FUNC_NAME=xxx -v MACRO_FILE=xxx
 3 # i.e.: print_flag.awk -v FUNC_NAME=output -v MACRO_FILE=oflag.sym
 4 BEGIN {
 5 printf("#include \"../apue.h\"\n")
 6 printf("#include <termios.h>\n")
 7 printf("\n")
 8 printf("void print_%s_flag (tcflag_t flag)\n", FUNC_NAME)
 9 printf("{\n")
10 printf("    printf (\"%s flag 0x%%08x\\n\", flag); \n", FUNC_NAME)
11 FS=":"
12 while (getline < MACRO_FILE > 0) {
13 printf("#ifdef %s\n", $1)
14 printf("    if (flag & %s)\n", $1)
15 printf("        printf (\"    %s\\n\"); \n", $1)
16 printf("    else\n")
17 printf("        printf (\"    %s not in\\n\"); \n", $1)
18 printf("#else\n")
19 printf("    printf (\"    %s not defined\\n\"); \n", $1)
20 printf("#endif\n")
21 }
22 close (MACRO_FILE)
23 exit
24 }
25 END {
26 printf("}")
27 }      

生成的 c 檔案類似這樣:

1 #include "../apue.h"
 2 #include <termios.h>
 3 
 4 void print_input_flag (tcflag_t flag)
 5 {
 6     printf ("input flag 0x%08x\n", flag); 
 7 #ifdef BRKINT
 8     if (flag & BRKINT)
 9         printf ("    BRKINT\n"); 
10     else
11         printf ("    BRKINT not in\n"); 
12 #else
13     printf ("    BRKINT not defined\n"); 
14 #endif
15 #ifdef ICRNL
16     if (flag & ICRNL)
17         printf ("    ICRNL\n"); 
18     else
19         printf ("    ICRNL not in\n"); 
20 #else
21     printf ("    ICRNL not defined\n"); 
22 #endif
23 }      

再看下 Makefile 的生成規則就更清楚啦:

Makefile

1 all: term 
 2 
 3 term: term.o print_iflag.o print_oflag.o print_cflag.o print_lflag.o print_cchar.o apue.o 
 4     gcc -Wall -g $^ -o $@
 5 
 6 term.o: term.c ../apue.h
 7     gcc -Wall -g -c $< -o $@
 8 
 9 print_iflag.o: print_iflag.c ../apue.h
10     gcc -Wall -g -c $< -o $@
11 
12 print_iflag.c: print_flag.awk iflag.sym
13     ./print_flag.awk -v FUNC_NAME=input -v MACRO_FILE=iflag.sym > print_iflag.c
14 
15 print_oflag.o: print_oflag.c ../apue.h
16     gcc -Wall -g -c $< -o $@
17 
18 print_oflag.c: print_flag.awk oflag.sym
19     ./print_flag.awk -v FUNC_NAME=output -v MACRO_FILE=oflag.sym > print_oflag.c
20 
21 print_cflag.o: print_cflag.c ../apue.h
22     gcc -Wall -g -c $< -o $@
23 
24 print_cflag.c: print_flag.awk cflag.sym
25     ./print_flag.awk -v FUNC_NAME=control -v MACRO_FILE=cflag.sym > print_cflag.c
26 
27 print_lflag.o: print_lflag.c ../apue.h
28     gcc -Wall -g -c $< -o $@
29 
30 print_lflag.c: print_flag.awk lflag.sym
31     ./print_flag.awk -v FUNC_NAME=local -v MACRO_FILE=lflag.sym > print_lflag.c
32 
33 print_cchar.o: print_cchar.c ../apue.h
34     gcc -Wall -g -c $< -o $@
35 
36 print_cchar.c: print_char.awk cchar.sym
37     ./print_char.awk -v FUNC_NAME=control -v MACRO_FILE=cchar.sym > print_cchar.c
38 
39 log.o: ../log.c ../log.h
40     gcc -Wall -g -c $< -o $@
41 
42 apue.o: ../apue.c ../apue.h 
43     gcc -Wall -g -c $< -o $@ -D__USE_BSD
44 
45 clean: 
46     @echo "start clean..."
47     -rm -f *.o core.* *.log *~ *.swp term 
48     @echo "end clean"
49 
50 .PHONY: clean      

具體分析下生成過程:

1.通過 print_flag.awk 分别生成 print_iflag.c / print_oflag.c / print_cflag.c / print_lflag.c

2.分别将生成的 .c 編譯為 .o 檔案

3.在生成 term 工具時連結上述 .o 檔案生成最終的可執行檔案

當然了,除了各種标志位外,這裡還處理了 cc_t cc 字段,它列印每個特殊輸入字元,原理和上面相仿,就不再贅述了。

檢查列印的特殊字元,發現少了下标為 5 / 6 / 7 的字元,檢視頭檔案定義,原來是 linux 上面增加了三個新的定義:

cchar.sym

VTIME
VMIN
VSWTC
      

将它們添加到 sym 檔案中,重新編譯、運作,果然新的輸出裡有了:

cc[VTIME=5] = 0 (CTRL+@)
    cc[VMIN=6] = 1 (CTRL+A)
    cc[VSWTC=7] = 255 (CTRL+?)
      

這對于在不同平台上進行測試有很大的幫助。

pty