buffer.c 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. /* buffer.c, Ait Emacs, Kevin Bloom, BSD 3-Clause, 2023-2025 */
  2. #include <assert.h>
  3. #include <string.h>
  4. #include "syntax.h"
  5. #include "termbox.h"
  6. void buffer_init(buffer_t *bp)
  7. {
  8. bp->b_mark = NOMARK;
  9. bp->b_pmark[0] = NOMARK;
  10. bp->b_pmark[1] = NOMARK;
  11. bp->b_pmark[2] = NOMARK;
  12. bp->b_pmark[3] = NOMARK;
  13. bp->b_pmark[4] = NOMARK;
  14. bp->b_point = 0;
  15. bp->b_cpoint = 0;
  16. bp->b_opoint = 0;
  17. bp->b_page = 0;
  18. bp->b_opage = 0;
  19. bp->b_epage = 0;
  20. bp->b_reframe = 0;
  21. bp->b_size = 0;
  22. bp->b_psize = 0;
  23. bp->b_flags = 0;
  24. bp->b_cnt = 0;
  25. bp->b_buf = NULL;
  26. bp->b_ebuf = NULL;
  27. bp->b_gap = NULL;
  28. bp->b_egap = NULL;
  29. bp->b_next = NULL;
  30. bp->b_prev = NULL;
  31. bp->b_fname[0] = '\0';
  32. bp->b_fmtime = 0;
  33. bp->b_bname[0] = '\0';
  34. bp->b_path = TRUE;
  35. bp->b_undo = NULL;
  36. bp->b_redo = NULL;
  37. bp->b_mode = NULL;
  38. bp->b_line = 1;
  39. bp->b_row = 1;
  40. }
  41. /*
  42. Find a buffer by filename or create if requested.
  43. If an initialization run, put the buffers at the end otherwise,
  44. put them in the beginning.
  45. */
  46. buffer_t* find_buffer (char *fname, int cflag, int init)
  47. {
  48. buffer_t *bp = NULL;
  49. buffer_t *ebp = NULL;
  50. buffer_t *sb = NULL;
  51. smode_t *k;
  52. char filepath[PATH_MAX];
  53. int len, extlen, c = 0;
  54. int match = FALSE, i = 1;
  55. strcpy(filepath, fname);
  56. bp = bheadp;
  57. while (bp != NULL) {
  58. if (strcmp (fname, bp->b_fname) == 0 || strcmp(fname, bp->b_bname) == 0) {
  59. return (bp);
  60. }
  61. bp = bp->b_next;
  62. }
  63. if (cflag != FALSE) {
  64. if ((bp = (buffer_t *) malloc (sizeof (buffer_t))) == NULL)
  65. return (0);
  66. buffer_init(bp);
  67. assert(bp != NULL);
  68. if(filepath[0] != '\0') {
  69. strcpy(bp->b_fname, fname);
  70. modify_buffer_name(bp, 0);
  71. bp->b_fname[0] = '\0';
  72. for (c = 0, ebp = bheadp; ebp != NULL; ebp = ebp->b_next, c++) {
  73. while((match = compare_buffer_name(bp, ebp)) == TRUE && i < 20) {
  74. strcpy(bp->b_fname, fname);
  75. modify_buffer_name(bp, i);
  76. bp->b_fname[0] = '\0';
  77. modify_buffer_name(ebp, i);
  78. i++;
  79. for(window_t *wp = wheadp; wp != NULL; wp = wp->w_next) {
  80. if(wp->w_bufp == ebp) {
  81. wp->w_update = TRUE;
  82. }
  83. }
  84. }
  85. if(match) break;
  86. i = 1;
  87. }
  88. bp->b_bname[strlen(bp->b_bname)] = '\0';
  89. for(k = modes; k->extension != NULL; ++k) {
  90. len = strlen(bp->b_bname);
  91. extlen = strlen(k->extension) - 1;
  92. c = 0;
  93. for(int f = len - 1 - extlen; c <= extlen; f++, c++) {
  94. if(bp->b_bname[f] != k->extension[c]) {
  95. c = 0;
  96. break;
  97. }
  98. }
  99. if(c > 0) {
  100. bp->b_mode = k;
  101. break;
  102. }
  103. }
  104. }
  105. /* find the place in the list to insert this buffer */
  106. if (bheadp == NULL) {
  107. bheadp = bp;
  108. } else if (!init) {
  109. /* insert at the beginning */
  110. // bp->b_next = bheadp;
  111. // bheadp = bp;
  112. bp->b_next = curbp->b_next;
  113. curbp->b_next = bp;
  114. } else {
  115. for (sb = bheadp; sb->b_next != NULL; sb = sb->b_next)
  116. if (strcmp (sb->b_next->b_fname, fname) > 0)
  117. break;
  118. /* and insert it */
  119. bp->b_next = sb->b_next;
  120. sb->b_next = bp;
  121. }
  122. }
  123. return bp;
  124. }
  125. /* unlink from the list of buffers, free associated memory, assumes buffer has been saved if modified */
  126. int delete_buffer (buffer_t *bp)
  127. {
  128. buffer_t *sb = NULL;
  129. /* we must have switched to a different buffer first */
  130. assert(bp != curbp);
  131. /* if buffer is the head buffer */
  132. if (bp == bheadp) {
  133. bheadp = bp->b_next;
  134. } else {
  135. /* find place where the bp buffer is next */
  136. for (sb = bheadp; sb->b_next != bp && sb->b_next != NULL; sb = sb->b_next)
  137. ;
  138. assert(sb->b_next == bp || sb->b_next == NULL);
  139. sb->b_next = bp->b_next;
  140. }
  141. /* now we can delete */
  142. free(bp->b_buf);
  143. free(bp);
  144. return TRUE;
  145. }
  146. void prev_buffer()
  147. {
  148. buffer_t *bp;
  149. disassociate_b(curwp);
  150. for(bp = bheadp; bp->b_next != NULL; bp = bp->b_next) {
  151. if(bp->b_next == curbp)
  152. break;
  153. }
  154. curbp = bp;
  155. associate_b2w(curbp,curwp);
  156. }
  157. void next_buffer()
  158. {
  159. disassociate_b(curwp);
  160. curbp = curbp->b_next != NULL ? curbp->b_next : bheadp;
  161. associate_b2w(curbp,curwp);
  162. }
  163. void switch_buffer()
  164. {
  165. buffer_t *next, *prev, *bp;
  166. int ret = 0;
  167. char message[TEMPBUF] = "Switch to buffer (default ";
  168. assert(curbp != NULL);
  169. assert(bheadp != NULL);
  170. for(prev = bheadp; prev->b_next != NULL; prev = prev->b_next) {
  171. if(prev->b_next == curbp)
  172. break;
  173. }
  174. if(prev == NULL)
  175. prev = bheadp;
  176. strcat(message, prev->b_bname);
  177. strcat(message, "): ");
  178. next = getbuffername(message, (char*)temp, PATH_MAX, &ret);
  179. if(!ret)
  180. return;
  181. if(next == curbp) {
  182. msg("Same buffer!");
  183. return;
  184. }
  185. if(next == NULL) {
  186. next = prev;
  187. if(next == NULL)
  188. next = bheadp;
  189. }
  190. if(next != NULL) {
  191. tb_present();
  192. disassociate_b(curwp);
  193. /* If a normal next-buffer, no shifting required */
  194. if(next == curbp->b_next ||
  195. (curbp->b_next == NULL && next == bheadp)) {
  196. goto assign;
  197. }
  198. /* prev is the next buffer's previous buffer */
  199. for(prev = bheadp; prev != NULL; prev = prev->b_next) {
  200. if(prev->b_next == next)
  201. break;
  202. }
  203. /* bp is the current buffer's previous buffer */
  204. for(bp = bheadp; bp != NULL; bp = bp->b_next) {
  205. if(bp->b_next == curbp)
  206. break;
  207. }
  208. /* if the next buffer has a previous buffer, that buffer's
  209. next buffer is the current buffer
  210. */
  211. if(prev != NULL && prev->b_next != NULL)
  212. prev->b_next = curbp;
  213. /* if the current buffer has a previous buffer, that buffer's
  214. next buffer is the current buffer's next buffer
  215. */
  216. if(bp != NULL && bp->b_next != NULL)
  217. bp->b_next = curbp->b_next;
  218. /* if the current buffer is the head buffer, the current buffer's
  219. next buffer becomes the head buffer
  220. if the next buffer is the head buffer, the current buffer
  221. becomes the head buffer
  222. */
  223. if(curbp == bheadp)
  224. bheadp = curbp->b_next;
  225. else if(next == bheadp)
  226. bheadp = curbp;
  227. /* if the next buffer's next buffer is the current buffer
  228. then the next buffer's next buffer becomes the current
  229. buffer's next buffer
  230. */
  231. if(next->b_next == curbp)
  232. next->b_next = curbp->b_next;
  233. /* Finally, set the current buffer's next buffer to the
  234. to the next buffer
  235. */
  236. curbp->b_next = next;
  237. assign:
  238. curbp = next;
  239. associate_b2w(curbp,curwp);
  240. } else {
  241. msg("Buffer doesn't exist");
  242. }
  243. tb_set_cursor(0, MSGLINE);
  244. clrtoeol(0, MSGLINE);
  245. }
  246. char* get_buffer_name(buffer_t *bp)
  247. {
  248. return (strlen(bp->b_fname) > 0) ? bp->b_fname : bp->b_bname;
  249. }
  250. int count_buffers()
  251. {
  252. buffer_t* bp;
  253. int i = 0;
  254. for (i=0, bp=bheadp; bp != NULL; bp = bp->b_next)
  255. i++;
  256. return i;
  257. }
  258. int modified_buffers()
  259. {
  260. buffer_t* bp;
  261. for (bp=bheadp; bp != NULL; bp = bp->b_next)
  262. if (bp->b_flags & B_MODIFIED)
  263. return TRUE;
  264. return FALSE;
  265. }
  266. int compare_buffer_name(buffer_t *bp, buffer_t *ebp)
  267. {
  268. int match = FALSE;
  269. int elen = strlen(ebp->b_bname);
  270. int len = strlen(bp->b_bname);
  271. int longer_name = elen > len ? elen : len;
  272. for(int i = 0; i < longer_name; i++)
  273. if(ebp->b_bname[i] == bp->b_bname[i]) {
  274. match = TRUE;
  275. } else {
  276. match = FALSE;
  277. break;
  278. }
  279. return match;
  280. }
  281. /* Used to either truncate or expand buffer name
  282. flag = 0, truncate to file name only (.mailrc)
  283. flag > 0, truncate to previous directory / file name (i.e. home/.mailrc)
  284. i.e. for a file found at /home/kev/src/ait/README.md, if we had a flag
  285. of 3, we'd see: kev/src/ait/README.md
  286. */
  287. void modify_buffer_name(buffer_t *bp, int flag)
  288. {
  289. char *dir, fname[PATH_MAX + 1];
  290. const char *list_dirs[20];
  291. int d = 0;
  292. strcpy(fname, bp->b_fname);
  293. dir = "\0";
  294. dir = strtok(fname, "/");
  295. while( dir != NULL ) {
  296. list_dirs[d] = dir;
  297. d++;
  298. dir = strtok(NULL, "/");
  299. }
  300. if(flag > 0) {
  301. strcpy(bp->b_bname, list_dirs[d-(flag + 1)]);
  302. strcat(bp->b_bname, "/");
  303. flag--;
  304. for(; flag > 0; flag--) {
  305. strcat(bp->b_bname, list_dirs[d-(flag + 1)]);
  306. strcat(bp->b_bname, "/");
  307. }
  308. }
  309. strcat(bp->b_bname, list_dirs[d-1]);
  310. free(dir);
  311. }