[BACK]Return to util.c CVS log [TXT][DIR] Up to [cvsweb.bsd.lv] / texi2mdoc

Diff for /texi2mdoc/util.c between version 1.7 and 1.23

version 1.7, 2015/02/23 11:44:30 version 1.23, 2015/03/02 18:12:53
Line 20 
Line 20 
 #include <assert.h>  #include <assert.h>
 #include <ctype.h>  #include <ctype.h>
 #include <fcntl.h>  #include <fcntl.h>
 #include <getopt.h>  
 #include <libgen.h>  
 #include <limits.h>  #include <limits.h>
 #include <stdarg.h>  #include <stdarg.h>
 #include <stdio.h>  #include <stdio.h>
Line 43  texifilepop(struct texi *p)
Line 41  texifilepop(struct texi *p)
   
         assert(p->filepos > 0);          assert(p->filepos > 0);
         f = &p->files[--p->filepos];          f = &p->files[--p->filepos];
         munmap(f->map, f->mapsz);          free(f->map);
 }  }
   
 static void  static void
Line 79  texiexit(struct texi *p)
Line 77  texiexit(struct texi *p)
   
         /* Make sure we're newline-terminated. */          /* Make sure we're newline-terminated. */
         if (p->outcol)          if (p->outcol)
                 putchar('\n');                  fputc('\n', p->outfile);
           if (NULL != p->chapters)
                   teximdocclose(p, 1);
   
         /* Unmap all files. */          /* Unmap all files. */
         while (p->filepos > 0)          while (p->filepos > 0)
Line 122  texiabort(struct texi *p, const char *errstring)
Line 122  texiabort(struct texi *p, const char *errstring)
 void  void
 texiwarn(const struct texi *p, const char *fmt, ...)  texiwarn(const struct texi *p, const char *fmt, ...)
 {  {
         va_list  ap;          va_list                  ap;
           const struct texifile   *f;
   
         fprintf(stderr, "%s:%zu:%zu: warning: ",          f = &p->files[p->filepos - 1];
                 p->files[p->filepos - 1].name,  
                 p->files[p->filepos - 1].line + 1,          if (f->insplice)
                 p->files[p->filepos - 1].col + 1);                  fprintf(stderr, "%s:%zu:%zu (%zuB left in splice): "
                           "warning: ", f->name, f->line + 1,
                           f->col + 1, f->insplice);
           else
                   fprintf(stderr, "%s:%zu:%zu: warning: ",
                           f->name, f->line + 1, f->col + 1);
   
         va_start(ap, fmt);          va_start(ap, fmt);
         vfprintf(stderr, fmt, ap);          vfprintf(stderr, fmt, ap);
         va_end(ap);          va_end(ap);
Line 141  texiwarn(const struct texi *p, const char *fmt, ...)
Line 148  texiwarn(const struct texi *p, const char *fmt, ...)
 void  void
 texierr(struct texi *p, const char *fmt, ...)  texierr(struct texi *p, const char *fmt, ...)
 {  {
         va_list  ap;          va_list          ap;
           struct texifile *f;
   
         fprintf(stderr, "%s:%zu:%zu: error: ",          f = &p->files[p->filepos - 1];
                 p->files[p->filepos - 1].name,  
                 p->files[p->filepos - 1].line + 1,          if (f->insplice)
                 p->files[p->filepos - 1].col + 1);                  fprintf(stderr, "%s:%zu:%zu: (%zuB left in splice): "
                           "error: ", f->name, f->line + 1,
                           f->col + 1, f->insplice);
           else
                   fprintf(stderr, "%s:%zu:%zu: error: ",
                           f->name, f->line + 1, f->col + 1);
   
         va_start(ap, fmt);          va_start(ap, fmt);
         vfprintf(stderr, fmt, ap);          vfprintf(stderr, fmt, ap);
         va_end(ap);          va_end(ap);
Line 157  texierr(struct texi *p, const char *fmt, ...)
Line 171  texierr(struct texi *p, const char *fmt, ...)
   
 /*  /*
  * Put a single data character to the output if we're not ignoring.   * Put a single data character to the output if we're not ignoring.
  * Makes sure we don't spurriously start a macro.   * Escape starting a line with a control character and slashes.
  * Adjusts our output status.  
  * This shouldn't be called for macros: just for ordinary text.  
  */   */
 void  void
 texiputchar(struct texi *p, char c)  texiputchar(struct texi *p, char c)
Line 167  texiputchar(struct texi *p, char c)
Line 179  texiputchar(struct texi *p, char c)
   
         if (p->ign)          if (p->ign)
                 return;                  return;
   
         if ('.' == c && 0 == p->outcol)          if ('.' == c && 0 == p->outcol)
                 fputs("\\&", stdout);                  fputs("\\&", p->outfile);
           if ('\'' == c && 0 == p->outcol)
                   fputs("\\&", p->outfile);
   
         putchar(c);          if (p->uppercase)
                   fputc(toupper((unsigned int)c), p->outfile);
           else
                   fputc(c, p->outfile);
           if ('\\' == c)
                   fputc('e', p->outfile);
         p->seenvs = 0;          p->seenvs = 0;
         if ('\n' == c) {          if ('\n' == c) {
                 p->outcol = 0;                  p->outcol = 0;
Line 181  texiputchar(struct texi *p, char c)
Line 199  texiputchar(struct texi *p, char c)
 }  }
   
 /*  /*
  * Put multiple characters (see texiputchar()).   * Put an opaque series of characters.
  * This shouldn't be called for macros: just for ordinary text.   * Characters starting a line with a control character are escaped, but
    * that's it, so don't use this for non-controlled sequences of text.
  */   */
 void  void
 texiputchars(struct texi *p, const char *s)  texiputchars(struct texi *p, const char *s)
 {  {
   
         while ('\0' != *s)          if (p->ign)
                 texiputchar(p, *s++);                  return;
           if ('.' == *s && 0 == p->outcol)
                   fputs("\\&", p->outfile);
           if ('\'' == *s && 0 == p->outcol)
                   fputs("\\&", p->outfile);
           if (p->uppercase)
                   for ( ; '\0' != *s; s++)
                           p->outcol += fputc(toupper
                                   ((unsigned int)*s), p->outfile);
           else
                   p->outcol += fputs(s, p->outfile);
           p->seenvs = 0;
 }  }
   
 /*  /*
    * This puts all characters onto the output stream but makes sure to
    * escape mdoc(7) slashes.
    * FIXME: useless.
    */
   void
   texiputbuf(struct texi *p, size_t start, size_t end)
   {
   
           for ( ; start < end; start++)
                   texiputchar(p, BUF(p)[start]);
   }
   
   /*
  * Close an mdoc(7) macro opened with teximacroopen().   * Close an mdoc(7) macro opened with teximacroopen().
  * If there are no more macros on the line, prints a newline.   * If there are no more macros on the line, prints a newline.
  */   */
Line 204  teximacroclose(struct texi *p)
Line 247  teximacroclose(struct texi *p)
                 return;                  return;
   
         if (0 == --p->outmacro) {          if (0 == --p->outmacro) {
                 putchar('\n');                  fputc('\n', p->outfile);
                 p->outcol = p->seenws = 0;                  p->outcol = p->seenws = 0;
         }          }
 }  }
Line 224  teximacroopen(struct texi *p, const char *s)
Line 267  teximacroopen(struct texi *p, const char *s)
                 return;                  return;
   
         if (p->outcol && 0 == p->outmacro) {          if (p->outcol && 0 == p->outmacro) {
                 putchar('\n');                  fputc('\n', p->outfile);
                 p->outcol = 0;                  p->outcol = 0;
         }          }
   
         if (0 == p->outmacro)          if (0 == p->outmacro)
                 putchar('.');                  fputc('.', p->outfile);
         else          else
                 putchar(' ');                  fputc(' ', p->outfile);
   
         if (EOF != (rc = fputs(s, stdout)))          if (EOF != (rc = fputs(s, p->outfile)))
                 p->outcol += rc;                  p->outcol += rc;
   
         putchar(' ');          fputc(' ', p->outfile);
         p->outcol++;          p->outcol++;
         p->outmacro++;          p->outmacro++;
         p->seenws = 0;          p->seenws = 0;
Line 258  teximacro(struct texi *p, const char *s)
Line 301  teximacro(struct texi *p, const char *s)
                 texierr(p, "\"%s\" in a literal scope!?", s);                  texierr(p, "\"%s\" in a literal scope!?", s);
   
         if (p->outcol)          if (p->outcol)
                 putchar('\n');                  fputc('\n', p->outfile);
   
         putchar('.');          fputc('.', p->outfile);
         puts(s);          fputs(s, p->outfile);
           fputc('\n', p->outfile);
         p->outcol = p->seenws = 0;          p->outcol = p->seenws = 0;
 }  }
   
