Index: gnats-3.104-beta/gnats/Makefile.in
diff -c gnats-3.104-beta/gnats/Makefile.in:1.1.1.1 gnats-3.104-beta/gnats/Makefile.in:1.7
*** gnats-3.104-beta/gnats/Makefile.in:1.1.1.1	Thu Oct  9 21:37:29 1997
--- gnats-3.104-beta/gnats/Makefile.in	Thu Oct 23 13:24:14 1997
***************
*** 30,35 ****
--- 30,41 ----
  # default: bugs@`hostname`
  GNATS_ADDR = @GNATS_ADDR@
  
+ #cvs location
+ CVS = @CVS@
+ 
+ #cvs root
+ CVS_ROOT = @CVS_ROOT@
+ 
  # The name of your site, to be used by send-pr to find your
  # category list.
  # default: The second-to-last part of your hostname.
***************
*** 189,196 ****
  
  #### Host-, target-, and site-specific makefiles are inserted here.
  
! SOURCES = btime.c main.c file-pr.c resp-lookup.c
! OBJECTS = btime.o main.o file-pr.o resp-lookup.o regex.o
  
  EXTRA_OBJS = @EXTRA_OBJS@
  
--- 195,202 ----
  
  #### Host-, target-, and site-specific makefiles are inserted here.
  
! SOURCES = btime.c main.c file-pr.c resp-lookup.c billing.c
! OBJECTS = btime.o main.o file-pr.o resp-lookup.o regex.o billing.o
  
  EXTRA_OBJS = @EXTRA_OBJS@
  
***************
*** 198,204 ****
  LIBOBJS = edit.o files.o getdate.o headers.o internal.o misc.o pr.o xmalloc.o index.o lists.o query.o qvariable.o config.o version.o $(EXTRA_OBJS)
  
  DISTFILES= $(SOURCES) COPYING ChangeLog INSTALL Makefile.in README \
! categories responsible submitters regex.c regex.h \
  at-pr.sh config.h configure configure.in error.c files.h getdate.y \
  getdate.c globals.h headers.h index.c mkcat.sh pathmax.h pr.h getclose.c \
  pr-addr.c pr-age.c pr-mail.c pr-stat.c gnats-dirs.h gnats-el.in gnats.h \
--- 204,211 ----
  LIBOBJS = edit.o files.o getdate.o headers.o internal.o misc.o pr.o xmalloc.o index.o lists.o query.o qvariable.o config.o version.o $(EXTRA_OBJS)
  
  DISTFILES= $(SOURCES) COPYING ChangeLog INSTALL Makefile.in README \
! categories responsible submitters accounts products supercategories \
! regex.c regex.h \
  at-pr.sh config.h configure configure.in error.c files.h getdate.y \
  getdate.c globals.h headers.h index.c mkcat.sh pathmax.h pr.h getclose.c \
  pr-addr.c pr-age.c pr-mail.c pr-stat.c gnats-dirs.h gnats-el.in gnats.h \
***************
*** 260,267 ****
  	mkcat rmcat mkdist config gnats-man-pages $(EXTRA_STUFF)
  
  all-tools: libgnats.a query-pr nquery-pr pr-addr pr-age pr-edit npr-edit \
! 	pr-mail sub-type edit-pr nedit-pr getclose \
!         gnats.elc config-send-pr tools-man-pages
  
  libgnats.a: $(LIBOBJS)
  	-rm -f tmplibgnats.a libgnats.a
--- 267,274 ----
  	mkcat rmcat mkdist config gnats-man-pages $(EXTRA_STUFF)
  
  all-tools: libgnats.a query-pr nquery-pr pr-addr pr-age pr-edit npr-edit \
! 	pr-mail sub-type edit-pr rating-sum send-patch check-cvs-tag \
! 	nedit-pr getclose gnats.elc config-send-pr tools-man-pages
  
  libgnats.a: $(LIBOBJS)
  	-rm -f tmplibgnats.a libgnats.a
***************
*** 300,305 ****
--- 307,314 ----
  	@echo "BWEEK_START=$(BWEEK_START)" >> $@-t
  	@echo "BWEEK_END=$(BWEEK_END)" >> $@-t
  	@echo "DEBUG_MODE=$(DEBUG_MODE)" >> $@-t
+ 	@echo "CVS=$(CVS)" >> $@-t
+ 	@echo "CVS_ROOT=$(CVS_ROOT)" >> $@-t
  	@mv $@-t $@
  
  version.c: Makefile
***************
*** 401,406 ****
--- 410,441 ----
  	@mv $@-t $@
  	@chmod a+x $@
  
+ rating-sum: rating-sum.sh Makefile
+ 	@echo Creating rating-sum...
+ 	@sed -e 's,xGNATS_ROOTx,$(GNATS_ROOT),g' \
+ 	    -e 's,xVERSIONx,$(VERSION),g' $(srcdir)/rating-sum.sh > $@-t
+ 	@mv $@-t $@
+ 	@chmod a+x $@
+ 
+ send-patch: send-patch.sh Makefile
+ 	@echo Creating send-patch...
+ 	@sed -e 's,xCVS_ROOTx,$(CVS_ROOT),g' \
+ 	    -e 's,xGNATS_ROOT,$(GNATS_ROOT),g'\
+ 	    -e 's,xCVSx,$(CVS),g'\
+ 	    -e 's,xGNATS_ADDRx,$(GNATS_ADDR),g'\
+ 	    -e 's,xMAIL_AGENTx,$(MAIL_AGENT),g' \
+ 	    -e 's,xVERSIONx,$(VERSION),g' $(srcdir)/send-patch.sh > $@-t
+ 	@mv $@-t $@
+ 	@chmod a+x $@
+ 
+ check-cvs-tag: check-cvs-tag.sh Makefile
+ 	@echo Creating check-cvs-tag...
+ 	@sed -e 's,xCVS_ROOTx,$(CVS_ROOT),g' \
+ 	    -e 's,xCVSx,$(CVS),g'\
+ 	    -e 's,xVERSIONx,$(VERSION),g' $(srcdir)/check-cvs-tag.sh > $@-t
+ 	@mv $@-t $@
+ 	@chmod a+x $@
+ 
  nedit-pr: nedit-pr.sh Makefile
  	@echo Creating nedit-pr...
  	@sed -e 's,xGNATS_ROOTx,$(GNATS_ROOT),g' \
***************
*** 468,473 ****
--- 503,511 ----
  	$(INSTALL_PROGRAM) query-pr $(bindir)/query-pr
  	$(INSTALL_PROGRAM) nquery-pr $(bindir)/nquery-pr
  	$(INSTALL_PROGRAM) edit-pr $(bindir)/edit-pr
+ 	$(INSTALL_PROGRAM) rating-sum $(bindir)/rating-sum
+ 	$(INSTALL_PROGRAM) send-patch $(libexecdir)/gnats/send-patch
+ 	$(INSTALL_PROGRAM) check-cvs-tag $(libexecdir)/gnats/check-cvs-tag
  	$(INSTALL_PROGRAM) nedit-pr $(bindir)/nedit-pr
  	$(INSTALL_PROGRAM) getclose $(bindir)/getclose
  	$(INSTALL_PROGRAM) pr-age $(libexecdir)/gnats/pr-age
***************
*** 514,519 ****
--- 552,563 ----
  		$(GNATS_ROOT)/gnats-adm/responsible ; \
  	   $(INSTALL_DATA) -o $(GNATS_USER) $(srcdir)/submitters \
  		$(GNATS_ROOT)/gnats-adm/submitters ; \
+ 	   $(INSTALL_DATA) -o $(GNATS_USER) $(srcdir)/accounts \
+ 		$(GNATS_ROOT)/gnats-adm/accounts ; \
+ 	   $(INSTALL_DATA) -o $(GNATS_USER) $(srcdir)/products \
+ 		$(GNATS_ROOT)/gnats-adm/products ; \
+ 	   $(INSTALL_DATA) -o $(GNATS_USER) $(srcdir)/supercategories \
+ 		$(GNATS_ROOT)/gnats-adm/supercategories ; \
  	   $(INSTALL_DATA) -o $(GNATS_USER) $(srcdir)/gnatsd.conf \
  		$(GNATS_ROOT)/gnats-adm/gnatsd.conf ; \
  	fi
***************
*** 561,566 ****
--- 605,613 ----
  # Don't use rm -rf in case there's other stuff in the directories
  uninstall:
  	-rm -f $(bindir)/edit-pr
+ 	-rm -f $(bindir)/rating-sum
+ 	-rm -f $(libexecdir)/gnats/send-patch
+ 	-rm -f $(libexecdir)/gnats/check-cvs-tag
  	-rm -f $(bindir)/nedit-pr
  	-rm -f $(bindir)/query-pr
  	-rm -f $(bindir)/sub-type
***************
*** 627,632 ****
--- 674,680 ----
  	-rm -f file-pr mkdist mail-query
  	-rm -f libgnats.a
  	-rm -f pr-addr pr-age pr-mail pr-stat query-pr pr-edit edit-pr gen-index
+ 	-rm -f rating-sum
  	-rm -f nquery-pr gnatsd sub-type npr-edit nedit-pr getclose
  	-rm -f *.dvi version.c
  clean-man:
***************
*** 754,756 ****
--- 802,807 ----
  npr-edit.o: $(srcdir)/headers.h $(srcdir)/pr.h
  edit.o: $(srcdir)/config.h $(srcdir)/gnats.h $(srcdir)/gnatsd.h
  edit.o: $(srcdir)/headers.h $(srcdir)/pr.h
+ billing.o: $(srcdir)/config.h $(srcdir)/gnats.h $(srcdir)/headers.h
+ billing.o: $(srcdir)/pr.h $(srcdir)/files.h $(srcdir)/globals.h
+ billing.o: $(srcdir)/pathmax.h $(srcdir)/regex.h $(srcdir)/billing.h
Index: gnats-3.104-beta/gnats/accounts
diff -c /dev/null gnats-3.104-beta/gnats/accounts:1.1
*** /dev/null	Thu Oct 23 13:27:07 1997
--- gnats-3.104-beta/gnats/accounts	Mon Oct 20 15:17:36 1997
***************
*** 0 ****
--- 1,19 ----
+ #
+ #		    accounts database for GNATS
+ #
+ # Any line which begins with a `#' is considered a comment, and GNATS
+ # will ignore it. 
+ #
+ # Each entry has the format:
+ # 
+ #	submitter-id:account-id:response-time:list-of-products
+ #
+ #	* submitter-id: submitter e-mail address
+ #	* account-id: unique identifier of the account
+ #	* response time: If configured with `NOTIFY' set to `TRUE', GNATS
+ #	  will use this field to schedule when at_pr should notify the
+ #	  gnats-admin and gnats-manager that the PR wasn't analyzed
+ #	  within the agreed response time.
+ #       * list-of-products: is "|" - separated list of products
+ #         assigned to the account
+ test:test_account:24:test
Index: gnats-3.104-beta/gnats/billing.c
diff -c /dev/null gnats-3.104-beta/gnats/billing.c:1.2
*** /dev/null	Thu Oct 23 13:27:07 1997
--- gnats-3.104-beta/gnats/billing.c	Mon Oct 20 14:53:04 1997
***************
*** 0 ****
--- 1,415 ----
+ /* Billing info implementation file.
+    Contributed by Sergey Vanskov (vanskov@aha.ru).
+ 
+ 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, 59 Temple Place - Suite 330, Boston, MA 02111, USA. 
+ */
+ #include "config.h"
+ #include "gnats.h"
+ #include "billing.h"
+ 
+ #include <des_crypt.h>
+ #undef __DEB__
+ #ifdef __DEB__
+ #define  log(smth)	\
+ {FILE* f = fopen ("/tmp/file-pr.log", "a"); if(f){ fprintf smth; fclose(f);}}
+ #else
+ #define	log(smth)
+ #endif
+ 
+ /* looks through ">Billing-Info:" field
+ *  and extracts to *billing any
+ *  known billing information
+ *  return value:
+ *	0 - empty field (may be some blanks)
+ *	1 - billing information in a known format is found
+ *	2 - evident deviations from known formats
+ *	3 - something is found however it does
+ *	    not completely match to known formats
+ */
+ int	parse_billing_info(billing)
+ 	Billing	*billing;
+ {
+   int	return_value;
+   char	*billing_str;
+   char	*str1, *str2;
+   memset (billing, '\0', sizeof (Billing));
+   return_value = 0;
+ 
+   billing_str = strdup (field_value (BILLING_INFO));
+   log ((f, "billing info: %s.\n", billing_str))
+ 
+   str1 = strtok (billing_str, " \t\n");
+   while (str1 != NULL)
+     {
+       if (!strcasecmp (str1, CREDIT_CARD_SIGN))
+ 	{
+ 	  int	i;
+ 	  char	ccn[20];
+ 
+ 	  ccn[0] = '\0';
+ 	  /* credit card number has XXXX XXXX XXXX XXXX format */
+ 	  for (i = 0; i < 4; i++)
+ 	    {
+ 	      str1 = strtok (NULL, " \t\n");
+ 	      if (str1 != NULL && strlen (str1) == 4)
+ 		if (ccn[0] == '\0')
+ 		   strcpy (&ccn[0], str1);
+ 		else
+ 		   strcat (strcat (&ccn[0], " "), str1);
+ 	      else
+ 		break;
+ 	    }
+ 	  if (i == 4)
+ 	    {
+ 	      if (billing->credit_card == NULL)
+ 		{
+ 	          billing->credit_card = strdup (&ccn[0]);
+ 	          return_value = 1;
+ 		  log ((f, "credit card is found.\n"))
+ 		}
+ 	    }
+ 	  else
+ 	    return_value = return_value == 1 || return_value == 3?
+ 		return_value:
+ 		2;
+         }
+       else if (!strcasecmp (str1, ACCOUNT_ID_SIGN))
+ 	{
+ 	  str1 = strtok (NULL, " \t\n");
+ 	  if (str1 != NULL)
+ 	    {
+ 	      billing->account_id = strdup (str1);
+ 	      return_value = 1;
+ 	    }
+ 	  else
+ 	    return_value = return_value == 1 || return_value == 3?
+ 		return_value:
+ 		2;
+ 	}
+       else if (!strcasecmp (str1, PR_SIGN))
+ 	{
+ 	  int	pr;
+  	  str1 = strtok (NULL, " \t\n");
+ 	  if (str1 != NULL && (pr = atoi (str1)) != 0)
+ 	    {
+ 	      if (billing->problem_report == 0)
+ 		{
+ 		  billing->problem_report = pr;
+ /*
+ 		  return_value = 1;
+ */
+ 		}
+ 	    }
+ /*
+ 	  else
+ 	    return_value = return_value == 1 || return_value == 3?
+ 		return_value:
+ 		2;
+ */
+ 	}
+       else
+ 	/* do not pay attention if smth. is wrong */
+ 	return_value = return_value == 1? return_value: 3;
+ 
+       if (str1 = 0)
+ 	break;
+       str1 = strtok (NULL, " \t\n");
+     }
+ 
+   free (billing_str);
+   return return_value;
+ }
+ /* checks whether the category is related to the account
+ *  return value:
+ *	0 - No
+ *	1 - Yes
+ */
+ static	int	is_category_present(account, category)
+ 	Account*	account;
+ 	char*		category;
+ {
+   char	*str1;
+   char	*categories;
+   log ((f, "Checking category %s in %s\n", category, account->products));
+   if (category == NULL || *category == '\0')
+     return 0;
+   categories = strdup (account->products);
+ 
+   str1 = strtok (categories, " |\t\n");
+   while (str1)
+     {
+       if (!strcmp(str1, category))
+ 	break;
+       str1 = strtok (NULL, " |\t\n");
+     }
+   free (categories);
+   return str1? 1: 0;
+ }
+ /* checks billing information
+ *  outputs authorization information and
+ *  billing status (Ok, Not Ok,...)
+ *  return value:
+ *	1 - Ok (billing info is valid)
+ *	2 - Not Ok (billing info is not valid)
+ */
+ int	do_authorization(submitter, category, billing, authorization)
+ 	char		*submitter;
+ 	char		*category;
+ 	Billing		*billing;
+ 	Authorization	*authorization;
+ {
+   int	billing_status;
+   billing_status = 2;
+   memset (authorization, 0, sizeof (Authorization));
+   log ((f, "authorization fo %s in %s\n", submitter, category));
+ 
+   if (billing == NULL)
+     return 2;
+ 
+   /* check account first */
+   if (billing->account_id != NULL && *billing->account_id != '\0' &&
+       submitter != NULL && category != NULL){
+       Account	account;
+       log ((f, "account-id is being checked\n"))
+       if (find_account (&account, submitter) >= 0){
+ 	  log ((f, "Account is found\n"));
+ 	  expand_products (&account.products, 1);
+ 	  log ((f, "Categories are expanded\n"));
+ 	  log ((f, "supplied id: %s real id: %s\n", billing->account_id, account.id));
+ 	  if (!strcmp (billing->account_id, account.id) &&
+ 	      is_category_present (&account, category)){
+ 	      billing_status = 1;
+ 	      authorization->response_time = account.rtime;
+ 	      log ((f, "account is Ok\n"));
+ 	  }else{
+ 	      log ((f, "Invalid account ID\n"));
+ 	  }
+       }else{
+ 	  log ((f, "account not found\n"));
+       }
+   }
+   if (billing_status == 2 && billing->credit_card != NULL &&
+       billing->credit_card != '\0'){
+       char	*tmp_file_name;
+       log ((f, "checking credit card\n"));
+       tmp_file_name = tmpnam(NULL);
+       if (tmp_file_name == NULL){
+ 	  log ((f, "tmpnam failed\n"));
+ 	  punt(0, "Could not open output pipe for auth-pr.");
+ 	  log_msg (LOG_INFO, 0, "tmpnam failed:");
+ 	  billing_status = 3;
+       }else{
+           log ((f, "creating temp pipe\n"));
+ 	  if (mknod (tmp_file_name, S_IFIFO | 0666, 0) < 0){
+ 	      log ((f, "mknod failed\n"));
+ 	      punt(0, "Could not open output pipe for auth-pr.");
+ 	      log_msg (LOG_INFO, 0, "mknod failed:");
+ 	      billing_status = 3;
+ 	  }else{
+ 	      int	in;
+               log ((f, "opening temp pipe\n"));
+ 	      if ((in = open (tmp_file_name, O_RDONLY | O_NONBLOCK)) < 0){
+ 		  log ((f, "open input pipe failed\n"));
+ 	          punt(0, "Could not open output pipe for auth-pr.");
+ 	          log_msg (LOG_INFO, 0, "open failed:");
+ 		  billing_status = 3;
+ 	      }else{
+ 		  FILE	*p;
+                   char	*command;
+                   command = (char*) malloc (strlen (bindir) + 10 +
+ 		        strlen (tmp_file_name));
+ 	          strcpy (command, bindir); strcat (command, "/auth-pr");
+ 		  log ((f, "access to %s is %d\n",
+ 		    command, access(command, X_OK)));
+ 		  strcat (command, " "); strcat (command, tmp_file_name);
+ 		  log ((f, "executing %s\n", command));
+ 		  p = popen (command, "w");
+ 		  if (p == (FILE*)NULL){
+ 		      log ((f, "cannot execute auth-pr\n"));
+ 	              punt(0, "Could not open input pipe for auth-pr.");
+ 	              log_msg (LOG_INFO, 0, "popen failed:");
+ 		      billing_status = 3;
+ 		  }else{
+ 		      int	i;
+ 		      int	sread;
+ 		      char	auth[2049];
+ 		      log ((f, "%s\n", billing->credit_card));
+ 		      fprintf (p, "%s\n", billing->credit_card);
+ 		      pclose (p);
+ 		      log ((f, "reading from auth-pr\n"));
+ 		      for (i = 0; i < MAX_AUTH_ATTEMPTS; i++){
+ 		          sread = read (in, auth, 2048);
+ 			  if (sread == 0){
+ 			      log ((f, "data not ready %d sread %d\n", i+1, sread));
+ 			      sleep (AUTH_ATTEMPT_WAIT_SLICE);
+ 			      continue;
+ 			  }else{
+ 			      log ((f, " error read %d ret %d errno %d\n", i+1, sread, errno));
+ 			      break;
+ 			  }
+ 			  
+ 		      }
+ 		      if (i == MAX_AUTH_ATTEMPTS){
+ 			  log ((f, "timeout\n"));
+ 		      }
+ 		      if (sread == -1 || sread == 0){
+ 			  log ((f, "could not read input from auth-pr\n"));
+ 	                  punt(0, "Could not read input from auth-pr.");
+ 			  billing_status = 3;
+ 		      }else{
+ 			  char	*str1;
+ 			  auth[sread] = '\0';
+ 			  log ((f, "read %s\n", auth));
+ 			  str1 = strtok (auth, ":\n");
+ 			  if (str1 != NULL){
+ 			      log ((f, "input is valid\n"));
+ 			      authorization->response_time = atoi (str1);
+ 			      if ((str1 = strtok (NULL, "\n")) != NULL){
+ 			          authorization->transaction_number =
+ 				    strdup (str1);
+ 				  log ((f, "Ok %s\n", authorization->transaction_number));
+ 			          billing_status = 1;
+ 			      }else{
+ 				  authorization->transaction_number = NULL;
+ 				  log ((f, "Not Ok\n"));
+ 				  billing_status = 2;
+ 			      }
+ 			  }else{
+ 			      log ((f, "input is invalid\n"));
+ 	                      punt(0, "Invalid input from auth-pr.");
+ 			      billing_status = 3;
+ 			  }
+ 		      }
+ 		      log ((f, "closing pipe\n"));
+ 		  }
+ 		  free (command);
+ 		  close (in);
+ 	      }
+ 	      unlink (tmp_file_name);
+ 	  }
+       }
+   }
+   return billing_status;
+ }
+ /* adds a new record to billing info file.
+ *
+ *  Note: billing information and authorization
+ *  information is truncated to 256 bytes
+ *  if the value will turn out to be small
+ *  increase it and start new billing file
+ */
+ #define	BILL_LEN	256
+ void	add_billing_info(pr_number, billing, authorization)
+ 	int		pr_number;
+ 	Billing		*billing;
+ 	Authorization	*authorization;
+ {
+   char	filename[PATH_MAX];
+   FILE	*bill_file;
+   sprintf(filename, "%s/gnats-adm/%s", gnats_root, BILLING);
+ 
+   bill_file = fopen (filename, "a");
+   if (bill_file == NULL)
+     {
+       punt (0, "Could not open \"billing\" file.");
+     }
+   else
+     {
+       int	err;
+       char	bill[BILL_LEN];
+ 
+       static	char	key[] = PASSWORD;
+ 
+       memset (bill, 0, BILL_LEN);
+       sprintf (bill, "%d:", pr_number);
+       if (billing->account_id != NULL && *billing->account_id != '\0')
+ 	strcat (strcat (bill, " ACCOUNT_ID "), billing->account_id);
+ 
+       if (billing->credit_card != NULL && *billing->credit_card != '\0')
+ 	strcat (strcat (bill, " VISA "), billing->credit_card);
+ 
+       if (billing->problem_report)
+ 	{
+ 	  char	buf[32];
+ 	  sprintf (buf, " PR %d", billing->problem_report);
+ 	  strcat (bill, buf);
+ 	}
+       strcat (bill, ":");
+ 
+       if (authorization->response_time > 0)
+ 	{
+ 	  char	buf[32];
+ 	  sprintf (buf, "%d|", authorization->response_time);
+ 	  strcat (bill, buf);
+ 	}
+ 
+       if (authorization->transaction_number != NULL &&
+ 	 *authorization->transaction_number != '\0')
+ 	strcat (bill, authorization->transaction_number);
+ 
+       des_setparity (key);
+       err = ecb_crypt (key, bill, BILL_LEN, DES_ENCRYPT | DES_SW);
+       if (err != DESERR_NONE)
+ 	{
+ 	  punt (0, "Encryption error.");
+ 	}
+       err = fwrite (bill, BILL_LEN, 1, bill_file);
+       if (err != 1)
+ 	{
+ 	  punt (0, "Could not write billing information.");
+ 	}
+       fclose (bill_file);
+     }
+ }
+ #undef	BILL_LEN
+ /* Substitutes billing status for
+ *  billing information.
+ */
+ void	set_billing_field (billing_status)
+ 	int	billing_status;
+ {
+   char	*str;
+ 
+   switch (billing_status)
+     {
+       case 1: str = ACCEPTED; break;
+       case 2: str = REJECTED; break;
+       case 3: str = DEFERRED; break;
+       default: str = NONE; break;
+     }
+   set_field (BILLING_INFO, str);
+ }
+ /* Release memory occupated by the struct.
+ */
+ void	free_billing(billing)
+ 	Billing	*billing;
+ {
+   if (billing == NULL)
+     return;
+   if (billing->credit_card != NULL)
+     xfree (billing->credit_card);
+   if (billing->account_id != NULL)
+     xfree (billing->account_id);
+ }
+ void	free_authorization(authorization)
+ 	Authorization	*authorization;
+ {
+   if (authorization == NULL)
+     return;
+   if (authorization->transaction_number != NULL)
+     xfree (authorization->transaction_number);
+ }
Index: gnats-3.104-beta/gnats/billing.h
diff -c /dev/null gnats-3.104-beta/gnats/billing.h:1.1
*** /dev/null	Thu Oct 23 13:27:07 1997
--- gnats-3.104-beta/gnats/billing.h	Sun Oct 19 18:15:07 1997
***************
*** 0 ****
--- 1,82 ----
+ /* Billing info header fields.
+    Contributed by Sergey Vanskov (vanskov@aha.ru).
+ 
+ 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, 59 Temple Place - Suite 330, Boston, MA 02111, USA. 
+ */
+ 
+ #ifndef __BILLING__HEADER__
+ #define __BILLING__HEADER__
+ 
+ typedef struct billing_entry {
+   /* credit card number */
+   char *credit_card;
+ 
+   /* account-id; see in "accounts" */
+   char *account_id;
+ 
+   /* problem report number */
+   int	problem_report;
+ } Billing;
+ 
+ typedef struct authorization_entry {
+   /* transaction number of the charge operation */
+   char *transaction_number;
+ 
+   /* response time (if it is defined
+   *  for the authorization information)
+   */
+   int response_time;
+ } Authorization;
+ 
+ typedef struct billing_info_entry {
+   /* problem report number */
+   int problem_report;
+ 
+   /* billing */
+   Billing billing;
+ 
+   /* authorization */
+   Authorization	authorization;
+ } Billing_Info;
+ 
+ extern	int	parse_billing_info	PARAMS((Billing*));
+ extern	int	do_authorization	PARAMS(
+ 		(char*, char*, Billing*, Authorization*));
+ extern	void	set_billing_field	PARAMS((int));
+ extern	void	add_billing_info	PARAMS((int, Billing*, Authorization*));
+ extern	void	free_billing		PARAMS((Billing*));
+ extern	void	free_authorization	PARAMS((Authorization*));
+ 
+ /* signs in the Billing-Info field */
+ #define	CREDIT_CARD_SIGN	"VISA"
+ #define ACCOUNT_ID_SIGN		"ACCOUNT_ID"
+ #define PR_SIGN			"PR"
+ #define MAX_AUTH_ATTEMPTS	10
+ #define AUTH_ATTEMPT_WAIT_SLICE	1
+ 
+ /*
+ *  PASSWORD is used by billing info file encryption.
+ *  PASSWORD should be exactly of 8 character (letter, digit, underscore)
+ */
+ #define	PASSWORD		"PASSWORD"
+ 
+ #define ACCEPTED		"Accepted"
+ #define REJECTED		"Rejected"
+ #define DEFERRED		"Deferred"
+ #define NONE			"None"
+ #endif/*__BILLING__HEADER__*/
+ 
Index: gnats-3.104-beta/gnats/categories
diff -c gnats-3.104-beta/gnats/categories:1.1.1.1 gnats-3.104-beta/gnats/categories:1.2
*** gnats-3.104-beta/gnats/categories:1.1.1.1	Thu Oct  9 21:37:29 1997
--- gnats-3.104-beta/gnats/categories	Tue Oct 21 22:39:11 1997
***************
*** 5,11 ****
  #
  # Each entry has the format:
  #
! # 	category:description:responsible:notify
  #
  # * `category' is the name of the classification for the PR.
  # * `description' can be a normal text description for the
--- 5,11 ----
  #
  # Each entry has the format:
  #
! # 	category:description:responsible:notify:releases
  #
  # * `category' is the name of the classification for the PR.
  # * `description' can be a normal text description for the
***************
*** 15,20 ****
--- 15,22 ----
  #   appearing in this category.
  # * `notify' are other email addresses which should be given copies of
  #    any PR in this category.
+ # * `releases' is "|" - separated list of releases stored in CVS
+ #    repository
  #
  # The following category is mandatory for GNATS to work.
  #
***************
*** 22,28 ****
  #
  # Sample categories:
  #
! doc:Documentation Bug:jeffrey:pesch
! gcc:The GNU C compiler:wilson:tiemann
! g++:The GNU C++ compiler:brendan:tiemann, mrs
! test:*Test Category:gnats-admin:
--- 24,30 ----
  #
  # Sample categories:
  #
! doc:Documentation Bug:jeffrey:pesch:1.0|1.1|beta-2.0
! gcc:The GNU C compiler:wilson:tiemann:2.5|2.6|2.7.5|2.8-alpha
! g++:The GNU C++ compiler:brendan:tiemann,mrs:2.5|2.6|2.7.5|2.8-alpha
! test:*Test Category:gnats-admin::1.0-test
Index: gnats-3.104-beta/gnats/check-cvs-tag.sh
diff -c /dev/null gnats-3.104-beta/gnats/check-cvs-tag.sh:1.2
*** /dev/null	Thu Oct 23 13:27:07 1997
--- gnats-3.104-beta/gnats/check-cvs-tag.sh	Tue Oct 21 22:39:12 1997
***************
*** 0 ****
--- 1,70 ----
+ #!/bin/sh
+ # Program to check the availability of a tag in cvs repository
+ # for a module.
+ # Copyright (C) 1993, 1994, 1995 Free Software Foundation, Inc.
+ # Hacked on the basis of edit-pr by Sergey V. Vanskov (vanskov@aha.ru)
+ #
+ # 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.
+ 
+ CVS="xCVSx"
+ CVS_ROOT="xCVS_ROOTx"
+ version=xVERSIONx
+ 
+ COMMAND=`echo $0 | sed -e 's,.*/,,'`
+ usage="Usage: $COMMAND [-hV] [--version] [--help] tag module"
+ 
+ # Newer config information?
+ [ -f ${GNATS_ROOT}/gnats-adm/config ] && . ${GNATS_ROOT}/gnats-adm/config
+ 
+ if [ $# -eq 0 ]; then
+   echo "$usage" ; exit 1
+ fi
+ if [ $# -eq 1 ]; then
+   case "$1" in
+     -V|-v|--version|--ve*)
+       echo "$version"; exit 0
+       ;;
+     -h|--help*)
+       echo "$usage"; exit 0
+       ;;
+     -*)
+       echo "$usage"; exit 1
+       ;;
+   esac
+ fi
+ 
+ if [ $# -ne 2 ]; then
+   echo "$usage" ; exit 1
+ fi
+ 
+ if [ -z "$TMPDIR" ]; then
+   TMPDIR=/tmp
+ else
+   if [ "`echo $TMPDIR | grep '/$'`" != "" ]; then
+     TMPDIR="`echo $TMPDIR | sed -e 's,/$,,'`"
+   fi
+ fi
+ 
+ TMP_DIRECTORY=$TMPDIR/cct$$
+ 
+ $CVS -d $CVS_ROOT -nlQf export -n -r $1 -d $TMP_DIRECTORY $2 2> /dev/null \
+ 1> /dev/null
+ 
+ RETURN_CODE=$?
+ 
+ rmdir -f $TMP_DIRECTORY
+ exit $RETURN_CODE
Index: gnats-3.104-beta/gnats/cmds.c
diff -c gnats-3.104-beta/gnats/cmds.c:1.1.1.1 gnats-3.104-beta/gnats/cmds.c:1.3
*** gnats-3.104-beta/gnats/cmds.c:1.1.1.1	Thu Oct  9 21:37:29 1997
--- gnats-3.104-beta/gnats/cmds.c	Thu Oct 16 22:54:54 1997
***************
*** 630,636 ****
  		  CODE_INVALID_CATEGORY, pr[CATEGORY].value);
  	  return;
  	}
!       if (find_submitter (&submitter, pr[SUBMITTER].value) == -1)
  	{
  	  printf ("%d No such submitter as %s.\r\n",
  		  CODE_INVALID_SUBMITTER, pr[SUBMITTER].value);
--- 630,636 ----
  		  CODE_INVALID_CATEGORY, pr[CATEGORY].value);
  	  return;
  	}
!       if (get_submitter_info (&submitter, pr[SUBMITTER].value) == -1)
  	{
  	  printf ("%d No such submitter as %s.\r\n",
  		  CODE_INVALID_SUBMITTER, pr[SUBMITTER].value);
***************
*** 722,728 ****
      return;
  
    memset ((void *) &submitter, 0, sizeof (Submitter));
!   if (find_submitter (&submitter, av[0]) == -1)
      {
        printf ("%d No such submitter as %s.\r\n",
  	      CODE_INVALID_SUBMITTER, av[0]);
--- 722,728 ----
      return;
  
    memset ((void *) &submitter, 0, sizeof (Submitter));
!   if (get_submitter_info (&submitter, av[0]) == -1)
      {
        printf ("%d No such submitter as %s.\r\n",
  	      CODE_INVALID_SUBMITTER, av[0]);
***************
*** 747,753 ****
      return;
  
    memset ((void *) &submitter, 0, sizeof (Submitter));
!   if (find_submitter (&submitter, av[0]) == -1)
      {
        printf ("%d No such submitter as %s.\r\n",
  	      CODE_INVALID_SUBMITTER, av[0]);
--- 747,753 ----
      return;
  
    memset ((void *) &submitter, 0, sizeof (Submitter));
!   if (get_submitter_info (&submitter, av[0]) == -1)
      {
        printf ("%d No such submitter as %s.\r\n",
  	      CODE_INVALID_SUBMITTER, av[0]);
***************
*** 1452,1458 ****
    if (get_gnats_file (LIST_RESPONSIBLE, NULL) == 0)
      printf (".\r\n");
  }
! 
  void
  GNATS_lsub (ac, av)
       int ac;
--- 1452,1458 ----
    if (get_gnats_file (LIST_RESPONSIBLE, NULL) == 0)
      printf (".\r\n");
  }
! #ifndef __PRIVATE_SID__
  void
  GNATS_lsub (ac, av)
       int ac;
***************
*** 1467,1472 ****
--- 1467,1473 ----
    if (get_gnats_file (LIST_SUBMITTERS, NULL) == 0)
      printf (".\r\n");
  }
+ #endif
  
  void
  GNATS_help (ac, av)
Index: gnats-3.104-beta/gnats/config.h
diff -c gnats-3.104-beta/gnats/config.h:1.1.1.1 gnats-3.104-beta/gnats/config.h:1.5
*** gnats-3.104-beta/gnats/config.h:1.1.1.1	Thu Oct  9 21:37:29 1997
--- gnats-3.104-beta/gnats/config.h	Wed Oct 22 20:27:21 1997
***************
*** 65,70 ****
--- 65,84 ----
     the person responsible may well be a local user.  But the name stuck.  */
  #define RESPONSIBLE_FILE  "/responsible"
  
+ /* Accounts of submitters */
+ #define	ACCOUNTS		"/accounts"
+ 
+ /* Billing info storage file */
+ #define	BILLING			"/billing"
+ 
+ /* Definition of products */
+ #define PRODUCTS		"/products"
+ 
+ /* Definition of supercategories */
+ #define SUPERCATEGORIES		"/supercategories"
+ 
+ /* closed PRs log */
+ #define RATING			"/.rating"
  /* File used to provide fast lookups on certain fields.  */
  #define INDEX "/index"
  
Index: gnats-3.104-beta/gnats/configure
diff -c gnats-3.104-beta/gnats/configure:1.1.1.1 gnats-3.104-beta/gnats/configure:1.2
*** gnats-3.104-beta/gnats/configure:1.1.1.1	Thu Oct  9 21:37:29 1997
--- gnats-3.104-beta/gnats/configure	Fri Oct 17 22:51:34 1997
***************
*** 2576,2581 ****
--- 2576,2584 ----
  # echo guessing optimal GNATS configuration
  
  
+ CVS=/usr/bin/cvs
+ CVS_ROOT=/usr/CVSROOT
+ 
  # Get GNATS_ROOT from environment, if possible
  
  if test "x$prefix" != xNONE; then
***************
*** 2902,2907 ****
--- 2905,2912 ----
  s%@HAVE_KERBEROS@%$HAVE_KERBEROS%g
  s%@KRBINCLUDE@%$KRBINCLUDE%g
  s%@EXTRA_OBJS@%$EXTRA_OBJS%g
+ s%@CVS@%$CVS%g
+ s%@CVS_ROOT@%$CVS_ROOT%g
  s%@GNATS_ROOT@%$GNATS_ROOT%g
  s%@GNATS_ADDR@%$GNATS_ADDR%g
  s%@GNATS_SERVER@%$GNATS_SERVER%g
Index: gnats-3.104-beta/gnats/edit.c
diff -c gnats-3.104-beta/gnats/edit.c:1.1.1.1 gnats-3.104-beta/gnats/edit.c:1.5
*** gnats-3.104-beta/gnats/edit.c:1.1.1.1	Thu Oct  9 21:37:29 1997
--- gnats-3.104-beta/gnats/edit.c	Thu Oct 23 13:25:17 1997
***************
*** 26,31 ****
--- 26,47 ----
  
  extern int force;
  
+ /* log for debugging purposes */
+ #undef __DEB__
+ #ifdef __DEB__
+ #define  log(smth)	\
+   {\
+     FILE* f = fopen ("/tmp/file-pr.log", "a");\
+     if(f)\
+       {\
+ 	fprintf smth;\
+ 	fclose(f);\
+       }\
+   }
+ #else
+ #define	log(smth)
+ #endif/*__DEB__*/
+ 
  int
  modify_pr (fp)
      FILE *fp;
***************
*** 46,51 ****
--- 62,76 ----
    char *s;
    Index *i, *prev_index = NULL, *old_index = NULL;
  
+   /* rating vars */
+   char *str;
+   char *tmp_filename;
+   int rating_copy = 0;
+   char *n_pr_number;
+   char *n_category;
+   char *n_responsible;
+   /* end of rating vars */
+ 
    /* this will print the bad values out. then return and exit.  */
    if (!check_pr (fp))
      return 0;
***************
*** 167,172 ****
--- 192,225 ----
      sprintf (path, "%s/%s/%s", gnats_root, new_index->category,
  	     new_index->number);
  
+   if (!strcmp (field_value (STATE), "closed"))
+     {
+       str = tmpnam (NULL);
+       tmp_filename = (char*) alloca (strlen (str) + 1);
+       strcpy (tmp_filename, str);
+ 
+       log ((f, "start %s\n", tmp_filename));
+ 
+       log ((f, "coping %s to %s\n", old_path, tmp_filename));
+ 
+       if(!copy_file (old_path, tmp_filename))
+ 	{
+ 	  log ((f, "copy is made\n"));
+ 	  n_pr_number = (char*) alloca (strlen (field_value (NUMBER)) + 1);
+ 	  strcpy (n_pr_number, field_value (NUMBER));
+ 	  n_category = (char*) alloca (strlen (field_value (CATEGORY)) + 1);
+ 	  strcpy (n_category, field_value (CATEGORY));
+ 	  n_responsible = (char*) alloca
+ 	    (strlen (field_value (RESPONSIBLE)) + 1);
+ 	  strcpy (n_responsible, field_value (RESPONSIBLE));
+ 	  rating_copy = 1;
+ 	}
+       else
+ 	{
+ 	  punt (0, "unable to make a copy of PR old version");
+ 	}
+     }
+ 
    if (! force && rename (old_path, lock_path) < 0)
      {
        if (errno != EXDEV)
***************
*** 272,277 ****
--- 325,384 ----
  
    free_index (current_index);
    free_index_entry (old_index);
+ 
+   log ((f, "hello\n"));
+   if (rating_copy)
+     {
+       FILE *old_pr;
+       log ((f, "inside rating %s\n", tmp_filename));
+       if ((old_pr = fopen (tmp_filename, "r")) != NULL)
+ 	{
+ 	  extern void forget_pr ();
+ 	  forget_pr ();
+ 	  log ((f, "old pr is open\n"));
+ 	  if (read_header (old_pr) < 0)
+ 	    {
+ 	      log ((f, "invalid header\n"));
+ 	      punt (0, "invalid header in old pr file");
+ 	      fclose (old_pr);
+ 	      return 1;
+ 	    }
+ 	  log ((f, "header is done\n"));
+ 	  read_pr (old_pr, 0);
+ 	  log ((f, "file is read\n"));
+ 	  fclose (old_pr);
+ 	  log ((f, "file is read\n"));
+ 	  if (strcmp (field_value (STATE), "closed"))
+ 	    {
+ 	      FILE *rate_pr;
+ 	      char *rate_path;
+ 	      rate_path = (char*) alloca (strlen (gnats_root) +
+ 		strlen (n_category) + strlen (RATING) + 2);
+ 	      sprintf (rate_path, "%s/%s%s", gnats_root, n_category, RATING);
+ 	      rate_pr = fopen (rate_path, "a");
+ 	      if (rate_pr != NULL)
+ 		{
+ 		  time_t n_time;
+ 		  struct tm* current_time;
+ 		  time (&n_time);
+ 		  current_time = localtime (&n_time);
+ 		  fprintf (rate_pr, "%s:%s:%d/%d/%d:::\n",
+ 		    n_pr_number, n_responsible,
+ 		    current_time->tm_year + 1900,
+ 		    current_time->tm_mon,
+ 		    current_time->tm_mday);
+ 
+ 		  fclose (rate_pr);
+ 		}
+ 	      else
+ 		punt (0, "unable to open %s file", rate_path);
+ 	    }
+ 	}
+       else
+ 	punt (0, "unable to open copy of old PR in temporary directory");
+       unlink (tmp_filename);
+     }
+ 
    xfree (lock_path);
    xfree (path);
    xfree (old_path);
***************
*** 318,324 ****
      }
    free_category (&category);
  
!   if (find_submitter (&submitter, pr[SUBMITTER].value) == -1)
      {
        if (is_daemon)
  	printf ("%d No such submitter as %s.\r\n",
--- 425,431 ----
      }
    free_category (&category);
  
!   if (get_submitter_info (&submitter, pr[SUBMITTER].value) == -1)
      {
        if (is_daemon)
  	printf ("%d No such submitter as %s.\r\n",
Index: gnats-3.104-beta/gnats/file-pr.c
diff -c gnats-3.104-beta/gnats/file-pr.c:1.1.1.1 gnats-3.104-beta/gnats/file-pr.c:1.21
*** gnats-3.104-beta/gnats/file-pr.c:1.1.1.1	Thu Oct  9 21:37:29 1997
--- gnats-3.104-beta/gnats/file-pr.c	Thu Oct 23 13:25:17 1997
***************
*** 22,37 ****
  #include "config.h"
  #include "gnats.h"
  #include "query.h"
  
  static int verify_analysis	PARAMS((void));
  static void run_atpr		PARAMS((Submitter *, struct tm *, char *, char *));
! static void reply_to_submitter	PARAMS((char *));
  static void create_report	PARAMS((char *, int));
  static int get_bug_number	PARAMS((void));
! static void notify_responsible	PARAMS((char*, char*, char*, char*, char*, int, struct tm *));
  
  static struct bad_enum *bad_enums = NULL;
  
  void
  gnats (fp)
       FILE *fp;
--- 22,99 ----
  #include "config.h"
  #include "gnats.h"
  #include "query.h"
+ #include "billing.h"
+ #include <sys/types.h>
+ #include <sys/wait.h>
  
  static int verify_analysis	PARAMS((void));
  static void run_atpr		PARAMS((Submitter *, struct tm *, char *, char *));
! static void reply_to_submitter	PARAMS((char *, int));
  static void create_report	PARAMS((char *, int));
  static int get_bug_number	PARAMS((void));
! static void notify_responsible	PARAMS((char*, char*, char*, char*, char*, int, struct tm *, int));
  
  static struct bad_enum *bad_enums = NULL;
  
+ /* log for debugging purposes */
+ #undef __DEB__
+ #ifdef __DEB__
+ #define  log(smth)	\
+   {\
+     FILE* f = fopen ("/tmp/file-pr.log", "a");\
+     if(f)\
+       {\
+ 	fprintf smth;\
+ 	fclose(f);\
+       }\
+   }
+ #else
+ #define	log(smth)
+ #endif/*__DEB__*/
+ 
+ typedef enum{
+   NO_SUCH_PR,
+   NO_SUCH_CATEGORY,
+   NOT_OWNER,
+ 
+   NO_BILLING_INFO,
+   INVALID_BILLING_INFO,
+   VALID_BILLING_INFO,
+   DEFERRED_BILLING_INFO,
+ 
+   BILLING_INFO_HAS_BEEN_DEFERRED,
+   ALREADY_UPGRADED
+ } FollowUpStatus;
+ 
+ static void reply_to_follow_up_pr	PARAMS((
+   char *submitter,
+   char *responsible,
+   char *follow_date,
+   int pr_number,
+   FollowUpStatus status,
+   int has_new_fields
+ ));
+ 
+ static void notify_responsible_about_follow_up
+ 					PARAMS((
+   char *responsible,
+   char *category_notify,
+   int response_time,
+   struct tm *expired,
+   int billing_status,
+   int has_new_fields
+ ));
+ 
+ static int concatenate_field		PARAMS((
+   PR_Name type,
+   char *str
+ ));
+ 
+ static int check_cvs_tag_presence	PARAMS((
+   char *tag,
+   char *module
+ ));
+ 
  void
  gnats (fp)
       FILE *fp;
***************
*** 54,62 ****
    struct stat sbuf;
    time_t seconds;
  
!   /* Zero things out to make sure.  */
!   memset ((void *) &submitter, 0, sizeof (Submitter));
!   memset ((void *) &category, 0, sizeof (Category));
  
    if (fp == (FILE *) NULL)
      return;
--- 116,129 ----
    struct stat sbuf;
    time_t seconds;
  
!   Billing billing;
!   Authorization authorization;
!   int	 billing_status; /*
! 			*	0 - for "None"
! 			*	1 - for "Accepted"
! 			*	2 - for "Rejected"
! 			*	3 - for "Deferred"
! 			 */
  
    if (fp == (FILE *) NULL)
      return;
***************
*** 68,87 ****
    read_pr (fp, 0);
    fclose (fp);
  
    site = field_value (SUBMITTER);
  
!   if (find_submitter (&submitter, site) == -1)
      {
        log_msg (LOG_INFO, 1, "resetting to default submitter from:", site);
        site = def_subm;
        set_field (SUBMITTER, def_subm);
  
        /* If it's still an error, punt because this should not happen.  */
!       if (find_submitter (&submitter, def_subm) == -1)
          punt (1, "can not find default submitter %s", def_subm);
      }
  
    bug_group = field_value (CATEGORY);
    if (find_category (&category, bug_group) == -1)
      {
        log_msg (LOG_INFO, 1, "resetting bug category to pending from:",
--- 135,162 ----
    read_pr (fp, 0);
    fclose (fp);
  
+   /* Zero things out to make sure.  */
+   memset ((void *) &submitter, 0, sizeof (Submitter));
+   memset ((void *) &category, 0, sizeof (Category));
+ 
    site = field_value (SUBMITTER);
+   log ((f, "site is %s\n", site));
  
!   if (get_submitter_info (&submitter, site) == -1)
      {
+       /* left for possible easier modification of get_submitter_info */
+ 
        log_msg (LOG_INFO, 1, "resetting to default submitter from:", site);
        site = def_subm;
        set_field (SUBMITTER, def_subm);
  
        /* If it's still an error, punt because this should not happen.  */
!       if (get_submitter_info (&submitter, def_subm) == -1)
          punt (1, "can not find default submitter %s", def_subm);
      }
  
    bug_group = field_value (CATEGORY);
+   log ((f, "category %s\n", bug_group));
    if (find_category (&category, bug_group) == -1)
      {
        log_msg (LOG_INFO, 1, "resetting bug category to pending from:",
***************
*** 94,102 ****
--- 169,477 ----
        if (find_category (&category, bug_group) == -1)
  	punt (1, "No `pending' directory under gnats-root (%s).", gnats_root);
      }
+   bug_group = category.key;
+ 
+   /* check billing information */
+   memset (&billing, 0, sizeof (Billing));
+   billing_status = parse_billing_info (&billing);
+ 
+   if (!strcasecmp (site, def_subm))
+     {
+       log ((f, "anonymous submitter\n"));
+       /*
+       *  forget billing information from anonymous submitter.
+       */
+       billing_status =0;
+     }
+ 
+   log ((f, "billing status before auth: %d\n", billing_status))
+ 
+   /* check whether it is a follow-up PR */
+   if (billing.problem_report != 0)
+     {
+       /*
+       * it is a follow-up PR.
+       * get MultyText fields, check the PR for existance,
+       * check billing information, concatenate MultyText fields,
+       * upgrade status, Reply to the submitter, exit
+       */
+       char *pr_path;
+       char *pr_lock;
+       char *follow_date = (char*) alloca (strlen (header_value (DATE)) + 1);
+       int has_new_fields = 0;  /* 0 if nothing to concatenate */
+ 
+       log ((f, "it is a follow-up PR\n"));
+       strcpy (follow_date, header_value (DATE));
+       log ((f, "follow_date: %s;\n", follow_date));
+ 
+       if (!strcmp (category.key, PENDING))
+ 	{
+ 	  log ((f, "No such category\n"));
+           /* Acknowledge the person's problem report.  */
+           if (flag_ack)
+ 	    reply_to_follow_up_pr (submitter.key, NULL, follow_date,
+ 	      billing.problem_report, NO_SUCH_CATEGORY, has_new_fields);
+ 	  return;
+ 	}
+ 
+       /*
+       * the existence of the category directory is checked above
+       * if it will be changed the check should be placed here
+       */
+ 
+       pr_path = (char*) alloca (strlen (gnats_root) + strlen (bug_group) + 15);
+       sprintf (pr_path, "%s/%s/%i",
+ 	gnats_root, bug_group, billing.problem_report);
+       pr_lock = (char*) alloca (strlen (pr_path) + 6);
+       log ((f, "path for old PR: %s;\n", pr_path));
+       sprintf (pr_lock, "%s.lock", pr_path);
+       log ((f, "path for old PR lock: %s;\n", pr_lock));
+ 
+       if (access (pr_lock, R_OK) == 0)
+ 	{
+           /*
+ 	  *  Before adding follow-up info, see if someone's got the PR
+           *  locked; if they do, then try it at the next run of the queue.
+ 	  */
+ 	  log ((f, "lock found wait for the next trial\n"));
+ 	  unlock_gnats ();
+ 	  exit (1);
+ 	}
+       else
+ 	{
+ 	  char *str;
+ 
+           char *environment;
+           char *description;
+           char *how_to_repeat;
+           char *fix;
+ 	  /*
+           char *unformatted;
+ 	  */
+ 
+ 	  char *billing_old;
+ 
+ 	  FILE *fp_old;
+ 
+  	  fp_old = fopen (pr_path, "r");
+ 	  if (fp_old == NULL)
+ 	    {
+               /* Acknowledge the person's problem report.  */
+ 	      log ((f, "no old problem report\n"));
+               if (flag_ack)
+ 		reply_to_follow_up_pr (submitter.key, NULL, follow_date,
+ 		  billing.problem_report, NO_SUCH_PR, has_new_fields);
+ 	      return;
+ 	    }
+ 
+ 	  /* make copies */
+           environment = (char*) alloca (strlen (field_value (ENVIRONMENT)) + 1);
+ 	  strcpy (environment, field_value (ENVIRONMENT));
+ 
+           description = (char*) alloca (strlen (field_value (DESCRIPTION)) + 1);
+           strcpy (description, field_value (DESCRIPTION));
+ 
+           how_to_repeat =
+ 	    (char*) alloca (strlen (field_value (HOW_TO_REPEAT)) + 1);
+           strcpy (how_to_repeat, field_value (HOW_TO_REPEAT));
+ 
+           fix = (char*) alloca (strlen (field_value (FIX)) + 1);
+           strcpy (fix, field_value (FIX));
+ /*
+           unformatted = (char*) alloca (strlen (field_value (UNFORMATTED)) + 1);
+           strcpy (unformatted, field_value (UNFORMATTED));
+ */
+ 	  log ((f, "copies are made\n"));
+ 	  /* get old PR */
+ 	  clean_pr ();
+ 	  read_pr (fp_old, 0);
+ 	  log ((f, "PR has been read\n"));
+ 	  fclose (fp_old);
+ 
+ 	  /* new submitter-id and old must be similar  */
+ 	  if (strcasecmp (submitter.key, field_value (SUBMITTER)))
+ 	    {
+ 	      log ((f, "not owner implied %s, real %s\n",
+ 		submitter.key, field_value (SUBMITTER)));
+               /* Acknowledge the person's problem report.  */
+               if (flag_ack)
+ 		reply_to_follow_up_pr (submitter.key, NULL, follow_date,
+ 		  billing.problem_report, NOT_OWNER, has_new_fields);
+ 	      return;
+ 	    }
+ 
+ 	  billing_old =
+ 	    (char*) alloca (strlen (field_value (BILLING_INFO)) + 1);
+ 	  strcpy (billing_old, field_value (BILLING_INFO));
+ 
+ 	  has_new_fields += concatenate_field (ENVIRONMENT, environment);
+ 	  has_new_fields += concatenate_field (DESCRIPTION, description);
+ 	  has_new_fields += concatenate_field (HOW_TO_REPEAT, how_to_repeat);
+ 	  has_new_fields += concatenate_field (FIX, fix);
+ 	  /* not to make PR a huge one
+ 	  * has_new_fields += concatenate_field (UNFORMATTED, unformatted);
+ 	  */
+ 
+ 	  log ((f, "fields concatenated\n"));
+ 	  log ((f, "has_new_fields: %s\n", has_new_fields? "true": "false"));
+ 
+ 	  responsible = get_responsible_address (field_value (RESPONSIBLE));
+ 	  if (!responsible)
+ 	    {
+ 	      responsible = get_responsible (&category);
+ 	      if (!responsible)
+ 		{
+ 		  responsible = get_responsible_address (gnats_admin);
+ 		  if (!responsible)
+ 		    {
+ 		      char str[256];
+ 		      char bug_str[16];
+ 		      sprintf (bug_str, "%d", billing.problem_report);
+ 		      sprintf (str, "responsible entry not found for PR %s",
+ 			bug_str);
+ 		      log_msg (LOG_INFO, 1,
+ 			"responsible entry not found for PR", bug_str);
+ 		      punt (0, str);
+ 		    }
+ 		}
+               set_field (RESPONSIBLE, responsible->key);
+ 	    }
+ 
+ 	  log ((f, "responsible: %s;\n", responsible->key));
+ 	  if (!strcmp (billing_old, ACCEPTED))
+ 	    {
+ 	      log ((f, "already accepted\n"));
+ 	      if (has_new_fields)
+ 		create_report (pr_path, 1);
+ 
+ 	      notify_responsible_about_follow_up (
+ 		responsible->key,
+ 		category.notify,
+ 		-1,
+ 		NULL,
+ 		0, /* NO_BILLING_INFO */
+ 		has_new_fields
+ 	      );
+ 
+               /* Acknowledge the person's problem report.  */
+               if (flag_ack)
+ 		reply_to_follow_up_pr (
+ 		  submitter.key,
+ 		  responsible->alias,
+ 		  follow_date,
+ 		  billing.problem_report,
+ 		  billing_status == 1
+ 		    ?ALREADY_UPGRADED
+ 		    :NO_BILLING_INFO,
+ 		  has_new_fields
+ 		);
+ 
+ 	      return;
+ 	    }
+ 	  else if (!strcmp(billing_old, DEFERRED))
+ 	    {
+ 	      log ((f, "deferred\n"));
+ 
+ 	      if (has_new_fields)
+ 		create_report (pr_path, 1);
+ 
+ 	      notify_responsible_about_follow_up (
+ 		responsible->key,
+ 		category.notify,
+ 		-1,
+ 		NULL,
+ 		0, /* NO_BILLING_INFO */
+ 		has_new_fields
+ 	      );
+ 
+               /* Acknowledge the person's problem report.  */
+               if (flag_ack)
+ 		reply_to_follow_up_pr (
+ 		  submitter.key,
+ 		  responsible->alias,
+ 		  follow_date,
+ 		  billing.problem_report,
+ 		  billing_status
+ 		    ?BILLING_INFO_HAS_BEEN_DEFERRED
+ 		    :NO_BILLING_INFO,
+ 		  has_new_fields
+ 		);
+ 
+ 	      return;
+ 	    }
+ 
+  	  /*
+ 	  * do authorization if any valid billing information
+ 	  * is present
+ 	  */
+ 	  log ((f, "billing status %d\n", billing_status));
+ 	  if (billing_status == 1)
+ 	    {
+ 	      log ((f, "do authorization\n"));
+ 
+               memset (&authorization, 0, sizeof (Authorization));
+               billing_status = do_authorization (submitter.key, bug_group,
+ 	        &billing, &authorization);
+ 
+               log ((f, "billing status after auth: %d\n", billing_status));
+               add_billing_info (billing.problem_report,
+ 		&billing, &authorization);
+ 
+ 	      free_authorization (&authorization);
+ 	    }
+ 	  log ((f, "billing info is being stored.\n"));
+ 	  set_billing_field (billing_status);
+ 
+ 	  create_report (pr_path, 1);
+ 
+ 	  notify_responsible_about_follow_up (
+ 	    responsible->key,
+ 	    category.notify,
+ 	    authorization.response_time,
+ 	    get_response_time (authorization.response_time),
+ 	    billing_status,
+ 	    has_new_fields
+ 	  );
+ 
+ 	  {
+ 	    FollowUpStatus	status;
+ 	    switch (billing_status)
+ 	      {
+ 		case 0: status = NO_BILLING_INFO; break;
+ 		case 1: status = VALID_BILLING_INFO; break;
+ 		case 2: status = INVALID_BILLING_INFO; break;
+ 		case 3: status = DEFERRED_BILLING_INFO; break;
+ 	      }
+             /* Acknowledge the person's problem report.  */
+             if (flag_ack)
+ 	      reply_to_follow_up_pr (
+ 		submitter.key,
+ 		responsible->alias,
+ 		follow_date,
+ 		billing.problem_report,
+ 		status,
+ 		has_new_fields
+ 	      );
+ 	  }
+ 
+ 	  if (billing_status == 1 && authorization.response_time > 0 &&
+ 	      flag_notify)
+ 	    {
+ 	      run_atpr (
+ 		&submitter,
+ 		get_response_time (authorization.response_time),
+ 		field_value (NUMBER),
+ 		pr_path
+ 	      );
+ 	    }
+ 
+           return;
+         }
+     }
  
    /* Set responsible field, adding full name as well (if found).  If it's
       coming in with a Responsible: field already, don't wipe it out.  */
+   log ((f, "it's gonna to get responsible\n"));
    p = field_value (RESPONSIBLE);
    if (p && p[0])
      {
***************
*** 148,153 ****
--- 523,529 ----
    /* Check the subject and the synopsis lines.  If both of these are null,
       then forget it.  currently, it seems that values are set to '\0' when
       there is nothing there. */
+   log ((f, "it's gonna to syncronize subject & synopsis\n"));
    synopsis = field_value (SYNOPSIS);
    subject = header_value (SUBJECT);
    if (*subject == '\0' && *synopsis != '\0')
***************
*** 172,177 ****
--- 548,554 ----
        set_field (SYNOPSIS, synopsis);
      }
  
+   log ((f, "it's gonna to get arrival time\n"));
    /* Set arrival date and time of response.  */
    seconds = time ((time_t) 0);
    strftime (arrival_time, GNATS_TIME_LENGTH, "%a %b %e %T %Z %Y",
***************
*** 186,191 ****
--- 563,569 ----
    /* Put together the path to where the bug will be stored.  If the dir
       is not there, and the category is PENDING, auto-create that one,
       if we want to.  If not, make the bug pending, and store in there.  */
+   log ((f, "it's gonna to get final strike on category\n"));
    sprintf (path, "%s/%s", gnats_root, bug_group);
    err = stat (path, &sbuf);
    if (err == -1 && !flag_autocreate)
***************
*** 228,277 ****
  	}
      }
  
-   /* Retrieve a unique bug number.  */
-   bug_number = get_bug_number ();
-   sprintf (number, "%d", bug_number);
-   set_field (NUMBER, number);
- 
-   /* Make sure all the values are ok; patch in any bogons, and keep
-      track of the bad ones.  */
-   bad_enums = check_enum_types (1);
-  
-   /* Write the file out.  */
-   sprintf (bug_name, "%s/%d", bug_group, bug_number);
-   sprintf (path, "%s/%s", gnats_root, bug_name);
-   create_report (path, 1);
-   log_msg (LOG_INFO, 1, "PR written out:", path);
- 
-   /* Add a line into the index for use by query-pr.  */
-   /* This should be done regardless of whether the bug is pending or not.  */
-   add_to_index ();
- 
-   /* If it fell into pending, don't send all of the mail for it, don't create
-      an index entry, and don't try to run at_pr.  Just punt it to
-      the GNATS_ADMIN.  */
-   if (strcmp (category.key, PENDING) != 0)
      {
!       /* Acknowledge the person's problem report.  */
!       if (flag_ack)
! 	reply_to_submitter (responsible->alias);
! 
!       /* Notify the responsible parties about their duty.  */
!       notify_responsible (responsible->alias,
! 			  submitter.contact,
! 			  submitter.notify, category.notify,
! 			  submitter.type, submitter.rtime, expired);
  
!       if (submitter.rtime != -1 && flag_notify && verify_analysis ())
! 	run_atpr (&submitter, expired, bug_name, path);
      }
-   else
-     notify_responsible (gnats_admin, NULL, NULL, NULL, NULL, 0, (struct tm *)0);
- 
-   free_category (&category);
-   free_submitter (&submitter);
-   free_responsible (responsible);
-   xfree ((char *)responsible);
  }
  
  static int
--- 606,705 ----
  	}
      }
  
      {
!       log ((f, "new code\n"));
!       /* Retrieve a unique bug number.  */
!       bug_number = get_bug_number ();
!       log ((f, "PR number is %d\n", bug_number));
!       sprintf (number, "%d", bug_number);
!       set_field (NUMBER, number);
! 
!       if (billing_status == 1)
! 	if (strcmp (bug_group, PENDING))
! 	  {
! 	    memset (&authorization, 0, sizeof (Authorization));
! 	    billing_status = do_authorization (submitter.key, bug_group,
! 	      &billing, &authorization);
! 
! 	    log ((f, "billing status after auth: %d\n", billing_status));
! 	    add_billing_info (bug_number, &billing, &authorization);
! 
!             free_authorization (&authorization);
! 
! 	    if (authorization.response_time > 0)
! 	      {
! 	        expired = get_response_time (authorization.response_time);
! 	        submitter.rtime = authorization.response_time ;
! 	      }
! 	  }
! 	else
! 	  {
! 	    log ((f, "in pending no billing information\n"));
! 	    /*
! 	    *  forget about billing information if the PR
! 	    *  falls into pending
! 	    */
! 	    billing_status = 0;
! 	  }
! 
!       /* Make sure all the values are ok; patch in any bogons, and keep
!          track of the bad ones.  */
!       bad_enums = check_enum_types (1);
!  
!       log ((f, "billing info is being stored.\n"));
!       set_billing_field (billing_status);
  
!       /* Write the file out.  */
!       log ((f, "cat: %s; num: %d; root: %s;\n", bug_group, bug_number,
! 	gnats_root));
!       sprintf (bug_name, "%s/%d", bug_group, bug_number);
!       sprintf (path, "%s/%s", gnats_root, bug_name);
!       log ((f, "path is %s\n", path));
!       create_report (path, 1);
!       log_msg (LOG_INFO, 1, "PR written out:", path);
! 
!       /* Add a line into the index for use by query-pr.  */
!       /* This should be done regardless of whether the   */
!       /* bug is pending or not.                          */
!       add_to_index ();
! 
!       /* If it fell into pending, don't send all of the  */
!       /* mail for it, don't create an index entry, and   */
!       /* don't try to run at_pr.  Just punt it to the    */
!       /* GNATS_ADMIN.                                    */
!       if (strcmp (category.key, PENDING) != 0)
!         {
!           /* Acknowledge the person's problem report.  */
!           if (flag_ack)
! 	    reply_to_submitter (responsible->alias, billing_status);
! 
!           /* Notify the responsible parties about their duty.  */
!           notify_responsible (
! 	    responsible->alias,
! 	    submitter.contact,
! 	    submitter.notify,
! 	    category.notify,
! 	    submitter.type,
! 	    authorization.response_time,
! 	    expired,
! 	    billing_status
! 	  );
! 
!           if (submitter.rtime != -1 && flag_notify &&
! 	      (billing_status == 1 || verify_analysis ()))
! 	    run_atpr (&submitter, expired, bug_name, path);
!         }
!       else
!         notify_responsible (gnats_admin, NULL, NULL, NULL, NULL,
! 	  0, (struct tm *)0, billing_status);
!     
!       free_billing (&billing);
!       log ((f, "auth info has been released.\n"));
!       free_category (&category);
!       free_submitter (&submitter);
!       free_responsible (responsible);
!       xfree ((char *)responsible);
      }
  }
  
  static int
***************
*** 339,352 ****
  
    if (ats[i] == NULL)
      {
!       punt (0, "%s: couldn't find `at'\n", program_name);
        return;
      }
  
    p = popen (command, "w");
    if (p == (FILE *)NULL)
      {
!       punt (0, "Couldn't open the pipe for at-pr.");
        log_msg (LOG_INFO, 0, "popen failed:");
      }
    else
--- 767,780 ----
  
    if (ats[i] == NULL)
      {
!       punt (0, "%s: could not find \"at\"\n", program_name);
        return;
      }
  
    p = popen (command, "w");
    if (p == (FILE *)NULL)
      {
!       punt (0, "Could not open the pipe for at-pr.");
        log_msg (LOG_INFO, 0, "popen failed:");
      }
    else
***************
*** 355,361 ****
        char *cont = arg_quote (submitter->contact);
        char *pa = arg_quote (gnats_admin);
        
!       fprintf (p, "%s %d %s %s %s '%s' '%s' '%s'\n", at_pr,
  	       submitter->rtime, bug_name, path, submitter->key,
  	       orig, cont, pa);
  
--- 783,789 ----
        char *cont = arg_quote (submitter->contact);
        char *pa = arg_quote (gnats_admin);
        
!       fprintf (p, "%s %d %s %s %s \'%s\' \'%s\' \'%s\'\n", at_pr,
  	       submitter->rtime, bug_name, path, submitter->key,
  	       orig, cont, pa);
  
***************
*** 434,462 ****
  /* Send a reply to the submitter of the bug.  */
  
  static void
! reply_to_submitter (responsible)
       char *responsible;
  {
    FILE *msg;
    char *subject, *reply_to;
  
    block_signals ();
  
    msg = open_mail_file ();
    if (msg == (FILE *)NULL)
      punt (1, "%s: cannot open mail agent.", program_name);
  
!   reply_to = header_value (REPLY_TO);
!   if (*reply_to == '\0')
!     reply_to = header_value (FROM);
!   fprintf (msg, "To: %s", reply_to);
    fprintf (msg, "From: %s\n", gnats_addr);
    subject = header_value (SUBJECT);
    fprintf (msg, "Subject: Re: %s/%s: %s",
! 	   field_value (CATEGORY), field_value (NUMBER), subject);
    if (*subject == '\0')
      fprintf (msg, "\n");
!   fprintf (msg, "Reply-To: %s, %s\n", gnats_addr, responsible);
    fprintf (msg, "In-Reply-To: Your message of %s\t%s\n",
  	   header_value (DATE), header_value (MSG_ID));
  
--- 862,900 ----
  /* Send a reply to the submitter of the bug.  */
  
  static void
! reply_to_submitter (responsible, billing_status)
       char *responsible;
+      int  billing_status;
  {
    FILE *msg;
    char *subject, *reply_to;
  
+   if (!field_value (SUBMITTER) || !*field_value (SUBMITTER))
+     return;
+ 
    block_signals ();
  
    msg = open_mail_file ();
    if (msg == (FILE *)NULL)
      punt (1, "%s: cannot open mail agent.", program_name);
  
!   reply_to = field_value (SUBMITTER);
! 
!   fprintf (msg, "To: %s\n", reply_to);
! 
    fprintf (msg, "From: %s\n", gnats_addr);
+ 
    subject = header_value (SUBJECT);
    fprintf (msg, "Subject: Re: %s/%s: %s",
!     field_value (CATEGORY), field_value (NUMBER), subject);
    if (*subject == '\0')
      fprintf (msg, "\n");
! 
!   fprintf (msg, "Reply-To: %s", gnats_addr);
!   if (responsible && *responsible)
!     fprintf (msg, ", %s", responsible);
!   fprintf (msg, "\n");
! 
    fprintf (msg, "In-Reply-To: Your message of %s\t%s\n",
  	   header_value (DATE), header_value (MSG_ID));
  
***************
*** 466,471 ****
--- 904,941 ----
    fprintf (msg, "The individual assigned to look at your\nreport is: %s. \n",
  		field_value (RESPONSIBLE));
  
+   switch (billing_status)
+     {
+       case 0:
+         fprintf (msg, "\nThe billing information is not present in \n");
+         fprintf (msg, "your problem report.\n");
+         fprintf (msg, "Your report will be taken into consideration in \n");
+ 	fprintf (msg, "the future releases of our software product.\n");
+ 	fprintf (msg, "\nYou may convert this problem report into \n");
+ 	fprintf (msg, "commercial technical support call with\n");
+ 	fprintf (msg, "the \"upgrade-pr\" utility.\n");
+         break;
+       case 1:
+ 	fprintf (msg, "\nThe billing information supplied in your problem\n");
+ 	fprintf (msg, "is valid. Your problem report has gained the status\n");
+ 	fprintf (msg, "of commercial technical support call.\n");
+ 	break;
+       case 2:
+         fprintf (msg, "\nThe billing information supplied in your problem\n");
+         fprintf (msg, "report is invalid.\n");
+         fprintf (msg, "Your report will be taken into consideration in \n");
+ 	fprintf (msg, "the future releases of our software product.\n");
+ 	fprintf (msg, "\nYou may convert this problem report into \n");
+ 	fprintf (msg, "commercial technical support call with\n");
+ 	fprintf (msg, "the \"upgrade-pr\" utility.\n");
+         break;
+       case 3:
+ 	fprintf (msg, "\nThe billing information supplied in your problem\n");
+ 	fprintf (msg, "report cannot be verified immediately.\n");
+ 	fprintf (msg, "You will be notified the result of the verification\n");
+ 	fprintf (msg, "as soon as it is completed.\n");
+ 	break;
+     }
    if (bad_enums != NULL)
      {
        /* Report the enums that were bad, separating them by an empty line.  */
***************
*** 549,558 ****
  /* Generate a bug report and send it off to the responsible person.  */
  static void
  notify_responsible (responsible, subcontact, subnotify, cnotify,
! 		    type, rtime, expired)
       char *responsible, *subcontact, *subnotify, *cnotify, *type;
       int rtime;
       struct tm *expired;
  {
    FILE *msg;
    char *notify = (char *) alloca (BUFSIZ); /* FIXME - huge */
--- 1019,1029 ----
  /* Generate a bug report and send it off to the responsible person.  */
  static void
  notify_responsible (responsible, subcontact, subnotify, cnotify,
! 		    type, rtime, expired, billing_status)
       char *responsible, *subcontact, *subnotify, *cnotify, *type;
       int rtime;
       struct tm *expired;
+      int billing_status;
  {
    FILE *msg;
    char *notify = (char *) alloca (BUFSIZ); /* FIXME - huge */
***************
*** 634,645 ****
  
    fprintf (msg, "\n");
  
!   if (flag_notify && rtime != -1 && expired && verify_analysis ())
      {
        int len = strftime (buf, GNATS_TIME_LENGTH, "%a %b %d %X %Z %Y", expired);
!       fprintf (msg,
! 	       "\n\tThe contract type is `%s' with a response time of %d business hours.\n",
! 	       type, rtime);
        if (len)
  	fprintf (msg, "\tA first analysis should be sent before: %s\n\n", buf);
      }
--- 1105,1128 ----
  
    fprintf (msg, "\n");
  
!   if (billing_status == 1)
!     {
!       fprintf (msg, "\n\tIt is a commercial technical support call.\n");
!     }
!   if (flag_notify && rtime != -1 && expired &&
!       (billing_status == 1 || verify_analysis ()))
      {
        int len = strftime (buf, GNATS_TIME_LENGTH, "%a %b %d %X %Z %Y", expired);
!       if (type && *type)
! 	{
! 	  fprintf (msg, "\n\tThe contract type is `%s' with a response time\n",
! 	    type);
! 	  fprintf (msg, "of %d business hours.\n", rtime);
! 	}
!       else
! 	{
! 	  fprintf (msg, "\n\tThe response time is %d business hours.\n", rtime);
! 	}
        if (len)
  	fprintf (msg, "\tA first analysis should be sent before: %s\n\n", buf);
      }
***************
*** 806,812 ****
      punt (1, "%s: cannot open mail agent.", program_name);
    fprintf (msg, "To: %s\n", responsible->alias);
  
!   if (find_submitter (&submitter, field_value (SUBMITTER)) == -1)
      punt (0, "%s: PR %s has a Submitter field of `%s', which is invalid.",
  	  program_name, field_value (NUMBER), field_value (SUBMITTER));
    else
--- 1289,1295 ----
      punt (1, "%s: cannot open mail agent.", program_name);
    fprintf (msg, "To: %s\n", responsible->alias);
  
!   if (get_submitter_info (&submitter, field_value (SUBMITTER)) == -1)
      punt (0, "%s: PR %s has a Submitter field of `%s', which is invalid.",
  	  program_name, field_value (NUMBER), field_value (SUBMITTER));
    else
***************
*** 892,895 ****
--- 1375,2664 ----
    unblock_signals ();
  
    return bug_number;
+ }
+ /*
+ * Checks whether the subj. is in pattern
+ *
+ * [ \t]+[tT]ecnical[ \t]+[sS]upport[ \t]+[rR]ating[ \t\n]+
+ */
+ int check_if_technical_support_rating()
+ {
+   int return_value = 0;
+   char *subj, *str, *str1;
+ 
+   subj = header_value (SUBJECT);
+ 
+   if (*subj == '\0')
+     return 0;
+ 
+   str = strdup (subj);
+   str1 = strtok (str, " \t");
+   if (str1 && (str1[0] = toupper (str1[0])) && !strcmp (str1, "Technical")) {
+     char *str2;
+     str2 = strtok (NULL, " \t");
+     if (str2 && (str2[0] = toupper (str2[0])) && !strcmp (str2, "Support")) {
+       char *str3;
+       str3 = strtok (NULL, " \t\n");
+       if (str3 && (str3[0] = toupper (str3[0])) && !strcmp (str3, "Rating")) {
+           return_value = 1;
+       }
+     }
+   }
+ 
+   free (str);
+   return return_value;
+ }
+ static void reply_to_rating_report	PARAMS((
+   int error_code,
+   char *submitter,
+   char *bug_number,
+   char *bug_group
+ ));
+ /*
+ *  Error code:
+ *    0 - Ok
+ *    1 - invalid submitter
+ *    2 - invalid bug number
+ *    3 - inavlid category
+ *    4 - invalid state
+ *    5 - invalid quality
+ *    6 - invalid speed
+ *    7 - invalid courtesy
+ */
+ static void reply_to_rating_report (error_code, submitter, bug_number,
+     bug_group)
+   int error_code;
+   char *submitter;
+   char *bug_number;
+   char *bug_group;
+ {
+   FILE *msg;
+   log ((f, "error in rating rep %d\n", error_code));
+ 
+   msg = open_mail_file ();
+   if (msg == (FILE *)NULL)
+     punt (1, "%s: cannot open mail agent.", program_name);
+ 
+   block_signals ();
+ 
+   fprintf (msg, "From: %s\n", gnats_addr);
+   fprintf (msg, "To: %s\n", submitter);
+   fprintf (msg, "Subject: Reply to rating report for %s/%s\n",
+     bug_group && *bug_group?
+       bug_group:
+       "unknown-category",
+     bug_number);
+ 
+   fprintf (msg, "Reply-To: %s\n", gnats_addr);
+ 
+   switch (error_code)
+     {
+       case 0:
+ 	fprintf (msg, "\nThank you very much for your rating report.\n");
+ 	fprintf (msg, "It will help us to improve our technical support\n");
+ 	fprintf (msg, "service.\n");
+ 	break;
+       case 1:
+ 	fprintf (msg, "\nInvalid \">Submitter-Id:\" field has been supplied\n");
+ 	fprintf (msg, "in your rating report.\n");
+ 	fprintf (msg, "Please, be so kind to check and correct the \">Submitter-Id:\" field\n");
+ 	fprintf (msg, "and thereafter resend your rating report.\n\n");
+ 	fprintf (msg, "Note: only the problem report initiator is able to\n");
+ 	fprintf (msg, "submit rating report for the problem report.\n");
+ 	break;
+       case 2:
+ 	fprintf (msg, "\nInvalid problem report number has been supplied\n");
+ 	fprintf (msg, "in your rating report.\n");
+ 	fprintf (msg, "Please, be so kind to check and correct the problem\n");
+ 	fprintf (msg, "report number and thereafter resend your rating report.\n");
+ 	break;
+       case 3:
+ 	fprintf (msg, "\nInvalid category has been supplied in your\n");
+ 	fprintf (msg, "in your rating report.\n");
+ 	fprintf (msg, "Please, be so kind to check and correct the category and\n");
+ 	fprintf (msg, "thereafter resend your rating report.\n");
+ 	break;
+       case 4:
+ 	fprintf (msg, "The problem report is not in \"closed\" state.\n");
+ 	fprintf (msg, "Hence, your rating report can not be accepted now.\n");
+ 	fprintf (msg, "Please, wait until the problem report will be closed.\n");
+ 	fprintf (msg, "and then be so kind to resend your rating report.\n");
+ 	break;
+       case 5:
+ 	fprintf (msg, "\nInvalid \">Response-Quality:\" field has been supplied\n");
+ 	fprintf (msg, "in your rating report.\n");
+ 	fprintf (msg, "Please, be so kind to check and correct the \">Response-Quality:\" field\n");
+ 	fprintf (msg, "and thereafter resend your rating report.\n");
+ 	break;
+       case 6:
+ 	fprintf (msg, "\nInvalid \">Response-Speed:\" field has been supplied\n");
+ 	fprintf (msg, "in your rating report.\n");
+ 	fprintf (msg, "Please, be so kind to check and correct the \">Response-Speed:\" field\n");
+ 	fprintf (msg, "and thereafter resend your rating report.\n");
+ 	break;
+       case 7:
+ 	fprintf (msg, "\nInvalid \">Response-Courtesy:\" field has been supplied\n");
+ 	fprintf (msg, "in your rating report.\n");
+ 	fprintf (msg, "Please, be so kind to check and correct the \">Response-Courtesy:\" field\n");
+ 	fprintf (msg, "and thereafter resend your rating report.\n");
+ 	break;
+     }
+ 
+   fprintf (msg, "\n");
+ 
+   fprintf (msg, "%s\t%s\n", pr[NUMBER].name, bug_number);
+   fprintf (msg, "%s\t%s\n", pr[CATEGORY].name, bug_group);
+   fprintf (msg, "%s\t%s\n", pr[SUBMITTER].name, submitter);
+ 
+   close_mail_file (msg);
+ 
+   unblock_signals ();
+ }
+ /*
+ * The procedure checks the validity of rating and if it is valid
+ * 
+ */
+ void store_rating(fp)
+   FILE *fp;
+ {
+   char *submitter;
+   char *bug_number_str;
+   char *bug_group;
+   char *responsible;
+ 
+   int bug_number;
+ 
+   Category category;
+ 
+   char *path, *pr_lock;
+   FILE *pr_file;
+ 
+   char *rate_path;
+   FILE *rating;
+ 
+   char *str;
+   int quality;
+   int speed;
+   int courtesy;
+   char *comments;
+ 
+   if (fp == (FILE *) NULL)
+     return;
+ 
+   read_pr (fp, 0);
+   fclose (fp);
+ 
+   log ((f, "rating rep\n%s\n%s\n%s\n%s\n%s\n%s\n%s",
+     field_value (NUMBER),
+     field_value (SUBMITTER),
+     field_value (CATEGORY),
+     field_value (RESPONSE_QUALITY),
+     field_value (RESPONSE_SPEED),
+     field_value (RESPONSE_COURTESY),
+     field_value (RATING_COMMENTS)
+   ));
+ 
+   submitter = field_value (SUBMITTER);
+   bug_number_str = field_value (NUMBER);
+   bug_group = field_value (CATEGORY);
+ 
+   if (!(submitter && *submitter))
+     {
+       char *submitter = header_value (REPLY_TO);
+       if (! (submitter && *submitter))
+ 	submitter = strtok (header_value (FROM), "\n");
+       reply_to_rating_report (1, submitter, bug_number_str, bug_group);
+       return;
+     }
+   submitter = (char*) alloca (strlen (field_value (SUBMITTER)) + 1);
+   strcpy (submitter, field_value (SUBMITTER));
+ 
+   if (!(bug_number_str && *bug_number_str &&
+       (bug_number = atoi (bug_number_str))))
+     {
+       reply_to_rating_report (2, submitter, bug_number_str, bug_group);
+       return;
+     }
+   bug_number_str = (char*) alloca (strlen (field_value (NUMBER)) + 1);
+   strcpy (bug_number_str, field_value (NUMBER));
+ 
+   if (!(bug_group && *bug_group))
+     {
+       reply_to_rating_report (3, submitter, bug_number_str, bug_group);
+       return;
+     }
+   bug_group = (char*) alloca (strlen (field_value (CATEGORY)) + 1);
+   strcpy (bug_group, field_value (CATEGORY));
+ 
+   memset ((void *) &category, 0, sizeof (Category));
+ 
+   if (find_category (&category, bug_group) == -1 ||
+       !strcmp (category.key, PENDING))
+     {
+       reply_to_rating_report (3, submitter, bug_number_str, bug_group);
+       return;
+     }
+   path = (char*) alloca (strlen (gnats_root) +
+     strlen (bug_group) + strlen (bug_number_str) + 3);
+   sprintf (path, "%s/%s/%s", gnats_root, bug_group, bug_number_str);
+ 
+   log ((f, "PR path %s\n", path));
+   if (access (path, F_OK) != 0)
+     {
+       reply_to_rating_report (2, submitter, bug_number_str, bug_group);
+       return;
+     }
+ 
+   pr_lock = (char*) alloca (strlen (path) + 6);
+   sprintf (pr_lock, "%s.lock", path);
+ 
+   if (access (pr_lock, R_OK) == 0)
+     {
+       log ((f, "lock found wait for the next trial\n"));
+       unlock_gnats ();
+       exit (1);
+     }
+ 
+   str = field_value (RESPONSE_QUALITY);
+   if (!(str && *str))
+     {
+       reply_to_rating_report (5, submitter, bug_number_str, bug_group);
+       return;
+     }
+   if (!strcmp (str, "Excellent"))
+     quality = 5;
+   else if (!strcmp (str, "Good"))
+     quality = 4;
+   else if (!strcmp (str, "So-so"))
+     quality = 3;
+   else if (!strcmp (str, "Bad"))
+     quality = 2;
+   else if (!strcmp (str, "Awfully"))
+     quality = 1;
+   else
+     {
+       reply_to_rating_report (5, submitter, bug_number_str, bug_group);
+       return;
+     }
+ 
+   str = field_value (RESPONSE_SPEED);
+   if (!(str && *str))
+     {
+       reply_to_rating_report (6, submitter, bug_number_str, bug_group);
+       return;
+     }
+   if (!strcmp (str, "Instantaneous"))
+     speed = 5;
+   else if (!strcmp (str, "Quick"))
+     speed = 4;
+   else if (!strcmp (str, "Rather-quick"))
+     speed = 3;
+   else if (!strcmp (str, "Slow"))
+     speed = 2;
+   else if (!strcmp (str, "Very-slow"))
+     speed = 1;
+   else
+     {
+       reply_to_rating_report (6, submitter, bug_number_str, bug_group);
+       return;
+     }
+ 
+   str = field_value (RESPONSE_COURTESY);
+   if (!(str && *str))
+     {
+       reply_to_rating_report (7, submitter, bug_number_str, bug_group);
+       return;
+     }
+   if (!strcmp (str, "Courteous"))
+     courtesy = 5;
+   else if (!strcmp (str, "Normal"))
+     courtesy = 3;
+   else if (!strcmp (str, "Not-courteous"))
+     courtesy = 1;
+   else
+     {
+       reply_to_rating_report (7, submitter, bug_number_str, bug_group);
+       return;
+     }
+ 
+   str = field_value (RATING_COMMENTS);
+   if (!(str && *str))
+     {
+       comments = (char*) alloca (1);
+       *comments = '\0';
+     }
+   else
+    {
+      comments = (char*) alloca (strlen (str) + 1);
+      strcpy (comments, str);
+    }
+ 
+   pr_file = fopen (path, "r");
+   if (pr_file == NULL)
+     {
+       punt (0, "Can't open PR %s.", path);
+       return;
+     }
+ 
+   read_pr (pr_file, 0);
+   fclose (pr_file);
+ 
+   if (!( field_value (STATE) && *field_value (STATE) &&
+       !strcmp (field_value (STATE), "closed")))
+     {
+       reply_to_rating_report (4, submitter, bug_number_str, bug_group);
+       return;
+     }
+   if (field_value (SUBMITTER) && strcmp (submitter, field_value (SUBMITTER)))
+     {
+       reply_to_rating_report (1, submitter, bug_number_str, bug_group);
+       return;
+     }
+ 
+   log ((f, "Ok-fields are checked\n"));
+   if (comments && *comments)
+     set_field (RATING_COMMENTS, comments);
+ 
+   create_report (path, 1);
+ 
+   log ((f, "Ok-report updated\n"));
+   rate_path = (char*) alloca (strlen (gnats_root) + strlen (bug_group) + 
+     + strlen (RATING) + 2);
+   sprintf (rate_path, "%s/%s%s", gnats_root, bug_group, RATING);
+ 
+   log ((f, "rating path %s\n", rate_path));
+   if (access (rate_path, F_OK) == 0)
+     {
+       int success = 0;
+       FILE *tmp_file;
+       char buf[STR_MAX];
+       char tmp_filename[PATH_MAX];
+ 
+       log ((f, "file %s exists\n", rate_path));
+       if (tmpnam (tmp_filename) == NULL)
+ 	{
+ 	  punt (0, "tmpnam failed in file-pr");
+ 	  return;
+ 	}
+       tmp_file = fopen (tmp_filename, "w+");
+       if (tmp_file == NULL)
+ 	{
+ 	  punt (0, "fopen w+ failed in file-pr");
+ 	  return;
+ 	}
+       rating = fopen (rate_path, "r");
+       if (rating == NULL)
+ 	{
+ 	  fclose (tmp_file);
+ 	  unlink (tmp_filename);
+ 	  punt (0, "fopen r failed in file-pr");
+ 	  return;
+ 	}
+       log ((f, "files are opened\n"));
+       while (fgets (buf, STR_MAX, rating) != NULL)
+ 	{
+ 	  char *str;
+ 	  log ((f, "string %s is read\n", buf));
+ 	  if (buf[0] == '#')
+ 	    {
+ 	      if (fputs (buf, tmp_file) == EOF)
+ 		{
+ 		  punt (0, "fputs failed in file-pr");
+ 		  success = -1;
+ 		  break;
+ 		}
+ 	      continue;
+ 	    }
+ 	  str = strchr (buf, ':');
+ 	  if (!str)
+ 	    continue;
+ 	  else
+ 	    *str = '\0';
+ 	  log ((f, "comp %s %s\n", str, bug_number_str));
+ 	  if (strcmp (buf, bug_number_str))
+ 	    {
+ 	      *str = ':';
+ 	      log ((f, buf));
+ 	      if (fputs (buf, tmp_file) == EOF)
+ 		{
+ 		  punt (0, "fputs failed in file-pr");
+ 		  success = -1;
+ 		  break;
+ 		}
+ 	      continue;
+ 	    }
+ 	  else
+ 	    {
+ 	      char *new_ptr;
+ 	      *str = ':';
+ 	      success = 1;
+ 	      new_ptr = strchr (str+1, ':');
+ 	      if (new_ptr)
+ 		new_ptr = strchr (new_ptr + 1, ':');
+ 	      if (new_ptr)
+ 		{
+ 		  *new_ptr = '\0';
+ 		  fprintf (tmp_file, "%s:%d:%d:%d\n",
+ 		    buf, quality, speed, courtesy);
+ 		}
+               else
+ 		{
+ 		  time_t n_time;
+ 		  struct tm *n_tm;
+ 		  time (&n_time);
+ 		  n_tm = localtime (&n_time);
+ 		  fprintf (tmp_file, "%s:%s:%d/%d/%d:%d:%d:%d\n",
+ 		    bug_number_str, field_value (RESPONSIBLE),
+ 		    n_tm->tm_year + 1900, n_tm->tm_mon, n_tm->tm_mday,
+ 		    quality, speed, courtesy);
+ 		}
+ 	    }
+ 	}
+       if (success == 0)
+ 	{
+ 	  time_t n_time;
+ 	  struct tm *n_tm;
+ 	  time (&n_time);
+ 	  n_tm = localtime (&n_time);
+ 	  fprintf (tmp_file, "%s:%s:%d/%d/%d:%d:%d:%d\n",
+ 	    bug_number_str, field_value (RESPONSIBLE),
+ 	    n_tm->tm_year + 1900, n_tm->tm_mon, n_tm->tm_mday,
+ 	    quality, speed, courtesy);
+ 	 }
+       fclose (rating);
+       fclose (tmp_file);
+       unlink (rate_path);
+       if (copy_file (tmp_filename, rate_path))
+ 	{
+ 	  punt (0, "file_copy failed in file pr");
+ 	}
+       unlink (tmp_filename);
+     }
+   else
+     {
+       time_t n_time;
+       struct tm *n_tm;
+       time (&n_time);
+       n_tm = localtime (&n_time);
+       log ((f, "rating %s was empty\n", rate_path));
+       rating = fopen (rate_path, "w+");
+       if (rating == NULL)
+ 	{
+ 	  punt (0, "Can't open rating %s.", rate_path);
+ 	  return;
+ 	}
+       fprintf (rating, "%s:%s:%d/%d/%d:%d:%d:%d\n",
+         bug_number_str, field_value (RESPONSIBLE),
+ 	n_tm->tm_year + 1900, n_tm->tm_mon, n_tm->tm_mday,
+         quality, speed, courtesy);
+       fclose (rating);
+     }
+    reply_to_rating_report (0, submitter, bug_number_str, bug_group);
+ }
+ /*
+ * Checks whether the subj. is in pattern
+ *
+ * [ \t]+[rR]equest[ \t]+[fF]or[ \t]+[pP]atch[ \t\n]+
+ */
+ int check_if_request_for_patch()
+ {
+   char *subj, *str, *str1;
+   int return_value = 0;
+ 
+   subj = header_value (SUBJECT);
+ 
+   if (*subj == '\0')
+     return 0;
+ 
+   str = strdup (subj);
+   str1 = strtok (str, " \t");
+   if (str1 && (str1[0] = toupper (str1[0])) && !strcmp (str1, "Request")) {
+     char *str2;
+     str2 = strtok (NULL, " \t");
+     if (str2 && (str2[0] = toupper (str2[0])) && !strcmp (str2, "For")) {
+       char *str3;
+       str3 = strtok (NULL, " \t\n");
+       if (str3 && (str3[0] = toupper (str3[0])) && !strcmp (str3, "Patch")) {
+         return_value = 1;
+       }
+     }
+   }
+ 
+   free (str);
+   return return_value;
+ }
+ /*
+ *  Reports errors to submitter.
+ *    1 - invalid number
+ *    2 - invalid category
+ *    3 - invalid release
+ *    4 - submitter-id is not set
+ */
+ static void reply_to_patch_request (error_code)
+   int error_code;
+ {
+   FILE *msg;
+ 
+   char *bug_number;
+   char *bug_group;
+   char *release;
+   char *submitter;
+   log ((f, "error in req patch %d\n", error_code));
+ 
+   bug_number = field_value (NUMBER);
+   bug_group = field_value (CATEGORY);
+   release = field_value (RELEASE);
+   submitter = field_value (SUBMITTER);
+ 
+ 
+   msg = open_mail_file ();
+   if (msg == (FILE *)NULL)
+     punt (1, "%s: cannot open mail agent.", program_name);
+ 
+   block_signals ();
+ 
+   fprintf (msg, "From: %s\n", gnats_addr);
+ 
+   if (!(submitter && *submitter))
+     {
+       char *reply_to = header_value (REPLY_TO);
+       if (! (reply_to && *reply_to))
+ 	reply_to = header_value (FROM);
+       fprintf(msg, "To: %s", reply_to);
+     }
+   else
+     fprintf (msg, "To: %s\n", submitter);
+ 
+   fprintf (msg, "Subject: Reply to patch request for %s/%s\n",
+     bug_group && *bug_group?
+       bug_group:
+       "unknown-category",
+     bug_number);
+ 
+   fprintf (msg, "Reply-To: %s\n", gnats_addr);
+ 
+   if (*header_value (DATE))
+     fprintf (msg, "In-Reply-To: Your message of %s\t%s",
+       header_value (DATE), header_value (MSG_ID));
+ 
+   switch (error_code)
+     {
+       case 1:
+ 	fprintf (msg, "\nInvalid problem report number has been supplied\n");
+ 	fprintf (msg, "in your request for patch.\n");
+ 	fprintf (msg, "Please, check and correct the problem report number\n");
+ 	fprintf (msg, "and thereafter repeat your request.\n");
+ 	break;
+       case 2:
+ 	fprintf (msg, "\nInvalid category has been supplied in your\n");
+ 	fprintf (msg, "request for patch.\n");
+ 	fprintf (msg, "Please, check and correct the category and\n");
+ 	fprintf (msg, "thereafter repeat your request.\n");
+ 	break;
+       case 3:
+ 	fprintf (msg, "\nInvalid release has been supplied in your\n");
+ 	fprintf (msg, "request for patch.\n");
+ 	fprintf (msg, "Please, fill the field out with the release id\n");
+ 	fprintf (msg, "you have and repeat your request.\n");
+ 	break;
+       case 4:
+ 	fprintf (msg, "\nYou has not set the field >Submitter-Id: in your\n");
+ 	fprintf (msg, "request for patch.\n");
+ 	fprintf (msg, "Please, fill the field out with your e-mail address\n");
+ 	fprintf (msg, "and repeat your request.\n");
+ 	break;
+     }
+ 
+   fprintf (msg, "\n");
+ 
+   write_pr (msg, NUMBER);
+   write_pr (msg, CATEGORY);
+   write_pr (msg, RELEASE);
+   write_pr (msg, SUBMITTER);
+ 
+   close_mail_file (msg);
+ 
+   unblock_signals ();
+ }
+ /*
+ *  Checks whether the "release" is a valid release of the
+ *  "category". If it is so then it returns 1 otherwise 0.
+ */
+ static int valid_release		PARAMS((
+   Category *category,
+   char *release
+ ));
+ 
+ static int valid_release (category, release)
+   Category *category;
+   char *release;
+ {
+   char *local_rel;
+   char *str, *str1, *str2;
+   int return_value = 0;
+ 
+   if (!(category && category->releases && *category->releases &&
+       release && *release))
+     return 0;
+ 
+   log ((f, "release %s is in %s?\n",
+     release, category->releases));
+ 
+   local_rel = (char*) xmalloc (strlen (category->releases) + 1);
+   strcpy (local_rel, category->releases);
+ 
+   str = strtok (local_rel, "|");
+   while (str)
+     {
+       str1 = str;
+       while (*str1 && isspace (*str1))
+ 	str1++;
+       str2 = str1;
+       while (*str2 && !isspace (*str2))
+ 	str2++;
+       *str2 = '\0';
+       if (!strcmp (str1, release))
+ 	{
+ 	  return_value = 1;
+ 	  break;
+ 	}
+       str = strtok (NULL, "|");
+     }
+ 
+   xfree (local_rel);
+   log ((f, "release %s is %d in %s\n",
+     release, return_value, category->releases));
+   return return_value;
+ }
+ static void reply_to_patch_request_after
+ 				PARAMS((
+   int error_code,
+   char *submitter,
+   char *bug_number_str,
+   char *bug_group,
+   char *release
+ ));
+ 
+ static void reply_to_patch_request_after (
+   error_code, submitter, bug_number_str, bug_group, release)
+   int error_code;
+   char *submitter;
+   char *bug_number_str;
+   char *bug_group;
+   char *release;
+ {
+   FILE *msg;
+ 
+   if (error_code == 2)
+     {
+       msg = open_mail_file ();
+       if (msg == (FILE *)NULL)
+ 	punt (1, "%s: cannot open mail agent.", program_name);
+ 
+       block_signals ();
+ 
+       fprintf (msg, "From: %s\n", gnats_addr);
+       fprintf (msg, "To: %s\n", gnats_admin);
+       fprintf (msg, "Subject: %s\n", "Problem with patch.");
+       fprintf (msg, "Reply-To: %s\n", submitter);
+ 
+       fprintf (msg, "%s\t%s\n", pr[NUMBER].name, bug_number_str);
+       fprintf (msg, "%s\t%s\n", pr[CATEGORY].name, bug_group);
+       fprintf (msg, "%s\t%s\n", pr[RELEASE].name, release);
+       fprintf (msg, "%s\t%s\n", pr[SUBMITTER].name, submitter);
+ 
+       fprintf (msg, "\n");
+       fprintf (msg, "A problem with patch. May be it is not available.\n");
+       fprintf (msg, "Please, make sure that it is Ok. If it is not a case\n");
+       fprintf (msg, "then ask developers or maintainers.\n");
+       fprintf (msg, "Report the result to submitter. His address is in the\n");
+       fprintf (msg, "\"Reply-To:\" field.\n");
+       fprintf (msg, "\n");
+ 
+       close_mail_file (msg);
+ 
+       unblock_signals ();
+     }
+ 
+   msg = open_mail_file ();
+ 
+   if (msg == (FILE *)NULL)
+     punt (1, "%s: cannot open mail agent.", program_name);
+ 
+   block_signals ();
+ 
+   fprintf (msg, "From: %s\n", gnats_addr);
+   fprintf (msg, "To: %s\n", submitter);
+   fprintf (msg, "Subject: Reply to patch request for %s/%s\n",
+     bug_group, bug_number_str);
+ 
+   fprintf (msg, "Reply-To: %s\n", gnats_addr);
+ 
+   fprintf (msg, "%s\t%s\n", pr[NUMBER].name, bug_number_str);
+   fprintf (msg, "%s\t%s\n", pr[CATEGORY].name, bug_group);
+   fprintf (msg, "%s\t%s\n", pr[RELEASE].name, release);
+   fprintf (msg, "%s\t%s\n", pr[SUBMITTER].name, submitter);
+ 
+   fprintf (msg, "\n");
+ 
+   if (error_code == 1)
+     {
+       fprintf (msg, "\nYou request the patch for the problem report which\n");
+       fprintf (msg, "is not in \"closed\" or \"feedback\" state.\n");
+       fprintf (msg, "The patch is not available. Please, wait until\n");
+       fprintf (msg, "the problem report state will be changed to \"closed\"\n");
+       fprintf (msg, "or \"feedback\". Then you may request the patch.\n");
+     }
+   else if (error_code == 2)
+     {
+       fprintf (msg, "\nInternal problem has occured around the patch.\n");
+       fprintf (msg, "The incident has been reported to GNATS administrator\n");
+       fprintf (msg, "The GNATS administrator will find out the problem\n");
+       fprintf (msg, "and report the result to you.\n");
+     }
+   fprintf (msg, "\n");
+ 
+   close_mail_file (msg);
+ 
+   unblock_signals ();
+ }
+ void retrieve_patch(fp)
+   FILE *fp;
+ {
+   int bug_number;
+   char *bug_number_str;
+   char *bug_group;
+   char *release;
+   char *submitter;
+ 
+   Category category;
+ 
+   char *path;
+   char *pr_lock;
+ 
+   pid_t child;
+   char *str;
+ 
+   FILE *pr_file;
+ 
+   char *release_str;
+ 
+   if (fp == (FILE *) NULL)
+     return;
+ 
+   read_pr (fp, 0);
+   fclose (fp);
+ 
+   log ((f, "patch req\n%s\n%s\n%s\n%s\n",
+     field_value (NUMBER),
+     field_value (SUBMITTER),
+     field_value (CATEGORY),
+     field_value (RELEASE)
+   ));
+ 
+   submitter = field_value (SUBMITTER);
+   if (!(submitter && *submitter))
+     {
+       reply_to_patch_request (4);
+       return;
+     }
+   submitter = (char*) alloca (strlen (field_value (SUBMITTER)) + 1);
+   strcpy (submitter, field_value (SUBMITTER));
+ 
+   bug_number_str = field_value (NUMBER);
+   if (!(bug_number_str && *bug_number_str &&
+       (bug_number = atoi (bug_number_str))))
+     {
+       reply_to_patch_request (1);
+       return;
+     }
+   bug_number_str = (char*) alloca (strlen (field_value (NUMBER)) + 1);
+   strcpy (bug_number_str, field_value (NUMBER));
+ 
+   bug_group = field_value (CATEGORY);
+   if (!(bug_group && *bug_group))
+     {
+       reply_to_patch_request (2);
+       return;
+     }
+   bug_group = (char*) alloca (strlen (field_value (CATEGORY)) + 1);
+   strcpy (bug_group, field_value (CATEGORY));
+ 
+   memset ((void *) &category, 0, sizeof (Category));
+ 
+   if (find_category (&category, bug_group) == -1 ||
+       !strcmp (category.key, PENDING))
+     {
+       reply_to_patch_request (2);
+       return;
+     }
+   path = (char*) alloca (strlen (gnats_root) +
+     strlen (bug_group) + strlen (bug_number_str) + 3);
+   sprintf (path, "%s/%s/%s", gnats_root, bug_group, bug_number_str);
+ 
+   log ((f, "PR path %s\n", path));
+   if (access (path, F_OK) != 0)
+     {
+       reply_to_patch_request (1);
+       return;
+     }
+ 
+   release = field_value (RELEASE);
+   if (!(release && *release &&
+       valid_release (&category, release)))
+     {
+       reply_to_patch_request (3);
+       return;
+     }
+   release = (char*) alloca (strlen (field_value (RELEASE)) + 1);
+   strcpy (release, field_value (RELEASE));
+ 
+   pr_lock = (char*) alloca (strlen (path) + 6);
+   sprintf (pr_lock, "%s.lock", path);
+ 
+   if (access (pr_lock, R_OK) == 0)
+     {
+       log ((f, "lock found wait for the next trial\n"));
+       unlock_gnats ();
+       exit (1);
+     }
+ 
+   pr_file = fopen (path, "r");
+   if (pr_file == NULL)
+     {
+       punt (0, "Can't open PR %s.", path);
+       reply_to_patch_request_after (2, submitter, bug_number_str,
+         bug_group, release);
+       return;
+     }
+ 
+   read_pr (pr_file, 0);
+   fclose (pr_file);
+ 
+   if (!( field_value (STATE) && *field_value (STATE) &&
+       (!strcmp (field_value (STATE), "closed") ||
+        !strcmp (field_value (STATE), "feedback")) ))
+     {
+       reply_to_patch_request_after (1, submitter, bug_number_str,
+ 	bug_group, release);
+       return;
+     }
+ 
+   release_str = (char*) alloca (strlen (release) + 1);
+   strcpy (release_str, release);
+ 
+   for (str = release_str; str = strchr (str, '.');)
+     *str = '-';
+ 
+   {
+     int si, sp;
+     char *init_rel;
+     char *patch_rel;
+     init_rel = (char*) alloca (strlen (release_str) + 9);
+     patch_rel = (char*)
+       alloca (strlen (bug_number_str) + 7);
+ 
+     sprintf (init_rel, "RELEASE-%s", release_str);
+     sprintf (patch_rel, "PATCH-%s", bug_number_str);
+     si = check_cvs_tag_presence (init_rel, bug_group);
+     sp = check_cvs_tag_presence (patch_rel, bug_group);
+ 
+     log ((f, "si %d sp %d\n", si, sp));
+     if (si != 0 || sp != 0)
+       {
+ 	reply_to_patch_request_after (2, submitter, bug_number_str,
+ 	  bug_group, release);
+ 	return;
+       }
+   }
+ 
+   if ((child = fork()) > 0)
+     {
+       int err;
+       wait (&err);
+       if (WIFEXITED (err) && WEXITSTATUS (err) != 0)
+ 	{
+ 	  log_msg (LOG_INFO, 1, "problem with", "send-patch");
+ 	  punt (0, "problem with send-patch for PR %s",
+ 	    bug_number_str);
+ 	  reply_to_patch_request_after (2, submitter, bug_number_str,
+ 	    bug_group, release);
+           log ((f, "parent %d\n", WEXITSTATUS (err)));
+ 	}
+       return;
+     }
+   else if (child == 0)
+     {
+       static char *safe_env[] =
+ 	{ "", "PATH=/bin:/usr/bin", NULL };
+       char *gnats_bin = (char*) alloca (strlen (bindir) + 12);
+ 
+       strcat (strcpy (gnats_bin, bindir), "/send-patch");
+ 
+       if (execle (gnats_bin, "send-patch",
+ 	  bug_number_str,
+ 	  release_str,
+ 	  bug_group,
+ 	  submitter,
+ 	  NULL, safe_env) < 0)
+ 	{
+ 	  punt (0, "%s: execle of gnats failed: %s\n",
+ 	    program_name, strerror (errno));
+ 	  log_msg (LOG_INFO, 1, "execle failed in", "file-pr");
+ 	  log ((f, "execle failed\n"));
+ 	  exit (1);
+ 	}
+     }
+   else
+     {
+       /* fork failed -> make another trial in a while */
+       log_msg (LOG_INFO, 1, "fork failed in", "file-pr");
+       punt (0, "fork failed in file-pr");
+       unlock_gnats ();
+       exit (1);
+     }
+ }
+ static void notify_responsible_about_follow_up (
+   responsible, category_notify, response_time,
+   expired, billing_status, has_new_fields
+ )
+   char *responsible;
+   char *category_notify;
+   int response_time;
+   struct tm *expired;
+   int billing_status;
+   int has_new_fields;
+ {
+   FILE *msg;
+   char *notify = NULL;
+   char *cnote;
+ 
+   /*
+   *  no new fields, no billing status change, no responsibles ->
+   *  -> no notification
+   */
+   if (!has_new_fields && billing_status != 1 && !responsible)
+     return;
+ 
+   cnote = append_notify (category_notify);
+   if (cnote)
+     {
+       notify = strdup (cnote);
+       xfree (cnote);
+     }
+   block_signals ();
+ 
+   msg = open_mail_file ();
+   if (msg == (FILE *)NULL)
+     punt (1, "can not open mail agent. ");
+ 
+ #ifdef DONT_USE_RESENT
+   {
+     char *f = header_value (FROM);
+     if (*f)
+       {
+ 	fprintf (msg, "From: %s", f);
+ 	fprintf (msg, "Reply-To: %s", f);
+       }
+   }
+   fprintf (msg, "To: %s\n", responsible);
+   if (notify)
+     fprintf (msg, "Cc: %s\n", notify);
+ 
+   if (*header_value (DATE))
+     fprintf (msg, "In-Reply-To: Your message of %s\t%s",
+ 	     header_value (DATE), header_value (MSG_ID));
+ 
+ #else /* ! DONT_USE_RESENT */
+   fprintf(msg, "Resent-From: %s (GNATS Management)\n", gnats_user);
+   fprintf(msg, "Resent-To: %s\n", responsible);
+   if (notify)
+     fprintf(msg, "Resent-Cc: %s\n", notify);
+   {
+     char *reply_to = header_value (REPLY_TO);
+     if (! (reply_to && *reply_to))
+       reply_to = header_value (FROM);
+     fprintf(msg, "Resent-Reply-To: %s, %s", gnats_addr, reply_to);
+   }
+   {
+     Header_Name i;
+     for (i = RETURN_PATH; i < NUM_HEADER_ITEMS; i++)
+       {
+ 	if (i == SUBJECT)
+ 	  continue;
+ 	if (*header_value(i))
+ 	  fprintf(msg, "%s%s", header_name(i), header_value(i));
+       }
+   }
+ #endif /* DONT_USE_RESENT */
+   {
+     char *subject = header_value (SUBJECT);
+     fprintf (msg, "Subject: Follow-up PR is received for %s/%s : %s",
+       field_value (CATEGORY), field_value (NUMBER), header_value (SUBJECT));
+   }
+ 
+   fprintf (msg, "\n");
+ 
+   if (billing_status == 1)
+     {
+       fprintf (msg, "\nProblem report %s has been converted into \n",
+ 	field_value (NUMBER));
+       fprintf (msg, "commercial technical support call.\n");
+     }
+ 
+   if (has_new_fields)
+     {
+       fprintf (msg, "\nA new information has been supplied in follow-up\n");
+       fprintf (msg, "PR for the problem report %s.\n", field_value (NUMBER));
+     }
+ 
+   if (flag_notify && response_time > 0 && expired)
+     {
+       char buf[GNATS_TIME_LENGTH];
+       int len = strftime (buf, GNATS_TIME_LENGTH, "%a %b %d %X %Z %Y", expired);
+       fprintf (msg, "\nThe response time is %d business hours.\n",
+ 	response_time);
+       if (len)
+ 	fprintf (msg, "A first analysis should be sent before: %s\n\n", buf);
+     }
+ 
+   write_pr (msg, NUM_PR_ITEMS);
+ 
+   close_mail_file (msg);
+ 
+   unblock_signals ();
+ }
+ static void reply_to_follow_up_pr (submitter, responsible, follow_date,
+     pr_num, status, has_new_fields)
+   char *submitter;
+   char *responsible;
+   char *follow_date;
+   int	pr_num;
+   FollowUpStatus status;
+   int has_new_fields;
+ {
+   FILE *msg;
+ 
+   if (!submitter || !*submitter)
+     return;
+ 
+   block_signals ();
+ 
+   msg = open_mail_file ();
+   if (msg == (FILE *)NULL)
+     punt (1, "%s: cannot open mail agent.", program_name);
+ 
+   fprintf (msg, "To: %s\n", submitter);
+ 
+   fprintf (msg, "From: %s\n", gnats_addr);
+ 
+   fprintf (msg, "Subject: Reply to follow-up problem report for %s/%d\n",
+ 	   strcmp (field_value (CATEGORY), PENDING)?
+ 	     field_value  (CATEGORY):
+ 	     "unknown-category",
+ 	   pr_num);
+ 
+   fprintf (msg, "Reply-To: %s", gnats_addr);
+ 
+   if (responsible && *responsible)
+     fprintf (msg, ", %s", responsible);
+ 
+   fprintf (msg, "\n");
+ 
+   fprintf (msg, "In-Reply-To: Your message of %s\n",
+     follow_date);
+ 
+   if (has_new_fields && status != NO_SUCH_PR &&
+       status != NO_SUCH_CATEGORY && status != NOT_OWNER)
+     {
+       fprintf (msg, "\nThank you very much for additional information\n");
+       fprintf (msg, "supplied in your follow-up problem report.\n");
+       fprintf (msg, "\n");
+     }
+ 
+   switch (status)
+     {
+       case NO_SUCH_PR:
+ 	fprintf (msg, "Sorry. The initial problem report %d for your\n",
+ 	  pr_num);
+ 	fprintf (msg, "follow-up problem report does not exist.\n");
+ 	break;
+       case NO_SUCH_CATEGORY:
+ 	fprintf (msg, "Sorry. The problem category %s for the your follow-up\n",
+ 	  field_value (CATEGORY));
+ 	fprintf (msg, "problem report does not exist.\n");
+ 	break;
+       case NOT_OWNER:
+ 	fprintf (msg, "Sorry. You have no permission to add follow-up\n");
+ 	fprintf (msg, "information to the problem report %d.\n",
+ 	  pr_num);
+ 	fprintf (msg, "You are not the owner for the problem report.\n");
+ 	break;
+       case NO_BILLING_INFO:
+ 	if (!has_new_fields)
+ 	  {
+ 	    fprintf (msg, "Sorry. Your follow-up problem report is empty.\n");
+ 	    fprintf (msg, "It has been ignored.\n");
+ 	  }
+ 	break;
+       case INVALID_BILLING_INFO:
+ 	fprintf (msg, "The billing information supplied in your follow-up\n");
+ 	fprintf (msg, "problem report is invalid.\n");
+ 	fprintf (msg, "The problem report %d has  not  been converted\n",
+ 	  pr_num);
+ 	fprintf (msg, "into commercial technical support call.\n");
+ 	break;
+       case VALID_BILLING_INFO:
+ 	fprintf (msg, "The billing information supplied in your follow-up\n");
+ 	fprintf (msg, "problem report is accepted.\n");
+ 	fprintf (msg, "The problem report %d has been successfully converted\n",
+ 	  pr_num);
+ 	fprintf (msg, "into commercial technical support call.\n");
+ 	break;
+       case DEFERRED_BILLING_INFO:
+ 	fprintf (msg, "The billing information supplied in your follow-up\n");
+ 	fprintf (msg, "problem report can not be verified immediately.\n\n");
+ 	fprintf (msg, "The problem report %d has  not  been converted\n",
+ 	  pr_num);
+ 	fprintf (msg, "into commercial technical support call.\n\n");
+ 	fprintf (msg, "Upon the final verification of the billing\n");
+ 	fprintf (msg, "information supplied in your follow-up problem\n");
+         fprintf (msg, "you will be notified the verification result.\n");
+ 	break;
+       case BILLING_INFO_HAS_BEEN_DEFERRED:
+ 	fprintf (msg, "You were trying to upgrade billing status of\n");
+ 	fprintf (msg, "the problem report which billing information is\n");
+ 	fprintf (msg, "still being verified.\n\n");
+ 	fprintf (msg, "Please, wait for the result of the current\n");
+ 	fprintf (msg, "verification. You will be notified the result\n");
+ 	fprintf (msg, "through e-mail message.\n");
+ 	break;
+       case ALREADY_UPGRADED:
+ 	fprintf (msg, "You were attempting to convert to commercial\n");
+ 	fprintf (msg, "technical support call the problem report that\n");
+ 	fprintf (msg, "is already a commercial call.\n");
+ 	break;
+     }
+ 
+   fprintf (msg, "\n");
+ 
+   if (has_new_fields || (status != NO_SUCH_PR &&
+       status != NO_SUCH_CATEGORY && status != NOT_OWNER))
+     {
+       write_pr (msg, NUMBER);
+       write_pr (msg, CATEGORY);
+       write_pr (msg, RESPONSIBLE);
+       write_pr (msg, SYNOPSIS);
+     }
+ 
+   close_mail_file (msg);
+ 
+   unblock_signals ();
+ }
+ /*
+ * checks the value of "str" if it is not empty and not default
+ * concatenates it to the field "type".
+ * return value:
+ *   0 - str is default or empty
+ *   1 - str is smth else
+ */
+ static int concatenate_field (type, str)
+   PR_Name type;
+   char *str;
+ {
+   time_t t;
+   char *str1, *date;
+ 
+   if (!str || !*str || pr[type].datatype != MultiText ||
+       (pr[type].default_value && !strcmp (str, pr[type].default_value)))
+     {
+       return 0;
+     }
+ 
+   str1 = str;
+   while (*str1 && isspace (*str1))
+     {
+       str1++;
+     }
+   /* blanks are ignored */
+   if (!*str1)
+     {
+       return 0;
+     }
+ 
+   t = time((time_t) 0);
+   date = (char *) alloca (GNATS_TIME_LENGTH);
+   strftime (date, GNATS_TIME_LENGTH, "%a, %d %b %y %T %Z\n", localtime(&t));
+ 
+   str1 = (char*) alloca (strlen (field_value (type)) + strlen (str) +
+     GNATS_TIME_LENGTH + 40);
+ 
+   sprintf (str1, "%s\nAppended on follow-up PR at %s\n%s",
+     field_value (type), date, str);
+   set_field (type, str1);
+   return 1;
+ }
+ /*
+ *  Runs check-tag-cvs and analyzes its return code.
+ *  check-tag-cvs return code:
+ *    0 - tag and module is Ok
+ *    1 - smth wrong
+ *
+ *  The function return code:
+ *    0 - tag and module is Ok
+ *    1 - tag or module is wrong
+ *   -1 - unable to run check-cvs-tag
+ */
+ static int check_cvs_tag_presence (tag, module)
+   char *tag;
+   char *module;
+ {
+   pid_t child;
+   if (!(tag && *tag && module && *module))
+     return 1;
+ 
+   if ((child = fork()) == 0)
+     {
+       static char *safe_env[] = { "", "PATH=/bin:/usr/bin", NULL };
+       char *gnats_bin = (char*) alloca (strlen (bindir) + 15);
+ 
+       strcat (strcpy (gnats_bin, bindir), "/check-cvs-tag");
+ 
+       if (execle (gnats_bin, "check-cvs-tag",
+           tag, module, NULL, safe_env) < 0)
+         {
+           punt (0, "%s: execle of gnats failed: %s\n",
+ 	    program_name, strerror (errno));
+ 	  punt (0, "\
+ Unable to check the presence of cvs tag %s for module %s\n\
+ Please, check the presence of libexec/gnats/check-cvs-tag\n\
+ utiltiy and its configuration. Also, please, check the tag\n\
+ availability. If this is not the case, contact developers\n\
+ or maintainers of %s.", tag, module, module);
+           log_msg (LOG_INFO, 1,
+             "execle failed for check-cvs-tag in", "file-pr");
+           log ((f, "execle failed\n"));
+           exit (-1);
+         }
+     }
+   else if (child > 0)
+     {
+       int err;
+       wait (&err);
+       switch (WEXITSTATUS (err))
+        {
+ 	 case 0:
+ 	   return 0;
+ 	 case 1:
+ 	   return 1;
+ 	 default:
+ 	   return -1;
+        }
+     }
+   else
+    {
+      log ((f, "fork failed\n"));
+      punt (0, "fork failed in file-pr");
+      return -1;
+    }
  }
Index: gnats-3.104-beta/gnats/files.c
diff -c gnats-3.104-beta/gnats/files.c:1.1.1.1 gnats-3.104-beta/gnats/files.c:1.7
*** gnats-3.104-beta/gnats/files.c:1.1.1.1	Thu Oct  9 21:37:29 1997
--- gnats-3.104-beta/gnats/files.c	Tue Oct 21 22:39:12 1997
***************
*** 26,31 ****
--- 26,50 ----
  static int get_adm_record		PARAMS((char*, char**, char*));
  static int next_record			PARAMS((char*, char**));
  
+ /* Find SITE in the submitters list, on failure return a stub.*/
+ int 
+ get_submitter_info (cptr, site)
+      Submitter *cptr;
+      char *site;
+ {
+ 	if (!site || !*site)
+ 	  return -1;
+ 	if (find_submitter (cptr, site)==-1)
+ 	{
+ 		cptr->key = (char*) strdup (site);
+ 		cptr->fullname = (char*) NULL;
+ 		cptr->type = (char*) NULL;
+ 		cptr->rtime = -1;
+ 		cptr->contact = gnats_admin;
+ 		cptr->notify = (char*) NULL;
+ 	}
+ 	return 0;
+ }
  /* Find SITE in the submitters list, copying it into CPTR.  */
  int 
  find_submitter (cptr, site)
***************
*** 93,98 ****
--- 112,119 ----
  
    memset (cptr, 0, sizeof (Category));
    
+   if (err >= 4)
+     cptr->releases = array[4];
    if (err >= 3)
      cptr->notify = array[3];
    if (err >= 2)
***************
*** 126,132 ****
  	    responsible->key = r->key;
  	    responsible->fullname = r->fullname;
  	    responsible->alias = r->alias;
- 	    responsible->authentic = r->authentic;
  	    return 0;
  	  }
      }
--- 147,152 ----
***************
*** 153,159 ****
        responsible->alias = array[2];
        responsible->fullname = array[1];
        responsible->key = array[0];
-       responsible->authentic = 1;
  
        xfree ((char *) array);
        return 0;
--- 173,178 ----
***************
*** 243,253 ****
  	  if (p != NULL)
  	    *p = '\0';
  	  responsible->fullname = (char *) strdup (passwd->pw_gecos);
- 	  responsible->authentic = 1;
  	}
        else {
  	responsible->fullname = strdup("");
- 	responsible->authentic = 0;
        }
      } 
  
--- 262,270 ----
***************
*** 383,386 ****
--- 400,606 ----
    if (category->notify && category->notify != gnats_admin)
      xfree (category->notify);
    return;
+ }
+ int
+ find_account(account, submitter)
+ 	Account	*account;
+ 	char	*submitter;
+ {
+   int	err;
+   char	*path;
+   char	**array;
+ 
+   if (account == NULL)
+     return -1;
+ 
+   path = (char*) alloca (PATH_MAX);
+   array = (char**) alloca (NUM_ACCOUNT_FIELDS * sizeof (char*));
+ 
+   sprintf (path, "%s/gnats-adm/%s", gnats_root, ACCOUNTS);
+ 
+   err = get_adm_record (path, array, submitter);
+   if (err == -1)
+     return err;
+ 
+   memset (account, 0, sizeof (Account)); 
+ 
+   if (err > 3)
+     err = 3;
+ 
+ #undef __DEB__
+ #ifdef __DEB__
+ #define  log(smth)	\
+ {FILE* f = fopen ("/tmp/file-pr.log", "a"); if(f){ fprintf smth; fclose(f);}}
+ #else
+ #define	log(smth)
+ #endif
+   log ((f, "fields found %d\n", err));
+   switch (err)
+     {
+       case 3:
+ 	account->products = array[3];
+        log ((f, "producta found %s\n", array[3]));
+       case 2:
+ 	account->rtime = atoi (array[2]);
+ 	xfree (array[2]);
+       case 1:
+ 	account->id = array[1];
+       case 0:
+ 	account->key = array[0];
+     }
+   if (account->rtime == 0)
+     account->rtime = -1;
+ 
+   return err;
+ }
+ /*
+ *  expands products and supercategories
+ *  replaces categories for expanded one
+ *  code:
+ *   1 - prod
+ *   2 - super
+ *   3 - cat
+ */
+ void	expand_products(categories, code)
+ 	char **categories;
+ 	int code;
+ {
+   char *path_cat = (char *) alloca (PATH_MAX);
+   char **array_cat =
+     (char **) alloca (NUM_CATEGORY_FIELDS * sizeof (char *));
+ 
+   char *path_pr = (char *) alloca (PATH_MAX);
+   char **array_pr =
+     (char **) alloca (NUM_PRODUCT_FIELDS * sizeof (char *));
+ 
+   char *path_sup = (char *) alloca (PATH_MAX);
+   char **array_sup =
+     (char **) alloca (NUM_SUPERCATEGORY_FIELDS * sizeof (char *));
+ 
+   int err;
+   char *str_back = strchr (*categories, '|');
+   char *str_back1;
+   char *str = strtok (*categories, "|");
+ 
+   int buf_size = 1024;		/* real "expanded" buffer size */
+   int buf_occupation = 1;       /* "exapnded" buffer occupation */
+   char *expanded = (char*) xmalloc (buf_size);
+ 
+   *expanded = '\0';
+   
+   log ((f, "expand %s %d\n", *categories, code));
+   sprintf (path_cat, "%s/gnats-adm/%s", gnats_root, CATEGORIES);
+   sprintf (path_pr, "%s/gnats-adm/%s", gnats_root, PRODUCTS);
+   sprintf (path_sup, "%s/gnats-adm/%s", gnats_root, SUPERCATEGORIES);
+ 
+   while (str)
+     {
+       char *str1 = str;
+       char *str2;
+       /* trim */ 
+       while (*str1 && isspace (*str1))
+ 	str1++;
+       if (!*str1)
+ 	break;
+       str2 = str1;
+       while (*str2 && !isspace (*str2))
+ 	str2++;
+       *str2 = '\0';
+ 
+       log ((f, "str1 %s\n", str1));
+       /* search in products */
+       if (code <= 1)
+ 	err = get_adm_record (path_pr, array_pr, str1);
+       else
+ 	err = -1;
+       if (err != -1)
+         {
+ 	  if (err > 0)
+ 	    {
+ 	      int sz;
+               char *str3 = (char*) xmalloc (strlen (array_pr[1]) + 1);
+ 	      log ((f, "ar[1] %s\n", array_pr[1]));
+ 	      strcpy (str3, array_pr[1]);
+ 	      log ((f, "product found\n"));
+ 	      expand_products (&str3, 2);
+ 	      sz = strlen (str3);
+ 	      if (buf_occupation + sz + 1 > buf_size)
+ 		{
+ 		   expanded = (char*) xrealloc (expanded, buf_size * 2);
+ 		   buf_size *= 2;
+ 		}
+ 	      if (buf_occupation != 1)
+ 		{
+ 	          strcat (expanded, "|");
+ 		  buf_occupation++;
+ 		}
+ 	      strcat (expanded, str3);
+ 	      buf_occupation += sz;
+ 	      xfree (str3);
+ 	    }
+ 	}
+       else
+ 	{
+ 	  if (code <= 2)
+             err = get_adm_record (path_sup, array_sup, str1);
+ 	  else
+ 	    err = -1;
+ 
+           if (err != -1)
+ 	    {
+ 	      if (err > 0)
+ 		{
+ 		  int sz;
+                   char *str3 = (char*) xmalloc (strlen (array_sup[1]) + 1);
+ 		  strcpy (str3, array_sup[1]);
+ 	          expand_products (&str3, 3);
+ 	          sz = strlen (str3);
+ 	          if (buf_occupation + sz + 1 > buf_size)
+ 		    {
+ 		      expanded = (char*) xrealloc (expanded, buf_size * 2);
+ 		      buf_size *= 2;
+ 		    }
+ 		  if (buf_occupation != 1)
+ 		    {
+ 	              strcat (expanded, "|");
+ 		      buf_occupation++;
+ 		    }
+ 		  strcat (expanded, str3);
+ 	          buf_occupation += sz;
+ 		  xfree (str3);
+ 		}
+ 	    }
+ 	  else
+ 	   {
+ 	     err = get_adm_record(path_cat, array_cat, str1);
+ 	     if (err != -1)
+ 	       {
+ 		 int sz;
+ 		 log ((f, "category %s is found\n", str1));
+ 	         sz = strlen (str1);
+ 	         if (buf_occupation + sz + 1 > buf_size)
+ 	           {
+ 		     expanded = (char*) xrealloc (expanded, buf_size * 2);
+ 		     buf_size *= 2;
+ 		   }
+ 		 if (buf_occupation != 1)
+ 		   {
+ 		     strcat (expanded, "|");
+ 		     buf_occupation++;
+ 		   }
+ 	         strcat (expanded, str1);
+ 	         buf_occupation += sz;
+ 	       }
+ 	   }
+ 	}
+       if (str_back == NULL)
+ 	break;
+       str_back1 = strchr (str_back + 1, '|');
+       str = strtok (str_back + 1, "|");
+       str_back = str_back1;
+     }
+ 
+   xfree (*categories);
+   *categories = expanded;
+   log ((f, "expand end %s %d\n", *categories, code));
  }
Index: gnats-3.104-beta/gnats/files.h
diff -c gnats-3.104-beta/gnats/files.h:1.1.1.1 gnats-3.104-beta/gnats/files.h:1.4
*** gnats-3.104-beta/gnats/files.h:1.1.1.1	Thu Oct  9 21:37:29 1997
--- gnats-3.104-beta/gnats/files.h	Tue Oct 21 22:39:12 1997
***************
*** 65,73 ****
    /* Other mail addresses to whom notification should be sent with bugs
       in this category.  */
    char *notify;
  } Category;
  
! #define NUM_CATEGORY_FIELDS 4
  
  /*  Format for the responsible file.  */
  typedef struct responsible_entry {
--- 65,76 ----
    /* Other mail addresses to whom notification should be sent with bugs
       in this category.  */
    char *notify;
+ 
+   /* "|" - separated list of available to cvs releases */
+   char *releases;
  } Category;
  
! #define NUM_CATEGORY_FIELDS 5
  
  /*  Format for the responsible file.  */
  typedef struct responsible_entry {
***************
*** 81,101 ****
       the address, assuming that it is a local user.  */
    char *alias;
  
-   /* Whether this entry is "authentic" or not.  I.e., if this is true,
-      then the entry indicates someone who was found either in the
-      "responsible" field or in the passwd file; if it's false, then
-      the entry indicates an entry that was constructed dynamically
-      (e.g., perhaps because the person left the company and is no
-      longer in the passwd file, but they still appear in the
-      Responsible field of some PRs so the value should be considered
-      valid). */
-   int authentic;
- 
    /* pointer to next record */
    struct responsible_entry *next;
  
  } Responsible;
  
  #define NUM_RESPONSIBLE_FIELDS 3
  
  #endif /* _files_h_ */
--- 84,134 ----
       the address, assuming that it is a local user.  */
    char *alias;
  
    /* pointer to next record */
    struct responsible_entry *next;
  
  } Responsible;
  
  #define NUM_RESPONSIBLE_FIELDS 3
