Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

424 řádky
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] == 81) { /* q, q! */
  219. if (cmd[1] == 33) return 1;
  220. if (dirty) {
  221. note = "unsaved - :q! quits";
  222. return 0;
  223. }
  224. return 1;
  225. }
  226. ok = 0;
  227. qf = 0;
  228. p = 1;
  229. if (cmd[0] == 87) ok = 1; /* w, wq, w name */
  230. if (cmd[0] == 88) { /* x */
  231. ok = 1;
  232. qf = 1;
  233. }
  234. if (ok == 0) {
  235. note = "unknown command";
  236. return 0;
  237. }
  238. if (cmd[0] == 87 && cmd[1] == 81) {
  239. qf = 1;
  240. p = 2;
  241. }
  242. while (cmd[p] == 32) p++;
  243. if (cmd[p]) {
  244. n = 0;
  245. while (cmd[p] && n < 20) fname[n++] = cmd[p++];
  246. fname[n] = 0;
  247. }
  248. if (fname[0] == 0) {
  249. note = "no file name";
  250. return 0;
  251. }
  252. if (savef()) {
  253. dirty = 0;
  254. note = "written";
  255. return qf;
  256. }
  257. note = "cannot write";
  258. return 0;
  259. }
  260. main()
  261. {
  262. int k, ok, p;
  263. sinit();
  264. mlinit(); /* ML helpers to $c000: before malloc */
  265. bfr = malloc(19000);
  266. if (bfr == 0) {
  267. sps(0, 0, "out of memory");
  268. skey();
  269. return;
  270. }
  271. blen = 0;
  272. nlines = 1;
  273. lptr[0] = 0;
  274. cy = 0;
  275. cx = 0;
  276. top = 0;
  277. leftcol = 0;
  278. fname[0] = 0;
  279. mode = 0;
  280. dirty = 0;
  281. pend = 0;
  282. note = 0;
  283. ylen = 0;
  284. yok = 0;
  285. split = 0;
  286. redraw = 2;
  287. for (;;) { /* a file to edit */
  288. askname();
  289. shortn(20);
  290. if (fname[0] == 0) { /* no name: an empty buffer */
  291. ok = 1;
  292. break;
  293. }
  294. ok = loadf();
  295. if (ok) break;
  296. if (askyn("not found - create it? (y/n) ")) break;
  297. }
  298. buildidx();
  299. if (ok == 0) { /* keep the name: :w creates it */
  300. nlines = 1;
  301. blen = 0;
  302. note = "new file";
  303. }
  304. if (split) note = "long lines split at 80";
  305. for (;;) {
  306. scroll();
  307. dall();
  308. k = skey();
  309. note = 0;
  310. if (mode) { /* ---- insert mode */
  311. if (k == 3) { /* RUN/STOP = Esc */
  312. mode = 0;
  313. if (cx > 0) cx--;
  314. } else if (k == 13) {
  315. newline();
  316. } else if (k == 20) {
  317. bkspace();
  318. } else if (arrow(k)) {
  319. cx = cx;
  320. } else if (k >= 32 && k < 128) {
  321. inschar(k);
  322. } else if (k >= 161) {
  323. inschar(k);
  324. }
  325. continue;
  326. }
  327. if (pend) { /* ---- second key of dd / gg */
  328. p = pend;
  329. pend = 0;
  330. if (p == 68 && k == 68) delline();
  331. if (p == 71 && k == 71) {
  332. cy = 0;
  333. cx = 0;
  334. }
  335. clampx();
  336. continue;
  337. }
  338. if (arrow(k)) {
  339. cx = cx;
  340. } else if (k == 72) { /* h */
  341. if (cx > 0) cx--;
  342. } else if (k == 76) { /* l */
  343. cx++;
  344. clampx();
  345. } else if (k == 74) { /* j */
  346. arrow(17);
  347. } else if (k == 75) { /* k */
  348. arrow(145);
  349. } else if (k == 48) { /* 0 */
  350. cx = 0;
  351. } else if (k == 36) { /* $ */
  352. cx = linend(cy) - lptr[cy];
  353. } else if (k == 199) { /* G */
  354. cy = nlines - 1;
  355. cx = 0;
  356. } else if (k == 68) { /* d.. */
  357. pend = 68;
  358. } else if (k == 71) { /* g.. */
  359. pend = 71;
  360. } else if (k == 88) { /* x */
  361. delchar();
  362. clampx();
  363. } else if (k == 80) { /* p */
  364. paste();
  365. } else if (k == 73) { /* i */
  366. mode = 1;
  367. } else if (k == 65) { /* a */
  368. cx++;
  369. clampx();
  370. mode = 1;
  371. } else if (k == 193) { /* A */
  372. cx = linend(cy) - lptr[cy];
  373. mode = 1;
  374. } else if (k == 201) { /* I */
  375. cx = 0;
  376. mode = 1;
  377. } else if (k == 79) { /* o */
  378. cx = linend(cy) - lptr[cy];
  379. newline();
  380. mode = 1;
  381. } else if (k == 207) { /* O */
  382. cx = 0;
  383. newline();
  384. cy--;
  385. mode = 1;
  386. } else if (k == 58) { /* : */
  387. if (doscmd() == 1) {
  388. sdone();
  389. return;
  390. }
  391. }
  392. }
  393. }

Powered by TurnKey Linux.