From pst@Shockwave.COM  Tue Oct 31 07:45:49 1995
Received: from precipice.shockwave.com (precipice.shockwave.com [171.69.108.33])
          by freefall.freebsd.org (8.6.12/8.6.6) with ESMTP id HAA25743
          for <FreeBSD-gnats-submit@freebsd.org>; Tue, 31 Oct 1995 07:45:46 -0800
Received: (from pst@localhost) by precipice.shockwave.com (8.6.12/8.6.12) id HAA17304; Tue, 31 Oct 1995 07:45:05 -0800
Message-Id: <199510311545.HAA17304@precipice.shockwave.com>
Date: Tue, 31 Oct 1995 07:45:05 -0800
From: Paul Traina <pst@Shockwave.COM>
Reply-To: pst@Shockwave.COM
To: FreeBSD-gnats-submit@freebsd.org
Subject: bsd m4 chokes on config script
X-Send-Pr-Version: 3.2

>Number:         803
>Category:       bin
>Synopsis:       bsd m4 chokes and dies while FSF m4 works...
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          closed
>Quarter:
>Keywords:
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Tue Oct 31 07:50:01 PST 1995
>Closed-Date:    Mon Aug 18 14:13:51 PDT 1997
>Last-Modified:  Mon Aug 18 14:14:33 PDT 1997
>Originator:     Paul Traina
>Release:        FreeBSD 2.1-STABLE i386
>Organization:
Shockwave Engineering
>Environment:

2.1-stable as of this week

>Description:

I was porting over the new gnats-3.97 beta from Cygnus.  When I went to
build gnats, m4 chocked and died.  Input files and sample output are below.
I pulled down FSF m4 and it worked just fine.  As you can see from the output
of m4, it's insanely confused at this point.

I checked NetBSD's and Lite-2's m4 distributions to see if there were any
fixes that we failed to pick up, and we are at least current with those
distributions.

>How-To-Repeat:

/usr/bin/m4 config.m4 config.c.in

output from broken m4 (edited down)
--------------
char *gnats_root = NULL;

 char[] char[] <static char _gnats_addr GNATS_ADDR STR_MAX[] = ;
char *gnats_addr GNATS_ADDR STR_MAX = _gnats_addr GNATS_ADDR STR_MAX;> = ; char[] char[] <static char _gnats_addr GNATS_ADDR STR_MAX[] = ;
char *gnats_addr GNATS_ADDR STR_MAX = _gnats_addr GNATS_ADDR STR_MAX;>;
)>
, , char[], dnl
static char _gnats_server GNATS_SERVER char[] STR_MAX[] = ;
char *gnats_server GNATS_SERVER char[] STR_MAX = _gnats_server GNATS_SERVER char[] STR_MAX;dnl
define(>,   { ""^ _gnats_server GNATS_SERVER char[] STR_MAX }^
)
m4: missing right quote

output from FSF m4 (edited down to cover same range...)
---------------
char *gnats_root = NULL;

static char _gnats_addr[STR_MAX] = GNATS_ADDR;
char *gnats_addr = _gnats_addr;
static char _gnats_server[STR_MAX] = GNATS_SERVER;


