buffer.c 8.0 KB

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