; /*
gcc -noixemul wordwrap.c -o wordwrap
quit 0 ; */
/*************************************************************************\
  wordwrap.c:
    Reformat a text on the input stream to a given maximum line length.
  arguments and their meanings:
    -l<len>         line length, defaults to 75
    -b              protect blank lines
    -i              protect indentation
    -i<indent>       " , enforcing a fixed indentation width
    -ic             convert indentation to blank lines
    -ia[<indent>]   add blank lines before indented paragraphs
    -s<len>         protect short lines
    -w              enforce wide space between sentences (after ".!?")
    -W               " , and after colons (":"), too
    -m<width>       add a left margin
    -M<width>       ignore a left margin on the input
    -e<escword>     enable 'escaped' sections
\*************************************************************************/

#include <stdio.h>
#include <string.h>

char version[] = "$VER: wordwrap 1.3 (15.09.96)";

#define MAXLEN 200  /* max. size of an input word */
typedef unsigned char UBYTE;   /* to make isspace() etc. work ... */

int shortchars = 0;
int widespace = 0;

int getword(UBYTE s[], int lim);
int scrollmode(UBYTE *stop_at, int imrg, int omrg);
void help(UBYTE *s);

int main(int argc, UBYTE* argv[])
{
  int i, lword, lline, dented;
  UBYTE *s, c, word[MAXLEN], newline[MAXLEN], escword[MAXLEN];
  /* adjustable parameters: */
  int lmax = 75, blanks = 0, indent = 0, i2blank = 0;
  unsigned int imargin = 0, omargin = 0;

  escword[0] = '\0';
  while (--argc) {
    s = *++argv;
    if (*s++ == '-') {
      switch (*s++) {
        case 'l': lmax = atoi(s); break;
        case 'b': blanks = 1; break;
        case 's': shortchars = atoi(s); break;
        case 'e': strcpy(escword, s); break;
        case 'w': widespace = 1; break;
        case 'W': widespace = 2; break;
        case 'm': omargin = atoi(s); break;
        case 'M': imargin = atoi(s); break;
        case 'i': indent = -1;
          if (*s == 'c') {
            indent = 0; i2blank = 1;
          }
          if (*s == 'a') {
            i2blank = 1; s++;
          }
          if (isdigit(*s)) indent = atoi(s);
          break;
        default: help(argv[0]); return 10;
      }
    } else {
      help(argv[0]); return 10;
    }
  }
  /* prepare the left margin string: */
  if (omargin>MAXLEN-2) omargin = MAXLEN-2;
  newline[0] = '\n';
  for (i=1; i<=omargin; i++) newline[i] = ' ';
  newline[omargin+1] = '\0';
  /* let's go */
  lline = 0; dented = 0;
  while (lword = getword(word, MAXLEN)) {
    if ((c = word[lword-1]) == '\n')  /* "short line" break request? */
      word[--lword] = '\0';
    if (lword == 0) {      /* this was even a blank line! */
      if (blanks) {
        if (lline) printf("%s", newline);
        printf("%s", newline); lline = 0;
      }
    } else if (isspace(word[0])) {   /* indented line */
      if ((indent || i2blank) && lword>imargin) {
        if (lline) printf("%s", newline);
        if (i2blank) printf("%s", newline);
        dented = 1;
        if (indent>0) {
          for (lline = 0; lline<indent; lline++) putchar(' ');
        } else if (indent) {
          printf("%s", &word[imargin]); lline = lword-imargin;
        } else {
          lline = 0; dented = 0;
        }
      }
    } else if (strcmp(word, escword) == 0) {   /* the escape word! */
      if (lline) printf("%s", newline);
      lline = scrollmode(escword, imargin, omargin);
    } else {    /* regular word */
      if (lline == 0 || dented) {   /* at the start of the line */
        printf("%s", word); lline += lword; dented = 0;
      } else {     /* append to an existing line */
        if (lline + lword < lmax) {
          printf(" %s", word); lline += lword + 1;
        } else {
          printf("%s%s", newline, word); lline = lword;
        }
      }
      if (c == '\n') {   /* "short line" break pending */
        printf("%s", newline); lline = 0;
      }
    }
  }
  if (lline)
    putchar('\n');
  return 0;
}


