Add basic lock/unlock support.
[eco.git] / buffer.c
blobcdd97c707d29071c8f1c2b2fad833fd9e1c23eae
1 /*
2 * Copyright (C) 2008 Diego Hernan Borghetti.
3 * Eco
4 */
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
10 #include "debug.h"
11 #include "term.h"
12 #include "screen.h"
13 #include "buffer.h"
14 #include "view.h"
15 #include "eco.h"
16 #include "kill.h"
17 #include "file.h"
20 E_Line *e_line_alloc(void)
22 E_Line *ln;
24 ln= (E_Line *)malloc(sizeof(E_Line));
25 ln->next= NULL;
26 ln->prev= NULL;
27 ln->flag= 0;
28 ln->size= 16;
29 ln->used= 0;
30 ln->text= (char *)malloc(16);
31 return(ln);
34 void e_line_realloc(E_Line *ln)
36 ln->size+= 16;
37 ln->text= (char *)realloc((void *)ln->text, ln->size);
40 void e_line_free(E_Line *ln)
42 free((void *)ln->text);
43 free((void *)ln);
46 void e_line_add(E_Buffer *bf, E_Line *ln)
48 ln->next= bf->line->next;
49 ln->prev= bf->line;
51 if (ln->next)
52 ln->next->prev= ln;
53 bf->line->next= ln;
54 bf->line= ln;
55 bf->nlines++;
58 void e_line_add_first(E_Buffer *bf, E_Line *ln)
60 ln->next= bf->lines;
61 ln->prev= NULL;
63 if (ln->next)
64 ln->next->prev= ln;
66 bf->lines= ln;
67 bf->first= ln; /* auto-reframe. */
68 bf->nlines++;
71 void e_line_remove(E_Buffer *bf, E_Line *ln)
73 if (ln->next)
74 ln->next->prev= ln->prev;
75 if (ln->prev)
76 ln->prev->next= ln->next;
78 if (bf->lines == ln)
79 bf->lines= ln->next;
81 if (bf->first == ln) {
82 if (bf->first->prev)
83 bf->first= bf->first->prev;
84 else
85 bf->first= bf->first->next;
88 if (bf->line == ln) {
89 if (bf->line->next)
90 bf->line= bf->line->next;
91 else
92 bf->line= bf->line->prev;
95 ln->next= NULL;
96 ln->prev= NULL;
97 bf->nlines--;
100 E_Buffer *e_buffer_new(char *file)
102 E_Buffer *bf;
104 bf= (E_Buffer *)malloc(sizeof(E_Buffer));
105 bf->next= NULL;
106 bf->paths= e_file_get_paths(file);
108 if (!bf->paths) {
109 free((void *)bf);
110 return(NULL);
113 if (!bf->paths->path) {
114 free((void *)bf->paths);
115 free((void *)bf);
116 return(NULL);
119 if (!bf->paths->file) {
120 free((void *)bf->paths->path);
121 free((void *)bf->paths);
122 return(NULL);
125 /* remove the full path, only show the name. */
126 bf->name= strrchr(bf->paths->file, '/');
127 if (!bf->name)
128 bf->name= bf->paths->file;
129 else
130 bf->name++;
132 e_debug_printf("path: %s\n", bf->paths->path);
133 e_debug_printf("file: %s\n", bf->paths->file);
134 e_debug_printf("name: %s\n", bf->name);
136 bf->lines= e_line_alloc();
137 bf->first= bf->lines;
138 bf->line= bf->lines;
139 bf->dot= 0;
140 bf->dot_pad= 0;
141 bf->flag= 0;
142 bf->nlines= 1;
143 bf->nl= 1;
144 return(bf);
147 void e_buffer_free(E_Buffer *bf)
149 E_Line *p;
151 /* only remove the lock when we free the buffer. */
152 e_file_lock_rem(bf->paths);
154 free((void *)bf->paths->path);
155 free((void *)bf->paths->file);
156 free((void *)bf->paths->lock_file);
157 free((void *)bf->paths);
159 while (bf->lines) {
160 p= bf->lines->next;
161 e_line_free(bf->lines);
162 bf->lines= p;
164 free((void *)bf);
167 void e_buffer_recalc(E_Buffer *bf)
169 E_Line *p;
171 bf->nl= 1;
172 p= bf->lines;
173 while (p) {
174 if (p == bf->line)
175 break;
176 bf->nl++;
177 p= p->next;
181 /* Add a new line at the current position in the buffer. */
182 void e_buffer_newline(E_Buffer *bf)
184 E_Line *ln;
186 BUFFER_SET(bf, BUFFER_FLUSH);
188 ln= e_line_alloc();
189 e_line_add(bf, ln);
191 /* Always clean the current position, the new line don't
192 * have any text..
194 bf->dot= 0;
195 bf->nl++;
198 /* Add a new line at the begin of the buffer. */
199 void e_buffer_newline_first(E_Buffer *bf)
201 E_Line *ln;
203 BUFFER_SET(bf, BUFFER_FLUSH);
205 ln= e_line_alloc();
206 e_line_add_first(bf, ln);
207 bf->dot= 0;
208 bf->nl++;
211 /* Split the current line in two line, the starting
212 * point for split is the current cursor position.
214 void e_buffer_splitline(E_Buffer *bf)
216 E_Line *ln;
217 int i, dot;
219 /* save the current line. */
220 ln= bf->line;
222 /* and the cursor position. */
223 dot= bf->dot;
225 /* insert a new line and add all the character
226 * from the current position, to the end.
228 e_buffer_newline(bf);
229 for (i= dot; i < ln->used; i++)
230 e_buffer_insert(bf, ln->text[i]);
232 ln->used= dot;
234 /* Always go to the begin of the line. */
235 bf->dot= 0;
238 /* Join two line, the source line is the current line and
239 * the dest line is the prev.
241 void e_buffer_joinline(E_Buffer *bf)
243 E_Line *ln;
244 int i, dot;
246 /* if we are at the begin, return. */
247 if (!bf->line->prev)
248 return;
250 /* save the current line. */
251 ln= bf->line;
253 /* move to the previous. */
254 e_buffer_up(bf);
256 /* put the cursor to the end of the line. */
257 bf->dot= bf->line->used;
259 /* and save the position. */
260 dot= bf->dot;
262 /* insert all the characters. */
263 for (i= 0; i < ln->used; i++)
264 e_buffer_insert(bf, ln->text[i]);
266 /* remove the unused line. */
267 e_line_remove(bf, ln);
268 e_line_free(ln);
270 /* restore the original cursor position. */
271 bf->dot= dot;
272 e_buffer_recalc(bf);
275 /* Remove the current line from the buffer, this also
276 * can save the line in the cut-buffer.
278 void e_buffer_killline(E_Buffer *bf, int cut)
280 E_Line *ln;
282 if (cut)
283 e_kill_cut(bf->line);
285 if ((!bf->line->prev) && (!bf->line->next)) {
286 /* If we don't have previous or next line,
287 * the only thing to do is clean the
288 * current line.
290 e_buffer_cleanline(bf);
291 return;
293 else if (!bf->line->next) {
294 /* If we don't have next line, only clean. */
295 e_buffer_cleanline(bf);
296 return;
299 if (bf->line->used) {
300 /* This work like emacs, if the line have text
301 * first clean the text.
303 e_buffer_cleanline(bf);
304 return;
307 /* And if the line don't have text, remove it. */
308 BUFFER_SET(bf, BUFFER_FLUSH);
310 ln= bf->line;
311 e_buffer_down(bf);
312 e_line_remove(bf, ln);
313 e_line_free(ln);
314 e_buffer_recalc(bf);
317 /* Insert a character in the current line at the current
318 * cursor position.
320 void e_buffer_insert(E_Buffer *bf, int c)
322 int i;
324 BUFFER_SET(bf, BUFFER_FLUSH);
326 /* check if we need reallocate the line. */
327 if ((bf->line->used+1) >= bf->line->size)
328 e_line_realloc(bf->line);
330 /* if the cursor is not at the end of the line,
331 * we need scroll the text to the right.
333 if (bf->dot != bf->line->used) {
334 for (i= bf->line->used; i > bf->dot; i--)
335 bf->line->text[i]= bf->line->text[i-1];
338 /* insert the new character. */
339 bf->line->text[bf->dot]= c;
340 bf->dot++;
341 bf->line->used++;
344 /* Just that, insert a backspace ;) */
345 void e_buffer_backspace(E_Buffer *bf)
347 int i;
349 /* The only case that we have here is if the cursor position
350 * is at the begin of the line, in that case we need join
351 * the two lines.
353 if (bf->dot > 0) {
354 BUFFER_SET(bf, BUFFER_FLUSH);
356 /* If the cursor is not at the end of the line,
357 * we need scroll the text.
359 for (i= bf->dot; i < bf->line->used; i++)
360 bf->line->text[i-1]= bf->line->text[i];
361 bf->dot--;
362 bf->line->used--;
364 else
365 e_buffer_joinline(bf);
368 void e_buffer_del(E_Buffer *bf)
370 if (bf->dot+1 < bf->line->used) {
371 bf->dot++;
372 e_buffer_backspace(bf);
376 void e_buffer_up(E_Buffer *bf)
378 if (bf->line->prev) {
379 bf->line= bf->line->prev;
380 if (bf->dot > bf->line->used)
381 bf->dot= bf->line->used;
382 bf->nl--;
386 void e_buffer_down(E_Buffer *bf)
388 if (bf->line->next) {
389 bf->line= bf->line->next;
390 if (bf->dot > bf->line->used)
391 bf->dot= bf->line->used;
392 bf->nl++;
396 void e_buffer_left(E_Buffer *bf)
398 if (bf->dot > 0)
399 bf->dot--;
400 else {
401 e_buffer_up(bf);
402 e_buffer_eol(bf);
406 void e_buffer_right(E_Buffer *bf)
408 if (bf->dot < bf->line->used)
409 bf->dot++;
410 else {
411 e_buffer_down(bf);
412 e_buffer_bol(bf);
416 void e_buffer_goto_begin(E_Buffer *bf)
418 bf->line= bf->lines;
419 bf->dot= 0;
420 bf->nl= 1;
423 void e_buffer_goto_end(E_Buffer *bf)
425 E_Line *p;
427 p= bf->line;
428 while (p->next)
429 p= p->next;
431 bf->line= p;
432 bf->dot= 0;
433 bf->nl= bf->nlines+1; /* zero */
436 void e_buffer_bol(E_Buffer *bf)
438 bf->dot= 0;
441 void e_buffer_eol(E_Buffer *bf)
443 bf->dot= bf->line->used;
446 void e_buffer_scroll(E_Buffer *bf, int nline, int dir)
448 int i;
450 for(i= 0; i < nline; i++) {
451 if (dir) {
452 if (!bf->first->prev)
453 break;
454 bf->first= bf->first->prev;
456 else {
457 if (!bf->first->next)
458 break;
459 bf->first= bf->first->next;
462 bf->line= bf->first;
463 if (bf->dot > bf->line->used)
464 bf->dot= bf->line->used;
465 e_buffer_recalc(bf);
468 void e_buffer_goto(E_Buffer *bf, int line)
470 E_Line *p;
471 int num;
473 num= 1;
474 p= bf->lines;
475 while (p) {
476 if (num == line)
477 break;
478 num++;
479 p= p->next;
482 if (!p)
483 return;
485 bf->nl= num;
486 bf->line= p;
487 if (bf->dot > bf->line->used)
488 bf->dot= 0;
491 void e_buffer_cleanline(E_Buffer *bf)
493 BUFFER_SET(bf, BUFFER_FLUSH);
495 bf->line->used= 0;
496 bf->dot= 0;
499 E_Line *__e_buffer_search(E_Buffer *bf, char *pattern, int *dot_found, int dir)
501 E_Line *ln;
502 int i, e, a, len, dot, found;
504 len= strlen(pattern);
505 found= 0;
506 dot= 0;
508 /* I know.. this is not the best backward.. but work. */
509 if (dir == BUFFER_SEARCH_FORWARD)
510 ln= bf->line;
511 else
512 ln= bf->line->prev;
514 while (ln) {
515 if (dir == BUFFER_SEARCH_FORWARD && ln == bf->line)
516 i= bf->dot;
517 else
518 i= 0;
520 for (; i < ln->used; i++) {
521 if (ln->text[i] == pattern[0]) {
522 /* save the current position of the cursor. */
523 a= i;
524 dot= i;
525 found= 1;
527 for (e= 0; e < len; e++) {
528 if (ln->text[i] != pattern[e]) {
529 found= 0;
530 break;
533 i++;
534 if (i > ln->used) {
535 found= 0;
536 break;
540 i= a;
543 if (found)
544 break;
547 if (found)
548 break;
550 if (dir == BUFFER_SEARCH_FORWARD)
551 ln= ln->next;
552 else
553 ln= ln->prev;
556 if (dir == BUFFER_SEARCH_FORWARD)
557 *dot_found= (dot+len);
558 else
559 *dot_found= dot;
561 return(ln);
564 void e_buffer_search(E_Buffer *bf, char *pattern, int dir)
566 E_Line *ln;
567 int dot;
569 ln= __e_buffer_search(bf, pattern, &dot, dir);
570 if (ln) {
571 bf->first= ln;
572 bf->line= ln;
573 bf->dot= dot;
574 e_buffer_recalc(bf);
578 int e_buffer_replace(E_Buffer *bf, char *pattern, char *replace)
580 E_Line *ln;
581 int i, c, len, ncount, dot;
583 c= strlen(pattern);
584 len= strlen(replace);
585 ncount= 0;
587 ln= __e_buffer_search(bf, pattern, &dot, BUFFER_SEARCH_FORWARD);
588 while (ln) {
589 /* first set the line, so all the next call
590 * work fine.
592 bf->first= ln;
593 bf->line= ln;
594 bf->dot= dot;
595 e_buffer_recalc(bf);
597 /* now remove the pattern. */
598 for (i= 0; i < c; i++)
599 e_buffer_backspace(bf);
601 /* and now insert the new string. */
602 for (i= 0; i < len; i++)
603 e_buffer_insert(bf, replace[i]);
605 ncount++;
606 ln= __e_buffer_search(bf, pattern, &dot, BUFFER_SEARCH_FORWARD);
608 return(ncount);