+ 
+ /* Format for the accounts file. */
+ typedef struct account_entry {
+   /* submitter e-mail address */
+   char *key;
+ 
+   /* account-id */
+   char *id;
+ 
+   /* response time in business hours */
+   int rtime;
+ 
+   /* list of |-separated products;
+   *  leading and trailing blancs for every product are wiped out
+   */
+   char* products;
+ } Account;
+ 
+ #define NUM_ACCOUNT_FIELDS 4
+ 
+ /* Format of the products file. */
+ typedef struct product_entry {
+   /* product-id */
+   char *key;
+ 
+   /* list of |-separated categories and supercategories */
+   char *categories;
+ } Product;
+ 
+ #define NUM_PRODUCT_FIELDS 2
+ 
+ /* Format of the supercategories file */
+ typedef struct supercategories_entry {
+   /* supercategory-id */
+   char *key;
+ 
+   /* list of |-separated categories */
+   char *categories;
+ } SuperCategory;
  
+ #define NUM_SUPERCATEGORY_FIELDS 2
  #endif /* _files_h_ */
Index: gnats-3.104-beta/gnats/globals.h
diff -c gnats-3.104-beta/gnats/globals.h:1.1.1.1 gnats-3.104-beta/gnats/globals.h:1.4
*** gnats-3.104-beta/gnats/globals.h:1.1.1.1	Thu Oct  9 21:37:29 1997
--- gnats-3.104-beta/gnats/globals.h	Sun Oct 19 17:44:30 1997
***************
*** 95,107 ****
--- 95,117 ----
  extern char *check_if_reply		PARAMS((void));
  extern void append_report		PARAMS((FILE *, char *));
  