Line 283  texivspace(struct texi *p)
Line 327  texivspace(struct texi *p)
  * in the current input file.   * in the current input file.
  */   */
 void  void
 advance(struct texi *p, const char *buf, size_t *pos)  advance(struct texi *p, size_t *pos)
 {  {
           struct texifile *f;
   
         if ('\n' == buf[*pos]) {          f = &p->files[p->filepos - 1];
                 p->files[p->filepos - 1].line++;  
                 p->files[p->filepos - 1].col = 0;  
         } else  
                 p->files[p->filepos - 1].col++;  
   
           if (0 == f->insplice) {
                   if ('\n' == BUF(p)[*pos]) {
                           f->line++;
                           f->col = 0;
                   } else
                           f->col++;
           } else {
                   --f->insplice;
                   if (0 == f->insplice)
                           f->depth = 0;
           }
   
         (*pos)++;          (*pos)++;
 }  }
   
Line 303  advance(struct texi *p, const char *buf, size_t *pos)
Line 356  advance(struct texi *p, const char *buf, size_t *pos)
  * appropriately flush-left punctuation alongside the macro.   * appropriately flush-left punctuation alongside the macro.
  */   */
 void  void
 texipunctuate(struct texi *p, const char *buf, size_t sz, size_t *pos)  texipunctuate(struct texi *p, size_t *pos)
 {  {
         size_t   start, end;          size_t   start, end;
   
         if (1 != p->outmacro)          if (1 != p->outmacro)
                 return;                  return;
   
         for (start = end = *pos; end < sz; end++) {          for (start = end = *pos; end < BUFSZ(p); end++) {
                 switch (buf[end]) {                  switch (BUF(p)[end]) {
                 case (','):                  case (','):
                 case (')'):                  case (')'):
                 case ('.'):                  case ('.'):
                 case ('"'):                  case ('"'):
                 case (':'):                  case (':'):
                   case (';'):
                 case ('!'):                  case ('!'):
                 case ('?'):                  case ('?'):
                         continue;                          continue;
Line 327  texipunctuate(struct texi *p, const char *buf, size_t 
Line 381  texipunctuate(struct texi *p, const char *buf, size_t 
         }          }
         if (end == *pos)          if (end == *pos)
                 return;                  return;
         if (end + 1 == sz || ' ' == buf[end] || '\n' == buf[end]) {          if (end + 1 == BUFSZ(p) || ' ' == BUF(p)[end] ||
                   '\n' == BUF(p)[end]) {
                 for ( ; start < end; start++) {                  for ( ; start < end; start++) {
                         texiputchar(p, ' ');                          texiputchar(p, ' ');
                         texiputchar(p, buf[start]);                          texiputchar(p, BUF(p)[start]);
                         advance(p, buf, pos);                          advance(p, pos);
                 }                  }
         }          }
 }  }
Line 342  texipunctuate(struct texi *p, const char *buf, size_t 
Line 397  texipunctuate(struct texi *p, const char *buf, size_t 
  * doing so.   * doing so.
  */   */
 static size_t  static size_t
 advancenext(struct texi *p, const char *buf, size_t sz, size_t *pos)  advancenext(struct texi *p, size_t *pos)
 {  {
   
         if (p->literal) {          if (p->literal) {
                 while (*pos < sz && ismspace(buf[*pos])) {                  while (*pos < BUFSZ(p) && ismspace(BUF(p)[*pos])) {
                         if (*pos && '\n' == buf[*pos] &&                          texiputchar(p, BUF(p)[*pos]);
                                 '\\' == buf[*pos - 1])                          advance(p, pos);
                                 texiputchar(p, 'e');  
                         texiputchar(p, buf[*pos]);  
                         advance(p, buf, pos);  
                 }                  }
                 return(*pos);                  return(*pos);
         }          }
   
         while (*pos < sz && ismspace(buf[*pos])) {          while (*pos < BUFSZ(p) && ismspace(BUF(p)[*pos])) {
                 p->seenws = 1;                  p->seenws = 1;
                 /*                  /*
                  * If it looks like we've printed a double-line, then                   * If it looks like we've printed a double-line, then
                  * output a paragraph.                   * output a paragraph.
                  * FIXME: this is stupid.                   * FIXME: this is stupid.
                  */                   */
                 if (*pos && '\n' == buf[*pos] && '\n' == buf[*pos - 1])                  if (*pos && '\n' == BUF(p)[*pos] && '\n' == BUF(p)[*pos - 1])
                         texivspace(p);                          texivspace(p);
                 advance(p, buf, pos);                  advance(p, pos);
         }          }
         return(*pos);          return(*pos);
 }  }
   
 /*  /*
  * Advance to the EOLN in the input stream.   * Advance to the EOLN in the input stream.
  * NOTE: THIS SHOULD NOT BE CALLED ON BLANK TEXT, as it will read up to   * This will skip over '@' markers in an effort to ignore escaped
  * the @\n.   * newlines.
  */   */
 size_t  size_t
 advanceeoln(struct texi *p, const char *buf,  advanceeoln(struct texi *p, size_t *pos, int consumenl)
         size_t sz, size_t *pos, int consumenl)  
 {  {
   
         while (*pos < sz && '\n' != buf[*pos])          while (*pos < BUFSZ(p) && '\n' != BUF(p)[*pos]) {
                 advance(p, buf, pos);                  if ('@' == BUF(p)[*pos])
         if (*pos < sz && consumenl)                          advance(p, pos);
                 advance(p, buf, pos);                  advance(p, pos);
           }
           if (*pos < BUFSZ(p) && consumenl)
                   advance(p, pos);
         return(*pos);          return(*pos);
 }  }
   
Line 392  advanceeoln(struct texi *p, const char *buf, 
Line 446  advanceeoln(struct texi *p, const char *buf, 
  * current buffer greater than or equal to the current position.   * current buffer greater than or equal to the current position.
  */   */
 void  void
 advanceto(struct texi *p, const char *buf, size_t *pos, size_t end)  advanceto(struct texi *p, size_t *pos, size_t end)
 {  {
   
         assert(*pos <= end);          assert(*pos <= end);
         while (*pos < end)          while (*pos < end)
                 advance(p, buf, pos);                  advance(p, pos);
 }  }
   
 static void  static void
 texiexecmacro(struct texi *p, struct teximacro *m,  texiexecmacro(struct texi *p, struct teximacro *m, size_t sv, size_t *pos)
         const char *buf, size_t sz, size_t *pos)  
 {  {
         size_t    valsz, realsz, aasz, asz,          size_t            valsz, realsz, aasz, asz,
                   ssz, i, j, k, start, end;                             ssz, i, j, k, start, end;
         char     *val;          char             *val;
         char    **args;          char            **args;
           const char       *cp;
   
         args = argparse(p, buf, sz, pos, &asz);          /* Disregard empty macros. */
           if (0 == (valsz = realsz = strlen(m->value))) {
                   args = argparse(p, pos, &asz, m->argsz);
                   for (i = 0; i < asz; i++)
                           free(args[i]);
                   free(args);
                   return;
           }
   
           /*
            * This is important: it protect us from macros that invoke more
            * macros, possibly going on infinitely.
            * We use "sv" instead of the current position because we might
            * be invoked at the end of the macro (i.e., insplice == 0).
            * The "sv" value was initialised at the start of the macro.
            */
           if (sv > 0)
                   if (++p->files[p->filepos].depth > 64)
                           texierr(p, "maximium recursive depth");
   
           args = argparse(p, pos, &asz, m->argsz);
         if (asz != m->argsz)          if (asz != m->argsz)
                 texiwarn(p, "invalid macro argument length");                  texiwarn(p, "invalid macro argument length");
         aasz = asz < m->argsz ? asz : m->argsz;          aasz = asz < m->argsz ? asz : m->argsz;
   
         if (0 == aasz) {          if (0 == aasz) {
                 parseeof(p, m->value, strlen(m->value));                  texisplice(p, m->value, valsz, *pos);
                 return;                  return;
         }          }
   
         valsz = realsz = strlen(m->value);  
         val = strdup(m->value);          val = strdup(m->value);
   
         for (i = j = 0; i < realsz; i++) {          for (i = j = 0; i < realsz; i++) {
Line 458  texiexecmacro(struct texi *p, struct teximacro *m,
Line 531  texiexecmacro(struct texi *p, struct teximacro *m,
   
                 /*                  /*
                  * Argument didn't exist in argument table.                   * Argument didn't exist in argument table.
                  * No need to reallocate here: we just copy the text                   * Just ignore it.
                  * directly from the macro value into the buffer.  
                  */                   */
                 if (k == aasz) {                  if (k == aasz) {
                         for ( ; i < end; i++)                          i = end;
                                 val[j++] = m->value[i];  
                         assert('\\' == m->value[i]);  
                         val[j++] = m->value[i];  
                         val[j] = '\0';  
                         continue;                          continue;
                 }                  }
   
Line 477  texiexecmacro(struct texi *p, struct teximacro *m,
Line 545  texiexecmacro(struct texi *p, struct teximacro *m,
                                 texiabort(p, NULL);                                  texiabort(p, NULL);
                 }                  }
   
                 j = strlcat(val, args[k], valsz + 1);                  for (cp = args[k]; '\0' != *cp; cp++)
                           val[j++] = *cp;
   
                   val[j] = '\0';
                 i = end;                  i = end;
         }          }
   
         parseeof(p, val, strlen(val));          texisplice(p, val, strlen(val), *pos);
   
         for (i = 0; i < asz; i++)          for (i = 0; i < asz; i++)
                 free(args[i]);                  free(args[i]);
Line 495  texiexecmacro(struct texi *p, struct teximacro *m,
Line 566  texiexecmacro(struct texi *p, struct teximacro *m,
  * This also will advance the input stream.   * This also will advance the input stream.
  */   */
 static void  static void
 texiword(struct texi *p, const char *buf,  parseword(struct texi *p, size_t *pos, char extra)
         size_t sz, size_t *pos, char extra)  
 {  {
   
         if (p->seenws && 0 == p->outmacro &&          if (p->seenws && 0 == p->outmacro &&
Line 508  texiword(struct texi *p, const char *buf, 
Line 578  texiword(struct texi *p, const char *buf, 
   
         p->seenws = 0;          p->seenws = 0;
   
         while (*pos < sz && ! ismspace(buf[*pos])) {          while (*pos < BUFSZ(p) && ! ismspace(BUF(p)[*pos])) {
                 switch (buf[*pos]) {                  switch (BUF(p)[*pos]) {
                 case ('@'):                  case ('@'):
                 case ('}'):                  case ('}'):
                 case ('{'):                  case ('{'):
                         return;                          return;
                 }                  }
                 if ('\0' != extra && buf[*pos] == extra)                  if ('\0' != extra && BUF(p)[*pos] == extra)
                         return;                          return;
                 if (*pos < sz - 1 &&                  if (*pos < BUFSZ(p) - 1 &&
                          '`' == buf[*pos] &&                           '`' == BUF(p)[*pos] &&
                          '`' == buf[*pos + 1]) {                           '`' == BUF(p)[*pos + 1]) {
                         texiputchars(p, "\\(lq");                          texiputchars(p, "\\(lq");
                         advance(p, buf, pos);                          advance(p, pos);
                 } else if (*pos < sz - 1 &&                  } else if (*pos < BUFSZ(p) - 1 &&
                          '\'' == buf[*pos] &&                           '\'' == BUF(p)[*pos] &&
                          '\'' == buf[*pos + 1]) {                           '\'' == BUF(p)[*pos + 1]) {
                         texiputchars(p, "\\(rq");                          texiputchars(p, "\\(rq");
                         advance(p, buf, pos);                          advance(p, pos);
                 } else                  } else
                         texiputchar(p, buf[*pos]);                          texiputchar(p, BUF(p)[*pos]);
                 advance(p, buf, pos);                  advance(p, pos);
         }          }
 }  }
   
Line 539  texiword(struct texi *p, const char *buf, 
Line 609  texiword(struct texi *p, const char *buf, 
  * index after the command name.   * index after the command name.
  */   */
 enum texicmd  enum texicmd
 texicmd(struct texi *p, const char *buf, size_t pos,  texicmd(const struct texi *p, size_t pos, size_t *end, struct teximacro **macro)
         size_t sz, size_t *end, struct teximacro **macro)  
 {  {
         size_t   i, len, toksz;          size_t   i, len, toksz;
   
         assert('@' == buf[pos]);          assert('@' == BUF(p)[pos]);
   
         if (NULL != macro)          if (NULL != macro)
                 *macro = NULL;                  *macro = NULL;
   
         if ((*end = pos) == sz)          if ((*end = pos) == BUFSZ(p))
                 return(TEXICMD__MAX);                  return(TEXICMD__MAX);
         else if ((*end = ++pos) == sz)          else if ((*end = ++pos) == BUFSZ(p))
                 return(TEXICMD__MAX);                  return(TEXICMD__MAX);
   
         /* Alphabetic commands are special. */          /* Alphabetic commands are special. */
         if ( ! isalpha(buf[pos])) {          if ( ! isalpha((unsigned int)BUF(p)[pos])) {
                 if ((*end = pos + 1) == sz)                  if ((*end = pos + 1) == BUFSZ(p))
                         return(TEXICMD__MAX);                          return(TEXICMD__MAX);
                 for (i = 0; i < TEXICMD__MAX; i++) {                  for (i = 0; i < TEXICMD__MAX; i++) {
                         if (1 != texitoks[i].len)                          if (1 != texitoks[i].len)
                                 continue;                                  continue;
                         if (0 == strncmp(texitoks[i].tok, &buf[pos], 1))                          if (0 == strncmp(texitoks[i].tok, &BUF(p)[pos], 1))
                                 return(i);                                  return(i);
                 }                  }
                 texiwarn(p, "bad command: @%c", buf[pos]);                  texiwarn(p, "bad command: @%c", BUF(p)[pos]);
                 return(TEXICMD__MAX);                  return(TEXICMD__MAX);
         }          }
   
         /* Scan to the end of the possible command name. */          /* Scan to the end of the possible command name. */
         for (*end = pos; *end < sz && ! ismspace(buf[*end]); (*end)++)          for (*end = pos; *end < BUFSZ(p) && ! ismspace(BUF(p)[*end]); (*end)++)
                 if ((*end > pos && ('@' == buf[*end] ||                  if ((*end > pos && ('@' == BUF(p)[*end] ||
                           '{' == buf[*end] || '}' == buf[*end])))                            '{' == BUF(p)[*end] || '}' == BUF(p)[*end])))
                         break;                          break;
   
         /* Look for the command. */          /* Look for the command. */
Line 579  texicmd(struct texi *p, const char *buf, size_t pos, 
Line 648  texicmd(struct texi *p, const char *buf, size_t pos, 
         for (i = 0; i < TEXICMD__MAX; i++) {          for (i = 0; i < TEXICMD__MAX; i++) {
                 if (len != texitoks[i].len)                  if (len != texitoks[i].len)
                         continue;                          continue;
                 if (0 == strncmp(texitoks[i].tok, &buf[pos], len))                  if (0 == strncmp(texitoks[i].tok, &BUF(p)[pos], len))
                         return(i);                          return(i);
         }          }
   
Line 588  texicmd(struct texi *p, const char *buf, size_t pos, 
Line 657  texicmd(struct texi *p, const char *buf, size_t pos, 
                 toksz = strlen(p->indexs[i]);                  toksz = strlen(p->indexs[i]);
                 if (len != 5 + toksz)                  if (len != 5 + toksz)
                         continue;                          continue;
                 if (strncmp(&buf[pos], p->indexs[i], toksz))                  if (strncmp(&BUF(p)[pos], p->indexs[i], toksz))
                         continue;                          continue;
                 if (0 == strncmp(&buf[pos + toksz], "index", 5))                  if (0 == strncmp(&BUF(p)[pos + toksz], "index", 5))
                         return(TEXICMD_USER_INDEX);                          return(TEXICMD_USER_INDEX);
         }          }
   
         for (i = 0; i < p->macrosz; i++) {          for (i = 0; i < p->macrosz; i++) {
                 if (len != strlen(p->macros[i].key))                  if (len != strlen(p->macros[i].key))
                         continue;                          continue;
                 if (strncmp(&buf[pos], p->macros[i].key, len))                  if (strncmp(&BUF(p)[pos], p->macros[i].key, len))
                         continue;                          continue;
                 if (NULL != macro)                  if (NULL != macro)
                         *macro = &p->macros[i];                          *macro = &p->macros[i];
                 return(TEXICMD__MAX);                  return(TEXICMD__MAX);
         }          }
   
         texiwarn(p, "bad command: @%.*s", (int)len, &buf[pos]);          texiwarn(p, "bad command: @%.*s", (int)len, &BUF(p)[pos]);
         return(TEXICMD__MAX);          return(TEXICMD__MAX);
 }  }
   
Line 617  texicmd(struct texi *p, const char *buf, size_t pos, 
Line 686  texicmd(struct texi *p, const char *buf, size_t pos, 
  * bracket for the zeroth parse.   * bracket for the zeroth parse.
  */   */
 int  int
 parsearg(struct texi *p, const char *buf,  parsearg(struct texi *p, size_t *pos, size_t num)
         size_t sz, size_t *pos, size_t num)  
 {  {
         size_t            end;          size_t            end, sv;
         enum texicmd      cmd;          enum texicmd      cmd;
         struct teximacro *macro;          struct teximacro *macro;
   
         while (*pos < sz && ismspace(buf[*pos]))          while (*pos < BUFSZ(p) && ismspace(BUF(p)[*pos]))
                 advance(p, buf, pos);                  advance(p, pos);
         if (*pos == sz || (0 == num && '{' != buf[*pos]))          if (*pos == BUFSZ(p) || (0 == num && '{' != BUF(p)[*pos]))
                 return(0);                  return(0);
         if (0 == num)          if (0 == num)
                 advance(p, buf, pos);                  advance(p, pos);
   
         while ((*pos = advancenext(p, buf, sz, pos)) < sz) {          while ((*pos = advancenext(p, pos)) < BUFSZ(p)) {
                 switch (buf[*pos]) {                  switch (BUF(p)[*pos]) {
                 case (','):                  case (','):
                         advance(p, buf, pos);                          advance(p, pos);
                         return(1);                          return(1);
                 case ('}'):                  case ('}'):
                         advance(p, buf, pos);                          advance(p, pos);
                         return(0);                          return(0);
                 case ('{'):                  case ('{'):
                         if (0 == p->ign)                          if (0 == p->ign)
                                 texiwarn(p, "unexpected \"{\"");                                  texiwarn(p, "unexpected \"{\"");
                         advance(p, buf, pos);                          advance(p, pos);
                         continue;                          continue;
                 case ('@'):                  case ('@'):
                         break;                          break;
                 default:                  default:
                         texiword(p, buf, sz, pos, ',');                          parseword(p, pos, ',');
                         continue;                          continue;
                 }                  }
   
                 cmd = texicmd(p, buf, *pos, sz, &end, &macro);                  sv = p->files[p->filepos - 1].insplice;
                 advanceto(p, buf, pos, end);                  cmd = texicmd(p, *pos, &end, &macro);
                   advanceto(p, pos, end);
                 if (NULL != macro)                  if (NULL != macro)
                         texiexecmacro(p, macro, buf, sz, pos);                          texiexecmacro(p, macro, sv, pos);
                 if (TEXICMD__MAX == cmd)                  if (TEXICMD__MAX == cmd)
                         continue;                          continue;
                 if (NULL != texitoks[cmd].fp)                  if (NULL != texitoks[cmd].fp)
                         (*texitoks[cmd].fp)(p, cmd, buf, sz, pos);                          (*texitoks[cmd].fp)(p, cmd, pos);
         }          }
         return(0);          return(0);
 }  }
Line 668  parsearg(struct texi *p, const char *buf, 
Line 737  parsearg(struct texi *p, const char *buf, 
  * This will stop in the event of EOF or if we're not at a bracket.   * This will stop in the event of EOF or if we're not at a bracket.
  */   */
 void  void
 parsebracket(struct texi *p, const char *buf, size_t sz, size_t *pos)  parsebracket(struct texi *p, size_t *pos, int dostack)
 {  {
         size_t            end;          size_t            end, sv, stack;
         enum texicmd      cmd;          enum texicmd      cmd;
         struct teximacro *macro;          struct teximacro *macro;
   
         while (*pos < sz && ismspace(buf[*pos]))          while (*pos < BUFSZ(p) && ismspace(BUF(p)[*pos]))
                 advance(p, buf, pos);                  advance(p, pos);
   
         if (*pos == sz || '{' != buf[*pos])          if (*pos == BUFSZ(p) || '{' != BUF(p)[*pos])
                 return;                  return;
         advance(p, buf, pos);          advance(p, pos);
   
         while ((*pos = advancenext(p, buf, sz, pos)) < sz) {          stack = 0;
                 switch (buf[*pos]) {          while ((*pos = advancenext(p, pos)) < BUFSZ(p)) {
                   switch (BUF(p)[*pos]) {
                 case ('}'):                  case ('}'):
                         advance(p, buf, pos);                          if (stack > 0) {
                                   stack--;
                                   advance(p, pos);
                                   texiputchar(p, '}');
                                   continue;
                           }
                           advance(p, pos);
                         return;                          return;
                 case ('{'):                  case ('{'):
                           if (dostack) {
                                   stack++;
                                   advance(p, pos);
                                   texiputchar(p, '{');
                                   continue;
                           }
                         if (0 == p->ign)                          if (0 == p->ign)
                                 texiwarn(p, "unexpected \"{\"");                                  texiwarn(p, "unexpected \"{\"");
                         advance(p, buf, pos);                          advance(p, pos);
                         continue;                          continue;
                 case ('@'):                  case ('@'):
                         break;                          break;
                 default:                  default:
                         texiword(p, buf, sz, pos, '\0');                          parseword(p, pos, '\0');
                         continue;                          continue;
                 }                  }
   
                 cmd = texicmd(p, buf, *pos, sz, &end, &macro);                  sv = p->files[p->filepos - 1].insplice;
                 advanceto(p, buf, pos, end);                  cmd = texicmd(p, *pos, &end, &macro);
                   advanceto(p, pos, end);
                 if (NULL != macro)                  if (NULL != macro)
                         texiexecmacro(p, macro, buf, sz, pos);                          texiexecmacro(p, macro, sv, pos);
                 if (TEXICMD__MAX == cmd)                  if (TEXICMD__MAX == cmd)
                         continue;                          continue;
                 if (NULL != texitoks[cmd].fp)                  if (NULL != texitoks[cmd].fp)
                         (*texitoks[cmd].fp)(p, cmd, buf, sz, pos);                          (*texitoks[cmd].fp)(p, cmd, pos);
         }          }
 }  }
   
Line 715  parsebracket(struct texi *p, const char *buf, size_t s
Line 798  parsebracket(struct texi *p, const char *buf, size_t s
  * the way.   * the way.
  */   */
 void  void
 parseeoln(struct texi *p, const char *buf, size_t sz, size_t *pos)  parseeoln(struct texi *p, size_t *pos)
 {  {
         size_t            end;          size_t            end, sv;
         enum texicmd      cmd;          enum texicmd      cmd;
         struct teximacro *macro;          struct teximacro *macro;
   
         while (*pos < sz && '\n' != buf[*pos]) {          while (*pos < BUFSZ(p) && '\n' != BUF(p)[*pos]) {
                 while (*pos < sz && isws(buf[*pos])) {                  while (*pos < BUFSZ(p) && isws(BUF(p)[*pos])) {
                         p->seenws = 1;                          p->seenws = 1;
                         if (p->literal)                          if (p->literal)
                                 texiputchar(p, buf[*pos]);                                  texiputchar(p, BUF(p)[*pos]);
                         advance(p, buf, pos);                          advance(p, pos);
                 }                  }
                 switch (buf[*pos]) {                  switch (BUF(p)[*pos]) {
                 case ('}'):                  case ('}'):
                         if (0 == p->ign)                          if (0 == p->ign)
                                 texiwarn(p, "unexpected \"}\"");                                  texiwarn(p, "unexpected \"}\"");
                         advance(p, buf, pos);                          advance(p, pos);
                         continue;                          continue;
                 case ('{'):                  case ('{'):
                         if (0 == p->ign)                          if (0 == p->ign)
                                 texiwarn(p, "unexpected \"{\"");                                  texiwarn(p, "unexpected \"{\"");
                         advance(p, buf, pos);                          advance(p, pos);
                         continue;                          continue;
                 case ('@'):                  case ('@'):
                         break;                          break;
                 default:                  default:
                         texiword(p, buf, sz, pos, '\0');                          parseword(p, pos, '\0');
                         continue;                          continue;
                 }                  }
   
                 cmd = texicmd(p, buf, *pos, sz, &end, &macro);                  sv = p->files[p->filepos - 1].insplice;
                 advanceto(p, buf, pos, end);                  cmd = texicmd(p, *pos, &end, &macro);
                   advanceto(p, pos, end);
                 if (NULL != macro)                  if (NULL != macro)
                         texiexecmacro(p, macro, buf, sz, pos);                          texiexecmacro(p, macro, sv, pos);
                 if (TEXICMD__MAX == cmd)                  if (TEXICMD__MAX == cmd)
                         continue;                          continue;
                 if (NULL != texitoks[cmd].fp)                  if (NULL != texitoks[cmd].fp)
                         (*texitoks[cmd].fp)(p, cmd, buf, sz, pos);                          (*texitoks[cmd].fp)(p, cmd, pos);
         }          }
   
           if (*pos < BUFSZ(p) && '\n' == BUF(p)[*pos])
                   advance(p, pos);
 }  }
   
 /*  /*
    * Peek to see if there's a command after subsequent whitespace.
    * If so, return the macro identifier.
    * This DOES NOT work with user-defined macros.
    */
   enum texicmd
   peekcmd(const struct texi *p, size_t pos)
   {
           size_t          end;
   
           while (pos < BUFSZ(p) && ismspace(BUF(p)[pos]))
                   pos++;
           if (pos == BUFSZ(p) || '@' != BUF(p)[pos])
                   return(TEXICMD__MAX);
           return(texicmd(p, pos, &end, NULL));
   }
   
   /*
  * Parse a single word or command.   * Parse a single word or command.
  * This will return immediately at the EOF.   * This will return immediately at the EOF.
  */   */
 void  static void
 parsesingle(struct texi *p, const char *buf, size_t sz, size_t *pos)  parsesingle(struct texi *p, size_t *pos)
 {  {
         size_t            end;          size_t            end, sv;
         enum texicmd      cmd;          enum texicmd      cmd;
         struct teximacro *macro;          struct teximacro *macro;
   
         if ((*pos = advancenext(p, buf, sz, pos)) >= sz)          if ((*pos = advancenext(p, pos)) >= BUFSZ(p))
                 return;                  return;
   
         switch (buf[*pos]) {          switch (BUF(p)[*pos]) {
         case ('}'):          case ('}'):
                 if (0 == p->ign)                  if (0 == p->ign)
                         texiwarn(p, "unexpected \"}\"");                          texiwarn(p, "unexpected \"}\"");
                 advance(p, buf, pos);                  advance(p, pos);
                 return;                  return;
         case ('{'):          case ('{'):
                 if (0 == p->ign)                  if (0 == p->ign)
                         texiwarn(p, "unexpected \"{\"");                          texiwarn(p, "unexpected \"{\"");
                 advance(p, buf, pos);                  advance(p, pos);
                 return;                  return;
         case ('@'):          case ('@'):
                 break;                  break;
         default:          default:
                 texiword(p, buf, sz, pos, '\0');                  parseword(p, pos, '\0');
                 return;                  return;
         }          }
   
         cmd = texicmd(p, buf, *pos, sz, &end, &macro);          sv = p->files[p->filepos - 1].insplice;
         advanceto(p, buf, pos, end);          cmd = texicmd(p, *pos, &end, &macro);
           advanceto(p, pos, end);
         if (NULL != macro)          if (NULL != macro)
                 texiexecmacro(p, macro, buf, sz, pos);                  texiexecmacro(p, macro, sv, pos);
         if (TEXICMD__MAX == cmd)          if (TEXICMD__MAX == cmd)
                 return;                  return;
         if (NULL != texitoks[cmd].fp)          if (NULL != texitoks[cmd].fp)
                 (*texitoks[cmd].fp)(p, cmd, buf, sz, pos);                  (*texitoks[cmd].fp)(p, cmd, pos);
 }  }
   
 /*  /*
Line 808  parsesingle(struct texi *p, const char *buf, size_t sz
Line 913  parsesingle(struct texi *p, const char *buf, size_t sz
  * line or 1 otherwise.   * line or 1 otherwise.
  */   */
 int  int
 parselinearg(struct texi *p, const char *buf, size_t sz, size_t *pos)  parselinearg(struct texi *p, size_t *pos)
 {  {
   
         while (*pos < sz && isws(buf[*pos])) {          while (*pos < BUFSZ(p) && isws(BUF(p)[*pos])) {
                 p->seenws = 1;                  p->seenws = 1;
                 advance(p, buf, pos);                  advance(p, pos);
         }          }
   
         if (*pos < sz && '{' == buf[*pos])          if (*pos < BUFSZ(p) && '{' == BUF(p)[*pos])
                 parsebracket(p, buf, sz, pos);                  parsebracket(p, pos, 0);
         else if (*pos < sz && '\n' != buf[*pos])          else if (*pos < BUFSZ(p) && '\n' != BUF(p)[*pos])
                 parsesingle(p, buf, sz, pos);                  parsesingle(p, pos);
         else          else
                 return(0);                  return(0);
   
Line 829  parselinearg(struct texi *p, const char *buf, size_t s
Line 934  parselinearg(struct texi *p, const char *buf, size_t s
 /*  /*
  * Parse til the end of the buffer.   * Parse til the end of the buffer.
  */   */
 void  static void
 parseeof(struct texi *p, const char *buf, size_t sz)  parseeof(struct texi *p)
 {  {
         size_t   pos;          size_t   pos;
   
         for (pos = 0; pos < sz; )          for (pos = 0; pos < BUFSZ(p); )
                 parsesingle(p, buf, sz, &pos);                  parsesingle(p, &pos);
 }  }
   
   void
   texisplice(struct texi *p, const char *buf, size_t sz, size_t pos)
   {
           char            *cp;
           struct texifile *f;
   
           assert(p->filepos > 0);
           f = &p->files[p->filepos - 1];
   
           if (f->mapsz + sz > f->mapmaxsz) {
                   f->mapmaxsz = f->mapsz + sz + 1024;
                   cp = realloc(f->map, f->mapmaxsz);
                   if (NULL == cp)
                           texiabort(p, NULL);
                   f->map = cp;
           }
   
           f->insplice += sz;
           memmove(f->map + pos + sz, f->map + pos, f->mapsz - pos);
           memcpy(f->map + pos, buf, sz);
           f->mapsz += sz;
   }
   
 /*  /*
  * Parse a block sequence until we have the "@end endtoken" command   * Parse a block sequence until we have the "@end endtoken" command
  * invocation.   * invocation.
  * This will return immediately at EOF.   * This will return immediately at EOF.
  */   */
 void  void
 parseto(struct texi *p, const char *buf,  parseto(struct texi *p, size_t *pos, const char *endtoken)
         size_t sz, size_t *pos, const char *endtoken)  
 {  {
         size_t            end;          size_t            end, sv;
         enum texicmd      cmd;          enum texicmd      cmd;
         size_t            endtoksz;          size_t            endtoksz;
         struct teximacro *macro;          struct teximacro *macro;
Line 855  parseto(struct texi *p, const char *buf, 
Line 982  parseto(struct texi *p, const char *buf, 
         endtoksz = strlen(endtoken);          endtoksz = strlen(endtoken);
         assert(endtoksz > 0);          assert(endtoksz > 0);
   
         while ((*pos = advancenext(p, buf, sz, pos)) < sz) {          while ((*pos = advancenext(p, pos)) < BUFSZ(p)) {
                 switch (buf[*pos]) {                  switch (BUF(p)[*pos]) {
                 case ('}'):                  case ('}'):
                         if (0 == p->ign)                          if (0 == p->ign)
                                 texiwarn(p, "unexpected \"}\"");                                  texiwarn(p, "unexpected \"}\"");
                         advance(p, buf, pos);                          advance(p, pos);
                         continue;                          continue;
                 case ('{'):                  case ('{'):
                         if (0 == p->ign)                          if (0 == p->ign)
                                 texiwarn(p, "unexpected \"{\"");                                  texiwarn(p, "unexpected \"{\"");
                         advance(p, buf, pos);                          advance(p, pos);
                         continue;                          continue;
                 case ('@'):                  case ('@'):
                         break;                          break;
                 default:                  default:
                         texiword(p, buf, sz, pos, '\0');                          parseword(p, pos, '\0');
                         continue;                          continue;
                 }                  }
   
                 cmd = texicmd(p, buf, *pos, sz, &end, &macro);                  sv = p->files[p->filepos - 1].insplice;
                 advanceto(p, buf, pos, end);                  cmd = texicmd(p, *pos, &end, &macro);
                   advanceto(p, pos, end);
                 if (TEXICMD_END == cmd) {                  if (TEXICMD_END == cmd) {
                         while (*pos < sz && isws(buf[*pos]))                          while (*pos < BUFSZ(p) && isws(BUF(p)[*pos]))
                                 advance(p, buf, pos);                                  advance(p, pos);
                         /*                          /*
                          * FIXME: check the full word, not just its                           * FIXME: check the full word, not just its
                          * initial substring!                           * initial substring!
                          */                           */
                         if (sz - *pos >= endtoksz && 0 == strncmp                          if (BUFSZ(p) - *pos >= endtoksz && 0 == strncmp
                                  (&buf[*pos], endtoken, endtoksz)) {                                   (&BUF(p)[*pos], endtoken, endtoksz)) {
                                 advanceeoln(p, buf, sz, pos, 0);                                  advanceeoln(p, pos, 0);
                                 break;                                  break;
                         }                          }
                         if (0 == p->ign)                          if (0 == p->ign)
                                 texiwarn(p, "unexpected \"end\"");                                  texiwarn(p, "unexpected \"end\"");
                         advanceeoln(p, buf, sz, pos, 0);                          advanceeoln(p, pos, 0);
                         continue;                          continue;
                 }                  }
                 if (NULL != macro)                  if (NULL != macro)
                         texiexecmacro(p, macro, buf, sz, pos);                          texiexecmacro(p, macro, sv, pos);
                 if (TEXICMD__MAX == cmd)                  if (TEXICMD__MAX == cmd)
                         continue;                          continue;
                 if (NULL != texitoks[cmd].fp)                  if (NULL != texitoks[cmd].fp)
                         (*texitoks[cmd].fp)(p, cmd, buf, sz, pos);                          (*texitoks[cmd].fp)(p, cmd, pos);
         }          }
 }  }
   
 /*  /*
    * Like parsefile() but used for reading from stdandard input.
    * This can only be called for the first file!
    */
   void
   parsestdin(struct texi *p)
   {
           struct texifile *f;
           ssize_t          ssz;
   
           assert(0 == p->filepos);
           f = &p->files[p->filepos];
           memset(f, 0, sizeof(struct texifile));
   
           f->type = TEXISRC_STDIN;
           f->name = "<stdin>";
   
           for (f->mapsz = 0; ; f->mapsz += (size_t)ssz) {
                   if (f->mapsz == f->mapmaxsz) {
                           if (f->mapmaxsz == (1U << 31))
                                   texierr(p, "stdin buffer too long");
                           f->mapmaxsz = f->mapmaxsz > 65536 / 2 ?
                                   2 * f->mapmaxsz : 65536;
                           f->map = realloc(f->map, f->mapmaxsz);
                           if (NULL == f->map)
                                   texiabort(p, NULL);
                   }
                   ssz = read(STDIN_FILENO, f->map +
                           (int)f->mapsz, f->mapmaxsz - f->mapsz);
                   if (0 == ssz)
                           break;
                   else if (-1 == ssz)
                           texiabort(p, NULL);
           }
   
           p->filepos++;
           parseeof(p);
           texifilepop(p);
   }
   
   /*
  * Memory-map the file "fname" and begin parsing it unless "parse" is   * Memory-map the file "fname" and begin parsing it unless "parse" is
  * zero, in which case we just dump the file to stdout (making sure it   * zero, in which case we just dump the file to stdout (making sure it
  * doesn't trip up mdoc(7) along the way).   * doesn't trip up mdoc(7) along the way).
Line 915  parsefile(struct texi *p, const char *fname, int parse
Line 1083  parsefile(struct texi *p, const char *fname, int parse
         int              fd;          int              fd;
         struct stat      st;          struct stat      st;
         size_t           i;          size_t           i;
           char            *map;
   
         if (64 == p->filepos)          if (64 == p->filepos)
                 texierr(p, "too many open files");                  texierr(p, "too many open files");
         f = &p->files[p->filepos];          f = &p->files[p->filepos];
         memset(f, 0, sizeof(struct texifile));          memset(f, 0, sizeof(struct texifile));
   
           f->type = TEXISRC_FILE;
         f->name = fname;          f->name = fname;
         if (-1 == (fd = open(fname, O_RDONLY, 0))) {          if (-1 == (fd = open(fname, O_RDONLY, 0))) {
                 texiabort(p, fname);                  texiabort(p, fname);
Line 929  parsefile(struct texi *p, const char *fname, int parse
Line 1099  parsefile(struct texi *p, const char *fname, int parse
                 texiabort(p, fname);                  texiabort(p, fname);
         }          }
   
         f->mapsz = st.st_size;          f->mapsz = f->mapmaxsz = st.st_size;
         f->map = mmap(NULL, f->mapsz,          map = mmap(NULL, f->mapsz,
                 PROT_READ, MAP_SHARED, fd, 0);                  PROT_READ, MAP_SHARED, fd, 0);
         close(fd);          close(fd);
   
         if (MAP_FAILED == f->map)          if (MAP_FAILED == map)
                 texiabort(p, fname);                  texiabort(p, fname);
   
         p->filepos++;  
         if ( ! parse) {          if ( ! parse) {
                 /*                  for (i = 0; i < f->mapsz; i++)
                  * We're printing verbatim output.                          texiputchar(p, map[i]);
                  * Make sure it doesn't get interpreted as mdoc by                  if (p->outcol)
                  * escaping escapes and making sure leading dots don't                          texiputchar(p, '\n');
                  * trigger mdoc(7) expansion.                  munmap(map, f->mapsz);
                  */                  return;
                 for (i = 0; i < f->mapsz; i++) {          }
                         if (i > 0 && '.' == f->map[i])  
                                 if ('\n' == f->map[i - 1])          p->filepos++;
                                         fputs("\\&", stdout);          f->map = malloc(f->mapsz);
                         putchar(f->map[i]);          memcpy(f->map, map, f->mapsz);
                         if ('\\' == f->map[i])          munmap(map, f->mapsz);
                                 putchar('e');          parseeof(p);
                 }  
         } else  
                 parseeof(p, f->map, f->mapsz);  
         texifilepop(p);          texifilepop(p);
 }  }
   
Line 966  parsefile(struct texi *p, const char *fname, int parse
Line 1132  parsefile(struct texi *p, const char *fname, int parse
  * The pointer can point to NULL if the value has been unset.   * The pointer can point to NULL if the value has been unset.
  */   */
 static char **  static char **
 valuequery(const struct texi *p,  valuequery(const struct texi *p, size_t start, size_t end)
         const char *buf, size_t start, size_t end)  
 {  {
         size_t   i, sz, len;          size_t   i, sz, len;
   
Line 979  valuequery(const struct texi *p, 
Line 1144  valuequery(const struct texi *p, 
                 sz = strlen(p->vals[i].key);                  sz = strlen(p->vals[i].key);
                 if (sz != len)                  if (sz != len)
                         continue;                          continue;
                 if (0 == strncmp(p->vals[i].key, &buf[start], len))                  if (0 == strncmp(p->vals[i].key, &BUF(p)[start], len))
                         return(&p->vals[i].value);                          return(&p->vals[i].value);
         }          }
         return(NULL);          return(NULL);
Line 990  valuequery(const struct texi *p, 
Line 1155  valuequery(const struct texi *p, 
  * pointer to its value via valuequery().   * pointer to its value via valuequery().
  */   */
 static char **  static char **
 valuelquery(struct texi *p, const char *buf, size_t sz, size_t *pos)  valuelquery(struct texi *p, size_t *pos)
 {  {
         size_t    start, end;          size_t    start, end;
         char    **ret;          char    **ret;
   
         while (*pos < sz && isws(buf[*pos]))          while (*pos < BUFSZ(p) && isws(BUF(p)[*pos]))
                 advance(p, buf, pos);                  advance(p, pos);
         if (*pos == sz)          if (*pos == BUFSZ(p))
                 return(NULL);                  return(NULL);
         for (start = end = *pos; end < sz; end++)          for (start = end = *pos; end < BUFSZ(p); end++)
                 if ('\n' == buf[end])                  if ('\n' == BUF(p)[end])
                         break;                          break;
         advanceto(p, buf, pos, end);          advanceto(p, pos, end);
         if (*pos < sz) {          if (*pos < BUFSZ(p)) {
                 assert('\n' == buf[*pos]);                  assert('\n' == BUF(p)[*pos]);
                 advance(p, buf, pos);                  advance(p, pos);
         }          }
         if (NULL == (ret = valuequery(p, buf, start, end)))          if (NULL == (ret = valuequery(p, start, end)))
                 return(NULL);                  return(NULL);
         return(ret);          return(ret);
 }  }
   
 void  void
 valuelclear(struct texi *p, const char *buf, size_t sz, size_t *pos)  valuelclear(struct texi *p, size_t *pos)
 {  {
         char    **ret;          char    **ret;
   
         if (NULL == (ret = valuelquery(p, buf, sz, pos)))          if (NULL == (ret = valuelquery(p, pos)))
                 return;                  return;
         free(*ret);          free(*ret);
         *ret = NULL;          *ret = NULL;
 }  }
   
 const char *  const char *
 valuellookup(struct texi *p, const char *buf, size_t sz, size_t *pos)  valuellookup(struct texi *p, size_t *pos)
 {  {
         char    **ret;          char    **ret;
   
         if (NULL == (ret = valuelquery(p, buf, sz, pos)))          if (NULL == (ret = valuelquery(p, pos)))
                 return(NULL);                  return(NULL);
         return(*ret);          return(*ret);
 }  }
Line 1041  valuellookup(struct texi *p, const char *buf, size_t s
Line 1206  valuellookup(struct texi *p, const char *buf, size_t s
  * value had previously been unset.   * value had previously been unset.
  */   */
 const char *  const char *
 valueblookup(struct texi *p, const char *buf, size_t sz, size_t *pos)  valueblookup(struct texi *p, size_t *pos)
 {  {
         size_t    start, end;          size_t    start, end;
         char    **ret;          char    **ret;
   
         while (*pos < sz && isws(buf[*pos]))          while (*pos < BUFSZ(p) && isws(BUF(p)[*pos]))
                 advance(p, buf, pos);                  advance(p, pos);
         if (*pos == sz || '{' != buf[*pos])          if (*pos == BUFSZ(p) || '{' != BUF(p)[*pos])
                 return(NULL);                  return(NULL);
         advance(p, buf, pos);          advance(p, pos);
         for (start = end = *pos; end < sz; end++)          for (start = end = *pos; end < BUFSZ(p); end++)
                 if ('}' == buf[end])                  if ('}' == BUF(p)[end])
                         break;                          break;
         advanceto(p, buf, pos, end);          advanceto(p, pos, end);
         if (*pos < sz) {          if (*pos < BUFSZ(p)) {
                 assert('}' == buf[*pos]);                  assert('}' == BUF(p)[*pos]);
                 advance(p, buf, pos);                  advance(p, pos);
         }          }
         if (NULL == (ret = valuequery(p, buf, start, end)))          if (NULL == (ret = valuequery(p, start, end)))
                 return(NULL);                  return(NULL);
         return(*ret);          return(*ret);
 }  }
Line 1103  valueadd(struct texi *p, char *key, char *val)
Line 1268  valueadd(struct texi *p, char *key, char *val)
  * Ergo, textual: this doesn't interpret the arguments in any way.   * Ergo, textual: this doesn't interpret the arguments in any way.
  */   */
 char **  char **
 argparse(struct texi *p, const char *buf,  argparse(struct texi *p, size_t *pos, size_t *argsz, size_t hint)
         size_t sz, size_t *pos, size_t *argsz)  
 {  {
         char    **args;          char    **args;
         size_t    start, end, stack;          size_t    start, end, stack;
   
         while (*pos < sz && isws(buf[*pos]))          while (*pos < BUFSZ(p) && isws(BUF(p)[*pos]))
                 advance(p, buf, pos);                  advance(p, pos);
   
         args = NULL;          args = NULL;
         *argsz = 0;          *argsz = 0;
   
         /* Check for no arguments. */          if (*pos == BUFSZ(p))
         if ('{' != buf[*pos])  
                 return(args);                  return(args);
   
           if ('{' != BUF(p)[*pos] && hint) {
                   /*
                    * Special case: if we encounter an unbracketed argument
                    * and we're being invoked with non-zero arguments
                    * (versus being set, i.e., hint>0), then parse until
                    * the end of line.
                    */
                   *argsz = 1;
                   args = calloc(1, sizeof(char *));
                   if (NULL == args)
                           texiabort(p, NULL);
                   start = *pos;
                   while (*pos < BUFSZ(p)) {
                           if ('\n' == BUF(p)[*pos])
                                   break;
                           advance(p, pos);
                   }
                   args[0] = malloc(*pos - start + 1);
                   memcpy(args[0], &BUF(p)[start], *pos - start);
                   args[0][*pos - start] = '\0';
                   if (*pos < BUFSZ(p) && '\n' == BUF(p)[*pos])
                           advance(p, pos);
                   return(args);
           } else if ('{' != BUF(p)[*pos])
                   return(args);
   
           assert('{' == BUF(p)[*pos]);
   
         /* Parse til the closing '}', putting into the array. */          /* Parse til the closing '}', putting into the array. */
         advance(p, buf, pos);          advance(p, pos);
         while (*pos < sz) {          while (*pos < BUFSZ(p)) {
                 while (*pos < sz && isws(buf[*pos]))                  while (*pos < BUFSZ(p) && isws(BUF(p)[*pos]))
                         advance(p, buf, pos);                          advance(p, pos);
                 start = *pos;                  start = *pos;
                 stack = 0;                  stack = 0;
                 while (*pos < sz) {                  while (*pos < BUFSZ(p)) {
                         /*                          /*
                          * According to the manual, commas within                           * According to the manual, commas within
                          * embedded commands are escaped.                           * embedded commands are escaped.
                          * We keep track of embedded-ness in the "stack"                           * We keep track of embedded-ness in the "stack"
                          * state anyway, so this is free.                           * state anyway, so this is free.
                          */                           */
                         if (0 == stack && ',' == buf[*pos])                          if (',' == BUF(p)[*pos] && 0 == stack && 1 != hint)
                                 break;                                  break;
                         else if (0 == stack && '}' == buf[*pos])                          else if (0 == stack && '}' == BUF(p)[*pos])
                                 break;                                  break;
                         else if (0 != stack && '}' == buf[*pos])                          else if (0 != stack && '}' == BUF(p)[*pos])
                                 stack--;                                  stack--;
                         else if ('{' == buf[*pos])                          else if ('{' == BUF(p)[*pos])
                                 stack++;                                  stack++;
                         advance(p, buf, pos);                          advance(p, pos);
                 }                  }
                 if (stack)                  if (stack)
                         texiwarn(p, "unterminated macro "                          texiwarn(p, "unterminated macro "
                                 "in macro arguments");                                  "in macro arguments");
                 if ((end = *pos) == sz)                  if ((end = *pos) == BUFSZ(p))
                         break;                          break;
                 /* Test for zero-length '{  }'. */                  /* Test for zero-length '{  }'. */
                 if (start == end && '}' == buf[*pos] && 0 == *argsz)                  if (start == end && '}' == BUF(p)[*pos] && 0 == *argsz)
                         break;                          break;
                 if (start == end)  
                         texierr(p, "zero-length argument");  
                 /* FIXME: use reallocarray. */                  /* FIXME: use reallocarray. */
                 args = realloc                  args = realloc
                         (args, sizeof(char *) *                          (args, sizeof(char *) *
Line 1163  argparse(struct texi *p, const char *buf, 
Line 1352  argparse(struct texi *p, const char *buf, 
                 if (NULL == args[*argsz])                  if (NULL == args[*argsz])
                         texiabort(p, NULL);                          texiabort(p, NULL);
                 memcpy(args[*argsz],                  memcpy(args[*argsz],
                         &buf[start], end - start);                          &BUF(p)[start], end - start);
                 args[*argsz][end - start] = '\0';                  args[*argsz][end - start] = '\0';
                 (*argsz)++;                  (*argsz)++;
                 if ('}' == buf[*pos])                  if ('}' == BUF(p)[*pos])
                         break;                          break;
                 advance(p, buf, pos);                  advance(p, pos);
         }          }
   
         if (*pos == sz)          if (*pos == BUFSZ(p))
                 texierr(p, "unterminated arguments");                  texierr(p, "unterminated arguments");
         assert('}' == buf[*pos]);          assert('}' == BUF(p)[*pos]);
         advance(p, buf, pos);          advance(p, pos);
         return(args);          return(args);
 }  }
   
   /*
    * If we're printing chapters, then do some naviation here and then
    * close our outfile.
    * I want to call this the SEE ALSO section, but that's not really what
    * it is: we'll refer to the "initial" (top) node and the next and
    * previous chapters.
    */
   void
   teximdocclose(struct texi *p, int last)
   {
           char     buf[PATH_MAX];
   
           if (NULL == p->chapters || 0 == p->chapnum)
                   return;
   
           teximacro(p, "Sh INFO NAVIGATION");
   
           /* Print a reference to the "top" node. */
           if (p->chapnum > 1) {
                   texiputchars(p, "Top node,");
                   snprintf(buf, sizeof(buf), "node1 7");
                   teximacroopen(p, "Xr ");
                   texiputchars(p, buf);
                   texiputchars(p, " ;");
                   teximacroclose(p);
           }
   
           /* Print a reference to the previous node. */
           if (p->chapnum > 2) {
                   texiputchars(p, "previous node,");
                   snprintf(buf, sizeof(buf),
                           "node%zu 7", p->chapnum - 1);
                   teximacroopen(p, "Xr ");
                   texiputchars(p, buf);
                   if ( ! last)
                           texiputchars(p, " ;");
                   teximacroclose(p);
           }
   
           /* Print a reference to the next node. */
           if ( ! last) {
                   if (1 == p->chapnum)
                           texiputchars(p, "Next node,");
                   else
                           texiputchars(p, "next node,");
                   snprintf(buf, sizeof(buf),
                           "node%zu 7", p->chapnum + 1);
                   teximacroopen(p, "Xr ");
                   texiputchars(p, buf);
                   teximacroclose(p);
           }
   
           fclose(p->outfile);
   }
   
   /*
    * Open a mdoc(7) context.
    * If we're printing chapters, then open the outfile here, too.
    * Otherwise just print the mdoc(7) prologue.
    */
   void
   teximdocopen(struct texi *p, size_t *pos)
   {
           const char      *cp;
           time_t           t;
           char             date[32];
           char             fname[PATH_MAX];
   
           if (NULL != p->chapters) {
                   snprintf(fname, sizeof(fname), "%s/node%zu.7",
                           p->chapters, ++p->chapnum);
                   p->outfile = fopen(fname, "w");
                   if (NULL == p->outfile)
                           texiabort(p, fname);
           }
   
           /*
            * Here we print our standard mdoc(7) prologue.
            * We use the title set with @settitle for the `Nd' description
            * and the source document filename (the first one as invoked on
            * the command line) for the title.
            * The date is set to the current date.
            */
           t = time(NULL);
           strftime(date, sizeof(date), "%F", localtime(&t));
   
           teximacroopen(p, "Dd");
           texiputchars(p, date);
           teximacroclose(p);
           teximacroopen(p, "Dt");
           for (cp = p->title; '\0' != *cp; cp++)
                   texiputchar(p, toupper((unsigned int)*cp));
           texiputchars(p, " 7");
           teximacroclose(p);
           teximacro(p, "Os");
           teximacro(p, "Sh NAME");
           teximacroopen(p, "Nm");
           for (cp = p->title; '\0' != *cp; cp++)
                   texiputchar(p, *cp);
           teximacroclose(p);
           teximacroopen(p, "Nd");
           /*
            * The subtitle `Nd' can consist of arbitrary macros, so paste
            * it and parse to the end of the line.
            */
           if (NULL != p->subtitle) {
                   texisplice(p, p->subtitle, strlen(p->subtitle), *pos);
                   parseeoln(p, pos);
           } else
                   texiputchars(p, "Unknown description");
           teximacroclose(p);
           p->seenvs = 1;
   }
   

Legend:
Removed from v.1.7  
changed lines
  Added in v.1.23

CVSweb