xref: /csrg-svn/usr.bin/indent/io.c (revision 33767)
1 /*
2  * Copyright (c) 1980 Regents of the University of California.
3  * Copyright (c) 1976 Board of Trustees of the University of Illinois.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms are permitted
7  * provided that this notice is preserved and that due credit is given
8  * to the University of California at Berkeley and the University of
9  * Illinois at Urbana.  The name of either University may not be used
10  * to endorse or promote products derived from this software without
11  * specific prior written permission. This software is provided
12  * ``as is'' without express or implied warranty.
13  */
14 
15 #ifndef lint
16 static char sccsid[] = "@(#)io.c	5.6 (Berkeley) 03/22/88";
17 #endif /* not lint */
18 
19 /*
20  * FILE NAME:
21  *	io.c
22  * PURPOSE:
23  *	Contains routines to handle i/o related stuff for indent.
24  * GLOBALS:
25  *	None
26  * FUNCTIONS:
27  *	dump_line
28  *	fill_buffer
29  *	pad_output
30  *	count_spaces
31  *	eqin
32  *	cmp
33  *
34  */
35 /*-
36  *
37  *			  Copyright (C) 1976
38  *				by the
39  *			  Board of Trustees
40  *				of the
41  *			University of Illinois
42  *
43  *			 All rights reserved
44  *
45  *
46  * NAME:
47  *	dump_line
48  *
49  * FUNCTION:
50  *	Does the actual printing of the stored up line
51  *
52  * ALGORITHM:
53  *	For each of the label, code, and comment sections which are used on
54  *	this line:
55  *
56  *	1) Use pad_output to get the section aligned properly.
57  *	2) write the section
58  *
59  *	The indentation level used for the code is set by ps.ind_level.  After
60  *	printing, ps.ind_level is set to ps.i_l_follow.
61  *
62  *	An extra level of indentation is added if ps.ind_stmt is 1.  After
63  *	printing, ps.ind_stmt is set to 1 iff the line just printed has an
64  *	unterminated, non-declaration statement.
65  *
66  * HISTORY:
67  *	initial coding 	November 1976	D A Willcox of CAC
68  *
69  */
70 #include "indent_globs.h"
71 
72 
73 
74 int         ff = 014;		/* used to write a form feed */
75 int         comment_open;
76 static      paren_target;
77 
78 dump_line()
79 {				/* dump_line is the routine that actually
80 				 * effects the printing of the new source.
81 				 * It prints the label section, followed
82 				 * by the code section with the
83 				 * appropriate nesting level, followed by
84 				 * any comments */
85     register int cur_col,
86                 temp_col,
87                 target_col;
88 
89     if (ps.procname[0]) {
90 	if (troff)
91 	    fprintf(output, ".Pr \"%s\"\n", ps.procname);
92 	ps.ind_level = 0;
93 	ps.procname[0] = 0;
94     }
95     if (s_code == e_code && s_lab == e_lab && s_com == e_com) {
96 	if (suppress_blanklines>0) suppress_blanklines--;
97 	else {
98 	ps.bl_line = true;
99 	n_real_blanklines++;
100 	}
101     }
102     else if (!inhibit_formatting) {
103 	suppress_blanklines = 0;
104 	ps.bl_line = false;
105 	if (prefix_blankline_requested)
106 	    if (swallow_optional_blanklines) {
107 		if (n_real_blanklines == 1)
108 		    n_real_blanklines = 0;
109 	    }
110 	    else {
111 		if (n_real_blanklines == 0)
112 		    n_real_blanklines = 1;
113 	    }
114 	while (--n_real_blanklines >= 0)
115 	    putc('\n', output);
116 	n_real_blanklines = 0;
117 	if (ps.ind_level == 0)
118 	    ps.ind_stmt = 0;	/* this is a class A kludge. dont do
119 				 * additional statement indentation if we
120 				 * are at bracket level 0 */
121 
122 	if (e_lab != s_lab || e_code != s_code)
123 	    ++code_lines;	/* keep count of lines with code */
124 
125 
126 	if (e_lab != s_lab) {	/* print lab, if any */
127 	    if (comment_open) {
128 		comment_open = 0;
129 		fprintf(output, ".*/\n");
130 	    }
131 	    while (e_lab > s_lab && (e_lab[-1] == ' ' || e_lab[-1] == '\t'))
132 		e_lab--;
133 	    cur_col = pad_output(1, compute_label_target());
134 	    fprintf(output, "%.*s", e_lab - s_lab, s_lab);
135 	    cur_col = count_spaces(cur_col, s_lab);
136 	}
137 	else
138 	    cur_col = 1;	/* there is no label section */
139 
140 	ps.pcase = false;
141 
142 	if (s_code != e_code) {	/* print code section, if any */
143 	    register char *p;
144 
145 	    if (comment_open) {
146 		comment_open = 0;
147 		fprintf(output, ".*/\n");
148 	    }
149 	    target_col = compute_code_target();
150 	    {
151 		register    i;
152 
153 		for (i = 0; i < ps.p_l_follow; i++)
154 		    if (ps.paren_indents[i] >= 0)
155 			ps.paren_indents[i] = -(ps.paren_indents[i] + target_col);
156 	    }
157 	    cur_col = pad_output(cur_col, target_col);
158 	    for (p = s_code; p < e_code; p++)
159 		if (*p == (char)0200)
160 		    fprintf(output, "%d", target_col * 7);
161 		else
162 		    putc(*p, output);
163 	    cur_col = count_spaces(cur_col, s_code);
164 	}
165 	if (s_com != e_com)
166 	    if (troff) {
167 		register char *p;
168 
169 		if (e_com[-1] == '/' && e_com[-2] == '*')
170 		    e_com -= 2;
171 		while (e_com > s_com && e_com[-1] == ' ')
172 		    e_com--;
173 		*e_com = 0;
174 		p = s_com;
175 		while (*p == ' ')
176 		    p++;
177 		if (p[0] == '/' && p[1] == '*')
178 		    p += 2;
179 		else if (p[0] == '*')
180 		    p += p[1] == '/' ? 2 : 1;
181 		while (*p == ' ')
182 		    p++;
183 		if (*p == 0)
184 		    goto inhibit_newline;
185 		if (!comment_open) {
186 		    if ('a' <= *p && *p <= 'z')
187 			*p = *p + 'A' - 'a';
188 		    if (s_code != e_code || s_lab != e_lab) {
189 			fprintf(output, "\\c\n./* %dp 1 %dp\n",
190 				ps.com_col * 7, target_col * 7);
191 		    }
192 		    else
193 			fprintf(output, "./* %dp 0 %dp\n",
194 				ps.com_col * 7, target_col * 7);
195 		}
196 		comment_open = 1;
197 		while (*p) {
198 		    if (*p == BACKSLASH)
199 			putc(BACKSLASH, output);
200 		    putc(*p++, output);
201 		}
202 	    }
203 	    else {		/* print comment, if any */
204 		register    target = ps.com_col;
205 		register char *com_st = s_com;
206 
207 		target += ps.comment_delta;
208 		while (target <= 0)
209 		    if (*s_com == ' ')
210 			target++, s_com++;
211 		    else if (*s_com == '\t')
212 			target = ((target - 1) & ~7) + 9, s_com++;
213 		    else
214 			target = 1;
215 		if (cur_col > target) {	/* if comment cant fit on this
216 					 * line, put it on next line */
217 		    putc('\n', output);
218 		    cur_col = 1;
219 		    ++ps.out_lines;
220 		}
221 		cur_col = pad_output(cur_col, target);
222 		if (!ps.box_com) {
223 		    if (star_comment_cont && com_st[1] != '*')
224 			if (com_st[1] == ' ' && com_st[0] == ' ')
225 			    com_st[1] = '*';
226 			else
227 			    fwrite(" * ", com_st[0] == '\t' ? 2 : com_st[0] == '*' ? 1 : 3, 1, output);
228 		}
229 		fwrite(com_st, e_com - com_st, 1, output);
230 		ps.comment_delta = ps.n_comment_delta;
231 		cur_col = count_spaces(cur_col, com_st);
232 		++ps.com_lines;	/* count lines with comments */
233 	    }
234 	if (ps.use_ff)
235 	    putc('\014', output);
236 	else
237 	    putc('\n', output);
238 inhibit_newline:
239 	++ps.out_lines;
240 	if (ps.just_saw_decl == 1 && blanklines_after_declarations) {
241 	    prefix_blankline_requested = 1;
242 	    ps.just_saw_decl = 0;
243 	}
244 	else
245 	    prefix_blankline_requested = postfix_blankline_requested;
246 	postfix_blankline_requested = 0;
247     }
248     ps.decl_on_line = ps.in_decl;	/* if we are in the middle of a
249 					 * declaration, remember that fact
250 					 * for proper comment indentation */
251     ps.ind_stmt = ps.in_stmt & ~ps.in_decl;	/* next line should be
252 						 * indented if we have not
253 						 * completed this stmt and
254 						 * if we are not in the
255 						 * middle of a declaration */
256     ps.use_ff = false;
257     ps.dumped_decl_indent = 0;
258     *(e_lab = s_lab) = '\0';	/* reset buffers */
259     *(e_code = s_code) = '\0';
260     *(e_com = s_com) = '\0';
261     ps.ind_level = ps.i_l_follow;
262     ps.paren_level = ps.p_l_follow;
263     paren_target = -ps.paren_indents[ps.paren_level - 1];
264     return;
265 };
266 
267 compute_code_target() {
268     register    target_col = ps.ind_size * ps.ind_level + 1;
269 
270     if (ps.paren_level)
271 	if (!lineup_to_parens)
272 	    target_col += continuation_indent * ps.paren_level;
273 	else {
274 	    register    w;
275 	    register    t = paren_target;
276 
277 	    if ((w = count_spaces(t, s_code) - max_col) > 0
278 		&& count_spaces(target_col, s_code) <= max_col) {
279 		t -= w + 1;
280 		if (t > target_col)
281 		    target_col = t;
282 	    }
283 	    else
284 		target_col = t;
285 	}
286     else if (ps.ind_stmt)
287 	target_col += continuation_indent;
288     return target_col;
289 }
290 
291 compute_label_target()
292 {
293     return
294 	ps.pcase ? (int) (case_ind * ps.ind_size) +1
295 	: *s_lab == '#' ? 1
296 	: ps.ind_size * (ps.ind_level - label_offset) +1;
297 }
298 
299 
300 /*
301  * Copyright (C) 1976 by the Board of Trustees of the University of
302  * Illinois
303  *
304  * All rights reserved
305  *
306  *
307  * NAME: fill_buffer
308  *
309  * FUNCTION: Reads one block of input into input_buffer
310  *
311  * HISTORY: initial coding 	November 1976	D A Willcox of CAC 1/7/77
312  * A Willcox of CAC	Added check for switch back to partly full input
313  * buffer from temporary buffer
314  *
315  */
316 int
317 fill_buffer()
318 {				/* this routine reads stuff from the input */
319     int         count;
320     register char *p;
321     register int i;
322     register FILE *f = input;
323 
324     if (bp_save != 0) {		/* there is a partly filled input buffer
325 				 * left */
326 	buf_ptr = bp_save;	/* dont read anything, just switch buffers */
327 	buf_end = be_save;
328 	bp_save = be_save = 0;
329 	if (buf_ptr < buf_end)
330 	    return;		/* only return if there is really
331 				 * something in this buffer */
332     }
333     p = in_buffer;
334     buf_ptr = p;
335     while ((*p++ = i = getc(f)) != EOF && i != '\n');
336     if (i == EOF) {
337 	p[-1] = ' ';
338 	*p++ = '\n';
339 	had_eof = true;
340     }
341     buf_end = p;
342     if (p[-2] == '/' && p[-3] == '*') {
343 	if (in_buffer[3] == 'I' && strncmp(in_buffer, "/**INDENT**", 11) == 0)
344 	    fill_buffer();	/* flush indent error message */
345 	else {
346 	    int         com = 0;
347 
348 	    p = in_buffer;
349 	    while (*p == ' ' || *p == '\t')
350 		p++;
351 	    if (*p == '/' && p[1] == '*') {
352 		p += 2;
353 		while (*p == ' ' || *p == '\t')
354 		    p++;
355 		if (p[0] == 'I' && p[1] == 'N' && p[2] == 'D' && p[3] == 'E'
356 		    && p[4] == 'N' && p[5] == 'T') {
357 		    p += 6;
358 		    while (*p == ' ' || *p == '\t')
359 			p++;
360 		    if (*p == '*')
361 			com = 1;
362 		    else if (*p == 'O')
363 			if (*++p == 'N')
364 			    p++, com = 1;
365 			else if (*p == 'F' && *++p == 'F')
366 			    p++, com = 2;
367 		    while (*p == ' ' || *p == '\t')
368 			p++;
369 		    if (p[0] == '*' && p[1] == '/' && p[2] == '\n' && com) {
370 			if (s_com != e_com || s_lab != e_lab || s_code != e_code)
371 			    dump_line();
372 			if (!(inhibit_formatting = com - 1)) {
373 			    n_real_blanklines = 0;
374 			    postfix_blankline_requested = 0;
375 			    prefix_blankline_requested = 0;
376 			    suppress_blanklines = 1;
377 			}
378 		    }
379 		}
380 	    }
381 	}
382     }
383     if (inhibit_formatting) {
384 	p = in_buffer;
385 	do
386 	    putc(*p, output);
387 	while (*p++ != '\n');
388     }
389     return;
390 };
391 
392 /*
393  * Copyright (C) 1976 by the Board of Trustees of the University of
394  * Illinois
395  *
396  * All rights reserved
397  *
398  *
399  * NAME: pad_output
400  *
401  * FUNCTION: Writes tabs and spaces to move the current column up to the
402  * desired position.
403  *
404  * ALGORITHM: Put tabs and/or blanks into pobuf, then write pobuf.
405  *
406  * PARAMETERS: current		integer		The current column target
407  * nteger		The desired column
408  *
409  * RETURNS: Integer value of the new column.  (If current >= target, no
410  * action is taken, and current is returned.
411  *
412  * GLOBALS: None
413  *
414  * CALLS: write (sys)
415  *
416  * CALLED BY: dump_line
417  *
418  * HISTORY: initial coding 	November 1976	D A Willcox of CAC
419  *
420  */
421 pad_output(current, target)	/* writes tabs and blanks (if necessary)
422 				 * to get the current output position up
423 				 * to the target column */
424     int         current;	/* the current column value */
425     int         target;		/* position we want it at */
426 {
427     register int curr;		/* internal column pointer */
428     register int tcur;
429 
430     if (troff)
431 	fprintf(output, "\\h'|%dp'", (target - 1) * 7);
432     else {
433 	if (current >= target)
434 	    return (current);	/* line is already long enough */
435 	curr = current;
436 	while ((tcur = ((curr - 1) & tabmask) + tabsize + 1) <= target) {
437 	    putc('\t', output);
438 	    curr = tcur;
439 	}
440 	while (curr++ < target)
441 	    putc(' ', output);	/* pad with final blanks */
442     }
443     return (target);
444 };
445 
446 /*
447  * Copyright (C) 1976 by the Board of Trustees of the University of
448  * Illinois
449  *
450  * All rights reserved
451  *
452  *
453  * NAME: count_spaces
454  *
455  * FUNCTION: Find out where printing of a given string will leave the current
456  * character position on output.
457  *
458  * ALGORITHM: Run thru input string and add appropriate values to current
459  * position.
460  *
461  * RETURNS: Integer value of position after printing "buffer" starting in
462  * column "current".
463  *
464  * HISTORY: initial coding 	November 1976	D A Willcox of CAC
465  *
466  */
467 int
468 count_spaces(current, buffer)
469 
470 /*
471  * this routine figures out where the character position will be after
472  * printing the text in buffer starting at column "current"
473  */
474     int         current;
475     char       *buffer;
476 {
477     register char *buf;		/* used to look thru buffer */
478     register int cur;		/* current character counter */
479 
480     cur = current;
481 
482     for (buf = buffer; *buf != '\0'; ++buf) {
483 	switch (*buf) {
484 
485 	    case '\n':
486 	    case 014:		/* form feed */
487 		cur = 1;
488 		break;
489 
490 	    case '\t':
491 		cur = ((cur - 1) & tabmask) + tabsize + 1;
492 		break;
493 
494 	    case '':		/* this is a backspace */
495 		--cur;
496 		break;
497 
498 	    default:
499 		++cur;
500 		break;
501 	}			/* end of switch */
502     }				/* end of for loop */
503     return (cur);
504 };
505 
506 int	found_err;
507 diag(level, msg, a, b)
508 {
509     if (level)
510 	found_err = 1;
511     if (output == stdout) {
512 	fprintf(stdout, "/**INDENT** %s@%d: ", level == 0 ? "Warning" : "Error", line_no);
513 	fprintf(stdout, msg, a, b);
514 	fprintf(stdout, " */\n");
515     }
516     else {
517 	fprintf(stderr, "%s@%d: ", level == 0 ? "Warning" : "Error", line_no);
518 	fprintf(stderr, msg, a, b);
519 	fprintf(stderr, "\n");
520     }
521 }
522