[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 一下就可以啦~~~
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 (CTRL+@) cc[VMIN=6] = 1 (CTRL+A) cc[VSWTC=7] = 255 (CTRL+?) |
这对于在不同平台上进行测试有很大的帮助。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· C++代码改造为UTF-8编码问题的总结
· DeepSeek 解答了困扰我五年的技术问题
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 10亿数据,如何做迁移?
· 推荐几款开源且免费的 .NET MAUI 组件库
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· c# 半导体/led行业 晶圆片WaferMap实现 map图实现入门篇
· 易语言 —— 开山篇