+ extern int check_if_technical_support_rating
+ 					PARAMS((void));
+ extern void store_rating		PARAMS((FILE*));
+ 
+ extern int check_if_request_for_patch	PARAMS((void));
+ extern void retrieve_patch		PARAMS((FILE*));
+ 
  /* in files.c */
  extern int find_category		PARAMS((Category*, char *));
+ extern int get_submitter_info		PARAMS((Submitter*, char *));
  extern int find_submitter		PARAMS((Submitter*, char *));
  extern int init_responsibles		PARAMS((void));
  extern void free_responsible		PARAMS((Responsible*));
  extern void free_category		PARAMS((Category *));
  extern void free_submitter		PARAMS((Submitter *));
+ extern int find_account			PARAMS((Account*, char*));
+ extern void expand_products		PARAMS((char**, int));
  
  /* in resp_lookup.c */
  extern Responsible* get_responsible		PARAMS((Category *));
Index: gnats-3.104-beta/gnats/gnats.h
diff -c gnats-3.104-beta/gnats/gnats.h:1.1.1.1 gnats-3.104-beta/gnats/gnats.h:1.2
*** gnats-3.104-beta/gnats/gnats.h:1.1.1.1	Thu Oct  9 21:37:29 1997
--- gnats-3.104-beta/gnats/gnats.h	Fri Oct 17 13:07:28 1997
***************
*** 137,142 ****
--- 137,145 ----
  
  /* Prototypes that are generic to many GNATS utilities.  */
  
+ /* define __PRIVATE_SID__ if you want to supress output of submitter-id */
+ #undef __PRIVATE_SID__
+ 
  #include "headers.h"
  #include "pr.h"
  #include "files.h"
***************
*** 265,270 ****
--- 268,276 ----
  /* in pr.c */
  extern void read_pr			PARAMS((FILE *, int));
  extern void write_pr			PARAMS((FILE *, PR_Name));
+ #ifdef __PRIVATE_SID__
+ extern void safe_write_pr		PARAMS((FILE *));
+ #endif
  extern void init_pr			PARAMS((void));
  extern void clean_pr			PARAMS((void));
  extern char *field_value		PARAMS((PR_Name));
Index: gnats-3.104-beta/gnats/gnatsd.c
diff -c gnats-3.104-beta/gnats/gnatsd.c:1.1.1.1 gnats-3.104-beta/gnats/gnatsd.c:1.2
*** gnats-3.104-beta/gnats/gnatsd.c:1.1.1.1	Thu Oct  9 21:37:29 1997
--- gnats-3.104-beta/gnats/gnatsd.c	Thu Oct 16 22:54:55 1997
***************
*** 120,126 ****
--- 120,128 ----
  
    { "LCAT", GNATS_lcat, FLAG_NEED_AUTHORIZATION },
    { "LRES", GNATS_lres, FLAG_NEED_AUTHORIZATION },
+ #ifndef __PRIVATE_SID__
    { "LSUB", GNATS_lsub, FLAG_NEED_AUTHORIZATION },
+ #endif
  
    /* Stop and start the database.  */
    { "LKDB", GNATS_lkdb, FLAG_NEED_AUTHORIZATION },
Index: gnats-3.104-beta/gnats/gnatsd.h
diff -c gnats-3.104-beta/gnats/gnatsd.h:1.1.1.1 gnats-3.104-beta/gnats/gnatsd.h:1.2
*** gnats-3.104-beta/gnats/gnatsd.h:1.1.1.1	Thu Oct  9 21:37:29 1997
--- gnats-3.104-beta/gnats/gnatsd.h	Thu Oct 16 22:54:55 1997
***************
*** 80,86 ****
--- 80,88 ----
  extern void GNATS_auth			PARAMS((int, char**));
  extern void GNATS_lcat			PARAMS((int, char**));
  extern void GNATS_lres			PARAMS((int, char**));
+ #ifndef __PRIVATE_SID__
  extern void GNATS_lsub			PARAMS((int, char**));
+ #endif
  extern void GNATS_load			PARAMS((int, char**));
  extern void GNATS_chst			PARAMS((int, char**));
  extern void GNATS_chre			PARAMS((int, char**));
Index: gnats-3.104-beta/gnats/lists.c
diff -c gnats-3.104-beta/gnats/lists.c:1.1.1.1 gnats-3.104-beta/gnats/lists.c:1.2
*** gnats-3.104-beta/gnats/lists.c:1.1.1.1	Thu Oct  9 21:37:29 1997
--- gnats-3.104-beta/gnats/lists.c	Thu Oct 16 22:54:55 1997
***************
*** 48,59 ****
--- 48,61 ----
  	strcat (outf, ".cat");
        infile = CATEGORIES;
      }
+ #ifndef __PRIVATE_SID__
    else if (type == LIST_SUBMITTERS)
      {
        if (outf)
  	strcat (outf, ".sub");
        infile = SUBMITTERS;
      }
+ #endif
    else if (type == LIST_RESPONSIBLE)
      {
        if (outf)
Index: gnats-3.104-beta/gnats/main.c
diff -c gnats-3.104-beta/gnats/main.c:1.1.1.1 gnats-3.104-beta/gnats/main.c:1.2
*** gnats-3.104-beta/gnats/main.c:1.1.1.1	Thu Oct  9 21:37:29 1997
--- gnats-3.104-beta/gnats/main.c	Mon Oct 13 01:24:14 1997
***************
*** 232,237 ****
--- 232,241 ----
        append_report (fp, pr_path);
        xfree (pr_path);
      }
+   else if (check_if_technical_support_rating())
+     store_rating(fp);
+   else if (check_if_request_for_patch())
+     retrieve_patch(fp);
    else
      gnats (fp);
  
Index: gnats-3.104-beta/gnats/mkdist.sh
diff -c gnats-3.104-beta/gnats/mkdist.sh:1.1.1.1 gnats-3.104-beta/gnats/mkdist.sh:1.2
*** gnats-3.104-beta/gnats/mkdist.sh:1.1.1.1	Thu Oct  9 21:37:29 1997
--- gnats-3.104-beta/gnats/mkdist.sh	Mon Oct 20 01:59:42 1997
***************
*** 1,6 ****
--- 1,8 ----
  #!/bin/sh
  # Create a send-pr distribution for a GNATS site.
  # Copyright (C) 1993, 1994, 1995 Free Software Foundation, Inc.
+ #
+ # Hacked by Sergey V. Vanskov (vanskov@aha.ru).
  # Contributed by Brendan Kehoe (brendan@cygnus.com).
  #
  # This file is part of GNU GNATS.
***************
*** 24,29 ****
--- 26,32 ----
  DATADIR=XDATADIRX
  
  FILES="COPYING README INSTALL MANIFEST send-pr.sh install-sid.sh \
+         upgrade-pr.sh request-patch.sh rating-pr.sh \
  	send-pr-el.in send-pr.texi send-pr.1 send-pr.info texinfo.tex \
  	categ.texi fields.texi s-usage.texi states.texi version.texi \
  	categories"
***************
*** 111,123 ****
  mandir = $(prefix)/man
  man1dir = $(mandir)/man1
  
! all: send-pr send-pr.el install-sid
  
  send-pr: send-pr.sh
  	sed -e 's,@DATADIR@,$(datadir),g' \
  	    -e 's,@SUBMITTER@,$(SUBMITTER),g' \
  	    -e 's/@DEFAULT_RELEASE@/$(RELEASE)/g' send-pr.sh > send-pr
  
  send-pr.el: send-pr-el.in
  	sed -e 's,@DATADIR@,$(datadir),g' \
  	    -e 's,@SUBMITTER@,$(SUBMITTER),g' \
--- 114,142 ----
  mandir = $(prefix)/man
  man1dir = $(mandir)/man1
  
! all: send-pr upgrade-pr request-patch rating-pr send-pr.el install-sid
  
  send-pr: send-pr.sh
  	sed -e 's,@DATADIR@,$(datadir),g' \
  	    -e 's,@SUBMITTER@,$(SUBMITTER),g' \
  	    -e 's/@DEFAULT_RELEASE@/$(RELEASE)/g' send-pr.sh > send-pr
  
+ upgrade-pr: upgrade-pr.sh
+ 	sed -e 's,@DATADIR@,$(datadir),g' \
+ 	    -e 's,@SUBMITTER@,$(SUBMITTER),g' \
+ 	    -e 's/@DEFAULT_RELEASE@/$(RELEASE)/g' upgrade-pr.sh > upgrade-pr
+ 
+ request-patch: request-patch.sh
+ 	sed -e 's,@DATADIR@,$(datadir),g' \
+ 	    -e 's,@SUBMITTER@,$(SUBMITTER),g' \
+ 	    -e 's/@DEFAULT_RELEASE@/$(RELEASE)/g' request-patch.sh > \
+ 	    request-patch
+ 
+ rating-pr: rating-pr.sh
+ 	sed -e 's,@DATADIR@,$(datadir),g' \
+ 	    -e 's,@SUBMITTER@,$(SUBMITTER),g' \
+ 	    -e 's/@DEFAULT_RELEASE@/$(RELEASE)/g' rating-pr.sh > rating-pr
+ 
  send-pr.el: send-pr-el.in
  	sed -e 's,@DATADIR@,$(datadir),g' \
  	    -e 's,@SUBMITTER@,$(SUBMITTER),g' \
***************
*** 133,139 ****
--- 152,164 ----
  	if [ -d $(prefix) ]; then true ; else mkdir $(prefix) ; fi
  	if [ -d $(bindir) ]; then true ; else mkdir $(bindir) ; fi
  	cp send-pr $(bindir)
+ 	cp upgrade-pr $(bindir)
+ 	cp request-patch $(bindir)
+ 	cp rating-pr $(bindir)
  	chmod 755 $(bindir)/send-pr
+ 	chmod 755 $(bindir)/upgrade-pr
+ 	chmod 755 $(bindir)/request-patch
+ 	chmod 755 $(bindir)/rating-pr
  	cp install-sid $(bindir)
  	chmod 755 $(bindir)/install-sid
  	-parent=`echo $(lispdir)|sed -e 's@/[^/]*$$@@'`; \
***************
*** 155,161 ****
  	chmod 644 $(man1dir)/send-pr.1
  
  clean:
! 	rm -f install-sid send-pr send-pr.el*
  
  __EOF__
  
--- 180,186 ----
  	chmod 644 $(man1dir)/send-pr.1
  
  clean:
! 	rm -f install-sid send-pr upgrade-pr request-patch rating-pr send-pr.el*
  
  __EOF__
  
Index: gnats-3.104-beta/gnats/nquery-pr.c
diff -c gnats-3.104-beta/gnats/nquery-pr.c:1.1.1.1 gnats-3.104-beta/gnats/nquery-pr.c:1.2
*** gnats-3.104-beta/gnats/nquery-pr.c:1.1.1.1	Thu Oct  9 21:37:29 1997
--- gnats-3.104-beta/gnats/nquery-pr.c	Thu Oct 16 22:54:55 1997
***************
*** 122,128 ****
--- 122,130 ----
    {"sql", 0, NULL, 'i'},
    {"state", 1, NULL, 's'},
    {"summary", 0, NULL, 'q'},
+ #ifndef __PRIVATE_SID__
    {"submitter", 1, NULL, 'S'},
+ #endif
    {"text", 1, NULL, 't'},
  #ifdef GNATS_RELEASE_BASED
    {"required-before", 1, NULL, 'u'},
***************
*** 132,138 ****
--- 134,142 ----
    {"arrived-after", 1, NULL, 'a'},
    {"list-categories", 0, NULL, 'j'},
    {"list-responsible", 0, NULL, 'k'},
+ #ifndef __PRIVATE_SID__
    {"list-submitters", 0, NULL, 'l'},
+ #endif
    {"version", 0, NULL, 'V'},
    {NULL, 0, NULL, 0}
  };
***************
*** 164,175 ****
--- 168,181 ----
  	    fprintf (stderr, "%s: writing `LRES'\n", program_name);
  	  fprintf (serv_write, "LRES\r\n");
  	}
+ #ifndef __PRIVATE_SID__
        else if (list_format == LIST_SUBMITTERS)
  	{
  	  if (debug)
  	    fprintf (stderr, "%s: writing `LSUB'\n", program_name);
  	  fprintf (serv_write, "LSUB\r\n");
  	}
+ #endif
        return;
      }
  
***************
*** 188,193 ****
--- 194,200 ----
        get_reply ();
      }
  #endif
+ #ifndef __PRIVATE_SID__
    if (info->submitter)
      {
        if (debug)
***************
*** 196,201 ****
--- 203,209 ----
        fprintf (serv_write, "SUBM %s\r\n", info->submitter);
        get_reply ();
      }
+ #endif
    if (info->responsible)
      {
        if (debug)
***************
*** 509,517 ****
--- 517,527 ----
  	  info->state = optarg;
  	  break;
  
+ #ifndef __PRIVATE_SID__
  	case 'S':
  	  info->submitter = optarg;
  	  break;
+ #endif
  
  	case 't':
  	  text_search = optarg;
***************
*** 551,560 ****
--- 561,572 ----
  	  lists++;
  	  break;
  
+ #ifndef __PRIVATE_SID__
  	case 'l':
  	  list_format = LIST_SUBMITTERS;
  	  lists++;
  	  break;
+ #endif
  
  #ifdef GNATS_RELEASE_BASED
  	case 'u':
Index: gnats-3.104-beta/gnats/pr-addr.c
diff -c gnats-3.104-beta/gnats/pr-addr.c:1.1.1.1 gnats-3.104-beta/gnats/pr-addr.c:1.2
*** gnats-3.104-beta/gnats/pr-addr.c:1.1.1.1	Thu Oct  9 21:37:29 1997
--- gnats-3.104-beta/gnats/pr-addr.c	Thu Oct  9 21:38:45 1997
***************
*** 95,101 ****
    if (p != (char *) NULL)
      *p = '\0';
    r = get_responsible_address (name);
!   if (r && (r->authentic || !strict))
      {
        if (full)
  	printf ("%s:%s:%s\n", r->key, r->fullname, r->alias);
--- 95,101 ----
    if (p != (char *) NULL)
      *p = '\0';
    r = get_responsible_address (name);
!   if (r)
      {
        if (full)
  	printf ("%s:%s:%s\n", r->key, r->fullname, r->alias);
Index: gnats-3.104-beta/gnats/pr.c
diff -c gnats-3.104-beta/gnats/pr.c:1.1.1.1 gnats-3.104-beta/gnats/pr.c:1.5
*** gnats-3.104-beta/gnats/pr.c:1.1.1.1	Thu Oct  9 21:37:29 1997
--- gnats-3.104-beta/gnats/pr.c	Wed Oct 22 20:27:21 1997
***************
*** 252,258 ****
      }
    else
      {
!       for (i = 0; i < NUM_PR_ITEMS; i++)
  	if (pr[i].value != NULL)
  	  {
  	    /* For multi-text lines, always send a newline after the
--- 252,259 ----
      }
    else
      {
!       /* rating fields are to be stored in another file */
!       for (i = 0; i < RESPONSE_QUALITY; i++)
  	if (pr[i].value != NULL)
  	  {
  	    /* For multi-text lines, always send a newline after the
***************
*** 287,292 ****
--- 288,297 ----
    pr[SUBMITTER].datatype = Text;
    pr[SUBMITTER].default_value = def_subm;
  
+   pr[BILLING_INFO].name = BILLING_INFO_STRING;
+   pr[BILLING_INFO].datatype = Text;
+   pr[BILLING_INFO].default_value = '\0';
+ 
    pr[ARRIVAL_DATE].name = ARRIVAL_DATE_STRING;
    pr[ARRIVAL_DATE].default_value = '\0';
    pr[ARRIVAL_DATE].datatype = Date;
***************
*** 372,377 ****
--- 377,405 ----
  
    pr[UNFORMATTED].name = UNFORMATTED_STRING;
    pr[UNFORMATTED].datatype = MultiText;
+ 
+ /* Rating fields */
+   pr[RESPONSE_QUALITY].name = RESPONSE_QUALITY_STRING;
+   pr[RESPONSE_QUALITY].datatype = Enum;
+   pr[RESPONSE_QUALITY].enum_values =
+     "undefined | Excellent | Good | So-so | Bad | Awfully";
+   pr[RESPONSE_QUALITY].default_value = "undefined";
+ 
+   pr[RESPONSE_SPEED].name = RESPONSE_SPEED_STRING;
+   pr[RESPONSE_SPEED].datatype = Enum;
+   pr[RESPONSE_SPEED].enum_values =
+     "undefined | Instantaneous | Quick | Rather-quick | Slow | Very-slow";
+   pr[RESPONSE_SPEED].default_value = "undefined";
+ 
+   pr[RESPONSE_COURTESY].name = RESPONSE_COURTESY_STRING;
+   pr[RESPONSE_COURTESY].datatype = Enum;
+   pr[RESPONSE_COURTESY].enum_values =
+    "undefined | Courteous | Normal | Not-courteous";
+   pr[RESPONSE_COURTESY].default_value = "undefined";
+ 
+   pr[RATING_COMMENTS].name = RATING_COMMENTS_STRING;
+   pr[RATING_COMMENTS].datatype = MultiText;
+ /* End of Rating Fields */
  }
  
  void
***************
*** 575,578 ****
--- 603,655 ----
       }
  
    return found;
+ }
+ 
+ #ifdef __PRIVATE_SID__
+ /*
+ *  Write the entire PR into the file passed in via FP except
+ *  for the private fields.
+ *  To be used in queries.
+ */
+ void 
+ safe_write_pr (fp)
+      FILE *fp;
+ {
+   register int i;
+   for (i = 0; i < NUM_PR_ITEMS; i++)
+     {
+       if (pr[i].value != NULL)
+ 	{
+ 	  /* For multi-text lines, always send a newline after the
+ 	      field name, and don't emit one after the value, since
+ 	      it will have the newline we need.  */
+ 	  if (pr[i].datatype == MultiText)
+ 	    {
+ 	      fprintf (fp, "%s%s%s", pr[i].name, ret, pr[i].value);
+ 	      MAYBE_NL(pr[i].value);
+ 	    }
+ 	  else
+ 	    if (pr[i] == SUBMITTER)
+ 	      /* hide submitter id */
+ 	      fprintf (fp, "%-16s ********%s", pr[i].name, ret);
+ 	    else
+ 	      fprintf (fp, "%-16s %s%s", pr[i].name, pr[i].value, ret);
+ 	}
+       else
+ 	fprintf (fp, "%s%s", pr[i].name, ret);
+     }
+ }
+ #endif
+ /*
+ *  Stupid procedure to overwork "bad" memory managment in edit.c
+ */
+ void
+ forget_pr ()
+ {
+   PR_Name i;
+ 
+   for (i = NUMBER; i < NUM_PR_ITEMS; i++)
+     {
+       pr[i].value = (char *) NULL;
+     }
  }
Index: gnats-3.104-beta/gnats/pr.h
diff -c gnats-3.104-beta/gnats/pr.h:1.1.1.1 gnats-3.104-beta/gnats/pr.h:1.5
*** gnats-3.104-beta/gnats/pr.h:1.1.1.1	Thu Oct  9 21:37:29 1997
--- gnats-3.104-beta/gnats/pr.h	Wed Oct 22 20:27:21 1997
***************
*** 38,43 ****
--- 38,44 ----
  #endif
    CLASS,               /* Supplied by Receiver */
    SUBMITTER,	       /* Supplied by Sender */
+   BILLING_INFO,        /* Suppiled by Sender; changed by Receiver */
    ARRIVAL_DATE,        /* Supplied by Receiver */
    LAST_MODIFIED,       /* Supplied by Receiver */
    ORIGINATOR,          /* Supplied by Sender */
***************
*** 49,54 ****
--- 50,62 ----
    FIX,		       /* Supplied by either */
    AUDIT_TRAIL,         /* Supplied by Receiver */
    UNFORMATTED,         /* Catch all field */
+ /* Rating fields */
+   RATING_COMMENTS,     /* Supplied by Sender (stored in pr) */
+ 
+   RESPONSE_QUALITY,    /* Supplied by Sender (not stored in pr) */
+   RESPONSE_SPEED,      /* Supplied by Sender (not stored in pr) */
+   RESPONSE_COURTESY,   /* Supplied by Sender (not stored in pr) */
+ /* End of rating fields */
    NUM_PR_ITEMS	/* this needs to stay in */
  } PR_Name;	
  
***************
*** 77,83 ****
--- 85,93 ----
  
    char *category;
    char *number;
+ #ifndef __PRIVATE_SID__
    char *submitter;
+ #endif
    char *responsible;
    char *state;
    char *confidential;
***************
*** 103,108 ****
--- 113,119 ----
  #endif
  #define CONFIDENTIAL_STRING 	">Confidential:"
  #define SUBMITTER_STRING 	">Submitter-Id:"
+ #define BILLING_INFO_STRING 	">Billing-Info:"
  #define ALTERNATE_SUBMITTER	">Customer-Id:"
  #define DESCRIPTION_STRING 	">Description:"
  #define ENVIRONMENT_STRING 	">Environment:"
***************
*** 122,126 ****
--- 133,143 ----
  #define SYNOPSIS_STRING 	">Synopsis:"
  #define UNFORMATTED_STRING 	">Unformatted:"
  #define FIX_STRING		">Fix:"
+ /* Rating Field Strings */
+ #define RATING_COMMENTS_STRING		">Rating-Comments:"
+ #define RESPONSE_QUALITY_STRING		">Response-Quality:"
+ #define RESPONSE_SPEED_STRING		">Response-Speed:"
+ #define RESPONSE_COURTESY_STRING	">Response-Courtesy:"
+ /* End Of Rating Field Strings */
  
  #endif /* _pr_h_ */
Index: gnats-3.104-beta/gnats/products
diff -c /dev/null gnats-3.104-beta/gnats/products:1.1
*** /dev/null	Thu Oct 23 13:27:08 1997
--- gnats-3.104-beta/gnats/products	Mon Oct 20 15:17:37 1997
***************
*** 0 ****
--- 1,16 ----
+ #		    Possible products for a PR.
+ #
+ # Any line which begins with a `#' is considered a comment, and GNATS
+ # will ignore it. 
+ #
+ # Each entry has the format:
+ #
+ # 	product:list-of-supercategories
+ #
+ # * `product' is the name of the group of supercategories
+ # * `list-of-supercategories' is "|" - separated list of supercategories
+ #
+ #
+ # Sample supercategories:
+ #
+ Yggdrassil:c-development|gnats
Index: gnats-3.104-beta/gnats/query-pr.c
diff -c gnats-3.104-beta/gnats/query-pr.c:1.1.1.1 gnats-3.104-beta/gnats/query-pr.c:1.2
*** gnats-3.104-beta/gnats/query-pr.c:1.1.1.1	Thu Oct  9 21:37:29 1997
--- gnats-3.104-beta/gnats/query-pr.c	Thu Oct 16 22:54:55 1997
***************
*** 58,64 ****
--- 58,66 ----
    {"sql", 0, NULL, 'i'},
    {"state", 1, NULL, 's'},
    {"summary", 0, NULL, 'q'},
+ #ifndef __PRIVATE_SID__
    {"submitter", 1, NULL, 'S'},
+ #endif
    {"text", 1, NULL, 't'},
  #ifdef GNATS_RELEASE_BASED
    {"required-before", 1, NULL, 'u'},
***************
*** 68,74 ****
--- 70,78 ----
    {"arrived-after", 1, NULL, 'a'},
    {"list-categories", 0, NULL, 'j'},
    {"list-responsible", 0, NULL, 'k'},
+ #ifndef __PRIVATE_SID__
    {"list-submitters", 0, NULL, 'l'},
+ #endif
    {"version", 0, NULL, 'V'},
    {NULL, 0, NULL, 0}
  };
