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

Diff for /mandoc/main.c between version 1.64 and 1.76

version 1.64, 2010/05/14 17:59:07 version 1.76, 2010/05/16 10:59:36
Line 18 
Line 18 
 #include "config.h"  #include "config.h"
 #endif  #endif
   
   #include <sys/mman.h>
 #include <sys/stat.h>  #include <sys/stat.h>
   
 #include <assert.h>  #include <assert.h>
Line 28 
Line 29 
 #include <string.h>  #include <string.h>
 #include <unistd.h>  #include <unistd.h>
   
   #include "mandoc.h"
 #include "mdoc.h"  #include "mdoc.h"
 #include "man.h"  #include "man.h"
   #include "roff.h"
 #include "main.h"  #include "main.h"
   
 #define UNCONST(a)      ((void *)(uintptr_t)(const void *)(a))  #define UNCONST(a)      ((void *)(uintptr_t)(const void *)(a))
Line 69  struct curparse {
Line 72  struct curparse {
         const char       *file;         /* Current parse. */          const char       *file;         /* Current parse. */
         int               fd;           /* Current parse. */          int               fd;           /* Current parse. */
         int               wflags;          int               wflags;
           /* FIXME: set by max error */
 #define WARN_WALL        (1 << 0)       /* All-warnings mask. */  #define WARN_WALL        (1 << 0)       /* All-warnings mask. */
 #define WARN_WERR        (1 << 2)       /* Warnings->errors. */  #define WARN_WERR        (1 << 2)       /* Warnings->errors. */
         int               fflags;          int               fflags;
Line 76  struct curparse {
Line 80  struct curparse {
 #define FL_NIGN_ESCAPE   (1 << 1)       /* Don't ignore bad escapes. */  #define FL_NIGN_ESCAPE   (1 << 1)       /* Don't ignore bad escapes. */
 #define FL_NIGN_MACRO    (1 << 2)       /* Don't ignore bad macros. */  #define FL_NIGN_MACRO    (1 << 2)       /* Don't ignore bad macros. */
 #define FL_IGN_ERRORS    (1 << 4)       /* Ignore failed parse. */  #define FL_IGN_ERRORS    (1 << 4)       /* Ignore failed parse. */
   #define FL_STRICT         FL_NIGN_ESCAPE | \
                             FL_NIGN_MACRO
         enum intt         inttype;      /* Input parsers... */          enum intt         inttype;      /* Input parsers... */
         struct man       *man;          struct man       *man;
         struct man       *lastman;  
         struct mdoc      *mdoc;          struct mdoc      *mdoc;
         struct mdoc      *lastmdoc;          struct roff      *roff;
         enum outt         outtype;      /* Output devices... */          enum outt         outtype;      /* Output devices... */
         out_mdoc          outmdoc;          out_mdoc          outmdoc;
         out_man           outman;          out_man           outman;
Line 89  struct curparse {
Line 94  struct curparse {
         char              outopts[BUFSIZ];          char              outopts[BUFSIZ];
 };  };
   
 #define FL_STRICT         FL_NIGN_ESCAPE | \  static  void              fdesc(struct curparse *);
                           FL_NIGN_MACRO  static  void              ffile(const char *, struct curparse *);
   
 static  int               foptions(int *, char *);  static  int               foptions(int *, char *);
 static  int               toptions(struct curparse *, char *);  static  struct man       *man_init(struct curparse *);
   static  struct mdoc      *mdoc_init(struct curparse *);
   static  struct roff      *roff_init(struct curparse *);
   static  int               merr(void *, int, int, const char *); /* DEPRECATED */
 static  int               moptions(enum intt *, char *);  static  int               moptions(enum intt *, char *);
 static  int               woptions(int *, char *);  static  int               mwarn(void *, int, int, const char *); /* DEPRECATED */
 static  int               merr(void *, int, int, const char *);  static  int               mmsg(enum mandocerr, void *,
 static  int               mwarn(void *, int, int, const char *);                                  int, int, const char *);
 static  void              ffile(struct buf *, struct buf *,  
                                 const char *, struct curparse *);  
 static  void              fdesc(struct buf *, struct buf *,  
                                 struct curparse *);  
 static  int               pset(const char *, int, struct curparse *,  static  int               pset(const char *, int, struct curparse *,
                                 struct man **, struct mdoc **);                                  struct man **, struct mdoc **);
 static  struct man       *man_init(struct curparse *);  static  int               toptions(struct curparse *, char *);
 static  struct mdoc      *mdoc_init(struct curparse *);  
 static  void              version(void) __attribute__((noreturn));  
 static  void              usage(void) __attribute__((noreturn));  static  void              usage(void) __attribute__((noreturn));
   static  void              version(void) __attribute__((noreturn));
   static  int               woptions(int *, char *);
   
 static  const char       *progname;  static  const char       *progname;
 static  int               with_error, with_warning;  static  int               with_error;
   static  int               with_warning;
   
 int  int
 main(int argc, char *argv[])  main(int argc, char *argv[])
 {  {
         int              c;          int              c;
         struct buf       ln, blk;  
         struct curparse  curp;          struct curparse  curp;
   
         progname = strrchr(argv[0], '/');          progname = strrchr(argv[0], '/');
Line 164  main(int argc, char *argv[])
Line 167  main(int argc, char *argv[])
         argc -= optind;          argc -= optind;
         argv += optind;          argv += optind;
   
         memset(&ln, 0, sizeof(struct buf));  
         memset(&blk, 0, sizeof(struct buf));  
   
         if (NULL == *argv) {          if (NULL == *argv) {
                 curp.file = "<stdin>";                  curp.file = "<stdin>";
                 curp.fd = STDIN_FILENO;                  curp.fd = STDIN_FILENO;
   
                 fdesc(&blk, &ln, &curp);                  fdesc(&curp);
         }          }
   
         while (*argv) {          while (*argv) {
                 ffile(&blk, &ln, *argv, &curp);                  ffile(*argv, &curp);
   
                 if (with_error && !(curp.fflags & FL_IGN_ERRORS))                  if (with_error && !(curp.fflags & FL_IGN_ERRORS))
                         break;                          break;
                 ++argv;                  ++argv;
         }          }
   
         if (blk.buf)  
                 free(blk.buf);  
         if (ln.buf)  
                 free(ln.buf);  
         if (curp.outfree)          if (curp.outfree)
                 (*curp.outfree)(curp.outdata);                  (*curp.outfree)(curp.outdata);
           if (curp.mdoc)
                   mdoc_free(curp.mdoc);
           if (curp.man)
                   man_free(curp.man);
           if (curp.roff)
                   roff_free(curp.roff);
   
         return((with_warning || with_error) ? EXIT_FAILURE :  EXIT_SUCCESS );          return((with_warning || with_error) ?
                           EXIT_FAILURE :  EXIT_SUCCESS);
 }  }
   
   
Line 235  man_init(struct curparse *curp)
Line 238  man_init(struct curparse *curp)
 }  }
   
   
   static struct roff *
   roff_init(struct curparse *curp)
   {
   
           return(roff_alloc(mmsg, curp));
   }
   
   
 static struct mdoc *  static struct mdoc *
 mdoc_init(struct curparse *curp)  mdoc_init(struct curparse *curp)
 {  {
Line 260  mdoc_init(struct curparse *curp)
Line 271  mdoc_init(struct curparse *curp)
   
   
 static void  static void
 ffile(struct buf *blk, struct buf *ln,  ffile(const char *file, struct curparse *curp)
                 const char *file, struct curparse *curp)  
 {  {
   
         curp->file = file;          curp->file = file;
Line 271  ffile(struct buf *blk, struct buf *ln, 
Line 281  ffile(struct buf *blk, struct buf *ln, 
                 return;                  return;
         }          }
   
         fdesc(blk, ln, curp);          fdesc(curp);
   
         if (-1 == close(curp->fd))          if (-1 == close(curp->fd))
                 perror(curp->file);                  perror(curp->file);
 }  }
   
   
 static void  static int
 fdesc(struct buf *blk, struct buf *ln, struct curparse *curp)  resize_buf(struct buf *buf, size_t initial)
 {  {
         size_t           sz;          void *tmp;
         ssize_t          ssz;          size_t sz;
         struct stat      st;  
         int              j, i, pos, lnn, comment;  
         struct man      *man;  
         struct mdoc     *mdoc;  
   
         sz = BUFSIZ;          if (buf->sz == 0)
         man = NULL;                  sz = initial;
         mdoc = NULL;          else
                   sz = 2 * buf->sz;
           tmp = realloc(buf->buf, sz);
           if (NULL == tmp) {
                   perror(NULL);
                   return(0);
           }
           buf->buf = tmp;
           buf->sz = sz;
           return(1);
   }
   
         /*  
          * Two buffers: ln and buf.  buf is the input buffer optimised  
          * here for each file's block size.  ln is a line buffer.  Both  
          * growable, hence passed in by ptr-ptr.  
          */  
   
   static int
   read_whole_file(struct curparse *curp, struct buf *fb, int *with_mmap)
   {
           struct stat      st;
           size_t           off;
           ssize_t          ssz;
   
         if (-1 == fstat(curp->fd, &st)) {          if (-1 == fstat(curp->fd, &st)) {
                 perror(curp->file);                  perror(curp->file);
                 with_error = 1;                  with_error = 1;
                 return;                  return(0);
         }          }
         if ((size_t)st.st_blksize > sz)  
                 sz = st.st_blksize;  
   
         if (sz > blk->sz) {          /*
                 void *buf = realloc(blk->buf, sz);           * If we're a regular file, try just reading in the whole entry
            * via mmap().  This is faster than reading it into blocks, and
            * since each file is only a few bytes to begin with, I'm not
            * concerned that this is going to tank any machines.
            */
   
                 if (NULL == buf) {          if (S_ISREG(st.st_mode)) {
                         perror(NULL);                  if (st.st_size >= (1U << 31)) {
                           fprintf(stderr, "%s: input too large\n",
                                           curp->file);
                         with_error = 1;                          with_error = 1;
                         return;                          return(0);
                 }                  }
                 blk->buf = buf;                  *with_mmap = 1;
                 blk->sz = sz;                  fb->sz = (size_t)st.st_size;
                   fb->buf = mmap(NULL, fb->sz, PROT_READ,
                                   MAP_FILE, curp->fd, 0);
                   if (fb->buf != MAP_FAILED)
                           return(1);
         }          }
   
         /* Fill buf with file blocksize. */          /*
            * If this isn't a regular file (like, say, stdin), then we must
            * go the old way and just read things in bit by bit.
            */
   
         for (lnn = pos = comment = 0; ; ) {          *with_mmap = 0;
                 if (-1 == (ssz = read(curp->fd, blk->buf, sz))) {          off = 0;
           fb->sz = 0;
           fb->buf = NULL;
           for (;;) {
                   if (off == fb->sz) {
                           if (fb->sz == (1U << 31)) {
                                   fprintf(stderr, "%s: input too large\n",
                                                   curp->file);
                                   break;
                           }
                           if (! resize_buf(fb, 65536))
                                   break;
                   }
                   ssz = read(curp->fd, fb->buf + (int)off, fb->sz - off);
                   if (ssz == 0) {
                           fb->sz = off;
                           return(1);
                   }
                   if (ssz == -1) {
                         perror(curp->file);                          perror(curp->file);
                         goto bailout;  
                 } else if (0 == ssz)  
                         break;                          break;
                   }
                   off += (size_t)ssz;
           }
   
                 /* Parse the read block into partial or full lines. */          free(fb->buf);
           fb->buf = NULL;
           with_error = 1;
           return(0);
   }
   
                 for (i = 0; i < (int)ssz; i++) {  
                         if (pos >= (int)ln->sz) {  
                                 ln->sz += 256; /* Step-size. */  
                                 ln->buf = realloc(ln->buf, ln->sz);  
                                 if (NULL == ln->buf) {  
                                         perror(NULL);  
                                         goto bailout;  
                                 }  
                         }  
   
                         if ('\n' != blk->buf[i]) {  static void
                                 if (comment)  fdesc(struct curparse *curp)
                                         continue;  {
                                 ln->buf[pos++] = blk->buf[i];          struct buf       ln, blk;
           int              i, pos, lnn, lnn_start, with_mmap, of;
           enum rofferr     re;
           struct man      *man;
           struct mdoc     *mdoc;
           struct roff     *roff;
   
                                 /* Handle in-line `\"' comments. */          man = NULL;
           mdoc = NULL;
           roff = NULL;
           memset(&ln, 0, sizeof(struct buf));
   
                                 if (1 == pos || '\"' != ln->buf[pos - 1])          /*
                                         continue;           * Two buffers: ln and buf.  buf is the input file and may be
            * memory mapped.  ln is a line buffer and grows on-demand.
            */
   
                                 for (j = pos - 2; j >= 0; j--)          if ( ! read_whole_file(curp, &blk, &with_mmap))
                                         if ('\\' != ln->buf[j])                  return;
                                                 break;  
   
                                 if ( ! ((pos - 2 - j) % 2))          if (NULL == curp->roff)
                                         continue;                  curp->roff = roff_init(curp);
           if (NULL == (roff = curp->roff))
                   goto bailout;
   
                                 comment = 1;          for (i = 0, lnn = 1; i < (int)blk.sz;) {
                                 pos -= 2;                  pos = 0;
                   lnn_start = lnn;
                   while (i < (int)blk.sz) {
                           if ('\n' == blk.buf[i]) {
                                   ++i;
                                   ++lnn;
                                   break;
                           }
                           /* Trailing backslash is like a plain character. */
                           if ('\\' != blk.buf[i] || i + 1 == (int)blk.sz) {
                                   if (pos >= (int)ln.sz)
                                           if (! resize_buf(&ln, 256))
                                                   goto bailout;
                                   ln.buf[pos++] = blk.buf[i++];
                                   continue;
                           }
                           /* Found an escape and at least one other character. */
                           if ('\n' == blk.buf[i + 1]) {
                                   /* Escaped newlines are skipped over */
                                   i += 2;
                                   ++lnn;
                                   continue;
                           }
                           if ('"' == blk.buf[i + 1]) {
                                   i += 2;
                                   /* Comment, skip to end of line */
                                   for (; i < (int)blk.sz; ++i) {
                                           if ('\n' == blk.buf[i]) {
                                                   ++i;
                                                   ++lnn;
                                                   break;
                                           }
                                   }
                                   /* Backout trailing whitespaces */
                                 for (; pos > 0; --pos) {                                  for (; pos > 0; --pos) {
                                         if (ln->buf[pos - 1] != ' ')                                          if (ln.buf[pos - 1] != ' ')
                                                 break;                                                  break;
                                         if (pos > 2 && ln->buf[pos - 2] == '\\')                                          if (pos > 2 && ln.buf[pos - 2] == '\\')
                                                 break;                                                  break;
                                 }                                  }
                                 continue;                                  break;
                         }  
   
                         /* Handle escaped `\\n' newlines. */  
   
                         if (pos > 0 && 0 == comment &&  
                                         '\\' == ln->buf[pos - 1]) {  
                                 for (j = pos - 1; j >= 0; j--)  
                                         if ('\\' != ln->buf[j])  
                                                 break;  
                                 if ( ! ((pos - j) % 2)) {  
                                         pos--;  
                                         lnn++;  
                                         continue;  
                                 }  
                         }                          }
                           /* Some other escape sequence, copy and continue. */
                           if (pos + 1 >= (int)ln.sz)
                                   if (! resize_buf(&ln, 256))
                                           goto bailout;
   
                         ln->buf[pos] = 0;                          ln.buf[pos++] = blk.buf[i++];
                         lnn++;                          ln.buf[pos++] = blk.buf[i++];
                   }
   
                         /* If unset, assign parser in pset(). */                  if (pos >= (int)ln.sz)
                           if (! resize_buf(&ln, 256))
                         if ( ! (man || mdoc) && ! pset(ln->buf,  
                                                 pos, curp, &man, &mdoc))  
                                 goto bailout;                                  goto bailout;
                   ln.buf[pos] = '\0';
   
                         pos = comment = 0;                  /*
                    * A significant amount of complexity is contained by
                    * the roff preprocessor.  It's line-oriented but can be
                    * expressed on one line, so we need at times to
                    * readjust our starting point and re-run it.  The roff
                    * preprocessor can also readjust the buffers with new
                    * data, so we pass them in wholesale.
                    */
   
                         /* Pass down into parsers. */                  of = 0;
                   do {
                           re = roff_parseln(roff, lnn_start,
                                           &ln.buf, &ln.sz, of, &of);
                   } while (ROFF_RERUN == re);
   
                         if (man && ! man_parseln(man, lnn, ln->buf))                  if (ROFF_IGN == re)
                           continue;
                   else if (ROFF_ERR == re)
                           goto bailout;
   
                   /*
                    * If input parsers have not been allocated, do so now.
                    * We keep these instanced betwen parsers, but set them
                    * locally per parse routine since we can use different
                    * parsers with each one.
                    */
   
                   if ( ! (man || mdoc))
                           if ( ! pset(ln.buf + of, pos - of, curp, &man, &mdoc))
                                 goto bailout;                                  goto bailout;
                         if (mdoc && ! mdoc_parseln(mdoc, lnn, ln->buf))  
                                 goto bailout;                  /* Lastly, push down into the parsers themselves. */
                 }  
                   if (man && ! man_parseln(man, lnn_start, ln.buf, of))
                           goto bailout;
                   if (mdoc && ! mdoc_parseln(mdoc, lnn_start, ln.buf, of))
                           goto bailout;
         }          }
   
         /* NOTE a parser may not have been assigned, yet. */          /* NOTE a parser may not have been assigned, yet. */
Line 408  fdesc(struct buf *blk, struct buf *ln, struct curparse
Line 513  fdesc(struct buf *blk, struct buf *ln, struct curparse
                 goto bailout;                  goto bailout;
         }          }
   
           /* Clean up the parse routine ASTs. */
   
         if (mdoc && ! mdoc_endparse(mdoc))          if (mdoc && ! mdoc_endparse(mdoc))
                 goto bailout;                  goto bailout;
         if (man && ! man_endparse(man))          if (man && ! man_endparse(man))
                 goto bailout;                  goto bailout;
           if (roff && ! roff_endparse(roff))
                   goto bailout;
   
         /* If unset, allocate output dev now (if applicable). */          /* If unset, allocate output dev now (if applicable). */
   
Line 436  fdesc(struct buf *blk, struct buf *ln, struct curparse
Line 545  fdesc(struct buf *blk, struct buf *ln, struct curparse
                 case (OUTT_LINT):                  case (OUTT_LINT):
                         break;                          break;
                 default:                  default:
                         curp->outdata = ascii_alloc();                          curp->outdata = ascii_alloc(80);
                         curp->outman = terminal_man;                          curp->outman = terminal_man;
                         curp->outmdoc = terminal_mdoc;                          curp->outmdoc = terminal_mdoc;
                         curp->outfree = terminal_free;                          curp->outfree = terminal_free;
Line 452  fdesc(struct buf *blk, struct buf *ln, struct curparse
Line 561  fdesc(struct buf *blk, struct buf *ln, struct curparse
                 (*curp->outmdoc)(curp->outdata, mdoc);                  (*curp->outmdoc)(curp->outdata, mdoc);
   
  cleanup:   cleanup:
         if (curp->mdoc) {          if (mdoc)
                 mdoc_free(curp->mdoc);                  mdoc_reset(mdoc);
                 curp->mdoc = NULL;          if (man)
         }                  man_reset(man);
         if (curp->man) {          if (roff)
                 man_free(curp->man);                  roff_reset(roff);
                 curp->man = NULL;          if (ln.buf)
         }                  free(ln.buf);
           if (with_mmap)
                   munmap(blk.buf, blk.sz);
           else
                   free(blk.buf);
   
         return;          return;
   
  bailout:   bailout:
Line 482  pset(const char *buf, int pos, struct curparse *curp,
Line 596  pset(const char *buf, int pos, struct curparse *curp,
          * default to -man, which is more lenient.           * default to -man, which is more lenient.
          */           */
   
         if (buf[0] == '.') {          if ('.' == buf[0] || '\'' == buf[0]) {
                 for (i = 1; buf[i]; i++)                  for (i = 1; buf[i]; i++)
                         if (' ' != buf[i] && '\t' != buf[i])                          if (' ' != buf[i] && '\t' != buf[i])
                                 break;                                  break;
Line 496  pset(const char *buf, int pos, struct curparse *curp,
Line 610  pset(const char *buf, int pos, struct curparse *curp,
                         curp->mdoc = mdoc_init(curp);                          curp->mdoc = mdoc_init(curp);
                 if (NULL == (*mdoc = curp->mdoc))                  if (NULL == (*mdoc = curp->mdoc))
                         return(0);                          return(0);
                 curp->lastmdoc = *mdoc;  
                 return(1);                  return(1);
         case (INTT_MAN):          case (INTT_MAN):
                 if (NULL == curp->man)                  if (NULL == curp->man)
                         curp->man = man_init(curp);                          curp->man = man_init(curp);
                 if (NULL == (*man = curp->man))                  if (NULL == (*man = curp->man))
                         return(0);                          return(0);
                 curp->lastman = *man;  
                 return(1);                  return(1);
         default:          default:
                 break;                  break;
Line 514  pset(const char *buf, int pos, struct curparse *curp,
Line 626  pset(const char *buf, int pos, struct curparse *curp,
                         curp->mdoc = mdoc_init(curp);                          curp->mdoc = mdoc_init(curp);
                 if (NULL == (*mdoc = curp->mdoc))                  if (NULL == (*mdoc = curp->mdoc))
                         return(0);                          return(0);
                 curp->lastmdoc = *mdoc;  
                 return(1);                  return(1);
         }          }
   
Line 522  pset(const char *buf, int pos, struct curparse *curp,
Line 633  pset(const char *buf, int pos, struct curparse *curp,
                 curp->man = man_init(curp);                  curp->man = man_init(curp);
         if (NULL == (*man = curp->man))          if (NULL == (*man = curp->man))
                 return(0);                  return(0);
         curp->lastman = *man;  
         return(1);          return(1);
 }  }
   
Line 685  mwarn(void *arg, int line, int col, const char *msg)
Line 795  mwarn(void *arg, int line, int col, const char *msg)
         return(1);          return(1);
 }  }
   
   static  const char * const      mandocerrs[MANDOCERR_MAX] = {
           "ok",
           "multi-line scope open on exit",
           "request for scope closure when no matching scope is open",
           "line arguments will be lost",
           "memory exhausted"
   };
   
   /*
    * XXX: this is experimental code that will eventually become the
    * generic means of covering all warnings and errors!
    */
   /* ARGSUSED */
   static int
   mmsg(enum mandocerr t, void *arg, int ln, int col, const char *msg)
   {
   #if 0
           struct curparse *cp;
   
           cp = (struct curparse *)arg;
   
           fprintf(stderr, "%s:%d:%d: %s", cp->file,
                           ln, col + 1, mandocerrs[t]);
   
           if (msg)
                   fprintf(stderr, ": %s", msg);
   
           fputc('\n', stderr);
   #endif
           return(1);
   }

Legend:
Removed from v.1.64  
changed lines
  Added in v.1.76

CVSweb