add on-the-fly code checking
[vim_extended.git] / src / code_check.c
blob5bb3bfe0cd038de74cfdfee9ef3aa687a7cc9ba9
1 /* vi:set ts=8 sts=4 sw=4:
3 * VIM - Vi IMproved by Bram Moolenaar
4 * CodeCheck extension by Birgi Tamersoy
5 * birgitamersoy@gmail.com
7 * Do ":help uganda" in Vim to read copying and usage conditions.
8 * Do ":help credits" in Vim to see a list of people who contributed.
9 * See README.txt for an overview of the Vim source code.
13 * code_check.c: On-the-fly syntax checking tool.
16 #include <pthread.h>
17 #include <semaphore.h>
18 #include "vim.h"
20 #define CC_VERSION 0.1
22 enum cc_ret_vals {
23 CC_FAIL = 0,
24 CC_SUCCESS,
25 CC_BUFEXISTS,
26 CC_NOSUCHBUF,
29 enum cc_ew_types {
30 CC_NOEW = 0,
31 CC_WARNING,
32 CC_ERROR
35 enum cc_search_mode {
36 CC_FOR_ADD = 0,
37 CC_FOR_REM,
38 CC_FOR_FIND
41 enum cc_print_direc {
42 CC_FWD = 0,
43 CC_REW
46 enum cc_copy_type {
47 CC_STANDALONE = 0,
48 CC_MASTER,
49 CC_PROJECT /* Makefile exists */
53 * Multiple buffers can be syntax checked simultaneously and each buffer
54 * has its own list of errors and warnings. Consequently we have two list
55 * structures. One holds information about processed buffers and the other
56 * one holds information about buffer-specific errors & warnings.
58 * Both lists will be accessed frequently, so they will be sorted:
59 * buffer list will be sorted with respect to full file names, and
60 * error/warning list will be sorted with respect to line numbers.
63 #define MAX_EW_TEXT 200
65 /* compile command length */
66 #define MAX_CMD_LENGTH 600
68 #define MAX_PATH_LENGTH 300
70 typedef struct cc_ewline_S cc_ewline_T;
71 struct cc_ewline_S {
72 cc_ewline_T *prev;
73 cc_ewline_T *next;
74 int ew_type;
75 char_u ew_text[MAX_EW_TEXT];
76 linenr_T ew_lnum;
79 typedef struct cc_bufline_S cc_bufline_T;
80 struct cc_bufline_S {
81 cc_bufline_T *prev;
82 cc_bufline_T *next;
83 char_u *buf_name;
84 cc_ewline_T *buf_ewlist_head;
85 pthread_mutex_t buf_mutex;
86 char_u buf_compile_cmd[MAX_CMD_LENGTH];
89 typedef struct cc_info_S cc_info_T;
90 struct cc_info_S {
91 int cc_bufcount;
92 cc_bufline_T *cc_list_head;
93 cc_bufline_T *cc_list_curr;
96 /* global list of buffers. */
97 static cc_info_T cc_list;
98 #define MAX_BUFLINES 100
99 static cc_bufline_T *cc_bufline_ptrs[MAX_BUFLINES];
101 /* supported languages, the format should be ".<ext1>" ... */
102 /* each extension should padded with ' ' characters, st. total is 5 chars */
103 static char_u *cc_sup_exts = (char_u *) ".c "
104 ".cpp ";
106 /* worker thread. */
107 static pthread_t cc_slave;
109 /* a queue for the pending jobs that the worker thread should handle.
110 * basically a list of buffer compile requests. */
111 enum cc_pjob_types {
112 CC_COMPILE = 0
115 typedef struct cc_pjob_S cc_pjob_T;
116 struct cc_pjob_S {
117 int cc_pjob_type;
118 buf_T *cc_pjob_buf;
121 #define MAX_PENDING_JOBS 10
122 static cc_pjob_T *cc_pjobs[MAX_PENDING_JOBS];
123 static int pindex = 0;
124 static int cindex = 0;
126 static sem_t full;
127 static sem_t empty;
128 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
130 /* functions */
131 static void cc_free_ewlist(cc_ewline_T *ewlist_head);
132 static cc_bufline_T *cc_locate_buf(buf_T *buf);
133 static cc_bufline_T *cc_locate_buf_bin(buf_T *buf, int *buf_idx,
134 int *put_before, int mode);
135 static void cc_free_buflist(cc_info_T *cc_list_a,
136 cc_bufline_T *cc_bufline_ptrs_a[MAX_BUFLINES]);
137 static int cc_start_slave_thread(void);
138 static void *cc_slave_sroutine(void *args);
139 static int cc_pjobs_produce(cc_pjob_T *tmp_pjob);
140 static cc_pjob_T *cc_pjobs_consume(void);
141 static int cc_pjobs_buf_exists(cc_pjob_T *tmp_pjob);
142 static int cc_create_tmp_copy(buf_T *buf, char_u *tmp_copy_ffname,
143 int copy_type);
144 static int cc_compile_tmp_copy(cc_bufline_T *bufline,
145 char_u *tmp_copy_ffname, int copy_type);
146 static cc_ewline_T *cc_create_ewlist(char_u *tmp_out_ffname);
147 void cc_sigalrm_handler(int signum);
148 void cc_update_screen(void);
149 static void cc_free(void);
150 static cc_bufline_T* cc_add_buf(buf_T *buf);
151 static int cc_set_tmp_copy_ffname(buf_T *buf, char_u *tmp_copy_ffname);
153 static int cc_started = FALSE;
154 static int old_p_ut = 0;
157 * TODO:
159 * 2. error/warning format should be added so that new languages are
160 * easily added to the code_check.c.
164 * Initializes the related structures.
167 cc_init(void) {
168 int i;
169 int retval;
171 cc_list.cc_bufcount = 0;
172 cc_list.cc_list_head = NULL;
173 cc_list.cc_list_curr = NULL;
175 retval = sem_init(&full, 0, 0);
176 if (retval)
177 return CC_FAIL;
179 retval = sem_init(&empty, 0, MAX_PENDING_JOBS);
180 if (retval)
181 return CC_FAIL;
183 cc_start_slave_thread();
185 for (i = 0; i < MAX_BUFLINES; ++i)
186 cc_bufline_ptrs[i] = NULL;
188 for (i = 0; i < MAX_PENDING_JOBS; ++i)
189 cc_pjobs[i] = NULL;
191 old_p_ut = p_ut;
192 p_ut = 1000;
194 cc_started = TRUE;
196 return CC_SUCCESS;
200 * Returns the current number of buffers in the watchlist.
203 cc_get_bufcount(void) {
204 return cc_list.cc_bufcount;
208 * Returns TRUE if CodeCheck is already started.
211 cc_get_is_started(void) {
212 return cc_started;
216 * CodeCheck creates a working thread which would run in the background.
217 * This thread is responsible of compiling the specified buffers, parsing
218 * the outputs and forming the corresponding error/warning lists.
220 static int
221 cc_start_slave_thread(void) {
222 pthread_attr_t *attr = NULL;
223 int retval;
225 attr = (pthread_attr_t *) calloc(1, sizeof(pthread_attr_t));
227 retval = pthread_attr_init(attr);
228 if (retval)
229 goto sslave_fail;
231 retval = pthread_attr_setdetachstate(attr, PTHREAD_CREATE_DETACHED);
232 if (retval)
233 goto sslave_fail;
235 retval = pthread_create(&cc_slave, attr, cc_slave_sroutine, NULL);
236 if (retval)
237 goto sslave_fail;
239 return CC_SUCCESS;
241 sslave_fail:
242 free(attr);
243 return CC_FAIL;
247 * Function updates the error/warning line numbers after new lines are inserted
248 * or some existing lines are removed. This is just to move the highlighted
249 * parts before an actual compile.
251 * TODO: This function is mainly copied from color_me.c (the preliminary
252 * project). So, double check if everything is OK or not!!!
255 cc_update_ew_lnums(buf_T *buf, int lnum, int col, long xtra) {
256 if (!cc_started)
257 return CC_FAIL;
259 cc_bufline_T *tmp_bufline;
260 cc_ewline_T *tmp_ewline;
261 char_u *line = NULL;
262 int dummy;
263 int i;
264 int skip = FALSE;
266 tmp_bufline = cc_locate_buf_bin(buf, &dummy, &dummy, CC_FOR_FIND);
267 if (tmp_bufline == NULL)
268 return CC_FAIL;
270 pthread_mutex_lock(&(tmp_bufline->buf_mutex));
272 for (tmp_ewline = tmp_bufline->buf_ewlist_head;
273 tmp_ewline != NULL; tmp_ewline = tmp_ewline->next) {
275 skip = FALSE;
276 if (tmp_ewline->ew_lnum < lnum)
277 continue;
278 if (tmp_ewline->ew_lnum == lnum) {
279 /* is this the beginning of the line? */
280 line = ml_get_buf(buf, lnum, FALSE);
281 for (i = col - 1; i > 0 &&
282 (line[i] == '\t' || line[i] == ' '); --i)
284 if (i > 0)
285 continue;
287 tmp_ewline->ew_lnum += xtra;
290 pthread_mutex_unlock(&(tmp_bufline->buf_mutex));
291 return CC_SUCCESS;
295 * Worker thread function, which continuously checks for unserved requests.
297 static void *
298 cc_slave_sroutine(void *args) {
299 cc_pjob_T *tmp_pjob;
300 cc_bufline_T *tmp_bufline;
301 int retval;
302 int dummy;
303 char_u tmp_copy_ffname[MAX_PATH_LENGTH];
304 char_u tmp_out_ffname[MAX_CMD_LENGTH];
305 cc_ewline_T *ew_head = NULL;
307 while (TRUE) {
308 /* consume a pending job */
309 tmp_pjob = cc_pjobs_consume();
310 if (tmp_pjob == NULL)
311 continue;
313 /* when gui mode is used, an other update screen is required. */
314 /* i don't know why :)!!!. */
315 if (gui.in_use)
316 cc_update_screen();
318 /* find the buffer line */
320 /* create the temporary copy */
321 retval = cc_create_tmp_copy(tmp_pjob->cc_pjob_buf,
322 tmp_copy_ffname, CC_STANDALONE);
323 if (retval == CC_FAIL)
324 continue;
326 tmp_bufline = cc_locate_buf_bin(tmp_pjob->cc_pjob_buf, &dummy,
327 &dummy, CC_FOR_FIND);
328 if (tmp_bufline == NULL)
329 continue;
331 /* compile the temporary copy */
332 retval = cc_compile_tmp_copy(tmp_bufline, tmp_copy_ffname,
333 CC_STANDALONE);
334 if (retval == CC_FAIL)
335 continue;
337 /* create the error list from the temporary copy */
338 sprintf((char *)tmp_out_ffname, "%s.out", (char *)tmp_copy_ffname);
339 ew_head = cc_create_ewlist(tmp_out_ffname);
341 /* having ew_head == NULL is not an error, since all
342 * the errors and the warnings may be cleaned. */
344 /* free the previous list & set the new list */
345 pthread_mutex_lock(&(tmp_bufline->buf_mutex));
347 cc_free_ewlist(tmp_bufline->buf_ewlist_head);
348 tmp_bufline->buf_ewlist_head = ew_head;
350 pthread_mutex_unlock(&(tmp_bufline->buf_mutex));
352 /* update the screen */
353 cc_update_screen();
355 /* that would be it for this buffer */
360 * Function is used to update the screen properly.
362 void
363 cc_update_screen(void) {
364 if (!gui.in_use) {
365 /* in console mode updating the screen from the worker thread
366 * does not cause any problems. */
367 update_topline();
368 validate_cursor();
369 update_screen(SOME_VALID);
370 setcursor();
371 cursor_on();
372 out_flush();
373 } else {
374 /* updating the screen in gui mode is troublesome. */
375 char_u bytes[3];
377 bytes[0] = CSI;
378 bytes[1] = KS_EXTRA;
379 bytes[2] = KE_REDRAW;
381 add_to_input_buf(bytes, 3);
386 * Function returns the correct error/warning type for a specific lnum.
387 * Returns CC_NOEW if the lnum does not have an error or a warning.
390 cc_get_ew_type(buf_T *buf, linenr_T lnum) {
391 if (!cc_started)
392 return CC_FAIL;
394 cc_bufline_T *tmp_bufline;
395 cc_ewline_T *tmp_ewline;
396 int dummy;
397 int ew_type = CC_NOEW;
399 tmp_bufline = cc_locate_buf_bin(buf, &dummy, &dummy, CC_FOR_FIND);
400 if (tmp_bufline == NULL)
401 return CC_NOEW;
403 pthread_mutex_lock(&(tmp_bufline->buf_mutex));
404 tmp_ewline = tmp_bufline->buf_ewlist_head;
405 while (tmp_ewline != NULL) {
406 if (tmp_ewline->ew_lnum != lnum) {
407 tmp_ewline = tmp_ewline->next;
408 continue;
411 if (ew_type < tmp_ewline->ew_type)
412 ew_type = tmp_ewline->ew_type;
414 tmp_ewline = tmp_ewline->next;
416 pthread_mutex_unlock(&(tmp_bufline->buf_mutex));
417 return ew_type;
421 * Function creates a quickfix error list from the compiler output.
422 * TODO: Right now works only for .c files and the gcc compiler.
423 * Should be automated to work with multiple languages and multiple
424 * compilers.
426 static cc_ewline_T *
427 cc_create_ewlist(char_u *tmp_out_ffname) {
428 FILE *err_file = NULL;
429 char_u *buf = NULL;
430 size_t len = 0;
431 size_t read;
432 cc_ewline_T *ew_head = NULL;
433 cc_ewline_T *ew_curr = NULL;
434 char_u *token1 = NULL;
435 char_u *token2 = NULL;
436 char_u *token3 = NULL;
437 char_u *token4 = NULL;
438 cc_ewline_T *tmp_ewline = NULL;
441 err_file = fopen((char *)tmp_out_ffname, "r");
442 if (err_file == NULL)
443 return NULL;
445 while ((read = getline((char **) &buf, &len, err_file)) != -1) {
446 token1 = (char_u *)strtok((char *) buf, ":");
447 token2 = (char_u *)strtok(NULL, ":");
448 token3 = (char_u *)strtok(NULL, ":");
449 token4 = (char_u *)strtok(NULL, ":");
451 /* TODO: be sure it is gXX type of output. */
453 if (!token4)
454 continue;
456 tmp_ewline = (cc_ewline_T *) calloc(1, sizeof(cc_ewline_T));
457 STRCPY(tmp_ewline->ew_text, token4);
458 /* remove the last '\n' from the error/warning message */
459 tmp_ewline->ew_text[STRLEN(tmp_ewline->ew_text) - 1] = '\0';
460 tmp_ewline->ew_lnum = (linenr_T) atoi((char *) token2);
461 tmp_ewline->ew_type = token3[1] == 'w' ? CC_WARNING : CC_ERROR;
463 if (!ew_head) {
464 ew_head = tmp_ewline;
465 ew_curr = tmp_ewline;
466 } else {
467 ew_curr->next = tmp_ewline;
468 tmp_ewline->prev = ew_curr;
469 ew_curr = ew_curr->next;
473 /* buf should be freed, because it is allocated in getline */
474 if (buf)
475 free(buf);
477 return ew_head;
481 * Function compiles the temporary copy saving the output in a
482 * temporary file.
484 static int
485 cc_compile_tmp_copy(cc_bufline_T *bufline, char_u *tmp_copy_ffname,
486 int copy_type) {
487 char_u cmd[MAX_CMD_LENGTH];
489 if (bufline->buf_compile_cmd[0] == NUL)
490 return CC_FAIL;
492 /* clear the arrays. */
493 memset(cmd, 0, MAX_CMD_LENGTH);
495 /* direct STDOUT & STDERR to the same file, so that the output of
496 * this will not interfer with the vim terminal */
497 sprintf((char *)cmd, "%s > %s.out 2>&1",
498 (char *)bufline->buf_compile_cmd, (char *)tmp_copy_ffname);
499 #if 1
500 system((char *)cmd);
501 #endif
503 return CC_SUCCESS;
507 * Function creates a temporary copy of the buffer. copy_type determines
508 * the type of the copy process.
510 static int
511 cc_create_tmp_copy(buf_T *buf, char_u *tmp_copy_ffname, int copy_type) {
512 int retval;
513 char_u cmd[MAX_CMD_LENGTH];
515 switch (copy_type) {
516 case CC_STANDALONE:
517 cc_set_tmp_copy_ffname(buf, tmp_copy_ffname);
518 sprintf((char *)cmd, "touch %s", (char *)tmp_copy_ffname);
520 system((char *)cmd);
521 #if 1
522 retval = buf_write(buf, tmp_copy_ffname, NULL,
523 (linenr_T) 1, buf->b_ml.ml_line_count, NULL,
524 FALSE, FALSE, FALSE, TRUE);
526 if (retval == FAIL)
527 return CC_FAIL;
528 else
529 #endif
530 return CC_SUCCESS;
532 /* these are not implemented yet. */
533 case CC_MASTER:
534 case CC_PROJECT:
535 default:
536 return CC_FAIL;
541 * Outside accessible compile request for a specific buffer.
544 cc_request_compile(buf_T *buf) {
545 if (!cc_started)
546 return CC_FAIL;
548 cc_pjob_T *tmp_pjob;
550 tmp_pjob = (cc_pjob_T *) calloc(1, sizeof(cc_pjob_T));
551 if (tmp_pjob == NULL)
552 return CC_FAIL;
554 tmp_pjob->cc_pjob_buf = buf;
555 tmp_pjob->cc_pjob_type = CC_COMPILE;
557 return cc_pjobs_produce(tmp_pjob);
561 * Function required to populate the pending jobs array.
562 * TODO: FAILURE IN pthread_mutex_unlock is SERIOUS!!!
564 static int
565 cc_pjobs_produce(cc_pjob_T *tmp_pjob) {
566 int retval;
568 sem_wait(&empty);
570 retval = pthread_mutex_lock(&mutex);
571 if (retval)
572 return CC_FAIL;
574 /* check if this buffer already has a pending job request. */
575 /* if so directly return. */
576 if (cc_pjobs_buf_exists(tmp_pjob)) {
577 sem_post(&empty);
578 retval = pthread_mutex_unlock(&mutex);
579 if (retval)
580 return CC_FAIL;
581 return CC_BUFEXISTS;
584 cc_pjobs[pindex % MAX_PENDING_JOBS] = tmp_pjob;
585 ++pindex;
587 retval = pthread_mutex_unlock(&mutex);
588 if (retval)
589 return CC_FAIL;
591 retval = sem_post(&full);
592 if (retval)
593 return CC_FAIL;
595 return CC_SUCCESS;
599 * Returns TRUE if there is another pending job for this process.
601 static int
602 cc_pjobs_buf_exists(cc_pjob_T *tmp_pjob) {
603 int i;
605 for (i = 0; i < MAX_PENDING_JOBS; ++i) {
606 if (cc_pjobs[i] == NULL ||
607 strcmp((char *) cc_pjobs[i]->cc_pjob_buf->b_ffname,
608 (char *) tmp_pjob->cc_pjob_buf->b_ffname))
609 continue;
610 else
611 return TRUE;
613 return FALSE;
617 * Removes the "top" element.
619 static cc_pjob_T *
620 cc_pjobs_consume(void) {
621 int retval;
622 cc_pjob_T *tmp_pjob = NULL;
624 tmp_pjob = (cc_pjob_T *) calloc(1, sizeof(cc_pjob_T));
625 if (tmp_pjob == NULL)
626 return NULL;
628 sem_wait(&full);
630 retval = pthread_mutex_lock(&mutex);
631 if (retval)
632 goto con_fail;
634 tmp_pjob->cc_pjob_type = cc_pjobs[cindex % MAX_PENDING_JOBS]->cc_pjob_type;
635 tmp_pjob->cc_pjob_buf = cc_pjobs[cindex % MAX_PENDING_JOBS]->cc_pjob_buf;
637 /* free the old job!!! */
638 free(cc_pjobs[cindex % MAX_PENDING_JOBS]);
639 cc_pjobs[cindex % MAX_PENDING_JOBS] = NULL;
641 cindex++;
643 retval = pthread_mutex_unlock(&mutex);
644 if (retval)
645 goto con_fail;
647 retval = sem_post(&empty);
648 if (retval)
649 goto con_fail;
651 return tmp_pjob;
653 con_fail:
654 free(tmp_pjob);
655 return NULL;
659 * Checks if the specified buffer can be syntax checked or not.
662 cc_is_buf_ok(buf_T *buf) {
663 if (!cc_started)
664 return CC_FAIL;
666 char_u *ffname = NULL;
667 char_u *p = NULL;
668 char_u *r = NULL;
670 if (buf == NULL || ((ffname = buf->b_ffname) == NULL))
671 return CC_FAIL;
673 /* find the file extension */
674 p = (char_u *) strrchr((char *) ffname, '.');
676 if (p == NULL)
677 return CC_FAIL;
679 /* check if this extension is supported or not */
680 r = (char_u *) strstr((char *) cc_sup_exts, (char *) p);
682 if (r == NULL)
683 return CC_FAIL;
684 else
685 return CC_SUCCESS;
689 * Returns TRUE if the specified buffer is in the watchlist.
690 * TODO: THIS CAN RETURN THE FOUND BUFFER TO SAVE SOME TIME.
694 cc_is_buf_watched(buf_T *buf) {
695 if (!cc_started)
696 return CC_FAIL;
698 cc_bufline_T *tmp_bufline;
699 int dummy;
701 tmp_bufline = cc_locate_buf_bin(buf, &dummy, &dummy, CC_FOR_FIND);
702 if (tmp_bufline == NULL)
703 return FALSE;
704 else
705 return TRUE;
709 * Finds and sets a file name for the temporary copy of the buffer.
711 static int
712 cc_set_tmp_copy_ffname(buf_T *buf, char_u *tmp_copy_ffname) {
713 char_u *buf_sfname;
714 char_u tmp_buf[MAX_PATH_LENGTH];
715 char_u *p;
717 /* clear the array. */
718 memset(tmp_buf, 0, MAX_PATH_LENGTH);
720 /* find the tmp_copy_ffname. */
721 /* uses gettail(buf->b_sfname) because sometimes b_sfname is
722 * actually b_ffname. */
723 buf_sfname = buf->b_sfname ? gettail(buf->b_sfname)
724 : gettail(buf->b_ffname);
725 if (buf_sfname == NULL || !strcmp((char *) buf_sfname, ""))
726 return CC_FAIL;
728 /* finds the folder path of the buffer. */
729 p = (char_u *)strstr((char *)buf->b_ffname, (char *)buf_sfname);
730 if (p == NULL)
731 return CC_FAIL;
732 STRNCPY(tmp_buf, buf->b_ffname, (p - buf->b_ffname));
734 /* rather than creating the copy in /tmp/, create it in the directory
735 * of the buffer. this solves a few issues without a lot of effort. */
736 sprintf((char *) tmp_copy_ffname, "%s.cc_%s",
737 (char *) tmp_buf, (char *) buf_sfname);
738 return CC_SUCCESS;
742 * Adds the specified buffer and sets the compile command.
745 cc_addbuf_setcmd(buf_T *buf, char_u *cmd) {
746 cc_bufline_T *tmp_bufline = NULL;
747 char_u tmp_compile_cmd[MAX_CMD_LENGTH];
748 char_u *p = NULL;
749 char_u tmp_copy_ffname[MAX_PATH_LENGTH];
750 size_t bname_len;
752 /* clear the arrays. */
753 memset(tmp_compile_cmd, 0, MAX_CMD_LENGTH);
754 memset(tmp_copy_ffname, 0, MAX_PATH_LENGTH);
756 tmp_bufline = cc_add_buf(buf);
757 if (tmp_bufline == NULL)
758 return CC_FAIL;
760 cc_set_tmp_copy_ffname(buf, tmp_copy_ffname);
762 /* at this point we should update the compile command, so
763 * that it works for the temporary copy, rather than the
764 * original copy.
765 * *** assumes that the user entered compile command has the
766 * full file name of the current buffer. */
767 p = (char_u *)strstr((char *)cmd,
768 (char *)tmp_bufline->buf_name);
769 if (p == NULL)
770 return CC_FAIL;
771 bname_len = STRLEN(tmp_bufline->buf_name);
773 STRNCPY(tmp_compile_cmd, cmd,
774 (p - cmd));
775 STRCAT(tmp_compile_cmd, tmp_copy_ffname);
776 STRCAT(tmp_compile_cmd, p + bname_len);
778 STRCPY(tmp_bufline->buf_compile_cmd, tmp_compile_cmd);
780 /* TODO: check if tmp_bufline->buf_compile_cmd is valid. */
781 return CC_SUCCESS;
785 * Adds the specified buffer to the watch list.
787 * modified a little: returns a pointer to the inserted cc_bufline_T
788 * object, to make things a little faster.
791 static cc_bufline_T*
792 cc_add_buf(buf_T *buf) {
793 if (!cc_started)
794 return NULL;
796 cc_bufline_T *tmp_bufline = NULL;
797 cc_bufline_T *tmp_insafter = NULL;
798 int buf_idx;
799 int put_before;
800 int i;
801 int retval;
803 /* check if this buffer is in the watch list, if so don't add */
804 tmp_bufline = cc_locate_buf_bin(buf, &buf_idx, &put_before, CC_FOR_FIND);
805 if (tmp_bufline != NULL)
806 return NULL;
808 if (cc_list.cc_bufcount == MAX_BUFLINES)
809 return NULL;
811 tmp_bufline = (cc_bufline_T *) calloc(1, sizeof(cc_bufline_T));
813 if (tmp_bufline == NULL)
814 return NULL;
816 tmp_bufline->buf_name = buf->b_ffname;
817 memset(tmp_bufline->buf_compile_cmd, 0, MAX_CMD_LENGTH);
818 pthread_mutex_init(&(tmp_bufline->buf_mutex), NULL);
820 if (cc_list.cc_bufcount == 0) {
821 /* this is the first buffer */
822 cc_list.cc_list_head = tmp_bufline;
823 tmp_bufline->prev = NULL;
824 tmp_bufline->next = NULL;
825 cc_bufline_ptrs[0] = tmp_bufline;
826 } else {
827 tmp_insafter = cc_locate_buf_bin(buf, &buf_idx, &put_before, CC_FOR_ADD);
829 if (tmp_insafter == NULL) {
830 free(tmp_bufline);
831 return NULL;
834 if (put_before) {
835 /* move elements in cc_bufline_ptrs */
836 for (i = cc_list.cc_bufcount; i > buf_idx; --i)
837 cc_bufline_ptrs[i] = cc_bufline_ptrs[i - 1];
839 cc_bufline_ptrs[buf_idx] = tmp_bufline;
841 tmp_bufline->prev = tmp_insafter->prev;
842 if (tmp_insafter->prev != NULL)
843 tmp_insafter->prev->next = tmp_bufline;
844 else
845 cc_list.cc_list_head = tmp_bufline;
847 tmp_bufline->next = tmp_insafter;
848 tmp_insafter->prev = tmp_bufline;
849 } else {
850 /* move elements in cc_bufline_ptrs */
851 for (i = cc_list.cc_bufcount; i > buf_idx + 1; --i)
852 cc_bufline_ptrs[i] = cc_bufline_ptrs[i - 1];
854 cc_bufline_ptrs[buf_idx + 1] = tmp_bufline;
856 tmp_bufline->next = tmp_insafter->next;
857 if (tmp_bufline->next != NULL)
858 tmp_bufline->next->prev = tmp_bufline;
860 tmp_insafter->next = tmp_bufline;
861 tmp_bufline->prev = tmp_insafter;
865 /* create an initial compile request for this buffer */
866 retval = cc_request_compile(buf);
867 if (retval == CC_FAIL)
868 return NULL;
870 cc_list.cc_list_curr = tmp_bufline;
871 cc_list.cc_bufcount++;
873 return tmp_bufline;
877 * Removes the specified buffer from the watch list.
878 * TODO: current implementation does not have sorted lists.
881 cc_rem_buf(buf_T *buf) {
882 if (!cc_started)
883 return CC_FAIL;
885 cc_bufline_T *tmp_bufline = NULL;
886 int buf_idx;
887 int put_before;
888 int i;
890 tmp_bufline = cc_locate_buf_bin(buf, &buf_idx, &put_before, CC_FOR_REM);
892 if (tmp_bufline == NULL)
893 return CC_NOSUCHBUF;
895 if (tmp_bufline->prev != NULL)
896 tmp_bufline->prev->next = tmp_bufline->next;
898 if (tmp_bufline->next != NULL)
899 tmp_bufline->next->prev = tmp_bufline->prev;
901 if (tmp_bufline->buf_ewlist_head != NULL)
902 cc_free_ewlist(tmp_bufline->buf_ewlist_head);
904 vim_free(tmp_bufline);
906 /* update cc_bufline_ptrs */
907 for (i = buf_idx; i < cc_list.cc_bufcount; ++i)
908 cc_bufline_ptrs[i] = cc_bufline_ptrs[i + 1];
910 cc_list.cc_bufcount--;
912 /* update the screen in case there are some highlighted lines. */
913 cc_update_screen();
915 return CC_SUCCESS;
919 * Returns a pointer to the specified buffer node.
920 * Returns NULL if there is no such buffer.
921 * This is the linear search version.
922 * THIS SHOULD NOT BE USED ANYMORE!!!
924 static cc_bufline_T *
925 cc_locate_buf(buf_T *buf) {
926 cc_bufline_T *tmp_bufline = NULL;
927 char_u *ffname = NULL;
929 ffname = buf->b_ffname;
931 for (tmp_bufline = cc_list.cc_list_head;
932 tmp_bufline != NULL; tmp_bufline = tmp_bufline->next) {
933 if (strcmp((char *) tmp_bufline->buf_name, (char *) ffname))
934 continue;
935 else
936 break;
939 return tmp_bufline;
943 * Binary search of the buffer list. Has two modes:
944 * - search for addition (find the correct position in the list which the
945 * specified buffer should be inserted to)(return CC_BUFEXISTS if the
946 * buffer is already in the watch list),
947 * - search for removal (find the exact location of the buffer, return NULL
948 * if could not be found).
950 static cc_bufline_T *
951 cc_locate_buf_bin(buf_T *buf, int *buf_idx, int *put_before, int mode) {
952 cc_bufline_T *tmp_bufline = NULL;
953 int start = 0;
954 int end = cc_list.cc_bufcount - 1;
955 int mid = (start + end) / 2;
956 int retval;
958 while (start <= end) {
959 mid = (start + end) / 2;
961 /* since this is a linked list we are using cc_bufline_ptrs
962 * to find the correct node. */
963 tmp_bufline = cc_bufline_ptrs[mid];
965 retval = strcmp((char *) buf->b_ffname, (char *) tmp_bufline->buf_name);
967 if (retval == 0) {
968 *buf_idx = mid;
969 if (mode == CC_FOR_ADD)
970 return NULL;
971 else if (mode == CC_FOR_REM || mode == CC_FOR_FIND)
972 return tmp_bufline;
973 } else if (retval < 0) {
974 end = mid - 1;
975 *put_before = TRUE;
976 } else {
977 start = mid + 1;
978 *put_before = FALSE;
982 /* the buffer is not inside the list */
983 *buf_idx = mid;
984 if (mode == CC_FOR_ADD)
985 return tmp_bufline;
986 else if (mode == CC_FOR_REM || mode == CC_FOR_FIND)
987 return NULL;
988 else /* should not be reached. */
989 return NULL;
993 * Frees the specified error & warning list.
995 static void
996 cc_free_ewlist(cc_ewline_T *ewlist_head) {
997 if (ewlist_head == NULL)
998 return;
1000 cc_ewline_T *tmp_ewline = ewlist_head;
1001 cc_ewline_T *tmp_ewlinep = ewlist_head;
1003 while (tmp_ewline != NULL) {
1004 tmp_ewline = tmp_ewline->next;
1005 free(tmp_ewlinep);
1006 tmp_ewlinep = tmp_ewline;
1011 * Frees the specified buffer watch list.
1012 * TODO: CHECK THIS BEHAVIOR!!! IMPORTANT!!!
1014 static void
1015 cc_free_buflist(cc_info_T *cc_list_a,
1016 cc_bufline_T *cc_bufline_ptrs_a[MAX_BUFLINES]) {
1017 int i;
1019 if (cc_list_a->cc_list_head == NULL)
1020 return;
1022 cc_bufline_T *tmp_bufline = cc_list_a->cc_list_head;
1023 cc_bufline_T *tmp_buflinep = cc_list_a->cc_list_head;
1025 while (tmp_bufline != NULL) {
1026 cc_free_ewlist(tmp_bufline->buf_ewlist_head);
1028 tmp_bufline = tmp_bufline->next;
1029 free(tmp_buflinep);
1030 tmp_buflinep = tmp_bufline;
1033 for (i = 0; i < cc_list_a->cc_bufcount; ++i)
1034 cc_bufline_ptrs_a[i] = NULL;
1036 cc_list_a->cc_list_head = NULL;
1037 cc_list_a->cc_list_curr = NULL;
1038 cc_list_a->cc_bufcount = 0;
1042 * Frees the memory.
1044 static void
1045 cc_free(void) {
1046 if (!cc_started)
1047 return;
1049 cc_list.cc_list_curr = NULL;
1050 cc_free_buflist(&cc_list, cc_bufline_ptrs);
1054 * TODO: cc_exit() function.
1055 * what else should it do???
1057 void
1058 cc_exit(void) {
1059 p_ut = old_p_ut;
1060 cc_started = FALSE;
1061 cc_free();