Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

430 рядки
11KB

  1. /* vim.c - a small modal editor for the commodore 64, written for power c.
  2. *
  3. * Increment 2 edits: it reads a petscii SEQ file into one buffer, keeps a line
  4. * index, shows 24 lines plus a status row, and supports
  5. * move h j k l / arrows, 0 $ gg G
  6. * insert i a A I o O (RUN/STOP leaves insert mode, DEL backspaces)
  7. * change x dd p
  8. * colon :w :w name :wq :x :q :q!
  9. *
  10. * The buffer holds the file exactly as it sits on disk: petscii bytes with $0d
  11. * between lines. Keys are compared as numbers, never as character constants,
  12. * so nothing depends on how the compiler treats literals. (Unshifted letters
  13. * arrive as $41-$5a, so h = 72; shifted ones as $c1-$da, so G = 199.)
  14. */
  15. char *malloc();
  16. char *bfr; /* file text, $0d separated */
  17. unsigned blen; /* bytes used */
  18. unsigned lptr[600]; /* offset of each line start */
  19. int nlines;
  20. int cy, cx; /* cursor line and column */
  21. int top; /* first line on screen */
  22. int leftcol; /* horizontal scroll */
  23. char fname[24];
  24. int mode; /* 0 normal, 1 insert */
  25. int dirty; /* buffer changed since the last save */
  26. int pend; /* first key of a two key command: d or g */
  27. char *note; /* one-shot message for the status row */
  28. char ybuf[160]; /* the yanked line */
  29. int ylen, yok;
  30. int redraw; /* 1 cursor row, 2 whole screen */
  31. /* screen rows are 0..23, the status row is 24 */
  32. /* shorten fname to what fits */
  33. shortn(n)
  34. int n;
  35. {
  36. int i;
  37. i = 0;
  38. while (fname[i] && i < n) i++;
  39. fname[i] = 0;
  40. return 0;
  41. }
  42. /* a message that waits for a key */
  43. msg(s)
  44. char *s;
  45. {
  46. sclr(24, 0, 40);
  47. sps(24, 0, s);
  48. skey();
  49. return 0;
  50. }
  51. /* read a line of lowercase petscii into s, echoed from column c of row 24 */
  52. getln(c, s, max)
  53. int c, max;
  54. char *s;
  55. {
  56. int n, k;
  57. n = 0;
  58. s[0] = 0;
  59. for (;;) {
  60. k = skey();
  61. if (k == 13) break;
  62. if (k == 3) {
  63. n = 0;
  64. s[0] = 0;
  65. break;
  66. }
  67. if (k == 20) {
  68. if (n > 0) {
  69. n--;
  70. s[n] = 0;
  71. }
  72. } else {
  73. if (k >= 0xc1 && k <= 0xda) k = k - 0x80;
  74. if (k >= 32 && k != 127 && n < max) {
  75. s[n] = k;
  76. n++;
  77. s[n] = 0;
  78. }
  79. }
  80. sclr(24, c, max);
  81. sps(24, c, s);
  82. }
  83. return n;
  84. }
  85. /* a yes/no question on the status row: 1 for y, 0 for n or run/stop */
  86. askyn(q)
  87. char *q;
  88. {
  89. int k;
  90. sclr(24, 0, 40);
  91. sps(24, 0, q);
  92. for (;;) {
  93. k = skey();
  94. if (k == 89) return 1; /* y */
  95. if (k == 217) return 1; /* Y */
  96. if (k == 78) return 0; /* n */
  97. if (k == 206) return 0; /* N */
  98. if (k == 3) return 0;
  99. }
  100. }
  101. /* ask which file to open */
  102. askname()
  103. {
  104. sclr(24, 0, 40);
  105. sps(24, 0, "file: ");
  106. if (getln(6, fname, 20) == 0) fname[0] = 0;
  107. return 0;
  108. }
  109. /* the drive's error channel (already open as file 15): the two digit code of
  110. the last command. 0-19 are fine ("00, ok"), 62 is file not found. open() of
  111. a missing file succeeds on a cbm drive - only the error channel tells.
  112. do not close 15 while another file is open: closing the command channel
  113. closes every file on the drive. */
  114. dosst()
  115. {
  116. int ch, n, d;
  117. n = 0;
  118. d = 0;
  119. for (;;) {
  120. ch = getc(15);
  121. if (ch < 0) break;
  122. if (ch == 13) break;
  123. if (d < 2) {
  124. if (ch >= 48 && ch <= 57) {
  125. n = n * 10 + ch - 48;
  126. d++;
  127. }
  128. }
  129. }
  130. if (d < 2) return 99;
  131. return n;
  132. }
  133. int split; /* a long line was split on load */
  134. /* read fname into bfr. 1 on success, 0 if the file is not there.
  135. lines longer than 80 are split - the editor's line limit */
  136. loadf()
  137. {
  138. int f, ch, prev, c, col;
  139. char on[28];
  140. int i;
  141. if (fname[0] == 0) return 1;
  142. i = 0;
  143. while (fname[i] && i < 20) {
  144. on[i] = fname[i];
  145. i++;
  146. }
  147. on[i++] = 44; /* , */
  148. on[i++] = 83; /* S (petscii, not ascii) */
  149. on[i++] = 44; /* , */
  150. on[i++] = 82; /* R */
  151. on[i] = 0;
  152. if (open(15, 8, 15, "") == 0) return 0; /* status channel first */
  153. f = open(5, 8, 2, on);
  154. if (f == 0) {
  155. close(15);
  156. return 0;
  157. }
  158. if (dosst() >= 20) {
  159. close(5);
  160. close(15);
  161. return 0;
  162. }
  163. blen = 0;
  164. prev = 0;
  165. col = 0;
  166. split = 0;
  167. for (;;) {
  168. ch = getc(5);
  169. if (ch < 0) break; /* end of file */
  170. if (ch == 26) break;
  171. if (ch == 10 && prev == 13) { /* crlf seen as two bytes */
  172. prev = ch;
  173. continue;
  174. }
  175. if (ch == 13) c = 13;
  176. else if (ch == 10) c = 13;
  177. else c = ch;
  178. if (c != 13 && col >= 80) { /* 80 columns at most */
  179. if (blen < 18999) bfr[blen++] = 13;
  180. col = 0;
  181. split = 1;
  182. }
  183. if (blen < 18999) bfr[blen++] = c;
  184. if (c == 13) col = 0;
  185. else col++;
  186. prev = ch;
  187. }
  188. close(5);
  189. close(15); /* last: it closes everything */
  190. return 1;
  191. }
  192. /* find the start of every line */
  193. buildidx()
  194. {
  195. unsigned i;
  196. nlines = 1;
  197. lptr[0] = 0;
  198. for (i = 0; i < blen; i++) {
  199. if (bfr[i] == 13) {
  200. if (nlines < 599) {
  201. lptr[nlines] = i + 1;
  202. nlines++;
  203. }
  204. }
  205. }
  206. return 0;
  207. }
  208. /* the ":" command line. returns 1 when the editor should quit */
  209. doscmd()
  210. {
  211. int n, p, ok, qf;
  212. char cmd[24];
  213. sclr(24, 0, 40);
  214. spc(24, 0, 58); /* : */
  215. n = getln(1, cmd, 20);
  216. sclr(24, 0, 40);
  217. if (n == 0) return 0;
  218. if (cmd[0] == 68 && cmd[1] == 73 && cmd[2] == 82 && cmd[3] == 0) { /* dir */
  219. if (dirlist() == 0) note = "cannot read directory";
  220. return 0;
  221. }
  222. if (cmd[0] == 81) { /* q, q! */
  223. if (cmd[1] == 33) return 1;
  224. if (dirty) {
  225. note = "unsaved - :q! quits";
  226. return 0;
  227. }
  228. return 1;
  229. }
  230. ok = 0;
  231. qf = 0;
  232. p = 1;
  233. if (cmd[0] == 87) ok = 1; /* w, wq, w name */
  234. if (cmd[0] == 88) { /* x */
  235. ok = 1;
  236. qf = 1;
  237. }
  238. if (ok == 0) {
  239. note = "unknown command";
  240. return 0;
  241. }
  242. if (cmd[0] == 87 && cmd[1] == 81) {
  243. qf = 1;
  244. p = 2;
  245. }
  246. while (cmd[p] == 32) p++;
  247. if (cmd[p]) {
  248. n = 0;
  249. while (cmd[p] && n < 20) fname[n++] = cmd[p++];
  250. fname[n] = 0;
  251. }
  252. if (fname[0] == 0) {
  253. note = "no file name";
  254. return 0;
  255. }
  256. if (savef()) {
  257. dirty = 0;
  258. note = "written";
  259. return qf;
  260. }
  261. note = "cannot write";
  262. return 0;
  263. }
  264. main()
  265. {
  266. int k, ok, p;
  267. sinit();
  268. mlinit(); /* ML helpers to $c000: before malloc */
  269. bfr = malloc(19000);
  270. if (bfr == 0) {
  271. sps(0, 0, "out of memory");
  272. skey();
  273. return;
  274. }
  275. blen = 0;
  276. nlines = 1;
  277. lptr[0] = 0;
  278. cy = 0;
  279. cx = 0;
  280. top = 0;
  281. leftcol = 0;
  282. fname[0] = 0;
  283. mode = 0;
  284. dirty = 0;
  285. pend = 0;
  286. note = 0;
  287. ylen = 0;
  288. yok = 0;
  289. split = 0;
  290. redraw = 2;
  291. for (;;) { /* a file to edit */
  292. askname();
  293. shortn(20);
  294. if (fname[0] == 0) { /* no name: an empty buffer */
  295. ok = 1;
  296. break;
  297. }
  298. ok = loadf();
  299. if (ok) break;
  300. if (askyn("not found - create it? (y/n) ")) break;
  301. }
  302. buildidx();
  303. if (ok == 0) { /* keep the name: :w creates it */
  304. nlines = 1;
  305. blen = 0;
  306. note = "new file";
  307. }
  308. if (split) note = "long lines split at 80";
  309. for (;;) {
  310. scroll();
  311. dall();
  312. k = skey();
  313. note = 0;
  314. if (mode) { /* ---- insert mode */
  315. if (k == 3) { /* RUN/STOP = Esc */
  316. mode = 0;
  317. if (cx > 0) cx--;
  318. } else if (k == 13) {
  319. newline();
  320. } else if (k == 20) {
  321. bkspace();
  322. } else if (arrow(k)) {
  323. cx = cx;
  324. } else if (k >= 32 && k < 128) {
  325. inschar(k);
  326. } else if (k >= 161) {
  327. inschar(k);
  328. }
  329. continue;
  330. }
  331. if (pend) { /* ---- second key of dd / gg */
  332. p = pend;
  333. pend = 0;
  334. if (p == 68 && k == 68) delline();
  335. if (p == 71 && k == 71) {
  336. cy = 0;
  337. cx = 0;
  338. }
  339. clampx();
  340. continue;
  341. }
  342. if (arrow(k)) {
  343. cx = cx;
  344. } else if (k == 72) { /* h */
  345. if (cx > 0) cx--;
  346. } else if (k == 76) { /* l */
  347. cx++;
  348. clampx();
  349. } else if (k == 74) { /* j */
  350. arrow(17);
  351. } else if (k == 75) { /* k */
  352. arrow(145);
  353. } else if (k == 48) { /* 0 */
  354. cx = 0;
  355. } else if (k == 36) { /* $ */
  356. cx = linend(cy) - lptr[cy];
  357. } else if (k == 199) { /* G */
  358. cy = nlines - 1;
  359. cx = 0;
  360. } else if (k == 68) { /* d.. */
  361. pend = 68;
  362. } else if (k == 71) { /* g.. */
  363. pend = 71;
  364. } else if (k == 88) { /* x */
  365. delchar();
  366. clampx();
  367. } else if (k == 80) { /* p */
  368. paste();
  369. } else if (k == 73) { /* i */
  370. mode = 1;
  371. } else if (k == 65) { /* a */
  372. cx++;
  373. clampx();
  374. mode = 1;
  375. } else if (k == 193) { /* A */
  376. cx = linend(cy) - lptr[cy];
  377. mode = 1;
  378. } else if (k == 201) { /* I */
  379. cx = 0;
  380. mode = 1;
  381. } else if (k == 79) { /* o */
  382. cx = linend(cy) - lptr[cy];
  383. newline();
  384. mode = 1;
  385. } else if (k == 207) { /* O */
  386. cx = 0;
  387. newline();
  388. cy--;
  389. mode = 1;
  390. } else if (k == 58) { /* : */
  391. if (doscmd() == 1) {
  392. sdone(); /* resets the machine: never returns.
  393. (a return here made power c emit a
  394. jump to garbage - see NOTES.md) */
  395. }
  396. }
  397. }
  398. }

Powered by TurnKey Linux.