***************
*** 162,170 ****
--- 166,182 ----
    memset (s, 0, sizeof (Index));
  
  #ifdef GNATS_RELEASE_BASED
+ #ifndef __PRIVATE_SID__
    while ((optc = getopt_long (argc, argv, "A:a:c:C:D:d:e:K:L:m:o:O:p:PQ:s:S:r:t:u:U:y:VFixhqRjkl",
  #else
+   while ((optc = getopt_long (argc, argv, "A:a:c:C:D:d:e:K:L:m:o:O:p:PQ:s:r:t:u:U:y:VFixhqRjk",
+ #endif
+ #else
+ #ifndef __PRIVATE_SID__
    while ((optc = getopt_long (argc, argv, "A:b:c:C:D:d:e:L:m:o:O:p:Ps:S:r:t:u:U:y:VFixhqRjkl",
+ #else
+   while ((optc = getopt_long (argc, argv, "A:b:c:C:D:d:e:L:m:o:O:p:Ps:r:t:u:U:y:VFixhqRjk",
+ #endif
  #endif
  			      long_options, (int *) 0)) != EOF)
      {
***************
*** 277,288 ****
  	  s->state = optarg;
  	  searching = 1;
  	  break;
! 
  	case 'S':
  	  s->submitter = optarg;
  	  searching = 1;
  	  break;
! 
  	case 't':
  	  text_search = optarg;
  	  break;
--- 289,300 ----
  	  s->state = optarg;
  	  searching = 1;
  	  break;
! #ifndef __PRIVATE_SID__
  	case 'S':
  	  s->submitter = optarg;
  	  searching = 1;
  	  break;
! #endif
  	case 't':
  	  text_search = optarg;
  	  break;
***************
*** 320,331 ****
  	  list_format = LIST_RESPONSIBLE;
  	  lists++;
  	  break;
! 
  	case 'l':
  	  list_format = LIST_SUBMITTERS;
  	  lists++;
  	  break;
! 
  #ifdef GNATS_RELEASE_BASED
  	case 'u':
  	  required_before = get_date (optarg, NULL);
--- 332,343 ----
  	  list_format = LIST_RESPONSIBLE;
  	  lists++;
  	  break;
! #ifndef __PRIVATE_SID__
  	case 'l':
  	  list_format = LIST_SUBMITTERS;
  	  lists++;
  	  break;
! #endif
  #ifdef GNATS_RELEASE_BASED
  	case 'u':
  	  required_before = get_date (optarg, NULL);
***************
*** 429,436 ****
  #ifdef GNATS_RELEASE_BASED
    fprintf (stderr, "\
  Usage: %s [-FhiPRqVx] [-C confidential] [-c category] [-d directory]\n\
!        [-e severity] [-m mtext] [-O originator] [-o outfile] [-p priority]\n\
!        [-L class] [-r responsible] [-S submitter] [-s state] [-t text]\n\
         [-y synopsis] [-A release] [--full] [--help] [--print-path] [--version]\n\
         [--summary] [--sql] [--skip-closed] [--category=category]\n\
         [--confidential=yes|no] [--directory=directory] [--output=outfile]\n\
--- 441,455 ----
  #ifdef GNATS_RELEASE_BASED
    fprintf (stderr, "\
  Usage: %s [-FhiPRqVx] [-C confidential] [-c category] [-d directory]\n\
!        [-e severity] [-m mtext] [-O originator] [-o outfile] [-p priority]\n"
! #ifndef __PRIVATE_SID__
!        "\
!        [-L class] [-r responsible] [-S submitter] [-s state] [-t text]\n"
! #else
!        "\
!        [-L class] [-r responsible] [-s state] [-t text]\n"
! #endif
!        "\
         [-y synopsis] [-A release] [--full] [--help] [--print-path] [--version]\n\
         [--summary] [--sql] [--skip-closed] [--category=category]\n\
         [--confidential=yes|no] [--directory=directory] [--output=outfile]\n\
***************
*** 438,461 ****
         [--responsible=person] [--release=release] [--restricted]\n\
         [--quarter=quarter] [--keywords=regexp]\n\
         [--required-before=date] [--required-after=date]\n\
!        [--arrived-before=date] [--arrived-after=date]\n\
         [--severity=severity] [--state=state] [--submitter=submitter]\n\
!        [--list-categories] [--list-responsible] [--list-submitters]\n\
         [--synopsis=synopsis] [--text=text] [--multitext=mtext] [PR] [PR]...\n",
  	   program_name);
  #else
    fprintf (stderr, "\
  Usage: %s [-FhiPRqVx] [-C confidential] [-c category] [-d directory]\n\
!        [-e severity] [-m mtext] [-O originator] [-o outfile] [-p priority]\n\
!        [-L class] [-r responsible] [-S submitter] [-s state] [-t text]\n\
         [-y synopsis] [-A release] [--full] [--help] [--print-path] [--version]\n\
         [--summary] [--sql] [--skip-closed] [--category=category]\n\
         [--confidential=yes|no] [--directory=directory] [--output=outfile]\n\
         [--originator=name] [--priority=level] [--class=class]\n\
         [--responsible=person] [--release=release] [--restricted]\n\
!        [--arrived-before=date] [--arrived-after=date]\n\
         [--severity=severity] [--state=state] [--submitter=submitter]\n\
!        [--list-categories] [--list-responsible] [--list-submitters]\n\
         [--synopsis=synopsis] [--text=text] [--multitext=mtext] [PR] [PR]...\n",
  	   program_name);
  #endif
--- 457,503 ----
         [--responsible=person] [--release=release] [--restricted]\n\
         [--quarter=quarter] [--keywords=regexp]\n\
         [--required-before=date] [--required-after=date]\n\
!        [--arrived-before=date] [--arrived-after=date]\n"
! #ifndef __PRIVATE_SID__
!        "\
         [--severity=severity] [--state=state] [--submitter=submitter]\n\
!        [--list-categories] [--list-responsible] [--list-submitters]\n"
! #else
!        "\
!        [--severity=severity] [--state=state]\n\
!        [--list-categories] [--list-responsible]\n"
! #endif/*__PRIVATE_SID__*/
!        "\
         [--synopsis=synopsis] [--text=text] [--multitext=mtext] [PR] [PR]...\n",
  	   program_name);
  #else
    fprintf (stderr, "\
  Usage: %s [-FhiPRqVx] [-C confidential] [-c category] [-d directory]\n\
!        [-e severity] [-m mtext] [-O originator] [-o outfile] [-p priority]\n"
! #ifndef __PRIVATE_SID__
!        "\
!        [-L class] [-r responsible] [-S submitter] [-s state] [-t text]\n"
! #else
!        "\
!        [-L class] [-r responsible] [-s state] [-t text]\n"
! #endif/*__PRIVATE_SID__*/
!        "\
         [-y synopsis] [-A release] [--full] [--help] [--print-path] [--version]\n\
         [--summary] [--sql] [--skip-closed] [--category=category]\n\
         [--confidential=yes|no] [--directory=directory] [--output=outfile]\n\
         [--originator=name] [--priority=level] [--class=class]\n\
         [--responsible=person] [--release=release] [--restricted]\n\
!        [--arrived-before=date] [--arrived-after=date]\n"
! #ifndef __PRIVATE_SID__
!        "\
         [--severity=severity] [--state=state] [--submitter=submitter]\n\
!        [--list-categories] [--list-responsible] [--list-submitters]\n"
! #else
!        "\
!        [--severity=severity] [--state=state]\n\
!        [--list-categories] [--list-responsible]\n"
! #endif/*__PRIVATE_SID__*/
!        "\
         [--synopsis=synopsis] [--text=text] [--multitext=mtext] [PR] [PR]...\n",
  	   program_name);
  #endif
Index: gnats-3.104-beta/gnats/query.c
diff -c gnats-3.104-beta/gnats/query.c:1.1.1.1 gnats-3.104-beta/gnats/query.c:1.5
*** gnats-3.104-beta/gnats/query.c:1.1.1.1	Thu Oct  9 21:37:29 1997
--- gnats-3.104-beta/gnats/query.c	Fri Oct 17 22:51:34 1997
***************
*** 304,310 ****
--- 304,312 ----
    if (skip_closed && strcasecmp (i->state, "closed") == 0) return 0;
    if (!s || !searching) return 1;
    return (!s->category || (regcmp (s->category, i->category) == 0))
+ #ifndef __PRIVATE_SID__
      && (!s->submitter || (regcmp (s->submitter, i->submitter) == 0))
+ #endif
      && (!s->responsible || (regcmp (s->responsible, i->responsible) == 0))
      && (!s->state || (regcmp (s->state, i->state) == 0))
      && (!s->confidential || (regcmp (s->confidential, i->confidential) == 0))
***************
*** 468,476 ****
--- 470,482 ----
  
    if (query_format & FORMAT_FULL)
      {
+ #ifndef __PRIVATE_SID__
        write_header (outfile, NUM_HEADER_ITEMS);
        fprintf (outfile, "\n");
        write_pr (outfile, NUM_PR_ITEMS);
+ #else
+       safe_write_pr (outfile);
+ #endif
      }
    else if (query_format & FORMAT_SQL)
      {
***************
*** 483,499 ****
  	       field_value (NUMBER), field_value (CATEGORY),
  	       disbar(field_value (SYNOPSIS)),
  	       field_value (CONFIDENTIAL));
!       fprintf (outfile, "|%1.1d|%1.1d|%-16.16s|%1.1d|%1.1d|%-16.16s",
  	       sql_types (field_value (SEVERITY), Severity),
  	       sql_types (field_value (PRIORITY), Priority),
  	       field_value (RESPONSIBLE),
  	       sql_types (field_value (STATE), State),
! 	       sql_types (field_value (CLASS), Class),
! 	       field_value (SUBMITTER));
        t = sql_time (field_value (ARRIVAL_DATE));
!       fprintf (outfile, "|%-16.16s|%-64.64s|%-64.64s|", t,
  	       disbar (field_value (ORIGINATOR)),
  	       field_value (RELEASE));
        xfree (t);
      }
    else if (query_format & FORMAT_SUMM)
--- 489,514 ----
  	       field_value (NUMBER), field_value (CATEGORY),
  	       disbar(field_value (SYNOPSIS)),
  	       field_value (CONFIDENTIAL));
! 
!       fprintf (outfile, "|%1.1d|%1.1d|%-16.16s|%1.1d|%1.1d",
  	       sql_types (field_value (SEVERITY), Severity),
  	       sql_types (field_value (PRIORITY), Priority),
  	       field_value (RESPONSIBLE),
  	       sql_types (field_value (STATE), State),
! 	       sql_types (field_value (CLASS), Class));
! #ifndef __PRIVATE_SID__
!       fprintf (outfile, "|%-64.64s", field_value (SUBMITTER));
! #else
!       fprintf (outfile, "|%-64.64s",
!         disbar (field_value(ORIGINATOR)));
! #endif
        t = sql_time (field_value (ARRIVAL_DATE));
!       fprintf (outfile, "|%-16.16s|%-64.64s|%-64.64s", t,
  	       disbar (field_value (ORIGINATOR)),
  	       field_value (RELEASE));
+ 
+       fprintf (outfile, "|%-10.10s|", field_value (BILLING_INFO));
+ 
        xfree (t);
      }
    else if (query_format & FORMAT_SUMM)
***************
*** 504,514 ****
        if (s)
  	*s = '\0';
        fprintf (outfile,
! 	       "%8s %-8.8s %-8.8s %-9.9s %-9.9s %-8.8s %-10.10s %s",
  	       pr[NUMBER].value, pr[RESPONSIBLE].value,
  	       pr[CATEGORY].value, pr[STATE].value, pr[SEVERITY].value,
! 	       pr[PRIORITY].value, pr[SUBMITTER].value,
! 	       pr[SYNOPSIS].value);
      }
    else
      {
--- 519,535 ----
        if (s)
  	*s = '\0';
        fprintf (outfile,
! 	       "%8s %-8.8s %-8.8s %-9.9s %-9.9s %-8.8s",
  	       pr[NUMBER].value, pr[RESPONSIBLE].value,
  	       pr[CATEGORY].value, pr[STATE].value, pr[SEVERITY].value,
! 	       pr[PRIORITY].value);
! #ifndef __PRIVATE_SID_
!       fprintf (outfile, " %-10.10s", pr[SUBMITTER].value);
! #else
!       fprintf (outfile, " %-10.10s", pr[ORIGINATOR].value);
! #endif
!       fprintf (outfile, " %s", pr[SYNOPSIS].value);
!       fprintf (outfile, " %-10.10s", pr[BILLING_INFO].value);
      }
    else
      {
***************
*** 531,540 ****
--- 552,574 ----
        write_pr (outfile, KEYWORDS);
        write_pr (outfile, DATE_REQUIRED);
  #endif
+ #ifndef __PRIVATE_SID__
        write_pr (outfile, SUBMITTER);
+ #else
+       {
+         char *str = (char*) xmalloc (strlen (field_value (SUBMITTER)) + 1);
+         strcpy (str, field_value (SUBMITTER));
+         set_field (SUBMITTER, "********");
+         write_pr (outfile, SUBMITTER);
+         set_field (SUBMITTER, str);
+ 	xfree (str);
+       }
+ #endif
        write_pr (outfile, ORIGINATOR);
        write_pr (outfile, RELEASE);
        write_pr (outfile, ARRIVAL_DATE);
        write_pr (outfile, LAST_MODIFIED);
+ 
+       write_pr (outfile, BILLING_INFO);
      }
  }
Index: gnats-3.104-beta/gnats/query.h
diff -c gnats-3.104-beta/gnats/query.h:1.1.1.1 gnats-3.104-beta/gnats/query.h:1.3
*** gnats-3.104-beta/gnats/query.h:1.1.1.1	Thu Oct  9 21:37:29 1997
--- gnats-3.104-beta/gnats/query.h	Fri Oct 17 13:07:28 1997
***************
*** 29,36 ****
--- 29,38 ----
  
  /* Query the list of categories.  */
  #define LIST_CATEGORIES	(1<<1)
+ #ifndef __PRIVATE_SID__
  /* Query the list of submitters.  */
  #define LIST_SUBMITTERS	(1<<2)
+ #endif
  /* Query the list of responsible.  */
  #define LIST_RESPONSIBLE (1<<3)
  
Index: gnats-3.104-beta/gnats/rating-sum.sh
diff -c /dev/null gnats-3.104-beta/gnats/rating-sum.sh:1.1
*** /dev/null	Thu Oct 23 13:27:08 1997
--- gnats-3.104-beta/gnats/rating-sum.sh	Thu Oct 23 13:24:14 1997
***************
*** 0 ****
--- 1,229 ----
+ #!/bin/sh
+ #GNATS_ROOT=xGNATS_ROOTx
+ VERSION=xVERSIONx
+ GNATS_ROOT=/usr/local/share/gnats/gnats-db
+ COMMAND=`echo $0 | sed -e 's,.*/,,'`
+ USAGE="Usage: $COMMAND [-V] [-s start-date] [-e end-date] [-r responsible]
+        [-c category] [-S[r|c|a]] -q
+        -V show version
+        -s date should be of yyyy/mm/dd format if omitted from 1970/1/1
+        -e date should be of yyyy/mm/dd format if omitted until now
+        -r responsible person e-mail address (regular expression accepted)
+        -c category (regular expression accepted)
+        -Sr summary for responsible
+        -Sc summary for category
+        -Sa summary for category and responsible
+        -q show only summary"
+ 
+ ECHO='echo -e'
+ 
+ START_DATE=
+ END_DATE=
+ RESPONSIBLE=
+ CATEGORY=
+ SUMMARY=
+ QUEIT=
+ RATING_FILE=".rating"
+ 
+ while [ $# -gt 0 ]; do
+   case "$1" in
+     -s) if [ $# -eq 1 ]; then $ECHO "$USAGE"; exit 1; fi
+         shift ; START_DATE="$1"
+         ;;
+     -e) if [ $# -eq 1 ]; then $ECHO "$USAGE"; exit 1; fi
+         shift ; END_DATE="$1"
+         ;;
+     -r) if [ $# -eq 1 ]; then $ECHO "$USAGE"; exit 1; fi
+         shift ; RESPONSIBLE="$1"
+         ;;
+     -c) if [ $# -eq 1 ]; then $ECHO "$USAGE"; exit 1; fi
+         shift ; CATEGORY="$1"
+         ;;
+     -q) QUEIT=Y
+ 	;;
+     -Sr) if [ ! -z $SUMMARY ]; then $ECHO "$USAGE"; exit 1; fi
+         SUMMARY="R"
+         ;;
+     -Sc) if [ ! -z $SUMMARY ]; then $ECHO "$USAGE"; exit 1; fi
+         SUMMARY="C"
+         ;;
+     -Sa) if [ ! -z $SUMMARY ]; then $ECHO "$USAGE"; exit 1; fi
+         SUMMARY="A"
+         ;;
+     -V) $ECHO "$USAGE"; exit 1
+ 	;;
+     *) $ECHO "$USAGE"; exit 1
+         ;;
+  esac
+  shift
+ done
+ 
+ if [ -z "$START_DATE" ]; then
+   START_DATE="1970/01/01"
+ fi
+ if [ -z "$END_DATE" ]; then
+   END_DATE=`date +%Y/%m/%d`
+ fi
+ if [ -z "$SUMMARY" ]; then
+   SUMMARY=N
+ fi
+ if [ -z "QUEIT" ]; then
+   QUEIT=N
+ fi
+ if [ -z "$CATEGORY" ]; then
+   CATEGORY=".*"
+ fi
+ CATEGORY=`'grep' -v '^#' $GNATS_ROOT/gnats-adm/categories | \
+ cut -d : -f 1 -s | 'grep' "$CATEGORY"`
+ if [ -z "$CATEGORY" ]; then
+   $ECHO "no category matches"; exit 1
+ fi
+ if [ -z "$RESPONSIBLE" ]; then
+   RESPONSIBLE='.*'
+ fi
+ 
+ for SUBDIR in $CATEGORY
+ do
+   $ECHO Category: $SUBDIR
+   $ECHO
+   if [ ! -d $GNATS_ROOT/$SUBDIR ]; then
+     $ECHO "$GNATS_ROOT/$SUBDIR" "does not exist"
+     continue
+   fi
+   if [ ! -r $GNATS_ROOT/$SUBDIR/$RATING_FILE ]; then
+     continue
+   fi
+ 
+   SUBDIR_RESP=`cut -d : -f 2 -s $GNATS_ROOT/$SUBDIR/$RATING_FILE| \
+   'grep' "$RESPONSIBLE"| uniq`
+ 
+   sort -t : +1 -2 "$GNATS_ROOT/$SUBDIR/$RATING_FILE" | awk -F : \
+   -v sum=$SUMMARY \
+   -v queit=$QUEIT \
+   -v start=$START_DATE \
+   -v end=$END_DATE \
+   -v resp="$SUBDIR_RESP" '
+ BEGIN {
+   OLD_FS=FS;
+   FS="/";
+   split (resp, resp_arr, " ");
+   for (i in resp_arr)
+     {
+       resp_quality[i]=0;
+       resp_speed[i]=0;
+       resp_courtesy[i]=0;
+       resp_rec[i]=0;
+       resp_rated_rec[i]=0;
+     }
+ 
+   $0=start;
+   start_year=$1+0;
+   start_mon=$2+0;
+   start_day=$3+0;
+ 
+   $0=end;
+   end_year=$1+0;
+   end_mon=$2+0;
+   end_day=$3+0;
+ 
+   FS=OLD_FS;
+   print_format="| %10s | %25s |  %10s  |  %1s  |  %1s  |  %1s  |\n"
+   if (queit != "Y")
+     {
+       printf "+------------+---------------------------+--------------+-----+-----+-----+\n";
+       printf print_format, "Number", "Responsible", "Date", "Q", "S", "C";
+       printf "+------------+---------------------------+--------------+-----+-----+-----+\n";
+     }
+ }
+ {
+   quality=$4;
+   speed=$5;
+   courtesy=$6;
+   OLD_FS=FS;
+   OLD_0=$0;
+   FS="/";
+   $0=$3;
+   year=$1+0;
+   mon=$2+0;
+   day=$3+0;
+   FS=OLD_FS;
+   $0=OLD_0;
+   if ( (year > start_year ||
+     (year == start_year && (mon > start_mon ||
+     (mon == start_mon && day >= start_day)))) && 
+     (end_year > year ||
+     (end_year == year && (end_mon > mon ||
+     (end_mon == mon && end_day >= day)))) )
+     for (i in resp_arr)
+       if (resp_arr[i] == $2)
+ 	{
+ 	   if (queit != "Y")
+ 	     printf print_format, $1, $2, $3, $4, $5, $6;
+ 	   resp_rec[i]++;
+ 	   if (quality != "" && speed != "" && courtesy != "")
+ 	     {
+ 	       resp_quality[i]+=quality;
+ 	       resp_speed[i]+=speed;
+ 	       resp_courtesy[i]+=courtesy;
+ 	       resp_rated_rec[i]++;
+ 	     }
+ 	  break;
+ 	}
+ }
+ END {
+   if (queit != "Y")
+     {
+       printf "+------------+---------------------------+--------------+-----+-----+-----+\n";
+       printf "\n"
+     }
+   if (sum != "N")
+     {
+       printf "+----------------------+--------+--------+----------+----------+----------+\n";
+       sum_format="| %-20s | %6s | %6s | %8s | %8s | %8s |\n"
+       printf (sum_format, "Responsible", "Closed", "Rated",
+ 	"Quality", "Speed", "Courtesy");
+       printf "+----------------------+--------+--------+----------+----------+----------+\n";
+ 
+       total_rec=0;
+       total_rated_rec=0;
+       total_quality=0;
+       total_speed=0;
+       total_courtesy=0;
+       for (i in resp_arr)
+         {
+ 	  total_rec+=resp_rec[i];
+ 	  total_rated_rec+=resp_rated_rec[i];
+ 	  total_quality+=resp_quality[i];
+ 	  total_speed+=resp_speed[i];
+ 	  total_courtesy+=resp_courtesy[i];
+ 
+ 	  if (sum != "C")
+ 	    if (resp_rated_rec[i] != 0)
+               printf (sum_format, resp_arr[i], resp_rec[i], resp_rated_rec[i],
+ 	        resp_quality[i]/resp_rated_rec[i],
+ 	        resp_speed[i]/resp_rated_rec[i], 
+ 	        resp_courtesy[i]/resp_rated_rec[i]);
+ 	    else
+               printf (sum_format, resp_arr[i], resp_rec[i], resp_rated_rec[i],
+ 	        "N/A", "N/A", "N/A");
+         }
+       if (sum != "C")
+       printf "+----------------------+--------+--------+----------+----------+----------+\n";
+       if (sum != "R")
+ 	{
+ 
+ 	  if (total_rated_rec != 0)
+             printf (sum_format, "Total", total_rec, total_rated_rec,
+ 	      total_quality/total_rated_rec,
+ 	      total_speed/total_rated_rec, 
+ 	      total_courtesy/total_rated_rec);
+ 	  else
+             printf (sum_format, "Total", total_rec, total_rated_rec,
+ 	      "N/A", "N/A", "N/A");
+       printf "+----------------------+--------+--------+----------+----------+----------+\n";
+ 	
+ 	}
+       printf "\n";
+     }
+ }'
+ done
Index: gnats-3.104-beta/gnats/send-patch.sh
diff -c /dev/null gnats-3.104-beta/gnats/send-patch.sh:1.4
*** /dev/null	Thu Oct 23 13:27:08 1997
--- gnats-3.104-beta/gnats/send-patch.sh	Thu Oct 23 13:24:14 1997
***************
*** 0 ****
--- 1,86 ----
+ #!/bin/sh
+ # Program to send patches for problem reports for GNATS.
+ # Copyright (C) 1993, 1994, 1995 Free Software Foundation, Inc.
+ # Hacked on the basis of edit-pr by Sergey V. Vanskov (vanskov@aha.ru)
+ #
+ # 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.
+ 
+ CVS="xCVSx"
+ CVS_ROOT="xCVS_ROOTx"
+ GNATS_ROOT=xGNATS_ROOTx
+ GNATS_ADDR="xGNATS_ADDRx"
+ version=xVERSIONx
+ 
+ COMMAND=`echo $0 | sed -e 's,.*/,,'`
+ usage="Usage: $COMMAND [-hV] [--version] [--help] PR release category submitter\
+ -e-mail-address"
+ 
+ # Newer config information?
+ [ -f ${GNATS_ROOT}/gnats-adm/config ] && . ${GNATS_ROOT}/gnats-adm/config
+ 
+ # Host-specific; must come after config file.
+ MAIL_AGENT="xMAIL_AGENTx"
+ 
+ if [ $# -eq 0 ]; then
+   echo "$usage" ; exit 1
+ fi
+ if [ $# -eq 1 ]; then
+   case "$1" in
+     -V|-v|--version|--ve*)
+       echo "$version"; exit 0
+       ;;
+     -h|--help*)
+       echo "$usage"; exit 0
+       ;;
+     -*)
+       echo "$usage"; exit 1
+       ;;
+   esac
+ fi
+ if [ $# -ne 4 ]; then
+   echo "$usage" ; exit 1
+ fi
+ 
+ if [ -z "$TMPDIR" ]; then
+   TMPDIR=/tmp
+ else
+   if [ "`echo $TMPDIR | grep '/$'`" != "" ]; then
+     TMPDIR="`echo $TMPDIR | sed -e 's,/$,,'`"
+   fi
+ fi
+ 
+ TMP_FILE=$TMPDIR/rps$$
+ echo "To: $4
+ From: $GNATS_ADDR
+ Subject: Patch for PR $1
+ " > $TMP_FILE
+ 
+ if which gzip > /dev/null && which uuencode > /dev/null; then
+   if $CVS -d $CVS_ROOT rdiff -u -r RELEASE-$2 -r PATCH-$1 $3 2>/dev/null\
+   | gzip -c | uuencode /dev/stdout >> $TMP_FILE; then
+     $MAIL_AGENT < $TMP_FILE; exit 0
+   else
+     exit -1
+   fi
+ else
+   if $CVS -d $CVS_ROOT rdiff -u -r RELEASE-$2 -r PATCH-$1 $3 >> $TMP_FILE \
+   2>/dev/null; then
+     $MAIL_AGENT < $TMP_FILE; exit 0
+   else
+     exit -1
+   fi
+ fi
Index: gnats-3.104-beta/gnats/supercategories
diff -c /dev/null gnats-3.104-beta/gnats/supercategories:1.1
*** /dev/null	Thu Oct 23 13:27:09 1997
--- gnats-3.104-beta/gnats/supercategories	Mon Oct 20 15:17:37 1997
***************
*** 0 ****
--- 1,16 ----
+ #		    Possible supercategories for a PR.
+ #
+ # Any line which begins with a `#' is considered a comment, and GNATS
+ # will ignore it. 
+ #
+ # Each entry has the format:
+ #
+ # 	supercategory:list-of-categories
+ #
+ # * `supercategory' is the name of the group of categories
+ # * `list-of-categories' is "|" - separated list of categories
+ #
+ #
+ # Sample supercategories:
+ #
+ c-development:gcc|g++|doc
Index: gnats-3.104-beta/send-pr/Makefile.in
diff -c gnats-3.104-beta/send-pr/Makefile.in:1.1.1.1 gnats-3.104-beta/send-pr/Makefile.in:1.6
*** gnats-3.104-beta/send-pr/Makefile.in:1.1.1.1	Thu Oct  9 21:37:29 1997
--- gnats-3.104-beta/send-pr/Makefile.in	Mon Oct 20 02:54:44 1997
***************
*** 1,4 ****
! # Makefile for GNU send-pr.
  # Copyright (C) 1993 Free Software Foundation, Inc.
  #
  # This file is part of GNU GNATS.
--- 1,4 ----
! # Makefile for GNU send-pr and upgrade-pr and request-patch and rating-pr.
  # Copyright (C) 1993 Free Software Foundation, Inc.
  #
  # This file is part of GNU GNATS.
***************
*** 118,128 ****
  
  DISTFILES= COPYING ChangeLog Makefile.in README configure.in \
  install-sid.sh send-pr-el.in send-pr.man send-pr.texi fields.texi \
! states.texi s-usage.texi categ.texi send-pr.sh
  
  # Gordon took out send-pr.elc, instead installing send-pr.el.
  # all: send-pr install-sid send-pr.elc send-pr.1
! all: send-pr install-sid send-pr.el send-pr.1 version.texi
  
  send-pr: send-pr.sh Makefile
  	sed -e 's,xVERSIONx,$(VERSION),' \
--- 118,130 ----
  
  DISTFILES= COPYING ChangeLog Makefile.in README configure.in \
  install-sid.sh send-pr-el.in send-pr.man send-pr.texi fields.texi \
! states.texi s-usage.texi categ.texi send-pr.sh upgrade-pr.sh \
! request-patch.sh rating-pr.sh
  
  # Gordon took out send-pr.elc, instead installing send-pr.el.
  # all: send-pr install-sid send-pr.elc send-pr.1
! all: send-pr upgrade-pr request-patch rating-pr install-sid send-pr.el \
!      send-pr.1 version.texi
  
  send-pr: send-pr.sh Makefile
  	sed -e 's,xVERSIONx,$(VERSION),' \
***************
*** 139,144 ****
--- 141,185 ----
  	mv $@-t $@
  	chmod 755 $@
  
+ upgrade-pr: upgrade-pr.sh Makefile
+ 	sed -e 's,xVERSIONx,$(VERSION),' \
+ 	    -e 's,xGNATS_ROOTx,$(GNATS_ROOT),' \
+ 	    -e 's,xGNATS_ADDRx,$(GNATS_ADDR),' \
+ 	    -e 's,xGNATS_SITEx,$(GNATS_SITE),' \
+ 	    -e 's,xSUBMITTERx,$(SUBMITTER),' \
+ 	    -e 's,xECHONx,$(ECHON),' \
+ 	    -e 's,xMAIL_AGENTx,$(MAIL_AGENT),' \
+ 	    -e 's,xDEFAULT_RELEASEx,$(DEFAULT_RELEASE),' \
+ 	    -e 's,xDATADIRx,$(datadir),' $(srcdir)/upgrade-pr.sh > $@-t
+ 	mv $@-t $@
+ 	chmod 755 $@
+ 
+ request-patch: request-patch.sh Makefile
+ 	sed -e 's,xVERSIONx,$(VERSION),' \
+ 	    -e 's,xGNATS_ROOTx,$(GNATS_ROOT),' \
+ 	    -e 's,xGNATS_ADDRx,$(GNATS_ADDR),' \
+ 	    -e 's,xGNATS_SITEx,$(GNATS_SITE),' \
+ 	    -e 's,xSUBMITTERx,$(SUBMITTER),' \
+ 	    -e 's,xECHONx,$(ECHON),' \
+ 	    -e 's,xMAIL_AGENTx,$(MAIL_AGENT),' \
+ 	    -e 's,xDEFAULT_RELEASEx,$(DEFAULT_RELEASE),' \
+ 	    -e 's,xDATADIRx,$(datadir),' $(srcdir)/request-patch.sh > $@-t
+ 	mv $@-t $@
+ 	chmod 755 $@
+ 
+ rating-pr: rating-pr.sh Makefile
+ 	sed -e 's,xVERSIONx,$(VERSION),' \
+ 	    -e 's,xGNATS_ROOTx,$(GNATS_ROOT),' \
+ 	    -e 's,xGNATS_ADDRx,$(GNATS_ADDR),' \
+ 	    -e 's,xGNATS_SITEx,$(GNATS_SITE),' \
+ 	    -e 's,xSUBMITTERx,$(SUBMITTER),' \
+ 	    -e 's,xECHONx,$(ECHON),' \
+ 	    -e 's,xMAIL_AGENTx,$(MAIL_AGENT),' \
+ 	    -e 's,xDEFAULT_RELEASEx,$(DEFAULT_RELEASE),' \
+ 	    -e 's,xDATADIRx,$(datadir),' $(srcdir)/rating-pr.sh > $@-t
+ 	mv $@-t $@
+ 	chmod 755 $@
+ 
  install-sid: install-sid.sh Makefile
  	sed -e 's,xBINDIRx,$(bindir),g' \
  	    -e 's,xVERSIONx,$(VERSION),g' $(srcdir)/install-sid.sh > $@-t
***************
*** 178,183 ****
--- 219,227 ----
  
  install-norm-arch-dep:
  	$(INSTALL_PROGRAM) send-pr $(bindir)/send-pr
+ 	$(INSTALL_PROGRAM) upgrade-pr $(bindir)/upgrade-pr
+ 	$(INSTALL_PROGRAM) request-patch $(bindir)/request-patch
+ 	$(INSTALL_PROGRAM) rating-pr $(bindir)/rating-pr
  	$(INSTALL_PROGRAM) install-sid $(bindir)/install-sid
  	$(INSTALL_DATA) send-pr.1 $(man1dir)/send-pr.1
  
***************
*** 204,209 ****
--- 248,274 ----
  	    send-pr > $(datadir)/gnats/dist/send-pr.sh-t
  	mv $(datadir)/gnats/dist/send-pr.sh-t \
  		$(datadir)/gnats/dist/send-pr.sh
+ 	sed -e 's/GNATS_ROOT=.*/GNATS_ROOT=/' \
+ 	    -e 's/SUBMITTER=.*/SUBMITTER=@''SUBMITTER@/' \
+ 	    -e 's/DEFAULT_RELEASE=.*/DEFAULT_RELEASE="@''DEFAULT_RELEASE@"/' \
+ 	    -e 's/DATADIR=.*/DATADIR=@''DATADIR@/' \
+ 	    upgrade-pr > $(datadir)/gnats/dist/upgrade-pr.sh-t
+ 	mv $(datadir)/gnats/dist/upgrade-pr.sh-t \
+ 		$(datadir)/gnats/dist/upgrade-pr.sh
+ 	sed -e 's/GNATS_ROOT=.*/GNATS_ROOT=/' \
+ 	    -e 's/SUBMITTER=.*/SUBMITTER=@''SUBMITTER@/' \
+ 	    -e 's/DEFAULT_RELEASE=.*/DEFAULT_RELEASE="@''DEFAULT_RELEASE@"/' \
+ 	    -e 's/DATADIR=.*/DATADIR=@''DATADIR@/' \
+ 	    request-patch > $(datadir)/gnats/dist/request-patch.sh-t
+ 	mv $(datadir)/gnats/dist/request-patch.sh-t \
+ 		$(datadir)/gnats/dist/request-patch.sh
+ 	sed -e 's/GNATS_ROOT=.*/GNATS_ROOT=/' \
+ 	    -e 's/SUBMITTER=.*/SUBMITTER=@''SUBMITTER@/' \
+ 	    -e 's/DEFAULT_RELEASE=.*/DEFAULT_RELEASE="@''DEFAULT_RELEASE@"/' \
+ 	    -e 's/DATADIR=.*/DATADIR=@''DATADIR@/' \
+ 	    rating-pr > $(datadir)/gnats/dist/rating-pr.sh-t
+ 	mv $(datadir)/gnats/dist/rating-pr.sh-t \
+ 		$(datadir)/gnats/dist/rating-pr.sh
  	sed -e 's/^BINDIR=.*/BINDIR=@''BINDIR@/' \
  	    install-sid > $(datadir)/gnats/dist/install-sid.sT
  	mv $(datadir)/gnats/dist/install-sid.sT \
***************
*** 233,239 ****
  	done
  
  uninstall:
! 	-rm -f $(bindir)/send-pr $(bindir)/install-sid
  	-rm -f $(datadir)/gnats/$(GNATS_SITE)
  	-[ x$$GNATS_ROOT = x ] || rm -rf $(datadir)/gnats/dist
  	-rmdir $(datadir)/gnats
--- 298,306 ----
  	done
  
  uninstall:
! 	-rm -f $(bindir)/send-pr $(bindir)/upgrade-pr
! 	-rm -f $(bindir)/request-patch $(bindir)/install-sid
! 	-rm -f $(bindir)/rating-pr
  	-rm -f $(datadir)/gnats/$(GNATS_SITE)
  	-[ x$$GNATS_ROOT = x ] || rm -rf $(datadir)/gnats/dist
  	-rmdir $(datadir)/gnats
***************
*** 282,288 ****
  # Clean things up.
  
  clean: mostlyclean
! 	-rm -f send-pr install-sid send-pr.1 send-pr.el send-pr.elc stamp-gnats
  	-rm -f *.dvi
  
  mostlyclean:
--- 349,356 ----
  # Clean things up.
  
  clean: mostlyclean
! 	-rm -f send-pr upgrade-pr request-patch rating-pr install-sid send-pr.1
! 	-rm -f send-pr.el send-pr.elc stamp-gnats
  	-rm -f *.dvi
  
  mostlyclean:
Index: gnats-3.104-beta/send-pr/rating-pr.sh
diff -c /dev/null gnats-3.104-beta/send-pr/rating-pr.sh:1.5
*** /dev/null	Thu Oct 23 13:27:09 1997
--- gnats-3.104-beta/send-pr/rating-pr.sh	Wed Oct 22 20:27:24 1997
***************
*** 0 ****
--- 1,428 ----
+ #!/bin/sh
+ # Submit a technical support rating for a problem report to a GNATS site.
+ # Copyright (C) 1993 Free Software Foundation, Inc.
+ #
+ # Contributed by Sergey V. Vanskov (vanskov@aha.ru) based on 'edit-pr'
+ # contributed by Brendan Kehoe (brendan@cygnus.com), based on a
+ # version written by Heinz G. Seidl (hgs@cygnus.com).
+ #
+ # 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 version of this rating-pr.
+ VERSION=xVERSIONx
+ 
+ # The submitter-id for your site.
+ SUBMITTER=xSUBMITTERx
+ 
+ # Where the GNATS directory lives, if at all.
+ [ -z "$GNATS_ROOT" ] && 
+ GNATS_ROOT=xGNATS_ROOTx
+ 
+ # The default mail address for PR submissions. 
+ GNATS_ADDR=xGNATS_ADDRx
+ 
+ # Where the gnats category tree lives.
+ DATADIR=xDATADIRx
+ 
+ # If we've been moved around, try using GCC_EXEC_PREFIX.
+ [ ! -d $DATADIR/gnats -a -d "$GCC_EXEC_PREFIX" ] && 
+   DATADIR=${GCC_EXEC_PREFIX}../../../`basename xDATADIRx`
+ 
+ # The default release for this host.
+ DEFAULT_RELEASE="xDEFAULT_RELEASEx"
+ 
+ # The default site to look for.
+ GNATS_SITE=xGNATS_SITEx
+ 
+ # Newer config information?
+ [ -f ${GNATS_ROOT}/gnats-adm/config ] && . ${GNATS_ROOT}/gnats-adm/config
+ 
+ # What mailer to use.  This must come after the config file, since it is
+ # host-dependent.
+ MAIL_AGENT="xMAIL_AGENTx"
+ 
+ ECHON=xECHONx
+ 
+ if [ $ECHON = bsd ] ; then
+   ECHON1="echo -n"
+   ECHON2=
+ elif [ $ECHON = sysv ] ; then
+   ECHON1=echo
+   ECHON2='\c'
+ else
+   ECHON1=echo
+   ECHON2=
+ fi
+ 
+ #
+ 
+ if [ -z "$TMPDIR" ]; then
+   TMPDIR=/tmp
+ else
+   if [ "`echo $TMPDIR | grep '/$'`" != "" ]; then
+     TMPDIR="`echo $TMPDIR | sed -e 's,/$,,'`"
+   fi
+ fi
+ 
+ TEMP=$TMPDIR/ratep$$
+ BAD=$TMPDIR/ratepbad$$
+ REF=$TMPDIR/ratepf$$
+ 
+ FROM="$SUBMITTER"
+ if [ -z "$REPLYTO" ]; then
+   REPLYTO="$SUBMITTER"
+ fi
+ 
+ # If they don't have a preferred editor set, then use
+ if [ -z "$VISUAL" ]; then
+   if [ -z "$EDITOR" ]; then
+     EDIT=vi
+   else
+     EDIT="$EDITOR"
+   fi
+ else
+   EDIT="$VISUAL"
+ fi
+ 
+ COMMAND=`echo $0 | sed -e 's,.*/,,'`
+ USAGE="Usage: $COMMAND [-PVL] [-t address] [-f filename]
+        [-c address] [--version]"
+ REMOVE=
+ BATCH=
+ CC=
+ 
+ while [ $# -gt 0 ]; do
+   case "$1" in
+     -t | --to) if [ $# -eq 1 ]; then echo "$USAGE"; exit 1; fi
+ 	shift ; GNATS_ADDR="$1"
+ 	EXPLICIT_GNATS_ADDR=true
+         ;;
+     -f | --file) if [ $# -eq 1 ]; then echo "$USAGE"; exit 1; fi
+ 	shift ; IN_FILE="$1"
+ 	if [ "$IN_FILE" != "-" -a ! -r "$IN_FILE" ]; then
+ 	  echo "$COMMAND: cannot read $IN_FILE"
+ 	  exit 1
+ 	fi
+ 	;;
+     -b | --batch) BATCH=true ;;
+     -c | --cc) if [ $# -eq 1 ]; then echo "$USAGE"; exit 1; fi
+ 	shift ; CC="$1"
+ 	;;
+     -p | -P | --print) PRINT=true ;;
+     -L | --list) FORMAT=norm ;;
+     -l | -CL | --lisp) FORMAT=lisp ;;
+     -h | --help) echo "$USAGE"; exit 0 ;;
+     -V | --version) echo "$VERSION"; exit 0 ;;
+     -*) echo "$USAGE" ; exit 1 ;;
+     *) if [ -z "$USER_GNATS_SITE" ]; then
+ 	 if [ ! -r "$DATADIR/gnats/$1" ]; then
+ 	   echo "$COMMAND: the GNATS site $1 does not have a categories list."
+ 	   exit 1
+ 	 else
+ 	   # The site name is the alias they'll have to have created.
+ 	   USER_GNATS_SITE=$1
+ 	 fi
+        else
+ 	 echo "$USAGE" ; exit 1
+        fi
+        ;;
+  esac
+  shift
+ done
+ 
+ if [ -n "$USER_GNATS_SITE" ] && [ "$USER_GNATS_SITE" != "$GNATS_SITE" ]; then
+   GNATS_SITE=$USER_GNATS_SITE
+   GNATS_ADDR=$USER_GNATS_SITE-gnats
+ fi
+ 
+ if [ "$SUBMITTER" = "unknown" -a -z "$IN_FILE" ]; then
+   cat << '__EOF__'
+ It seems that rating-pr is not installed with your unique submitter-id.
+ You need to run
+ 
+           install-sid YOUR-SID
+ 
+ where YOUR-SID is your e-mail address. `rating-pr' will automatically
+ insert this value into the template field `>Submitter-Id'.
+ __EOF__
+   exit 1
+ fi
+ 
+ if [ -r "$DATADIR/gnats/$GNATS_SITE" ]; then
+   CATEGORIES=`grep -v '^#' $DATADIR/gnats/$GNATS_SITE | sort`
+ else
+   echo "$COMMAND: could not read $DATADIR/gnats/$GNATS_SITE for categories list."
+   exit 1
+ fi
+ 
+ if [ -z "$CATEGORIES" ]; then
+   echo "$COMMAND: the categories list for $GNATS_SITE is empty!"
+   exit 1
+ fi
+ 
+ case "$FORMAT" in
+   lisp) echo "$CATEGORIES" | \
+         awk 'BEGIN {printf "( "} {printf "(\"%s\") ",$0} END {printf ")\n"}'
+         exit 0
+         ;;
+   norm) l=`echo "$CATEGORIES" | \
+ 	awk 'BEGIN {max = 0; } { if (length($0) > max) { max = length($0); } }
+ 	     END {print max + 1;}'`
+ 	c=`expr 70 / $l`
+ 	if [ $c -eq 0 ]; then c=1; fi
+ 	echo "$CATEGORIES" | \
+         awk 'BEGIN {print "Known categories:"; i = 0 }
+           { printf ("%-'$l'.'$l's", $0); if ((++i % '$c') == 0) { print "" } }
+             END { print ""; }'
+         exit 0
+         ;;
+ esac
+ 
+ QUALITY=
+ SPEED=
+ COURTESY=
+ COMMENTS=
+ 
+ NUMBER_C='<number of the problem report to rate (one line)>'
+ CATEGORY_C='<name of the product (one line)>'
+ QUALITY_C='<[ Excellent | Good | So-so | Bad | Awfully ] (one line)>'
+ SPEED_C='<[ Instantaneous | Quick | Rather-quick | Slow | Very-slow ] (one line)>'
+ COURTESY_C='<[ Courteous | Normal | Not-courteous] (one line)>'
+ COMMENTS_C='<any comments (multiple lines)>'
+ 
+ # Catch some signals. ($xs kludge needed by Sun /bin/sh)
+ xs=0
+ trap 'rm -f $REF $TEMP; exit $xs' 0
+ trap 'echo "$COMMAND: Aborting ..."; rm -f $REF $TEMP; xs=1; exit' 1 3 13 15
+ 
+ # If they told us to use a specific file, then do so.
+ if [ -n "$IN_FILE" ]; then
+   if [ "$IN_FILE" = "-" ]; then
+     # The PR is coming from the standard input.
+     if [ -n "$EXPLICIT_GNATS_ADDR" ]; then
+       sed -e "s;^[Tt][Oo]:.*;To: $GNATS_ADDR;" > $TEMP
+     else
+       cat > $TEMP
+     fi
+   else
+     # Use the file they named.
+     if [ -n "$EXPLICIT_GNATS_ADDR" ]; then
+       sed -e "s;^[Tt][Oo]:.*;To: $GNATS_ADDR;" $IN_FILE > $TEMP
+     else
+       cat $IN_FILE > $TEMP
+     fi
+   fi
+ else
+ 
+   if [ -n "$PR_FORM" -a -z "$PRINT_INTERN" ]; then
+     # If their PR_FORM points to a bogus entry, then bail.
+     if [ ! -f "$PR_FORM" -o ! -r "$PR_FORM" -o ! -s "$PR_FORM" ]; then
+       echo "$COMMAND: can't seem to read your template file (\`$PR_FORM'), ignoring PR_FORM"
+       sleep 1
+       PRINT_INTERN=bad_prform
+     fi
+   fi
+ 
+   if [ -n "$PR_FORM" -a -z "$PRINT_INTERN" ]; then
+     cp $PR_FORM $TEMP || 
+       ( echo "$COMMAND: could not copy $PR_FORM" ; xs=1; exit )
+   else
+     for file in $TEMP $REF ; do
+       cat  > $file << '__EOF__'
+ RATING-PR: -*- rating-pr -*-
+ RATING-PR: Lines starting with `RATING-PR' will be removed automatically, as
+ RATING-PR: will all comments (text enclosed in `<' and `>').
+ RATING-PR: 
+ RATING-PR: Please consult the rating-pr man page `rating-pr(1)' or the Texinfo
+ RATING-PR: manual if you are not sure how to fill out a rating report.
+ RATING-PR:
+ RATING-PR: Choose from the following categories:
+ RATING-PR:
+ __EOF__
+ 
+       # Format the categories so they fit onto lines.
+ 	l=`echo "$CATEGORIES" | \
+ 	awk 'BEGIN {max = 0; } { if (length($0) > max) { max = length($0); } }
+ 	     END {print max + 1;}'`
+ 	c=`expr 61 / $l`
+ 	if [ $c -eq 0 ]; then c=1; fi
+ 	echo "$CATEGORIES" | \
+         awk 'BEGIN {printf "RATING-PR: "; i = 0 }
+           { printf ("%-'$l'.'$l's", $0);
+ 	    if ((++i % '$c') == 0) { printf "\nRATING-PR: " } }
+             END { printf "\nRATING-PR:\n"; }' >> $file
+ 
+       cat >> $file << __EOF__
+ To: $GNATS_ADDR
+ Subject: Technical Support Rating
+ From: $FROM
+ Reply-To: $REPLYTO
+ Cc: $CC
+ X-rating-pr-version: $VERSION
+ 
+ >Submitter-Id:		$SUBMITTER
+ >Number:		$NUMBER_C
+ >Category:		$CATEGORY_C
+ >Response-Quality:	$QUALITY_C
+ >Response-Speed:	$SPEED_C
+ >Response-Courtesy:	$COURTESY_C
+ >Rating-Comments:
+ 	$COMMENTS_C
+ __EOF__
+     done
+   fi
+ 
+   if [ "$PRINT" = true -o "$PRINT_INTERN" = true ]; then
+     cat $TEMP
+     xs=0; exit
+   fi
+ 
+   chmod u+w $TEMP
+   eval $EDIT $TEMP
+ 
+   if cmp -s $REF $TEMP ; then
+     echo "$COMMAND: rating report not filled out, therefore not sent"
+     xs=1; exit
+   fi
+ fi
+ 
+ #
+ #	Check the enumeration fields
+ 
+ # This is a "sed-subroutine" with one keyword parameter 
+ # (with workaround for Sun sed bug)
+ #
+ SED_CMD='
+ /$PATTERN/{
+ s|||
+ s|<.*>||
+ s|^[ 	]*||
+ s|[ 	]*$||
+ p
+ q
+ }'
+ 
+ while true; do
+   CNT=0
+ 
+   # 1) Response-Quality
+   #
+   PATTERN=">Response-Quality:"
+   QUALITY=`eval sed -n -e "\"$SED_CMD\"" $TEMP`
+   case "$QUALITY" in
+     ""|Excellent|Good|So-so|Bad|Awfully) CNT=`expr $CNT + 1` ;;
+     *) echo "$COMMAND: \`$QUALITY' is not a valid value for \`Response-Quality'." ;;
+   esac
+   #
+   # 2) Response-Speed
+   #
+   PATTERN=">Response-Speed:"
+   SPEED=`eval sed -n -e "\"$SED_CMD\"" $TEMP`
+   case "$SPEED" in
+     ""|Instantaneous|Quick|Rather-quick|Slow|Very-slow) CNT=`expr $CNT + 1` ;;
+     *)  echo "$COMMAND: \`$SPEED' is not a valid value for \`Response-Speed'."
+   esac
+   #
+   # 3) Response-Courtesy
+   #
+   PATTERN=">Response-Courtesy:"
+   COURTESY=`eval sed -n -e "\"$SED_CMD\"" $TEMP`
+   case "$COURTESY" in
+     ""|Courteous|Normal|Not-courteous) CNT=`expr $CNT + 1` ;;
+     *)  echo "$COMMAND: \`$COURTESY' is not a valid value for \`Response-Courtesy'."
+   esac
+   #
+   # 4) Category
+   #
+   PATTERN=">Category:"
+   CATEGORY=`eval sed -n -e "\"$SED_CMD\"" $TEMP`
+   FOUND=
+   for C in $CATEGORIES
+   do
+     if [ "$C" = "$CATEGORY" ]; then FOUND=true ; break ; fi
+   done
+   if [ -n "$FOUND" ]; then
+     CNT=`expr $CNT + 1`	
+   else
+     if [ -z "$CATEGORY" ]; then
+       echo "$COMMAND: you must include a Category: field in your report."
+     else
+       echo "$COMMAND: \`$CATEGORY' is not a known category."
+     fi
+   fi
+ 
+   [ $CNT -lt 4 -a -z "$BATCH" ] && 
+     echo "Errors were found with the rating report."
+ 
+   while true; do
+     if [ -z "$BATCH" ]; then
+       $ECHON1 "a)bort, e)dit or s)end? $ECHON2"
+       read input
+     else
+       if [ $CNT -eq 4 ]; then
+         input=s
+       else
+         input=a
+       fi
+     fi
+     case "$input" in
+       a*)
+ 	if [ -z "$BATCH" ]; then
+ 	  echo "$COMMAND: the rating report remains in $BAD and is not sent."
+ 	  mv $TEMP $BAD
+         else
+ 	  echo "$COMMAND: the rating report is not sent."
+ 	fi
+ 	xs=1; exit
+ 	;;
+       e*)
+         eval $EDIT $TEMP
+ 	continue 2
+ 	;;
+       s*)
+ 	break 2
+ 	;;
+     esac
+   done
+ done
+ #
+ #	Remove comments and send the rating report
+ #	(we have to use patterns, where the comment contains regex chars)
+ #
+ sed  -e "
+ /^RATING-PR:/d
+ /^>Number:/,/^>[A-Za-z-]*:/s;$NUMBER_C;;
+ /^>Category:/s;$CATEGORY_C;;
+ /^>Response-Quality:/s;<.*>;;
+ /^>Response-Speed:/s;<.*>;;
+ /^>Response-Courtesy:/s;<.*>;;
+ /^>Rating-Comments:/,/^>[A-Za-z-]*:/s;$COMMENTS_C;;
+ " $TEMP > $REF
+ 
+ if $MAIL_AGENT < $REF; then
+   echo "$COMMAND: rating report sent"
+   xs=0; exit
+ else
+   echo "$COMMAND: mysterious mail failure."
+   if [ -z "$BATCH" ]; then
+     echo "$COMMAND: the rating report remains in $BAD and is not sent."
+     mv $REF $BAD
+   else
+     echo "$COMMAND: the rating report is not sent."
+   fi
+   xs=1; exit
+ fi
Index: gnats-3.104-beta/send-pr/request-patch.sh
diff -c /dev/null gnats-3.104-beta/send-pr/request-patch.sh:1.3
*** /dev/null	Thu Oct 23 13:27:09 1997
--- gnats-3.104-beta/send-pr/request-patch.sh	Tue Oct 21 17:19:22 1997
***************
*** 0 ****
--- 1,386 ----
+ #!/bin/sh
+ # Submit a patch request for a problem report to a GNATS site.
+ # Copyright (C) 1993 Free Software Foundation, Inc.
+ #
+ # Contributed by Sergey V. Vanskov (vanskov@aha.ru)
+ # based on send-pr.sh contributed by Brendan Kehoe (brendan@cygnus.com),
+ # based on a version written by Heinz G. Seidl (hgs@cygnus.com).
+ #
+ # 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 version of this request-patch
+ VERSION=xVERSIONx
+ 
+ # The e-mail address of your site.
+ SUBMITTER=xSUBMITTERx
+ 
+ # Where the GNATS directory lives, if at all.
+ [ -z "$GNATS_ROOT" ] && 
+ GNATS_ROOT=xGNATS_ROOTx
+ 
+ # The default mail address for PR submissions. 
+ GNATS_ADDR=xGNATS_ADDRx
+ 
+ # Where the gnats category tree lives.
+ DATADIR=xDATADIRx
+ 
+ # If we've been moved around, try using GCC_EXEC_PREFIX.
+ [ ! -d $DATADIR/gnats -a -d "$GCC_EXEC_PREFIX" ] && 
+   DATADIR=${GCC_EXEC_PREFIX}../../../`basename xDATADIRx`
+ 
+ # The default release for this host.
+ DEFAULT_RELEASE="xDEFAULT_RELEASEx"
+ 
+ # The default site to look for.
+ GNATS_SITE=xGNATS_SITEx
+ 
+ # Newer config information?
+ [ -f ${GNATS_ROOT}/gnats-adm/config ] && . ${GNATS_ROOT}/gnats-adm/config
+ 
+ # What mailer to use.  This must come after the config file, since it is
+ # host-dependent.
+ MAIL_AGENT="xMAIL_AGENTx"
+ 
+ ECHON=xECHONx
+ 
+ if [ $ECHON = bsd ] ; then
+   ECHON1="echo -n"
+   ECHON2=
+ elif [ $ECHON = sysv ] ; then
+   ECHON1=echo
+   ECHON2='\c'
+ else
+   ECHON1=echo
+   ECHON2=
+ fi
+ 
+ #
+ 
+ if [ -z "$TMPDIR" ]; then
+   TMPDIR=/tmp
+ else
+   if [ "`echo $TMPDIR | grep '/$'`" != "" ]; then
+     TMPDIR="`echo $TMPDIR | sed -e 's,/$,,'`"
+   fi
+ fi
+ 
+ TEMP=$TMPDIR/reqp$$
+ BAD=$TMPDIR/reqpbad$$
+ REF=$TMPDIR/reqpf$$
+ 
+ FROM="$SUBMITTER"
+ if [ -z "$REPLYTO" ]; then
+   REPLYTO="$SUBMITTER"
+ fi
+ 
+ # If they don't have a preferred editor set, then use
+ if [ -z "$VISUAL" ]; then
+   if [ -z "$EDITOR" ]; then
+     EDIT=vi
+   else
+     EDIT="$EDITOR"
+   fi
+ else
+   EDIT="$VISUAL"
+ fi
+ 
+ COMMAND=`echo $0 | sed -e 's,.*/,,'`
+ USAGE="Usage: $COMMAND [-PVL] [-t address] [-f filename]
+        [-c address] [--version]"
+ REMOVE=
+ BATCH=
+ CC=
+ SEVERITY_C=
+ 
+ while [ $# -gt 0 ]; do
+   case "$1" in
+     -t | --to) if [ $# -eq 1 ]; then echo "$USAGE"; exit 1; fi
+ 	shift ; GNATS_ADDR="$1"
+ 	EXPLICIT_GNATS_ADDR=true
+         ;;
+     -f | --file) if [ $# -eq 1 ]; then echo "$USAGE"; exit 1; fi
+ 	shift ; IN_FILE="$1"
+ 	if [ "$IN_FILE" != "-" -a ! -r "$IN_FILE" ]; then
+ 	  echo "$COMMAND: cannot read $IN_FILE"
+ 	  exit 1
+ 	fi
+ 	;;
+     -b | --batch) BATCH=true ;;
+     -c | --cc) if [ $# -eq 1 ]; then echo "$USAGE"; exit 1; fi
+ 	shift ; CC="$1"
+ 	;;
+     -p | -P | --print) PRINT=true ;;
+     -L | --list) FORMAT=norm ;;
+     -l | -CL | --lisp) FORMAT=lisp ;;
+     -h | --help) echo "$USAGE"; exit 0 ;;
+     -V | --version) echo "$VERSION"; exit 0 ;;
+     -*) echo "$USAGE" ; exit 1 ;;
+     *) if [ -z "$USER_GNATS_SITE" ]; then
+ 	 if [ ! -r "$DATADIR/gnats/$1" ]; then
+ 	   echo "$COMMAND: the GNATS site $1 does not have a categories list."
+ 	   exit 1
+ 	 else
+ 	   # The site name is the alias they'll have to have created.
+ 	   USER_GNATS_SITE=$1
+ 	 fi
+        else
+ 	 echo "$USAGE" ; exit 1
+        fi
+        ;;
+  esac
+  shift
+ done
+ 
+ if [ -n "$USER_GNATS_SITE" ] && [ "$USER_GNATS_SITE" != "$GNATS_SITE" ]; then
+   GNATS_SITE=$USER_GNATS_SITE
+   GNATS_ADDR=$USER_GNATS_SITE-gnats
+ fi
+ 
+ if [ "$SUBMITTER" = "unknown" -a -z "$IN_FILE" ]; then
+   cat << '__EOF__'
+ It seems that request-patch is not installed with your unique submitter-id.
+ You need to run
+ 
+           install-sid YOUR-SID
+ 
+ where YOUR-SID is your e-mail address. `request-patch' will automatically
+ insert this value into the template field `>Submitter-Id'.
+ __EOF__
+   exit 1
+ fi
+ 
+ if [ -r "$DATADIR/gnats/$GNATS_SITE" ]; then
+   CATEGORIES=`grep -v '^#' $DATADIR/gnats/$GNATS_SITE | sort`
+ else
+   echo "$COMMAND: could not read $DATADIR/gnats/$GNATS_SITE for categories list."
+   exit 1
+ fi
+ 
+ if [ -z "$CATEGORIES" ]; then
+   echo "$COMMAND: the categories list for $GNATS_SITE was empty!"
+   exit 1
+ fi
+ 
+ case "$FORMAT" in
+   lisp) echo "$CATEGORIES" | \
+         awk 'BEGIN {printf "( "} {printf "(\"%s\") ",$0} END {printf ")\n"}'
+         exit 0
+         ;;
+   norm) l=`echo "$CATEGORIES" | \
+ 	awk 'BEGIN {max = 0; } { if (length($0) > max) { max = length($0); } }
+ 	     END {print max + 1;}'`
+ 	c=`expr 70 / $l`
+ 	if [ $c -eq 0 ]; then c=1; fi
+ 	echo "$CATEGORIES" | \
+         awk 'BEGIN {print "Known categories:"; i = 0 }
+           { printf ("%-'$l'.'$l's", $0); if ((++i % '$c') == 0) { print "" } }
+             END { print ""; }'
+         exit 0
+         ;;
+ esac
+ 
+ NUMBER_C='<number of the problem report to patch (one line)>'
+ CATEGORY_C='<name of the product (one line)>'
+ RELEASE_C='<release number or tag (one line)>'
+ 
+ # Catch some signals. ($xs kludge needed by Sun /bin/sh)
+ xs=0
+ trap 'rm -f $REF $TEMP; exit $xs' 0
+ trap 'echo "$COMMAND: Aborting ..."; rm -f $REF $TEMP; xs=1; exit' 1 3 13 15
+ 
+ # If they told us to use a specific file, then do so.
+ if [ -n "$IN_FILE" ]; then
+   if [ "$IN_FILE" = "-" ]; then
+     # The PR is coming from the standard input.
+     if [ -n "$EXPLICIT_GNATS_ADDR" ]; then
+       sed -e "s;^[Tt][Oo]:.*;To: $GNATS_ADDR;" > $TEMP
+     else
+       cat > $TEMP
+     fi
+   else
+     # Use the file they named.
+     if [ -n "$EXPLICIT_GNATS_ADDR" ]; then
+       sed -e "s;^[Tt][Oo]:.*;To: $GNATS_ADDR;" $IN_FILE > $TEMP
+     else
+       cat $IN_FILE > $TEMP
+     fi
+   fi
+ else
+ 
+   if [ -n "$PR_FORM" -a -z "$PRINT_INTERN" ]; then
+     # If their PR_FORM points to a bogus entry, then bail.
+     if [ ! -f "$PR_FORM" -o ! -r "$PR_FORM" -o ! -s "$PR_FORM" ]; then
+       echo "$COMMAND: can't seem to read your template file (\`$PR_FORM'), ignoring PR_FORM"
+       sleep 1
+       PRINT_INTERN=bad_prform
+     fi
+   fi
+ 
+   if [ -n "$PR_FORM" -a -z "$PRINT_INTERN" ]; then
+     cp $PR_FORM $TEMP || 
+       ( echo "$COMMAND: could not copy $PR_FORM" ; xs=1; exit )
+   else
+     for file in $TEMP $REF ; do
+       cat  > $file << '__EOF__'
+ REQUEST-PATCH: -*- request-patch -*-
+ REQUEST-PATCH: Lines starting with `REQUEST-PATCH' will be removed
+ REQUEST-PATCH: automatically, as will all comments (text enclosed in
+ REQUEST-PATCH: `<' and `>').
+ REQUEST-PATCH: Please consult the request-patch man page `request-patch(1)'
+ REQUEST-PATCH: or the Texinfo manual if you are not sure how to fill out a
+ REQUEST-PATCH: a patch request for a problem report.
+ REQUEST-PATCH: Choose from the following categories:
+ REQUEST-PATCH:
+ __EOF__
+ 
+       # Format the categories so they fit onto lines.
+ 	l=`echo "$CATEGORIES" | \
+ 	awk 'BEGIN {max = 0; } { if (length($0) > max) { max = length($0); } }
+ 	     END {print max + 1;}'`
+ 	c=`expr 61 / $l`
+ 	if [ $c -eq 0 ]; then c=1; fi
+ 	echo "$CATEGORIES" | \
+         awk 'BEGIN {printf "REQUEST-PATCH: "; i = 0 }
+           { printf ("%-'$l'.'$l's", $0);
+ 	    if ((++i % '$c') == 0) { printf "\nREQUEST-PATCH: " } }
+             END { printf "\nREQUEST-PATCH:\n"; }' >> $file
+ 
+       cat >> $file << __EOF__
+ To: $GNATS_ADDR
+ Subject: Request For Patch
+ From: $FROM
+ Reply-To: $REPLYTO
+ Cc: $CC
+ X-request-patch-version: $VERSION
+ 
+ >Number:        $NUMBER_C
+ >Submitter-Id:	$SUBMITTER
+ >Category:	$CATEGORY_C
+ >Release:	${DEFAULT_RELEASE-$RELEASE_C}
+ __EOF__
+     done
+   fi
+ 
+   if [ "$PRINT" = true -o "$PRINT_INTERN" = true ]; then
+     cat $TEMP
+     xs=0; exit
+   fi
+ 
+   chmod u+w $TEMP
+   eval $EDIT $TEMP
+ 
+   if cmp -s $REF $TEMP ; then
+     echo "$COMMAND: problem report not filled out, therefore not sent"
+     xs=1; exit
+   fi
+ fi
+ 
+ #
+ #	Check the enumeration fields
+ 
+ # This is a "sed-subroutine" with one keyword parameter 
+ # (with workaround for Sun sed bug)
+ #
+ SED_CMD='
+ /$PATTERN/{
+ s|||
+ s|<.*>||
+ s|^[ 	]*||
+ s|[ 	]*$||
+ p
+ q
+ }'
+ 
+ while true; do
+   CNT=0
+   # Category
+   PATTERN=">Category:"
+   CATEGORY=`eval sed -n -e "\"$SED_CMD\"" $TEMP`
+   FOUND=
+   for C in $CATEGORIES
+   do
+     if [ "$C" = "$CATEGORY" ]; then FOUND=true ; break ; fi
+   done
+   if [ -n "$FOUND" ]; then
+     CNT=`expr $CNT + 1`	
+   else
+     if [ -z "$CATEGORY" ]; then
+       echo "$COMMAND: you must include a Category: field in your report."
+     else
+       echo "$COMMAND: \`$CATEGORY' is not a known category."
+     fi
+   fi
+ 
+   [ $CNT -lt 1 -a -z "$BATCH" ] && 
+     echo "Errors were found with the patch request for problem report."
+ 
+   while true; do
+     if [ -z "$BATCH" ]; then
+       $ECHON1 "a)bort, e)dit or s)end? $ECHON2"
+       read input
+     else
+       if [ $CNT -eq 1 ]; then
+         input=s
+       else
+         input=a
+       fi
+     fi
+     case "$input" in
+       a*)
+ 	if [ -z "$BATCH" ]; then
+ 	  echo "$COMMAND: the patch request for problem report remains in $BAD and is not sent."
+ 	  mv $TEMP $BAD
+         else
+ 	  echo "$COMMAND: the patch request for problem report is not sent."
+ 	fi
+ 	xs=1; exit
+ 	;;
+       e*)
+         eval $EDIT $TEMP
+ 	continue 2
+ 	;;
+       s*)
+ 	break 2
+ 	;;
+     esac
+   done
+ done
+ #
+ #	Remove comments and send the problem report
+ #	(we have to use patterns, where the comment contains regex chars)
+ #
+ # /^>Originator:/s;$ORIGINATOR;;
+ sed  -e "
+ /^REQUEST-PATCH:/d
+ /^>Number:/,/^>[A-Za-z-]*:/s;$NUMBER_C;;
+ /^>Category:/s;$CATEGORY_C;;
+ /^>Release:/,/^>[A-Za-z-]*:/s;$RELEASE_C;;
+ " $TEMP > $REF
+ 
+ if $MAIL_AGENT < $REF; then
+   echo "$COMMAND: problem report sent"
+   xs=0; exit
+ else
+   echo "$COMMAND: mysterious mail failure."
+   if [ -z "$BATCH" ]; then
+     echo "$COMMAND: the patch request for problem report remains in $BAD and is not sent."
+     mv $REF $BAD
+   else
+     echo "$COMMAND: the patch request for problem report is not sent."
+   fi
+   xs=1; exit
+ fi
Index: gnats-3.104-beta/send-pr/send-pr.sh
diff -c gnats-3.104-beta/send-pr/send-pr.sh:1.1.1.1 gnats-3.104-beta/send-pr/send-pr.sh:1.2
*** gnats-3.104-beta/send-pr/send-pr.sh:1.1.1.1	Thu Oct  9 21:37:29 1997
--- gnats-3.104-beta/send-pr/send-pr.sh	Wed Oct 15 22:15:49 1997
***************
*** 96,104 ****
  	fi
  fi
  
