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

Diff for /mandoc/roff.c between version 1.105 and 1.106

version 1.105, 2010/12/01 16:54:25 version 1.106, 2010/12/02 10:53:03
Line 7 
Line 7 
  * purpose with or without fee is hereby granted, provided that the above   * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.   * copyright notice and this permission notice appear in all copies.
  *   *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES   * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF   * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR   * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES   * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN   * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF   * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
Line 66  enum rofft {
Line 66  enum rofft {
         ROFF_tr,          ROFF_tr,
         ROFF_cblock,          ROFF_cblock,
         ROFF_ccond, /* FIXME: remove this. */          ROFF_ccond, /* FIXME: remove this. */
           ROFF_USERDEF,
         ROFF_MAX          ROFF_MAX
 };  };
   
Line 88  struct roff {
Line 89  struct roff {
         enum roffrule    rstack[RSTACK_MAX]; /* stack of !`ie' rules */          enum roffrule    rstack[RSTACK_MAX]; /* stack of !`ie' rules */
         int              rstackpos; /* position in rstack */          int              rstackpos; /* position in rstack */
         struct regset   *regs; /* read/writable registers */          struct regset   *regs; /* read/writable registers */
         struct roffstr  *first_string;          struct roffstr  *first_string; /* user-defined strings & macros */
           const char      *current_string; /* value of last called user macro */
 };  };
   
 struct  roffnode {  struct  roffnode {
Line 96  struct roffnode {
Line 98  struct roffnode {
         struct roffnode *parent; /* up one in stack */          struct roffnode *parent; /* up one in stack */
         int              line; /* parse line */          int              line; /* parse line */
         int              col; /* parse col */          int              col; /* parse col */
           char            *name; /* node name, e.g. macro name */
         char            *end; /* end-rules: custom token */          char            *end; /* end-rules: custom token */
         int              endspan; /* end-rules: next-line or infty */          int              endspan; /* end-rules: next-line or infty */
         enum roffrule    rule; /* current evaluation rule */          enum roffrule    rule; /* current evaluation rule */
Line 141  static enum rofferr  roff_nr(ROFF_ARGS);
Line 144  static enum rofferr  roff_nr(ROFF_ARGS);
 static  int              roff_res(struct roff *,  static  int              roff_res(struct roff *,
                                 char **, size_t *, int);                                  char **, size_t *, int);
 static  void             roff_setstr(struct roff *,  static  void             roff_setstr(struct roff *,
                                 const char *, const char *);                                  const char *, const char *, int);
 static  enum rofferr     roff_so(ROFF_ARGS);  static  enum rofferr     roff_so(ROFF_ARGS);
 static  char            *roff_strdup(const char *);  static  enum rofferr     roff_userdef(ROFF_ARGS);
   
 /* See roff_hash_find() */  /* See roff_hash_find() */
   
Line 175  static struct roffmac  roffs[ROFF_MAX] = {
Line 178  static struct roffmac  roffs[ROFF_MAX] = {
         { "tr", roff_line_ignore, NULL, NULL, 0, NULL },          { "tr", roff_line_ignore, NULL, NULL, 0, NULL },
         { ".", roff_cblock, NULL, NULL, 0, NULL },          { ".", roff_cblock, NULL, NULL, 0, NULL },
         { "\\}", roff_ccond, NULL, NULL, 0, NULL },          { "\\}", roff_ccond, NULL, NULL, 0, NULL },
           { NULL, roff_userdef, NULL, NULL, 0, NULL },
 };  };
   
 static  void             roff_free1(struct roff *);  static  void             roff_free1(struct roff *);
 static  enum rofft       roff_hash_find(const char *);  static  enum rofft       roff_hash_find(const char *, size_t);
 static  void             roff_hash_init(void);  static  void             roff_hash_init(void);
 static  void             roffnode_cleanscope(struct roff *);  static  void             roffnode_cleanscope(struct roff *);
 static  void             roffnode_push(struct roff *,  static  void             roffnode_push(struct roff *, enum rofft,
                                 enum rofft, int, int);                                  const char *, int, int);
 static  void             roffnode_pop(struct roff *);  static  void             roffnode_pop(struct roff *);
 static  enum rofft       roff_parse(const char *, int *);  static  enum rofft       roff_parse(struct roff *, const char *, int *);
 static  int              roff_parse_nat(const char *, unsigned int *);  static  int              roff_parse_nat(const char *, unsigned int *);
   
 /* See roff_hash_find() */  /* See roff_hash_find() */
Line 196  roff_hash_init(void)
Line 200  roff_hash_init(void)
         struct roffmac   *n;          struct roffmac   *n;
         int               buc, i;          int               buc, i;
   
         for (i = 0; i < (int)ROFF_MAX; i++) {          for (i = 0; i < (int)ROFF_USERDEF; i++) {
                 assert(roffs[i].name[0] >= ASCII_LO);                  assert(roffs[i].name[0] >= ASCII_LO);
                 assert(roffs[i].name[0] <= ASCII_HI);                  assert(roffs[i].name[0] <= ASCII_HI);
   
Line 217  roff_hash_init(void)
Line 221  roff_hash_init(void)
  * the nil-terminated string name could be found.   * the nil-terminated string name could be found.
  */   */
 static enum rofft  static enum rofft
 roff_hash_find(const char *p)  roff_hash_find(const char *p, size_t s)
 {  {
         int              buc;          int              buc;
         struct roffmac  *n;          struct roffmac  *n;
Line 237  roff_hash_find(const char *p)
Line 241  roff_hash_find(const char *p)
         if (NULL == (n = hash[buc]))          if (NULL == (n = hash[buc]))
                 return(ROFF_MAX);                  return(ROFF_MAX);
         for ( ; n; n = n->next)          for ( ; n; n = n->next)
                 if (0 == strcmp(n->name, p))                  if (0 == strncmp(n->name, p, s) && '\0' == n->name[(int)s])
                         return((enum rofft)(n - roffs));                          return((enum rofft)(n - roffs));
   
         return(ROFF_MAX);          return(ROFF_MAX);
Line 262  roffnode_pop(struct roff *r)
Line 266  roffnode_pop(struct roff *r)
   
         ROFF_DEBUG("roff: popping scope\n");          ROFF_DEBUG("roff: popping scope\n");
         r->last = r->last->parent;          r->last = r->last->parent;
         if (p->end)          free(p->name);
                 free(p->end);          free(p->end);
         free(p);          free(p);
 }  }
   
Line 273  roffnode_pop(struct roff *r)
Line 277  roffnode_pop(struct roff *r)
  * removed with roffnode_pop().   * removed with roffnode_pop().
  */   */
 static void  static void
 roffnode_push(struct roff *r, enum rofft tok, int line, int col)  roffnode_push(struct roff *r, enum rofft tok, const char *name,
                   int line, int col)
 {  {
         struct roffnode *p;          struct roffnode *p;
   
         p = mandoc_calloc(1, sizeof(struct roffnode));          p = mandoc_calloc(1, sizeof(struct roffnode));
         p->tok = tok;          p->tok = tok;
           if (name)
                   p->name = mandoc_strdup(name);
         p->parent = r->last;          p->parent = r->last;
         p->line = line;          p->line = line;
         p->col = col;          p->col = col;
Line 412  roff_parseln(struct roff *r, int ln, char **bufp, 
Line 419  roff_parseln(struct roff *r, int ln, char **bufp, 
          */           */
   
         if (r->first_string && ! roff_res(r, bufp, szp, pos))          if (r->first_string && ! roff_res(r, bufp, szp, pos))
                 return(ROFF_RERUN);                  return(ROFF_REPARSE);
   
         /*          /*
          * First, if a scope is open and we're not a macro, pass the           * First, if a scope is open and we're not a macro, pass the
Line 453  roff_parseln(struct roff *r, int ln, char **bufp, 
Line 460  roff_parseln(struct roff *r, int ln, char **bufp, 
          */           */
   
         ppos = pos;          ppos = pos;
         if (ROFF_MAX == (t = roff_parse(*bufp, &pos)))          if (ROFF_MAX == (t = roff_parse(r, *bufp, &pos)))
                 return(ROFF_CONT);                  return(ROFF_CONT);
   
         ROFF_DEBUG("roff: intercept new-scope: %s, [%s]\n",          ROFF_DEBUG("roff: intercept new-scope: %s, [%s]\n",
Line 481  roff_endparse(struct roff *r)
Line 488  roff_endparse(struct roff *r)
  * form of ".foo xxx" in the usual way.   * form of ".foo xxx" in the usual way.
  */   */
 static enum rofft  static enum rofft
 roff_parse(const char *buf, int *pos)  roff_parse(struct roff *r, const char *buf, int *pos)
 {  {
         int              j;          const char      *mac;
         char             mac[5];          size_t           maclen;
         enum rofft       t;          enum rofft       t;
   
         assert(ROFF_CTL(buf[*pos]));          assert(ROFF_CTL(buf[*pos]));
         (*pos)++;          (*pos)++;
   
         while (buf[*pos] && (' ' == buf[*pos] || '\t' == buf[*pos]))          while (' ' == buf[*pos] || '\t' == buf[*pos])
                 (*pos)++;                  (*pos)++;
   
         if ('\0' == buf[*pos])          if ('\0' == buf[*pos])
                 return(ROFF_MAX);                  return(ROFF_MAX);
   
         for (j = 0; j < 4; j++, (*pos)++)          mac = buf + *pos;
                 if ('\0' == (mac[j] = buf[*pos]))          maclen = strcspn(mac, " \\\t\0");
                         break;  
                 else if (' ' == buf[*pos] || (j && '\\' == buf[*pos]))  
                         break;  
   
         if (j == 4 || j < 1)          t = (r->current_string = roff_getstrn(r, mac, maclen))
                 return(ROFF_MAX);              ? ROFF_USERDEF : roff_hash_find(mac, maclen);
   
         mac[j] = '\0';          *pos += maclen;
   
         if (ROFF_MAX == (t = roff_hash_find(mac)))  
                 return(t);  
   
         while (buf[*pos] && ' ' == buf[*pos])          while (buf[*pos] && ' ' == buf[*pos])
                 (*pos)++;                  (*pos)++;
   
Line 564  roff_cblock(ROFF_ARGS)
Line 564  roff_cblock(ROFF_ARGS)
                 /* FALLTHROUGH */                  /* FALLTHROUGH */
         case (ROFF_dei):          case (ROFF_dei):
                 /* FALLTHROUGH */                  /* FALLTHROUGH */
         case (ROFF_de1):  
                 /* FALLTHROUGH */  
         case (ROFF_ig):          case (ROFF_ig):
                 break;                  break;
         default:          default:
Line 643  roff_block(ROFF_ARGS)
Line 641  roff_block(ROFF_ARGS)
 {  {
         int             sv;          int             sv;
         size_t          sz;          size_t          sz;
           char            *name;
   
         if (ROFF_ig != tok && '\0' == (*bufp)[pos]) {          name = NULL;
                 if ( ! (*r->msg)(MANDOCERR_NOARGS, r->data, ln, ppos, NULL))  
                         return(ROFF_ERR);          if (ROFF_ig != tok) {
                 return(ROFF_IGN);                  if ('\0' == (*bufp)[pos]) {
         } else if (ROFF_ig != tok) {                          (*r->msg)(MANDOCERR_NOARGS, r->data, ln, ppos, NULL);
                           return(ROFF_IGN);
                   }
                   if (ROFF_de1 == tok)
                           tok = ROFF_de;
                   if (ROFF_de == tok)
                           name = *bufp + pos;
                   else
                           (*r->msg)(MANDOCERR_REQUEST, r->data, ln, ppos,
                               roffs[tok].name);
                 while ((*bufp)[pos] && ' ' != (*bufp)[pos])                  while ((*bufp)[pos] && ' ' != (*bufp)[pos])
                         pos++;                          pos++;
                 while (' ' == (*bufp)[pos])                  while (' ' == (*bufp)[pos])
                         pos++;                          (*bufp)[pos++] = '\0';
         }          }
   
         roffnode_push(r, tok, ln, ppos);          roffnode_push(r, tok, name, ln, ppos);
   
           /*
            * At the beginning of a `de' macro, clear the existing string
            * with the same name, if there is one.  New content will be
            * added from roff_block_text() in multiline mode.
            */
           if (ROFF_de == tok)
                   roff_setstr(r, name, "", 0);
   
         if ('\0' == (*bufp)[pos])          if ('\0' == (*bufp)[pos])
                 return(ROFF_IGN);                  return(ROFF_IGN);
   
Line 722  roff_block_sub(ROFF_ARGS)
Line 738  roff_block_sub(ROFF_ARGS)
                         roffnode_pop(r);                          roffnode_pop(r);
                         roffnode_cleanscope(r);                          roffnode_cleanscope(r);
   
                         if (ROFF_MAX != roff_parse(*bufp, &pos))                          if (ROFF_MAX != roff_parse(r, *bufp, &pos))
                                 return(ROFF_RERUN);                                  return(ROFF_RERUN);
                         return(ROFF_IGN);                          return(ROFF_IGN);
                 }                  }
Line 734  roff_block_sub(ROFF_ARGS)
Line 750  roff_block_sub(ROFF_ARGS)
          */           */
   
         ppos = pos;          ppos = pos;
         t = roff_parse(*bufp, &pos);          t = roff_parse(r, *bufp, &pos);
   
         /* If we're not a comment-end, then throw it away. */          /*
         if (ROFF_cblock != t)           * Macros other than block-end are only significant
            * in `de' blocks; elsewhere, simply throw them away.
            */
           if (ROFF_cblock != t) {
                   if (ROFF_de == tok)
                           roff_setstr(r, r->last->name, *bufp + ppos, 1);
                 return(ROFF_IGN);                  return(ROFF_IGN);
           }
   
         assert(roffs[t].proc);          assert(roffs[t].proc);
         return((*roffs[t].proc)(r, t, bufp, szp,          return((*roffs[t].proc)(r, t, bufp, szp,
Line 751  static enum rofferr
Line 773  static enum rofferr
 roff_block_text(ROFF_ARGS)  roff_block_text(ROFF_ARGS)
 {  {
   
           if (ROFF_de == tok)
                   roff_setstr(r, r->last->name, *bufp + pos, 1);
   
         return(ROFF_IGN);          return(ROFF_IGN);
 }  }
   
Line 772  roff_cond_sub(ROFF_ARGS)
Line 797  roff_cond_sub(ROFF_ARGS)
   
         roffnode_cleanscope(r);          roffnode_cleanscope(r);
   
         if (ROFF_MAX == (t = roff_parse(*bufp, &pos))) {          if (ROFF_MAX == (t = roff_parse(r, *bufp, &pos))) {
                 if ('\\' == (*bufp)[pos] && '}' == (*bufp)[pos + 1])                  if ('\\' == (*bufp)[pos] && '}' == (*bufp)[pos + 1])
                         return(roff_ccond                          return(roff_ccond
                                 (r, ROFF_ccond, bufp, szp,                                  (r, ROFF_ccond, bufp, szp,
                                  ln, pos, pos + 2, offs));                                   ln, pos, pos + 2, offs));
                 return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);                  return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
         }          }
Line 913  roff_cond(ROFF_ARGS)
Line 938  roff_cond(ROFF_ARGS)
                 return(ROFF_ERR);                  return(ROFF_ERR);
         }          }
   
         roffnode_push(r, tok, ln, ppos);          roffnode_push(r, tok, NULL, ln, ppos);
   
         r->last->rule = rule;          r->last->rule = rule;
   
Line 1010  roff_ds(ROFF_ARGS)
Line 1035  roff_ds(ROFF_ARGS)
                 string++;                  string++;
   
         /* The rest is the value. */          /* The rest is the value. */
         roff_setstr(r, name, string);          roff_setstr(r, name, string, 0);
         return(ROFF_IGN);          return(ROFF_IGN);
 }  }
   
Line 1082  roff_so(ROFF_ARGS)
Line 1107  roff_so(ROFF_ARGS)
         return(ROFF_SO);          return(ROFF_SO);
 }  }
   
 static char *  /* ARGSUSED */
 roff_strdup(const char *name)  static enum rofferr
   roff_userdef(ROFF_ARGS)
 {  {
         char            *namecopy, *sv;          const char       *arg[9];
           char             *cp, *n1, *n2;
           int               i, quoted, pairs;
   
         /*          /*
          * This isn't a nice simple mandoc_strdup() because we must           * Collect pointers to macro argument strings
          * handle roff's stupid double-escape rule.           * and null-terminate them.
          */           */
         sv = namecopy = mandoc_malloc(strlen(name) + 1);          cp = *bufp + pos;
         while (*name) {          for (i = 0; i < 9; i++) {
                 if ('\\' == *name && '\\' == *(name + 1))                  /* Quoting can only start with a new word. */
                         name++;                  if ('"' == *cp) {
                 *namecopy++ = *name++;                          quoted = 1;
                           cp++;
                   } else
                           quoted = 0;
                   arg[i] = cp;
                   for (pairs = 0; '\0' != *cp; cp++) {
                           /* Unquoted arguments end at blanks. */
                           if (0 == quoted) {
                                   if (' ' == *cp)
                                           break;
                                   continue;
                           }
                           /* After pairs of quotes, move left. */
                           if (pairs)
                                   cp[-pairs] = cp[0];
                           /* Pairs of quotes do not end words, ... */
                           if ('"' == cp[0] && '"' == cp[1]) {
                                   pairs++;
                                   cp++;
                                   continue;
                           }
                           /* ... but solitary quotes do. */
                           if ('"' != *cp)
                                   continue;
                           if (pairs)
                                   cp[-pairs] = '\0';
                           *cp = ' ';
                           break;
                   }
                   /* Last argument; the remaining ones are empty strings. */
                   if ('\0' == *cp)
                           continue;
                   /* Null-terminate argument and move to the next one. */
                   *cp++ = '\0';
                   while (' ' == *cp)
                           cp++;
         }          }
   
         *namecopy = '\0';          /*
         return(sv);           * Expand macro arguments.
 }           */
           *szp = 0;
           n1 = cp = mandoc_strdup(r->current_string);
           while (NULL != (cp = strstr(cp, "\\$"))) {
                   i = cp[2] - '1';
                   if (0 > i || 8 < i) {
                           /* Not an argument invocation. */
                           cp += 2;
                           continue;
                   }
   
                   *szp = strlen(n1) - 3 + strlen(arg[i]) + 1;
                   n2 = mandoc_malloc(*szp);
   
                   strlcpy(n2, n1, (size_t)(cp - n1 + 1));
                   strlcat(n2, arg[i], *szp);
                   strlcat(n2, cp + 3, *szp);
   
                   cp = n2 + (cp - n1);
                   free(n1);
                   n1 = n2;
           }
   
           /*
            * Replace the macro invocation
            * by the expanded macro.
            */
           free(*bufp);
           *bufp = n1;
           if (0 == *szp)
                   *szp = strlen(*bufp) + 1;
   
           return(*szp > 1 && '\n' == (*bufp)[(int)*szp - 2] ?
              ROFF_REPARSE : ROFF_APPEND);
   }
   
   /*
    * Store *string into the user-defined string called *name.
    * In multiline mode, append to an existing entry and append '\n';
    * else replace the existing entry, if there is one.
    * To clear an existing entry, call with (*r, *name, NULL, 0).
    */
 static void  static void
 roff_setstr(struct roff *r, const char *name, const char *string)  roff_setstr(struct roff *r, const char *name, const char *string,
           int multiline)
 {  {
         struct roffstr   *n;          struct roffstr   *n;
         char             *namecopy;          char             *c;
           size_t            oldch, newch;
   
           /* Search for an existing string with the same name. */
         n = r->first_string;          n = r->first_string;
         while (n && strcmp(name, n->name))          while (n && strcmp(name, n->name))
                 n = n->next;                  n = n->next;
   
         if (NULL == n) {          if (NULL == n) {
                 namecopy = mandoc_strdup(name);                  /* Create a new string table entry. */
                 n = mandoc_malloc(sizeof(struct roffstr));                  n = mandoc_malloc(sizeof(struct roffstr));
                 n->name = namecopy;                  n->name = mandoc_strdup(name);
                   n->string = NULL;
                 n->next = r->first_string;                  n->next = r->first_string;
                 r->first_string = n;                  r->first_string = n;
         } else          } else if (0 == multiline) {
                   /* In multiline mode, append; else replace. */
                 free(n->string);                  free(n->string);
                   n->string = NULL;
           }
   
         /* Don't use mandoc_strdup: clean out double-escapes. */          if (NULL == string)
         n->string = string ? roff_strdup(string) : NULL;                  return;
         ROFF_DEBUG("roff: new symbol: [%s] = [%s]\n", name, n->string);  
           /*
            * One additional byte for the '\n' in multiline mode,
            * and one for the terminating '\0'.
            */
           newch = strlen(string) + (multiline ? 2 : 1);
           if (NULL == n->string) {
                   n->string = mandoc_malloc(newch);
                   *n->string = '\0';
                   oldch = 0;
           } else {
                   oldch = strlen(n->string);
                   n->string = mandoc_realloc(n->string, oldch + newch);
           }
   
           /* Skip existing content in the destination buffer. */
           c = n->string + oldch;
   
           /* Append new content to the destination buffer. */
           while (*string) {
                   /*
                    * Rudimentary roff copy mode:
                    * Handle escaped backslashes.
                    */
                   if ('\\' == *string && '\\' == *(string + 1))
                           string++;
                   *c++ = *string++;
           }
   
           /* Append terminating bytes. */
           if (multiline)
                   *c++ = '\n';
           *c = '\0';
 }  }
   
   

Legend:
Removed from v.1.105  
changed lines
  Added in v.1.106

CVSweb