delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp-workers/2001/01/23/17:06:42

Sender: rich AT phekda DOT freeserve DOT co DOT uk
Message-ID: <3A6E0054.12D5CC7E@phekda.freeserve.co.uk>
Date: Tue, 23 Jan 2001 22:06:12 +0000
From: Richard Dawe <rich AT phekda DOT freeserve DOT co DOT uk>
X-Mailer: Mozilla 4.51 [en] (X11; I; Linux 2.2.17 i586)
X-Accept-Language: de,fr
MIME-Version: 1.0
To: DJGPP workers <djgpp-workers AT delorie DOT com>
Subject: mkdoc patch, take 3
Reply-To: djgpp-workers AT delorie DOT com

Hello.

This is a resend of a couple of mails. No-one commented last time, so I'm
posting it again. The originals were dated Thu, 23 Nov 2000. If the size
of the patch is a problem, I'm not sure what I can do to make it easier to
read, since it really is just one change.

I'd rather apply the updates to mkdoc one at a time, since I don't know
when I'll have time to work on the other requests changes.

Thanks, bye, Rich =]

---Things I forgot---
I forgot a couple of things:

Richard Dawe wrote:
> What the patch doesn't do:
> 
> * You can't define your portability types "on the fly" as suggested by
> DJ.

* An option to dump out the table of targets and their qualifiers.

* Multi-line @port-note support.

---Original message---
Hello.

It's been a while, but here's another take on my patch to mkdoc.cc. Here's
what it does:

* Allows you to specify a portability target, e.g. ansi, and a qualifier,
e.g. c99, so you can have portability statements like:

@portability ansi-c99

or:

@portability unix-98

It handles the case where you just specify 'ansi' by listing all the ANSI
standards it knows about - C89, C99. The targets + qualifiers are table
driven, so you can add commonly-used portability cases easily.

* Puts the portability information in a tabular format.

Changes since the last patch:

* Now puts all !portability cases at the end of a line, to avoid a
confusing mix of postives and negatives. Previously we could have:

@portability !ansi, posix

producing:

Not ANSI, POSIX

It is unclear from this whether it's only "not ANSI" or neither ANSI nor
POSIX that are supported. Moving to a tabular format removed this vagarity
initially, e.g.

ANSI:  No
POSIX: Yes

But then adding C99 brought the confusion back. Now we can have:

@portability ansi-c89, !ansi-c99

producing:

ANSI/ISO C     C89, not C99

or:

@portability !ansi-c89, ansi-c99

producing:

ANSI/ISO C     C99, not C89

What the patch doesn't do:

* You can't define your portability types "on the fly" as suggested by DJ.

I also have a Perl script that converts @portability lines with 'ansi' to
equivalent lines with both 'ansi-c89', 'ansi-c99', if anyone's interested.
The script isn't much use now, since the patched mkdoc handles it as you'd
expect (!ansi -> !ansi-c89, !ansi-c99, ansi -> ansi-c89, ansi-c99).

Comments, etc. welcome. Bye, Rich =]

*** mkdoc.cc.orig       Thu Nov 23 17:21:42 2000
--- mkdoc.cc    Thu Nov 23 17:21:50 2000
***************
*** 1,3 ****
--- 1,4 ----
+ /* Copyright (C) 2000 DJ Delorie, see COPYING.DJ for details */
  /* Copyright (C) 1999 DJ Delorie, see COPYING.DJ for details */
  /* Copyright (C) 1998 DJ Delorie, see COPYING.DJ for details */
  /* Copyright (C) 1997 DJ Delorie, see COPYING.DJ for details */
*************** int count_nodes = 0;
*** 29,47 ****
  
  typedef void (*TFunc)(Node *);
  
! 
! #define NUM_PORT_TARGETS 2
  
  #define PORT_UNKNOWN 0
  #define PORT_NO      1
  #define PORT_PARTIAL 2
  #define PORT_YES     3
  
