/* Copyright (c) 1993 - 1998, Joseph Arceneaux. All rights reserved. * * This file is subject to the terms of the GNU General Public License as * published by the Free Software Foundation. A copy of this license is * included with this software distribution in the file COPYING. If you * do not have a copy, you may obtain a copy by writing to the Free * Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include "sys.h" #include "indent.h" #include "io.h" #include "comments.h" #include "globs.h" #include "parse.h" RCSTAG_CC ("$Id: comments.c,v 1.33 2002/08/04 17:08:41 david Exp $"); /* Check the limits of the comment buffer, and expand as neccessary. */ #define CHECK_COM_SIZE \ if (e_com >= l_com) \ { \ int nsize = l_com - s_com + 400; \ combuf = (char *) xrealloc (combuf, nsize); \ e_com = combuf + (e_com - s_com) + 1; \ l_com = combuf + nsize - 5; \ s_com = combuf + 1; \ } /* Output a comment. `buf_ptr' is pointing to the character after * the beginning comment delimiter when this is called. This handles * both C and C++ comments. * * As far as indent is concerned, there are basically two types * of comments -- those on lines by themselves and those which are * on lines with other code. Variables (and the options specifying them) * affecting the printing of comments are: * * `format_comments' ("fca"): Ignore newlines in the * comment and perform filling up to `comment_max_col'. Double * newlines indicate paragraph breaks. * * `format_col1_comments' ("fc1"): Format comments which * begin in column 1. * * `unindent_displace' ("d"): The hanging indentation for * comments which do not appear to the right of code. * * `comment_delimiter_on_blankline' ("cdb"): If set, place the comment * delimiters on lines by themselves. This only affects comments * which are not to the right of code. * * `com_ind' ("c"): The column in which to begin * comments that are to the right of code. * * `decl_com_ind' ("cd"): The column in which to begin * comments that are to the right of declarations. * * `else_endif_col' ("cp"): The column in which to begin * comments to the right of preprocessor directives. * * `star_comment_cont' ("sc"): Place a star ('*') to the * left of the comment body. * * `comment_max_col' ("lc"): The length of a comment line. * Formatted comments which extend past this column will be continued on * the following line. If this option is not specified, `max_col' is * used. */ void print_comment ( int *paren_targ) { register int column, format; codes_ty comment_type; int start_column; int found_column; int first_comment_line; int right_margin; int boxed_comment; int stars; int blankline_delims; int paragraph_break; int merge_blank_comment_lines; int two_contiguous_comments = 0; int save_length = 0; char * save_ptr = NULL; char * text_on_line = NULL; char * line_break_ptr = NULL; char * start_delim = NULL; char * line_preamble = NULL; int line_preamble_length; int visible_preamble; int suppress_cdb = 0; /* GDB_HOOK_print_comment() */ /* Increment the parser stack, as we will store some things * there for dump_line to use. */ inc_pstack (); /* Have to do it this way because this piece of shit program doesn't * always place the last token code on the stack. */ if (*(token + 1) == '/') { comment_type = cplus_comment; } else { comment_type = comment; } /* First, decide what kind of comment this is: C++, C, or boxed C. * Even if this appears to be a normal C comment, we may change our * minds if we find a star in the right column of the second line, * in which case that's a boxed comment too. */ if (comment_type == cplus_comment) { start_delim = "//"; line_preamble = "// "; line_preamble_length = strlen(line_preamble); visible_preamble = 1; boxed_comment = 0; stars = 0; blankline_delims = 0; } else if ((*buf_ptr == '*') || (*buf_ptr == '-') || (*buf_ptr == '=') || (*buf_ptr == '_') || (parser_state_tos->col_1 && !settings.format_col1_comments)) { /* Boxed comment. This block of code will return. */ int comment_lines_count = 1; stars = 0; boxed_comment = 0; blankline_delims = 0; line_preamble_length = 0; visible_preamble = 0; start_column = current_column () - 2; found_column = start_column; parser_state_tos->box_com = 1; parser_state_tos->com_col = found_column; if (settings.blanklines_before_blockcomments) { prefix_blankline_requested = 1; } *e_com++ = '/'; *e_com++ = '*'; while (1) { do { if (*buf_ptr == EOL) /* Count line numbers within comment blocks */ { ++line_no; } *e_com++ = *buf_ptr++; CHECK_COM_SIZE; } while ((*buf_ptr != '*') && (buf_ptr < buf_end)); /* We have reached the end of the comment, and it's all on * this line. */ if ((*buf_ptr == '*') && (*(buf_ptr + 1) == '/')) { if (buf_ptr == buf_end) { fill_buffer (); } buf_ptr += 2; if (buf_ptr == buf_end) { fill_buffer (); } *e_com++ = '*'; *e_com++ = '/'; *e_com = '\0'; parser_state_tos->tos--; /* If this is the only line of a boxed comment, it may * be after some other text (e.g., #if foo ), * in which case we want to specify the correct column. * In the other cases, the leading spaces account for * the columns and we start it in column 1. */ if (comment_lines_count > 1) { parser_state_tos->com_col = 1; } else { parser_state_tos->com_col = found_column; } return; } /* End of the line, or end of file. */ if (buf_ptr == buf_end) { if (*(buf_ptr - 1) == EOL) { *(--e_com) = EOS; dump_line (true, paren_targ); comment_lines_count++; parser_state_tos->com_col = 1; } fill_buffer (); if (had_eof) { *e_com++ = '\0'; parser_state_tos->tos--; parser_state_tos->com_col = start_column; return; } } } } else { start_delim = "/*"; line_preamble = 0; line_preamble_length = 0; visible_preamble = 0; boxed_comment = 0; stars = settings.star_comment_cont; blankline_delims = settings.comment_delimiter_on_blankline; } paragraph_break = 0; merge_blank_comment_lines = 0; first_comment_line = com_lines; right_margin = settings.comment_max_col; /* Now, compute the correct indentation for this comment * and whether or not it should be formatted. */ found_column = current_column () - 2; if ((s_lab == e_lab) && (s_code == e_code)) { /* First handle comments which begin the line. */ if (parser_state_tos->col_1 && !settings.format_col1_comments) { format = settings.format_col1_comments; start_column = 1; } else { format = settings.format_comments; if ( (parser_state_tos->ind_level <= 0) && (!parser_state_tos->in_stmt || (parser_state_tos->in_decl && (parser_state_tos->paren_level == 0)))) { start_column = found_column; } else { /* This comment is within a procedure or other code. */ start_column = compute_code_target (*paren_targ) - settings.unindent_displace; if (start_column < 0) { start_column = 1; } } } } else { /* This comment follows code of some sort. */ int target; suppress_cdb = 1; /* First, compute where the comment SHOULD go. */ if (parser_state_tos->decl_on_line) { target = settings.decl_com_ind; } else if (else_or_endif) { target = settings.else_endif_col; } else { target = settings.com_ind; } /* Now determine if the code on the line is short enough * to allow the comment to begin where it should. */ if (s_code != e_code) { start_column = count_columns (compute_code_target (*paren_targ), s_code, NULL_CHAR); } else { /* s_lab != e_lab : there is a label here. */ start_column = count_columns (compute_label_target (), s_lab, NULL_CHAR); } if (start_column < target) { start_column = target; } else { /* If the too-long code is a pre-processor command, start the comment 1 space afterwards, otherwise start at the next tab mark. */ if (else_or_endif) { start_column++; else_or_endif = false; } else { start_column += settings.tabsize - ((start_column - 1) % settings.tabsize); } } format = settings.format_comments; } if (!line_preamble) { line_preamble_length = 3; if (stars) { line_preamble = " * "; visible_preamble = 1; } else { line_preamble = " "; visible_preamble = 0; } } /* These are the parser stack variables used to communicate * formatting information to dump_line (). */ parser_state_tos->com_col = (two_contiguous_comments ? 1 : start_column); parser_state_tos->box_com = boxed_comment; /* Output the beginning comment delimiter. They are both two * characters long. */ *e_com++ = *start_delim; *e_com++ = *(start_delim + 1); column = start_column + 2; /* If the user specified -cdb, put the delimiter on one line. */ if (blankline_delims && !suppress_cdb) { char *p = buf_ptr; *e_com = '\0'; dump_line (true, paren_targ); /* Check if the delimiter was already on a line by itself, and skip whitespace if formating. */ p = skip_horiz_space(p); if (*p == EOL) { buf_ptr = p + 1; } else if (format) { buf_ptr = p; } if (buf_ptr >= buf_end) { fill_buffer (); } column = start_column; goto begin_line; } else if (format) { *e_com++ = ' '; column = start_column + 3; skip_buffered_space(); /* adjusts buf_ptr */ } /* Iterate through the lines of the comment */ while (!had_eof) { /* Iterate through the characters on one line */ while (!had_eof) { CHECK_COM_SIZE; switch (*buf_ptr) { case ' ': case TAB: /* If formatting, and previous break marker is * nonexistant, or before text on line, reset * it to here. */ if (format && (line_break_ptr < text_on_line)) { line_break_ptr = e_com; } if (format) { /* Don't write two spaces after another, unless * the first space is preceeded by a dot. */ if ((e_com == s_com) || (e_com[-1] != ' ') || (e_com - 1 == s_com) || (e_com[-2] == '.')) { *e_com++ = ' '; column++; } } else if (*buf_ptr == ' ') { *e_com++ = ' '; column++; } else { /* Convert the tab to the appropriate number of spaces, based on the column we found the comment in, not the one we're printing in. */ int tab_width = (settings.tabsize - ((column + found_column - start_column - 1) % settings.tabsize)); column += tab_width; while (tab_width--) { *e_com++ = ' '; } } break; case EOL: /* We may be at the end of a C++ comment */ if (comment_type == cplus_comment) { cplus_exit: parser_state_tos->tos--; parser_state_tos->com_col = (two_contiguous_comments ? 1 : start_column); parser_state_tos->box_com = boxed_comment; *e_com = 0; return; } if (format) { /* Newline and null are the two characters which end an input line, so check here if we need to get the next line. */ if (*buf_ptr == EOL) { ++line_no; } buf_ptr++; if (buf_ptr >= buf_end) { fill_buffer (); } /* If there are any spaces between the text and this newline character, remove them. */ if ((e_com > line_break_ptr) && (text_on_line < line_break_ptr)) { e_com = line_break_ptr; } /* If this is "\n\n", or "\n\n", * it's a paragraph break. */ skip_buffered_space(); /* adjusts buf_ptr */ if ((*buf_ptr == EOL) || !text_on_line) { paragraph_break = 1; goto end_line; } /* Also need to eat the preamble. */ if (!boxed_comment && (current_column () == found_column + 1) && (buf_ptr[0] == '*') && (buf_ptr[1] != '/')) { if (++buf_ptr >= buf_end) { fill_buffer (); } if ((*buf_ptr == ' ') && (++buf_ptr >= buf_end)) { fill_buffer (); } } /* This is a single newline. Transform it (and any * following whitespace) into a single blank. */ if (e_com[-1] != ' ') { line_break_ptr = e_com; *e_com++ = ' '; column++; } continue; } /* We are printing this line "as is", so output it and continue on to the next line. */ goto end_line; case '*': /* Check if we've reached the end of the comment. */ if (comment_type == comment) { if (*(buf_ptr + 1) == '/') { /* If it's not a boxed comment, put some whitespace * before the ending delimiter. Otherwise, simply * insert the delimiter. */ if (!boxed_comment) { if (text_on_line) { if (blankline_delims && !suppress_cdb) { *e_com = '\0'; dump_line (true, paren_targ); *e_com++ = ' '; } else { /* Insert space before closing delim */ if ((*(e_com - 1) != ' ') && (*(e_com - 1) != TAB)) { *e_com++ = ' '; } } } else if ((s_com == e_com) || (*s_com != '/')) { /* If no text on line, then line is completely empty * or starts with preamble, or is beginning of * comment and starts with beginning delimiter. */ e_com = s_com; *e_com++ = ' '; } else { /* This is case of first comment line. Test * with: * if (first_comment_line != com_lines) * abort (); */ if ((*(e_com - 1) != ' ') && (*(e_com - 1) != TAB)) { *e_com++ = ' '; } } } /* Now insert the ending delimiter */ *e_com++ = '*'; *e_com++ = '/'; *e_com = '\0'; /* Skip any whitespace following the comment. If * there is only whitespace after it, print the line. * * NOTE: We're not printing the line: TRY IT! */ buf_ptr += 2; buf_ptr = skip_horiz_space(buf_ptr); if (buf_ptr >= buf_end) { fill_buffer (); } parser_state_tos->tos--; parser_state_tos->com_col = (two_contiguous_comments ? 1 : start_column); parser_state_tos->box_com = boxed_comment; return; } /* If this star is on the second line of the * comment in the same column as the star of the * beginning delimiter, then consider it * a boxed comment. */ if ((first_comment_line == com_lines - 1) && (e_com == s_com + line_preamble_length)) { /* Account for change in line_preamble_length: */ column -= line_preamble_length - 1; line_preamble = " "; line_preamble_length = 1; boxed_comment = 1; format = 0; blankline_delims = 0; *s_com = ' '; *(s_com + 1) = '*'; text_on_line = e_com = s_com + 2; column++; break; } } /* If it was not the end of the comment, drop through * and insert the star on the line. */ default: /* Some textual character. */ text_on_line = e_com; *e_com++ = *buf_ptr; column++; break; } /* If we are formatting, check that we haven't exceeded the line length. If we haven't set line_break_ptr, keep going. */ if (format && (column > right_margin) && line_break_ptr) { if (line_break_ptr < e_com - 1) { /* Here if we are really "breaking" the line: the line * break is before some text we've seen. */ *line_break_ptr = '\0'; save_ptr = line_break_ptr + 1; save_length = e_com - save_ptr; e_com = line_break_ptr; /* If we had to go past `right_margin' to print stuff out, * extend `right_margin' out to this point. */ if ((column - save_length) > right_margin) { right_margin = column - save_length; } } else { /* The line break is after the last text; we're really * truncating the line. */ if (comment_type == cplus_comment) { buf_ptr = skip_horiz_space(buf_ptr); buf_ptr--; if (*buf_ptr == EOL) { goto cplus_exit; } } else { while ((*buf_ptr == TAB) || (*buf_ptr == ' ') || (*buf_ptr == EOL)) { if (*buf_ptr == EOL) { ++line_no; } buf_ptr++; if (buf_ptr >= buf_end) { fill_buffer (); } } buf_ptr--; } *e_com = EOS; } goto end_line; } if (*buf_ptr == EOL) { ++line_no; } buf_ptr++; if (buf_ptr == buf_end) { fill_buffer (); } } end_line: /* Compress pure whitespace lines into newlines. */ if (!text_on_line && !visible_preamble && !(first_comment_line == com_lines)) { e_com = s_com; } *e_com = EOS; dump_line (true, paren_targ); /* We're in the middle of a C-comment, don't add blank lines! */ prefix_blankline_requested = 0; /* If formatting (paragraph_break is only used for formatted * comments) and user wants blank lines merged, kill all white * space after the "\n\n" indicating a paragraph break. */ if (paragraph_break) { if (merge_blank_comment_lines) { while ((*buf_ptr == EOL) || (*buf_ptr == ' ') || (*buf_ptr == TAB)) { if (*buf_ptr == EOL) { ++line_no; } if (++buf_ptr >= buf_end) { fill_buffer (); } } } paragraph_break = 0; } else { /* If it was a paragraph break (`if' clause), we scanned ahead * one character. So, here in the `else' clause, advance buf_ptr. */ if (*buf_ptr == EOL) { ++line_no; } buf_ptr++; if (buf_ptr >= buf_end) { fill_buffer (); } } begin_line: if (had_eof) { break; } /* Indent the line properly. If it's a boxed comment, align with * the '*' in the beginning slash-star and start inserting there. * Otherwise, insert blanks for alignment, or a star if the * user specified -sc. */ if (line_preamble) { (void) memcpy (e_com, line_preamble, line_preamble_length); e_com += line_preamble_length; column = start_column + line_preamble_length; } else { column = start_column; } line_break_ptr = 0; /* If we have broken the line before the end for formatting, * copy the text after the break onto the beginning of this * new comment line. */ if (save_ptr) { while (((*save_ptr == ' ') || (*save_ptr == TAB)) && save_length) { save_ptr++; save_length--; } (void) memcpy (e_com, save_ptr, save_length); text_on_line = e_com; e_com += save_length; /* We only break if formatting, in which cases there * are no tabs, only spaces. */ column += save_length; save_ptr = 0; save_length = 0; } else { skip_buffered_space(); /* adjusts buf_ptr */ text_on_line = 0; } } parser_state_tos->tos--; parser_state_tos->com_col = (two_contiguous_comments ? 1 : start_column); parser_state_tos->box_com = boxed_comment; }