/**
 * Copyright (c) 1985 Sun Microsystems, Inc.
 * Copyright (c) 1980 The Regents of the University of California.
 * Copyright (c) 1976 Board of Trustees of the University of Illinois.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that the above copyright notice and this paragraph are
 * duplicated in all such forms and that any documentation,
 * advertising materials, and other materials related to such
 * distribution and use acknowledge that the software was developed
 * by the University of California, Berkeley, the University of Illinois,
 * Urbana, and Sun Microsystems, Inc.  The name of either University
 * or Sun Microsystems may not be used to endorse or promote products
 * derived from this software without specific prior written permission.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

/*
 * NAME: pr_comment
 *
 * FUNCTION: This routine takes care of scanning and printing comments.
 *
 * ALGORITHM: 1) Decide where the comment should be aligned, and if lines should
 * be broken. 2) If lines should not be broken and filled, just copy up to
 * end of comment. 3) If lines should be filled, then scan thru input_buffer
 * copying characters to com_buf.  Remember where the last blank, tab, or
 * newline was.  When line is filled, print up to last blank and continue
 * copying.
 *
 * HISTORY: November 1976	D A Willcox of CAC	Initial coding 12/6/76
 *  A Willcox of CAC	Modification to handle UNIX-style comments
 *
 */

/*
 * this routine processes comments.  It makes an attempt to keep comments
 * from going over the max line length.  If a line is too long, it moves
 * everything from the last blank to the next comment line.  Blanks and tabs
 * from the beginning of the input line are removed
 */


#define PUBLIC extern
#include <stdlib.h>
#include "globs.h"
#include "proto.h"


