2 文本阅览器(harib27b)

“纸娃娃系统”中已经有了type.hrb,可以显示出文本文件的内容。不过由于屏幕滚动的速度太快,而且没有办法往回滚动,用起来挺不方便的,而且命令行窗口也太小了。

于是,我们来做一个用来查看文本文件内容的文本阅览器吧。

首先来看程序……话说每次都一上来就先拿一大段程序出来好像挺无聊的,从现在起就先给大家展示一下完成后的运行画面好了。如果事先知道要做出来的程序是什么样,再去看程序的话应该会更容易理解吧。

输入“tview ipl20.nas –w100 –h30”,运行结果如下图。

2 文本阅览器(harib27b) - 图1

咚!显示出来了

用光标键可以上下左右进行滚动,在QEMU中可能速度不是很快,这是由于模拟器性能不佳造成的,在真机环境下速度还是相当快的哦。

滚动的速度也是可以调节的。按“b”可以将纵向滚动的速度设为每次2行,“c”则每次4行,“d”则每次8行。以此类推,可以一直用到“f”,按“a”则恢复为初始状态,每次滚动1行。同样地,用“A”~“F”可以改变横向滚动的速度。

关于启动时的命令行选项,-w和-h代表窗口的大小。-w表示打开一个宽度为100个半角字符的窗口,最大可以指定为126。-h表示行数,最大可以指定为45。如果不指定-w和-h,则默认为-w30、-h10。

此外,用-t选项可以指定制表符的大小,省略的话则按照笔者的习惯默认为-t4。制表符的大小在程序启动后也可以通过“<”和“>”键来进行调节。

按“q”或者“Q”可以退出程序。其实这个键倒没那么重要。即便忘记了,点击“×”按钮或者按Shift+F1强制结束也没什么问题。

■■■■■

下面我们来看程序吧。

