Annotation of src/usr.bin/vim/misccmds.c, Revision 1.1.1.1
1.1 downsj 1: /* $OpenBSD$ */
2: /* vi:set ts=4 sw=4:
3: *
4: * VIM - Vi IMproved by Bram Moolenaar
5: *
6: * Do ":help uganda" in Vim to read copying and usage conditions.
7: * Do ":help credits" in Vim to see a list of people who contributed.
8: */
9:
10: /*
11: * misccmds.c: functions that didn't seem to fit elsewhere
12: */
13:
14: #include "vim.h"
15: #include "globals.h"
16: #include "proto.h"
17: #include "option.h"
18: #ifdef HAVE_FCNTL_H
19: # include <fcntl.h> /* for chdir() */
20: #endif
21:
22: static int get_indent_str __ARGS((char_u *ptr));
23: static void check_status __ARGS((BUF *));
24:
25: /*
26: * count the size of the indent in the current line
27: */
28: int
29: get_indent()
30: {
31: return get_indent_str(ml_get_curline());
32: }
33:
34: /*
35: * count the size of the indent in line "lnum"
36: */
37: int
38: get_indent_lnum(lnum)
39: linenr_t lnum;
40: {
41: return get_indent_str(ml_get(lnum));
42: }
43:
44: /*
45: * count the size of the indent in line "ptr"
46: */
47: static int
48: get_indent_str(ptr)
49: register char_u *ptr;
50: {
51: register int count = 0;
52:
53: for ( ; *ptr; ++ptr)
54: {
55: if (*ptr == TAB) /* count a tab for what it is worth */
56: count += (int)curbuf->b_p_ts - (count % (int)curbuf->b_p_ts);
57: else if (*ptr == ' ')
58: ++count; /* count a space for one */
59: else
60: break;
61: }
62: return (count);
63: }
64:
65: /*
66: * set the indent of the current line
67: * leaves the cursor on the first non-blank in the line
68: */
69: void
70: set_indent(size, del_first)
71: register int size;
72: int del_first;
73: {
74: int oldstate = State;
75: register int c;
76:
77: State = INSERT; /* don't want REPLACE for State */
78: curwin->w_cursor.col = 0;
79: if (del_first) /* delete old indent */
80: {
81: /* vim_iswhite() is a define! */
82: while ((c = gchar_cursor()), vim_iswhite(c))
83: (void)delchar(FALSE);
84: }
85: if (!curbuf->b_p_et) /* if 'expandtab' is set, don't use TABs */
86: while (size >= (int)curbuf->b_p_ts)
87: {
88: ins_char(TAB);
89: size -= (int)curbuf->b_p_ts;
90: }
91: while (size)
92: {
93: ins_char(' ');
94: --size;
95: }
96: State = oldstate;
97: }
98:
99: #if defined(CINDENT) || defined(SMARTINDENT)
100:
101: static int is_cinword __ARGS((char_u *line));
102:
103: /*
104: * Return TRUE if the string "line" starts with a word from 'cinwords'.
105: */
106: static int
107: is_cinword(line)
108: char_u *line;
109: {
110: char_u *cinw;
111: char_u *cinw_buf;
112: int cinw_len;
113: int retval = FALSE;
114: int len;
115:
116: cinw_len = STRLEN(curbuf->b_p_cinw) + 1;
117: cinw_buf = alloc((unsigned)cinw_len);
118: if (cinw_buf != NULL)
119: {
120: line = skipwhite(line);
121: for (cinw = curbuf->b_p_cinw; *cinw; )
122: {
123: len = copy_option_part(&cinw, cinw_buf, cinw_len, ",");
124: if (STRNCMP(line, cinw_buf, len) == 0 &&
125: (!iswordchar(line[len]) || !iswordchar(line[len - 1])))
126: {
127: retval = TRUE;
128: break;
129: }
130: }
131: vim_free(cinw_buf);
132: }
133: return retval;
134: }
135: #endif
136:
137: /*
138: * Opencmd
139: *
140: * Add a new line below or above the current line.
141: * Caller must take care of undo.
142: *
143: * Return TRUE for success, FALSE for failure
144: */
145:
146: int
147: Opencmd(dir, redraw, del_spaces)
148: int dir; /* FORWARD or BACKWARD */
149: int redraw; /* redraw afterwards */
150: int del_spaces; /* delete spaces after cursor */
151: {
152: char_u *saved_line; /* copy of the original line */
153: char_u *p_extra = NULL; /* what goes to next line */
154: int extra_len = 0; /* length of p_extra string */
155: FPOS old_cursor; /* old cursor position */
156: int newcol = 0; /* new cursor column */
157: int newindent = 0; /* auto-indent of the new line */
158: int n;
159: int trunc_line = FALSE; /* truncate current line afterwards */
160: int retval = FALSE; /* return value, default is FAIL */
161: int lead_len; /* length of comment leader */
162: char_u *lead_flags; /* position in 'comments' for comment leader */
163: char_u *leader = NULL; /* copy of comment leader */
164: char_u *allocated = NULL; /* allocated memory */
165: char_u *p;
166: int saved_char = NUL; /* init for GCC */
167: FPOS *pos;
168: int old_plines = 0; /* init for GCC */
169: int new_plines = 0; /* init for GCC */
170: #ifdef SMARTINDENT
171: int no_si = FALSE; /* reset did_si afterwards */
172: int first_char = NUL; /* init for GCC */
173: #endif
174:
175: /*
176: * make a copy of the current line so we can mess with it
177: */
178: saved_line = strsave(ml_get_curline());
179: if (saved_line == NULL) /* out of memory! */
180: return FALSE;
181:
182: if (State == INSERT || State == REPLACE)
183: {
184: p_extra = saved_line + curwin->w_cursor.col;
185: #ifdef SMARTINDENT
186: if (curbuf->b_p_si) /* need first char after new line break */
187: {
188: p = skipwhite(p_extra);
189: first_char = *p;
190: }
191: #endif
192: extra_len = STRLEN(p_extra);
193: saved_char = *p_extra;
194: *p_extra = NUL;
195: }
196:
197: u_clearline(); /* cannot do "U" command when adding lines */
198: #ifdef SMARTINDENT
199: did_si = FALSE;
200: #endif
201:
202: /*
203: * If 'autoindent' and/or 'smartindent' is set, try to figure out what
204: * indent to use for the new line.
205: */
206: if (curbuf->b_p_ai
207: #ifdef SMARTINDENT
208: || curbuf->b_p_si
209: #endif
210: )
211: {
212: /*
213: * count white space on current line
214: */
215: newindent = get_indent();
216: if (newindent == 0)
217: newindent = old_indent; /* for ^^D command in insert mode */
218: old_indent = 0;
219:
220: /*
221: * If we just did an auto-indent, then we didn't type anything on
222: * the prior line, and it should be truncated.
223: */
224: if (dir == FORWARD && did_ai)
225: trunc_line = TRUE;
226:
227: #ifdef SMARTINDENT
228: /*
229: * Do smart indenting.
230: * In insert/replace mode (only when dir == FORWARD)
231: * we may move some text to the next line. If it starts with '{'
232: * don't add an indent. Fixes inserting a NL before '{' in line
233: * "if (condition) {"
234: */
235: else if (curbuf->b_p_si && *saved_line != NUL &&
236: (p_extra == NULL || first_char != '{'))
237: {
238: char_u *ptr;
239: char_u last_char;
240:
241: old_cursor = curwin->w_cursor;
242: ptr = saved_line;
243: lead_len = get_leader_len(ptr, NULL);
244: if (dir == FORWARD)
245: {
246: /*
247: * Skip preprocessor directives, unless they are
248: * recognised as comments.
249: */
250: if (lead_len == 0 && ptr[0] == '#')
251: {
252: while (ptr[0] == '#' && curwin->w_cursor.lnum > 1)
253: ptr = ml_get(--curwin->w_cursor.lnum);
254: newindent = get_indent();
255: }
256: lead_len = get_leader_len(ptr, NULL);
257: if (lead_len > 0)
258: {
259: /*
260: * This case gets the following right:
261: * \*
262: * * A comment (read "\" as "/").
263: * *\
264: * #define IN_THE_WAY
265: * This should line up here;
266: */
267: p = skipwhite(ptr);
268: if (p[0] == '/' && p[1] == '*')
269: p++;
270: if (p[0] == '*')
271: {
272: for (p++; *p; p++)
273: {
274: if (p[0] == '/' && p[-1] == '*')
275: {
276: /*
277: * End of C comment, indent should line up
278: * with the line containing the start of
279: * the comment
280: */
281: curwin->w_cursor.col = p - ptr;
282: if ((pos = findmatch(NUL)) != NULL)
283: {
284: curwin->w_cursor.lnum = pos->lnum;
285: newindent = get_indent();
286: }
287: }
288: }
289: }
290: }
291: else /* Not a comment line */
292: {
293: /* Find last non-blank in line */
294: p = ptr + STRLEN(ptr) - 1;
295: while (p > ptr && vim_iswhite(*p))
296: --p;
297: last_char = *p;
298:
299: /*
300: * find the character just before the '{' or ';'
301: */
302: if (last_char == '{' || last_char == ';')
303: {
304: if (p > ptr)
305: --p;
306: while (p > ptr && vim_iswhite(*p))
307: --p;
308: }
309: /*
310: * Try to catch lines that are split over multiple
311: * lines. eg:
312: * if (condition &&
313: * condition) {
314: * Should line up here!
315: * }
316: */
317: if (*p == ')')
318: {
319: curwin->w_cursor.col = p - ptr;
320: if ((pos = findmatch('(')) != NULL)
321: {
322: curwin->w_cursor.lnum = pos->lnum;
323: newindent = get_indent();
324: ptr = ml_get_curline();
325: }
326: }
327: /*
328: * If last character is '{' do indent, without
329: * checking for "if" and the like.
330: */
331: if (last_char == '{')
332: {
333: did_si = TRUE; /* do indent */
334: no_si = TRUE; /* don't delete it when '{' typed */
335: }
336: /*
337: * Look for "if" and the like, use 'cinwords'.
338: * Don't do this if the previous line ended in ';' or
339: * '}'.
340: */
341: else if (last_char != ';' && last_char != '}' &&
342: is_cinword(ptr))
343: did_si = TRUE;
344: }
345: }
346: else /* dir == BACKWARD */
347: {
348: /*
349: * Skip preprocessor directives, unless they are
350: * recognised as comments.
351: */
352: if (lead_len == 0 && ptr[0] == '#')
353: {
354: int was_backslashed = FALSE;
355:
356: while ((ptr[0] == '#' || was_backslashed) &&
357: curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
358: {
359: if (*ptr && ptr[STRLEN(ptr) - 1] == '\\')
360: was_backslashed = TRUE;
361: else
362: was_backslashed = FALSE;
363: ptr = ml_get(++curwin->w_cursor.lnum);
364: }
365: if (was_backslashed)
366: newindent = 0; /* Got to end of file */
367: else
368: newindent = get_indent();
369: }
370: p = skipwhite(ptr);
371: if (*p == '}') /* if line starts with '}': do indent */
372: did_si = TRUE;
373: else /* can delete indent when '{' typed */
374: can_si_back = TRUE;
375: }
376: curwin->w_cursor = old_cursor;
377: }
378: if (curbuf->b_p_si)
379: can_si = TRUE;
380: #endif /* SMARTINDENT */
381:
382: did_ai = TRUE;
383: }
384:
385: /*
386: * Find out if the current line starts with a comment leader.
387: * This may then be inserted in front of the new line.
388: */
389: lead_len = get_leader_len(saved_line, &lead_flags);
390: if (lead_len > 0)
391: {
392: char_u *lead_repl = NULL; /* replaces comment leader */
393: int lead_repl_len = 0; /* length of *lead_repl */
394: char_u lead_middle[COM_MAX_LEN]; /* middle-comment string */
395: char_u lead_end[COM_MAX_LEN]; /* end-comment string */
396: char_u *comment_end = NULL; /* where lead_end has been found */
397: int extra_space = FALSE; /* append extra space */
398: int current_flag;
399:
400: /*
401: * If the comment leader has the start, middle or end flag, it may not
402: * be used or may be replaced with the middle leader.
403: */
404: for (p = lead_flags; *p && *p != ':'; ++p)
405: {
406: if (*p == COM_START || *p == COM_MIDDLE)
407: {
408: current_flag = *p;
409: if (*p == COM_START)
410: {
411: /*
412: * Doing "O" on a start of comment does not insert leader.
413: */
414: if (dir == BACKWARD)
415: {
416: lead_len = 0;
417: break;
418: }
419:
420: /* find start of middle part */
421: (void)copy_option_part(&p, lead_middle, COM_MAX_LEN, ",");
422: }
423:
424: /*
425: * Isolate the strings of the middle and end leader.
426: */
427: while (*p && p[-1] != ':') /* find end of middle flags */
428: ++p;
429: (void)copy_option_part(&p, lead_middle, COM_MAX_LEN, ",");
430: while (*p && p[-1] != ':') /* find end of end flags */
431: ++p;
432: (void)copy_option_part(&p, lead_end, COM_MAX_LEN, ",");
433:
434: /*
435: * If the end of the comment is in the same line, don't use
436: * the comment leader.
437: */
438: if (dir == FORWARD)
439: {
440: n = STRLEN(lead_end);
441: for (p = saved_line + lead_len; *p; ++p)
442: if (STRNCMP(p, lead_end, n) == 0)
443: {
444: comment_end = p;
445: lead_len = 0;
446: break;
447: }
448: }
449:
450: /*
451: * Doing "o" on a start of comment inserts the middle leader.
452: */
453: if (lead_len)
454: {
455: if (current_flag == COM_START)
456: {
457: lead_repl = lead_middle;
458: lead_repl_len = STRLEN(lead_middle);
459: }
460:
461: /*
462: * If we have hit RETURN immediately after the start
463: * comment leader, then put a space after the middle
464: * comment leader on the next line.
465: */
466: if (!vim_iswhite(saved_line[lead_len - 1]) &&
467: ((p_extra != NULL &&
468: (int)curwin->w_cursor.col == lead_len) ||
469: (p_extra == NULL && saved_line[lead_len] == NUL)))
470: extra_space = TRUE;
471: }
472: break;
473: }
474: if (*p == COM_END)
475: {
476: /*
477: * Doing "o" on the end of a comment does not insert leader.
478: * Remember where the end is, might want to use it to find the
479: * start (for C-comments).
480: */
481: if (dir == FORWARD)
482: {
483: comment_end = skipwhite(saved_line);
484: lead_len = 0;
485: break;
486: }
487:
488: /*
489: * Doing "O" on the end of a comment inserts the middle leader.
490: * Find the string for the middle leader, searching backwards.
491: */
492: while (p > curbuf->b_p_com && *p != ',')
493: --p;
494: for (lead_repl = p; lead_repl > curbuf->b_p_com &&
495: lead_repl[-1] != ':'; --lead_repl)
496: ;
497: lead_repl_len = p - lead_repl;
498: break;
499: }
500: if (*p == COM_FIRST)
501: {
502: /*
503: * Comment leader for first line only: Don't repeat leader
504: * when using "O", blank out leader when using "o".
505: */
506: if (dir == BACKWARD)
507: lead_len = 0;
508: else
509: {
510: lead_repl = (char_u *)"";
511: lead_repl_len = 0;
512: }
513: break;
514: }
515: }
516: if (lead_len)
517: {
518: /* allocate buffer (may concatenate p_exta later) */
519: leader = alloc(lead_len + lead_repl_len + extra_space +
520: extra_len + 1);
521: allocated = leader; /* remember to free it later */
522:
523: if (leader == NULL)
524: lead_len = 0;
525: else
526: {
527: STRNCPY(leader, saved_line, lead_len);
528: leader[lead_len] = NUL;
529:
530: /*
531: * Replace leader with lead_repl, right or left adjusted
532: */
533: if (lead_repl != NULL)
534: {
535: for (p = lead_flags; *p && *p != ':'; ++p)
536: if (*p == COM_RIGHT || *p == COM_LEFT)
537: break;
538: if (*p == COM_RIGHT) /* right adjusted leader */
539: {
540: /* find last non-white in the leader to line up with */
541: for (p = leader + lead_len - 1; p > leader &&
542: vim_iswhite(*p); --p)
543: ;
544:
545: ++p;
546: if (p < leader + lead_repl_len)
547: p = leader;
548: else
549: p -= lead_repl_len;
550: vim_memmove(p, lead_repl, (size_t)lead_repl_len);
551: if (p + lead_repl_len > leader + lead_len)
552: p[lead_repl_len] = NUL;
553:
554: /* blank-out any other chars from the old leader. */
555: while (--p >= leader)
556: if (!vim_iswhite(*p))
557: *p = ' ';
558: }
559: else /* left adjusted leader */
560: {
561: p = skipwhite(leader);
562: vim_memmove(p, lead_repl, (size_t)lead_repl_len);
563:
564: /* blank-out any other chars from the old leader. */
565: for (p += lead_repl_len; p < leader + lead_len; ++p)
566: if (!vim_iswhite(*p))
567: *p = ' ';
568: *p = NUL;
569: }
570:
571: /* Recompute the indent, it may have changed. */
572: if (curbuf->b_p_ai
573: #ifdef SMARTINDENT
574: || curbuf->b_p_si
575: #endif
576: )
577: newindent = get_indent_str(leader);
578: }
579:
580: lead_len = STRLEN(leader);
581: if (extra_space)
582: {
583: leader[lead_len++] = ' ';
584: leader[lead_len] = NUL;
585: }
586:
587: newcol = lead_len;
588:
589: /*
590: * if a new indent will be set below, remove the indent that
591: * is in the comment leader
592: */
593: if (newindent
594: #ifdef SMARTINDENT
595: || did_si
596: #endif
597: )
598: {
599: while (lead_len && vim_iswhite(*leader))
600: {
601: --lead_len;
602: --newcol;
603: ++leader;
604: }
605: }
606:
607: }
608: #ifdef SMARTINDENT
609: did_si = can_si = FALSE;
610: #endif
611: }
612: else if (comment_end != NULL)
613: {
614: /*
615: * We have finished a comment, so we don't use the leader.
616: * If this was a C-comment and 'ai' or 'si' is set do a normal
617: * indent to align with the line containing the start of the
618: * comment.
619: */
620: if (comment_end[0] == '*' && comment_end[1] == '/' &&
621: (curbuf->b_p_ai
622: #ifdef SMARTINDENT
623: || curbuf->b_p_si
624: #endif
625: ))
626: {
627: old_cursor = curwin->w_cursor;
628: curwin->w_cursor.col = comment_end - saved_line;
629: if ((pos = findmatch(NUL)) != NULL)
630: {
631: curwin->w_cursor.lnum = pos->lnum;
632: newindent = get_indent();
633: }
634: curwin->w_cursor = old_cursor;
635: }
636: }
637: }
638:
639: /* (State == INSERT || State == REPLACE), only when dir == FORWARD */
640: if (p_extra != NULL)
641: {
642: *p_extra = saved_char; /* restore char that NUL replaced */
643:
644: /*
645: * When 'ai' set or "del_spaces" TRUE, skip to the first non-blank.
646: *
647: * When in REPLACE mode, put the deleted blanks on the replace
648: * stack, followed by a NUL, so they can be put back when
649: * a BS is entered.
650: */
651: if (State == REPLACE)
652: replace_push(NUL); /* end of extra blanks */
653: if (curbuf->b_p_ai || del_spaces)
654: {
655: while (*p_extra == ' ' || *p_extra == '\t')
656: {
657: if (State == REPLACE)
658: replace_push(*p_extra);
659: ++p_extra;
660: }
661: }
662: if (*p_extra != NUL)
663: did_ai = FALSE; /* append some text, don't trucate now */
664: }
665:
666: if (p_extra == NULL)
667: p_extra = (char_u *)""; /* append empty line */
668:
669: /* concatenate leader and p_extra, if there is a leader */
670: if (lead_len)
671: {
672: STRCAT(leader, p_extra);
673: p_extra = leader;
674: }
675:
676: old_cursor = curwin->w_cursor;
677: if (dir == BACKWARD)
678: --curwin->w_cursor.lnum;
679: if (ml_append(curwin->w_cursor.lnum, p_extra, (colnr_t)0, FALSE) == FAIL)
680: goto theend;
681: mark_adjust(curwin->w_cursor.lnum + 1, MAXLNUM, 1L, 0L);
682: if (newindent
683: #ifdef SMARTINDENT
684: || did_si
685: #endif
686: )
687: {
688: ++curwin->w_cursor.lnum;
689: #ifdef SMARTINDENT
690: if (did_si)
691: {
692: if (p_sr)
693: newindent -= newindent % (int)curbuf->b_p_sw;
694: newindent += (int)curbuf->b_p_sw;
695: }
696: #endif
697: set_indent(newindent, FALSE);
698: /*
699: * In REPLACE mode the new indent must be put on
700: * the replace stack for when it is deleted with BS
701: */
702: if (State == REPLACE)
703: for (n = 0; n < (int)curwin->w_cursor.col; ++n)
704: replace_push(NUL);
705: newcol += curwin->w_cursor.col;
706: #ifdef SMARTINDENT
707: if (no_si)
708: did_si = FALSE;
709: #endif
710: }
711: /*
712: * In REPLACE mode the extra leader must be put on the replace stack for
713: * when it is deleted with BS.
714: */
715: if (State == REPLACE)
716: while (lead_len-- > 0)
717: replace_push(NUL);
718:
719: curwin->w_cursor = old_cursor;
720:
721: if (dir == FORWARD)
722: {
723: if (redraw) /* want to know the old number of screen lines */
724: {
725: old_plines = plines(curwin->w_cursor.lnum);
726: new_plines = old_plines;
727: }
728: if (trunc_line || State == INSERT || State == REPLACE)
729: {
730: if (trunc_line)
731: {
732: /* find start of trailing white space */
733: for (n = STRLEN(saved_line); n > 0 &&
734: vim_iswhite(saved_line[n - 1]); --n)
735: ;
736: saved_line[n] = NUL;
737: }
738: else /* truncate current line at cursor */
739: *(saved_line + curwin->w_cursor.col) = NUL;
740: ml_replace(curwin->w_cursor.lnum, saved_line, FALSE);
741: saved_line = NULL;
742: new_plines = plines(curwin->w_cursor.lnum);
743: }
744:
745: /*
746: * Get the cursor to the start of the line, so that 'curwin->w_row'
747: * gets set to the right physical line number for the stuff that
748: * follows...
749: */
750: curwin->w_cursor.col = 0;
751:
752: if (redraw)
753: {
754: /*
755: * Call cursupdate() to compute w_row.
756: * But we don't want it to update the srceen.
757: */
758: ++RedrawingDisabled;
759: cursupdate();
760: --RedrawingDisabled;
761:
762: /*
763: * If we're doing an open on the last logical line, then go ahead
764: * and scroll the screen up. Otherwise, just insert a blank line
765: * at the right place if the number of screen lines changed.
766: * We use calls to plines() in case the cursor is resting on a
767: * long line, we want to know the row below the line.
768: */
769: n = curwin->w_row + new_plines;
770: if (n == curwin->w_winpos + curwin->w_height)
771: scrollup(1L);
772: else
773: win_ins_lines(curwin, n,
774: plines(curwin->w_cursor.lnum + 1) + new_plines - old_plines,
775: TRUE, TRUE);
776: }
777:
778: /*
779: * Put the cursor on the new line. Careful: the cursupdate() and
780: * scrollup() above may have moved w_cursor, we must use old_cursor.
781: */
782: curwin->w_cursor.lnum = old_cursor.lnum + 1;
783: }
784: else if (redraw) /* insert physical line above current line */
785: win_ins_lines(curwin, curwin->w_row, 1, TRUE, TRUE);
786:
787: curwin->w_cursor.col = newcol;
788:
789: #ifdef LISPINDENT
790: /*
791: * May do lisp indenting.
792: */
793: if (leader == NULL && curbuf->b_p_lisp && curbuf->b_p_ai)
794: fixthisline(get_lisp_indent);
795: #endif
796: #ifdef CINDENT
797: /*
798: * May do indenting after opening a new line.
799: */
800: if (leader == NULL && curbuf->b_p_cin &&
801: in_cinkeys(dir == FORWARD ? KEY_OPEN_FORW :
802: KEY_OPEN_BACK, ' ', linewhite(curwin->w_cursor.lnum)))
803: fixthisline(get_c_indent);
804: #endif
805:
806: if (redraw)
807: {
808: updateScreen(VALID_TO_CURSCHAR);
809: cursupdate(); /* update curwin->w_row */
810: }
811: CHANGED;
812:
813: retval = TRUE; /* success! */
814: theend:
815: vim_free(saved_line);
816: vim_free(allocated);
817: return retval;
818: }
819:
820: /*
821: * get_leader_len() returns the length of the prefix of the given string
822: * which introduces a comment. If this string is not a comment then 0 is
823: * returned.
824: * When "flags" is non-zero, it is set to point to the flags of the recognized
825: * comment leader.
826: */
827: int
828: get_leader_len(line, flags)
829: char_u *line;
830: char_u **flags;
831: {
832: int i, j;
833: int got_com = FALSE;
834: int found_one;
835: char_u part_buf[COM_MAX_LEN]; /* buffer for one option part */
836: char_u *string; /* pointer to comment string */
837: char_u *list;
838:
839: if (!fo_do_comments) /* don't format comments at all */
840: return 0;
841:
842: i = 0;
843: while (vim_iswhite(line[i])) /* leading white space is ignored */
844: ++i;
845:
846: /*
847: * Repeat to match several nested comment strings.
848: */
849: while (line[i])
850: {
851: /*
852: * scan through the 'comments' option for a match
853: */
854: found_one = FALSE;
855: for (list = curbuf->b_p_com; *list; )
856: {
857: /*
858: * Get one option part into part_buf[]. Advance list to next one.
859: * put string at start of string.
860: */
861: if (!got_com && flags != NULL) /* remember where flags started */
862: *flags = list;
863: (void)copy_option_part(&list, part_buf, COM_MAX_LEN, ",");
864: string = vim_strchr(part_buf, ':');
865: if (string == NULL) /* missing ':', ignore this part */
866: continue;
867: *string++ = NUL; /* isolate flags from string */
868:
869: /*
870: * When already found a nested comment, only accept further
871: * nested comments.
872: */
873: if (got_com && vim_strchr(part_buf, COM_NEST) == NULL)
874: continue;
875:
876: /*
877: * Line contents and string must match.
878: */
879: for (j = 0; string[j] != NUL && string[j] == line[i + j]; ++j)
880: ;
881: if (string[j] != NUL)
882: continue;
883:
884: /*
885: * When 'b' flag used, there must be white space or an
886: * end-of-line after the string in the line.
887: */
888: if (vim_strchr(part_buf, COM_BLANK) != NULL &&
889: !vim_iswhite(line[i + j]) && line[i + j] != NUL)
890: continue;
891:
892: /*
893: * We have found a match, stop searching.
894: */
895: i += j;
896: got_com = TRUE;
897: found_one = TRUE;
898: break;
899: }
900:
901: /*
902: * No match found, stop scanning.
903: */
904: if (!found_one)
905: break;
906:
907: /*
908: * Include any trailing white space.
909: */
910: while (vim_iswhite(line[i]))
911: ++i;
912:
913: /*
914: * If this comment doesn't nest, stop here.
915: */
916: if (vim_strchr(part_buf, COM_NEST) == NULL)
917: break;
918: }
919: return (got_com ? i : 0);
920: }
921:
922: /*
923: * plines(p) - return the number of physical screen lines taken by line 'p'
924: */
925: int
926: plines(p)
927: linenr_t p;
928: {
929: return plines_win(curwin, p);
930: }
931:
932: int
933: plines_win(wp, p)
934: WIN *wp;
935: linenr_t p;
936: {
937: register long col;
938: register char_u *s;
939: register int lines;
940:
941: if (!wp->w_p_wrap)
942: return 1;
943:
944: s = ml_get_buf(wp->w_buffer, p, FALSE);
945: if (*s == NUL) /* empty line */
946: return 1;
947:
948: col = linetabsize(s);
949:
950: /*
951: * If list mode is on, then the '$' at the end of the line takes up one
952: * extra column.
953: */
954: if (wp->w_p_list)
955: col += 1;
956:
957: /*
958: * If 'number' mode is on, add another 8.
959: */
960: if (wp->w_p_nu)
961: col += 8;
962:
963: lines = (col + (Columns - 1)) / Columns;
964: if (lines <= wp->w_height)
965: return lines;
966: return (int)(wp->w_height); /* maximum length */
967: }
968:
969: /*
970: * Count the physical lines (rows) for the lines "first" to "last" inclusive.
971: */
972: int
973: plines_m(first, last)
974: linenr_t first, last;
975: {
976: return plines_m_win(curwin, first, last);
977: }
978:
979: int
980: plines_m_win(wp, first, last)
981: WIN *wp;
982: linenr_t first, last;
983: {
984: int count = 0;
985:
986: while (first <= last)
987: count += plines_win(wp, first++);
988: return (count);
989: }
990:
991: /*
992: * Insert or replace a single character at the cursor position.
993: * When in REPLACE mode, replace any existing character.
994: */
995: void
996: ins_char(c)
997: int c;
998: {
999: register char_u *p;
1000: char_u *newp;
1001: char_u *oldp;
1002: int oldlen;
1003: int extra;
1004: colnr_t col = curwin->w_cursor.col;
1005: linenr_t lnum = curwin->w_cursor.lnum;
1006:
1007: oldp = ml_get(lnum);
1008: oldlen = STRLEN(oldp) + 1;
1009:
1010: if (State != REPLACE || *(oldp + col) == NUL)
1011: extra = 1;
1012: else
1013: extra = 0;
1014:
1015: /*
1016: * a character has to be put on the replace stack if there is a
1017: * character that is replaced, so it can be put back when BS is used.
1018: * Otherwise a 0 is put on the stack, indicating that a new character
1019: * was inserted, which can be deleted when BS is used.
1020: */
1021: if (State == REPLACE)
1022: replace_push(!extra ? *(oldp + col) : 0);
1023: newp = alloc_check((unsigned)(oldlen + extra));
1024: if (newp == NULL)
1025: return;
1026: vim_memmove(newp, oldp, (size_t)col);
1027: p = newp + col;
1028: vim_memmove(p + extra, oldp + col, (size_t)(oldlen - col));
1029: *p = c;
1030: ml_replace(lnum, newp, FALSE);
1031:
1032: /*
1033: * If we're in insert or replace mode and 'showmatch' is set, then check for
1034: * right parens and braces. If there isn't a match, then beep. If there
1035: * is a match AND it's on the screen, then flash to it briefly. If it
1036: * isn't on the screen, don't do anything.
1037: */
1038: #ifdef RIGHTLEFT
1039: if (p_sm && (State & INSERT) &&
1040: ((!curwin->w_p_rl && (c == ')' || c == '}' || c == ']')) ||
1041: (curwin->w_p_rl && (c == '(' || c == '{' || c == '['))))
1042: #else
1043: if (p_sm && (State & INSERT) && (c == ')' || c == '}' || c == ']'))
1044: #endif
1045: showmatch();
1046:
1047: #ifdef RIGHTLEFT
1048: if (!p_ri || State == REPLACE) /* normal insert: cursor right */
1049: #endif
1050: ++curwin->w_cursor.col;
1051: CHANGED;
1052: }
1053:
1054: /*
1055: * Insert a string at the cursor position.
1056: * Note: Nothing special for replace mode.
1057: */
1058: void
1059: ins_str(s)
1060: char_u *s;
1061: {
1062: register char_u *oldp, *newp;
1063: register int newlen = STRLEN(s);
1064: int oldlen;
1065: colnr_t col = curwin->w_cursor.col;
1066: linenr_t lnum = curwin->w_cursor.lnum;
1067:
1068: oldp = ml_get(lnum);
1069: oldlen = STRLEN(oldp);
1070:
1071: newp = alloc_check((unsigned)(oldlen + newlen + 1));
1072: if (newp == NULL)
1073: return;
1074: vim_memmove(newp, oldp, (size_t)col);
1075: vim_memmove(newp + col, s, (size_t)newlen);
1076: vim_memmove(newp + col + newlen, oldp + col, (size_t)(oldlen - col + 1));
1077: ml_replace(lnum, newp, FALSE);
1078: curwin->w_cursor.col += newlen;
1079: CHANGED;
1080: }
1081:
1082: /*
1083: * delete one character under the cursor
1084: *
1085: * return FAIL for failure, OK otherwise
1086: */
1087: int
1088: delchar(fixpos)
1089: int fixpos; /* if TRUE fix the cursor position when done */
1090: {
1091: char_u *oldp, *newp;
1092: colnr_t oldlen;
1093: linenr_t lnum = curwin->w_cursor.lnum;
1094: colnr_t col = curwin->w_cursor.col;
1095: int was_alloced;
1096:
1097: oldp = ml_get(lnum);
1098: oldlen = STRLEN(oldp);
1099:
1100: if (col >= oldlen) /* can't do anything (happens with replace mode) */
1101: return FAIL;
1102:
1103: /*
1104: * If the old line has been allocated the deletion can be done in the
1105: * existing line. Otherwise a new line has to be allocated
1106: */
1107: was_alloced = ml_line_alloced(); /* check if oldp was allocated */
1108: if (was_alloced)
1109: newp = oldp; /* use same allocated memory */
1110: else
1111: {
1112: newp = alloc((unsigned)oldlen); /* need to allocated a new line */
1113: if (newp == NULL)
1114: return FAIL;
1115: vim_memmove(newp, oldp, (size_t)col);
1116: }
1117: vim_memmove(newp + col, oldp + col + 1, (size_t)(oldlen - col));
1118: if (!was_alloced)
1119: ml_replace(lnum, newp, FALSE);
1120:
1121: /*
1122: * If we just took off the last character of a non-blank line, we don't
1123: * want to end up positioned at the NUL.
1124: */
1125: if (fixpos && curwin->w_cursor.col > 0 && col == oldlen - 1)
1126: --curwin->w_cursor.col;
1127:
1128: CHANGED;
1129: return OK;
1130: }
1131:
1132: /*
1133: * Delete from cursor to end of line.
1134: *
1135: * return FAIL for failure, OK otherwise
1136: */
1137: int
1138: truncate_line(fixpos)
1139: int fixpos; /* if TRUE fix the cursor position when done */
1140: {
1141: char_u *newp;
1142: linenr_t lnum = curwin->w_cursor.lnum;
1143: colnr_t col = curwin->w_cursor.col;
1144:
1145: if (col == 0)
1146: newp = strsave((char_u *)"");
1147: else
1148: newp = strnsave(ml_get(lnum), col);
1149:
1150: if (newp == NULL)
1151: return FAIL;
1152:
1153: ml_replace(lnum, newp, FALSE);
1154:
1155: /*
1156: * If "fixpos" is TRUE we don't want to end up positioned at the NUL.
1157: */
1158: if (fixpos && curwin->w_cursor.col > 0)
1159: --curwin->w_cursor.col;
1160:
1161: CHANGED;
1162: return OK;
1163: }
1164:
1165: void
1166: dellines(nlines, dowindow, undo)
1167: long nlines; /* number of lines to delete */
1168: int dowindow; /* if true, update the window */
1169: int undo; /* if true, prepare for undo */
1170: {
1171: int num_plines = 0;
1172:
1173: if (nlines <= 0)
1174: return;
1175: /*
1176: * There's no point in keeping the window updated if redrawing is disabled
1177: * or we're deleting more than a window's worth of lines.
1178: */
1179: if (RedrawingDisabled)
1180: dowindow = FALSE;
1181: else if (nlines > (curwin->w_height - curwin->w_row) && dowindow)
1182: {
1183: dowindow = FALSE;
1184: /* flaky way to clear rest of window */
1185: win_del_lines(curwin, curwin->w_row, curwin->w_height, TRUE, TRUE);
1186: }
1187: /* save the deleted lines for undo */
1188: if (undo && u_savedel(curwin->w_cursor.lnum, nlines) == FAIL)
1189: return;
1190:
1191: /* adjust marks for deleted lines and lines that follow */
1192: mark_adjust(curwin->w_cursor.lnum, curwin->w_cursor.lnum + nlines - 1,
1193: MAXLNUM, -nlines);
1194:
1195: while (nlines-- > 0)
1196: {
1197: if (curbuf->b_ml.ml_flags & ML_EMPTY) /* nothing to delete */
1198: break;
1199:
1200: /*
1201: * Set up to delete the correct number of physical lines on the
1202: * window
1203: */
1204: if (dowindow)
1205: num_plines += plines(curwin->w_cursor.lnum);
1206:
1207: ml_delete(curwin->w_cursor.lnum, TRUE);
1208:
1209: CHANGED;
1210:
1211: /* If we delete the last line in the file, stop */
1212: if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
1213: {
1214: curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
1215: break;
1216: }
1217: }
1218: curwin->w_cursor.col = 0;
1219: /*
1220: * Delete the correct number of physical lines on the window
1221: */
1222: if (dowindow && num_plines > 0)
1223: win_del_lines(curwin, curwin->w_row, num_plines, TRUE, TRUE);
1224: }
1225:
1226: int
1227: gchar(pos)
1228: FPOS *pos;
1229: {
1230: return (int)(*(ml_get_pos(pos)));
1231: }
1232:
1233: int
1234: gchar_cursor()
1235: {
1236: return (int)(*(ml_get_cursor()));
1237: }
1238:
1239: /*
1240: * Write a character at the current cursor position.
1241: * It is directly written into the block.
1242: */
1243: void
1244: pchar_cursor(c)
1245: int c;
1246: {
1247: *(ml_get_buf(curbuf, curwin->w_cursor.lnum, TRUE) +
1248: curwin->w_cursor.col) = c;
1249: }
1250:
1251: /*
1252: * Put *pos at end of current buffer
1253: */
1254: void
1255: goto_endofbuf(pos)
1256: FPOS *pos;
1257: {
1258: char_u *p;
1259:
1260: pos->lnum = curbuf->b_ml.ml_line_count;
1261: pos->col = 0;
1262: p = ml_get(pos->lnum);
1263: while (*p++)
1264: ++pos->col;
1265: }
1266:
1267: /*
1268: * When extra == 0: Return TRUE if the cursor is before or on the first
1269: * non-blank in the line.
1270: * When extra == 1: Return TRUE if the cursor is before the first non-blank in
1271: * the line.
1272: */
1273: int
1274: inindent(extra)
1275: int extra;
1276: {
1277: register char_u *ptr;
1278: register colnr_t col;
1279:
1280: for (col = 0, ptr = ml_get_curline(); vim_iswhite(*ptr); ++col)
1281: ++ptr;
1282: if (col >= curwin->w_cursor.col + extra)
1283: return TRUE;
1284: else
1285: return FALSE;
1286: }
1287:
1288: /*
1289: * skipwhite: skip over ' ' and '\t'.
1290: */
1291: char_u *
1292: skipwhite(p)
1293: register char_u *p;
1294: {
1295: while (vim_iswhite(*p)) /* skip to next non-white */
1296: ++p;
1297: return p;
1298: }
1299:
1300: /*
1301: * skipdigits: skip over digits;
1302: */
1303: char_u *
1304: skipdigits(p)
1305: register char_u *p;
1306: {
1307: while (isdigit(*p)) /* skip to next non-digit */
1308: ++p;
1309: return p;
1310: }
1311:
1312: /*
1313: * skiptowhite: skip over text until ' ' or '\t' or NUL.
1314: */
1315: char_u *
1316: skiptowhite(p)
1317: register char_u *p;
1318: {
1319: while (*p != ' ' && *p != '\t' && *p != NUL)
1320: ++p;
1321: return p;
1322: }
1323:
1324: /*
1325: * skiptowhite_esc: Like skiptowhite(), but also skip escaped chars
1326: */
1327: char_u *
1328: skiptowhite_esc(p)
1329: register char_u *p;
1330: {
1331: while (*p != ' ' && *p != '\t' && *p != NUL)
1332: {
1333: if ((*p == '\\' || *p == Ctrl('V')) && *(p + 1) != NUL)
1334: ++p;
1335: ++p;
1336: }
1337: return p;
1338: }
1339:
1340: /*
1341: * getdigits: get a number from a string and skip over it
1342: *
1343: * note: you must give a pointer to a char_u pointer!
1344: */
1345:
1346: long
1347: getdigits(pp)
1348: char_u **pp;
1349: {
1350: register char_u *p;
1351: long retval;
1352:
1353: p = *pp;
1354: retval = atol((char *)p);
1355: p = skipdigits(p); /* skip to next non-digit */
1356: *pp = p;
1357: return retval;
1358: }
1359:
1360: /*
1361: * Skip to next part of an option argument: Skip space and comma.
1362: */
1363: char_u *
1364: skip_to_option_part(p)
1365: char_u *p;
1366: {
1367: if (*p == ',')
1368: ++p;
1369: while (*p == ' ')
1370: ++p;
1371: return p;
1372: }
1373:
1374: char *
1375: plural(n)
1376: long n;
1377: {
1378: static char buf[2] = "s";
1379:
1380: if (n == 1)
1381: return &(buf[1]);
1382: return &(buf[0]);
1383: }
1384:
1385: /*
1386: * set_Changed is called when something in the current buffer is changed
1387: */
1388: void
1389: set_Changed()
1390: {
1391: if (!curbuf->b_changed)
1392: {
1393: change_warning();
1394: curbuf->b_changed = TRUE;
1395: check_status(curbuf);
1396: }
1397: modified = TRUE; /* used for redrawing */
1398: }
1399:
1400: /*
1401: * unset_Changed is called when the changed flag must be reset for buffer 'buf'
1402: */
1403: void
1404: unset_Changed(buf)
1405: BUF *buf;
1406: {
1407: if (buf->b_changed)
1408: {
1409: buf->b_changed = 0;
1410: check_status(buf);
1411: }
1412: }
1413:
1414: /*
1415: * check_status: called when the status bars for the buffer 'buf'
1416: * need to be updated
1417: */
1418: static void
1419: check_status(buf)
1420: BUF *buf;
1421: {
1422: WIN *wp;
1423: int i;
1424:
1425: i = 0;
1426: for (wp = firstwin; wp != NULL; wp = wp->w_next)
1427: if (wp->w_buffer == buf && wp->w_status_height)
1428: {
1429: wp->w_redr_status = TRUE;
1430: ++i;
1431: }
1432: if (i)
1433: redraw_later(NOT_VALID);
1434: }
1435:
1436: /*
1437: * If the file is readonly, give a warning message with the first change.
1438: * Don't do this for autocommands.
1439: * Don't use emsg(), because it flushes the macro buffer.
1440: * If we have undone all changes b_changed will be FALSE, but b_did_warn
1441: * will be TRUE.
1442: */
1443: void
1444: change_warning()
1445: {
1446: if (curbuf->b_did_warn == FALSE && curbuf->b_changed == 0 &&
1447: #ifdef AUTOCMD
1448: !autocmd_busy &&
1449: #endif
1450: curbuf->b_p_ro)
1451: {
1452: curbuf->b_did_warn = TRUE;
1453: MSG("Warning: Changing a readonly file");
1454: mch_delay(1000L, TRUE); /* give him some time to think about it */
1455: }
1456: }
1457:
1458: /*
1459: * Ask for a reply from the user, a 'y' or a 'n'.
1460: * No other characters are accepted, the message is repeated until a valid
1461: * reply is entered or CTRL-C is hit.
1462: * If direct is TRUE, don't use vgetc but mch_inchar, don't get characters from
1463: * any buffers but directly from the user.
1464: *
1465: * return the 'y' or 'n'
1466: */
1467: int
1468: ask_yesno(str, direct)
1469: char_u *str;
1470: int direct;
1471: {
1472: int r = ' ';
1473: char_u buf[20];
1474: int len = 0;
1475: int idx = 0;
1476:
1477: if (exiting) /* put terminal in raw mode for this question */
1478: settmode(1);
1479: while (r != 'y' && r != 'n')
1480: {
1481: (void)set_highlight('r'); /* same highlighting as for wait_return */
1482: msg_highlight = TRUE;
1483: smsg((char_u *)"%s (y/n)?", str);
1484: if (direct)
1485: {
1486: flushbuf();
1487: if (idx >= len)
1488: {
1489: len = mch_inchar(buf, 20, -1L);
1490: idx = 0;
1491: }
1492: r = buf[idx++];
1493: }
1494: else
1495: r = vgetc();
1496: if (r == Ctrl('C') || r == ESC)
1497: r = 'n';
1498: msg_outchar(r); /* show what you typed */
1499: flushbuf();
1500: }
1501: return r;
1502: }
1503:
1504: /*
1505: * get a number from the user
1506: */
1507: int
1508: get_number()
1509: {
1510: int n = 0;
1511: int c;
1512:
1513: for (;;)
1514: {
1515: windgoto(msg_row, msg_col);
1516: c = vgetc();
1517: if (isdigit(c))
1518: {
1519: n = n * 10 + c - '0';
1520: msg_outchar(c);
1521: }
1522: else if (c == K_DEL || c == K_BS || c == Ctrl('H'))
1523: {
1524: n /= 10;
1525: MSG_OUTSTR("\b \b");
1526: }
1527: else if (c == CR || c == NL || c == Ctrl('C'))
1528: break;
1529: }
1530: return n;
1531: }
1532:
1533: void
1534: msgmore(n)
1535: long n;
1536: {
1537: long pn;
1538:
1539: if (global_busy || /* no messages now, wait until global is finished */
1540: keep_msg) /* there is a message already, skip this one */
1541: return;
1542:
1543: if (n > 0)
1544: pn = n;
1545: else
1546: pn = -n;
1547:
1548: if (pn > p_report)
1549: {
1550: sprintf((char *)msg_buf, "%ld %s line%s %s",
1551: pn, n > 0 ? "more" : "fewer", plural(pn),
1552: got_int ? "(Interrupted)" : "");
1553: if (msg(msg_buf))
1554: keep_msg = msg_buf;
1555: }
1556: }
1557:
1558: /*
1559: * flush map and typeahead buffers and give a warning for an error
1560: */
1561: void
1562: beep_flush()
1563: {
1564: flush_buffers(FALSE);
1565: vim_beep();
1566: }
1567:
1568: /*
1569: * give a warning for an error
1570: */
1571: void
1572: vim_beep()
1573: {
1574: if (p_vb)
1575: {
1576: #ifdef DJGPP
1577: ScreenVisualBell();
1578: #else
1579: outstr(T_VB);
1580: #endif
1581: }
1582: else
1583: {
1584: #if defined MSDOS || defined WIN32 /* ? gvr */
1585: /*
1586: * The number of beeps outputted is reduced to avoid having to wait
1587: * for all the beeps to finish. This is only a problem on systems
1588: * where the beeps don't overlap.
1589: */
1590: if (beep_count == 0 || beep_count == 10)
1591: {
1592: outchar('\007');
1593: beep_count = 1;
1594: }
1595: else
1596: ++beep_count;
1597: #else
1598: outchar('\007');
1599: #endif
1600: }
1601: }
1602:
1603: /*
1604: * To get the "real" home directory:
1605: * - get value of $HOME
1606: * For Unix:
1607: * - go to that directory
1608: * - do mch_dirname() to get the real name of that directory.
1609: * This also works with mounts and links.
1610: * Don't do this for MS-DOS, it will change the "current dir" for a drive.
1611: */
1612: static char_u *homedir = NULL;
1613:
1614: void
1615: init_homedir()
1616: {
1617: char_u *var;
1618:
1619: var = vim_getenv((char_u *)"HOME");
1620: #if defined(OS2) || defined(MSDOS) || defined(WIN32)
1621: /*
1622: * Default home dir is C:/
1623: * Best assumption we can make in such a situation.
1624: */
1625: if (var == NULL)
1626: var = "C:/";
1627: #endif
1628: if (var != NULL)
1629: {
1630: #ifdef UNIX
1631: if (mch_dirname(NameBuff, MAXPATHL) == OK)
1632: {
1633: if (!vim_chdir((char *)var) && mch_dirname(IObuff, IOSIZE) == OK)
1634: var = IObuff;
1635: vim_chdir((char *)NameBuff);
1636: }
1637: #endif
1638: homedir = strsave(var);
1639: }
1640: }
1641:
1642: /*
1643: * Expand environment variable with path name.
1644: * For Unix and OS/2 "~/" is also expanded, like $HOME.
1645: * If anything fails no expansion is done and dst equals src.
1646: * Note that IObuff must NOT be used as either src or dst! This is because
1647: * vim_getenv() may use IObuff to do its expansion.
1648: */
1649: void
1650: expand_env(src, dst, dstlen)
1651: char_u *src; /* input string e.g. "$HOME/vim.hlp" */
1652: char_u *dst; /* where to put the result */
1653: int dstlen; /* maximum length of the result */
1654: {
1655: char_u *tail;
1656: int c;
1657: char_u *var;
1658: int copy_char;
1659: #if defined(UNIX) || defined(OS2)
1660: int mustfree;
1661: int at_start = TRUE;
1662: #endif
1663:
1664: src = skipwhite(src);
1665: --dstlen; /* leave one char space for "\," */
1666: while (*src && dstlen > 0)
1667: {
1668: copy_char = TRUE;
1669: if (*src == '$'
1670: #if defined(UNIX) || defined(OS2)
1671: || (*src == '~' && at_start)
1672: #endif
1673: )
1674: {
1675: #if defined(UNIX) || defined(OS2)
1676: mustfree = FALSE;
1677:
1678: /*
1679: * The variable name is copied into dst temporarily, because it may
1680: * be a string in read-only memory and a NUL needs to be inserted.
1681: */
1682: if (*src == '$') /* environment var */
1683: {
1684: #endif
1685: tail = src + 1;
1686: var = dst;
1687: c = dstlen - 1;
1688: while (c-- > 0 && *tail && isidchar(*tail))
1689: #ifdef OS2
1690: { /* env vars only in uppercase */
1691: *var++ = toupper(*tail); /* toupper() may be a macro! */
1692: tail++;
1693: }
1694: #else
1695: *var++ = *tail++;
1696: #endif
1697: *var = NUL;
1698: #if defined(OS2) || defined(MSDOS) || defined(WIN32)
1699: /* use "C:/" when $HOME is not set */
1700: if (STRCMP(dst, "HOME") == 0)
1701: var = homedir;
1702: else
1703: #endif
1704: var = vim_getenv(dst);
1705: #if defined(UNIX) || defined(OS2)
1706: }
1707: /* home directory */
1708: else if (src[1] == NUL ||
1709: vim_strchr((char_u *)"/ ,\t\n", src[1]) != NULL)
1710: {
1711: var = homedir;
1712: tail = src + 1;
1713: }
1714: else /* user directory */
1715: # ifdef OS2
1716: {
1717: /* cannot expand user's home directory, so don't try */
1718: var = NULL;
1719: tail = ""; /* shut gcc up about "may be used uninitialized" */
1720: }
1721: # else
1722: {
1723: /*
1724: * Copy ~user to dst[], so we can put a NUL after it.
1725: */
1726: tail = src;
1727: var = dst;
1728: c = dstlen - 1;
1729: while (c-- > 0 && *tail &&
1730: isfilechar(*tail) && !ispathsep(*tail))
1731: *var++ = *tail++;
1732: *var = NUL;
1733:
1734: /*
1735: * If the system supports getpwnam(), use it.
1736: * Otherwise, or if getpwnam() fails, the shell is used to
1737: * expand ~user. This is slower and may fail if the shell
1738: * does not support ~user (old versions of /bin/sh).
1739: */
1740: # if defined(HAVE_GETPWNAM) && defined(HAVE_PWD_H)
1741: {
1742: struct passwd *pw;
1743:
1744: pw = getpwnam((char *)dst + 1);
1745: if (pw != NULL)
1746: var = (char_u *)pw->pw_dir;
1747: else
1748: var = NULL;
1749: }
1750: if (var == NULL)
1751: # endif
1752: {
1753: var = ExpandOne(dst, NULL, 0, WILD_EXPAND_FREE);
1754: mustfree = TRUE;
1755: }
1756: }
1757: # endif /* OS2 */
1758: #endif /* UNIX || OS2 */
1759: if (var != NULL && *var != NUL &&
1760: (STRLEN(var) + STRLEN(tail) + 1 < (unsigned)dstlen))
1761: {
1762: STRCPY(dst, var);
1763: dstlen -= STRLEN(var);
1764: dst += STRLEN(var);
1765: /* if var[] ends in a path separator and tail[] starts
1766: * with it, skip a character */
1767: if (*var && ispathsep(*(dst-1)) && ispathsep(*tail))
1768: ++tail;
1769: src = tail;
1770: copy_char = FALSE;
1771: }
1772: #if defined(UNIX) || defined(OS2)
1773: if (mustfree)
1774: vim_free(var);
1775: #endif
1776: }
1777:
1778: if (copy_char) /* copy at least one char */
1779: {
1780: #if defined(UNIX) || defined(OS2)
1781: /*
1782: * Recogize the start of a new name, for '~'.
1783: */
1784: at_start = FALSE;
1785: #endif
1786: if (src[0] == '\\')
1787: {
1788: *dst++ = *src++;
1789: --dstlen;
1790: }
1791: #if defined(UNIX) || defined(OS2)
1792: else if (src[0] == ' ' || src[0] == ',')
1793: at_start = TRUE;
1794: #endif
1795: *dst++ = *src++;
1796: --dstlen;
1797: }
1798: }
1799: *dst = NUL;
1800: }
1801:
1802: /*
1803: * Replace home directory by "~/" in each space or comma separated filename in
1804: * 'src'. If anything fails (except when out of space) dst equals src.
1805: */
1806: void
1807: home_replace(buf, src, dst, dstlen)
1808: BUF *buf; /* when not NULL, check for help files */
1809: char_u *src; /* input file name */
1810: char_u *dst; /* where to put the result */
1811: int dstlen; /* maximum length of the result */
1812: {
1813: size_t dirlen = 0, envlen = 0;
1814: size_t len;
1815: char_u *homedir_env;
1816: char_u *p;
1817:
1818: if (src == NULL)
1819: {
1820: *dst = NUL;
1821: return;
1822: }
1823:
1824: /*
1825: * If the file is a help file, remove the path completely.
1826: */
1827: if (buf != NULL && buf->b_help)
1828: {
1829: STRCPY(dst, gettail(src));
1830: return;
1831: }
1832:
1833: /*
1834: * We check both the value of the $HOME environment variable and the
1835: * "real" home directory.
1836: */
1837: if (homedir != NULL)
1838: dirlen = STRLEN(homedir);
1839: homedir_env = vim_getenv((char_u *)"HOME");
1840: if (homedir_env != NULL)
1841: envlen = STRLEN(homedir_env);
1842:
1843: src = skipwhite(src);
1844: while (*src && dstlen > 0)
1845: {
1846: /*
1847: * Here we are at the beginning of a filename.
1848: * First, check to see if the beginning of the filename matches
1849: * $HOME or the "real" home directory. Check that there is a '/'
1850: * after the match (so that if e.g. the file is "/home/pieter/bla",
1851: * and the home directory is "/home/piet", the file does not end up
1852: * as "~er/bla" (which would seem to indicate the file "bla" in user
1853: * er's home directory)).
1854: */
1855: p = homedir;
1856: len = dirlen;
1857: for (;;)
1858: {
1859: if (len && fnamencmp(src, p, len) == 0 && (ispathsep(src[len]) ||
1860: src[len] == ',' || src[len] == ' ' || src[len] == NUL))
1861: {
1862: src += len;
1863: if (--dstlen > 0)
1864: *dst++ = '~';
1865: /*
1866: * If it's just the home directory, make it "~/".
1867: */
1868: if (!ispathsep(src[0]) && --dstlen > 0)
1869: *dst++ = '/';
1870: }
1871: if (p == homedir_env)
1872: break;
1873: p = homedir_env;
1874: len = envlen;
1875: }
1876:
1877: /* skip to separator: space or comma */
1878: while (*src && *src != ',' && *src != ' ' && --dstlen > 0)
1879: *dst++ = *src++;
1880: /* skip separator */
1881: while ((*src == ' ' || *src == ',') && --dstlen > 0)
1882: *dst++ = *src++;
1883: }
1884: /* if (dstlen == 0) out of space, what to do??? */
1885:
1886: *dst = NUL;
1887: }
1888:
1889: /*
1890: * Like home_replace, store the replaced string in allocated memory.
1891: * When something fails, NULL is returned.
1892: */
1893: char_u *
1894: home_replace_save(buf, src)
1895: BUF *buf; /* when not NULL, check for help files */
1896: char_u *src; /* input file name */
1897: {
1898: char_u *dst;
1899: unsigned len;
1900:
1901: if (src == NULL) /* just in case */
1902: len = 3;
1903: else
1904: len = STRLEN(src) + 3; /* extra space for "~/" and trailing NUL */
1905: dst = alloc(len);
1906: if (dst != NULL)
1907: home_replace(buf, src, dst, len);
1908: return dst;
1909: }
1910:
1911: /*
1912: * Compare two file names and return:
1913: * FPC_SAME if they both exist and are the same file.
1914: * FPC_DIFF if they both exist and are different files.
1915: * FPC_NOTX if they both don't exist.
1916: * FPC_DIFFX if one of them doesn't exist.
1917: * For the first name environment variables are expanded
1918: */
1919: int
1920: fullpathcmp(s1, s2)
1921: char_u *s1, *s2;
1922: {
1923: #ifdef UNIX
1924: char_u buf1[MAXPATHL];
1925: struct stat st1, st2;
1926: int r1, r2;
1927:
1928: expand_env(s1, buf1, MAXPATHL);
1929: r1 = stat((char *)buf1, &st1);
1930: r2 = stat((char *)s2, &st2);
1931: if (r1 != 0 && r2 != 0)
1932: return FPC_NOTX;
1933: if (r1 != 0 || r2 != 0)
1934: return FPC_DIFFX;
1935: if (st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino)
1936: return FPC_SAME;
1937: return FPC_DIFF;
1938: #else
1939: char_u *buf1 = NULL;
1940: char_u *buf2 = NULL;
1941: int retval = FPC_DIFF;
1942: int r1, r2;
1943:
1944: if ((buf1 = alloc(MAXPATHL)) != NULL && (buf2 = alloc(MAXPATHL)) != NULL)
1945: {
1946: expand_env(s1, buf2, MAXPATHL);
1947: /*
1948: * If FullName() failed, the file probably doesn't exist.
1949: */
1950: r1 = FullName(buf2, buf1, MAXPATHL, FALSE);
1951: r2 = FullName(s2, buf2, MAXPATHL, FALSE);
1952: if (r1 != OK && r2 != OK)
1953: retval = FPC_NOTX;
1954: else if (r1 != OK || r2 != OK)
1955: retval = FPC_DIFFX;
1956: else if (fnamecmp(buf1, buf2))
1957: retval = FPC_DIFF;
1958: else
1959: retval = FPC_SAME;
1960: }
1961: vim_free(buf1);
1962: vim_free(buf2);
1963: return retval;
1964: #endif
1965: }
1966:
1967: /*
1968: * get the tail of a path: the file name.
1969: */
1970: char_u *
1971: gettail(fname)
1972: char_u *fname;
1973: {
1974: register char_u *p1, *p2;
1975:
1976: if (fname == NULL)
1977: return (char_u *)"";
1978: for (p1 = p2 = fname; *p2; ++p2) /* find last part of path */
1979: {
1980: if (ispathsep(*p2))
1981: p1 = p2 + 1;
1982: }
1983: return p1;
1984: }
1985:
1986: /*
1987: * return TRUE if 'c' is a path separator.
1988: */
1989: int
1990: ispathsep(c)
1991: int c;
1992: {
1993: #ifdef UNIX
1994: return (c == PATHSEP); /* UNIX has ':' inside file names */
1995: #else
1996: # ifdef BACKSLASH_IN_FILENAME
1997: return (c == ':' || c == PATHSEP || c == '\\');
1998: # else
1999: return (c == ':' || c == PATHSEP);
2000: # endif
2001: #endif
2002: }
2003:
2004: /*
2005: * Concatenate filenames fname1 and fname2 into allocated memory.
2006: * Only add a '/' when 'sep' is TRUE and it is neccesary.
2007: */
2008: char_u *
2009: concat_fnames(fname1, fname2, sep)
2010: char_u *fname1;
2011: char_u *fname2;
2012: int sep;
2013: {
2014: char_u *dest;
2015:
2016: dest = alloc((unsigned)(STRLEN(fname1) + STRLEN(fname2) + 2));
2017: if (dest != NULL)
2018: {
2019: STRCPY(dest, fname1);
2020: if (sep && *dest && !ispathsep(*(dest + STRLEN(dest) - 1)))
2021: STRCAT(dest, PATHSEPSTR);
2022: STRCAT(dest, fname2);
2023: }
2024: return dest;
2025: }
2026:
2027: /*
2028: * FullName_save - Make an allocated copy of a full file name.
2029: * Returns NULL when failed.
2030: */
2031: char_u *
2032: FullName_save(fname)
2033: char_u *fname;
2034: {
2035: char_u *buf;
2036: char_u *new_fname = NULL;
2037:
2038: buf = alloc((unsigned)MAXPATHL);
2039: if (buf != NULL)
2040: {
2041: if (FullName(fname, buf, MAXPATHL, FALSE) != FAIL)
2042: new_fname = strsave(buf);
2043: vim_free(buf);
2044: }
2045: return new_fname;
2046: }
2047:
2048: #ifdef CINDENT
2049:
2050: /*
2051: * Functions for C-indenting.
2052: * Most of this originally comes from Eric Fischer.
2053: */
2054: /*
2055: * Below "XXX" means that this function may unlock the current line.
2056: */
2057:
2058: static int isdefault __ARGS((char_u *));
2059: static char_u *after_label __ARGS((char_u *l));
2060: static int get_indent_nolabel __ARGS((linenr_t lnum));
2061: static int skip_label __ARGS((linenr_t, char_u **pp, int ind_maxcomment));
2062: static int ispreproc __ARGS((char_u *));
2063: static int iscomment __ARGS((char_u *));
2064: static int commentorempty __ARGS((char_u *));
2065: static int isterminated __ARGS((char_u *));
2066: static int isfuncdecl __ARGS((char_u *));
2067: static char_u *skip_string __ARGS((char_u *p));
2068: static int isif __ARGS((char_u *));
2069: static int iselse __ARGS((char_u *));
2070: static int isdo __ARGS((char_u *));
2071: static int iswhileofdo __ARGS((char_u *, linenr_t, int));
2072: static FPOS *find_start_comment __ARGS((int ind_maxcomment));
2073: static FPOS *find_start_brace __ARGS((int));
2074: static FPOS *find_match_paren __ARGS((int, int));
2075: static int find_last_paren __ARGS((char_u *l));
2076: static int find_match __ARGS((int lookfor, linenr_t ourscope,
2077: int ind_maxparen, int ind_maxcomment));
2078:
2079: /*
2080: * Recognize a label: "label:".
2081: * Note: curwin->w_cursor must be where we are looking for the label.
2082: */
2083: int
2084: islabel(ind_maxcomment) /* XXX */
2085: int ind_maxcomment;
2086: {
2087: char_u *s;
2088:
2089: s = skipwhite(ml_get_curline());
2090:
2091: /*
2092: * Exclude "default" from labels, since it should be indented
2093: * like a switch label.
2094: */
2095:
2096: if (isdefault(s))
2097: return FALSE;
2098:
2099: if (!isidchar(*s)) /* need at least one ID character */
2100: return FALSE;
2101:
2102: while (isidchar(*s))
2103: s++;
2104:
2105: s = skipwhite(s);
2106:
2107: /* "::" is not a label, it's C++ */
2108: if (*s == ':' && s[1] != ':')
2109: {
2110: /*
2111: * Only accept a label if the previous line is terminated or is a case
2112: * label.
2113: */
2114: FPOS cursor_save;
2115: FPOS *trypos;
2116: char_u *line;
2117:
2118: cursor_save = curwin->w_cursor;
2119: while (curwin->w_cursor.lnum > 1)
2120: {
2121: --curwin->w_cursor.lnum;
2122:
2123: /*
2124: * If we're in a comment now, skip to the start of the comment.
2125: */
2126: curwin->w_cursor.col = 0;
2127: if ((trypos = find_start_comment(ind_maxcomment)) != NULL) /* XXX */
2128: curwin->w_cursor = *trypos;
2129:
2130: line = ml_get_curline();
2131: if (ispreproc(line)) /* ignore #defines, #if, etc. */
2132: continue;
2133: if (commentorempty(line))
2134: continue;
2135:
2136: curwin->w_cursor = cursor_save;
2137: if (isterminated(line) || iscase(line))
2138: return TRUE;
2139: return FALSE;
2140: }
2141: curwin->w_cursor = cursor_save;
2142: return TRUE; /* label at start of file??? */
2143: }
2144: return FALSE;
2145: }
2146:
2147: /*
2148: * Recognize a switch label: "case .*:" or "default:".
2149: */
2150: int
2151: iscase(s)
2152: char_u *s;
2153: {
2154: s = skipwhite(s);
2155: if (STRNCMP(s, "case", 4) == 0 && !isidchar(s[4]))
2156: {
2157: for (s += 4; *s; ++s)
2158: if (*s == ':')
2159: {
2160: if (s[1] == ':') /* skip over "::" for C++ */
2161: ++s;
2162: else
2163: return TRUE;
2164: }
2165: return FALSE;
2166: }
2167:
2168: if (isdefault(s))
2169: return TRUE;
2170: return FALSE;
2171: }
2172:
2173: /*
2174: * Recognize a "default" switch label.
2175: */
2176: static int
2177: isdefault(s)
2178: char_u *s;
2179: {
2180: return (STRNCMP(s, "default", 7) == 0 &&
2181: *(s = skipwhite(s + 7)) == ':' &&
2182: s[1] != ':');
2183: }
2184:
2185: /*
2186: * Return a pointer to the first non-empty non-comment character after a ':'.
2187: * Return NULL if not found.
2188: * case 234: a = b;
2189: * ^
2190: */
2191: static char_u *
2192: after_label(l)
2193: char_u *l;
2194: {
2195: for ( ; *l; ++l)
2196: if (*l == ':')
2197: {
2198: if (l[1] == ':') /* skip over "::" for C++ */
2199: ++l;
2200: else
2201: break;
2202: }
2203: if (*l == NUL)
2204: return NULL;
2205: l = skipwhite(l + 1);
2206: if (commentorempty(l))
2207: return NULL;
2208: return l;
2209: }
2210:
2211: /*
2212: * Get indent of line "lnum", skipping a label.
2213: * Return 0 if there is nothing after the label.
2214: */
2215: static int
2216: get_indent_nolabel(lnum) /* XXX */
2217: linenr_t lnum;
2218: {
2219: char_u *l;
2220: FPOS fp;
2221: colnr_t col;
2222: char_u *p;
2223:
2224: l = ml_get(lnum);
2225: p = after_label(l);
2226: if (p == NULL)
2227: return 0;
2228:
2229: fp.col = p - l;
2230: fp.lnum = lnum;
2231: getvcol(curwin, &fp, &col, NULL, NULL);
2232: return (int)col;
2233: }
2234:
2235: /*
2236: * Find indent for line "lnum", ignoring any case or jump label.
2237: * Also return a pointer to the text (after the label).
2238: * label: if (asdf && asdfasdf)
2239: * ^
2240: */
2241: static int
2242: skip_label(lnum, pp, ind_maxcomment)
2243: linenr_t lnum;
2244: char_u **pp;
2245: int ind_maxcomment;
2246: {
2247: char_u *l;
2248: int amount;
2249: FPOS cursor_save;
2250:
2251: cursor_save = curwin->w_cursor;
2252: curwin->w_cursor.lnum = lnum;
2253: l = ml_get_curline();
2254: if (iscase(l) || islabel(ind_maxcomment)) /* XXX */
2255: {
2256: amount = get_indent_nolabel(lnum);
2257: l = after_label(ml_get_curline());
2258: if (l == NULL) /* just in case */
2259: l = ml_get_curline();
2260: }
2261: else
2262: {
2263: amount = get_indent();
2264: l = ml_get_curline();
2265: }
2266: *pp = l;
2267:
2268: curwin->w_cursor = cursor_save;
2269: return amount;
2270: }
2271:
2272: /*
2273: * Recognize a preprocessor statement: Any line that starts with '#'.
2274: */
2275: static int
2276: ispreproc(s)
2277: char_u *s;
2278: {
2279: s = skipwhite(s);
2280: if (*s == '#')
2281: return TRUE;
2282: return 0;
2283: }
2284:
2285: /*
2286: * Recognize the start of a C or C++ comment.
2287: */
2288: static int
2289: iscomment(p)
2290: char_u *p;
2291: {
2292: return (p[0] == '/' && (p[1] == '*' || p[1] == '/'));
2293: }
2294:
2295: /*
2296: * Recognize an empty or comment line.
2297: */
2298: static int
2299: commentorempty(s)
2300: char_u *s;
2301: {
2302: s = skipwhite(s);
2303: if (*s == NUL || iscomment(s))
2304: return TRUE;
2305: return FALSE;
2306: }
2307:
2308: /*
2309: * Recognize a line that starts with '{' or '}', or ends with ';' or '}'.
2310: * Also consider a line terminated if it ends in ','. This is not 100%
2311: * correct, but this mostly means we are in initializations and then it's OK.
2312: */
2313: static int
2314: isterminated(s)
2315: char_u *s;
2316: {
2317: s = skipwhite(s);
2318:
2319: if (*s == '{' || *s == '}')
2320: return TRUE;
2321:
2322: while (*s)
2323: {
2324: if (iscomment(s)) /* at start of comment ignore rest of line */
2325: return FALSE;
2326: s = skip_string(s);
2327: if ((*s == ';' || *s == '{' || *s == ',') && commentorempty(s + 1))
2328: return TRUE;
2329: s++;
2330: }
2331: return FALSE;
2332: }
2333:
2334: /*
2335: * Recognize the basic picture of a function declaration -- it needs to
2336: * have an open paren somewhere and a close paren at the end of the line and
2337: * no semicolons anywhere.
2338: */
2339: static int
2340: isfuncdecl(s)
2341: char_u *s;
2342: {
2343: while (*s && *s != '(' && *s != ';')
2344: if (iscomment(s++))
2345: return FALSE; /* comment before () ??? */
2346: if (*s != '(')
2347: return FALSE; /* ';' before any () or no '(' */
2348:
2349: while (*s && *s != ';')
2350: {
2351: if (*s == ')' && commentorempty(s + 1))
2352: return TRUE;
2353: if (iscomment(s++))
2354: return FALSE; /* comment between ( and ) ??? */
2355: }
2356: return FALSE;
2357: }
2358:
2359: /*
2360: * Skip over a "string" and a 'c' character.
2361: */
2362: static char_u *
2363: skip_string(p)
2364: char_u *p;
2365: {
2366: int i;
2367:
2368: /*
2369: * We loop, because strings may be concatenated: "date""time".
2370: */
2371: for ( ; ; ++p)
2372: {
2373: if (p[0] == '\'') /* 'c' or '\n' or '\000' */
2374: {
2375: if (!p[1]) /* ' at end of line */
2376: break;
2377: i = 2;
2378: if (p[1] == '\\') /* '\n' or '\000' */
2379: {
2380: ++i;
2381: while (isdigit(p[i - 1])) /* '\000' */
2382: ++i;
2383: }
2384: if (p[i] == '\'') /* check for trailing ' */
2385: {
2386: p += i;
2387: continue;
2388: }
2389: }
2390: else if (p[0] == '"') /* start of string */
2391: {
2392: for (++p; p[0]; ++p)
2393: {
2394: if (p[0] == '\\' && p[1])
2395: ++p;
2396: else if (p[0] == '"') /* end of string */
2397: break;
2398: }
2399: continue;
2400: }
2401: break; /* no string found */
2402: }
2403: if (!*p)
2404: --p; /* backup from NUL */
2405: return p;
2406: }
2407:
2408: static int
2409: isif(p)
2410: char_u *p;
2411: {
2412: return (STRNCMP(p, "if", 2) == 0 && !isidchar(p[2]));
2413: }
2414:
2415: static int
2416: iselse(p)
2417: char_u *p;
2418: {
2419: return (STRNCMP(p, "else", 4) == 0 && !isidchar(p[4]));
2420: }
2421:
2422: static int
2423: isdo(p)
2424: char_u *p;
2425: {
2426: return (STRNCMP(p, "do", 2) == 0 && !isidchar(p[2]));
2427: }
2428:
2429: /*
2430: * Check if this is a "while" that should have a matching "do".
2431: * We only accept a "while (condition) ;", with only white space between the
2432: * ')' and ';'. The condition may be spread over several lines.
2433: */
2434: static int
2435: iswhileofdo(p, lnum, ind_maxparen) /* XXX */
2436: char_u *p;
2437: linenr_t lnum;
2438: int ind_maxparen;
2439: {
2440: FPOS cursor_save;
2441: FPOS *trypos;
2442: int retval = FALSE;
2443:
2444: p = skipwhite(p);
2445: if (STRNCMP(p, "while", 5) == 0 && !isidchar(p[5]))
2446: {
2447: cursor_save = curwin->w_cursor;
2448: curwin->w_cursor.lnum = lnum;
2449: curwin->w_cursor.col = 0;
2450: if ((trypos = findmatchlimit(0, 0, ind_maxparen)) != NULL)
2451: {
2452: p = ml_get_pos(trypos) + 1;
2453: p = skipwhite(p);
2454: if (*p == ';')
2455: retval = TRUE;
2456: }
2457: curwin->w_cursor = cursor_save;
2458: }
2459: return retval;
2460: }
2461:
2462: /*
2463: * Find the start of a comment, not knowing if we are in a comment right now.
2464: * Search starts at w_cursor.lnum and goes backwards.
2465: */
2466: static FPOS *
2467: find_start_comment(ind_maxcomment) /* XXX */
2468: int ind_maxcomment;
2469: {
2470: FPOS *pos;
2471: char_u *line;
2472: char_u *p;
2473:
2474: if ((pos = findmatchlimit('*', FM_BACKWARD, ind_maxcomment)) == NULL)
2475: return NULL;
2476:
2477: /*
2478: * Check if the comment start we found is inside a string.
2479: */
2480: line = ml_get(pos->lnum);
2481: for (p = line; *p && (unsigned)(p - line) < pos->col; ++p)
2482: p = skip_string(p);
2483: if ((unsigned)(p - line) > pos->col)
2484: return NULL;
2485: return pos;
2486: }
2487:
2488: /*
2489: * Find the '{' at the start of the block we are in.
2490: * Return NULL of no match found.
2491: * Ignore a '{' that is in a comment, makes indenting the next three lines
2492: * work. */
2493: /* foo() */
2494: /* { */
2495: /* } */
2496:
2497: static FPOS *
2498: find_start_brace(ind_maxcomment) /* XXX */
2499: int ind_maxcomment;
2500: {
2501: FPOS cursor_save;
2502: FPOS *trypos;
2503: FPOS *pos;
2504: static FPOS pos_copy;
2505:
2506: cursor_save = curwin->w_cursor;
2507: while ((trypos = findmatchlimit('{', FM_BLOCKSTOP, 0)) != NULL)
2508: {
2509: pos_copy = *trypos; /* copy FPOS, next findmatch will change it */
2510: trypos = &pos_copy;
2511: curwin->w_cursor = *trypos;
2512: pos = NULL;
2513: if (!iscomment(skipwhite(ml_get(trypos->lnum))) &&
2514: (pos = find_start_comment(ind_maxcomment)) == NULL) /* XXX */
2515: break;
2516: if (pos != NULL)
2517: curwin->w_cursor.lnum = pos->lnum;
2518: }
2519: curwin->w_cursor = cursor_save;
2520: return trypos;
2521: }
2522:
2523: /*
2524: * Find the matching '(', failing if it is in a comment.
2525: * Return NULL of no match found.
2526: */
2527: static FPOS *
2528: find_match_paren(ind_maxparen, ind_maxcomment) /* XXX */
2529: int ind_maxparen;
2530: int ind_maxcomment;
2531: {
2532: FPOS cursor_save;
2533: FPOS *trypos;
2534: static FPOS pos_copy;
2535:
2536: cursor_save = curwin->w_cursor;
2537: if ((trypos = findmatchlimit('(', 0, ind_maxparen)) != NULL)
2538: {
2539: if (iscomment(skipwhite(ml_get(trypos->lnum))))
2540: trypos = NULL;
2541: else
2542: {
2543: pos_copy = *trypos; /* copy trypos, findmatch will change it */
2544: trypos = &pos_copy;
2545: curwin->w_cursor = *trypos;
2546: if (find_start_comment(ind_maxcomment) != NULL) /* XXX */
2547: trypos = NULL;
2548: }
2549: }
2550: curwin->w_cursor = cursor_save;
2551: return trypos;
2552: }
2553:
2554: /*
2555: * Set w_cursor.col to the column number of the last ')' in line "l".
2556: */
2557: static int
2558: find_last_paren(l)
2559: char_u *l;
2560: {
2561: int i;
2562: int retval = FALSE;
2563:
2564: curwin->w_cursor.col = 0; /* default is start of line */
2565:
2566: for (i = 0; l[i]; i++)
2567: {
2568: i = skip_string(l + i) - l; /* ignore parens in quotes */
2569: if (l[i] == ')')
2570: {
2571: curwin->w_cursor.col = i;
2572: retval = TRUE;
2573: }
2574: }
2575: return retval;
2576: }
2577:
2578: int
2579: get_c_indent()
2580: {
2581: /*
2582: * spaces from a block's opening brace the prevailing indent for that
2583: * block should be
2584: */
2585: int ind_level = curbuf->b_p_sw;
2586:
2587: /*
2588: * spaces from the edge of the line an open brace that's at the end of a
2589: * line is imagined to be.
2590: */
2591: int ind_open_imag = 0;
2592:
2593: /*
2594: * spaces from the prevailing indent for a line that is not precededof by
2595: * an opening brace.
2596: */
2597: int ind_no_brace = 0;
2598:
2599: /*
2600: * column where the first { of a function should be located
2601: */
2602: int ind_first_open = 0;
2603:
2604: /*
2605: * spaces from the prevailing indent a leftmost open brace should be
2606: * located
2607: */
2608: int ind_open_extra = 0;
2609:
2610: /*
2611: * spaces from the matching open brace (real location for one at the left
2612: * edge; imaginary location from one that ends a line) the matching close
2613: * brace should be located
2614: */
2615: int ind_close_extra = 0;
2616:
2617: /*
2618: * spaces from the edge of the line an open brace sitting in the leftmost
2619: * column is imagined to be
2620: */
2621: int ind_open_left_imag = 0;
2622:
2623: /*
2624: * spaces from the switch() indent a "case xx" label should be located
2625: */
2626: int ind_case = curbuf->b_p_sw;
2627:
2628: /*
2629: * spaces from the "case xx:" code after a switch() should be located
2630: */
2631: int ind_case_code = curbuf->b_p_sw;
2632:
2633: /*
2634: * amount K&R-style parameters should be indented
2635: */
2636: int ind_param = curbuf->b_p_sw;
2637:
2638: /*
2639: * amount a function type spec should be indented
2640: */
2641: int ind_func_type = curbuf->b_p_sw;
2642:
2643: /*
2644: * additional spaces beyond the prevailing indent a continuation line
2645: * should be located
2646: */
2647: int ind_continuation = curbuf->b_p_sw;
2648:
2649: /*
2650: * spaces from the indent of the line with an unclosed parentheses
2651: */
2652: int ind_unclosed = curbuf->b_p_sw * 2;
2653:
2654: /*
2655: * spaces from the comment opener when there is nothing after it.
2656: */
2657: int ind_in_comment = 3;
2658:
2659: /*
2660: * max lines to search for an open paren
2661: */
2662: int ind_maxparen = 20;
2663:
2664: /*
2665: * max lines to search for an open comment
2666: */
2667: int ind_maxcomment = 30;
2668:
2669: FPOS cur_curpos;
2670: int amount;
2671: int scope_amount;
2672: int cur_amount;
2673: colnr_t col;
2674: char_u *theline;
2675: char_u *linecopy;
2676: FPOS *trypos;
2677: FPOS our_paren_pos;
2678: char_u *start;
2679: int start_brace;
2680: #define BRACE_IN_COL0 1 /* '{' is in comumn 0 */
2681: #define BRACE_AT_START 2 /* '{' is at start of line */
2682: #define BRACE_AT_END 3 /* '{' is at end of line */
2683: linenr_t ourscope;
2684: char_u *l;
2685: char_u *look;
2686: int lookfor;
2687: #define LOOKFOR_IF 1
2688: #define LOOKFOR_DO 2
2689: #define LOOKFOR_CASE 3
2690: #define LOOKFOR_ANY 4
2691: #define LOOKFOR_TERM 5
2692: #define LOOKFOR_UNTERM 6
2693: int whilelevel;
2694: linenr_t lnum;
2695: char_u *options;
2696: int fraction = 0; /* init for GCC */
2697: int divider;
2698: int n;
2699:
2700: for (options = curbuf->b_p_cino; *options; )
2701: {
2702: l = options++;
2703: if (*options == '-')
2704: ++options;
2705: n = getdigits(&options);
2706: divider = 0;
2707: if (*options == '.') /* ".5s" means a fraction */
2708: {
2709: fraction = atol((char *)++options);
2710: while (isdigit(*options))
2711: {
2712: ++options;
2713: if (divider)
2714: divider *= 10;
2715: else
2716: divider = 10;
2717: }
2718: }
2719: if (*options == 's') /* "2s" means two times 'shiftwidth' */
2720: {
2721: if (n == 0 && fraction == 0)
2722: n = curbuf->b_p_sw; /* just "s" is one 'shiftwidth' */
2723: else
2724: {
2725: n *= curbuf->b_p_sw;
2726: if (divider)
2727: n += (curbuf->b_p_sw * fraction + divider / 2) / divider;
2728: }
2729: ++options;
2730: }
2731: if (l[1] == '-')
2732: n = -n;
2733: switch (*l)
2734: {
2735: case '>': ind_level = n; break;
2736: case 'e': ind_open_imag = n; break;
2737: case 'n': ind_no_brace = n; break;
2738: case 'f': ind_first_open = n; break;
2739: case '{': ind_open_extra = n; break;
2740: case '}': ind_close_extra = n; break;
2741: case '^': ind_open_left_imag = n; break;
2742: case ':': ind_case = n; break;
2743: case '=': ind_case_code = n; break;
2744: case 'p': ind_param = n; break;
2745: case 't': ind_func_type = n; break;
2746: case 'c': ind_in_comment = n; break;
2747: case '+': ind_continuation = n; break;
2748: case '(': ind_unclosed = n; break;
2749: case ')': ind_maxparen = n; break;
2750: case '*': ind_maxcomment = n; break;
2751: }
2752: }
2753:
2754: /* remember where the cursor was when we started */
2755:
2756: cur_curpos = curwin->w_cursor;
2757:
2758: /* get the current contents of the line.
2759: * This is required, because onle the most recent line obtained with
2760: * ml_get is valid! */
2761:
2762: linecopy = strsave(ml_get(cur_curpos.lnum));
2763: if (linecopy == NULL)
2764: return 0;
2765:
2766: /*
2767: * In insert mode and the cursor is on a ')' trunctate the line at the
2768: * cursor position. We don't want to line up with the matching '(' when
2769: * inserting new stuff.
2770: */
2771: if ((State & INSERT) && linecopy[curwin->w_cursor.col] == ')')
2772: linecopy[curwin->w_cursor.col] = NUL;
2773:
2774: theline = skipwhite(linecopy);
2775:
2776: /* move the cursor to the start of the line */
2777:
2778: curwin->w_cursor.col = 0;
2779:
2780: /*
2781: * #defines and so on always go at the left when included in 'cinkeys'.
2782: */
2783: if (*theline == '#' && (*linecopy == '#' || in_cinkeys('#', ' ', TRUE)))
2784: {
2785: amount = 0;
2786: }
2787:
2788: /*
2789: * Is it a non-case label? Then that goes at the left margin too.
2790: */
2791: else if (islabel(ind_maxcomment)) /* XXX */
2792: {
2793: amount = 0;
2794: }
2795:
2796: /*
2797: * If we're inside a comment and not looking at the start of the
2798: * comment...
2799: */
2800: else if (!iscomment(theline) &&
2801: (trypos = find_start_comment(ind_maxcomment)) != NULL) /* XXX */
2802: {
2803:
2804: /* find how indented the line beginning the comment is */
2805: getvcol(curwin, trypos, &col, NULL, NULL);
2806: amount = col;
2807:
2808: /* if our line starts with an asterisk, line up with the
2809: * asterisk in the comment opener; otherwise, line up
2810: * with the first character of the comment text.
2811: */
2812: if (theline[0] == '*')
2813: {
2814: amount += 1;
2815: }
2816: else
2817: {
2818: /*
2819: * If we are more than one line away from the comment opener, take
2820: * the indent of the previous non-empty line.
2821: * If we are just below the comment opener and there are any
2822: * white characters after it line up with the text after it.
2823: * up with them; otherwise, just use a single space.
2824: */
2825: amount = -1;
2826: for (lnum = cur_curpos.lnum - 1; lnum > trypos->lnum; --lnum)
2827: {
2828: if (linewhite(lnum)) /* skip blank lines */
2829: continue;
2830: amount = get_indent_lnum(lnum); /* XXX */
2831: break;
2832: }
2833: if (amount == -1) /* use the comment opener */
2834: {
2835: start = ml_get(trypos->lnum);
2836: look = start + trypos->col + 2; /* skip / and * */
2837: if (*look) /* if something after it */
2838: trypos->col = skipwhite(look) - start;
2839: getvcol(curwin, trypos, &col, NULL, NULL);
2840: amount = col;
2841: if (!*look)
2842: amount += ind_in_comment;
2843: }
2844: }
2845: }
2846:
2847: /*
2848: * Are we inside parentheses?
2849: */ /* XXX */
2850: else if ((trypos = find_match_paren(ind_maxparen, ind_maxcomment)) != NULL)
2851: {
2852: /*
2853: * If the matching paren is more than one line away, use the indent of
2854: * a previous non-empty line that matches the same paren.
2855: */
2856: amount = -1;
2857: our_paren_pos = *trypos;
2858: if (theline[0] != ')')
2859: {
2860: for (lnum = cur_curpos.lnum - 1; lnum > our_paren_pos.lnum; --lnum)
2861: {
2862: l = skipwhite(ml_get(lnum));
2863: if (commentorempty(l)) /* skip comment lines */
2864: continue;
2865: if (ispreproc(l)) /* ignore #defines, #if, etc. */
2866: continue;
2867: curwin->w_cursor.lnum = lnum;
2868: /* XXX */
2869: if ((trypos = find_match_paren(ind_maxparen,
2870: ind_maxcomment)) != NULL &&
2871: trypos->lnum == our_paren_pos.lnum &&
2872: trypos->col == our_paren_pos.col)
2873: {
2874: amount = get_indent_lnum(lnum); /* XXX */
2875: break;
2876: }
2877: }
2878: }
2879:
2880: /*
2881: * Line up with line where the matching paren is.
2882: * If the line starts with a '(' or the indent for unclosed
2883: * parentheses is zero, line up with the unclosed parentheses.
2884: */
2885: if (amount == -1)
2886: {
2887: amount = skip_label(our_paren_pos.lnum, &look, ind_maxcomment);
2888: if (theline[0] == ')' || ind_unclosed == 0 ||
2889: *skipwhite(look) == '(')
2890: {
2891:
2892: /*
2893: * If we're looking at a close paren, line up right there;
2894: * otherwise, line up with the next non-white character.
2895: */
2896: if (theline[0] != ')')
2897: {
2898: col = our_paren_pos.col + 1;
2899: look = ml_get(our_paren_pos.lnum);
2900: while (vim_iswhite(look[col]))
2901: col++;
2902: if (look[col] != NUL) /* In case of trailing space */
2903: our_paren_pos.col = col;
2904: else
2905: our_paren_pos.col++;
2906: }
2907:
2908: /*
2909: * Find how indented the paren is, or the character after it if
2910: * we did the above "if".
2911: */
2912: getvcol(curwin, &our_paren_pos, &col, NULL, NULL);
2913: amount = col;
2914: }
2915: else
2916: amount += ind_unclosed;
2917: }
2918: }
2919:
2920: /*
2921: * Are we at least inside braces, then?
2922: */
2923: else if ((trypos = find_start_brace(ind_maxcomment)) != NULL) /* XXX */
2924: {
2925: ourscope = trypos->lnum;
2926: start = ml_get(ourscope);
2927:
2928: /*
2929: * Now figure out how indented the line is in general.
2930: * If the brace was at the start of the line, we use that;
2931: * otherwise, check out the indentation of the line as
2932: * a whole and then add the "imaginary indent" to that.
2933: */
2934: look = skipwhite(start);
2935: if (*look == '{')
2936: {
2937: getvcol(curwin, trypos, &col, NULL, NULL);
2938: amount = col;
2939: if (*start == '{')
2940: start_brace = BRACE_IN_COL0;
2941: else
2942: start_brace = BRACE_AT_START;
2943: }
2944: else
2945: {
2946: /*
2947: * that opening brace might have been on a continuation
2948: * line. if so, find the start of the line.
2949: */
2950: curwin->w_cursor.lnum = ourscope;
2951:
2952: /*
2953: * position the cursor over the rightmost paren, so that
2954: * matching it will take us back to the start of the line.
2955: */
2956: lnum = ourscope;
2957: if (find_last_paren(start) &&
2958: (trypos = find_match_paren(ind_maxparen,
2959: ind_maxcomment)) != NULL)
2960: lnum = trypos->lnum;
2961:
2962: /*
2963: * It could have been something like
2964: * case 1: if (asdf &&
2965: * ldfd) {
2966: * }
2967: */
2968: amount = skip_label(lnum, &l, ind_maxcomment);
2969:
2970: start_brace = BRACE_AT_END;
2971: }
2972:
2973: /*
2974: * if we're looking at a closing brace, that's where
2975: * we want to be. otherwise, add the amount of room
2976: * that an indent is supposed to be.
2977: */
2978: if (theline[0] == '}')
2979: {
2980: /*
2981: * they may want closing braces to line up with something
2982: * other than the open brace. indulge them, if so.
2983: */
2984: amount += ind_close_extra;
2985: }
2986: else
2987: {
2988: /*
2989: * If we're looking at an "else", try to find an "if"
2990: * to match it with.
2991: * If we're looking at a "while", try to find a "do"
2992: * to match it with.
2993: */
2994: lookfor = 0;
2995: if (iselse(theline))
2996: lookfor = LOOKFOR_IF;
2997: else if (iswhileofdo(theline, cur_curpos.lnum, ind_maxparen))
2998: /* XXX */
2999: lookfor = LOOKFOR_DO;
3000: if (lookfor)
3001: {
3002: curwin->w_cursor.lnum = cur_curpos.lnum;
3003: if (find_match(lookfor, ourscope, ind_maxparen,
3004: ind_maxcomment) == OK)
3005: {
3006: amount = get_indent(); /* XXX */
3007: goto theend;
3008: }
3009: }
3010:
3011: /*
3012: * We get here if we are not on an "while-of-do" or "else" (or
3013: * failed to find a matching "if").
3014: * Search backwards for something to line up with.
3015: * First set amount for when we don't find anything.
3016: */
3017:
3018: /*
3019: * if the '{' is _really_ at the left margin, use the imaginary
3020: * location of a left-margin brace. Otherwise, correct the
3021: * location for ind_open_extra.
3022: */
3023:
3024: if (start_brace == BRACE_IN_COL0) /* '{' is in column 0 */
3025: {
3026: amount = ind_open_left_imag;
3027: }
3028: else
3029: {
3030: if (start_brace == BRACE_AT_END) /* '{' is at end of line */
3031: amount += ind_open_imag;
3032: else
3033: {
3034: amount -= ind_open_extra;
3035: if (amount < 0)
3036: amount = 0;
3037: }
3038: }
3039:
3040: if (iscase(theline)) /* it's a switch() label */
3041: {
3042: lookfor = LOOKFOR_CASE; /* find a previous switch() label */
3043: amount += ind_case;
3044: }
3045: else
3046: {
3047: lookfor = LOOKFOR_ANY;
3048: amount += ind_level; /* ind_level from start of block */
3049: }
3050: scope_amount = amount;
3051: whilelevel = 0;
3052:
3053: /*
3054: * Search backwards. If we find something we recognize, line up
3055: * with that.
3056: *
3057: * if we're looking at an open brace, indent
3058: * the usual amount relative to the conditional
3059: * that opens the block.
3060: */
3061: curwin->w_cursor = cur_curpos;
3062: for (;;)
3063: {
3064: curwin->w_cursor.lnum--;
3065: curwin->w_cursor.col = 0;
3066:
3067: /*
3068: * If we went all the way back to the start of our scope, line
3069: * up with it.
3070: */
3071: if (curwin->w_cursor.lnum <= ourscope)
3072: {
3073: if (lookfor == LOOKFOR_UNTERM)
3074: amount += ind_continuation;
3075: else if (lookfor != LOOKFOR_TERM)
3076: amount = scope_amount;
3077: break;
3078: }
3079:
3080: /*
3081: * If we're in a comment now, skip to the start of the comment.
3082: */ /* XXX */
3083: if ((trypos = find_start_comment(ind_maxcomment)) != NULL)
3084: {
3085: curwin->w_cursor.lnum = trypos->lnum + 1;
3086: continue;
3087: }
3088:
3089: l = ml_get_curline();
3090:
3091: /*
3092: * If this is a switch() label, may line up relative to that.
3093: */
3094: if (iscase(l))
3095: {
3096: /*
3097: * case xx:
3098: * c = 99 + <- this indent plus continuation
3099: *-> here;
3100: */
3101: if (lookfor == LOOKFOR_UNTERM)
3102: {
3103: amount += ind_continuation;
3104: break;
3105: }
3106:
3107: /*
3108: * case xx: <- line up with this case
3109: * x = 333;
3110: * case yy:
3111: */
3112: if (lookfor == LOOKFOR_CASE)
3113: {
3114: /*
3115: * Check that this case label is not for another
3116: * switch()
3117: */ /* XXX */
3118: if ((trypos = find_start_brace(ind_maxcomment)) ==
3119: NULL || trypos->lnum == ourscope)
3120: {
3121: amount = get_indent(); /* XXX */
3122: break;
3123: }
3124: continue;
3125: }
3126:
3127: n = get_indent_nolabel(curwin->w_cursor.lnum); /* XXX */
3128:
3129: /*
3130: * case xx: if (cond) <- line up with this if
3131: * y = y + 1;
3132: * -> s = 99;
3133: *
3134: * case xx:
3135: * if (cond) <- line up with this line
3136: * y = y + 1;
3137: * -> s = 99;
3138: */
3139: if (lookfor == LOOKFOR_TERM)
3140: {
3141: if (n)
3142: amount = n;
3143: break;
3144: }
3145:
3146: /*
3147: * case xx: x = x + 1; <- line up with this x
3148: * -> y = y + 1;
3149: *
3150: * case xx: if (cond) <- line up with this if
3151: * -> y = y + 1;
3152: */
3153: if (n)
3154: {
3155: amount = n;
3156: l = after_label(ml_get_curline());
3157: if (l != NULL && is_cinword(l))
3158: amount += ind_level + ind_no_brace;
3159: break;
3160: }
3161:
3162: /*
3163: * Try to get the indent of a statement before the
3164: * switch label. If nothing is found, line up relative
3165: * to the switch label.
3166: * break; <- may line up with this line
3167: * case xx:
3168: * -> y = 1;
3169: */
3170: scope_amount = get_indent() + ind_case_code; /* XXX */
3171: lookfor = LOOKFOR_ANY;
3172: continue;
3173: }
3174:
3175: /*
3176: * Looking for a switch() label, ignore other lines.
3177: */
3178: if (lookfor == LOOKFOR_CASE)
3179: continue;
3180:
3181: /*
3182: * Ignore jump labels with nothing after them.
3183: */
3184: if (islabel(ind_maxcomment))
3185: {
3186: l = after_label(ml_get_curline());
3187: if (l == NULL || commentorempty(l))
3188: continue;
3189: }
3190:
3191: /*
3192: * Ignore #defines, #if, etc.
3193: * Ignore comment and empty lines.
3194: * (need to get the line again, islabel() may have unlocked it)
3195: */
3196: l = ml_get_curline();
3197: if (ispreproc(l) || commentorempty(l))
3198: continue;
3199:
3200: /*
3201: * What happens next depends on the line being terminated.
3202: */
3203: if (!isterminated(l))
3204: {
3205: /*
3206: * if we're in the middle of a paren thing,
3207: * go back to the line that starts it so
3208: * we can get the right prevailing indent
3209: * if ( foo &&
3210: * bar )
3211: */
3212: /*
3213: * position the cursor over the rightmost paren, so that
3214: * matching it will take us back to the start of the line.
3215: */
3216: (void)find_last_paren(l);
3217: if ((trypos = find_match_paren(ind_maxparen,
3218: ind_maxcomment)) != NULL)
3219: {
3220: /*
3221: * Check if we are on a case label now. This is
3222: * handled above.
3223: * case xx: if ( asdf &&
3224: * asdf)
3225: */
3226: curwin->w_cursor.lnum = trypos->lnum;
3227: l = ml_get_curline();
3228: if (iscase(l))
3229: {
3230: ++curwin->w_cursor.lnum;
3231: continue;
3232: }
3233: }
3234:
3235: /*
3236: * Get indent and pointer to text for current line,
3237: * ignoring any jump label. XXX
3238: */
3239: cur_amount = skip_label(curwin->w_cursor.lnum,
3240: &l, ind_maxcomment);
3241:
3242: /*
3243: * If this is just above the line we are indenting, and it
3244: * starts with a '{', line it up with this line.
3245: * while (not)
3246: * -> {
3247: * }
3248: */
3249: if (lookfor != LOOKFOR_TERM && theline[0] == '{')
3250: {
3251: amount = cur_amount + ind_open_extra;
3252: break;
3253: }
3254:
3255: /*
3256: * Check if we are after an "if", "while", etc.
3257: */
3258: if (is_cinword(l))
3259: {
3260: /*
3261: * Found an unterminated line after an if (), line up
3262: * with the last one.
3263: * if (cond)
3264: * 100 +
3265: * -> here;
3266: */
3267: if (lookfor == LOOKFOR_UNTERM)
3268: {
3269: amount += ind_continuation;
3270: break;
3271: }
3272:
3273: /*
3274: * If this is just above the line we are indenting, we
3275: * are finished.
3276: * while (not)
3277: * -> here;
3278: * Otherwise this indent can be used when the line
3279: * before this is terminated.
3280: * yyy;
3281: * if (stat)
3282: * while (not)
3283: * xxx;
3284: * -> here;
3285: */
3286: amount = cur_amount;
3287: if (lookfor != LOOKFOR_TERM)
3288: {
3289: amount += ind_level + ind_no_brace;
3290: break;
3291: }
3292:
3293: /*
3294: * Special trick: when expecting the while () after a
3295: * do, line up with the while()
3296: * do
3297: * x = 1;
3298: * -> here
3299: */
3300: l = skipwhite(ml_get_curline());
3301: if (isdo(l))
3302: {
3303: if (whilelevel == 0)
3304: break;
3305: --whilelevel;
3306: }
3307:
3308: /*
3309: * When searching for a terminated line, don't use the
3310: * one between the "if" and the "else".
3311: */
3312: if (iselse(l))
3313: {
3314: if (find_match(LOOKFOR_IF, ourscope,
3315: ind_maxparen, ind_maxcomment) == FAIL)
3316: break;
3317: }
3318: }
3319:
3320: /*
3321: * If we're below an unterminated line that is not an
3322: * "if" or something, we may line up with this line or
3323: * add someting for a continuation line, depending on
3324: * the line before this one.
3325: */
3326: else
3327: {
3328: /*
3329: * Found two unterminated lines on a row, line up with
3330: * the last one.
3331: * c = 99 +
3332: * 100 +
3333: * -> here;
3334: */
3335: if (lookfor == LOOKFOR_UNTERM)
3336: break;
3337:
3338: /*
3339: * Found first unterminated line on a row, may line up
3340: * with this line, remember its indent
3341: * 100 +
3342: * -> here;
3343: */
3344: amount = cur_amount;
3345: if (lookfor != LOOKFOR_TERM)
3346: lookfor = LOOKFOR_UNTERM;
3347: }
3348: }
3349:
3350: /*
3351: * Check if we are after a while (cond);
3352: * If so: Ignore the matching "do".
3353: */
3354: /* XXX */
3355: else if (iswhileofdo(l, curwin->w_cursor.lnum, ind_maxparen))
3356: {
3357: /*
3358: * Found an unterminated line after a while ();, line up
3359: * with the last one.
3360: * while (cond);
3361: * 100 + <- line up with this one
3362: * -> here;
3363: */
3364: if (lookfor == LOOKFOR_UNTERM)
3365: {
3366: amount += ind_continuation;
3367: break;
3368: }
3369:
3370: if (whilelevel == 0)
3371: {
3372: lookfor = LOOKFOR_TERM;
3373: amount = get_indent(); /* XXX */
3374: if (theline[0] == '{')
3375: amount += ind_open_extra;
3376: }
3377: ++whilelevel;
3378: }
3379:
3380: /*
3381: * We are after a "normal" statement.
3382: * If we had another statement we can stop now and use the
3383: * indent of that other statement.
3384: * Otherwise the indent of the current statement may be used,
3385: * search backwards for the next "normal" statement.
3386: */
3387: else
3388: {
3389: /*
3390: * Found a terminated line above an unterminated line. Add
3391: * the amount for a continuation line.
3392: * x = 1;
3393: * y = foo +
3394: * -> here;
3395: */
3396: if (lookfor == LOOKFOR_UNTERM)
3397: {
3398: amount += ind_continuation;
3399: break;
3400: }
3401:
3402: /*
3403: * Found a terminated line above a terminated line or "if"
3404: * etc. line. Use the amount of the line below us.
3405: * x = 1; x = 1;
3406: * if (asdf) y = 2;
3407: * while (asdf) ->here;
3408: * here;
3409: * ->foo;
3410: */
3411: if (lookfor == LOOKFOR_TERM)
3412: {
3413: if (whilelevel == 0)
3414: break;
3415: }
3416:
3417: /*
3418: * First line above the one we're indenting is terminated.
3419: * To know what needs to be done look further backward for
3420: * a terminated line.
3421: */
3422: else
3423: {
3424: /*
3425: * position the cursor over the rightmost paren, so
3426: * that matching it will take us back to the start of
3427: * the line. Helps for:
3428: * func(asdr,
3429: * asdfasdf);
3430: * here;
3431: */
3432: l = ml_get_curline();
3433: if (find_last_paren(l) &&
3434: (trypos = find_match_paren(ind_maxparen,
3435: ind_maxcomment)) != NULL)
3436: {
3437: /*
3438: * Check if we are on a case label now. This is
3439: * handled above.
3440: * case xx: if ( asdf &&
3441: * asdf)
3442: */
3443: curwin->w_cursor.lnum = trypos->lnum;
3444: l = ml_get_curline();
3445: if (iscase(l))
3446: {
3447: ++curwin->w_cursor.lnum;
3448: continue;
3449: }
3450: }
3451:
3452: /*
3453: * Get indent and pointer to text for current line,
3454: * ignoring any jump label.
3455: */
3456: amount = skip_label(curwin->w_cursor.lnum,
3457: &l, ind_maxcomment);
3458:
3459: if (theline[0] == '{')
3460: amount += ind_open_extra;
3461: lookfor = LOOKFOR_TERM;
3462:
3463: /*
3464: * If we're at the end of a block, skip to the start of
3465: * that block.
3466: */
3467: if (*skipwhite(l) == '}' &&
3468: (trypos = find_start_brace(ind_maxcomment))
3469: != NULL) /* XXX */
3470: curwin->w_cursor.lnum = trypos->lnum;
3471: }
3472: }
3473: }
3474: }
3475: }
3476:
3477: /*
3478: * ok -- we're not inside any sort of structure at all!
3479: *
3480: * this means we're at the top level, and everything should
3481: * basically just match where the previous line is, except
3482: * for the lines immediately following a function declaration,
3483: * which are K&R-style parameters and need to be indented.
3484: */
3485: else
3486: {
3487: /*
3488: * if our line starts with an open brace, forget about any
3489: * prevailing indent and make sure it looks like the start
3490: * of a function
3491: */
3492:
3493: if (theline[0] == '{')
3494: {
3495: amount = ind_first_open;
3496: }
3497:
3498: /*
3499: * If the NEXT line is a function declaration, the current
3500: * line needs to be indented as a function type spec.
3501: * Don't do this if the current line looks like a comment.
3502: */
3503: else if (cur_curpos.lnum < curbuf->b_ml.ml_line_count &&
3504: !commentorempty(theline) &&
3505: isfuncdecl(ml_get(cur_curpos.lnum + 1)))
3506: {
3507: amount = ind_func_type;
3508: }
3509: else
3510: {
3511: amount = 0;
3512: curwin->w_cursor = cur_curpos;
3513:
3514: /* search backwards until we find something we recognize */
3515:
3516: while (curwin->w_cursor.lnum > 1)
3517: {
3518: curwin->w_cursor.lnum--;
3519: curwin->w_cursor.col = 0;
3520:
3521: l = ml_get_curline();
3522:
3523: /*
3524: * If we're in a comment now, skip to the start of the comment.
3525: */ /* XXX */
3526: if ((trypos = find_start_comment(ind_maxcomment)) != NULL)
3527: {
3528: curwin->w_cursor.lnum = trypos->lnum + 1;
3529: continue;
3530: }
3531:
3532: /*
3533: * If the line looks like a function declaration, and we're
3534: * not in a comment, put it the left margin.
3535: */
3536: if (isfuncdecl(theline))
3537: break;
3538:
3539: /*
3540: * Skip preprocessor directives and blank lines.
3541: */
3542: if (ispreproc(l))
3543: continue;
3544:
3545: if (commentorempty(l))
3546: continue;
3547:
3548: /*
3549: * If the PREVIOUS line is a function declaration, the current
3550: * line (and the ones that follow) needs to be indented as
3551: * parameters.
3552: */
3553: if (isfuncdecl(l))
3554: {
3555: amount = ind_param;
3556: break;
3557: }
3558:
3559: /*
3560: * Doesn't look like anything interesting -- so just
3561: * use the indent of this line.
3562: *
3563: * Position the cursor over the rightmost paren, so that
3564: * matching it will take us back to the start of the line.
3565: */
3566: find_last_paren(l);
3567:
3568: if ((trypos = find_match_paren(ind_maxparen,
3569: ind_maxcomment)) != NULL)
3570: curwin->w_cursor.lnum = trypos->lnum;
3571: amount = get_indent(); /* XXX */
3572: break;
3573: }
3574: }
3575: }
3576:
3577: theend:
3578: /* put the cursor back where it belongs */
3579: curwin->w_cursor = cur_curpos;
3580:
3581: vim_free(linecopy);
3582:
3583: if (amount < 0)
3584: return 0;
3585: return amount;
3586: }
3587:
3588: static int
3589: find_match(lookfor, ourscope, ind_maxparen, ind_maxcomment)
3590: int lookfor;
3591: linenr_t ourscope;
3592: int ind_maxparen;
3593: int ind_maxcomment;
3594: {
3595: char_u *look;
3596: FPOS *theirscope;
3597: char_u *mightbeif;
3598: int elselevel;
3599: int whilelevel;
3600:
3601: if (lookfor == LOOKFOR_IF)
3602: {
3603: elselevel = 1;
3604: whilelevel = 0;
3605: }
3606: else
3607: {
3608: elselevel = 0;
3609: whilelevel = 1;
3610: }
3611:
3612: curwin->w_cursor.col = 0;
3613:
3614: while (curwin->w_cursor.lnum > ourscope + 1)
3615: {
3616: curwin->w_cursor.lnum--;
3617: curwin->w_cursor.col = 0;
3618:
3619: look = skipwhite(ml_get_curline());
3620: if (iselse(look) || isif(look) || isdo(look) ||
3621: iswhileofdo(look, curwin->w_cursor.lnum, ind_maxparen)) /* XXX */
3622: {
3623: /*
3624: * if we've gone outside the braces entirely,
3625: * we must be out of scope...
3626: */
3627: theirscope = find_start_brace(ind_maxcomment); /* XXX */
3628: if (theirscope == NULL)
3629: break;
3630:
3631: /*
3632: * and if the brace enclosing this is further
3633: * back than the one enclosing the else, we're
3634: * out of luck too.
3635: */
3636: if (theirscope->lnum < ourscope)
3637: break;
3638:
3639: /*
3640: * and if they're enclosed in a *deeper* brace,
3641: * then we can ignore it because it's in a
3642: * different scope...
3643: */
3644: if (theirscope->lnum > ourscope)
3645: continue;
3646:
3647: /*
3648: * if it was an "else" (that's not an "else if")
3649: * then we need to go back to another if, so
3650: * increment elselevel
3651: */
3652: look = skipwhite(ml_get_curline());
3653: if (iselse(look))
3654: {
3655: mightbeif = skipwhite(look + 4);
3656: if (!isif(mightbeif))
3657: ++elselevel;
3658: continue;
3659: }
3660:
3661: /*
3662: * if it was a "while" then we need to go back to
3663: * another "do", so increment whilelevel.
3664: */
3665: if (iswhileofdo(look, curwin->w_cursor.lnum, ind_maxparen))/* XXX */
3666: {
3667: ++whilelevel;
3668: continue;
3669: }
3670:
3671: /* If it's an "if" decrement elselevel */
3672: look = skipwhite(ml_get_curline());
3673: if (isif(look))
3674: {
3675: elselevel--;
3676: /*
3677: * When looking for an "if" ignore "while"s that
3678: * get in the way.
3679: */
3680: if (elselevel == 0 && lookfor == LOOKFOR_IF)
3681: whilelevel = 0;
3682: }
3683:
3684: /* If it's a "do" decrement whilelevel */
3685: if (isdo(look))
3686: whilelevel--;
3687:
3688: /*
3689: * if we've used up all the elses, then
3690: * this must be the if that we want!
3691: * match the indent level of that if.
3692: */
3693: if (elselevel <= 0 && whilelevel <= 0)
3694: {
3695: return OK;
3696: }
3697: }
3698: }
3699: return FAIL;
3700: }
3701:
3702: #endif /* CINDENT */
3703:
3704: #ifdef LISPINDENT
3705: int
3706: get_lisp_indent()
3707: {
3708: FPOS *pos, realpos;
3709: long amount = 0;
3710: char_u *that;
3711: colnr_t col;
3712: colnr_t maybe;
3713: colnr_t firsttry;
3714:
3715:
3716: realpos = curwin->w_cursor;
3717: curwin->w_cursor.col = 0;
3718:
3719: if ((pos = findmatch('(')) != NULL)
3720: {
3721: curwin->w_cursor.lnum = pos->lnum;
3722: curwin->w_cursor.col = pos->col;
3723: col = pos->col;
3724:
3725: that = ml_get_curline();
3726: maybe = get_indent(); /* XXX */
3727:
3728: if (maybe == 0)
3729: amount = 2;
3730: else
3731: {
3732: while (*that && col)
3733: {
3734: amount += lbr_chartabsize(that, (colnr_t)amount);
3735: col--;
3736: that++;
3737: }
3738:
3739: that++;
3740: amount++;
3741: firsttry = amount;
3742:
3743: /*
3744: * Go to the start of the second word.
3745: * If there is no second word, go back to firsttry.
3746: * Also stop at a '('.
3747: */
3748:
3749: while (vim_iswhite(*that))
3750: {
3751: amount += lbr_chartabsize(that, (colnr_t)amount);
3752: that++;
3753: }
3754: while (*that && !vim_iswhite(*that) && *that != '(')
3755: {
3756: amount += lbr_chartabsize(that, (colnr_t)amount);
3757: that++;
3758: }
3759: while (vim_iswhite(*that))
3760: {
3761: amount += lbr_chartabsize(that, (colnr_t)amount);
3762: that++;
3763: }
3764: if (! *that)
3765: amount = firsttry;
3766: }
3767: }
3768: else /* no matching '(' found, use indent of previous non-empty line */
3769: {
3770: while (curwin->w_cursor.lnum > 1)
3771: {
3772: --curwin->w_cursor.lnum;
3773: if (!linewhite(curwin->w_cursor.lnum))
3774: break;
3775: }
3776: amount = get_indent(); /* XXX */
3777: }
3778:
3779: curwin->w_cursor = realpos;
3780:
3781: if (amount < 0)
3782: amount = 0;
3783: return (int)amount;
3784: }
3785: #endif /* LISPINDENT */
3786:
3787: #if defined(UNIX) || defined(WIN32) || defined(__EMX__)
3788: /*
3789: * Preserve files and exit.
3790: * When called IObuff must contain a message.
3791: */
3792: void
3793: preserve_exit()
3794: {
3795: BUF *buf;
3796:
3797: #ifdef USE_GUI
3798: if (gui.in_use)
3799: {
3800: gui.dying = TRUE;
3801: trash_output_buf(); /* trash any pending output */
3802: }
3803: else
3804: #endif
3805: {
3806: windgoto((int)Rows - 1, 0);
3807:
3808: /*
3809: * Switch terminal mode back now, so these messages end up on the
3810: * "normal" screen (if there are two screens).
3811: */
3812: settmode(0);
3813: #ifdef WIN32
3814: if (can_end_termcap_mode(FALSE) == TRUE)
3815: #endif
3816: stoptermcap();
3817: flushbuf();
3818: }
3819:
3820: outstr(IObuff);
3821: screen_start(); /* don't know where cursor is now */
3822: flushbuf();
3823:
3824: ml_close_notmod(); /* close all not-modified buffers */
3825:
3826: for (buf = firstbuf; buf != NULL; buf = buf->b_next)
3827: {
3828: if (buf->b_ml.ml_mfp != NULL && buf->b_ml.ml_mfp->mf_fname != NULL)
3829: {
3830: OUTSTR("Vim: preserving files...\n");
3831: screen_start(); /* don't know where cursor is now */
3832: flushbuf();
3833: ml_sync_all(FALSE, FALSE); /* preserve all swap files */
3834: break;
3835: }
3836: }
3837:
3838: ml_close_all(FALSE); /* close all memfiles, without deleting */
3839:
3840: OUTSTR("Vim: Finished.\n");
3841:
3842: getout(1);
3843: }
3844: #endif /* defined(UNIX) || defined(WIN32) || defined(__EMX__) */
3845:
3846: /*
3847: * return TRUE if "fname" exists.
3848: */
3849: int
3850: vim_fexists(fname)
3851: char_u *fname;
3852: {
3853: struct stat st;
3854:
3855: if (stat((char *)fname, &st))
3856: return FALSE;
3857: return TRUE;
3858: }
3859:
3860: /*
3861: * Check for CTRL-C pressed, but only once in a while.
3862: * Should be used instead of mch_breakcheck() for functions that check for
3863: * each line in the file. Calling mch_breakcheck() each time takes too much
3864: * time, because it can be a system call.
3865: */
3866:
3867: #ifndef BREAKCHECK_SKIP
3868: # define BREAKCHECK_SKIP 32
3869: #endif
3870:
3871: void
3872: line_breakcheck()
3873: {
3874: static int count = 0;
3875:
3876: if (++count == BREAKCHECK_SKIP)
3877: {
3878: count = 0;
3879: mch_breakcheck();
3880: }
3881: }
3882:
3883: /*
3884: * Free the list of files returned by ExpandWildCards() or other expansion
3885: * functions.
3886: */
3887: void
3888: FreeWild(num, file)
3889: int num;
3890: char_u **file;
3891: {
3892: if (file == NULL || num == 0)
3893: return;
3894: #if defined(__EMX__) && defined(__ALWAYS_HAS_TRAILING_NULL_POINTER) /* XXX */
3895: /*
3896: * Is this still OK for when other functions thatn ExpandWildCards() have
3897: * been used???
3898: */
3899: _fnexplodefree((char **)file);
3900: #else
3901: while (num--)
3902: vim_free(file[num]);
3903: vim_free(file);
3904: #endif
3905: }
3906: