GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/mg/cmode.c Lines: 0 159 0.0 %
Date: 2017-11-07 Branches: 0 142 0.0 %

Line Branch Exec Source
1
/* $OpenBSD: cmode.c,v 1.16 2015/09/26 21:51:58 jasper Exp $ */
2
/*
3
 * This file is in the public domain.
4
 *
5
 * Author: Kjell Wooding <kjell@openbsd.org>
6
 */
7
8
/*
9
 * Implement an non-irritating KNF-compliant mode for editing
10
 * C code.
11
 */
12
13
#include <sys/queue.h>
14
#include <ctype.h>
15
#include <signal.h>
16
#include <stdio.h>
17
18
#include "def.h"
19
#include "funmap.h"
20
#include "kbd.h"
21
22
/* Pull in from modes.c */
23
extern int changemode(int, int, char *);
24
25
static int cc_strip_trailp = TRUE;	/* Delete Trailing space? */
26
static int cc_basic_indent = 8;		/* Basic Indent multiple */
27
static int cc_cont_indent = 4;		/* Continued line indent */
28
static int cc_colon_indent = -8;	/* Label / case indent */
29
30
static int getmatch(int, int);
31
static int getindent(const struct line *, int *);
32
static int in_whitespace(struct line *, int);
33
static int findcolpos(const struct buffer *, const struct line *, int);
34
static struct line *findnonblank(struct line *);
35
static int isnonblank(const struct line *, int);
36
37
void cmode_init(void);
38
int cc_comment(int, int);
39
40
/* Keymaps */
41
42
static PF cmode_brace[] = {
43
	cc_brace,	/* } */
44
};
45
46
static PF cmode_cCP[] = {
47
	compile,		/* C-c P */
48
};
49
50
51
static PF cmode_cc[] = {
52
	NULL,		/* ^C */
53
	rescan,		/* ^D */
54
	rescan,		/* ^E */
55
	rescan,		/* ^F */
56
	rescan,		/* ^G */
57
	rescan,		/* ^H */
58
	cc_tab,		/* ^I */
59
	rescan,		/* ^J */
60
	rescan,		/* ^K */
61
	rescan,		/* ^L */
62
	cc_lfindent,	/* ^M */
63
};
64
65
static PF cmode_spec[] = {
66
	cc_char,	/* : */
67
};
68
69
static struct KEYMAPE (1) cmode_cmap = {
70
	1,
71
	1,
72
	rescan,
73
	{
74
		{ 'P', 'P', cmode_cCP, NULL }
75
	}
76
};
77
78
static struct KEYMAPE (3) cmodemap = {
79
	3,
80
	3,
81
	rescan,
82
	{
83
		{ CCHR('C'), CCHR('M'), cmode_cc, (KEYMAP *) &cmode_cmap },
84
		{ ':', ':', cmode_spec, NULL },
85
		{ '}', '}', cmode_brace, NULL }
86
	}
87
};
88
89
/* Funtion, Mode hooks */
90
91
void
92
cmode_init(void)
93
{
94
	funmap_add(cmode, "c-mode");
95
	funmap_add(cc_char, "c-handle-special-char");
96
	funmap_add(cc_brace, "c-handle-special-brace");
97
	funmap_add(cc_tab, "c-tab-or-indent");
98
	funmap_add(cc_indent, "c-indent");
99
	funmap_add(cc_lfindent, "c-indent-and-newline");
100
	maps_add((KEYMAP *)&cmodemap, "c");
101
}
102
103
/*
104
 * Enable/toggle c-mode
105
 */
106
int
107
cmode(int f, int n)
108
{
109
	return(changemode(f, n, "c"));
110
}
111
112
/*
113
 * Handle special C character - selfinsert then indent.
114
 */
115
int
116
cc_char(int f, int n)
117
{
118
	if (n < 0)
119
		return (FALSE);
120
	if (selfinsert(FFRAND, n) == FALSE)
121
		return (FALSE);
122
	return (cc_indent(FFRAND, n));
123
}
124
125
/*
126
 * Handle special C character - selfinsert then indent.
127
 */
128
int
129
cc_brace(int f, int n)
130
{
131
	if (n < 0)
132
		return (FALSE);
133
	if (showmatch(FFRAND, 1) == FALSE)
134
		return (FALSE);
135
	return (cc_indent(FFRAND, n));
136
}
137
138
139
/*
140
 * If we are in the whitespace at the beginning of the line,
141
 * simply act as a regular tab. If we are not, indent
142
 * current line according to whitespace rules.
143
 */
144
int
145
cc_tab(int f, int n)
146
{
147
	int inwhitep = FALSE;	/* In leading whitespace? */
148
149
	inwhitep = in_whitespace(curwp->w_dotp, llength(curwp->w_dotp));
150
151
	/* If empty line, or in whitespace */
152
	if (llength(curwp->w_dotp) == 0 || inwhitep)
153
		return (selfinsert(f, n));
154
155
	return (cc_indent(FFRAND, 1));
156
}
157
158
/*
159
 * Attempt to indent current line according to KNF rules.
160
 */
161
int
162
cc_indent(int f, int n)
163
{
164
	int pi, mi;			/* Previous indents (mi is ignored) */
165
	int ci;				/* current indent */
166
	struct line *lp;
167
	int ret;
168
169
	if (n < 0)
170
		return (FALSE);
171
172
	undo_boundary_enable(FFRAND, 0);
173
	if (cc_strip_trailp)
174
		deltrailwhite(FFRAND, 1);
175
176
	/*
177
	 * Search backwards for a non-blank, non-preprocessor,
178
	 * non-comment line
179
	 */
180
181
	lp = findnonblank(curwp->w_dotp);
182
183
	pi = getindent(lp, &mi);
184
185
	/* Strip leading space on current line */
186
	delleadwhite(FFRAND, 1);
187
	/* current indent is computed only to current position */
188
	(void)getindent(curwp->w_dotp, &ci);
189
190
	if (pi + ci < 0)
191
		ret = indent(FFOTHARG, 0);
192
	else
193
		ret = indent(FFOTHARG, pi + ci);
194
195
	undo_boundary_enable(FFRAND, 1);
196
197
	return (ret);
198
}
199
200
/*
201
 * Indent-and-newline (technically, newline then indent)
202
 */
203
int
204
cc_lfindent(int f, int n)
205
{
206
	if (n < 0)
207
		return (FALSE);
208
	if (enewline(FFRAND, 1) == FALSE)
209
		return (FALSE);
210
	return (cc_indent(FFRAND, n));
211
}
212
213
/*
214
 * Get the level of indention after line lp is processed
215
 * Note getindent has two returns:
216
 * curi = value if indenting current line.
217
 * return value = value affecting subsequent lines.
218
 */
219
static int
220
getindent(const struct line *lp, int *curi)
221
{
222
	int lo, co;		/* leading space,  current offset*/
223
	int nicol = 0;		/* position count */
224
	int c = '\0';		/* current char */
225
	int newind = 0;		/* new index value */
226
	int stringp = FALSE;	/* in string? */
227
	int escp = FALSE;	/* Escape char? */
228
	int lastc = '\0';	/* Last matched string delimeter */
229
	int nparen = 0;		/* paren count */
230
	int obrace = 0;		/* open brace count */
231
	int cbrace = 0;		/* close brace count */
232
	int firstnwsp = FALSE;	/* First nonspace encountered? */
233
	int colonp = FALSE;	/* Did we see a colon? */
234
	int questionp = FALSE;	/* Did we see a question mark? */
235
	int slashp = FALSE;	/* Slash? */
236
	int astp = FALSE;	/* Asterisk? */
237
	int cpos = -1;		/* comment position */
238
	int cppp  = FALSE;	/* Preprocessor command? */
239
240
	*curi = 0;
241
242
	/* Compute leading space */
243
	for (lo = 0; lo < llength(lp); lo++) {
244
		if (!isspace(c = lgetc(lp, lo)))
245
			break;
246
		if (c == '\t'
247
#ifdef NOTAB
248
		    && !(curbp->b_flag & BFNOTAB)
249
#endif /* NOTAB */
250
		    ) {
251
			nicol |= 0x07;
252
		}
253
		nicol++;
254
	}
255
256
	/* If last line was blank, choose 0 */
257
	if (lo == llength(lp))
258
		nicol = 0;
259
260
	newind = 0;
261
	/* Compute modifiers */
262
	for (co = lo; co < llength(lp); co++) {
263
		c = lgetc(lp, co);
264
		/* We have a non-whitespace char */
265
		if (!firstnwsp && !isspace(c)) {
266
			if (c == '#')
267
				cppp = TRUE;
268
			firstnwsp = TRUE;
269
		}
270
		if (c == '\\')
271
			escp = !escp;
272
		else if (stringp) {
273
			if (!escp && (c == '"' || c == '\'')) {
274
				/* unescaped string char */
275
				if (getmatch(c, lastc))
276
					stringp = FALSE;
277
			}
278
		} else if (c == '"' || c == '\'') {
279
			stringp = TRUE;
280
			lastc = c;
281
		} else if (c == '(') {
282
			nparen++;
283
		} else if (c == ')') {
284
			nparen--;
285
		} else if (c == '{') {
286
			obrace++;
287
			firstnwsp = FALSE;
288
		} else if (c == '}') {
289
			cbrace++;
290
		} else if (c == '?') {
291
			questionp = TRUE;
292
		} else if (c == ':') {
293
			/* ignore (foo ? bar : baz) construct */
294
			if (!questionp)
295
				colonp = TRUE;
296
		} else if (c == '/') {
297
			/* first nonwhitespace? -> indent */
298
			if (firstnwsp) {
299
				/* If previous char asterisk -> close */
300
				if (astp)
301
					cpos = -1;
302
				else
303
					slashp = TRUE;
304
			}
305
		} else if (c == '*') {
306
			/* If previous char slash -> open */
307
			if (slashp)
308
				cpos = co;
309
			else
310
				astp = TRUE;
311
		} else if (firstnwsp) {
312
			firstnwsp = FALSE;
313
		}
314
315
		/* Reset matches that apply to next character only */
316
		if (c != '\\')
317
			escp = FALSE;
318
		if (c != '*')
319
			astp = FALSE;
320
		if (c != '/')
321
			slashp = FALSE;
322
	}
323
	/*
324
	 * If not terminated with a semicolon, and brace or paren open.
325
	 * we continue
326
	 */
327
	if (colonp) {
328
		*curi += cc_colon_indent;
329
		newind -= cc_colon_indent;
330
	}
331
332
	*curi -= (cbrace) * cc_basic_indent;
333
	newind += obrace * cc_basic_indent;
334
335
	if (nparen < 0)
336
		newind -= cc_cont_indent;
337
	else if (nparen > 0)
338
		newind += cc_cont_indent;
339
340
	*curi += nicol;
341
342
	/* Ignore preprocessor. Otherwise, add current column */
343
	if (cppp) {
344
		newind = nicol;
345
		*curi = 0;
346
	} else {
347
		newind += nicol;
348
	}
349
350
	if (cpos != -1)
351
		newind = findcolpos(curbp, lp, cpos);
352
353
	return (newind);
354
}
355
356
/*
357
 * Given a delimeter and its purported mate, tell us if they
358
 * match.
359
 */
360
static int
361
getmatch(int c, int mc)
362
{
363
	int match = FALSE;
364
365
	switch (c) {
366
	case '"':
367
		match = (mc == '"');
368
		break;
369
	case '\'':
370
		match = (mc == '\'');
371
		break;
372
	case '(':
373
		match = (mc == ')');
374
		break;
375
	case '[':
376
		match = (mc == ']');
377
		break;
378
	case '{':
379
		match = (mc == '}');
380
		break;
381
	}
382
383
	return (match);
384
}
385
386
static int
387
in_whitespace(struct line *lp, int len)
388
{
389
	int lo;
390
	int inwhitep = FALSE;
391
392
	for (lo = 0; lo < len; lo++) {
393
		if (!isspace(lgetc(lp, lo)))
394
			break;
395
		if (lo == len - 1)
396
			inwhitep = TRUE;
397
	}
398
399
	return (inwhitep);
400
}
401
402
403
/* convert a line/offset pair to a column position (for indenting) */
404
static int
405
findcolpos(const struct buffer *bp, const struct line *lp, int lo)
406
{
407
	int	col, i, c;
408
	char tmp[5];
409
410
	/* determine column */
411
	col = 0;
412
413
	for (i = 0; i < lo; ++i) {
414
		c = lgetc(lp, i);
415
		if (c == '\t'
416
#ifdef NOTAB
417
		    && !(bp->b_flag & BFNOTAB)
418
#endif /* NOTAB */
419
			) {
420
			col |= 0x07;
421
			col++;
422
		} else if (ISCTRL(c) != FALSE)
423
			col += 2;
424
		else if (isprint(c)) {
425
			col++;
426
		} else {
427
			col += snprintf(tmp, sizeof(tmp), "\\%o", c);
428
		}
429
430
	}
431
	return (col);
432
}
433
434
/*
435
 * Find a non-blank line, searching backwards from the supplied line pointer.
436
 * For C, nonblank is non-preprocessor, non C++, and accounts
437
 * for complete C-style comments.
438
 */
439
static struct line *
440
findnonblank(struct line *lp)
441
{
442
	int lo;
443
	int nonblankp = FALSE;
444
	int commentp = FALSE;
445
	int slashp;
446
	int astp;
447
	int c;
448
449
	while (lback(lp) != curbp->b_headp && (commentp || !nonblankp)) {
450
		lp = lback(lp);
451
		slashp = FALSE;
452
		astp = FALSE;
453
454
		/* Potential nonblank? */
455
		nonblankp = isnonblank(lp, llength(lp));
456
457
		/*
458
		 * Search from end, removing complete C-style
459
		 * comments. If one is found, ignore it and
460
		 * test for nonblankness from where it starts.
461
		 */
462
		for (lo = llength(lp) - 1; lo >= 0; lo--) {
463
			if (!isspace(c = lgetc(lp, lo))) {
464
				if (commentp) { /* find comment "open" */
465
					if (c == '*')
466
						astp = TRUE;
467
					else if (astp && c == '/') {
468
						commentp = FALSE;
469
						/* whitespace to here? */
470
						nonblankp = isnonblank(lp, lo);
471
					}
472
				} else { /* find comment "close" */
473
					if (c == '/')
474
						slashp = TRUE;
475
					else if (slashp && c == '*')
476
						/* found a comment */
477
						commentp = TRUE;
478
				}
479
			}
480
		}
481
	}
482
483
	/* Rewound to start of file? */
484
	if (lback(lp) == curbp->b_headp && !nonblankp)
485
		return (curbp->b_headp);
486
487
	return (lp);
488
}
489
490
/*
491
 * Given a line, scan forward to 'omax' and determine if we
492
 * are all C whitespace.
493
 * Note that preprocessor directives and C++-style comments
494
 * count as whitespace. C-style comments do not, and must
495
 * be handled elsewhere.
496
 */
497
static int
498
isnonblank(const struct line *lp, int omax)
499
{
500
	int nonblankp = FALSE;		/* Return value */
501
	int slashp = FALSE;		/* Encountered slash */
502
	int lo;				/* Loop index */
503
	int c;				/* char being read */
504
505
	/* Scan from front for preprocessor, C++ comments */
506
	for (lo = 0; lo < omax; lo++) {
507
		if (!isspace(c = lgetc(lp, lo))) {
508
			/* Possible nonblank line */
509
			nonblankp = TRUE;
510
			/* skip // and # starts */
511
			if (c == '#' || (slashp && c == '/')) {
512
				nonblankp = FALSE;
513
				break;
514
			} else if (!slashp && c == '/') {
515
				slashp = TRUE;
516
				continue;
517
			}
518
		}
519
		slashp = FALSE;
520
	}
521
	return (nonblankp);
522
}