/* Textformatter for vi, with justifying

   usage: mg width [any_arg]
	  where width = line length of formatted text
	  if width < 0 then wrap lines without justifying
   if "any_arg" given:
     format paragraphs (stop filling line if next indent is greater)
   else:
     format lists (stop filling line if next indent is smaller)

*/

#include <stdio.h>

#ifndef uchar
#define uchar unsigned char
#endif

#define REPLACE		('\377' & 0xff)		/* must not appear in text */

static uchar line0[1024], line1[1024], word[1024];
static ai[3], nl, limit, idflag;

/* ------------------------------------------------------------------- */

main(argc,argv)
  char *argv[];
  {
   int space, width, length, margin;

   sscanf(argv[1], "%d", &limit);
   if(limit > 0) margin = -1;
   else		{margin = -2; limit = -limit;}
   idflag = 2*argc - 5;			/* 1 if paragraph, -1 if list */

   for(width=0; getword(&length) != EOF; nl=0)
     {
      space = width ? 1 : ai[1-nl];
      if((width += length+space) > limit)
	{
	 putword(margin);
	 if(ai[2-nl] >= 0 && (ai[2-nl] - ai[1-nl]) * idflag < 0)
	    space = ai[2-nl];
	 else space = ai[1-nl];
	 width = space + length;
	}
      putword(space);
      if(nl && (ai[1] - ai[0]) * idflag > 0) {putword(-2); width = 0;}
     }
   if(width) putword(-2);
   return 0;
  }

/* ------------------------------------------------------------------- */

/* store words and print justified lines;
   sp >= 0: store word, store sp as indent if actai < 0
   sp = -1: justify margin, write line
   sp = -2: don't justify, write line
   on writing, transform REPLACE into blanks and blanks into tabs
*/

putword(sp)
  {
   static uchar line[1024], *p;
   static actai = -1, wcnt;
   char *strcpy(), *strchr();
   int l, size, fill, dfill;

   if(sp >= 0)
     {
      if(actai < 0) {actai = sp; p = line; wcnt = 0;}
      strcpy(p,word); p = (uchar *)strchr(p,'\0') + 1; ++wcnt;
      return 0;
     }

   size = limit - actai - (p-line) + wcnt;
   
   if(wcnt > 1 && size > wcnt-1 && sp == -1)
	{fill = size/(wcnt-1); dfill = size%(wcnt-1);}
   else {fill = 1; dfill = 0;}

   for(l = actai>>3; l--;) putchar('\t');
   for(actai &= 7; actai--;) putchar(' ');
   for(p=line; *p != '\0'; ++p) putchar(*p == REPLACE ? ' ' : *p);

   p = line;
   while(--wcnt > 0)
     {
      for(l = fill + (wcnt<=dfill); l--;) putchar(' ');
      printf("%s", p = (uchar *)strchr(p, '\0') + 1);
     }
   putchar('\n'); actai = -1;
  }

/* ------------------------------------------------------------------- */

/* read 1 word
   return: EOF if EOF and no word remains, 0 else
   return length of word in *psize
*/

getword(psize)
  int *psize;
  {
   int c;

   while((c=get(0)) == ' ');
   if(c == EOF) return EOF;
   get(1);
   for(*psize = 0; (c=get(0)) != EOF;)
     if(c != ' ') word[(*psize)++] = c; else break;
   word[*psize] = '\0';
   get(1);
   return 0;
  }

/* ------------------------------------------------------------------- */

/* return next character (unget=0) or unget input (unget=1);
   return \n as ' ';
   call getline and set ai, detect EOF
*/

get(unget)
  {
   static uchar *p = NULL;
   static flag = 0;
   int c;
   
   if(unget) {--p; return 0;}		/* get(0) called before */
   if(p == NULL)
     {
      ai[0] = ai[1] = getline(p=line0+1); ai[2] = getline(line1+1);
      if(idflag == -1 && ai[1] < ai[2]) join(line0+ai[1]+1, ai[2]-ai[1]);
      *line0 = *line1 = ' ';
     }
   if((c = *p++) == '\n')
     {
      ai[0] = ai[1]; ai[1] = ai[2];
      if(flag ^= 1) {ai[2] = getline(line0+1); p = line1;}
      else          {ai[2] = getline(line1+1); p = line0;}
      if(idflag == -1 && ai[0] > ai[1] && ai[1] < ai[2])
	join(flag ? line1+ai[1]+1 : line0+ai[1]+1, ai[2]-ai[1]);
      c = *p++;
      nl = 1;
     }
   return ai[1] < 0 ? EOF : c;
  }

/* ------------------------------------------------------------------- */

/* read next line, convert tabs, remove trailing blanks,
   return actual indent or -1 if EOF
*/

getline(buf)
  uchar buf[];
  {
   char *fgets();
   uchar locbuf[1024];
   int c, d, lpos, bpos, epos;

   if(fgets(locbuf, 1024, stdin) == NULL) return -1;
   lpos = bpos = epos = 0;

   while((c=locbuf[lpos++]) != '\n')
     {
      if(c == '\t') {c = ' '; d = 8 - (bpos&7);}
      else d = 1;
      while(d--) buf[bpos++] = c;
      if(c != ' ') epos = bpos;
     }

   buf[epos] = '\n';

   for(bpos=0; buf[bpos] == ' '; ++bpos);
   return bpos;
  }

/* ------------------------------------------------------------------- */

/* join some words by replacing blanks by REPLACE;
   input: buf - input buffer
	  w   - least size of joined word
*/

join(buf,w)
  uchar *buf;
  {
   for(; w--; ++buf)
     if(*buf == '\n') return;
     else if(*buf == ' ') *buf = REPLACE;
  }

/* ------------------------------------------------------------------- */