void pr_comment()
{
   int             now_col;		/* column we are in now */

   int             adj_max_col;		/* Adjusted max_col for when we
					 * decide to spill comments
					 *  over the right margin
					 */

   char           *last_bl;		/* points to the last blank in
					 * the output buffer 
					 */

   char           *t_ptr;		/* used for moving string */

   int             unix_comment;	/* tri-state variable used to
					 * decide if it is a unix-style
					 * comment. 0 means only blanks
					 * since / *, 1 means regular
					 * style comment, 2 means unix
					 * style comment 
					 */

   int             break_delim = del_on_bl;
   int             l_just_saw_decl = ps.just_saw_decl;
   /* int         ps.last_nl = 0; */	/* true iff the last
				      	   sig thing we have seen is a nl */

   int             one_liner = 1;	/* true iff this comment is a
					   one-liner */
   adj_max_col = max_col;
   ps.just_saw_decl = 0;
   last_bl = 0;				/* no blanks found so far */
   ps.box_com = false;			/* at first, assume that we are
					   not in a boxed comment or
					   some other comment that
					   should not be touched */
   ++ps.out_coms;			/* keep track of number of
					   comments */
   unix_comment = 1;			/* set flag to let us figure
					   out if there is a unix-style
					   comment ** DISABLED: use 0
					   to reenable this hack! */

   /* Figure where to align and how to treat the comment */

   if (ps.col_1 && !format_col1_comments)
   {					/* if comment starts in column
					   1 it should not be touched */
      ps.box_com = true;
      ps.com_col = 1;
   } else
   {
      if (*buf_ptr == '-' || *buf_ptr == '*')
      {
	 ps.box_com = true;		/* a comment with a '-' or '*'
					   immediately after the / * is
					   assumed to be a boxed
					   comment */
	 break_delim = 0;
      }
      if ( /* ps.bl_line && */ (s_lab == e_lab) && (s_code == e_code))
      {
	 /* klg: check only if this line is blank */
	 /* If this (*and previous lines are*) blank, dont put comment
	    way out at left */
	 ps.com_col = (ps.ind_level - ps.unindent_displace) * ps.ind_size + 1;
	 adj_max_col = bk_max_col;
	 if (ps.com_col <= 1)
	    ps.com_col = 1 + !format_col1_comments;
      } else
      {
	 register        target_col;
	 break_delim = 0;
	 if (s_code != e_code)
	    target_col = count_spaces(code_target(), s_code);
	 else
	 {
	    target_col = 1;
	    if (s_lab != e_lab)
	       target_col = count_spaces(label_target(), s_lab);
	 }
	 ps.com_col = ps.decl_on_line || ps.ind_level == 0 ? ps.decl_com_ind : ps.com_ind;
	 if (ps.com_col < target_col)
	    ps.com_col = ((target_col + 7) & ~7) + 1;
	 if (ps.com_col + 24 > adj_max_col)
	    adj_max_col = ps.com_col + 24;
      }
   }
   if (ps.box_com)
   {
      buf_ptr[-2] = 0;
      ps.n_comment_delta = 1 - count_spaces(1, in_buffer);
      buf_ptr[-2] = '/';
   } else
   {
      ps.n_comment_delta = 0;
      while (*buf_ptr == ' ' || *buf_ptr == '\t')
	 buf_ptr++;
   }
   ps.comment_delta = 0;
   *e_com++ = '/';			/* put '/ *' into buffer */
   *e_com++ = '*';
   if (*buf_ptr != ' ' && !ps.box_com)
      *e_com++ = ' ';

   *e_com = '\0';
   if (troff)
   {
      now_col = 1;
      adj_max_col = 80;
   } else
      now_col = count_spaces(ps.com_col, s_com);	/* figure what column we
							   would be in if we
							   printed the comment
							   now */

   /* Start to copy the comment */

   while (1)
   {					/* this loop will go until the
					   comment is copied */
      if (*buf_ptr > 040 && *buf_ptr != '*')
	 ps.last_nl = 0;
      if (e_com >= l_com)
      {
	 register        nsize = l_com - s_com + 400;
	 combuf = (char *) realloc(combuf, nsize);
	 e_com = combuf + (e_com - s_com) + 1;
	 l_com = combuf + nsize - 5;
	 s_com = combuf + 1;
      }
      switch (*buf_ptr)
      {					/* this checks for various spcl
					   cases */
      case 014:			/* check for a form feed */
	 if (!ps.box_com)
	 {				/* in a text comment, break the
					   line here */
	    ps.use_ff = true;
	    /* fix so dump_line uses a form feed */
	    dump_line();
	    last_bl = 0;
	    *e_com++ = ' ';
	    *e_com++ = '*';
	    *e_com++ = ' ';
	    while (*++buf_ptr == ' ' || *buf_ptr == '\t');
	 } else
	 {
	    if (++buf_ptr >= buf_end)
	       fill_buffer();
	    *e_com++ = 014;
	 }
	 break;

      case '\n':
	 if (had_eof)
	 {				/* check for unexpected eof */
	    printf("Unterminated comment\n");
	    *e_com = '\0';
	    dump_line();
	    return;
	 }
	 one_liner = 0;
	 if (ps.box_com || ps.last_nl)
	 {				/* if this is a boxed comment,
					   we dont ignore the newline */
	    if (s_com == e_com)
	    {
	       *e_com++ = ' ';
	       *e_com++ = ' ';
	    }
	    *e_com = '\0';
	    if (!ps.box_com && e_com - s_com > 3)
	    {
	       if (break_delim == 1 && s_com[0] == '/'
		   && s_com[1] == '*' && s_com[2] == ' ')
	       {
		  char           *t = e_com;
		  break_delim = 2;
		  e_com = s_com + 2;
		  *e_com = 0;
		  if (bl_bef_bk)
		     prefix_blankline_requested = 1;
		  dump_line();
		  e_com = t;
		  s_com[0] = s_com[1] = s_com[2] = ' ';
	       }
	       dump_line();
	       if (e_com >= l_com)
	       {
		  register        nsize = l_com - s_com + 400;
		  combuf = (char *) realloc(combuf, nsize);
		  e_com = combuf + (e_com - s_com) + 1;
		  l_com = combuf + nsize - 5;
		  s_com = combuf + 1;
	       }
	       *e_com++ = ' ';
	       *e_com++ = ' ';
	    }
	    dump_line();
	    now_col = ps.com_col;
	 } else
	 {
	    ps.last_nl = 1;
	    if (unix_comment != 1)
	    {				/* we not are in unix_style
					   comment */
	       if (unix_comment == 0 && s_code == e_code)
	       {
		  /* if it is a UNIX-style comment, ignore the
		     requirement that previous line be blank for
		     unindention */
		  ps.com_col = (ps.ind_level - ps.unindent_displace) * ps.ind_size + 1;
		  if (ps.com_col <= 1)
		     ps.com_col = 2;
	       }
	       unix_comment = 2;	/* permanently remember that we
					   are in this type of comment */
	       dump_line();
	       ++line_no;
	       now_col = ps.com_col;
	       *e_com++ = ' ';
	       /* fix so that the star at the start of the line will
	          line up */
	       do			/* flush leading white space */
		  if (++buf_ptr >= buf_end)
		     fill_buffer();
	       while (*buf_ptr == ' ' || *buf_ptr == '\t');
	       break;
	    }
	    if (*(e_com - 1) == ' ' || *(e_com - 1) == '\t')
	       last_bl = e_com - 1;
	    /* if there was a space at the end of the last line,
	       remember where it was */
	    else
	    {				/* otherwise, insert one */
	       last_bl = e_com;
	       *e_com++ = ' ';
	       if (e_com >= l_com)
	       {
		  register        nsize = l_com - s_com + 400;
		  combuf = (char *) realloc(combuf, nsize);
		  e_com = combuf + (e_com - s_com) + 1;
		  l_com = combuf + nsize - 5;
		  s_com = combuf + 1;
	       }
	       ++now_col;
	    }
	 }
	 ++line_no;			/* keep track of input line
					   number */
	 if (!ps.box_com)
	 {
	    int             nstar = 1;
	    do
	    {				/* flush any blanks and/or tabs
					   at start of next line */
	       if (++buf_ptr >= buf_end)
		  fill_buffer();
	       if (*buf_ptr == '*' && --nstar >= 0)
	       {
		  if (++buf_ptr >= buf_end)
		     fill_buffer();
		  if (*buf_ptr == '/')
		     goto end_of_comment;
	       }
	    } while (*buf_ptr == ' ' || *buf_ptr == '\t');
	 } else if (++buf_ptr >= buf_end)
	    fill_buffer();
	 break;				/* end of case for newline */

      case '*':			/* must check for possibility
					   of being at end of comment */
	 if (++buf_ptr >= buf_end)	/* get to next char after * */
	    fill_buffer();

	 if (unix_comment == 0)		/* set flag to show we are not
					   in unix-style comment */
	    unix_comment = 1;

	 if (*buf_ptr == '/')
	 {				/* it is the end!!! */
      end_of_comment:
	    if (++buf_ptr >= buf_end)
	       fill_buffer();

	    if (*(e_com - 1) != ' ' && !ps.box_com)
	    {				/* insure blank before end */
	       *e_com++ = ' ';
	       ++now_col;
	    }
	    if (break_delim == 1 && !one_liner && s_com[0] == '/'
		&& s_com[1] == '*' && s_com[2] == ' ')
	    {
	       char           *t = e_com;
	       break_delim = 2;
	       e_com = s_com + 2;
	       *e_com = 0;
	       if (bl_bef_bk)
		  prefix_blankline_requested = 1;
	       dump_line();
	       e_com = t;
	       s_com[0] = s_com[1] = s_com[2] = ' ';
	    }
	    if (break_delim == 2 && e_com > s_com + 3
		 /* now_col > adj_max_col - 2 && !ps.box_com */ )
	    {
	       *e_com = '\0';
	       dump_line();
	       now_col = ps.com_col;
	    }
	    if (e_com >= l_com)
	    {
	       register        nsize = l_com - s_com + 400;
	       combuf = (char *) realloc(combuf, nsize);
	       e_com = combuf + (e_com - s_com) + 1;
	       l_com = combuf + nsize - 5;
	       s_com = combuf + 1;
	    }
	    *e_com++ = '*';
	    *e_com++ = '/';
	    *e_com = '\0';
	    ps.just_saw_decl = l_just_saw_decl;
	    return;
	 } else
	 {				/* handle isolated '*' */
	    *e_com++ = '*';
	    ++now_col;
	 }
	 break;
      default:				/* we have a random char */
	 if (unix_comment == 0 && *buf_ptr != ' ' && *buf_ptr != '\t')
	    unix_comment = 1;		/* we are not in unix-style
					   comment */

	 *e_com = *buf_ptr++;
	 if (buf_ptr >= buf_end)
	    fill_buffer();

	 if (*e_com == '\t')		/* keep track of column */
	    now_col = ((now_col - 1) & tabmask) + tabsize + 1;
	 else if (*e_com == '\b')	/* this is a backspace */
	    --now_col;
	 else
	    ++now_col;

	 if (*e_com == ' ' || *e_com == '\t')
	    last_bl = e_com;
	 /* remember we saw a blank */

	 ++e_com;
	 if (now_col > adj_max_col && !ps.box_com && unix_comment == 1 && e_com[-1] > ' ')
	 {
	    /* the comment is too long, it must be broken up */
	    if (break_delim == 1 && s_com[0] == '/'
		&& s_com[1] == '*' && s_com[2] == ' ')
	    {
	       char           *t = e_com;
	       break_delim = 2;
	       e_com = s_com + 2;
	       *e_com = 0;
	       if (bl_bef_bk)
		  prefix_blankline_requested = 1;
	       dump_line();
	       e_com = t;
	       s_com[0] = s_com[1] = s_com[2] = ' ';
	    }
	    if (last_bl == 0)
	    {				/* we have seen no blanks */
	       last_bl = e_com;		/* fake it */
	       *e_com++ = ' ';
	    }
	    *e_com = '\0';		/* print what we have */
	    *last_bl = '\0';
	    while (last_bl > s_com && last_bl[-1] < 040)
	       *--last_bl = 0;
	    e_com = last_bl;
	    dump_line();

	    *e_com++ = ' ';		/* add blanks for continuation */
	    *e_com++ = ' ';
	    *e_com++ = ' ';

	    t_ptr = last_bl + 1;
	    last_bl = 0;
	    if (t_ptr >= e_com)
	    {
	       while (*t_ptr == ' ' || *t_ptr == '\t')
		  t_ptr++;
	       while (*t_ptr != '\0')
	       {			/* move unprinted part of
					   comment down in buffer */
		  if (*t_ptr == ' ' || *t_ptr == '\t')
		     last_bl = e_com;
		  *e_com++ = *t_ptr++;
	       }
	    }
	    *e_com = '\0';
	    now_col = count_spaces(ps.com_col, s_com);	/* recompute current
							   position */
	 }
	 break;
      }
   }
}