void help(UBYTE *s)
/* print a help text and nag about illegal parameter <s> */
{
  if (s) fprintf(stderr,"illegal option '%s'\n", s);
  fprintf(stderr,"'wordwrap' command line parameters:\n", s);
  fprintf(stderr," -l<len>                line length, defaults to 75\n");
  fprintf(stderr," -b                     protect blank lines\n");
  fprintf(stderr," -i / -i<width> / -ic   protect / convert indentation\n");
  fprintf(stderr," -ia / -ia<width>       add blank lines before indentations\n");
  fprintf(stderr," -s<len>                protect lines shorter than <len>\n");
  fprintf(stderr," -w / -W                wide spaces after '.!?' (and ':')\n");
  fprintf(stderr," -m<width> / -M<width>  add / strip left margin\n");
  fprintf(stderr," -e<escword>            enable 'escaped' sections\n");
}


int getword(UBYTE s[], int lim)
/* Copies one word of the input stream to s[] (return value is strlen(s)),
 * then skips all subsequent spaces, stopping at the next word or EOL.
 * - will return "\n" for an empty line
 * - will return a string of blanks for a line starting with blanks
 * - will append " " to a word ending a sentence (if widespace!=0)
 * - will append "\n" to the last word on a "short" line
 * - will return an empty string at EOF
 */
{
  int c, i = 0;
  static int nonblanks = 0;

  c = getchar();
  if (c == EOF) {
    s[0] = '\0'; return 0;
  }
  if (isspace(c)) {  /* indented line (or even a blank line?) */
    while (isspace(c) && c != '\n') {
      if (i<lim-1) s[i++] = c;
      c = getchar();
    }
    if (c == '\n' || c == EOF) {
      i = 0; s[i++] = '\n';   /* yeah, a blank line */
    }
  } else {       /* read a word of non-blanks */
    while (isgraph(c)) {
      if (i<lim-1)
        s[i++] = c;
      else {
        fprintf(stderr, "warning: long input word (> %d chars)\n", lim);
        break;
      }
      c = getchar();
    }
    nonblanks += i;
    while (isspace(c) && c != '\n')    /* skip blanks */
      c = getchar();
    if (widespace && i>2 && isalnum(s[i-3]))  /* check for end of sentence */
      switch(s[i-1]) {
        case '.': case '!': case '?':  s[i++] = ' '; break;
        case ':': if (widespace>1) s[i++] = ' '; break;
      }
    if (c == '\n') {         /* was this a "short line"? */
      if (nonblanks <= shortchars) s[i++] = '\n';
      nonblanks = 0;
    }
  }
  if (isgraph(c))   /* did we stop on a non-blank character? */
    ungetc(c, stdin);
  s[i] = '\0'; return i;
}


int scrollmode(UBYTE *stop_at, int imrg, int omrg)
/* copy line by line, only adjusting the left margin */
/* return value is the number of characters on the current output line */
{
  int c, i, lchars, lines=0;
  UBYTE s[MAXLEN];
  
  fflush(stdout); fprintf(stderr, "\e[2m"); fflush(stderr);
  s[i=0] = '\0'; lchars = 0;
  c = getchar();
  while(c != EOF && strcmp(s, stop_at)) {
    if (lchars == 0) {        /* margin handling */
      for (i = 0; i<omrg; i++)  putchar(' ');
      i = imrg; while(i-->0 && isspace(c)) c = getchar();
    }
    i=0;
    while (isgraph(c) && i<MAXLEN-1) {   /* accumulate a word */
      s[i++] = c; c = getchar();
    }
    s[i] = '\0';
    if (strcmp(s, stop_at)) {    /* and (probably) print it */
      printf("%s", s); lchars += i;
    } else {               /* else swallow trailing blanks and exit */
      while (isspace(c) && c != '\n')
        c = getchar();
      break;
    }
    while (isspace(c)) {         /* copy blanks */
      if (c == '\n') {
        putchar(c); c = getchar();
        lines++; lchars = 0; break;
      } else {
        putchar(c); c = getchar();
        lchars++;
      }
    }
  }
  if (isgraph(c))   /* did we stop on a non-blank character? */
    ungetc(c, stdin);
  fflush(stdout); fprintf(stderr, "\e[0m"); fflush(stderr);
  return lchars;
}