! /* Tokens for use in .txh files */
! char *port_target[NUM_PORT_TARGETS] = { "ansi", "posix" };
! /* Strings to output in .txi files */
! char *port_target_string[NUM_PORT_TARGETS] = { "ANSI", "POSIX" };
! 
  
  struct Tree {
    TreeNode *nodes;
--- 30,94 ----
  
  typedef void (*TFunc)(Node *);
  
! #define PORT_TARGET_NONE              0x00
! /* ANSI/ISO C */
! #define PORT_TARGET_ANSI_C89          0x10
! #define PORT_TARGET_ANSI_C99          0x11
! /* POSIX */
! #define PORT_TARGET_POSIX_1003_2_1992 0x20
! /* Single Unix Specification(s) (SUS) */
! #define PORT_TARGET_UNIX_98           0x30
  
  #define PORT_UNKNOWN 0
  #define PORT_NO      1
  #define PORT_PARTIAL 2
  #define PORT_YES     3
  
! /* This structure is used to store both the default information about a
!  * particular porting target and the information parsed from the
texinfo.
!  * For the default case, complete specifies the default value for this
!  * qualifier if the prefix is matched, but the suffix is not. */
! typedef struct {
!   char *suffix_token;  /* Suffix token used in texinfo, e.g. 'c89' */
!   char *suffix_name;   /* Portability qualifier name, e.g. C89 */
!   int target;          /* One of PORT_TARGET_* */
!   int complete;        /* One of PORT_UNKNOWN, etc. */
! } PortQualifier;
! 
! #define MAX_PORT_QUALIFIERS 2
! 
! typedef struct {
!   char *prefix_token; /* Token used in texinfo, e.g. 'ansi' */
!   char *prefix_name;  /* Actual textual name for token, e.g. ANSI/ISO C
*/
!   PortQualifier pq[MAX_PORT_QUALIFIERS];
! } PortInfo;
! 
! #define NUM_PORT_TARGETS    3
! 
! PortInfo port_target[] = {
!   /* ANSI/ISO C */
!   { "ansi",  "ANSI/ISO C",
!     {
!       { "c89", "C89", PORT_TARGET_ANSI_C89, PORT_YES },
!       { "c99", "C99", PORT_TARGET_ANSI_C99, PORT_YES }
!     }
!   },
!   /* POSIX */
!   { "posix", "POSIX",
!     {
!       { "1003.2-1992", "1003.2-1992",
!       PORT_TARGET_POSIX_1003_2_1992, PORT_YES },
!       { 0 }
!     }
!   },
!   /* SUSv2 */
!   { "unix",  "Unix",
!     {
!       { "98", "Unix98", PORT_TARGET_UNIX_98, PORT_UNKNOWN },
!       { 0 }
!     }
!   }
! };
  
  struct Tree {
    TreeNode *nodes;
*************** struct Line {
*** 59,65 ****
  
  struct PortNote {
    struct PortNote *next;
!   int target;
    int number;
    char *note;
  };
--- 106,113 ----
  
  struct PortNote {
    struct PortNote *next;
!   PortInfo *pi;
!   PortQualifier *pq;
    int number;
    char *note;
  };
*************** struct Node {
*** 73,79 ****
    Line *lastline;
    Tree subnodes;
    char *filename;
!   int port_info[NUM_PORT_TARGETS];
    PortNote *port_notes;
    PortNote *last_port_note;
    Node(char *name, char *cat);
--- 121,127 ----
    Line *lastline;
    Tree subnodes;
    char *filename;
!   PortInfo port_info[NUM_PORT_TARGETS];
    PortNote *port_notes;
    PortNote *last_port_note;
    Node(char *name, char *cat);
*************** Node::Node(char *Pname, char *Pcat)
*** 107,113 ****
    cat = strdup(Pcat);
    lines = 0;
    lastline = 0;
!   for (int i = 0; i < NUM_PORT_TARGETS; i++) port_info[i] =
PORT_UNKNOWN;
    port_notes = NULL;
    last_port_note = NULL;
  }
--- 155,162 ----
    cat = strdup(Pcat);
    lines = 0;
    lastline = 0;
!   for (int i = 0; i < NUM_PORT_TARGETS; i++)
!     bzero(&port_info[i], sizeof(port_info[i]));
    port_notes = NULL;
    last_port_note = NULL;
  }
*************** void
*** 151,159 ****
  Node::read_portability_note(char *str)
  {
    char *work_str = strdup (str);
!   char *s = work_str;
    char *target;
!   int targ_num;
  
    while (isspace(*s)) s++;
    target = s;
--- 200,208 ----
  Node::read_portability_note(char *str)
  {
    char *work_str = strdup (str);
!   char *s = work_str, *x = NULL;
    char *target;
!   int i, j;
  
    while (isspace(*s)) s++;
    target = s;
*************** Node::read_portability_note(char *str)
*** 162,182 ****
    while (isspace(*s)) s++;
    dj_strlwr (target);
  
!   for (targ_num = 0; targ_num < NUM_PORT_TARGETS; targ_num++)
!     if (!strcmp (target, port_target[targ_num])) break;
  
!   if (targ_num == NUM_PORT_TARGETS)
!   {
      error ("unrecognised portability note target `%s' ignored.\n",
target);
!   }
!   else
!   {
      PortNote *p = new PortNote;
      p->next = NULL;
      p->number = 0;
!     p->target = targ_num;
      p->note = strdup ("");
  
      if (port_notes)
      {
        last_port_note->next = p;
--- 211,252 ----
    while (isspace(*s)) s++;
    dj_strlwr (target);
  
!   for (i = 0; i < NUM_PORT_TARGETS; i++) {
!     if (   (strlen (target) >= strlen (port_target[i].prefix_token))
!       && !strncmp (target, port_target[i].prefix_token,
!                    strlen (port_target[i].prefix_token))) {
!       /* If matched, check that the next character is either: null, a
dash
!        * (to indicate a qualifier) or null. */
!       x = target + strlen (port_target[i].prefix_token);
!       if ((*x == '\0') || (*x == '-') || isspace((int) *x))
!       break;
!     }
!   }  
  
!   if (i == NUM_PORT_TARGETS) {
      error ("unrecognised portability note target `%s' ignored.\n",
target);
!   } else {
      PortNote *p = new PortNote;
      p->next = NULL;
      p->number = 0;
!     p->pi = &port_target[i];
!     p->pq = NULL;
      p->note = strdup ("");
+     
+     /* Try to match the portability note to a portability qualifier. */
+     x = target + strlen (p->pi->prefix_token);
+     if (*x == '-')
+       x++;
+ 
+     for (j = 0; j < MAX_PORT_QUALIFIERS; j++) {
+       if (p->pi->pq[j].suffix_token == NULL) continue;
+       if (!strcmp (x, p->pi->pq[j].suffix_token)) break;
+     }
  
+     if (j < MAX_PORT_QUALIFIERS)
+       p->pq = &p->pi->pq[j];
+ 
+     /* Attach portability note to note chain. */
      if (port_notes)
      {
        last_port_note->next = p;
*************** void
*** 196,207 ****
  Node::read_portability(char *str)
  {
    char *targets = dj_strlwr (strdup (str));
!   char *x, *target = targets;
!   int type,i;
  
    while (isspace (*target)) target++;
    while (*target) {
- 
      type = PORT_YES;
      if (*target == '~')
      {
--- 266,276 ----
  Node::read_portability(char *str)
  {
    char *targets = dj_strlwr (strdup (str));
!   char *p = NULL, *x = NULL, *target = targets;
!   int type, i, j;
  
    while (isspace (*target)) target++;
    while (*target) {
      type = PORT_YES;
      if (*target == '~')
      {
*************** Node::read_portability(char *str)
*** 216,228 ****
      for (x = target; *x && !isspace(*x) && (*x != ','); x++);
      if (*x) *x++ = 0;
  
!     for (i = 0; i < NUM_PORT_TARGETS; i++)
!       if (!strcmp (target, port_target[i])) break;
  
!     if (i < NUM_PORT_TARGETS)
!       port_info[i] = type;
!     else
        error ("unrecognised portability target `%s' ignored.\n", target);
  
      target = x;
      while (isspace (*target)) target++;
--- 285,340 ----
      for (x = target; *x && !isspace(*x) && (*x != ','); x++);
      if (*x) *x++ = 0;
  
!     for (i = 0; i < NUM_PORT_TARGETS; i++) {
!       if (   (strlen (target) >= strlen (port_target[i].prefix_token))
!         && !strncmp (target, port_target[i].prefix_token,
!                      strlen (port_target[i].prefix_token))) {
!       /* If matched, check that the next character is either: null, a
dash
!        * (to indicate a qualifier) or null. */
!       p = target + strlen (port_target[i].prefix_token);
!       if ((*p == '\0') || (*p == '-') || isspace((int) *p))
!         break;
!       }
!     }
  
!     if (i < NUM_PORT_TARGETS) {
!       /* Now match the portability qualifier, if present. */
!       p = target + strlen (port_target[i].prefix_token);
! 
!       if (port_info[i].prefix_name == NULL) {
!       /* Copy default portability information to uninitialised port
!        * info, qualifier list. */
!       memcpy(&port_info[i], &port_target[i], sizeof(port_target[i]));
!       }
! 
! 
!       if (*p == '-') {
!       /* A qualifier is present, so set the portability type for just
!        * this qualifier. */
!       p++;
! 
!       for (j = 0; j < MAX_PORT_QUALIFIERS; j++) {
!         if (port_target[i].pq[j].suffix_token == NULL)
!           continue;
! 
!         if (!strcmp (p, port_target[i].pq[j].suffix_token))
!           break;
!       }
! 
!       if (j < NUM_PORT_TARGETS)
!         port_info[i].pq[j].complete = type;
!       } else {
!       /* A qualifier is not present, so set the type for all qualifiers.
*/
!       /* TODO: If the bare prefix appears after the prefix has appeared
!        * with qualifiers in the line, then this will reset all
qualifiers.
!        * This is a bug. The solution is to be careful with @portability.
*/
!       for (j = 0; j < MAX_PORT_QUALIFIERS; j++) {
!         port_info[i].pq[j].complete = type;
!       }
!       }
!     } else {
        error ("unrecognised portability target `%s' ignored.\n", target);
+     }
  
      target = x;
      while (isspace (*target)) target++;
*************** Node::read_portability(char *str)
*** 233,263 ****
  
  void
  Node::write_portability()
! {
    char buffer[1024] = { 0 };
    int note_number = 1;
  
!   for (int i = 0; i < NUM_PORT_TARGETS; i++)
!   {
!     switch (port_info[i])
      {
!       case PORT_NO:
!       strcat (buffer, "not ");
!       strcat (buffer, port_target_string[i]);
!       break;
!       case PORT_YES:
!       strcat (buffer, port_target_string[i]);
!       break;
!       case PORT_PARTIAL:
!       strcat (buffer, "partially ");
!       strcat (buffer, port_target_string[i]);
!       break;
      }
!     if (port_info[i] != PORT_UNKNOWN)
!     {
!       for (PortNote *p = port_notes; p; p = p->next)
        {
!       if (p->target == i)
        {
          char smallbuffer[20];
          p->number = note_number++;
--- 345,456 ----
  
  void
  Node::write_portability()
! {  
!   /* Column-width calculation variables */
!   size_t maxsize = 0;
!   ssize_t size = 0;
!   static int largest_target = -1;
!   static char rightpad[80] = { 0 };
! 
    char buffer[1024] = { 0 };
+   int qualifier_number = 0;
+   PortNote *p = NULL;
    int note_number = 1;
  
!   /* If all qualifiers are set to a particular value, store it here
!    * (one of PORT_*). Otherwise it should be set to -1. */
!   int all_port_qualifiers = -1;
! 
!   int i, j;
! 
!   /* Deduce the largest target name length, for table's left-hand
column. */
!   if (largest_target == -1)
!   {    
!     for (i = 0; i < NUM_PORT_TARGETS; i++)
      {
!       if (strlen(port_target[i].prefix_name) > maxsize)
!       {
!       maxsize = strlen(port_target[i].prefix_name);
!       largest_target = i;
!       }
      }
!   }
! 
!   /* Make the right-hand column 80 columns less the left-hand column
width,
!    * less some more for safety. */
!   if (rightpad[0] == '\0') {
!     size = sizeof(rightpad) - maxsize - 10;
!     if (size > 0) memset(rightpad, (int) 'x', size);
!   }
! 
!   strcat (buffer, "@multitable {");
!   strcat (buffer, port_target[largest_target].prefix_name);
!   strcat (buffer, "} {");  
!   strcat (buffer, rightpad);
!   strcat (buffer, "}\n");
! 
!   for (i = 0; i < NUM_PORT_TARGETS; i++)
!   {
!     /* No information given => unknown => skip it. */
!     if (port_info[i].prefix_name == NULL)
!       continue;
! 
!     /* Are all qualifiers set to the same value of one of PORT_*? */
!     for (j = 0; j < MAX_PORT_QUALIFIERS; j++) {
!       /* Skip unnamed suffixes */
!       if (port_info[i].pq[j].suffix_name == NULL)
!       continue;
! 
!       if (all_port_qualifiers == -1) {
!       all_port_qualifiers = port_info[i].pq[j].complete;
!       } else {
!       if (all_port_qualifiers != port_info[i].pq[j].complete) {
!         /* Not all port qualifiers have the same completion status. */
!         all_port_qualifiers = -1;
!         break;
!       }
!       }
!     }
! 
!     /* If all qualifiers are all set to unknown, skip this target. */
!     if (all_port_qualifiers == PORT_UNKNOWN)
!       continue;
! 
!     /* Add an entry to the table. */
!     strcat (buffer, "@item ");
!     strcat (buffer, port_target[i].prefix_name);
!     strcat (buffer, " @tab ");    
! 
!     qualifier_number = 0;
! 
!     /* Add positive or partial qualifiers to the list. */
!     for (j = 0; j < MAX_PORT_QUALIFIERS; j++) {
!       /* Skip unnamed suffixes */
!       if (port_info[i].pq[j].suffix_name == NULL)
!       continue;
! 
!       if (   (port_info[i].pq[j].complete != PORT_YES)
!         && (port_info[i].pq[j].complete != PORT_PARTIAL))
!       continue;
! 
!       /* Add separator, if this isn't the first entry. */
!       qualifier_number++;
!       if (qualifier_number > 1)
!       strcat (buffer, "; ");
!       
!       if (port_info[i].pq[j].complete == PORT_YES) {
!       strcat (buffer, port_info[i].pq[j].suffix_name);
!       } else if (port_info[i].pq[j].complete == PORT_PARTIAL) {
!       strcat (buffer, port_info[i].pq[j].suffix_name);
!       strcat (buffer, " (partial)");
!       }
! 
!       /* Attach any qualifier-specific portability notes. */
!       for (p = port_notes; p; p = p->next)
        {
!       if (   !strcmp (p->pi->prefix_token, port_info[i].prefix_token)
!           && (p->pq != NULL)
!           && !strcmp (p->pq->suffix_token,
port_info[i].pq[j].suffix_token))
        {
          char smallbuffer[20];
          p->number = note_number++;
*************** Node::write_portability()
*** 266,284 ****
          break;
        }
        }
-       strcat (buffer, ", ");
      }
-   }
  
!   {
!     char *ch = strchr (buffer, 0) - 2;
!     if (*ch == ',')
!       *ch = 0;
!     else
!       strcpy (buffer, "Unknown.");
    }
  
!   strcat (buffer, "\n\n");
    add(buffer);
  
    if (note_number > 1)
--- 459,525 ----
          break;
        }
        }
      }
  
!     /* Add negative qualifiers to the list. */
!     if (all_port_qualifiers == PORT_NO)
!       strcat (buffer, "No");
! 
!     for (j = 0; j < MAX_PORT_QUALIFIERS; j++) {
!       /* Skip unnamed suffixes */
!       if (port_info[i].pq[j].suffix_name == NULL)
!       continue;
! 
!       if (port_info[i].pq[j].complete != PORT_NO)
!       continue;
! 
!       /* If all port qualifiers == PORT_NO, then we've already output.
*/
!       if (all_port_qualifiers != PORT_NO) {
!       /* Add separator, if this isn't the first entry. */
!       qualifier_number++;
!       if (qualifier_number > 1)
!         strcat (buffer, "; ");
!       
!       strcat (buffer, "not ");
!       strcat (buffer, port_info[i].pq[j].suffix_name);
!       }
! 
!       /* Attach any qualifier-specific portability notes. */
!       for (p = port_notes; p; p = p->next)
!       {
!       if (   !strcmp (p->pi->prefix_token, port_info[i].prefix_token)
!           && (p->pq != NULL)
!           && !strcmp (p->pq->suffix_token,
port_info[i].pq[j].suffix_token))
!       {
!         char smallbuffer[20];
!         p->number = note_number++;
!         sprintf (smallbuffer, " (see note %d)", p->number);
!         strcat (buffer, smallbuffer);
!         break;
!       }
!       }
!     }
! 
!     /* Attach any target-specific portability notes. */
!     for (p = port_notes; p; p = p->next)
!     {
!       if (   (port_info[i].prefix_token != NULL)
!         && !strcmp (p->pi->prefix_token, port_info[i].prefix_token)
!         && (p->pq == NULL))
!       {
!       char smallbuffer[20];
!       p->number = note_number++;
!       sprintf (smallbuffer, " (see note %d)", p->number);
!       strcat (buffer, smallbuffer);
!       break;
!       }
!     }
! 
!     strcat (buffer, "\n");
    }
  
!   strcat (buffer, "@end multitable\n\n");
! 
    add(buffer);
  
    if (note_number > 1)
*************** Node::write_portability()
*** 288,294 ****
      add("\n");
      add("@enumerate\n");
  
!     for (int i = 1; i < note_number; i++)
      {
        add("@item\n");
        for (PortNote *p = port_notes; p; p = p->next)
--- 529,535 ----
      add("\n");
      add("@enumerate\n");
  
!     for (i = 1; i < note_number; i++)
      {
        add("@item\n");
        for (PortNote *p = port_notes; p; p = p->next)
*************** void scan_directory(char *which)
*** 495,501 ****
    Node *curnode;
    DIR *d = opendir(which);
    struct dirent *de;
!   while (de = readdir(d))
    {
      if (de->d_name[0] == '.')
        continue;
--- 736,742 ----
    Node *curnode;
    DIR *d = opendir(which);
    struct dirent *de;
!   while ((de = readdir(d)) != NULL)
    {
      if (de->d_name[0] == '.')
        continue;
*************** void scan_directory(char *which)
*** 560,572 ****
      }
    }
  }
 
//-----------------------------------------------------------------------------
  
! main(int argc, char **argv)
  {
!   if (argc < 3)
!   {
!     fprintf(stderr, "Usage: mkdoc <directory> <output file>\n");
      return 1;
    }
  
--- 801,878 ----
      }
    }
  }
+ 
 
//-----------------------------------------------------------------------------
  
! void list_portability (void)
  {
!   int i, j;
!   char buffer[40];
! 
!   printf("Built-in portability targets:\n");
! 
!   for (i = 0; i < NUM_PORT_TARGETS; i++) {    
!     if (port_target[i].pq[0].suffix_token  == NULL) {
!       printf("%-32s = %-32s\n",
!            port_target[i].prefix_token,
!            port_target[i].prefix_name);
!     } else {
!       for (j = 0; j < MAX_PORT_QUALIFIERS; j++) {
!       if (port_target[i].pq[j].suffix_token == NULL) break;
! 
!       strcpy(buffer, port_target[i].prefix_token);
!       strcat(buffer, "-");
!       strcat(buffer, port_target[i].pq[j].suffix_token);
! 
!       printf("%-32s = ", buffer);
! 
!       strcpy(buffer, port_target[i].prefix_name);
!       strcat(buffer, ": ");
!       strcat(buffer, port_target[i].pq[j].suffix_name);
! 
!       printf("%-32s\n", buffer);
!       }
!     }
!   }
! }
! 
!
//-----------------------------------------------------------------------------
! 
! void usage (void)
! {
!   fprintf(stderr,
!         "Usage: mkdoc [<switches>] <directory> <output file>\n"
!         "\n"
!         "Switches:\n"
!         "       -h, -?, --help      -  Display this help\n"
!         "       -l, --list-targets  -  List built-in portability
targets\n"
!         "\n");
! }
! 
!
//-----------------------------------------------------------------------------
! 
! int main (int argc, char **argv)
! {
!   int i;  
! 
!   // Scan for help options
!   for (i = 1; i < argc; i++) {
!     if (   !strcmp (argv[i], "-h")
!       || !strcmp (argv[i], "--help")
!       || !strcmp (argv[i], "-?") ) {
!       usage();
!       return 1;
!     }
! 
!     if (   !strcmp (argv[i], "-l")
!       || !strcmp (argv[i], "--list-targets") ) {
!       list_portability();
!       return 1;
!     }
!   }
! 
!   if (argc < 3) {
!     usage();
      return 1;
    }

- Raw text -


  webmaster     delorie software   privacy  
  Copyright © 2019   by DJ Delorie     Updated Jul 2019