Annotation of src/usr.bin/vim/misccmds.c, Revision 1.3
1.3 ! downsj 1: /* $OpenBSD: misccmds.c,v 1.2 1996/09/21 06:23:10 downsj Exp $ */
1.1 downsj 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: {
1.2 downsj 1393: change_warning(0);
1.1 downsj 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
1.2 downsj 1444: change_warning(col)
1445: int col; /* column for message; non-zero when in insert
1446: mode and 'showmode' is on */
1.1 downsj 1447: {
1448: if (curbuf->b_did_warn == FALSE && curbuf->b_changed == 0 &&
1449: #ifdef AUTOCMD
1450: !autocmd_busy &&
1451: #endif
1452: curbuf->b_p_ro)
1453: {
1.2 downsj 1454: /*
1455: * Do what msg() does, but with a column offset.
1456: */
1457: msg_start();
1458: msg_col = col;
1459: MSG_OUTSTR("Warning: Changing a readonly file");
1460: msg_clr_eos();
1461: (void)msg_end();
1.3 ! downsj 1462: flushbuf();
1.2 downsj 1463: mch_delay(1000L, TRUE); /* give him some time to think about it */
1.1 downsj 1464: curbuf->b_did_warn = TRUE;
1465: }
1466: }
1467:
1468: /*
1469: * Ask for a reply from the user, a 'y' or a 'n'.
1470: * No other characters are accepted, the message is repeated until a valid
1471: * reply is entered or CTRL-C is hit.
1472: * If direct is TRUE, don't use vgetc but mch_inchar, don't get characters from
1473: * any buffers but directly from the user.
1474: *
1475: * return the 'y' or 'n'
1476: */
1477: int
1478: ask_yesno(str, direct)
1479: char_u *str;
1480: int direct;
1481: {
1482: int r = ' ';
1483: char_u buf[20];
1484: int len = 0;
1485: int idx = 0;
1486:
1487: if (exiting) /* put terminal in raw mode for this question */
1488: settmode(1);
1489: while (r != 'y' && r != 'n')
1490: {
1491: (void)set_highlight('r'); /* same highlighting as for wait_return */
1492: msg_highlight = TRUE;
1493: smsg((char_u *)"%s (y/n)?", str);
1494: if (direct)
1495: {
1496: flushbuf();
1497: if (idx >= len)
1498: {
1499: len = mch_inchar(buf, 20, -1L);
1500: idx = 0;
1501: }
1502: r = buf[idx++];
1503: }
1504: else
1505: r = vgetc();
1506: if (r == Ctrl('C') || r == ESC)
1507: r = 'n';
1508: msg_outchar(r); /* show what you typed */
1509: flushbuf();
1510: }
1511: return r;
1512: }
1513:
1514: /*
1515: * get a number from the user
1516: */
1517: int
1518: get_number()
1519: {
1520: int n = 0;
1521: int c;
1522:
1523: for (;;)
1524: {
1525: windgoto(msg_row, msg_col);
1526: c = vgetc();
1527: if (isdigit(c))
1528: {
1529: n = n * 10 + c - '0';
1530: msg_outchar(c);
1531: }
1532: else if (c == K_DEL || c == K_BS || c == Ctrl('H'))
1533: {
1534: n /= 10;
1535: MSG_OUTSTR("\b \b");
1536: }
1537: else if (c == CR || c == NL || c == Ctrl('C'))
1538: break;
1539: }
1540: return n;
1541: }
1542:
1543: void
1544: msgmore(n)
1545: long n;
1546: {
1547: long pn;
1548:
1549: if (global_busy || /* no messages now, wait until global is finished */
1550: keep_msg) /* there is a message already, skip this one */
1551: return;
1552:
1553: if (n > 0)
1554: pn = n;
1555: else
1556: pn = -n;
1557:
1558: if (pn > p_report)
1559: {
1560: sprintf((char *)msg_buf, "%ld %s line%s %s",
1561: pn, n > 0 ? "more" : "fewer", plural(pn),
1562: got_int ? "(Interrupted)" : "");
1563: if (msg(msg_buf))
1564: keep_msg = msg_buf;
1565: }
1566: }
1567:
1568: /*
1569: * flush map and typeahead buffers and give a warning for an error
1570: */
1571: void
1572: beep_flush()
1573: {
1574: flush_buffers(FALSE);
1575: vim_beep();
1576: }
1577:
1578: /*
1579: * give a warning for an error
1580: */
1581: void
1582: vim_beep()
1583: {
1584: if (p_vb)
1585: {
1586: #ifdef DJGPP
1587: ScreenVisualBell();
1588: #else
1589: outstr(T_VB);
1590: #endif
1591: }
1592: else
1593: {
1594: #if defined MSDOS || defined WIN32 /* ? gvr */
1595: /*
1596: * The number of beeps outputted is reduced to avoid having to wait
1597: * for all the beeps to finish. This is only a problem on systems
1598: * where the beeps don't overlap.
1599: */
1600: if (beep_count == 0 || beep_count == 10)
1601: {
1602: outchar('\007');
1603: beep_count = 1;
1604: }
1605: else
1606: ++beep_count;
1607: #else
1608: outchar('\007');
1609: #endif
1610: }
1611: }
1612:
1613: /*
1614: * To get the "real" home directory:
1615: * - get value of $HOME
1616: * For Unix:
1617: * - go to that directory
1618: * - do mch_dirname() to get the real name of that directory.
1619: * This also works with mounts and links.
1620: * Don't do this for MS-DOS, it will change the "current dir" for a drive.
1621: */
1622: static char_u *homedir = NULL;
1623:
1624: void
1625: init_homedir()
1626: {
1627: char_u *var;
1628:
1629: var = vim_getenv((char_u *)"HOME");
1630: #if defined(OS2) || defined(MSDOS) || defined(WIN32)
1631: /*
1632: * Default home dir is C:/
1633: * Best assumption we can make in such a situation.
1634: */
1635: if (var == NULL)
1636: var = "C:/";
1637: #endif
1638: if (var != NULL)
1639: {
1640: #ifdef UNIX
1641: if (mch_dirname(NameBuff, MAXPATHL) == OK)
1642: {
1643: if (!vim_chdir((char *)var) && mch_dirname(IObuff, IOSIZE) == OK)
1644: var = IObuff;
1645: vim_chdir((char *)NameBuff);
1646: }
1647: #endif
1648: homedir = strsave(var);
1649: }
1650: }
1651:
1652: /*
1653: * Expand environment variable with path name.
1654: * For Unix and OS/2 "~/" is also expanded, like $HOME.
1655: * If anything fails no expansion is done and dst equals src.
1656: * Note that IObuff must NOT be used as either src or dst! This is because
1657: * vim_getenv() may use IObuff to do its expansion.
1658: */
1659: void
1660: expand_env(src, dst, dstlen)
1661: char_u *src; /* input string e.g. "$HOME/vim.hlp" */
1662: char_u *dst; /* where to put the result */
1663: int dstlen; /* maximum length of the result */
1664: {
1665: char_u *tail;
1666: int c;
1667: char_u *var;
1668: int copy_char;
1669: #if defined(UNIX) || defined(OS2)
1670: int mustfree;
1671: int at_start = TRUE;
1672: #endif
1673:
1674: src = skipwhite(src);
1675: --dstlen; /* leave one char space for "\," */
1676: while (*src && dstlen > 0)
1677: {
1678: copy_char = TRUE;
1679: if (*src == '$'
1680: #if defined(UNIX) || defined(OS2)
1681: || (*src == '~' && at_start)
1682: #endif
1683: )
1684: {
1685: #if defined(UNIX) || defined(OS2)
1686: mustfree = FALSE;
1687:
1688: /*
1689: * The variable name is copied into dst temporarily, because it may
1690: * be a string in read-only memory and a NUL needs to be inserted.
1691: */
1692: if (*src == '$') /* environment var */
1693: {
1694: #endif
1695: tail = src + 1;
1696: var = dst;
1697: c = dstlen - 1;
1698: while (c-- > 0 && *tail && isidchar(*tail))
1699: #ifdef OS2
1700: { /* env vars only in uppercase */
1701: *var++ = toupper(*tail); /* toupper() may be a macro! */
1702: tail++;
1703: }
1704: #else
1705: *var++ = *tail++;
1706: #endif
1707: *var = NUL;
1708: #if defined(OS2) || defined(MSDOS) || defined(WIN32)
1709: /* use "C:/" when $HOME is not set */
1710: if (STRCMP(dst, "HOME") == 0)
1711: var = homedir;
1712: else
1713: #endif
1714: var = vim_getenv(dst);
1715: #if defined(UNIX) || defined(OS2)
1716: }
1717: /* home directory */
1718: else if (src[1] == NUL ||
1719: vim_strchr((char_u *)"/ ,\t\n", src[1]) != NULL)
1720: {
1721: var = homedir;
1722: tail = src + 1;
1723: }
1724: else /* user directory */
1725: # ifdef OS2
1726: {
1727: /* cannot expand user's home directory, so don't try */
1728: var = NULL;
1729: tail = ""; /* shut gcc up about "may be used uninitialized" */
1730: }
1731: # else
1732: {
1733: /*
1734: * Copy ~user to dst[], so we can put a NUL after it.
1735: */
1736: tail = src;
1737: var = dst;
1738: c = dstlen - 1;
1739: while (c-- > 0 && *tail &&
1740: isfilechar(*tail) && !ispathsep(*tail))
1741: *var++ = *tail++;
1742: *var = NUL;
1743:
1744: /*
1745: * If the system supports getpwnam(), use it.
1746: * Otherwise, or if getpwnam() fails, the shell is used to
1747: * expand ~user. This is slower and may fail if the shell
1748: * does not support ~user (old versions of /bin/sh).
1749: */
1750: # if defined(HAVE_GETPWNAM) && defined(HAVE_PWD_H)
1751: {
1752: struct passwd *pw;
1753:
1754: pw = getpwnam((char *)dst + 1);
1755: if (pw != NULL)
1756: var = (char_u *)pw->pw_dir;
1757: else
1758: var = NULL;
1759: }
1760: if (var == NULL)
1761: # endif
1762: {
1763: var = ExpandOne(dst, NULL, 0, WILD_EXPAND_FREE);
1764: mustfree = TRUE;
1765: }
1766: }
1767: # endif /* OS2 */
1768: #endif /* UNIX || OS2 */
1769: if (var != NULL && *var != NUL &&
1770: (STRLEN(var) + STRLEN(tail) + 1 < (unsigned)dstlen))
1771: {
1772: STRCPY(dst, var);
1773: dstlen -= STRLEN(var);
1774: dst += STRLEN(var);
1775: /* if var[] ends in a path separator and tail[] starts
1776: * with it, skip a character */
1777: if (*var && ispathsep(*(dst-1)) && ispathsep(*tail))
1778: ++tail;
1779: src = tail;
1780: copy_char = FALSE;
1781: }
1782: #if defined(UNIX) || defined(OS2)
1783: if (mustfree)
1784: vim_free(var);
1785: #endif
1786: }
1787:
1788: if (copy_char) /* copy at least one char */
1789: {
1790: #if defined(UNIX) || defined(OS2)
1791: /*
1792: * Recogize the start of a new name, for '~'.
1793: */
1794: at_start = FALSE;
1795: #endif
1796: if (src[0] == '\\')
1797: {
1798: *dst++ = *src++;
1799: --dstlen;
1800: }
1801: #if defined(UNIX) || defined(OS2)
1802: else if (src[0] == ' ' || src[0] == ',')
1803: at_start = TRUE;
1804: #endif
1805: *dst++ = *src++;
1806: --dstlen;
1807: }
1808: }
1809: *dst = NUL;
1810: }
1811:
1812: /*
1.2 downsj 1813: * Replace home directory by "~" in each space or comma separated filename in
1814: * 'src'. If anything fails (except when out of space) dst equals src.
1.1 downsj 1815: */
1816: void
1817: home_replace(buf, src, dst, dstlen)
1818: BUF *buf; /* when not NULL, check for help files */
1819: char_u *src; /* input file name */
1820: char_u *dst; /* where to put the result */
1821: int dstlen; /* maximum length of the result */
1822: {
1823: size_t dirlen = 0, envlen = 0;
1824: size_t len;
1825: char_u *homedir_env;
1826: char_u *p;
1827:
1828: if (src == NULL)
1829: {
1830: *dst = NUL;
1831: return;
1832: }
1833:
1834: /*
1835: * If the file is a help file, remove the path completely.
1836: */
1837: if (buf != NULL && buf->b_help)
1838: {
1839: STRCPY(dst, gettail(src));
1840: return;
1841: }
1842:
1843: /*
1844: * We check both the value of the $HOME environment variable and the
1845: * "real" home directory.
1846: */
1847: if (homedir != NULL)
1848: dirlen = STRLEN(homedir);
1849: homedir_env = vim_getenv((char_u *)"HOME");
1850: if (homedir_env != NULL)
1851: envlen = STRLEN(homedir_env);
1852:
1853: src = skipwhite(src);
1854: while (*src && dstlen > 0)
1855: {
1856: /*
1857: * Here we are at the beginning of a filename.
1858: * First, check to see if the beginning of the filename matches
1859: * $HOME or the "real" home directory. Check that there is a '/'
1860: * after the match (so that if e.g. the file is "/home/pieter/bla",
1861: * and the home directory is "/home/piet", the file does not end up
1862: * as "~er/bla" (which would seem to indicate the file "bla" in user
1863: * er's home directory)).
1864: */
1865: p = homedir;
1866: len = dirlen;
1867: for (;;)
1868: {
1869: if (len && fnamencmp(src, p, len) == 0 && (ispathsep(src[len]) ||
1870: src[len] == ',' || src[len] == ' ' || src[len] == NUL))
1871: {
1872: src += len;
1873: if (--dstlen > 0)
1874: *dst++ = '~';
1.2 downsj 1875:
1.1 downsj 1876: /*
1.2 downsj 1877: * If it's just the home directory, add "/".
1.1 downsj 1878: */
1879: if (!ispathsep(src[0]) && --dstlen > 0)
1880: *dst++ = '/';
1881: }
1882: if (p == homedir_env)
1883: break;
1884: p = homedir_env;
1885: len = envlen;
1886: }
1887:
1888: /* skip to separator: space or comma */
1889: while (*src && *src != ',' && *src != ' ' && --dstlen > 0)
1890: *dst++ = *src++;
1891: /* skip separator */
1892: while ((*src == ' ' || *src == ',') && --dstlen > 0)
1893: *dst++ = *src++;
1894: }
1895: /* if (dstlen == 0) out of space, what to do??? */
1896:
1897: *dst = NUL;
1898: }
1899:
1900: /*
1901: * Like home_replace, store the replaced string in allocated memory.
1902: * When something fails, NULL is returned.
1903: */
1904: char_u *
1905: home_replace_save(buf, src)
1906: BUF *buf; /* when not NULL, check for help files */
1907: char_u *src; /* input file name */
1908: {
1909: char_u *dst;
1910: unsigned len;
1911:
1.2 downsj 1912: len = 3; /* space for "~/" and trailing NUL */
1913: if (src != NULL) /* just in case */
1914: len += STRLEN(src);
1.1 downsj 1915: dst = alloc(len);
1916: if (dst != NULL)
1917: home_replace(buf, src, dst, len);
1918: return dst;
1919: }
1920:
1921: /*
1922: * Compare two file names and return:
1923: * FPC_SAME if they both exist and are the same file.
1924: * FPC_DIFF if they both exist and are different files.
1925: * FPC_NOTX if they both don't exist.
1926: * FPC_DIFFX if one of them doesn't exist.
1927: * For the first name environment variables are expanded
1928: */
1929: int
1930: fullpathcmp(s1, s2)
1931: char_u *s1, *s2;
1932: {
1933: #ifdef UNIX
1934: char_u buf1[MAXPATHL];
1935: struct stat st1, st2;
1936: int r1, r2;
1937:
1938: expand_env(s1, buf1, MAXPATHL);
1939: r1 = stat((char *)buf1, &st1);
1940: r2 = stat((char *)s2, &st2);
1941: if (r1 != 0 && r2 != 0)
1942: return FPC_NOTX;
1943: if (r1 != 0 || r2 != 0)
1944: return FPC_DIFFX;
1945: if (st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino)
1946: return FPC_SAME;
1947: return FPC_DIFF;
1948: #else
1949: char_u *buf1 = NULL;
1950: char_u *buf2 = NULL;
1951: int retval = FPC_DIFF;
1952: int r1, r2;
1953:
1954: if ((buf1 = alloc(MAXPATHL)) != NULL && (buf2 = alloc(MAXPATHL)) != NULL)
1955: {
1956: expand_env(s1, buf2, MAXPATHL);
1957: /*
1958: * If FullName() failed, the file probably doesn't exist.
1959: */
1960: r1 = FullName(buf2, buf1, MAXPATHL, FALSE);
1961: r2 = FullName(s2, buf2, MAXPATHL, FALSE);
1962: if (r1 != OK && r2 != OK)
1963: retval = FPC_NOTX;
1964: else if (r1 != OK || r2 != OK)
1965: retval = FPC_DIFFX;
1966: else if (fnamecmp(buf1, buf2))
1967: retval = FPC_DIFF;
1968: else
1969: retval = FPC_SAME;
1970: }
1971: vim_free(buf1);
1972: vim_free(buf2);
1973: return retval;
1974: #endif
1975: }
1976:
1977: /*
1978: * get the tail of a path: the file name.
1979: */
1980: char_u *
1981: gettail(fname)
1982: char_u *fname;
1983: {
1984: register char_u *p1, *p2;
1985:
1986: if (fname == NULL)
1987: return (char_u *)"";
1988: for (p1 = p2 = fname; *p2; ++p2) /* find last part of path */
1989: {
1990: if (ispathsep(*p2))
1991: p1 = p2 + 1;
1992: }
1993: return p1;
1994: }
1995:
1996: /*
1.2 downsj 1997: * Get a pointer to one character past the head of a path name.
1998: * Unix: after "/"; DOS: after "c:\"; Amiga: after "disk:/".
1999: * If there is no head, path is returned.
2000: */
2001: char_u *
2002: get_past_head(path)
2003: char_u *path;
2004: {
2005: char_u *retval;
2006:
2007: #if defined(MSDOS) || defined(WIN32) || defined(OS2)
2008: /* may skip "c:" */
2009: if (isalpha(path[0]) && path[1] == ':')
2010: retval = path + 2;
2011: else
2012: retval = path;
2013: #else
2014: # if defined(AMIGA)
2015: /* may skip "label:" */
2016: retval = vim_strchr(path, ':');
2017: if (retval == NULL)
2018: retval = path;
2019: # else /* Unix */
2020: retval = path;
2021: # endif
2022: #endif
2023:
2024: while (ispathsep(*retval))
2025: ++retval;
2026:
2027: return retval;
2028: }
2029:
2030: /*
1.1 downsj 2031: * return TRUE if 'c' is a path separator.
2032: */
2033: int
2034: ispathsep(c)
2035: int c;
2036: {
2037: #ifdef UNIX
2038: return (c == PATHSEP); /* UNIX has ':' inside file names */
2039: #else
2040: # ifdef BACKSLASH_IN_FILENAME
2041: return (c == ':' || c == PATHSEP || c == '\\');
2042: # else
2043: return (c == ':' || c == PATHSEP);
2044: # endif
2045: #endif
2046: }
2047:
2048: /*
2049: * Concatenate filenames fname1 and fname2 into allocated memory.
2050: * Only add a '/' when 'sep' is TRUE and it is neccesary.
2051: */
2052: char_u *
2053: concat_fnames(fname1, fname2, sep)
2054: char_u *fname1;
2055: char_u *fname2;
2056: int sep;
2057: {
2058: char_u *dest;
2059:
2060: dest = alloc((unsigned)(STRLEN(fname1) + STRLEN(fname2) + 2));
2061: if (dest != NULL)
2062: {
2063: STRCPY(dest, fname1);
2064: if (sep && *dest && !ispathsep(*(dest + STRLEN(dest) - 1)))
2065: STRCAT(dest, PATHSEPSTR);
2066: STRCAT(dest, fname2);
2067: }
2068: return dest;
2069: }
2070:
2071: /*
2072: * FullName_save - Make an allocated copy of a full file name.
2073: * Returns NULL when failed.
2074: */
2075: char_u *
2076: FullName_save(fname)
2077: char_u *fname;
2078: {
2079: char_u *buf;
2080: char_u *new_fname = NULL;
2081:
2082: buf = alloc((unsigned)MAXPATHL);
2083: if (buf != NULL)
2084: {
2085: if (FullName(fname, buf, MAXPATHL, FALSE) != FAIL)
2086: new_fname = strsave(buf);
2087: vim_free(buf);
2088: }
2089: return new_fname;
2090: }
2091:
2092: #ifdef CINDENT
2093:
2094: /*
2095: * Functions for C-indenting.
2096: * Most of this originally comes from Eric Fischer.
2097: */
2098: /*
2099: * Below "XXX" means that this function may unlock the current line.
2100: */
2101:
2102: static int isdefault __ARGS((char_u *));
2103: static char_u *after_label __ARGS((char_u *l));
2104: static int get_indent_nolabel __ARGS((linenr_t lnum));
2105: static int skip_label __ARGS((linenr_t, char_u **pp, int ind_maxcomment));
2106: static int ispreproc __ARGS((char_u *));
2107: static int iscomment __ARGS((char_u *));
2108: static int commentorempty __ARGS((char_u *));
2109: static int isterminated __ARGS((char_u *));
2110: static int isfuncdecl __ARGS((char_u *));
2111: static char_u *skip_string __ARGS((char_u *p));
2112: static int isif __ARGS((char_u *));
2113: static int iselse __ARGS((char_u *));
2114: static int isdo __ARGS((char_u *));
2115: static int iswhileofdo __ARGS((char_u *, linenr_t, int));
2116: static FPOS *find_start_comment __ARGS((int ind_maxcomment));
2117: static FPOS *find_start_brace __ARGS((int));
2118: static FPOS *find_match_paren __ARGS((int, int));
2119: static int find_last_paren __ARGS((char_u *l));
2120: static int find_match __ARGS((int lookfor, linenr_t ourscope,
2121: int ind_maxparen, int ind_maxcomment));
2122:
2123: /*
2124: * Recognize a label: "label:".
2125: * Note: curwin->w_cursor must be where we are looking for the label.
2126: */
2127: int
2128: islabel(ind_maxcomment) /* XXX */
2129: int ind_maxcomment;
2130: {
2131: char_u *s;
2132:
2133: s = skipwhite(ml_get_curline());
2134:
2135: /*
2136: * Exclude "default" from labels, since it should be indented
2137: * like a switch label.
2138: */
2139:
2140: if (isdefault(s))
2141: return FALSE;
2142:
2143: if (!isidchar(*s)) /* need at least one ID character */
2144: return FALSE;
2145:
2146: while (isidchar(*s))
2147: s++;
2148:
2149: s = skipwhite(s);
2150:
2151: /* "::" is not a label, it's C++ */
2152: if (*s == ':' && s[1] != ':')
2153: {
2154: /*
2155: * Only accept a label if the previous line is terminated or is a case
2156: * label.
2157: */
2158: FPOS cursor_save;
2159: FPOS *trypos;
2160: char_u *line;
2161:
2162: cursor_save = curwin->w_cursor;
2163: while (curwin->w_cursor.lnum > 1)
2164: {
2165: --curwin->w_cursor.lnum;
2166:
2167: /*
2168: * If we're in a comment now, skip to the start of the comment.
2169: */
2170: curwin->w_cursor.col = 0;
2171: if ((trypos = find_start_comment(ind_maxcomment)) != NULL) /* XXX */
2172: curwin->w_cursor = *trypos;
2173:
2174: line = ml_get_curline();
2175: if (ispreproc(line)) /* ignore #defines, #if, etc. */
2176: continue;
2177: if (commentorempty(line))
2178: continue;
2179:
2180: curwin->w_cursor = cursor_save;
2181: if (isterminated(line) || iscase(line))
2182: return TRUE;
2183: return FALSE;
2184: }
2185: curwin->w_cursor = cursor_save;
2186: return TRUE; /* label at start of file??? */
2187: }
2188: return FALSE;
2189: }
2190:
2191: /*
2192: * Recognize a switch label: "case .*:" or "default:".
2193: */
2194: int
2195: iscase(s)
2196: char_u *s;
2197: {
2198: s = skipwhite(s);
2199: if (STRNCMP(s, "case", 4) == 0 && !isidchar(s[4]))
2200: {
2201: for (s += 4; *s; ++s)
2202: if (*s == ':')
2203: {
2204: if (s[1] == ':') /* skip over "::" for C++ */
2205: ++s;
2206: else
2207: return TRUE;
2208: }
2209: return FALSE;
2210: }
2211:
2212: if (isdefault(s))
2213: return TRUE;
2214: return FALSE;
2215: }
2216:
2217: /*
2218: * Recognize a "default" switch label.
2219: */
2220: static int
2221: isdefault(s)
2222: char_u *s;
2223: {
2224: return (STRNCMP(s, "default", 7) == 0 &&
2225: *(s = skipwhite(s + 7)) == ':' &&
2226: s[1] != ':');
2227: }
2228:
2229: /*
2230: * Return a pointer to the first non-empty non-comment character after a ':'.
2231: * Return NULL if not found.
2232: * case 234: a = b;
2233: * ^
2234: */
2235: static char_u *
2236: after_label(l)
2237: char_u *l;
2238: {
2239: for ( ; *l; ++l)
2240: if (*l == ':')
2241: {
2242: if (l[1] == ':') /* skip over "::" for C++ */
2243: ++l;
2244: else
2245: break;
2246: }
2247: if (*l == NUL)
2248: return NULL;
2249: l = skipwhite(l + 1);
2250: if (commentorempty(l))
2251: return NULL;
2252: return l;
2253: }
2254:
2255: /*
2256: * Get indent of line "lnum", skipping a label.
2257: * Return 0 if there is nothing after the label.
2258: */
2259: static int
2260: get_indent_nolabel(lnum) /* XXX */
2261: linenr_t lnum;
2262: {
2263: char_u *l;
2264: FPOS fp;
2265: colnr_t col;
2266: char_u *p;
2267:
2268: l = ml_get(lnum);
2269: p = after_label(l);
2270: if (p == NULL)
2271: return 0;
2272:
2273: fp.col = p - l;
2274: fp.lnum = lnum;
2275: getvcol(curwin, &fp, &col, NULL, NULL);
2276: return (int)col;
2277: }
2278:
2279: /*
2280: * Find indent for line "lnum", ignoring any case or jump label.
2281: * Also return a pointer to the text (after the label).
2282: * label: if (asdf && asdfasdf)
2283: * ^
2284: */
2285: static int
2286: skip_label(lnum, pp, ind_maxcomment)
2287: linenr_t lnum;
2288: char_u **pp;
2289: int ind_maxcomment;
2290: {
2291: char_u *l;
2292: int amount;
2293: FPOS cursor_save;
2294:
2295: cursor_save = curwin->w_cursor;
2296: curwin->w_cursor.lnum = lnum;
2297: l = ml_get_curline();
2298: if (iscase(l) || islabel(ind_maxcomment)) /* XXX */
2299: {
2300: amount = get_indent_nolabel(lnum);
2301: l = after_label(ml_get_curline());
2302: if (l == NULL) /* just in case */
2303: l = ml_get_curline();
2304: }
2305: else
2306: {
2307: amount = get_indent();
2308: l = ml_get_curline();
2309: }
2310: *pp = l;
2311:
2312: curwin->w_cursor = cursor_save;
2313: return amount;
2314: }
2315:
2316: /*
2317: * Recognize a preprocessor statement: Any line that starts with '#'.
2318: */
2319: static int
2320: ispreproc(s)
2321: char_u *s;
2322: {
2323: s = skipwhite(s);
2324: if (*s == '#')
2325: return TRUE;
2326: return 0;
2327: }
2328:
2329: /*
2330: * Recognize the start of a C or C++ comment.
2331: */
2332: static int
2333: iscomment(p)
2334: char_u *p;
2335: {
2336: return (p[0] == '/' && (p[1] == '*' || p[1] == '/'));
2337: }
2338:
2339: /*
2340: * Recognize an empty or comment line.
2341: */
2342: static int
2343: commentorempty(s)
2344: char_u *s;
2345: {
2346: s = skipwhite(s);
2347: if (*s == NUL || iscomment(s))
2348: return TRUE;
2349: return FALSE;
2350: }
2351:
2352: /*
2353: * Recognize a line that starts with '{' or '}', or ends with ';' or '}'.
1.2 downsj 2354: * Don't consider "} else" a terminated line.
1.1 downsj 2355: * Also consider a line terminated if it ends in ','. This is not 100%
2356: * correct, but this mostly means we are in initializations and then it's OK.
2357: */
2358: static int
2359: isterminated(s)
2360: char_u *s;
2361: {
2362: s = skipwhite(s);
2363:
1.2 downsj 2364: if (*s == '{' || (*s == '}' && !iselse(s)))
1.1 downsj 2365: return TRUE;
2366:
2367: while (*s)
2368: {
2369: if (iscomment(s)) /* at start of comment ignore rest of line */
2370: return FALSE;
2371: s = skip_string(s);
2372: if ((*s == ';' || *s == '{' || *s == ',') && commentorempty(s + 1))
2373: return TRUE;
2374: s++;
2375: }
2376: return FALSE;
2377: }
2378:
2379: /*
2380: * Recognize the basic picture of a function declaration -- it needs to
2381: * have an open paren somewhere and a close paren at the end of the line and
2382: * no semicolons anywhere.
2383: */
2384: static int
2385: isfuncdecl(s)
2386: char_u *s;
2387: {
2388: while (*s && *s != '(' && *s != ';')
2389: if (iscomment(s++))
2390: return FALSE; /* comment before () ??? */
2391: if (*s != '(')
2392: return FALSE; /* ';' before any () or no '(' */
2393:
2394: while (*s && *s != ';')
2395: {
2396: if (*s == ')' && commentorempty(s + 1))
2397: return TRUE;
2398: if (iscomment(s++))
2399: return FALSE; /* comment between ( and ) ??? */
2400: }
2401: return FALSE;
2402: }
2403:
2404: /*
2405: * Skip over a "string" and a 'c' character.
2406: */
2407: static char_u *
2408: skip_string(p)
2409: char_u *p;
2410: {
2411: int i;
2412:
2413: /*
2414: * We loop, because strings may be concatenated: "date""time".
2415: */
2416: for ( ; ; ++p)
2417: {
2418: if (p[0] == '\'') /* 'c' or '\n' or '\000' */
2419: {
2420: if (!p[1]) /* ' at end of line */
2421: break;
2422: i = 2;
2423: if (p[1] == '\\') /* '\n' or '\000' */
2424: {
2425: ++i;
2426: while (isdigit(p[i - 1])) /* '\000' */
2427: ++i;
2428: }
2429: if (p[i] == '\'') /* check for trailing ' */
2430: {
2431: p += i;
2432: continue;
2433: }
2434: }
2435: else if (p[0] == '"') /* start of string */
2436: {
2437: for (++p; p[0]; ++p)
2438: {
2439: if (p[0] == '\\' && p[1])
2440: ++p;
2441: else if (p[0] == '"') /* end of string */
2442: break;
2443: }
2444: continue;
2445: }
2446: break; /* no string found */
2447: }
2448: if (!*p)
2449: --p; /* backup from NUL */
2450: return p;
2451: }
2452:
2453: static int
2454: isif(p)
2455: char_u *p;
2456: {
2457: return (STRNCMP(p, "if", 2) == 0 && !isidchar(p[2]));
2458: }
2459:
2460: static int
2461: iselse(p)
2462: char_u *p;
2463: {
1.2 downsj 2464: if (*p == '}') /* accept "} else" */
2465: p = skipwhite(p + 1);
1.1 downsj 2466: return (STRNCMP(p, "else", 4) == 0 && !isidchar(p[4]));
2467: }
2468:
2469: static int
2470: isdo(p)
2471: char_u *p;
2472: {
2473: return (STRNCMP(p, "do", 2) == 0 && !isidchar(p[2]));
2474: }
2475:
2476: /*
2477: * Check if this is a "while" that should have a matching "do".
2478: * We only accept a "while (condition) ;", with only white space between the
2479: * ')' and ';'. The condition may be spread over several lines.
2480: */
2481: static int
2482: iswhileofdo(p, lnum, ind_maxparen) /* XXX */
2483: char_u *p;
2484: linenr_t lnum;
2485: int ind_maxparen;
2486: {
2487: FPOS cursor_save;
2488: FPOS *trypos;
2489: int retval = FALSE;
2490:
2491: p = skipwhite(p);
1.2 downsj 2492: if (*p == '}') /* accept "} while (cond);" */
2493: p = skipwhite(p + 1);
1.1 downsj 2494: if (STRNCMP(p, "while", 5) == 0 && !isidchar(p[5]))
2495: {
2496: cursor_save = curwin->w_cursor;
2497: curwin->w_cursor.lnum = lnum;
2498: curwin->w_cursor.col = 0;
1.2 downsj 2499: p = ml_get_curline();
2500: while (*p && *p != 'w') /* skip any '}', until the 'w' of the "while" */
2501: {
2502: ++p;
2503: ++curwin->w_cursor.col;
2504: }
1.1 downsj 2505: if ((trypos = findmatchlimit(0, 0, ind_maxparen)) != NULL)
2506: {
2507: p = ml_get_pos(trypos) + 1;
2508: p = skipwhite(p);
2509: if (*p == ';')
2510: retval = TRUE;
2511: }
2512: curwin->w_cursor = cursor_save;
2513: }
2514: return retval;
2515: }
2516:
2517: /*
2518: * Find the start of a comment, not knowing if we are in a comment right now.
2519: * Search starts at w_cursor.lnum and goes backwards.
2520: */
2521: static FPOS *
2522: find_start_comment(ind_maxcomment) /* XXX */
2523: int ind_maxcomment;
2524: {
2525: FPOS *pos;
2526: char_u *line;
2527: char_u *p;
2528:
2529: if ((pos = findmatchlimit('*', FM_BACKWARD, ind_maxcomment)) == NULL)
2530: return NULL;
2531:
2532: /*
2533: * Check if the comment start we found is inside a string.
2534: */
2535: line = ml_get(pos->lnum);
2536: for (p = line; *p && (unsigned)(p - line) < pos->col; ++p)
2537: p = skip_string(p);
2538: if ((unsigned)(p - line) > pos->col)
2539: return NULL;
2540: return pos;
2541: }
2542:
2543: /*
2544: * Find the '{' at the start of the block we are in.
2545: * Return NULL of no match found.
2546: * Ignore a '{' that is in a comment, makes indenting the next three lines
2547: * work. */
2548: /* foo() */
2549: /* { */
2550: /* } */
2551:
2552: static FPOS *
2553: find_start_brace(ind_maxcomment) /* XXX */
2554: int ind_maxcomment;
2555: {
2556: FPOS cursor_save;
2557: FPOS *trypos;
2558: FPOS *pos;
2559: static FPOS pos_copy;
2560:
2561: cursor_save = curwin->w_cursor;
2562: while ((trypos = findmatchlimit('{', FM_BLOCKSTOP, 0)) != NULL)
2563: {
2564: pos_copy = *trypos; /* copy FPOS, next findmatch will change it */
2565: trypos = &pos_copy;
2566: curwin->w_cursor = *trypos;
2567: pos = NULL;
2568: if (!iscomment(skipwhite(ml_get(trypos->lnum))) &&
2569: (pos = find_start_comment(ind_maxcomment)) == NULL) /* XXX */
2570: break;
2571: if (pos != NULL)
2572: curwin->w_cursor.lnum = pos->lnum;
2573: }
2574: curwin->w_cursor = cursor_save;
2575: return trypos;
2576: }
2577:
2578: /*
2579: * Find the matching '(', failing if it is in a comment.
2580: * Return NULL of no match found.
2581: */
2582: static FPOS *
2583: find_match_paren(ind_maxparen, ind_maxcomment) /* XXX */
2584: int ind_maxparen;
2585: int ind_maxcomment;
2586: {
2587: FPOS cursor_save;
2588: FPOS *trypos;
2589: static FPOS pos_copy;
2590:
2591: cursor_save = curwin->w_cursor;
2592: if ((trypos = findmatchlimit('(', 0, ind_maxparen)) != NULL)
2593: {
2594: if (iscomment(skipwhite(ml_get(trypos->lnum))))
2595: trypos = NULL;
2596: else
2597: {
2598: pos_copy = *trypos; /* copy trypos, findmatch will change it */
2599: trypos = &pos_copy;
2600: curwin->w_cursor = *trypos;
2601: if (find_start_comment(ind_maxcomment) != NULL) /* XXX */
2602: trypos = NULL;
2603: }
2604: }
2605: curwin->w_cursor = cursor_save;
2606: return trypos;
2607: }
2608:
2609: /*
2610: * Set w_cursor.col to the column number of the last ')' in line "l".
2611: */
2612: static int
2613: find_last_paren(l)
2614: char_u *l;
2615: {
2616: int i;
2617: int retval = FALSE;
2618:
2619: curwin->w_cursor.col = 0; /* default is start of line */
2620:
2621: for (i = 0; l[i]; i++)
2622: {
2623: i = skip_string(l + i) - l; /* ignore parens in quotes */
2624: if (l[i] == ')')
2625: {
2626: curwin->w_cursor.col = i;
2627: retval = TRUE;
2628: }
2629: }
2630: return retval;
2631: }
2632:
2633: int
2634: get_c_indent()
2635: {
2636: /*
2637: * spaces from a block's opening brace the prevailing indent for that
2638: * block should be
2639: */
2640: int ind_level = curbuf->b_p_sw;
2641:
2642: /*
2643: * spaces from the edge of the line an open brace that's at the end of a
2644: * line is imagined to be.
2645: */
2646: int ind_open_imag = 0;
2647:
2648: /*
2649: * spaces from the prevailing indent for a line that is not precededof by
2650: * an opening brace.
2651: */
2652: int ind_no_brace = 0;
2653:
2654: /*
2655: * column where the first { of a function should be located
2656: */
2657: int ind_first_open = 0;
2658:
2659: /*
2660: * spaces from the prevailing indent a leftmost open brace should be
2661: * located
2662: */
2663: int ind_open_extra = 0;
2664:
2665: /*
2666: * spaces from the matching open brace (real location for one at the left
2667: * edge; imaginary location from one that ends a line) the matching close
2668: * brace should be located
2669: */
2670: int ind_close_extra = 0;
2671:
2672: /*
2673: * spaces from the edge of the line an open brace sitting in the leftmost
2674: * column is imagined to be
2675: */
2676: int ind_open_left_imag = 0;
2677:
2678: /*
2679: * spaces from the switch() indent a "case xx" label should be located
2680: */
2681: int ind_case = curbuf->b_p_sw;
2682:
2683: /*
2684: * spaces from the "case xx:" code after a switch() should be located
2685: */
2686: int ind_case_code = curbuf->b_p_sw;
2687:
2688: /*
2689: * amount K&R-style parameters should be indented
2690: */
2691: int ind_param = curbuf->b_p_sw;
2692:
2693: /*
2694: * amount a function type spec should be indented
2695: */
2696: int ind_func_type = curbuf->b_p_sw;
2697:
2698: /*
2699: * additional spaces beyond the prevailing indent a continuation line
2700: * should be located
2701: */
2702: int ind_continuation = curbuf->b_p_sw;
2703:
2704: /*
2705: * spaces from the indent of the line with an unclosed parentheses
2706: */
2707: int ind_unclosed = curbuf->b_p_sw * 2;
2708:
2709: /*
2710: * spaces from the comment opener when there is nothing after it.
2711: */
2712: int ind_in_comment = 3;
2713:
2714: /*
2715: * max lines to search for an open paren
2716: */
2717: int ind_maxparen = 20;
2718:
2719: /*
2720: * max lines to search for an open comment
2721: */
2722: int ind_maxcomment = 30;
2723:
2724: FPOS cur_curpos;
2725: int amount;
2726: int scope_amount;
2727: int cur_amount;
2728: colnr_t col;
2729: char_u *theline;
2730: char_u *linecopy;
2731: FPOS *trypos;
2732: FPOS our_paren_pos;
2733: char_u *start;
2734: int start_brace;
2735: #define BRACE_IN_COL0 1 /* '{' is in comumn 0 */
2736: #define BRACE_AT_START 2 /* '{' is at start of line */
2737: #define BRACE_AT_END 3 /* '{' is at end of line */
2738: linenr_t ourscope;
2739: char_u *l;
2740: char_u *look;
2741: int lookfor;
2742: #define LOOKFOR_IF 1
2743: #define LOOKFOR_DO 2
2744: #define LOOKFOR_CASE 3
2745: #define LOOKFOR_ANY 4
2746: #define LOOKFOR_TERM 5
2747: #define LOOKFOR_UNTERM 6
2748: int whilelevel;
2749: linenr_t lnum;
2750: char_u *options;
2751: int fraction = 0; /* init for GCC */
2752: int divider;
2753: int n;
2754:
2755: for (options = curbuf->b_p_cino; *options; )
2756: {
2757: l = options++;
2758: if (*options == '-')
2759: ++options;
2760: n = getdigits(&options);
2761: divider = 0;
2762: if (*options == '.') /* ".5s" means a fraction */
2763: {
2764: fraction = atol((char *)++options);
2765: while (isdigit(*options))
2766: {
2767: ++options;
2768: if (divider)
2769: divider *= 10;
2770: else
2771: divider = 10;
2772: }
2773: }
2774: if (*options == 's') /* "2s" means two times 'shiftwidth' */
2775: {
2776: if (n == 0 && fraction == 0)
2777: n = curbuf->b_p_sw; /* just "s" is one 'shiftwidth' */
2778: else
2779: {
2780: n *= curbuf->b_p_sw;
2781: if (divider)
2782: n += (curbuf->b_p_sw * fraction + divider / 2) / divider;
2783: }
2784: ++options;
2785: }
2786: if (l[1] == '-')
2787: n = -n;
2788: switch (*l)
2789: {
2790: case '>': ind_level = n; break;
2791: case 'e': ind_open_imag = n; break;
2792: case 'n': ind_no_brace = n; break;
2793: case 'f': ind_first_open = n; break;
2794: case '{': ind_open_extra = n; break;
2795: case '}': ind_close_extra = n; break;
2796: case '^': ind_open_left_imag = n; break;
2797: case ':': ind_case = n; break;
2798: case '=': ind_case_code = n; break;
2799: case 'p': ind_param = n; break;
2800: case 't': ind_func_type = n; break;
2801: case 'c': ind_in_comment = n; break;
2802: case '+': ind_continuation = n; break;
2803: case '(': ind_unclosed = n; break;
2804: case ')': ind_maxparen = n; break;
2805: case '*': ind_maxcomment = n; break;
2806: }
2807: }
2808:
2809: /* remember where the cursor was when we started */
2810:
2811: cur_curpos = curwin->w_cursor;
2812:
2813: /* get the current contents of the line.
2814: * This is required, because onle the most recent line obtained with
2815: * ml_get is valid! */
2816:
2817: linecopy = strsave(ml_get(cur_curpos.lnum));
2818: if (linecopy == NULL)
2819: return 0;
2820:
2821: /*
2822: * In insert mode and the cursor is on a ')' trunctate the line at the
2823: * cursor position. We don't want to line up with the matching '(' when
2824: * inserting new stuff.
2825: */
2826: if ((State & INSERT) && linecopy[curwin->w_cursor.col] == ')')
2827: linecopy[curwin->w_cursor.col] = NUL;
2828:
2829: theline = skipwhite(linecopy);
2830:
2831: /* move the cursor to the start of the line */
2832:
2833: curwin->w_cursor.col = 0;
2834:
2835: /*
2836: * #defines and so on always go at the left when included in 'cinkeys'.
2837: */
2838: if (*theline == '#' && (*linecopy == '#' || in_cinkeys('#', ' ', TRUE)))
2839: {
2840: amount = 0;
2841: }
2842:
2843: /*
2844: * Is it a non-case label? Then that goes at the left margin too.
2845: */
2846: else if (islabel(ind_maxcomment)) /* XXX */
2847: {
2848: amount = 0;
2849: }
2850:
2851: /*
2852: * If we're inside a comment and not looking at the start of the
2853: * comment...
2854: */
2855: else if (!iscomment(theline) &&
2856: (trypos = find_start_comment(ind_maxcomment)) != NULL) /* XXX */
2857: {
2858:
2859: /* find how indented the line beginning the comment is */
2860: getvcol(curwin, trypos, &col, NULL, NULL);
2861: amount = col;
2862:
2863: /* if our line starts with an asterisk, line up with the
2864: * asterisk in the comment opener; otherwise, line up
2865: * with the first character of the comment text.
2866: */
2867: if (theline[0] == '*')
2868: {
2869: amount += 1;
2870: }
2871: else
2872: {
2873: /*
2874: * If we are more than one line away from the comment opener, take
2875: * the indent of the previous non-empty line.
2876: * If we are just below the comment opener and there are any
2877: * white characters after it line up with the text after it.
2878: * up with them; otherwise, just use a single space.
2879: */
2880: amount = -1;
2881: for (lnum = cur_curpos.lnum - 1; lnum > trypos->lnum; --lnum)
2882: {
2883: if (linewhite(lnum)) /* skip blank lines */
2884: continue;
2885: amount = get_indent_lnum(lnum); /* XXX */
2886: break;
2887: }
2888: if (amount == -1) /* use the comment opener */
2889: {
2890: start = ml_get(trypos->lnum);
2891: look = start + trypos->col + 2; /* skip / and * */
2892: if (*look) /* if something after it */
2893: trypos->col = skipwhite(look) - start;
2894: getvcol(curwin, trypos, &col, NULL, NULL);
2895: amount = col;
2896: if (!*look)
2897: amount += ind_in_comment;
2898: }
2899: }
2900: }
2901:
2902: /*
2903: * Are we inside parentheses?
2904: */ /* XXX */
2905: else if ((trypos = find_match_paren(ind_maxparen, ind_maxcomment)) != NULL)
2906: {
2907: /*
2908: * If the matching paren is more than one line away, use the indent of
2909: * a previous non-empty line that matches the same paren.
2910: */
2911: amount = -1;
2912: our_paren_pos = *trypos;
2913: if (theline[0] != ')')
2914: {
2915: for (lnum = cur_curpos.lnum - 1; lnum > our_paren_pos.lnum; --lnum)
2916: {
2917: l = skipwhite(ml_get(lnum));
2918: if (commentorempty(l)) /* skip comment lines */
2919: continue;
2920: if (ispreproc(l)) /* ignore #defines, #if, etc. */
2921: continue;
2922: curwin->w_cursor.lnum = lnum;
2923: /* XXX */
2924: if ((trypos = find_match_paren(ind_maxparen,
2925: ind_maxcomment)) != NULL &&
2926: trypos->lnum == our_paren_pos.lnum &&
2927: trypos->col == our_paren_pos.col)
2928: {
2929: amount = get_indent_lnum(lnum); /* XXX */
2930: break;
2931: }
2932: }
2933: }
2934:
2935: /*
2936: * Line up with line where the matching paren is.
2937: * If the line starts with a '(' or the indent for unclosed
2938: * parentheses is zero, line up with the unclosed parentheses.
2939: */
2940: if (amount == -1)
2941: {
2942: amount = skip_label(our_paren_pos.lnum, &look, ind_maxcomment);
2943: if (theline[0] == ')' || ind_unclosed == 0 ||
2944: *skipwhite(look) == '(')
2945: {
2946:
2947: /*
2948: * If we're looking at a close paren, line up right there;
2949: * otherwise, line up with the next non-white character.
2950: */
2951: if (theline[0] != ')')
2952: {
2953: col = our_paren_pos.col + 1;
2954: look = ml_get(our_paren_pos.lnum);
2955: while (vim_iswhite(look[col]))
2956: col++;
2957: if (look[col] != NUL) /* In case of trailing space */
2958: our_paren_pos.col = col;
2959: else
2960: our_paren_pos.col++;
2961: }
2962:
2963: /*
2964: * Find how indented the paren is, or the character after it if
2965: * we did the above "if".
2966: */
2967: getvcol(curwin, &our_paren_pos, &col, NULL, NULL);
2968: amount = col;
2969: }
2970: else
2971: amount += ind_unclosed;
2972: }
2973: }
2974:
2975: /*
2976: * Are we at least inside braces, then?
2977: */
2978: else if ((trypos = find_start_brace(ind_maxcomment)) != NULL) /* XXX */
2979: {
2980: ourscope = trypos->lnum;
2981: start = ml_get(ourscope);
2982:
2983: /*
2984: * Now figure out how indented the line is in general.
2985: * If the brace was at the start of the line, we use that;
2986: * otherwise, check out the indentation of the line as
2987: * a whole and then add the "imaginary indent" to that.
2988: */
2989: look = skipwhite(start);
2990: if (*look == '{')
2991: {
2992: getvcol(curwin, trypos, &col, NULL, NULL);
2993: amount = col;
2994: if (*start == '{')
2995: start_brace = BRACE_IN_COL0;
2996: else
2997: start_brace = BRACE_AT_START;
2998: }
2999: else
3000: {
3001: /*
3002: * that opening brace might have been on a continuation
3003: * line. if so, find the start of the line.
3004: */
3005: curwin->w_cursor.lnum = ourscope;
3006:
3007: /*
3008: * position the cursor over the rightmost paren, so that
3009: * matching it will take us back to the start of the line.
3010: */
3011: lnum = ourscope;
3012: if (find_last_paren(start) &&
3013: (trypos = find_match_paren(ind_maxparen,
3014: ind_maxcomment)) != NULL)
3015: lnum = trypos->lnum;
3016:
3017: /*
3018: * It could have been something like
3019: * case 1: if (asdf &&
3020: * ldfd) {
3021: * }
3022: */
3023: amount = skip_label(lnum, &l, ind_maxcomment);
3024:
3025: start_brace = BRACE_AT_END;
3026: }
3027:
3028: /*
3029: * if we're looking at a closing brace, that's where
3030: * we want to be. otherwise, add the amount of room
3031: * that an indent is supposed to be.
3032: */
3033: if (theline[0] == '}')
3034: {
3035: /*
3036: * they may want closing braces to line up with something
3037: * other than the open brace. indulge them, if so.
3038: */
3039: amount += ind_close_extra;
3040: }
3041: else
3042: {
3043: /*
3044: * If we're looking at an "else", try to find an "if"
3045: * to match it with.
3046: * If we're looking at a "while", try to find a "do"
3047: * to match it with.
3048: */
3049: lookfor = 0;
3050: if (iselse(theline))
3051: lookfor = LOOKFOR_IF;
3052: else if (iswhileofdo(theline, cur_curpos.lnum, ind_maxparen))
3053: /* XXX */
3054: lookfor = LOOKFOR_DO;
3055: if (lookfor)
3056: {
3057: curwin->w_cursor.lnum = cur_curpos.lnum;
3058: if (find_match(lookfor, ourscope, ind_maxparen,
3059: ind_maxcomment) == OK)
3060: {
3061: amount = get_indent(); /* XXX */
3062: goto theend;
3063: }
3064: }
3065:
3066: /*
3067: * We get here if we are not on an "while-of-do" or "else" (or
3068: * failed to find a matching "if").
3069: * Search backwards for something to line up with.
3070: * First set amount for when we don't find anything.
3071: */
3072:
3073: /*
3074: * if the '{' is _really_ at the left margin, use the imaginary
3075: * location of a left-margin brace. Otherwise, correct the
3076: * location for ind_open_extra.
3077: */
3078:
3079: if (start_brace == BRACE_IN_COL0) /* '{' is in column 0 */
3080: {
3081: amount = ind_open_left_imag;
3082: }
3083: else
3084: {
3085: if (start_brace == BRACE_AT_END) /* '{' is at end of line */
3086: amount += ind_open_imag;
3087: else
3088: {
3089: amount -= ind_open_extra;
3090: if (amount < 0)
3091: amount = 0;
3092: }
3093: }
3094:
3095: if (iscase(theline)) /* it's a switch() label */
3096: {
3097: lookfor = LOOKFOR_CASE; /* find a previous switch() label */
3098: amount += ind_case;
3099: }
3100: else
3101: {
3102: lookfor = LOOKFOR_ANY;
3103: amount += ind_level; /* ind_level from start of block */
3104: }
3105: scope_amount = amount;
3106: whilelevel = 0;
3107:
3108: /*
3109: * Search backwards. If we find something we recognize, line up
3110: * with that.
3111: *
3112: * if we're looking at an open brace, indent
3113: * the usual amount relative to the conditional
3114: * that opens the block.
3115: */
3116: curwin->w_cursor = cur_curpos;
3117: for (;;)
3118: {
3119: curwin->w_cursor.lnum--;
3120: curwin->w_cursor.col = 0;
3121:
3122: /*
3123: * If we went all the way back to the start of our scope, line
3124: * up with it.
3125: */
3126: if (curwin->w_cursor.lnum <= ourscope)
3127: {
3128: if (lookfor == LOOKFOR_UNTERM)
3129: amount += ind_continuation;
3130: else if (lookfor != LOOKFOR_TERM)
3131: amount = scope_amount;
3132: break;
3133: }
3134:
3135: /*
3136: * If we're in a comment now, skip to the start of the comment.
3137: */ /* XXX */
3138: if ((trypos = find_start_comment(ind_maxcomment)) != NULL)
3139: {
3140: curwin->w_cursor.lnum = trypos->lnum + 1;
3141: continue;
3142: }
3143:
3144: l = ml_get_curline();
3145:
3146: /*
3147: * If this is a switch() label, may line up relative to that.
3148: */
3149: if (iscase(l))
3150: {
3151: /*
3152: * case xx:
3153: * c = 99 + <- this indent plus continuation
3154: *-> here;
3155: */
3156: if (lookfor == LOOKFOR_UNTERM)
3157: {
3158: amount += ind_continuation;
3159: break;
3160: }
3161:
3162: /*
3163: * case xx: <- line up with this case
3164: * x = 333;
3165: * case yy:
3166: */
3167: if (lookfor == LOOKFOR_CASE)
3168: {
3169: /*
3170: * Check that this case label is not for another
3171: * switch()
3172: */ /* XXX */
3173: if ((trypos = find_start_brace(ind_maxcomment)) ==
3174: NULL || trypos->lnum == ourscope)
3175: {
3176: amount = get_indent(); /* XXX */
3177: break;
3178: }
3179: continue;
3180: }
3181:
3182: n = get_indent_nolabel(curwin->w_cursor.lnum); /* XXX */
3183:
3184: /*
3185: * case xx: if (cond) <- line up with this if
3186: * y = y + 1;
3187: * -> s = 99;
3188: *
3189: * case xx:
3190: * if (cond) <- line up with this line
3191: * y = y + 1;
3192: * -> s = 99;
3193: */
3194: if (lookfor == LOOKFOR_TERM)
3195: {
3196: if (n)
3197: amount = n;
3198: break;
3199: }
3200:
3201: /*
3202: * case xx: x = x + 1; <- line up with this x
3203: * -> y = y + 1;
3204: *
3205: * case xx: if (cond) <- line up with this if
3206: * -> y = y + 1;
3207: */
3208: if (n)
3209: {
3210: amount = n;
3211: l = after_label(ml_get_curline());
3212: if (l != NULL && is_cinword(l))
3213: amount += ind_level + ind_no_brace;
3214: break;
3215: }
3216:
3217: /*
3218: * Try to get the indent of a statement before the
3219: * switch label. If nothing is found, line up relative
3220: * to the switch label.
3221: * break; <- may line up with this line
3222: * case xx:
3223: * -> y = 1;
3224: */
3225: scope_amount = get_indent() + ind_case_code; /* XXX */
3226: lookfor = LOOKFOR_ANY;
3227: continue;
3228: }
3229:
3230: /*
3231: * Looking for a switch() label, ignore other lines.
3232: */
3233: if (lookfor == LOOKFOR_CASE)
3234: continue;
3235:
3236: /*
3237: * Ignore jump labels with nothing after them.
3238: */
3239: if (islabel(ind_maxcomment))
3240: {
3241: l = after_label(ml_get_curline());
3242: if (l == NULL || commentorempty(l))
3243: continue;
3244: }
3245:
3246: /*
3247: * Ignore #defines, #if, etc.
3248: * Ignore comment and empty lines.
3249: * (need to get the line again, islabel() may have unlocked it)
3250: */
3251: l = ml_get_curline();
3252: if (ispreproc(l) || commentorempty(l))
3253: continue;
3254:
3255: /*
3256: * What happens next depends on the line being terminated.
3257: */
3258: if (!isterminated(l))
3259: {
3260: /*
3261: * if we're in the middle of a paren thing,
3262: * go back to the line that starts it so
3263: * we can get the right prevailing indent
3264: * if ( foo &&
3265: * bar )
3266: */
3267: /*
3268: * position the cursor over the rightmost paren, so that
3269: * matching it will take us back to the start of the line.
3270: */
3271: (void)find_last_paren(l);
3272: if ((trypos = find_match_paren(ind_maxparen,
3273: ind_maxcomment)) != NULL)
3274: {
3275: /*
3276: * Check if we are on a case label now. This is
3277: * handled above.
3278: * case xx: if ( asdf &&
3279: * asdf)
3280: */
3281: curwin->w_cursor.lnum = trypos->lnum;
3282: l = ml_get_curline();
3283: if (iscase(l))
3284: {
3285: ++curwin->w_cursor.lnum;
3286: continue;
3287: }
3288: }
3289:
3290: /*
3291: * Get indent and pointer to text for current line,
3292: * ignoring any jump label. XXX
3293: */
3294: cur_amount = skip_label(curwin->w_cursor.lnum,
3295: &l, ind_maxcomment);
3296:
3297: /*
3298: * If this is just above the line we are indenting, and it
3299: * starts with a '{', line it up with this line.
3300: * while (not)
3301: * -> {
3302: * }
3303: */
3304: if (lookfor != LOOKFOR_TERM && theline[0] == '{')
3305: {
3306: amount = cur_amount + ind_open_extra;
3307: break;
3308: }
3309:
3310: /*
3311: * Check if we are after an "if", "while", etc.
1.2 downsj 3312: * Also allow "} else".
1.1 downsj 3313: */
1.2 downsj 3314: if (is_cinword(l) || iselse(l))
1.1 downsj 3315: {
3316: /*
3317: * Found an unterminated line after an if (), line up
3318: * with the last one.
3319: * if (cond)
3320: * 100 +
3321: * -> here;
3322: */
3323: if (lookfor == LOOKFOR_UNTERM)
3324: {
3325: amount += ind_continuation;
3326: break;
3327: }
3328:
3329: /*
3330: * If this is just above the line we are indenting, we
3331: * are finished.
3332: * while (not)
3333: * -> here;
3334: * Otherwise this indent can be used when the line
3335: * before this is terminated.
3336: * yyy;
3337: * if (stat)
3338: * while (not)
3339: * xxx;
3340: * -> here;
3341: */
3342: amount = cur_amount;
3343: if (lookfor != LOOKFOR_TERM)
3344: {
3345: amount += ind_level + ind_no_brace;
3346: break;
3347: }
3348:
3349: /*
3350: * Special trick: when expecting the while () after a
3351: * do, line up with the while()
3352: * do
3353: * x = 1;
3354: * -> here
3355: */
3356: l = skipwhite(ml_get_curline());
3357: if (isdo(l))
3358: {
3359: if (whilelevel == 0)
3360: break;
3361: --whilelevel;
3362: }
3363:
3364: /*
3365: * When searching for a terminated line, don't use the
3366: * one between the "if" and the "else".
3367: */
3368: if (iselse(l))
3369: {
3370: if (find_match(LOOKFOR_IF, ourscope,
3371: ind_maxparen, ind_maxcomment) == FAIL)
3372: break;
3373: }
3374: }
3375:
3376: /*
3377: * If we're below an unterminated line that is not an
3378: * "if" or something, we may line up with this line or
3379: * add someting for a continuation line, depending on
3380: * the line before this one.
3381: */
3382: else
3383: {
3384: /*
3385: * Found two unterminated lines on a row, line up with
3386: * the last one.
3387: * c = 99 +
3388: * 100 +
3389: * -> here;
3390: */
3391: if (lookfor == LOOKFOR_UNTERM)
3392: break;
3393:
3394: /*
3395: * Found first unterminated line on a row, may line up
3396: * with this line, remember its indent
3397: * 100 +
3398: * -> here;
3399: */
3400: amount = cur_amount;
3401: if (lookfor != LOOKFOR_TERM)
3402: lookfor = LOOKFOR_UNTERM;
3403: }
3404: }
3405:
3406: /*
3407: * Check if we are after a while (cond);
1.2 downsj 3408: * If so: Ignore until the matching "do".
1.1 downsj 3409: */
3410: /* XXX */
3411: else if (iswhileofdo(l, curwin->w_cursor.lnum, ind_maxparen))
3412: {
3413: /*
3414: * Found an unterminated line after a while ();, line up
3415: * with the last one.
3416: * while (cond);
3417: * 100 + <- line up with this one
3418: * -> here;
3419: */
3420: if (lookfor == LOOKFOR_UNTERM)
3421: {
3422: amount += ind_continuation;
3423: break;
3424: }
3425:
3426: if (whilelevel == 0)
3427: {
3428: lookfor = LOOKFOR_TERM;
3429: amount = get_indent(); /* XXX */
3430: if (theline[0] == '{')
3431: amount += ind_open_extra;
3432: }
3433: ++whilelevel;
3434: }
3435:
3436: /*
3437: * We are after a "normal" statement.
3438: * If we had another statement we can stop now and use the
3439: * indent of that other statement.
3440: * Otherwise the indent of the current statement may be used,
3441: * search backwards for the next "normal" statement.
3442: */
3443: else
3444: {
3445: /*
3446: * Found a terminated line above an unterminated line. Add
3447: * the amount for a continuation line.
3448: * x = 1;
3449: * y = foo +
3450: * -> here;
3451: */
3452: if (lookfor == LOOKFOR_UNTERM)
3453: {
3454: amount += ind_continuation;
3455: break;
3456: }
3457:
3458: /*
3459: * Found a terminated line above a terminated line or "if"
3460: * etc. line. Use the amount of the line below us.
3461: * x = 1; x = 1;
3462: * if (asdf) y = 2;
3463: * while (asdf) ->here;
3464: * here;
3465: * ->foo;
3466: */
3467: if (lookfor == LOOKFOR_TERM)
3468: {
3469: if (whilelevel == 0)
3470: break;
3471: }
3472:
3473: /*
3474: * First line above the one we're indenting is terminated.
3475: * To know what needs to be done look further backward for
3476: * a terminated line.
3477: */
3478: else
3479: {
3480: /*
3481: * position the cursor over the rightmost paren, so
3482: * that matching it will take us back to the start of
3483: * the line. Helps for:
3484: * func(asdr,
3485: * asdfasdf);
3486: * here;
3487: */
3488: l = ml_get_curline();
3489: if (find_last_paren(l) &&
3490: (trypos = find_match_paren(ind_maxparen,
3491: ind_maxcomment)) != NULL)
3492: {
3493: /*
3494: * Check if we are on a case label now. This is
3495: * handled above.
3496: * case xx: if ( asdf &&
3497: * asdf)
3498: */
3499: curwin->w_cursor.lnum = trypos->lnum;
3500: l = ml_get_curline();
3501: if (iscase(l))
3502: {
3503: ++curwin->w_cursor.lnum;
3504: continue;
3505: }
3506: }
3507:
3508: /*
3509: * Get indent and pointer to text for current line,
3510: * ignoring any jump label.
3511: */
3512: amount = skip_label(curwin->w_cursor.lnum,
3513: &l, ind_maxcomment);
3514:
3515: if (theline[0] == '{')
3516: amount += ind_open_extra;
3517: lookfor = LOOKFOR_TERM;
3518:
3519: /*
3520: * If we're at the end of a block, skip to the start of
3521: * that block.
3522: */
3523: if (*skipwhite(l) == '}' &&
3524: (trypos = find_start_brace(ind_maxcomment))
3525: != NULL) /* XXX */
3526: curwin->w_cursor.lnum = trypos->lnum;
3527: }
3528: }
3529: }
3530: }
3531: }
3532:
3533: /*
3534: * ok -- we're not inside any sort of structure at all!
3535: *
3536: * this means we're at the top level, and everything should
3537: * basically just match where the previous line is, except
3538: * for the lines immediately following a function declaration,
3539: * which are K&R-style parameters and need to be indented.
3540: */
3541: else
3542: {
3543: /*
3544: * if our line starts with an open brace, forget about any
3545: * prevailing indent and make sure it looks like the start
3546: * of a function
3547: */
3548:
3549: if (theline[0] == '{')
3550: {
3551: amount = ind_first_open;
3552: }
3553:
3554: /*
3555: * If the NEXT line is a function declaration, the current
3556: * line needs to be indented as a function type spec.
3557: * Don't do this if the current line looks like a comment.
3558: */
3559: else if (cur_curpos.lnum < curbuf->b_ml.ml_line_count &&
3560: !commentorempty(theline) &&
3561: isfuncdecl(ml_get(cur_curpos.lnum + 1)))
3562: {
3563: amount = ind_func_type;
3564: }
3565: else
3566: {
3567: amount = 0;
3568: curwin->w_cursor = cur_curpos;
3569:
3570: /* search backwards until we find something we recognize */
3571:
3572: while (curwin->w_cursor.lnum > 1)
3573: {
3574: curwin->w_cursor.lnum--;
3575: curwin->w_cursor.col = 0;
3576:
3577: l = ml_get_curline();
3578:
3579: /*
3580: * If we're in a comment now, skip to the start of the comment.
3581: */ /* XXX */
3582: if ((trypos = find_start_comment(ind_maxcomment)) != NULL)
3583: {
3584: curwin->w_cursor.lnum = trypos->lnum + 1;
3585: continue;
3586: }
3587:
3588: /*
3589: * If the line looks like a function declaration, and we're
3590: * not in a comment, put it the left margin.
3591: */
3592: if (isfuncdecl(theline))
3593: break;
3594:
3595: /*
3596: * Skip preprocessor directives and blank lines.
3597: */
3598: if (ispreproc(l))
3599: continue;
3600:
3601: if (commentorempty(l))
3602: continue;
3603:
3604: /*
3605: * If the PREVIOUS line is a function declaration, the current
3606: * line (and the ones that follow) needs to be indented as
3607: * parameters.
3608: */
3609: if (isfuncdecl(l))
3610: {
3611: amount = ind_param;
3612: break;
3613: }
3614:
3615: /*
3616: * Doesn't look like anything interesting -- so just
3617: * use the indent of this line.
3618: *
3619: * Position the cursor over the rightmost paren, so that
3620: * matching it will take us back to the start of the line.
3621: */
3622: find_last_paren(l);
3623:
3624: if ((trypos = find_match_paren(ind_maxparen,
3625: ind_maxcomment)) != NULL)
3626: curwin->w_cursor.lnum = trypos->lnum;
3627: amount = get_indent(); /* XXX */
3628: break;
3629: }
3630: }
3631: }
3632:
3633: theend:
3634: /* put the cursor back where it belongs */
3635: curwin->w_cursor = cur_curpos;
3636:
3637: vim_free(linecopy);
3638:
3639: if (amount < 0)
3640: return 0;
3641: return amount;
3642: }
3643:
3644: static int
3645: find_match(lookfor, ourscope, ind_maxparen, ind_maxcomment)
3646: int lookfor;
3647: linenr_t ourscope;
3648: int ind_maxparen;
3649: int ind_maxcomment;
3650: {
3651: char_u *look;
3652: FPOS *theirscope;
3653: char_u *mightbeif;
3654: int elselevel;
3655: int whilelevel;
3656:
3657: if (lookfor == LOOKFOR_IF)
3658: {
3659: elselevel = 1;
3660: whilelevel = 0;
3661: }
3662: else
3663: {
3664: elselevel = 0;
3665: whilelevel = 1;
3666: }
3667:
3668: curwin->w_cursor.col = 0;
3669:
3670: while (curwin->w_cursor.lnum > ourscope + 1)
3671: {
3672: curwin->w_cursor.lnum--;
3673: curwin->w_cursor.col = 0;
3674:
3675: look = skipwhite(ml_get_curline());
3676: if (iselse(look) || isif(look) || isdo(look) ||
3677: iswhileofdo(look, curwin->w_cursor.lnum, ind_maxparen)) /* XXX */
3678: {
3679: /*
3680: * if we've gone outside the braces entirely,
3681: * we must be out of scope...
3682: */
3683: theirscope = find_start_brace(ind_maxcomment); /* XXX */
3684: if (theirscope == NULL)
3685: break;
3686:
3687: /*
3688: * and if the brace enclosing this is further
3689: * back than the one enclosing the else, we're
3690: * out of luck too.
3691: */
3692: if (theirscope->lnum < ourscope)
3693: break;
3694:
3695: /*
3696: * and if they're enclosed in a *deeper* brace,
3697: * then we can ignore it because it's in a
3698: * different scope...
3699: */
3700: if (theirscope->lnum > ourscope)
3701: continue;
3702:
3703: /*
3704: * if it was an "else" (that's not an "else if")
3705: * then we need to go back to another if, so
3706: * increment elselevel
3707: */
3708: look = skipwhite(ml_get_curline());
3709: if (iselse(look))
3710: {
3711: mightbeif = skipwhite(look + 4);
3712: if (!isif(mightbeif))
3713: ++elselevel;
3714: continue;
3715: }
3716:
3717: /*
3718: * if it was a "while" then we need to go back to
3719: * another "do", so increment whilelevel.
3720: */
3721: if (iswhileofdo(look, curwin->w_cursor.lnum, ind_maxparen))/* XXX */
3722: {
3723: ++whilelevel;
3724: continue;
3725: }
3726:
3727: /* If it's an "if" decrement elselevel */
3728: look = skipwhite(ml_get_curline());
3729: if (isif(look))
3730: {
3731: elselevel--;
3732: /*
3733: * When looking for an "if" ignore "while"s that
3734: * get in the way.
3735: */
3736: if (elselevel == 0 && lookfor == LOOKFOR_IF)
3737: whilelevel = 0;
3738: }
3739:
3740: /* If it's a "do" decrement whilelevel */
3741: if (isdo(look))
3742: whilelevel--;
3743:
3744: /*
3745: * if we've used up all the elses, then
3746: * this must be the if that we want!
3747: * match the indent level of that if.
3748: */
3749: if (elselevel <= 0 && whilelevel <= 0)
3750: {
3751: return OK;
3752: }
3753: }
3754: }
3755: return FAIL;
3756: }
3757:
3758: #endif /* CINDENT */
3759:
3760: #ifdef LISPINDENT
3761: int
3762: get_lisp_indent()
3763: {
3764: FPOS *pos, realpos;
3765: long amount = 0;
3766: char_u *that;
3767: colnr_t col;
3768: colnr_t maybe;
3769: colnr_t firsttry;
3770:
3771:
3772: realpos = curwin->w_cursor;
3773: curwin->w_cursor.col = 0;
3774:
3775: if ((pos = findmatch('(')) != NULL)
3776: {
3777: curwin->w_cursor.lnum = pos->lnum;
3778: curwin->w_cursor.col = pos->col;
3779: col = pos->col;
3780:
3781: that = ml_get_curline();
3782: maybe = get_indent(); /* XXX */
3783:
3784: if (maybe == 0)
3785: amount = 2;
3786: else
3787: {
3788: while (*that && col)
3789: {
3790: amount += lbr_chartabsize(that, (colnr_t)amount);
3791: col--;
3792: that++;
3793: }
3794:
3795: that++;
3796: amount++;
3797: firsttry = amount;
3798:
3799: /*
3800: * Go to the start of the second word.
3801: * If there is no second word, go back to firsttry.
3802: * Also stop at a '('.
3803: */
3804:
3805: while (vim_iswhite(*that))
3806: {
3807: amount += lbr_chartabsize(that, (colnr_t)amount);
3808: that++;
3809: }
3810: while (*that && !vim_iswhite(*that) && *that != '(')
3811: {
3812: amount += lbr_chartabsize(that, (colnr_t)amount);
3813: that++;
3814: }
3815: while (vim_iswhite(*that))
3816: {
3817: amount += lbr_chartabsize(that, (colnr_t)amount);
3818: that++;
3819: }
3820: if (! *that)
3821: amount = firsttry;
3822: }
3823: }
3824: else /* no matching '(' found, use indent of previous non-empty line */
3825: {
3826: while (curwin->w_cursor.lnum > 1)
3827: {
3828: --curwin->w_cursor.lnum;
3829: if (!linewhite(curwin->w_cursor.lnum))
3830: break;
3831: }
3832: amount = get_indent(); /* XXX */
3833: }
3834:
3835: curwin->w_cursor = realpos;
3836:
3837: if (amount < 0)
3838: amount = 0;
3839: return (int)amount;
3840: }
3841: #endif /* LISPINDENT */
3842:
3843: #if defined(UNIX) || defined(WIN32) || defined(__EMX__)
3844: /*
3845: * Preserve files and exit.
3846: * When called IObuff must contain a message.
3847: */
3848: void
3849: preserve_exit()
3850: {
3851: BUF *buf;
3852:
3853: #ifdef USE_GUI
3854: if (gui.in_use)
3855: {
3856: gui.dying = TRUE;
3857: trash_output_buf(); /* trash any pending output */
3858: }
3859: else
3860: #endif
3861: {
3862: windgoto((int)Rows - 1, 0);
3863:
3864: /*
3865: * Switch terminal mode back now, so these messages end up on the
3866: * "normal" screen (if there are two screens).
3867: */
3868: settmode(0);
3869: #ifdef WIN32
3870: if (can_end_termcap_mode(FALSE) == TRUE)
3871: #endif
3872: stoptermcap();
3873: flushbuf();
3874: }
3875:
3876: outstr(IObuff);
3877: screen_start(); /* don't know where cursor is now */
3878: flushbuf();
3879:
3880: ml_close_notmod(); /* close all not-modified buffers */
3881:
3882: for (buf = firstbuf; buf != NULL; buf = buf->b_next)
3883: {
3884: if (buf->b_ml.ml_mfp != NULL && buf->b_ml.ml_mfp->mf_fname != NULL)
3885: {
3886: OUTSTR("Vim: preserving files...\n");
3887: screen_start(); /* don't know where cursor is now */
3888: flushbuf();
3889: ml_sync_all(FALSE, FALSE); /* preserve all swap files */
3890: break;
3891: }
3892: }
3893:
3894: ml_close_all(FALSE); /* close all memfiles, without deleting */
3895:
3896: OUTSTR("Vim: Finished.\n");
3897:
3898: getout(1);
3899: }
3900: #endif /* defined(UNIX) || defined(WIN32) || defined(__EMX__) */
3901:
3902: /*
3903: * return TRUE if "fname" exists.
3904: */
3905: int
3906: vim_fexists(fname)
3907: char_u *fname;
3908: {
3909: struct stat st;
3910:
3911: if (stat((char *)fname, &st))
3912: return FALSE;
3913: return TRUE;
3914: }
3915:
3916: /*
3917: * Check for CTRL-C pressed, but only once in a while.
3918: * Should be used instead of mch_breakcheck() for functions that check for
3919: * each line in the file. Calling mch_breakcheck() each time takes too much
3920: * time, because it can be a system call.
3921: */
3922:
3923: #ifndef BREAKCHECK_SKIP
3924: # define BREAKCHECK_SKIP 32
3925: #endif
3926:
3927: void
3928: line_breakcheck()
3929: {
3930: static int count = 0;
3931:
3932: if (++count == BREAKCHECK_SKIP)
3933: {
3934: count = 0;
3935: mch_breakcheck();
3936: }
3937: }
3938:
3939: /*
3940: * Free the list of files returned by ExpandWildCards() or other expansion
3941: * functions.
3942: */
3943: void
3944: FreeWild(num, file)
3945: int num;
3946: char_u **file;
3947: {
3948: if (file == NULL || num == 0)
3949: return;
3950: #if defined(__EMX__) && defined(__ALWAYS_HAS_TRAILING_NULL_POINTER) /* XXX */
3951: /*
3952: * Is this still OK for when other functions thatn ExpandWildCards() have
3953: * been used???
3954: */
3955: _fnexplodefree((char **)file);
3956: #else
3957: while (num--)
3958: vim_free(file[num]);
3959: vim_free(file);
3960: #endif
3961: }
3962: