main.c 10 KB

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