話不多說,先看運行效果: >./term input flag 0x00000500 BRKINT not in ICRNL IGNBRK not in IGNCR not in IGNPAR not in IMAXBEL not in INLCR not in INPCK not in ISTRI ...
話不多說,先看運行效果:
>./term input flag 0x00000500 BRKINT not in ICRNL IGNBRK not in IGNCR not in IGNPAR not in IMAXBEL not in INLCR not in INPCK not in ISTRIP not in IUCLC not in IXANY not in IXOFF not in IXON PARMRK not in output flag 0x00000005 BSDLY not in CMSPAR not defined CRDLY not in FFDLY not in NLDLY not in OCRNL not in OFDEL not in OFILL not in OLCUC not in ONLCR ONLRET not in ONOCR not in ONOEOT not defined OPOST OXTABS not defined TABDLY not in VTDLY not in control flag 0x000000bf CBAUDEXT not defined CCAR_OFLOW not defined CCTS_OFLOW not defined CDSR_OFLOW not defined CDTR_IFLOW not defined CIBAUDEXT not defined CIGNORE not defined CLOCAL not in CREAD CRTSCTS not defined CRTS_IFLOW not defined CRTSXOFF not defined CSIZE CSTOPB not in HUPCL not in MDMBUF not defined PARENB not in PAREXT not defined PARODD not in local flag 0x00008a3b ALTWERASE not defined ECHO ECHOCTL not defined ECHOE ECHOK ECHOKE not defined ECHONL not in ECHOPRT not defined EXTPROC not defined FLUSHO not defined ICANON IEXTEN ISIG NOFLSH not in NOKERNINFO not defined PENDIN not defined TOSTOP not in XCASE not in input control char array size 32 cc[VDISCARD=13] = 15 () VDSUSP not defined cc[VEOF=4] = 4 () cc[VEOL=11] = 0 () cc[VEOL2=16] = 0 () cc[VERASE=2] = 127 () VERASE2 not defined cc[VINTR=0] = 3 () cc[VKILL=3] = 21 () cc[VLNEXT=15] = 22 () cc[VQUIT=1] = 28 () cc[VREPRINT=12] = 18 () cc[VSTART=8] = 17 () VSTATUS not defined cc[VSTOP=9] = 19 () cc[VSUSP=10] = 26 () cc[VWERASE=14] = 23 ()
眾所周知,通過 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 一下就可以啦~~~
BRKINT ICRNL IGNBRK IGNCR IGNPAR IMAXBEL INLCR INPCK ISTRIP IUCLC IXANY IXOFF IXON PARMRK
BSDLY CMSPAR CRDLY FFDLY NLDLY OCRNL OFDEL OFILL OLCUC ONLCR ONLRET ONOCR ONOEOT OPOST OXTABS TABDLY VTDLY
CBAUDEXT CCAR_OFLOW CCTS_OFLOW CDSR_OFLOW CDTR_IFLOW CIBAUDEXT CIGNORE CLOCAL CREAD CRTSCTS CRTS_IFLOW CRTSXOFF CSIZE CSTOPB HUPCL MDMBUF PARENB PAREXT PARODD
ALTWERASE ECHO ECHOCTL ECHOE ECHOK ECHOKE ECHONL ECHOPRT EXTPROC FLUSHO ICANON IEXTEN ISIG NOFLSH NOKERNINFO PENDIN TOSTOP XCASE
其實這裡是用 awk 讀取配置文件自動生成 c 語言的代碼來實現的:
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 的生成規則就更清楚啦:
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 上面增加了三個新的定義:
VTIME VMIN VSWTC
將它們添加到 sym 文件中,重新編譯、運行,果然新的輸出里有了:
cc[VTIME=5] = 0 () cc[VMIN=6] = 1 () cc[VSWTC=7] = 0 ()
這對於在不同平臺上進行測試有很大的幫助。