
/*
  ( cd xorriso ; cc -g -Wall -o make_xorriso_1 make_xorriso_1.c )
*/
/*
   Specialized converter from xorriso/xorriso.texi to xorriso/xorriso.1.

   The conversion rules are described at the beginning of xorriso/xorriso.texi

   Copyright 2010 Thomas Schmitt, <scdbackup@gmx.net>

   Provided under GPL version 2 or later.
*/

#ifdef HAVE_CONFIG_H
#include "../config.h"
#endif

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>


/* The conversion state
*/
struct Mx1 {

 char prog[4096];

 int count_in;
 int count_out;

 int skipping;          /* <0 stacked skipping , 0= no , >0 counting down */
 
};


int Mx1_init(struct Mx1 *m, char *prog, int flag)
{
 strncpy(m->prog, prog, sizeof(m->prog) - 1);
 m->prog[sizeof(m->prog) - 1]= 0;
 m->count_in= 0;
 m->count_out= 0;
 m->skipping= 0;
 return(1);
}


int Mx1_report_error(struct Mx1 *m, char *text, int flag)
{
 fprintf(stderr, "%s : line %d : %s\n", m->prog, m->count_in, text);
 return(1);
}


int Mx1__get_word(char *line, char word[256], char **remainder, int flag)
{
 char *cpt, *start;
 int l;

 word[0]= 0;
 *remainder= NULL;
 for(cpt= line; *cpt != 0 && isspace(*cpt); cpt++);
 if(*cpt == 0)
   return(0);
 start= cpt;
 for(cpt= line; *cpt != 0 && ! isspace(*cpt); cpt++);
 l= cpt - start;
 if(l > 0)
   strncpy(word, start, l);
 word[l]= 0;
 *remainder= cpt;
 return(1);
}


int Mx1_substitute(struct Mx1 *m, char line_in[256], char line_out[256],
                   int raw, int flag)
{
 char *rpt, *wpt, *ept;
 int l;

 wpt= line_out;
 for(rpt= line_in; *rpt != 0; rpt++) {
   if(rpt - line_in < raw) {
     *(wpt++)= *rpt;
 continue;
   } 
   if(*rpt == '@') {
     if(strncmp(rpt, "@strong{", 8) == 0) {
       /* @strong{-...} gets mapped to \fB\-...\fR . */;
       /* @strong{... } gets mapped to \fB...\fR . */
       ept= strchr(rpt, '}');
       if(ept == NULL) {
         Mx1_report_error(m, "No closing bracket found for '@strong{'", 0);
         return(-1);
       }
       l= ept - rpt - 8;
       if((wpt - line_out) + l + 6 + (rpt[8] == '-') > 255)
         goto overflow;
       strcpy(wpt, "\\fB");
       wpt+= 3;
       if(rpt[8] == '-')
         *(wpt++)= '\\';
       strncpy(wpt, rpt + 8, l);
       wpt+= l;
       strcpy(wpt, "\\fR");
       wpt+= 3;
       rpt+= ept - rpt;
     } else if(strncmp(rpt, "@minus{}", 8) == 0) {
       /* @minus{} will become "-". */
       if((wpt - line_out) + 1 > 255)
         goto overflow;
       *(wpt++)= '-';
       rpt+= 7;
     } else if(strncmp(rpt, "@@", 2) == 0 ||
               strncmp(rpt, "@{", 2) == 0 ||
               strncmp(rpt, "@}", 2) == 0) {
       /* @@ , @{, @} will get stripped of their first @. */
       *(wpt++)= *(rpt + 1);
       rpt++;
     }  
   } else if(*rpt == '\\') {
     /* "\" becomes "\\" */
     if((wpt - line_out) + 2 > 255)
       goto overflow;
     *(wpt++)= '\\';
     *(wpt++)= '\\';
   } else if((wpt - line_out) + 1 > 255) {
overflow:;
     Mx1_report_error(m, "Line length overflow while text substitution", 0);
     return(-1);
   } else
     *(wpt++)= *rpt;
 }
 *wpt= 0;
 return(1);
}


/*
  @return 1= line_out is valid, 0= do not put out line_out, -1 = error
*/
int Mx1_convert(struct Mx1 *m, char line_in[256], char line_out[256], int flag)
{
 int l, num, keep= 0, ret, raw;
 char word[256], buf[256], *remainder;

 m->count_in++;
 l= strlen(line_in);

 if(m->skipping > 0) {
   m->skipping--;
   return(0);
 }

 /* The first line gets discarded. */
 if(m->count_in == 1)
   return(0);

 /* Line start "@c man " will become "", the remainder is put out unaltered. */
 if(strncmp(line_in, "@c man ", 7) == 0) {
   strcpy(line_out, line_in + 7);
   m->count_out++;
   return(1);
 }

 /* Lines "@*" will be converted to ".br" */
 if(strcmp(line_in, "@*") == 0) {
   strcpy(line_out, ".br");
   m->count_out++;
   return(1);
 }

 /* @c man-ignore-lines N  will discard N following lines.
    "@c man-ignore-lines begin" discards all following lines
    up to "@c man-ignore-lines end".
 */
 if(strncmp(line_in, "@c man-ignore-lines ", 20) == 0) {
   if(strcmp(line_in + 20, "begin") == 0) {
     m->skipping--;
     return(0);
   } else if(strcmp(line_in + 20, "end") == 0) {
     if(m->skipping < 0)
       m->skipping++;
     return(0);
   } else if(m->skipping == 0) {
     num= 0;
     sscanf(line_in + 20, "%d", &num);
     if(num > 0) {
       m->skipping= num;
       return(0);
     }
   }
   Mx1_report_error(m, "Inappropriate use of '@c man-ignore-lines'", 0);
   return(-1);
 }

 /* Line blocks of "@menu" "@end menu" will be discarded. */
 if(strcmp(line_in, "@menu") == 0) {
   m->skipping--;
   return(0);
 }
 if(strcmp(line_in, "@end menu") == 0) {
   if(m->skipping < 0)
      m->skipping++;
   return(0);
 }
 if(m->skipping)
   return(0);

 /* "@item -word words" becomes "\fB\-word\fR words". */
 /* "@item word words" becomes "\fBword\fR words". */
 if(strncmp(line_in, "@item ", 6) == 0) {
   ret= Mx1__get_word(line_in + 6, word, &remainder, 0);
   if(ret <= 0) {
     Mx1_report_error(m, "Found no word after @item", 0);
     return(0);
   }
   strcpy(buf, "\\fB");
   if(word[0] == '-') {
     if(l >= 255) {
       Mx1_report_error(m, "Line length overflow while converting @item", 0);
       return(-1);
     }
     strcat(buf, "\\");
   }
   strcat(buf, word);
   strcat(buf, "\\fR");
   raw= strlen(buf);
   strcat(buf, remainder);
   ret= Mx1_substitute(m, buf, line_out, raw, 0);
   if(ret <= 0)
     return(-1);
   m->count_out++;
   return(1);
 }

 /* @strong{-...} gets mapped to \fB\-...\fR . */
 /* @strong{... } gets mapped to \fB...\fR . */
 /* @minus{} will become "-". */
 /* @@ , @{, @} will get stripped of their first @. */
 /* "\" becomes "\\" */
 if(line_in[0] != '@' ||
    strncmp(line_in, "@strong{", 8) == 0 ||
    strncmp(line_in, "@minus{}", 8) == 0 ||
    strncmp(line_in, "@@", 2) == 0 ||
    strncmp(line_in, "@{", 2) == 0 ||
    strncmp(line_in, "@}", 2) == 0) {
   keep= 1;
   ret= Mx1_substitute(m, line_in, line_out, 0, 0);
   if(ret <= 0)
     return(-1);
 }

 /* Other lines which begin by "@" will be discarded. */
 if(! keep) {
   if(line_in[0] == '@')
     return(0);
   strcpy(line_out, line_in);
 }
 m->count_out++;
 return(1);
}


int main(int argc, char **argv)
{
 int ret, l, as_filter= 0, i;
 char line_in[256], line_out[256], *got;
 static char name_in[]= {"xorriso/xorriso.texi"};
 static char name_out[]= {"xorriso/xorriso.1"};
 struct Mx1 m;
 FILE *fp_in= stdin, *fp_out= stdout;
 
 Mx1_init(&m, argv[0], 0);

 if(argc < 2) {
usage:;
   fprintf(stderr, "usage:  %s -auto|-filter\n", argv[0]);
   fprintf(stderr, "  -auto     xorriso/xorriso.texi -> xorriso/xorriso.1\n");
   fprintf(stderr, "  -filter   stdin                -> stdout\n");
   exit(2);
 }
 for(i= 1; i < argc; i++) {
   if(strcmp(argv[i], "-filter") == 0)
     as_filter= 1;
   else if(strcmp(argv[i], "-auto") == 0)
     as_filter= 0;
   else {
     fprintf(stderr, "%s : unknown option %s\n", argv[0], argv[i]);
     goto usage;
   }
 }

 if(!as_filter) {
   fp_in= fopen(name_in, "r");
   if(fp_in == NULL) {
     fprintf(stderr, "%s : failed to fopen( %s ,r) : %d %s\n",
             argv[0], name_in, errno, strerror(errno));
     exit(3);
   }
   fp_out= fopen(name_out, "w");
   if(fp_out == NULL) {
     fprintf(stderr, "%s : failed to fopen( %s ,w) : %d %s\n",
             argv[0], name_out, errno, strerror(errno));
     exit(4);
   }
 }
 while(1) {
   got= fgets(line_in, sizeof(line_in), fp_in);
   if(got == NULL)
 break;
   l= strlen(line_in);
   while(l > 0) {
     if(line_in[l - 1] == '\r' || line_in[l - 1] == '\n') {
       line_in[l - 1] = 0;
       l--;
     } else
   break;
   }
   ret= Mx1_convert(&m, line_in, line_out, 0);
   if(ret < 0)
     exit(1);
   if(ret == 0)
 continue;
   fprintf(fp_out, "%s\n", line_out);
 }
 exit(0);
}
