main.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. /* main.c, Ait, BSD 3-Clause, Kevin Bloom, 2023-2025,
  2. Derived from: Atto January 2017
  3. Derived from: Anthony's Editor January 93
  4. */
  5. #include <sys/types.h>
  6. #include "header.h"
  7. #include "termbox.h"
  8. #include "util.h"
  9. int done;
  10. kill_t scrap = { NULL, 0 };
  11. kill_t kill_ring[KILLRING_SIZE];
  12. kill_t shell_ring[SHELLRING_SIZE];
  13. char_t input[K_BUFFER_LENGTH];
  14. char_t *tinput;
  15. int msgflag;
  16. char msgline[TEMPBUF];
  17. char temp[TEMPBUF];
  18. char *gtemp = NULL;
  19. long ngtemp = 0;
  20. char searchtext[STRBUF_L];
  21. char replace[STRBUF_L];
  22. int found_point = -1;
  23. int search_dir = 1;
  24. int universal_argument = 0;
  25. int numeric_argument = 0;
  26. int negated = FALSE;
  27. int submatch = 0;
  28. uint32_t input_char = 0;
  29. int undoset_flag = FALSE;
  30. char editor_dir[PATH_MAX+1];
  31. int record_input = FALSE;
  32. int record_buffer_index = 0;
  33. int run_buffer_index = 0;
  34. struct tb_event record_buffer[256];
  35. int execute_kbd_macro = FALSE;
  36. int undo_index = -1;
  37. char unicode_buf[7];
  38. char character[1];
  39. int lastcommand = KBD_DEFAULT;
  40. int currentcommand = KBD_DEFAULT;
  41. int window_mode = WINDOW_DEFAULT;
  42. int ignorenotbound = FALSE;
  43. char *backup_dir = NULL;
  44. char *switch_command = NULL;
  45. int lastcol = 0;
  46. int lastline = 0;
  47. char lastchar = 0;
  48. char lastsymb = 0;
  49. point_t recentbracket = -1;
  50. const char* term;
  51. dyna_t dynaex;
  52. int io = 0;
  53. keymap_t *key_return;
  54. keymap_t *key_map;
  55. buffer_t *curbp; /* current buffer */
  56. buffer_t *bheadp; /* head of list of buffers */
  57. window_t *curwp;
  58. window_t *wheadp;
  59. buffer_t *lastbp = NULL; /* last active buffer */
  60. int LINES;
  61. int COLS;
  62. int MSGLINE; /* */
  63. static void graceful_exit()
  64. {
  65. tb_shutdown();
  66. exit(1);
  67. }
  68. static void cont()
  69. {
  70. int ol = LINES, oc = COLS;
  71. tb_init();
  72. LINES = tb_height();
  73. COLS = tb_width();
  74. MSGLINE = LINES-1;
  75. tb_set_input_mode(TB_INPUT_ALT);
  76. tb_clear();
  77. if(ol != LINES || oc != COLS)
  78. resize();
  79. update_display();
  80. redraw();
  81. }
  82. static void setup_signal_handlers()
  83. {
  84. struct sigaction action;
  85. memset(&action, 0, sizeof(struct sigaction));
  86. action.sa_handler = graceful_exit;
  87. sigaction(SIGTERM, &action, NULL);
  88. sigaction(SIGINT, &action, NULL);
  89. sigaction(SIGQUIT, &action, NULL);
  90. sigaction(SIGHUP, &action, NULL);
  91. action.sa_handler = cont;
  92. sigaction(SIGCONT, &action, NULL);
  93. signal(SIGPIPE, SIG_IGN);
  94. }
  95. void check_flags(char **argv, int idx)
  96. {
  97. if(!strcmp(argv[idx], "-v")) {
  98. fprintf(stdout, "%s\n\r", VERSION);
  99. exit(0);
  100. }
  101. if(!strcmp(argv[idx], "-h")) {
  102. fprintf(stdout, "%s\n\r\nUsage: ait [vh] [file]... +/-line...\n\r ait [-b backup_dir] [file]... +/-line...\n\r\n-h Print help and exit\n\r-v Print version and exit\n\r-b backup_dir Set the backup directory\n\r-s switch_cmd Set the switch command\n\r\n", VERSION);
  103. exit(0);
  104. }
  105. return;
  106. }
  107. int main(int argc, char **argv)
  108. {
  109. int ret, u, buffers = 0;
  110. int line = 0, current = 0, lastln = 0;
  111. int args = 0, v = 1;
  112. point_t p;
  113. if(2 <= argc) {
  114. for(int i = argc - 1; i >= 1; i--)
  115. check_flags(argv, i);
  116. if(!strcmp(argv[1], "-b") && argc >= 2) {
  117. if((backup_dir = strdup(argv[2])) == NULL) {
  118. fatal("%s: Failed to allocate required memory.\n");
  119. }
  120. }
  121. if(argc >= 2 && !strcmp(argv[1], "-s") && backup_dir == NULL) {
  122. if((switch_command = strdup(argv[2])) == NULL) {
  123. fatal("%s: Failed to allocate required memory.\n");
  124. }
  125. }
  126. if(argc >= 4 && !strcmp(argv[3], "-s")) {
  127. if((switch_command = strdup(argv[4])) == NULL) {
  128. fatal("%s: Failed to allocate required memory.\n");
  129. }
  130. }
  131. }
  132. ret = tb_init();
  133. LINES = tb_height();
  134. COLS = tb_width();
  135. MSGLINE = LINES-1;
  136. character[0] = '\0';
  137. term = getenv("LANG");
  138. dynaex.query = NULL;
  139. dynaex.nquery = -1;
  140. dynaex.nresult = -1;
  141. dynaex.start = -1;
  142. dynaex.end = -1;
  143. dynaex.sp = -1;
  144. if (ret) {
  145. fprintf(stderr, "Failed with error code %d\n", ret);
  146. exit(1);
  147. }
  148. getcwd(editor_dir, sizeof(editor_dir));
  149. strcat(editor_dir, "/");
  150. tb_set_input_mode(TB_INPUT_ALT);
  151. // tb_set_output_mode(TB_OUTPUT_216);
  152. tb_clear();
  153. setup_signal_handlers();
  154. // initialize the kill-ring
  155. for(int i = KILLRING_SIZE-1; i > 0; i--) {
  156. struct kill_t k;
  157. k.data = NULL;
  158. k.len = 0;
  159. kill_ring[i] = k;
  160. }
  161. /* TODO: this is really dumb... */
  162. if(backup_dir == NULL && switch_command == NULL) {
  163. args = 1 < argc;
  164. v = 1;
  165. } else if(backup_dir != NULL && switch_command != NULL) {
  166. args = 5 < argc;
  167. v = 5;
  168. } else if(backup_dir != NULL || switch_command != NULL) {
  169. args = 3 < argc;
  170. v = 3;
  171. }
  172. if (args) {
  173. for(; v < argc; v++) {
  174. buffers++;
  175. curbp = find_buffer(argv[v], TRUE, TRUE);
  176. (void) insert_file(argv[v], FALSE);
  177. /* Save filename regardless of load() success. */
  178. if(argv[v][0] == '-') {
  179. check_flags(argv, v);
  180. }
  181. curbp->b_path = TRUE;
  182. curbp->b_line = 1;
  183. curbp->b_pcol = 0;
  184. if (!growgap(curbp, CHUNK))
  185. fatal("%s: Failed to allocate required memory.\n");
  186. movegap(curbp, 0);
  187. if(argv[v+1]) {
  188. if(argv[v+1][0] == '-' || argv[v+1][0] == '+') {
  189. if(argv[v+1][0] == '+') {
  190. argv[v+1]++;
  191. line = atoi(argv[v+1]);
  192. } else {
  193. argv[v+1]++;
  194. get_line_stats(&current, &lastln, curbp);
  195. line = lastln - atoi(argv[v+1]);
  196. if(line < 0)
  197. line = 0;
  198. }
  199. p = line_to_point(line);
  200. if (p != -1) {
  201. curbp->b_point = p;
  202. curbp->b_opoint = p;
  203. curbp->b_cpoint = p;
  204. if (curbp->b_epage < pos(curbp, curbp->b_ebuf)) curbp->b_reframe = 1;
  205. curbp->b_line = line;
  206. msg("Line %d", line);
  207. } else {
  208. msg("Line %d, not found", line);
  209. }
  210. v++;
  211. }
  212. }
  213. }
  214. } else {
  215. curbp = find_buffer("*scratch*", TRUE, TRUE);
  216. strncpy(curbp->b_bname, "*scratch*", STRBUF_S);
  217. curbp->b_path = FALSE;
  218. }
  219. if(switch_command == NULL) {
  220. asprintf(&switch_command, "!dd of=/tmp/ait-switch-temp.txt ; clear ; cat /tmp/ait-switch-temp.txt | awk '{ print NR \". \" $0 }' ; read -p \"> \" AIT_ANSWER ; cat /tmp/ait-switch-temp.txt | awk -va=\"$AIT_ANSWER\" 'NR == a {print $0}' > /tmp/ait-temp.txt ; rm /tmp/ait-switch-temp.txt");
  221. }
  222. key_map = keymap;
  223. submatch = 0;
  224. wheadp = curwp = new_window();
  225. associate_b2w(curbp, curwp);
  226. one_window(curwp);
  227. if(buffers > 1) {
  228. chop_window();
  229. if(curbp->b_next == NULL)
  230. next_buffer();
  231. }
  232. while (!done) {
  233. update_display();
  234. if(curwp->w_recenter) {
  235. recenter();
  236. curwp->w_recenter = FALSE;
  237. update_display();
  238. }
  239. recentbracket = -1;
  240. curbp->b_opoint = curbp->b_point;
  241. curbp->b_opage = curbp->b_page;
  242. tinput = get_key(key_map, &key_return);
  243. strcpy((char *)input, (const char *)tinput);
  244. if (key_return != NULL) {
  245. /* TODO: a better way to figure out editing commands */
  246. if(key_return->key_desc[2] == 'd' || key_return->key_desc[4] == '%' ||
  247. key_return->key_desc[2] == 'i' || key_return->key_desc[2] == 'k' ||
  248. key_return->key_desc[2] == '/' || key_return->key_desc[2] == 'm' ||
  249. key_return->key_desc[2] == 't' || key_return->key_desc[2] == 'y' ||
  250. key_return->key_desc[4] == 'd' || key_return->key_desc[4] == 'i' ||
  251. key_return->key_desc[4] == 't' || key_return->key_desc[4] == '\\' ||
  252. key_return->key_desc[4] == 'k' || key_return->key_desc[4] == '/' ||
  253. key_return->key_desc[4] == 'l' || key_return->key_desc[4] == 'c' ||
  254. key_return->key_desc[4] == 'z' ||
  255. key_return->key_desc[4] == 'Z' || (key_return->key_desc[4] == 'b'
  256. && key_return->key_desc[5] == 'k') || key_return->key_desc[2] == 'h') {
  257. if(key_return->key_bytes[0] != TB_KEY_CTRL_W && key_return->key_bytes[0] != TB_KEY_PGDN)
  258. curbp->b_mark = NOMARK;
  259. if(is_file_modified(curbp->b_fname) && !file_was_modified_prompt()) {
  260. continue;
  261. }
  262. }
  263. submatch = 0;
  264. if(execute_kbd_macro) {
  265. (key_return->func)();
  266. } else {
  267. u = numeric_argument > 0 ? numeric_argument : power(4, universal_argument);
  268. if(numeric_argument > 0 &&
  269. key_return->universal_argument_action != UA_PREVENT)
  270. key_return->universal_argument_action = UA_REPEAT;
  271. switch(key_return->universal_argument_action) {
  272. case UA_REPEAT:
  273. if(u > 1)
  274. shift_pmark(TRUE, curbp->b_point);
  275. for(; u > 0; u--)
  276. (key_return->func)();
  277. universal_argument = 0;
  278. numeric_argument = 0;
  279. /* For gotochar */
  280. character[0] = '\0';
  281. if(curbp->b_point > curbp->b_epage || curbp->b_point < curbp->b_page)
  282. curbp->b_reframe = TRUE;
  283. break;
  284. default:
  285. (key_return->func)();
  286. break;
  287. }
  288. }
  289. if(temp[0] != 0)
  290. memset(temp, 0, TEMPBUF);
  291. if(key_return->key_desc[2] != 'u')
  292. universal_argument = 0;
  293. } else if(submatch > 0) {
  294. // do nothing
  295. } else {
  296. submatch = 0;
  297. if(unicode_buf[0] != '\0' || *input > 31 || *input == 13 || *input == 9) {
  298. curbp->b_mark = NOMARK;
  299. if(is_file_modified(curbp->b_fname) && !file_was_modified_prompt()) {
  300. continue;
  301. }
  302. }
  303. if(unicode_buf[0] != '\0') {
  304. if(!execute_kbd_macro)
  305. u = numeric_argument > 0 ? numeric_argument : power(4, universal_argument);
  306. else
  307. u = 1;
  308. char tempbuf[7];
  309. strncpy(tempbuf, unicode_buf, 7);
  310. for(; u > 0; u--) {
  311. unicode_buf[0] = tempbuf[0];
  312. insert_unicode();
  313. }
  314. if(is_bracket(input[0], FALSE, NULL)) {
  315. int dir = (input[0] == '}' || input[0] == ']' || input[0] == ')' || input[0] == '>');
  316. curbp->b_point--;
  317. recentbracket = find_matching_bracket(curbp, curwp, dir ? -1 : 1, TRUE);
  318. curbp->b_point++;
  319. }
  320. }
  321. else if(!ignorenotbound)
  322. msg("Not bound");
  323. else if(ignorenotbound) {
  324. msg("");
  325. ignorenotbound = FALSE;
  326. }
  327. if(!execute_kbd_macro) {
  328. universal_argument = 0;
  329. numeric_argument = 0;
  330. }
  331. }
  332. /* For gotochar */
  333. character[0] = '\0';
  334. if(currentcommand != KBD_DELETE_CHAR &&
  335. currentcommand != KBD_DELETE_WORD &&
  336. currentcommand != KBD_CUT &&
  337. currentcommand != KBD_EXPAND &&
  338. currentcommand != KBD_UNDO
  339. ) {
  340. if(curbp->b_opoint > curbp->b_point) {
  341. curbp->b_opoint--;
  342. while(curbp->b_opoint >= curbp->b_point) {
  343. if(*ptr(curbp, curbp->b_opoint) == '\n')
  344. curbp->b_line--;
  345. curbp->b_opoint--;
  346. }
  347. } else if(curbp->b_opoint < curbp->b_point) { /* */
  348. while(curbp->b_opoint < curbp->b_point) {
  349. if(*ptr(curbp, curbp->b_opoint) == '\n')
  350. curbp->b_line++;
  351. curbp->b_opoint++;
  352. }
  353. }
  354. }
  355. if(currentcommand != lastcommand) {
  356. if(lastcommand == KBD_EXPAND) {
  357. if(dynaex.query != NULL) {
  358. free(dynaex.query);
  359. dynaex.query = NULL;
  360. }
  361. dynaex.nquery = -1;
  362. dynaex.nresult = -1;
  363. dynaex.start = -1;
  364. dynaex.end = -1;
  365. dynaex.sp = -1;
  366. dynars_t *dr;
  367. dr = dynaex.results;
  368. while(dynaex.results != NULL) {
  369. dr = dynaex.results;
  370. dynaex.results = dynaex.results->d_next;
  371. if(dr->result != NULL) {
  372. free(dr->result);
  373. dr->result = NULL;
  374. }
  375. if(dr != NULL) {
  376. free(dr);
  377. dr = NULL;
  378. }
  379. }
  380. }
  381. lastcommand = currentcommand;
  382. }
  383. currentcommand = KBD_DEFAULT;
  384. }
  385. if (scrap.data != NULL) free(scrap.data);
  386. tb_set_cursor(0, LINES-1);
  387. tb_present();
  388. tb_shutdown();
  389. return 0;
  390. }
  391. void fatal(char *msg)
  392. {
  393. tb_present();
  394. fprintf(stderr, msg, PROG_NAME);
  395. exit(1);
  396. }
  397. void msg(char *msg, ...)
  398. {
  399. va_list args;
  400. va_start(args, msg);
  401. (void)vsprintf(msgline, msg, args);
  402. va_end(args);
  403. msgflag = TRUE;
  404. if(strlen(msgline) > COLS) {
  405. msgline[COLS] = '\0';
  406. }
  407. }