! FROM="$LOGNAME"
  if [ -z "$REPLYTO" ]; then
!   REPLYTO="$LOGNAME"
  fi
  
  # Find out the name of the originator of this PR.
--- 96,104 ----
  	fi
  fi
  
! FROM="$SUBMITTER"
  if [ -z "$REPLYTO" ]; then
!   REPLYTO="$SUBMITTER"
  fi
  
  # Find out the name of the originator of this PR.
***************
*** 256,261 ****
--- 256,262 ----
    SEVERITY_C='<[ non-critical | serious | critical ] (one line)>'
  fi
  PRIORITY_C='<[ low | medium | high ] (one line)>'
+ BILLING_INFO_C='<billing information (one line)>'
  CATEGORY_C='<name of the product (one line)>'
  CLASS_C='<[ sw-bug | doc-bug | change-request | support ] (one line)>'
  RELEASE_C='<release number or tag (one line)>'
***************
*** 336,341 ****
--- 337,343 ----
  
  
  >Submitter-Id:	$SUBMITTER
+ >Billing-Info:	$BILLING_INFO_C
  >Originator:	$ORIGINATOR
  >Organization:
  ${ORGANIZATION-	$ORGANIZATION_C}
***************
*** 502,507 ****
--- 504,510 ----
  /^SEND-PR:/d
  /^>Organization:/,/^>[A-Za-z-]*:/s;$ORGANIZATION_C;;
  /^>Confidential:/s;<.*>;;