config.m4:
--------------
dnl
dnl	Macros for generating declarations in sh/C configuration
dnl
changequote(<,>)dnl
define(<CN_CNT>, 0)dnl	# of decls processed
define(<CN_INIT>,)dnl	initializer list contents
define(<CN_KNR>,)dnl	K&R initializers
dnl
dnl Generate the ANSI initializer list
define(<CN_ANSI>,<changequote([,])translit(CN_INIT,^,[,])changequote(,)>)dnl
dnl
dnl Generate the decl and initializers for an integral option
define(<CN_DOINT>,<<dnl
int $1 = $2;dnl
define(<CN_INIT>, CN_INIT  { "$2"^ (char *) &$1 }^
)dnl
define(<CN_KNR>, CN_KNR  confs[CN_CNT].key = "$2"; dnl
confs[CN_CNT].val = (char *) &$1;
)>>)dnl
dnl
dnl Generate the decl and initializers for a string option
define(<CN_DOCHARA>,<<dnl
static char _$1[$3] = $2;
char *$1 = _$1;dnl
define(<CN_INIT>, CN_INIT  { "$2"^ _$1 }^
)dnl
define(<CN_KNR>, CN_KNR  confs[CN_CNT].key = "$2"; confs[CN_CNT].val = $1;
)>>)dnl
dnl
dnl Generate the decl and initializers for an option
define(<CN_OPT>,<dnl
changequote(<,>)dnl
ifelse($3, int, CN_DOINT($1, $2), $3, char[], CN_DOCHARA($1, $2, $4))dnl
define(<CN_CNT>, incr(CN_CNT))dnl
changequote(,)dnl
>Fix:
>Release-Note:
>Audit-Trail:
State-Changed-From-To: open->feedback 
State-Changed-By: scrappy 
State-Changed-When: Mon Oct 21 23:12:50 PDT 1996 
State-Changed-Why:  

Dust off the cobwebs - Confirm Status 
State-Changed-From-To: feedback->open 
State-Changed-By: scrappy 
State-Changed-When: Mon Oct 21 23:13:12 PDT 1996 
State-Changed-Why:  

Problem Still Exists 
State-Changed-From-To: open->closed 
State-Changed-By: jlemon 
State-Changed-When: Mon Aug 18 14:13:51 PDT 1997 
State-Changed-Why:  

Fixed in eval.c, rev 1.8 
>Unformatted:
 >)dnl
 dnl
 changequote(,)dnl
 
 
 config.c.in
 --------------------
 /* Configuration processing routines for GNATS -*- C -*-
    Copyright (C) 1993, 1994, 1995 Free Software Foundation, Inc.
 
 This file is part of GNU GNATS.
 
 GNU GNATS is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2, or (at your option)
 any later version.
 
 GNU GNATS is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with GNU GNATS; see the file COPYING.  If not, write to
 the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 
 /* The basic idea here is to be able to use the same configuration file for
    sh scripts and C programs; the sh script just uses `.', but the C program
    has to be a bit more trickier.  And a lot uglier.  */
 
 /* FIXME, maybe: Use hash table for more option-ridden programs.  */
 
 #include "config.h"
 #include "gnats.h"
 #include "pathmax.h"
 
 char *gnats_root = NULL;
 
 CN_OPT(gnats_addr, GNATS_ADDR, char[], STR_MAX)
 CN_OPT(gnats_server, GNATS_SERVER, char[], STR_MAX)
 CN_OPT(gnats_server_port, GNATS_SERVER_PORT, int)
 CN_OPT(gnats_service, GNATS_SERVICE, char[], STR_MAX)
 CN_OPT(gnats_user, GNATS_USER, char[], STR_MAX)
 CN_OPT(gnats_admin, GNATS_ADMIN, char[], STR_MAX)
 CN_OPT(def_subm, DEFAULT_SUBMITTER, char[], STR_MAX)
 CN_OPT(def_release, DEFAULT_RELEASE, char[], STR_MAX)
 CN_OPT(flag_notify, NOTIFY, int)
 CN_OPT(flag_ack, ACKNOWLEDGE, int)
 CN_OPT(keep_rec, KEEP_RECEIVED_HEADERS, int)
 CN_OPT(debug_mode, DEBUG_MODE, int)
 CN_OPT(bday_start, BDAY_START, int)
 CN_OPT(bday_end, BDAY_END, int)
 CN_OPT(bweek_start, BWEEK_START, int)
 CN_OPT(bweek_end, BWEEK_END, int)
 CN_OPT(mail_agent, MAIL_AGENT, char[], STR_MAX)
 CN_OPT(bindir, BINDIR, char[], STR_MAX)
 CN_OPT(queue_dir, QUEUE_DIR, char[], STR_MAX)
 
 struct conf
 {
   char* key;
   char* val;
 };
 
 #ifdef __STDC__
 #define STDCINIT
 #endif
    
 #ifdef STDCINIT
 static struct conf confs[] = {
 CN_ANSI};
 #else
 static struct conf confs[CN_CNT];
 #endif
 
 static char *fname = NULL;
 static char *string_extract_double_quoted ();
 static char *safe_strtok ();
 
 /* Read in and set the above configuration parameters */
 void
 configure()
 {
   FILE *fp;
   char line[STR_MAX];
 
 #ifndef STDCINIT
 CN_KNR#endif
   
   if (! gnats_root)
     {
       gnats_root = getenv ("GNATS_ROOT");
       if (! gnats_root)
 	gnats_root = X_GNATS_ROOT;
     }
   
   if (fname)
     xfree (fname);
   
 #define CONFSTR "%s/gnats-adm/config"
   fname = xmalloc (strlen (gnats_root) + sizeof (CONFSTR) - 1);
   sprintf (fname, CONFSTR, gnats_root);
   
   fp = fopen (fname, "r");
   
   if (fp == NULL)		/* no config file; no problem */
     return;
   
   while (fgets (line, STR_MAX, fp) != NULL)
     {
       int i;
       if (*line == '#') continue; /* comment */
       for (i=0; i < sizeof(confs)/sizeof(struct conf); i++) {
 	int n = strlen(confs[i].key);
 	if (! strncmp(confs[i].key, line, n) && line[n] == '=') {
 	  char *p = line+n+1;
 	  int d;
 	  if (sscanf (p, "%d", &d) > 0)	/* NOTIFY=1 */
 	    *((int *) confs[i].val) = d;
 	  else			/* string parameter */
 	    {
 	      /* Mimic somewhat the sh interpretation of the line.  */
 	      if (*p == '\'')
 		strcpy (confs[i].val, safe_strtok(p+1, "'"));
 	      else if (*p == '"')
 		strcpy (confs[i].val, string_extract_double_quoted(p+1));
 	      else strcpy (confs[i].val, safe_strtok(p, " \t\n"));
 	    }
 	  break;
 	}
       }
     }
 }
 
 static char *
 safe_strtok (s, d)
      char *s, *d;
 {
   char *r = strtok (s, d);
   if (!r && *s != '\n')
     r = strtok (s, "\n");
   if (!r)
     r = "";
   return r;
 }
 
 /* Lifted largely from bash/subst.c */
 static char *
 string_extract_double_quoted (string)
      char *string;
 {
   register int c, j, i;		/* character, temp pos, str pos */
   char *temp;			/* The new string we return. */
   int pass_next;		/* State variables for the machine. */
 
   pass_next = 0;
   temp = (char *)xmalloc (strlen (string) + 1);
 
   for (j = 0, i = 0; (c = string[i]) != '\0'; i++)
     {
       /* Process a character that was quoted by a backslash. */
       if (pass_next)
 	{
 	  /* Posix.2 sez:
 
 	     ``The backslash shall retain its special meaning as an escape
 	     character only when followed by one of the characters:
 	     	$	`	"	\	<newline>''.  */
 	  if (c != '$' && c != '`' && c != '"' && c != '\\' && c != '\n')
 	    temp[j++] = '\\';
           temp[j++] = c;
           pass_next = 0;
           continue;
 	}
 
       /* A backslash protects the next character.  The code just above
          handles preserving the backslash in front of any character but
          a double quote. */
       if (c == '\\')
 	{
 	  pass_next++;
 	  continue;
 	}
 
       /* An unescaped double quote serves to terminate the string. */
       if (c == '"')
         break;
 
       /* Add the character to the quoted string we're accumulating. */
       temp[j++] = c;
     }
   temp[j] = '\0';
   strcpy (string, temp);
   xfree (temp);
   return (string);
 }
 
