Merge branch 'vim' into feat/code-check
[vim_extended.git] / src / code_check.c
bloba78877b7e04d0dfa308b3e99d1c76dda2c3f5388
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 #ifdef FEAT_GUI
316 if (gui.in_use)
317 cc_update_screen();
318 #endif
320 /* find the buffer line */
322 /* create the temporary copy */
323 retval = cc_create_tmp_copy(tmp_pjob->cc_pjob_buf,
324 tmp_copy_ffname, CC_STANDALONE);
325 if (retval == CC_FAIL)
326 continue;
328 tmp_bufline = cc_locate_buf_bin(tmp_pjob->cc_pjob_buf, &dummy,
329 &dummy, CC_FOR_FIND);
330 if (tmp_bufline == NULL)
331 continue;
333 /* compile the temporary copy */
334 retval = cc_compile_tmp_copy(tmp_bufline, tmp_copy_ffname,
335 CC_STANDALONE);
336 if (retval == CC_FAIL)
337 continue;
339 /* create the error list from the temporary copy */
340 sprintf((char *)tmp_out_ffname, "%s.out", (char *)tmp_copy_ffname);
341 ew_head = cc_create_ewlist(tmp_out_ffname);
343 /* having ew_head == NULL is not an error, since all
344 * the errors and the warnings may be cleaned. */
346 /* free the previous list & set the new list */
347 pthread_mutex_lock(&(tmp_bufline->buf_mutex));
349 cc_free_ewlist(tmp_bufline->buf_ewlist_head);
350 tmp_bufline->buf_ewlist_head = ew_head;
352 pthread_mutex_unlock(&(tmp_bufline->buf_mutex));
354 /* update the screen */
355 cc_update_screen();
357 /* that would be it for this buffer */
362 * Function is used to update the screen properly.
364 void
365 cc_update_screen(void) {
366 #ifdef FEAT_GUI
367 if (!gui.in_use) {
368 #endif
369 /* in console mode updating the screen from the worker thread
370 * does not cause any problems. */
371 update_topline();
372 validate_cursor();
373 update_screen(SOME_VALID);
374 setcursor();
375 cursor_on();
376 out_flush();
377 #ifdef FEAT_GUI
378 } else {
379 /* updating the screen in gui mode is troublesome. */
380 char_u bytes[3];
382 bytes[0] = CSI;
383 bytes[1] = KS_EXTRA;
384 bytes[2] = KE_REDRAW;
386 add_to_input_buf(bytes, 3);
388 #endif
392 * Function returns the correct error/warning type for a specific lnum.
393 * Returns CC_NOEW if the lnum does not have an error or a warning.
396 cc_get_ew_type(buf_T *buf, linenr_T lnum) {
397 if (!cc_started)
398 return CC_FAIL;
400 cc_bufline_T *tmp_bufline;
401 cc_ewline_T *tmp_ewline;
402 int dummy;
403 int ew_type = CC_NOEW;
405 tmp_bufline = cc_locate_buf_bin(buf, &dummy, &dummy, CC_FOR_FIND);
406 if (tmp_bufline == NULL)
407 return CC_NOEW;
409 pthread_mutex_lock(&(tmp_bufline->buf_mutex));
410 tmp_ewline = tmp_bufline->buf_ewlist_head;
411 while (tmp_ewline != NULL) {
412 if (tmp_ewline->ew_lnum != lnum) {
413 tmp_ewline = tmp_ewline->next;
414 continue;
417 if (ew_type < tmp_ewline->ew_type)
418 ew_type = tmp_ewline->ew_type;
420 tmp_ewline = tmp_ewline->next;
422 pthread_mutex_unlock(&(tmp_bufline->buf_mutex));
423 return ew_type;
427 * Function creates a quickfix error list from the compiler output.
428 * TODO: Right now works only for .c files and the gcc compiler.
429 * Should be automated to work with multiple languages and multiple
430 * compilers.
432 static cc_ewline_T *
433 cc_create_ewlist(char_u *tmp_out_ffname) {
434 FILE *err_file = NULL;
435 char_u *buf = NULL;
436 size_t len = 0;
437 size_t read;
438 cc_ewline_T *ew_head = NULL;
439 cc_ewline_T *ew_curr = NULL;
440 char_u *token1 = NULL;
441 char_u *token2 = NULL;
442 char_u *token3 = NULL;
443 char_u *token4 = NULL;
444 cc_ewline_T *tmp_ewline = NULL;
447 err_file = fopen((char *)tmp_out_ffname, "r");
448 if (err_file == NULL)
449 return NULL;
451 while ((read = getline((char **) &buf, &len, err_file)) != -1) {
452 token1 = (char_u *)strtok((char *) buf, ":");
453 token2 = (char_u *)strtok(NULL, ":");
454 token3 = (char_u *)strtok(NULL, ":");
455 token4 = (char_u *)strtok(NULL, ":");
457 /* TODO: be sure it is gXX type of output. */
459 if (!token4)
460 continue;
462 tmp_ewline = (cc_ewline_T *) calloc(1, sizeof(cc_ewline_T));
463 STRCPY(tmp_ewline->ew_text, token4);
464 /* remove the last '\n' from the error/warning message */
465 tmp_ewline->ew_text[STRLEN(tmp_ewline->ew_text) - 1] = '\0';
466 tmp_ewline->ew_lnum = (linenr_T) atoi((char *) token2);
467 tmp_ewline->ew_type = token3[1] == 'w' ? CC_WARNING : CC_ERROR;
469 if (!ew_head) {
470 ew_head = tmp_ewline;
471 ew_curr = tmp_ewline;
472 } else {
473 ew_curr->next = tmp_ewline;
474 tmp_ewline->prev = ew_curr;
475 ew_curr = ew_curr->next;
479 /* buf should be freed, because it is allocated in getline */
480 if (buf)
481 free(buf);
483 return ew_head;
487 * Function compiles the temporary copy saving the output in a
488 * temporary file.
490 static int
491 cc_compile_tmp_copy(cc_bufline_T *bufline, char_u *tmp_copy_ffname,
492 int copy_type) {
493 char_u cmd[MAX_CMD_LENGTH];
494 int retval;
496 if (bufline->buf_compile_cmd[0] == NUL)
497 return CC_FAIL;
499 /* clear the arrays. */
500 memset(cmd, 0, MAX_CMD_LENGTH);
502 /* direct STDOUT & STDERR to the same file, so that the output of
503 * this will not interfer with the vim terminal */
504 sprintf((char *)cmd, "%s > %s.out 2>&1",
505 (char *)bufline->buf_compile_cmd, (char *)tmp_copy_ffname);
506 #if 1
507 retval = system((char *)cmd);
508 if (retval < 0)
509 return CC_FAIL;
510 #endif
512 return CC_SUCCESS;
516 * Function creates a temporary copy of the buffer. copy_type determines
517 * the type of the copy process.
519 static int
520 cc_create_tmp_copy(buf_T *buf, char_u *tmp_copy_ffname, int copy_type) {
521 int retval;
522 char_u cmd[MAX_CMD_LENGTH];
524 switch (copy_type) {
525 case CC_STANDALONE:
526 cc_set_tmp_copy_ffname(buf, tmp_copy_ffname);
527 sprintf((char *)cmd, "touch %s", (char *)tmp_copy_ffname);
529 retval = system((char *)cmd);
530 if (retval < 0)
531 return CC_FAIL;
532 #if 1
533 retval = buf_write(buf, tmp_copy_ffname, NULL,
534 (linenr_T) 1, buf->b_ml.ml_line_count, NULL,
535 FALSE, FALSE, FALSE, TRUE);
537 if (retval == FAIL)
538 return CC_FAIL;
539 else
540 #endif
541 return CC_SUCCESS;
543 /* these are not implemented yet. */
544 case CC_MASTER:
545 case CC_PROJECT:
546 default:
547 return CC_FAIL;
552 * Outside accessible compile request for a specific buffer.
555 cc_request_compile(buf_T *buf) {
556 if (!cc_started)
557 return CC_FAIL;
559 cc_pjob_T *tmp_pjob;
561 tmp_pjob = (cc_pjob_T *) calloc(1, sizeof(cc_pjob_T));
562 if (tmp_pjob == NULL)
563 return CC_FAIL;
565 tmp_pjob->cc_pjob_buf = buf;
566 tmp_pjob->cc_pjob_type = CC_COMPILE;
568 return cc_pjobs_produce(tmp_pjob);
572 * Function required to populate the pending jobs array.
573 * TODO: FAILURE IN pthread_mutex_unlock is SERIOUS!!!
575 static int
576 cc_pjobs_produce(cc_pjob_T *tmp_pjob) {
577 int retval;
579 sem_wait(&empty);
581 retval = pthread_mutex_lock(&mutex);
582 if (retval)
583 return CC_FAIL;
585 /* check if this buffer already has a pending job request. */
586 /* if so directly return. */
587 if (cc_pjobs_buf_exists(tmp_pjob)) {
588 sem_post(&empty);
589 retval = pthread_mutex_unlock(&mutex);
590 if (retval)
591 return CC_FAIL;
592 return CC_BUFEXISTS;
595 cc_pjobs[pindex % MAX_PENDING_JOBS] = tmp_pjob;
596 ++pindex;
598 retval = pthread_mutex_unlock(&mutex);
599 if (retval)
600 return CC_FAIL;
602 retval = sem_post(&full);
603 if (retval)
604 return CC_FAIL;
606 return CC_SUCCESS;
610 * Returns TRUE if there is another pending job for this process.
612 static int
613 cc_pjobs_buf_exists(cc_pjob_T *tmp_pjob) {
614 int i;
616 for (i = 0; i < MAX_PENDING_JOBS; ++i) {
617 if (cc_pjobs[i] == NULL ||
618 strcmp((char *) cc_pjobs[i]->cc_pjob_buf->b_ffname,
619 (char *) tmp_pjob->cc_pjob_buf->b_ffname))
620 continue;
621 else
622 return TRUE;
624 return FALSE;
628 * Removes the "top" element.
630 static cc_pjob_T *
631 cc_pjobs_consume(void) {
632 int retval;
633 cc_pjob_T *tmp_pjob = NULL;
635 tmp_pjob = (cc_pjob_T *) calloc(1, sizeof(cc_pjob_T));
636 if (tmp_pjob == NULL)
637 return NULL;
639 sem_wait(&full);
641 retval = pthread_mutex_lock(&mutex);
642 if (retval)
643 goto con_fail;
645 tmp_pjob->cc_pjob_type = cc_pjobs[cindex % MAX_PENDING_JOBS]->cc_pjob_type;
646 tmp_pjob->cc_pjob_buf = cc_pjobs[cindex % MAX_PENDING_JOBS]->cc_pjob_buf;
648 /* free the old job!!! */
649 free(cc_pjobs[cindex % MAX_PENDING_JOBS]);
650 cc_pjobs[cindex % MAX_PENDING_JOBS] = NULL;
652 cindex++;
654 retval = pthread_mutex_unlock(&mutex);
655 if (retval)
656 goto con_fail;
658 retval = sem_post(&empty);
659 if (retval)
660 goto con_fail;
662 return tmp_pjob;
664 con_fail:
665 free(tmp_pjob);
666 return NULL;
670 * Checks if the specified buffer can be syntax checked or not.
673 cc_is_buf_ok(buf_T *buf) {
674 if (!cc_started)
675 return CC_FAIL;
677 char_u *ffname = NULL;
678 char_u *p = NULL;
679 char_u *r = NULL;
681 if (buf == NULL || ((ffname = buf->b_ffname) == NULL))
682 return CC_FAIL;
684 /* find the file extension */
685 p = (char_u *) strrchr((char *) ffname, '.');
687 if (p == NULL)
688 return CC_FAIL;
690 /* check if this extension is supported or not */
691 r = (char_u *) strstr((char *) cc_sup_exts, (char *) p);
693 if (r == NULL)
694 return CC_FAIL;
695 else
696 return CC_SUCCESS;
700 * Returns TRUE if the specified buffer is in the watchlist.
701 * TODO: THIS CAN RETURN THE FOUND BUFFER TO SAVE SOME TIME.
705 cc_is_buf_watched(buf_T *buf) {
706 if (!cc_started)
707 return CC_FAIL;
709 cc_bufline_T *tmp_bufline;
710 int dummy;
712 tmp_bufline = cc_locate_buf_bin(buf, &dummy, &dummy, CC_FOR_FIND);
713 if (tmp_bufline == NULL)
714 return FALSE;
715 else
716 return TRUE;
720 * Finds and sets a file name for the temporary copy of the buffer.
722 static int
723 cc_set_tmp_copy_ffname(buf_T *buf, char_u *tmp_copy_ffname) {
724 char_u *buf_sfname;
725 char_u tmp_buf[MAX_PATH_LENGTH];
726 char_u *p;
728 /* clear the array. */
729 memset(tmp_buf, 0, MAX_PATH_LENGTH);
731 /* find the tmp_copy_ffname. */
732 /* uses gettail(buf->b_sfname) because sometimes b_sfname is
733 * actually b_ffname. */
734 buf_sfname = buf->b_sfname ? gettail(buf->b_sfname)
735 : gettail(buf->b_ffname);
736 if (buf_sfname == NULL || !strcmp((char *) buf_sfname, ""))
737 return CC_FAIL;
739 /* finds the folder path of the buffer. */
740 p = (char_u *)strstr((char *)buf->b_ffname, (char *)buf_sfname);
741 if (p == NULL)
742 return CC_FAIL;
743 STRNCPY(tmp_buf, buf->b_ffname, (p - buf->b_ffname));
745 /* rather than creating the copy in /tmp/, create it in the directory
746 * of the buffer. this solves a few issues without a lot of effort. */
747 sprintf((char *) tmp_copy_ffname, "%s.cc_%s",
748 (char *) tmp_buf, (char *) buf_sfname);
749 return CC_SUCCESS;
753 * Adds the specified buffer and sets the compile command.
756 cc_addbuf_setcmd(buf_T *buf, char_u *cmd) {
757 cc_bufline_T *tmp_bufline = NULL;
758 char_u tmp_compile_cmd[MAX_CMD_LENGTH];
759 char_u *p = NULL;
760 char_u tmp_copy_ffname[MAX_PATH_LENGTH];
761 size_t bname_len;
763 /* clear the arrays. */
764 memset(tmp_compile_cmd, 0, MAX_CMD_LENGTH);
765 memset(tmp_copy_ffname, 0, MAX_PATH_LENGTH);
767 tmp_bufline = cc_add_buf(buf);
768 if (tmp_bufline == NULL)
769 return CC_FAIL;
771 cc_set_tmp_copy_ffname(buf, tmp_copy_ffname);
773 /* at this point we should update the compile command, so
774 * that it works for the temporary copy, rather than the
775 * original copy.
776 * *** assumes that the user entered compile command has the
777 * full file name of the current buffer. */
778 p = (char_u *)strstr((char *)cmd,
779 (char *)tmp_bufline->buf_name);
780 if (p == NULL)
781 return CC_FAIL;
782 bname_len = STRLEN(tmp_bufline->buf_name);
784 STRNCPY(tmp_compile_cmd, cmd,
785 (p - cmd));
786 STRCAT(tmp_compile_cmd, tmp_copy_ffname);
787 STRCAT(tmp_compile_cmd, p + bname_len);
789 STRCPY(tmp_bufline->buf_compile_cmd, tmp_compile_cmd);
791 /* TODO: check if tmp_bufline->buf_compile_cmd is valid. */
792 return CC_SUCCESS;
796 * Adds the specified buffer to the watch list.
798 * modified a little: returns a pointer to the inserted cc_bufline_T
799 * object, to make things a little faster.
802 static cc_bufline_T*
803 cc_add_buf(buf_T *buf) {
804 if (!cc_started)
805 return NULL;
807 cc_bufline_T *tmp_bufline = NULL;
808 cc_bufline_T *tmp_insafter = NULL;
809 int buf_idx;
810 int put_before;
811 int i;
812 int retval;
814 /* check if this buffer is in the watch list, if so don't add */
815 tmp_bufline = cc_locate_buf_bin(buf, &buf_idx, &put_before, CC_FOR_FIND);
816 if (tmp_bufline != NULL)
817 return NULL;
819 if (cc_list.cc_bufcount == MAX_BUFLINES)
820 return NULL;
822 tmp_bufline = (cc_bufline_T *) calloc(1, sizeof(cc_bufline_T));
824 if (tmp_bufline == NULL)
825 return NULL;
827 tmp_bufline->buf_name = buf->b_ffname;
828 memset(tmp_bufline->buf_compile_cmd, 0, MAX_CMD_LENGTH);
829 pthread_mutex_init(&(tmp_bufline->buf_mutex), NULL);
831 if (cc_list.cc_bufcount == 0) {
832 /* this is the first buffer */
833 cc_list.cc_list_head = tmp_bufline;
834 tmp_bufline->prev = NULL;
835 tmp_bufline->next = NULL;
836 cc_bufline_ptrs[0] = tmp_bufline;
837 } else {
838 tmp_insafter = cc_locate_buf_bin(buf, &buf_idx, &put_before, CC_FOR_ADD);
840 if (tmp_insafter == NULL) {
841 free(tmp_bufline);
842 return NULL;
845 if (put_before) {
846 /* move elements in cc_bufline_ptrs */
847 for (i = cc_list.cc_bufcount; i > buf_idx; --i)
848 cc_bufline_ptrs[i] = cc_bufline_ptrs[i - 1];
850 cc_bufline_ptrs[buf_idx] = tmp_bufline;
852 tmp_bufline->prev = tmp_insafter->prev;
853 if (tmp_insafter->prev != NULL)
854 tmp_insafter->prev->next = tmp_bufline;
855 else
856 cc_list.cc_list_head = tmp_bufline;
858 tmp_bufline->next = tmp_insafter;
859 tmp_insafter->prev = tmp_bufline;
860 } else {
861 /* move elements in cc_bufline_ptrs */
862 for (i = cc_list.cc_bufcount; i > buf_idx + 1; --i)
863 cc_bufline_ptrs[i] = cc_bufline_ptrs[i - 1];
865 cc_bufline_ptrs[buf_idx + 1] = tmp_bufline;
867 tmp_bufline->next = tmp_insafter->next;
868 if (tmp_bufline->next != NULL)
869 tmp_bufline->next->prev = tmp_bufline;
871 tmp_insafter->next = tmp_bufline;
872 tmp_bufline->prev = tmp_insafter;
876 /* create an initial compile request for this buffer */
877 retval = cc_request_compile(buf);
878 if (retval == CC_FAIL)
879 return NULL;
881 cc_list.cc_list_curr = tmp_bufline;
882 cc_list.cc_bufcount++;
884 return tmp_bufline;
888 * Removes the specified buffer from the watch list.
889 * TODO: current implementation does not have sorted lists.
892 cc_rem_buf(buf_T *buf) {
893 if (!cc_started)
894 return CC_FAIL;
896 cc_bufline_T *tmp_bufline = NULL;
897 int buf_idx;
898 int put_before;
899 int i;
901 tmp_bufline = cc_locate_buf_bin(buf, &buf_idx, &put_before, CC_FOR_REM);
903 if (tmp_bufline == NULL)
904 return CC_NOSUCHBUF;
906 if (tmp_bufline->prev != NULL)
907 tmp_bufline->prev->next = tmp_bufline->next;
909 if (tmp_bufline->next != NULL)
910 tmp_bufline->next->prev = tmp_bufline->prev;
912 if (tmp_bufline->buf_ewlist_head != NULL)
913 cc_free_ewlist(tmp_bufline->buf_ewlist_head);
915 vim_free(tmp_bufline);
917 /* update cc_bufline_ptrs */
918 for (i = buf_idx; i < cc_list.cc_bufcount; ++i)
919 cc_bufline_ptrs[i] = cc_bufline_ptrs[i + 1];
921 cc_list.cc_bufcount--;
923 /* update the screen in case there are some highlighted lines. */
924 cc_update_screen();
926 return CC_SUCCESS;
930 * Returns a pointer to the specified buffer node.
931 * Returns NULL if there is no such buffer.
932 * This is the linear search version.
933 * THIS SHOULD NOT BE USED ANYMORE!!!
935 static cc_bufline_T *
936 cc_locate_buf(buf_T *buf) {
937 cc_bufline_T *tmp_bufline = NULL;
938 char_u *ffname = NULL;
940 ffname = buf->b_ffname;
942 for (tmp_bufline = cc_list.cc_list_head;
943 tmp_bufline != NULL; tmp_bufline = tmp_bufline->next) {
944 if (strcmp((char *) tmp_bufline->buf_name, (char *) ffname))
945 continue;
946 else
947 break;
950 return tmp_bufline;
954 * Binary search of the buffer list. Has two modes:
955 * - search for addition (find the correct position in the list which the
956 * specified buffer should be inserted to)(return CC_BUFEXISTS if the
957 * buffer is already in the watch list),
958 * - search for removal (find the exact location of the buffer, return NULL
959 * if could not be found).
961 static cc_bufline_T *
962 cc_locate_buf_bin(buf_T *buf, int *buf_idx, int *put_before, int mode) {
963 cc_bufline_T *tmp_bufline = NULL;
964 int start = 0;
965 int end = cc_list.cc_bufcount - 1;
966 int mid = (start + end) / 2;
967 int retval;
969 while (start <= end) {
970 mid = (start + end) / 2;
972 /* since this is a linked list we are using cc_bufline_ptrs
973 * to find the correct node. */
974 tmp_bufline = cc_bufline_ptrs[mid];
976 retval = strcmp((char *) buf->b_ffname, (char *) tmp_bufline->buf_name);
978 if (retval == 0) {
979 *buf_idx = mid;
980 if (mode == CC_FOR_ADD)
981 return NULL;
982 else if (mode == CC_FOR_REM || mode == CC_FOR_FIND)
983 return tmp_bufline;
984 } else if (retval < 0) {
985 end = mid - 1;
986 *put_before = TRUE;
987 } else {
988 start = mid + 1;
989 *put_before = FALSE;
993 /* the buffer is not inside the list */
994 *buf_idx = mid;
995 if (mode == CC_FOR_ADD)
996 return tmp_bufline;
997 else if (mode == CC_FOR_REM || mode == CC_FOR_FIND)
998 return NULL;
999 else /* should not be reached. */
1000 return NULL;
1004 * Frees the specified error & warning list.
1006 static void
1007 cc_free_ewlist(cc_ewline_T *ewlist_head) {
1008 if (ewlist_head == NULL)
1009 return;
1011 cc_ewline_T *tmp_ewline = ewlist_head;
1012 cc_ewline_T *tmp_ewlinep = ewlist_head;
1014 while (tmp_ewline != NULL) {
1015 tmp_ewline = tmp_ewline->next;
1016 free(tmp_ewlinep);
1017 tmp_ewlinep = tmp_ewline;
1022 * Frees the specified buffer watch list.
1023 * TODO: CHECK THIS BEHAVIOR!!! IMPORTANT!!!
1025 static void
1026 cc_free_buflist(cc_info_T *cc_list_a,
1027 cc_bufline_T *cc_bufline_ptrs_a[MAX_BUFLINES]) {
1028 int i;
1030 if (cc_list_a->cc_list_head == NULL)
1031 return;
1033 cc_bufline_T *tmp_bufline = cc_list_a->cc_list_head;
1034 cc_bufline_T *tmp_buflinep = cc_list_a->cc_list_head;
1036 while (tmp_bufline != NULL) {
1037 cc_free_ewlist(tmp_bufline->buf_ewlist_head);
1039 tmp_bufline = tmp_bufline->next;
1040 free(tmp_buflinep);
1041 tmp_buflinep = tmp_bufline;
1044 for (i = 0; i < cc_list_a->cc_bufcount; ++i)
1045 cc_bufline_ptrs_a[i] = NULL;
1047 cc_list_a->cc_list_head = NULL;
1048 cc_list_a->cc_list_curr = NULL;
1049 cc_list_a->cc_bufcount = 0;
1053 * Frees the memory.
1055 static void
1056 cc_free(void) {
1057 if (!cc_started)
1058 return;
1060 cc_list.cc_list_curr = NULL;
1061 cc_free_buflist(&cc_list, cc_bufline_ptrs);
1065 * TODO: cc_exit() function.
1066 * what else should it do???
1068 void
1069 cc_exit(void) {
1070 p_ut = old_p_ut;
1071 cc_started = FALSE;
1072 cc_free();