+ /^>Billing-Info:/s;$BILLING_INFO_C;;
  /^>Synopsis:/s;$SYNOPSIS_C;;
  /^>Severity:/s;<.*>;;
  /^>Priority:/s;<.*>;;
Index: gnats-3.104-beta/send-pr/upgrade-pr.sh
diff -c /dev/null gnats-3.104-beta/send-pr/upgrade-pr.sh:1.6
*** /dev/null	Thu Oct 23 13:27:09 1997
--- gnats-3.104-beta/send-pr/upgrade-pr.sh	Tue Oct 21 17:18:49 1997
***************
*** 0 ****
--- 1,399 ----
+ #!/bin/sh
+ # Submit a request for billing status upgrade to a GNATS site.
+ # Copyright (C) 1993 Free Software Foundation, Inc.
+ # Contributed by Brendan Kehoe (brendan@cygnus.com), based on a
+ # version written by Heinz G. Seidl (hgs@cygnus.com).
+ #
+ # Hacked on the basis of send-pr by Sergey V. Vanskov (vanskov@aha.ru)
+ #
+ # 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 version of this upgrade-pr.
+ VERSION=xVERSIONx
+ 
+ # The submitter-id for your site.
+ SUBMITTER=xSUBMITTERx
+ 
+ # Where the GNATS directory lives, if at all.
+ [ -z "$GNATS_ROOT" ] && 
+ GNATS_ROOT=xGNATS_ROOTx
+ 
+ # The default mail address for PR submissions. 
+ GNATS_ADDR=xGNATS_ADDRx
+ 
+ # Where the gnats category tree lives.
+ DATADIR=xDATADIRx
+ 
+ # If we've been moved around, try using GCC_EXEC_PREFIX.
+ [ ! -d $DATADIR/gnats -a -d "$GCC_EXEC_PREFIX" ] && 
+   DATADIR=${GCC_EXEC_PREFIX}../../../`basename xDATADIRx`
+ 
+ # The default release for this host.
+ DEFAULT_RELEASE="xDEFAULT_RELEASEx"
+ 
+ # The default site to look for.
+ GNATS_SITE=xGNATS_SITEx
+ 
+ # Newer config information?
+ [ -f ${GNATS_ROOT}/gnats-adm/config ] && . ${GNATS_ROOT}/gnats-adm/config
+ 
+ # What mailer to use.  This must come after the config file, since it is
+ # host-dependent.
+ MAIL_AGENT="xMAIL_AGENTx"
+ 
+ ECHON=xECHONx
+ 
+ if [ $ECHON = bsd ] ; then
+   ECHON1="echo -n"
+   ECHON2=
+ elif [ $ECHON = sysv ] ; then
+   ECHON1=echo
+   ECHON2='\c'
+ else
+   ECHON1=echo
+   ECHON2=
+ fi
+ 
+ #
+ 
+ if [ -z "$TMPDIR" ]; then
+   TMPDIR=/tmp
+ else
+   if [ "`echo $TMPDIR | grep '/$'`" != "" ]; then
+     TMPDIR="`echo $TMPDIR | sed -e 's,/$,,'`"
+   fi
+ fi
+ 
+ TEMP=$TMPDIR/upgr$$
+ BAD=$TMPDIR/upgrbad$$
+ REF=$TMPDIR/upgrf$$
+ 
+ FROM="$SUBMITTER"
+ if [ -z "$REPLYTO" ]; then
+   REPLYTO="$SUBMITTER"
+ fi
+ 
+ # If they don't have a preferred editor set, then use
+ if [ -z "$VISUAL" ]; then
+   if [ -z "$EDITOR" ]; then
+     EDIT=vi
+   else
+     EDIT="$EDITOR"
+   fi
+ else
+   EDIT="$VISUAL"
+ fi
+ 
+ COMMAND=`echo $0 | sed -e 's,.*/,,'`
+ USAGE="Usage: $COMMAND [-PVL] [-t address] [-f filename]
+        [-c address] [--version]"
+ REMOVE=
+ BATCH=
+ CC=
+ 
+ while [ $# -gt 0 ]; do
+   case "$1" in
+     -t | --to) if [ $# -eq 1 ]; then echo "$USAGE"; exit 1; fi
+ 	shift ; GNATS_ADDR="$1"
+ 	EXPLICIT_GNATS_ADDR=true
+         ;;
+     -f | --file) if [ $# -eq 1 ]; then echo "$USAGE"; exit 1; fi
+ 	shift ; IN_FILE="$1"
+ 	if [ "$IN_FILE" != "-" -a ! -r "$IN_FILE" ]; then
+ 	  echo "$COMMAND: cannot read $IN_FILE"
+ 	  exit 1
+ 	fi
+ 	;;
+     -b | --batch) BATCH=true ;;
+     -c | --cc) if [ $# -eq 1 ]; then echo "$USAGE"; exit 1; fi
+ 	shift ; CC="$1"
+ 	;;
+     -p | -P | --print) PRINT=true ;;
+     -L | --list) FORMAT=norm ;;
+     -l | -CL | --lisp) FORMAT=lisp ;;
+     -h | --help) echo "$USAGE"; exit 0 ;;
+     -V | --version) echo "$VERSION"; exit 0 ;;
+     -*) echo "$USAGE" ; exit 1 ;;
+     *) if [ -z "$USER_GNATS_SITE" ]; then
+ 	 if [ ! -r "$DATADIR/gnats/$1" ]; then
+ 	   echo "$COMMAND: the GNATS site $1 does not have a categories list."
+ 	   exit 1
+ 	 else
+ 	   # The site name is the alias they'll have to have created.
+ 	   USER_GNATS_SITE=$1
+ 	 fi
+        else
+ 	 echo "$USAGE" ; exit 1
+        fi
+        ;;
+  esac
+  shift
+ done
+ 
+ if [ -n "$USER_GNATS_SITE" ] && [ "$USER_GNATS_SITE" != "$GNATS_SITE" ]; then
+   GNATS_SITE=$USER_GNATS_SITE
+   GNATS_ADDR=$USER_GNATS_SITE-gnats
+ fi
+ 
+ if [ "$SUBMITTER" = "unknown" -a -z "$IN_FILE" ]; then
+   cat << '__EOF__'
+ It seems that upgrade-pr is not installed with your unique submitter-id.
+ You need to run
+ 
+           install-sid YOUR-SID
+ 
+ where YOUR-SID is your e-mail address. `upgrade-pr' will automatically
+ insert this value into the template field `>Submitter-Id'.
+ __EOF__
+   exit 1
+ fi
+ 
+ if [ -r "$DATADIR/gnats/$GNATS_SITE" ]; then
+   CATEGORIES=`grep -v '^#' $DATADIR/gnats/$GNATS_SITE | sort`
+ else
+   echo "$COMMAND: could not read $DATADIR/gnats/$GNATS_SITE for categories list."
+   exit 1
+ fi
+ 
+ if [ -z "$CATEGORIES" ]; then
+   echo "$COMMAND: the categories list for $GNATS_SITE is empty!"
+   exit 1
+ fi
+ 
+ case "$FORMAT" in
+   lisp) echo "$CATEGORIES" | \
+         awk 'BEGIN {printf "( "} {printf "(\"%s\") ",$0} END {printf ")\n"}'
+         exit 0
+         ;;
+   norm) l=`echo "$CATEGORIES" | \
+ 	awk 'BEGIN {max = 0; } { if (length($0) > max) { max = length($0); } }
+ 	     END {print max + 1;}'`
+ 	c=`expr 70 / $l`
+ 	if [ $c -eq 0 ]; then c=1; fi
+ 	echo "$CATEGORIES" | \
+         awk 'BEGIN {print "Known categories:"; i = 0 }
+           { printf ("%-'$l'.'$l's", $0); if ((++i % '$c') == 0) { print "" } }
+             END { print ""; }'
+         exit 0
+         ;;
+ esac
+ 
+ CATEGORY_C='<name of the product (one line)>'
+ BILLING_INFO_C='<your billing information  PR  the problem report to upgrade (one line)>'
+ 
+ ENVIRONMENT_C='<machine, os, target, libraries (multiple lines)>'
+ DESCRIPTION_C='<precise description of the problem (multiple lines)>'
+ HOW_TO_REPEAT_C='<code/input/activities to reproduce the problem (multiple lines)>'
+ FIX_C='<how to correct or work around the problem, if known (multiple lines)>'
+ 
+ # Catch some signals. ($xs kludge needed by Sun /bin/sh)
+ xs=0
+ trap 'rm -f $REF $TEMP; exit $xs' 0
+ trap 'echo "$COMMAND: Aborting ..."; rm -f $REF $TEMP; xs=1; exit' 1 3 13 15
+ 
+ # If they told us to use a specific file, then do so.
+ if [ -n "$IN_FILE" ]; then
+   if [ "$IN_FILE" = "-" ]; then
+     # The PR is coming from the standard input.
+     if [ -n "$EXPLICIT_GNATS_ADDR" ]; then
+       sed -e "s;^[Tt][Oo]:.*;To: $GNATS_ADDR;" > $TEMP
+     else
+       cat > $TEMP
+     fi
+   else
+     # Use the file they named.
+     if [ -n "$EXPLICIT_GNATS_ADDR" ]; then
+       sed -e "s;^[Tt][Oo]:.*;To: $GNATS_ADDR;" $IN_FILE > $TEMP
+     else
+       cat $IN_FILE > $TEMP
+     fi
+   fi
+ else
+ 
+   if [ -n "$PR_FORM" -a -z "$PRINT_INTERN" ]; then
+     # If their PR_FORM points to a bogus entry, then bail.
+     if [ ! -f "$PR_FORM" -o ! -r "$PR_FORM" -o ! -s "$PR_FORM" ]; then
+       echo "$COMMAND: can't seem to read your template file (\`$PR_FORM'), ignoring PR_FORM"
+       sleep 1
+       PRINT_INTERN=bad_prform
+     fi
+   fi
+ 
+   if [ -n "$PR_FORM" -a -z "$PRINT_INTERN" ]; then
+     cp $PR_FORM $TEMP || 
+       ( echo "$COMMAND: could not copy $PR_FORM" ; xs=1; exit )
+   else
+     for file in $TEMP $REF ; do
+       cat  > $file << '__EOF__'
+ UPGRADE-PR: -*- upgrade-pr -*-
+ UPGRADE-PR: Lines starting with `UPGRADE-PR' will be removed automatically, as
+ UPGRADE-PR: will all comments (text enclosed in `<' and `>').
+ UPGRADE-PR: 
+ UPGRADE-PR: Please consult the upgrade-pr man page `upgrade-pr(1)'
+ UPGRADE-PR: or the Texinfo manual if you are not sure how to fill
+ UPGRADE-PR: out a problem report.
+ UPGRADE-PR:
+ UPGRADE-PR: Choose from the following categories:
+ UPGRADE-PR:
+ __EOF__
+ 
+       # Format the categories so they fit onto lines.
+ 	l=`echo "$CATEGORIES" | \
+ 	awk 'BEGIN {max = 0; } { if (length($0) > max) { max = length($0); } }
+ 	     END {print max + 1;}'`
+ 	c=`expr 61 / $l`
+ 	if [ $c -eq 0 ]; then c=1; fi
+ 	echo "$CATEGORIES" | \
+         awk 'BEGIN {printf "UPGRADE-PR: "; i = 0 }
+           { printf ("%-'$l'.'$l's", $0);
+ 	    if ((++i % '$c') == 0) { printf "\nUPGRADE-PR: " } }
+             END { printf "\nUPGRADE-PR:\n"; }' >> $file
+ 
+       cat >> $file << __EOF__
+ To: $GNATS_ADDR
+ Subject: Upgrade billing status
+ From: $FROM
+ Reply-To: $REPLYTO
+ Cc: $CC
+ X-upgrade-pr-version: $VERSION
+ 
+ >Submitter-Id:	$SUBMITTER
+ >Category:      $CATEGORY_C
+ >Billing-Info:  $BILLING_INFO_C
+ >Environment:
+ 	$ENVIRONMENT_C
+ >Description:
+ 	$DESCRIPTION_C
+ >How-To-Repeat:
+ 	$HOW_TO_REPEAT_C
+ >Fix:
+ 	$FIX_C
+ __EOF__
+     done
+   fi
+ 
+   if [ "$PRINT" = true -o "$PRINT_INTERN" = true ]; then
+     cat $TEMP
+     xs=0; exit
+   fi
+ 
+   chmod u+w $TEMP
+   eval $EDIT $TEMP
+ 
+   if cmp -s $REF $TEMP ; then
+     echo "$COMMAND: problem report not filled out, therefore not sent"
+     xs=1; exit
+   fi
+ fi
+ 
+ #
+ #	Check the enumeration fields
+ 
+ # This is a "sed-subroutine" with one keyword parameter 
+ # (with workaround for Sun sed bug)
+ #
+ SED_CMD='
+ /$PATTERN/{
+ s|||
+ s|<.*>||
+ s|^[ 	]*||
+ s|[ 	]*$||
+ p
+ q
+ }'
+ 
+ while true; do
+   CNT=0
+   # Category
+   PATTERN=">Category:"
+   CATEGORY=`eval sed -n -e "\"$SED_CMD\"" $TEMP`
+   FOUND=
+   for C in $CATEGORIES
+   do
+     if [ "$C" = "$CATEGORY" ]; then FOUND=true ; break ; fi
+   done
+   if [ -n "$FOUND" ]; then
+     CNT=`expr $CNT + 1`	
+   else
+     if [ -z "$CATEGORY" ]; then
+       echo "$COMMAND: you must include a Category: field in your report."
+     else
+       echo "$COMMAND: \`$CATEGORY' is not a known category."
+     fi
+   fi
+ 
+   [ $CNT -lt 1 -a -z "$BATCH" ] && 
+     echo "Errors were found with the upgrade status request."
+ 
+   while true; do
+     if [ -z "$BATCH" ]; then
+       $ECHON1 "a)bort, e)dit or s)end? $ECHON2"
+       read input
+     else
+       if [ $CNT -eq 1 ]; then
+         input=s
+       else
+         input=a
+       fi
+     fi
+     case "$input" in
+       a*)
+ 	if [ -z "$BATCH" ]; then
+ 	  echo "$COMMAND: the status upgrade request remains in $BAD and is not sent."
+ 	  mv $TEMP $BAD
+         else
+ 	  echo "$COMMAND: the status upgrade request is not sent."
+ 	fi
+ 	xs=1; exit
+ 	;;
+       e*)
+         eval $EDIT $TEMP
+ 	continue 2
+ 	;;
+       s*)
+ 	break 2
+ 	;;
+     esac
+   done
+ done
+ #
+ #	Remove comments and send the problem report
+ #	(we have to use patterns, where the comment contains regex chars)
+ #
+ sed  -e "
+ /^UPGRADE-PR:/d
+ /^>Category:/s;$CATEGORY_C;;
+ /^>Billing-Info:/s;$BILLING_INFO_C;;
+ /^>Environment:/,/^>[A-Za-z-]*:/s;$ENVIRONMENT_C;;
+ /^>Description:/,/^>[A-Za-z-]*:/s;$DESCRIPTION_C;;
+ /^>How-To-Repeat:/,/^>[A-Za-z-]*:/s;$HOW_TO_REPEAT_C;;
+ /^>Fix:/,/^>[A-Za-z-]*:/s;$FIX_C;;
+ " $TEMP > $REF
+ 
+ if $MAIL_AGENT < $REF; then
+   echo "$COMMAND: problem report sent"
+   xs=0; exit
+ else
+   echo "$COMMAND: mysterious mail failure."
+   if [ -z "$BATCH" ]; then
+     echo "$COMMAND: the status upgrade request remains in $BAD and is not sent."
+     mv $REF $BAD
+   else
+     echo "$COMMAND: the status upgrade request is not sent."
+   fi
+   xs=1; exit
+ fi
