話很少說,先看運行效果:linux
>./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 結構體,咱們能夠獲取一個終端設備的設置信息:ios
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 手冊。git
這個工具能夠將二進制的標誌位,翻譯爲人類能夠讀懂的常量宏,例如上面的輸出中,能夠看到輸入標誌位打開了 ICRNL 與 IXON 兩個標誌位,github
對應的含義分別是「將輸入的CR轉換爲NL」、「使啓動/中止輸出控制流起做用」。bash
看這段輸出也許你已經想到了代碼的實現,就是挨個常量宏嘗試唄,這有啥難的。工具
不錯,可是考慮到不一樣平臺上定義的宏不一致,有時增長一兩個宏可能還須要修改源代碼,這是多麼痛苦的事啊!測試
這個小工具就解決了這個痛點,你能夠在配置文件中指定要測試的宏名稱,而後 make 一下就能夠啦~~~spa
BRKINT ICRNL IGNBRK IGNCR IGNPAR IMAXBEL INLCR INPCK ISTRIP IUCLC IXANY IXOFF IXON PARMRK
oflag.symcode
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 ()
這對於在不一樣平臺上進行測試有很大的幫助。