tview.c

  1. #include "apilib.h"
  2. #include <stdio.h>
  3. int strtol(char *s, char **endp, int base); /*标准函数(stdlib.h) */
  4. char *skipspace(char *p);
  5. void textview(int win, int w, int h, int xskip, char *p, int tab, int lang);
  6. char *lineview(int win, int w, int y, int xskip, unsigned char *p, int tab, int lang);
  7. int puttab(int x, int w, int xskip, char *s, int tab);
  8. void HariMain(void)
  9. {
  10. char winbuf[1024 * 757], txtbuf[240 * 1024];
  11. int w = 30, h = 10, t = 4, spd_x = 1, spd_y = 1;
  12. int win, i, j, lang = api_getlang(), xskip = 0;
  13. char s[30], *p, *q = 0, *r = 0;
  14. /*命令行解析*/
  15. api_cmdline(s, 30);
  16. for (p = s; *p > ' '; p++) { } /*一直读到空格为止*/
  17. for (; *p != 0; ) {
  18. p = skipspace(p);
  19. if (*p == '-') {
  20. if (p[1] == 'w') {
  21. w = strtol(p + 2, &p, 0);
  22. if (w < 20) {
  23. w = 20;
  24. }
  25. if (w > 126) {
  26. w = 126;
  27. }
  28. } else if (p[1] == 'h') {
  29. h = strtol(p + 2, &p, 0);
  30. if (h < 1) {
  31. h = 1;
  32. }
  33. if (h > 45) {
  34. h = 45;
  35. }
  36. } else if (p[1] == 't') {
  37. t = strtol(p + 2, &p, 0);
  38. if (t < 1) {
  39. t = 4;
  40. }
  41. } else {
  42. err:
  43. api_putstr0(" >tview file [-w30 -h10 -t4]\n");
  44. api_end();
  45. }
  46. } else { /*找到文件名*/
  47. if (q != 0) {
  48. goto err;
  49. }
  50. q = p;
  51. for (; *p > ' '; p++) { } /*一直读到空格为止*/
  52. r = p;
  53. }
  54. }
  55. if (q == 0) {
  56. goto err;
  57. }
  58. /*准备窗口*/
  59. win = api_openwin(winbuf, w * 8 + 16, h * 16 + 37, -1, "tview");
  60. api_boxfilwin(win, 6, 27, w * 8 + 9, h * 16 + 30, 7);
  61. /*载入文件*/
  62. *r = 0;
  63. i = api_fopen(q);
  64. if (i == 0) {
  65. api_putstr0("file open error.\n");
  66. api_end();
  67. }
  68. j = api_fsize(i, 0);
  69. if (j >= 240 * 1024 - 1) {
  70. j = 240 * 1024 - 2;
  71. }
  72. txtbuf[0] = 0x0a; /*卫兵用的换行代码*/
  73. api_fread(txtbuf + 1, j, i);
  74. api_fclose(i);
  75. txtbuf[j + 1] = 0;
  76. q = txtbuf + 1;
  77. for (p = txtbuf + 1; *p != 0; p++) { /*为了让处理变得简单,删掉0x0d的代码*/
  78. if (*p != 0x0d) {
  79. *q = *p;
  80. q++;
  81. }
  82. }
  83. *q = 0;
  84. /*主体*/
  85. p = txtbuf + 1;
  86. for (;;) {
  87. textview(win, w, h, xskip, p, t, lang);
  88. i = api_getkey(1);
  89. if (i == 'Q' || i == 'q') {
  90. api_end();
  91. }
  92. if ('A' <= i && i <= 'F') {
  93. spd_x = 1 << (i - 'A'); /* 1, 2, 4, 8, 16, 32 */
  94. }
  95. if ('a' <= i && i <= 'f') {
  96. spd_y = 1 << (i - 'a'); /* 1, 2, 4, 8, 16, 32 */
  97. }
  98. if (i == '<' && t > 1) {
  99. t /= 2;
  100. }
  101. if (i == '>' && t < 256) {
  102. t *= 2;
  103. }
  104. if (i == '4') {
  105. for (;;) {
  106. xskip -= spd_x;
  107. if (xskip < 0) {
  108. xskip = 0;
  109. }
  110. if (api_getkey(0) != '4') { /*如果没有按下“4”则处理结束*/
  111. break;
  112. }
  113. }
  114. }
  115. if (i == '6') {
  116. for (;;) {
  117. xskip += spd_x;
  118. if (api_getkey(0) != '6') {
  119. break;
  120. }
  121. }
  122. }
  123. if (i == '8') {
  124. for (;;) {
  125. for (j = 0; j < spd_y; j++) {
  126. if (p == txtbuf + 1) {
  127. break;
  128. }
  129. for (p--; p[-1] != 0x0a; p--) { } /*回溯到上一个字符为0x0a为止*/
  130. }
  131. if (api_getkey(0) != '8') {
  132. break;
  133. }
  134. }
  135. }
  136. if (i == '2') {
  137. for (;;) {
  138. for (j = 0; j < spd_y; j++) {
  139. for (q = p; *q != 0 && *q != 0x0a; q++) { }
  140. if (*q == 0) {
  141. break;
  142. }
  143. p = q + 1;
  144. }
  145. if (api_getkey(0) != '2') {
  146. break;
  147. }
  148. }
  149. }
  150. }
  151. }
  152. char *skipspace(char *p)
  153. {
  154. for (; *p == ' '; p++) { } /*跳过空格*/
  155. return p;
  156. }
  157. void textview(int win, int w, int h, int xskip, char *p, int tab, int lang)
  158. {
  159. int i;
  160. api_boxfilwin(win + 1, 8, 29, w * 8 + 7, h * 16 + 28, 7);
  161. for (i = 0; i < h; i++) {
  162. p = lineview(win, w, i * 16 + 29, xskip, p, tab, lang);
  163. }
  164. api_refreshwin(win, 8, 29, w * 8 + 8, h * 16 + 29);
  165. return;
  166. }
  167. char *lineview(int win, int w, int y, int xskip, unsigned char *p, int tab, int lang)
  168. {
  169. int x = - xskip;
  170. char s[130];
  171. for (;;) {
  172. if (*p == 0) {
  173. break;
  174. }
  175. if (*p == 0x0a) {
  176. p++;
  177. break;
  178. }
  179. if (lang == 0) { /* ASCII */
  180. if (*p == 0x09) {
  181. x = puttab(x, w, xskip, s, tab);
  182. } else {
  183. if (0 <= x && x < w) {
  184. s[x] = *p;
  185. }
  186. x++;
  187. }
  188. p++;
  189. }
  190. if (lang == 1) { /* SJIS */
  191. if (*p == 0x09) {
  192. x = puttab(x, w, xskip, s, tab);
  193. p++;
  194. } else if ((0x81 <= *p && *p <= 0x9f) || (0xe0 <= *p && *p <= 0xfc)) {
  195. /*全角字符*/
  196. if (x == -1) {
  197. s[0] = ' ';
  198. }
  199. if (0 <= x && x < w - 1) {
  200. s[x] = *p;
  201. s[x + 1] = p[1];
  202. }
  203. if (x == w - 1) {
  204. s[x] = ' ';
  205. }
  206. x += 2;
  207. p += 2;
  208. } else {
  209. if (0 <= x && x < w) {
  210. s[x] = *p;
  211. }
  212. x++;
  213. p++;
  214. }
  215. }
  216. if (lang == 2) { /* EUC */
  217. if (*p == 0x09) {
  218. x = puttab(x, w, xskip, s, tab);
  219. p++;
  220. } else if (0xa1 <= *p && *p <= 0xfe) {
  221. /*全角字符*/
  222. if (x == -1) {
  223. s[0] = ' ';
  224. }
  225. if (0 <= x && x < w - 1) {
  226. s[x] = *p;
  227. s[x + 1] = p[1];
  228. }
  229. if (x == w - 1) {
  230. s[x] = ' ';
  231. }
  232. x += 2;
  233. p += 2;
  234. } else {
  235. if (0 <= x && x < w) {
  236. s[x] = *p;
  237. }
  238. x++;
  239. p++;
  240. }
  241. }
  242. }
  243. if (x > w) {
  244. x = w;
  245. }
  246. if (x > 0) {
  247. s[x] = 0;
  248. api_putstrwin(win + 1, 8, y, 0, x, s);
  249. }
  250. return p;
  251. }
  252. int puttab(int x, int w, int xskip, char *s, int tab)
  253. {
  254. for (;;) {
  255. if (0 <= x && x < w) {
  256. s[x] = ' ';
  257. }
  258. x++;
  259. if ((x + xskip) % tab == 0) {
  260. break;
  261. }
  262. }
  263. return x;
  264. }

程序基本上没有什么难点(虽然看上去很长),只要慢慢读应该能看懂。和calc.hrb一样,即便看不懂也问题不大。

在屏幕滚动的处理上用了api_getkey(0),这是为了在QEMU上运行时可以尽量减少延迟。当同一个键被连续按下时,程序会将按键数据全部读取之后,再计算出总的移动量,然后再进行显示。

这个tview.hrb只有1753字节,跟calc.hrb大小差不多。嗯,也是短小精悍的。这么小的应用程序可以拥有如此不错的功能,我们的“纸娃娃系统”还真是挺强大的系统呢。

有人可能会说,既然都做到这一步了,还不如干脆做成文本编辑器呢。其实笔者也是这么想的,只不过现在的“纸娃娃系统”中还没有用于写入文件的API,即便做了文本编辑器也无法保存,感觉没有什么意义。看来“纸娃娃系统”还不太给力啊(笑)。