/* * Copyright (c) 2002 D.Ingamells * * File: output.c * Purpose: Interface to the output file for the indent tool. * * History: * 2002-03-04 D.Ingamells Creation. * 2002-11-10 Cristalle Azundris Sabon * Added --preprocessor-indentation (ppi) if set, will indent nested * preprocessor-statements with n spaces per level. overrides -lps. */ #include #include #include #include #include "indent.h" #include "sys.h" #include "globs.h" #include "output.h" RCSTAG_CC ("$Id: output.c,v 1.5 2002/12/12 17:36:49 david Exp $"); static FILE * output = NULL; static BOOLEAN inhibited = 0; static buf_break_st_ty * buf_break_list = NULL; /* Priority mask bits */ static const int boolean_operator = 1; buf_break_st_ty * buf_break = NULL; int out_lines = 0; /* used in output.c indent.c */ int com_lines = 0; /* used in output.c indent.c comments.c */ int prev_target_col_break = 0; int buf_break_used = 0; int preproc_indent = 0; /* * Returns `true' when `b1' is a better place to break the code than `b2'. * `b1' must be newer. * * When `lineup_to_parens' is true, do not break more then 1 level deeper * unless that doesn't cost us "too much" indentation. * What is "too much" is determined in a fuzzy way as follows: * Consider the example, * while (!(!(!(!(!(!(mask * || (((a_very_long_expression_that_cant_be_broken * here we prefer to break after `mask' instead of after `while'. * This is because the `target_col' is pretty close to the break point * of the `while': "mask"->target_col - "while"->col == 15 == "mask"->level * 2 + 1. */ static int better_break ( buf_break_st_ty *b1, const buf_break_st_ty *b2) { static int first_level; int is_better; if (!b2) { first_level = b1->level; b1->first_level = first_level; return 1; } if (b2->target_col >= b2->col + 1) { is_better = true; } else if (settings.honour_newlines && b2->priority_newline) { is_better = false; } else if (settings.honour_newlines && b1->priority_newline) { is_better = true; } else { int only_parens_till_b2 = 0; is_better = (b1->priority > b2->priority); if (is_better) { char *p; for (p = &s_code[b2->offset]; p >= s_code; --p) { if (*p == '!') { --p; } if (*p != '(') { break; } } if (p < s_code) { only_parens_till_b2 = 1; } } if (settings.lineup_to_parens && (b1->level > first_level + 1) && !(only_parens_till_b2 && (b1->target_col <= b2->col + (1 + 2 * b1->level))) && (b1->level > b2->level)) { is_better = false; } } if (is_better) { b1->first_level = first_level; } return is_better; } /* * Example: * e_code (`s_code' buffer, at the moment set_buf_break() is called) * ptr (`s_code' buffer) * corresponds_to (input buffer) * Left most column col+1 (the column (31 here)). * | | * 1234567890123456789012345678901234567890 * if (!(mask[0] == '\\0' * | * target_col (assuming `lineup_to_parens' is true in this example) * 1 2 3 2 | level == 2 * <---------------------> * priority_code_length */ /* * Calculate break priority. */ static void set_priority ( buf_break_st_ty *bb) { bb->priority = bb->priority_code_length; switch (bb->priority_code) { case bb_semicolon: bb->priority += 6000; break; case bb_before_boolean_binary_op: bb->priority += 5000; break; case bb_after_boolean_binary_op: if (bb->priority_code_length > 2) { bb->priority += 5000; } if (settings.break_before_boolean_operator) { bb->priority -= 3; } break; case bb_after_equal_sign: bb->priority += 4000; break; case bb_attribute: bb->priority += 3000; break; case bb_comma: bb->priority += 2000; break; case bb_comparisation: bb->priority += 1000; break; case bb_proc_call: bb->priority -= 1000; break; case bb_operator6: bb->priority += 600; break; case bb_operator5: bb->priority += 500; break; case bb_operator4: bb->priority += 400; break; case bb_operator2: bb->priority += 200; break; case bb_doublecolon: bb->priority += 100; break; default: break; } } /* * This function is called at every position where we possibly want * to break a line (if it gets too long). */ void set_buf_break ( bb_code_ty code, int paren_targ) { int target_col, level; int code_target = compute_code_target (paren_targ); buf_break_st_ty *bb; /* First, calculate the column that code following e_code would be * printed in if we'd break the line here. * This is done quite simular to compute_code_target(). */ /* Base indentation level (number of open left-braces) */ target_col = parser_state_tos->i_l_follow + 1; /* Did we just parse a brace that will be put on the next line * by this line break? */ if (*token == '{') { target_col -= settings.ind_size; /* Use `ind_size' because this only happens * for the first brace of initializer blocks. */ } /* Number of open brackets */ level = parser_state_tos->p_l_follow; /* Did we just parse a bracket that will be put on the next line * by this line break? */ if (*token == '(' || *token == '[') { --level; /* then don't take it into account */ } /* Procedure name of function declaration? */ if (parser_state_tos->procname[0] && token == parser_state_tos->procname) { target_col = 1; } else if (level == 0) /* No open brackets? */ { if (parser_state_tos->in_stmt) /* Breaking a statement? */ { target_col += settings.continuation_indent; } } else if (!settings.lineup_to_parens) { target_col += settings.continuation_indent + (settings.paren_indent * (level - 1)); } else { if (parser_state_tos->paren_indents[level - 1] < 0) { target_col = -parser_state_tos->paren_indents[level - 1]; } else { target_col = code_target + parser_state_tos->paren_indents[level - 1]; } } /* Store the position of `e_code' as the place to break this line. */ bb = (buf_break_st_ty *) xmalloc (sizeof (buf_break_st_ty)); bb->offset = e_code - s_code; bb->level = level; bb->target_col = target_col; bb->corresponds_to = token; *e_code = 0; bb->col = count_columns (code_target, s_code, NULL_CHAR) - 1; /* Calculate default priority. */ bb->priority_code_length = (e_code - s_code); bb->priority_newline = (parser_state_tos->last_saw_nl && !parser_state_tos->broken_at_non_nl); if (buf_break) { bb->first_level = buf_break->first_level; } switch (parser_state_tos->last_token) { case binary_op: if ((e_code - s_code) >= 3 && e_code[-3] == ' ' && ((e_code[-1] == '&' && e_code[-2] == '&') || (e_code[-1] == '|' && e_code[-2] == '|'))) { bb->priority_code = bb_after_boolean_binary_op; } else if ( (e_code - s_code >= 2) && (e_code[-1] == '=') && ( (e_code[-2] == ' ') || ( (e_code - s_code >= 3) && (e_code[-3] == ' ') && ( (e_code[-2] == '%') || (e_code[-2] == '^') || (e_code[-2] == '&') || (e_code[-2] == '*') || (e_code[-2] == '-') || (e_code[-2] == '+') || (e_code[-2] == '|'))))) { bb->priority_code = bb_after_equal_sign; } else if ( ( ( (e_code - s_code) >= 2) && (e_code[-2] == ' ') && ( (e_code[-1] == '<') || (e_code[-1] == '>'))) || ( ( (e_code - s_code) >= 3) && (e_code[-3] == ' ') && (e_code[-1] == '=') && ( (e_code[-2] == '=') || (e_code[-2] == '!') || (e_code[-2] == '<') || (e_code[-2] == '>')))) { bb->priority_code = bb_comparisation; } else if ( (e_code[-1] == '+') || (e_code[-1] == '-')) { bb->priority_code = bb_operator6; } else if ( (e_code[-1] == '*') || (e_code[-1] == '/') || (e_code[-1] == '%')) { bb->priority_code = bb_operator5; } else { bb->priority_code = bb_binary_op; } break; case comma: bb->priority_code = bb_comma; break; default: if ( (code == bb_binary_op) && ( (*token == '&') || (*token == '|')) && (*token == token[1])) { bb->priority_code = bb_before_boolean_binary_op; } else if (e_code[-1] == ';') { bb->priority_code = bb_semicolon; } else { bb->priority_code = code; if (code == bb_struct_delim) /* . -> .* or ->* */ { if (e_code[-1] == '*') { bb->priority_code = bb_operator4; } else { bb->priority_code = bb_operator2; } } } } set_priority (bb); /* Add buf_break to the list */ if (buf_break_list) { buf_break_list->next = bb; } bb->prev = buf_break_list; bb->next = NULL; buf_break_list = bb; if (buf_break && bb->col > settings.max_col) { return; } if (!better_break (bb, buf_break)) { return; } /* Found better buf_break. Get rid of all previous possible breaks. */ buf_break = bb; for (bb = bb->prev; bb;) { buf_break_st_ty *obb = bb; bb = bb->prev; free (obb); } buf_break->prev = NULL; } void clear_buf_break_list (void) { buf_break_st_ty *bb; for (bb = buf_break_list; bb;) { buf_break_st_ty *obb = bb; bb = bb->prev; free (obb); } buf_break = buf_break_list = NULL; break_line = 0; } /* * prev_code_target * | prev_code_target + offset * | | * <----->if ((aaa == bbb) && xxx * && xxx * | * new_code_target */ static void set_next_buf_break ( int prev_code_target, int new_code_target, int offset) { buf_break_st_ty *bb; better_break (buf_break, NULL); /* Reset first_level */ if (buf_break_list == buf_break) { clear_buf_break_list (); return; } /* Correct all elements of the remaining buf breaks: */ for (bb = buf_break_list; bb; bb = bb->prev) { if (bb->target_col > buf_break->target_col && settings.lineup_to_parens) { bb->target_col -= ((prev_code_target + offset) - new_code_target); } bb->col -= ((prev_code_target + offset) - new_code_target); bb->offset -= offset; bb->priority_code_length -= offset; bb->first_level = buf_break->first_level; if (!buf_break->priority_newline) { bb->priority_newline = false; } set_priority (bb); if (bb->prev == buf_break) { break; } } free (buf_break); /* Set buf_break to first break in the list */ buf_break = bb; /* GDB_HOOK_buf_break */ buf_break->prev = NULL; /* Find a better break of the existing breaks */ for (bb = buf_break; bb; bb = bb->next) { if (bb->col > settings.max_col) { continue; } if (better_break (bb, buf_break)) { /* Found better buf_break. Get rid of all previous possible breaks. */ buf_break = bb; for (bb = bb->prev; bb;) { buf_break_st_ty *obb = bb; bb = bb->prev; free (obb); } bb = buf_break; buf_break->prev = NULL; } } } /* * Name: pad_output * Description: Fill the output line with whitespace up to TARGET_COLUMN, * given that the line is currently in column CURRENT_COLUMN. * * Returns: the ending column number. * * History: */ static int pad_output ( int current_column, int target_column) { if (current_column >= target_column) { return current_column; } if (settings.use_tabs && settings.tabsize > 1) { int offset = settings.tabsize - (current_column - 1) % settings.tabsize; while (current_column + offset <= target_column) { putc (TAB, output); current_column += offset; offset = settings.tabsize; } } while (current_column < target_column) { putc (' ', output); current_column++; } return current_column; } /* * Name: dump_line * Description: routine that actually effects the printing of the new source. * It prints the label section, followed by the code section with * the appropriate nesting level, followed by any comments. * * Returns: None. * * History: */ extern void dump_line ( int force_nl, int *paren_targ) { int cur_col; int target_col = 0; int not_truncated = 1; int target_col_break = -1; if (buf_break_used) { buf_break_used = 0; target_col_break = prev_target_col_break; } else if (force_nl) { parser_state_tos->broken_at_non_nl = false; } if (parser_state_tos->procname[0] && !parser_state_tos->classname[0] && (s_code_corresponds_to == parser_state_tos->procname)) { parser_state_tos->procname = "\0"; } else if (parser_state_tos->procname[0] && parser_state_tos->classname[0] && (s_code_corresponds_to == parser_state_tos->classname)) { parser_state_tos->procname = "\0"; parser_state_tos->classname = "\0"; } /* A blank line */ if ((s_code == e_code) && (s_lab == e_lab) && (s_com == e_com)) { /* If we have a formfeed on a blank line, we should just output it, * rather than treat it as a normal blank line. */ if (parser_state_tos->use_ff) { putc ('\014', output); parser_state_tos->use_ff = false; } else { n_real_blanklines++; } } else { if (prefix_blankline_requested && n_real_blanklines == 0) { n_real_blanklines = 1; } else if (settings.swallow_optional_blanklines && (n_real_blanklines > 1)) { n_real_blanklines = 1; } while (--n_real_blanklines >= 0) { putc (EOL, output); } n_real_blanklines = 0; if (e_lab != s_lab || e_code != s_code) { ++code_lines; /* keep count of lines with code */ } if (e_lab != s_lab) { /* print lab, if any */ while ((e_lab > s_lab) && ((e_lab[-1] == ' ') || (e_lab[-1] == TAB))) { e_lab--; } cur_col = pad_output (1, compute_label_target ()); /* force indentation of preprocessor directives. * this happens when force_preproc_width > 0 */ if ((settings.force_preproc_width > 0) && (s_lab[0] == '#')) { int preproc_postcrement; char *p = &s_lab[1]; while(*p == ' ') { p++; } preproc_postcrement = settings.force_preproc_width; if (strncmp(p, "else", 4) == 0) { preproc_indent-=settings.force_preproc_width; } else if((strncmp(p, "if", 2) == 0) || (strncmp(p, "ifdef", 5) == 0)) { } else if (strncmp(p, "elif", 4) == 0) { preproc_indent -= settings.force_preproc_width; } else if(strncmp(p, "endif", 5) == 0) { preproc_indent -= settings.force_preproc_width; preproc_postcrement = 0; } else { preproc_postcrement = 0; } if (preproc_indent == 0) { fprintf (output, "#"); } else { fprintf (output, "#%*s", preproc_indent, " "); } fprintf (output, "%.*s", (int) (e_lab - p), p); cur_col = count_columns (cur_col + preproc_indent + 1, p, NULL_CHAR); preproc_indent += preproc_postcrement; } else if ((s_lab[0] == '#') && ((strncmp (&s_lab[1], "else", 4) == 0) || (strncmp (&s_lab[1], "endif", 5) == 0))) { /* Treat #else and #endif as a special case because any text * after #else or #endif should be converted to a comment. */ char *s = s_lab; if (e_lab[-1] == EOL) /* Don't include EOL in the comment */ { e_lab--; } do { putc (*s++, output); ++cur_col; } while ((s < e_lab) && ('a' <= *s) && (*s <= 'z')); while (((*s == ' ') || (*s == TAB)) && (s < e_lab)) { s++; } if (s < e_lab) { if (settings.tabsize > 1) { cur_col = pad_output (cur_col, cur_col + settings.tabsize - (cur_col - 1) % settings.tabsize); } else { cur_col = pad_output (cur_col, cur_col + 2); } if (s[0] == '/' && (s[1] == '*' || s[1] == '/')) { fprintf (output, "%.*s", e_lab - s, s); } else { fprintf (output, "/* %.*s */", e_lab - s, s); } /* no need to update cur_col: the very next thing will be a new-line (or end of file) */ } } else { fprintf (output, "%.*s", (int) (e_lab - s_lab), s_lab); cur_col = count_columns (cur_col, s_lab, NULL_CHAR); } } else { cur_col = 1; /* there is no label section */ } parser_state_tos->pcase = false; /* Remove trailing spaces */ while ((*(e_code - 1) == ' ') && (e_code > s_code)) { *(--e_code) = NULL_CHAR; } if (s_code != e_code) { /* print code section, if any */ char *p; int i; /* If a comment begins this line, then indent it to the right * column for comments, otherwise the line starts with code, * so indent it for code. */ if (embedded_comment_on_line == 1) { target_col = parser_state_tos->com_col; } else if (target_col_break != -1) { target_col = target_col_break; } else { target_col = compute_code_target (*paren_targ); } /* If a line ends in an lparen character, the following line should * not line up with the parenthesis, but should be indented by the * usual amount. */ if (parser_state_tos->last_token == lparen) { parser_state_tos->paren_indents[parser_state_tos->p_l_follow - 1] += settings.ind_size - 1; } cur_col = pad_output (cur_col, target_col); if (break_line && (s_com == e_com) && (buf_break->target_col <= buf_break->col)) { int offset, len; char c; char *ptr = &s_code[buf_break->offset]; if (*ptr != ' ') { --ptr; } /* Add target_col (and negate) the brackets that are * actually printed. The remaining brackets must * be given an offset of . */ offset = ptr - s_code + 1; for (i = 0; i < parser_state_tos->p_l_follow; i++) { if (parser_state_tos->paren_indents[i] >= 0) { if (parser_state_tos->paren_indents[i] < ptr - s_code) { parser_state_tos->paren_indents[i] = -(parser_state_tos->paren_indents[i] + target_col); } else { parser_state_tos->paren_indents[i] -= offset; } } } for (i = parser_state_tos->p_l_follow; i < parser_state_tos->paren_indents_size; ++i) { if (parser_state_tos->paren_indents[i] >= (ptr - s_code)) { parser_state_tos->paren_indents[i] -= offset; } } for (p = s_code; p < s_code + buf_break->offset; p++) { putc (*p, output); } c = s_code[buf_break->offset]; s_code[buf_break->offset] = '\0'; cur_col = count_columns (cur_col, s_code, NULL_CHAR); s_code[buf_break->offset] = c; not_truncated = 0; len = (e_code - ptr - 1); memmove (s_code, ptr + 1, len); e_code = s_code + len; #if COLOR_DEBUG fputs (" \e[31m", output); for (p = s_code; p < e_code; p++) { putc (*p, output); } fputs (" \e[0m", output); #endif *e_code = '\0'; s_code_corresponds_to = buf_break->corresponds_to; prev_target_col_break = buf_break->target_col; if (!buf_break->priority_newline) { parser_state_tos->broken_at_non_nl = true; } set_next_buf_break (target_col, buf_break->target_col, offset); buf_break_used = 1; break_line = (buf_break != NULL) && (output_line_length () > settings.max_col); } else { for (i = 0; i < parser_state_tos->p_l_follow; i++) { if (parser_state_tos->paren_indents[i] >= 0) { parser_state_tos->paren_indents[i] = -(parser_state_tos->paren_indents[i] + target_col); } } for (p = s_code; p < e_code; p++) { putc (*p, output); } cur_col = count_columns (cur_col, s_code, NULL_CHAR); clear_buf_break_list (); } } if (s_com != e_com) { { /* Here for comment printing. */ int target = parser_state_tos->com_col; char *com_st = s_com; if (cur_col > target) { putc (EOL, output); cur_col = 1; ++out_lines; } cur_col = pad_output (cur_col, target); fwrite (com_st, e_com - com_st, 1, output); cur_col += e_com - com_st; com_lines++; } } else if (embedded_comment_on_line) { com_lines++; } embedded_comment_on_line = 0; if (parser_state_tos->use_ff) { putc ('\014', output); parser_state_tos->use_ff = false; } else { putc (EOL, output); } ++out_lines; if ((parser_state_tos->just_saw_decl == 1) && settings.blanklines_after_declarations) { prefix_blankline_requested = 1; prefix_blankline_requested_code = decl; parser_state_tos->just_saw_decl = 0; } else { prefix_blankline_requested = postfix_blankline_requested; prefix_blankline_requested_code = postfix_blankline_requested_code; } postfix_blankline_requested = 0; } /* if we are in the middle of a declaration, remember that fact * for proper comment indentation */ parser_state_tos->decl_on_line = parser_state_tos->in_decl; /* next line should be indented if we have not completed this stmt */ parser_state_tos->ind_stmt = parser_state_tos->in_stmt; e_lab = s_lab; *s_lab = '\0'; /* reset buffers */ if (not_truncated) { e_code = s_code; *s_code = '\0'; s_code_corresponds_to = NULL; } e_com = s_com; *s_com = '\0'; parser_state_tos->ind_level = parser_state_tos->i_l_follow; parser_state_tos->paren_level = parser_state_tos->p_l_follow; if (parser_state_tos->paren_level > 0) { /* If we broke the line and the following line will * begin with a rparen, the indentation is set for * the column of the rparen *before* the break - reset * the column to the position after the break. */ if (!not_truncated && ((*s_code == '(') || (*s_code == '[')) && (parser_state_tos->paren_level >= 2)) { *paren_targ = -parser_state_tos->paren_indents[parser_state_tos->paren_level - 2]; } else { *paren_targ = -parser_state_tos->paren_indents[parser_state_tos->paren_level - 1]; } } else { *paren_targ = 0; } if (inhibited) { char *p = cur_line; while (--n_real_blanklines >= 0) { putc (EOL, output); } n_real_blanklines = 0; do { while ((*p != '\0') && (*p != EOL)) { putc (*p++, output); } if ((*p == '\0') && ((unsigned long) (p - current_input->data) == current_input->size)) { in_prog_pos = p; buf_end = p; buf_ptr = p; had_eof = true; return; } if (*p == EOL) { cur_line = p + 1; line_no++; } putc (*p++, output); while ((*p == ' ') || (*p == TAB)) { putc (*p, output); p++; } if ((*p == '/') && ((*(p + 1) == '*') || (*(p + 1) == '/'))) { /* We've hit a comment. See if turns formatting back on. */ putc (*p++, output); putc (*p++, output); while ((*p == ' ') || (*p == TAB)) { putc (*p, output); p++; } if (!strncmp (p, "*INDENT-ON*", 11)) { do { while ((*p != '\0') && (*p != EOL)) { putc (*p++, output); } if ((*p == '\0') && (((unsigned long) (p - current_input->data) == current_input->size))) { in_prog_pos = p; buf_end = p; buf_ptr = p; had_eof = true; return; } else { if (*p == EOL) { inhibited = false; cur_line = p + 1; line_no++; } putc (*p++, output); } } while (inhibited); } } } while (inhibited); in_prog_pos = cur_line; buf_end = cur_line; buf_ptr = cur_line; fill_buffer (); } /* Output the rest already if we really wanted a new-line after this code. */ if (buf_break_used && (s_code != e_code) && force_nl) { prefix_blankline_requested = 0; dump_line (true, paren_targ); } return; } /* * Name: flush_output * Description: Flushes any buffered output to the output file. * * Returns: None * * History: */ extern void flush_output(void) { fflush (output); } /* * Name: open_output * Description: Opens the output file in read/write mode. * * Returns: None * * History: */ void open_output( const char * filename, const char * mode) { if (filename == NULL) { output = stdout; } else { output = fopen(filename, mode); /* open the file for read + write * (but see the trunc function) */ if (output == NULL) { fprintf (stderr, _("indent: can't create %s\n"), filename); exit (indent_fatal); } } } /* * Name: reopen_output_trunc * Description: Reopens the output file truncated. * * Returns: None * * History: */ extern void reopen_output_trunc( const char * filename) { output = freopen(filename, "w", output); } /* * Name: close_output * Description: Closes the output file. * * Returns: None * * History: */ extern void close_output( struct stat * file_stats, const char * filename) { if (output != stdout) { if (fclose(output) != 0) { fatal(_("Can't close output file %s"), filename); } else { #ifdef PRESERVE_MTIME if (file_stats != NULL) { struct utimbuf buf; buf.actime = time (NULL); buf.modtime = file_stats->st_mtime; if (utime(filename, &buf) != 0) { WARNING(_("Can't preserve modification time on output file %s"), filename, 0); } } #endif } } } extern inhibit_indenting( BOOLEAN flag) { inhibited = flag; }