From zgabor@code.hu  Thu Jul 25 06:57:02 1996
Received: from mail.EUnet.hu (mail.eunet.hu [193.225.28.100])
          by freefall.freebsd.org (8.7.5/8.7.3) with ESMTP id GAA29909
          for <FreeBSD-gnats-submit@freebsd.org>; Thu, 25 Jul 1996 06:56:58 -0700 (PDT)
Received: by mail.EUnet.hu, id PAA21023; Thu, 25 Jul 1996 15:56:53 +0200
Received: from zg.CoDe.hu by CoDe.CoDe.hu (PAA01418); Thu, 25 Jul 1996 15:55:52 GMT
Received: (from zgabor@localhost) by zg.CoDe.hu (8.6.12/8.6.12) id QAA01059; Thu, 25 Jul 1996 16:18:53 +0200
Message-Id: <199607251418.QAA01059@zg.CoDe.hu>
Date: Thu, 25 Jul 1996 16:18:53 +0200
From: Zahemszky Gabor <zgabor@code.hu>
Reply-To: zgabor@code.hu
To: FreeBSD-gnats-submit@freebsd.org
Subject: sh(1) and getopts
X-Send-Pr-Version: 3.2

>Number:         1429
>Category:       bin
>Synopsis:       sh(1) and getopts
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    cracauer
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Thu Jul 25 07:00:02 PDT 1996
>Closed-Date:    Tue May 5 15:08:41 MEST 1998
>Last-Modified:  Tue May  5 15:09:52 MEST 1998
>Originator:     Zahemszky Gabor
>Release:        FreeBSD 2.1.0-RELEASE i386
>Organization:
>Environment:

	2.1R CD from Walnut Creek

>Description:

	The builtin getopts command of sh(1) is not the POSIX getopts as the
	manual says.
	1) The OPTIND variable should be 1 at the initialization
	2) The OPTIND variable should be handle in the getopts cycle, not
	only at the end
	3) On an illegal option, the OPTARG variable should set to the
	wrong option
	4) On an option, which has missing the argument, the OPTARG should
	set to the option, and the ``getopts options var'' list's
	var has to set to : instead of ?
	5) If the options begins with :, error messages shouldn't print.
	6) We should run more and more getopts in one shell invocation, with
	setting OPTIND to 1
	7) getopts should handle other options, not only the "$@", so
	we can use it: getopts options var optionlist

>How-To-Repeat:

	

>Fix:
	
	Here is my patches, which made getopts do 1-6.  (The 7 is not too
	hard, I think, but I havent enough time to look into the code
	Only one another note:
	To make the :options stuff working, this patch uses a simple local
	variable and a test on every invocation of getopts.  If it would be
	better (it's a bit more quick), we should use a static local
	variable, and the test made in only getopts initialization.

	Here are the two needed diffs, and after it the diff with the
	static variable:
===========================================
*** var.c.orig	Tue May 30 02:07:24 1995
--- var.c	Thu Jul 25 15:39:37 1996
***************
*** 76,81 ****
--- 76,82 ----
  struct var vifs;
  struct var vmail;
  struct var vmpath;
+ struct var voptind;
  struct var vpath;
  struct var vps1;
  struct var vps2;
***************
*** 92,97 ****
--- 93,99 ----
  	{&vifs,	VSTRFIXED|VTEXTFIXED,		"IFS= \t\n"},
  	{&vmail,	VSTRFIXED|VTEXTFIXED|VUNSET,	"MAIL="},
  	{&vmpath,	VSTRFIXED|VTEXTFIXED|VUNSET,	"MAILPATH="},
+ 	{&voptind,	VSTRFIXED|VTEXTFIXED,		"OPTIND=1"},
  	{&vpath,	VSTRFIXED|VTEXTFIXED,		"PATH=:/bin:/usr/bin"},
  	/*
  	 * vps1 depends on uid

------
===========================================
*** options.c.orig	Tue Oct 10 02:04:38 1995
--- options.c	Thu Jul 25 16:00:22 1996
***************
*** 346,354 ****
--- 346,361 ----
  	register char *p, *q;
  	char c;
  	char s[10];
+ 	char tmp_ptr[ 2 ];
+ 	char need_error_msg = 1;
  
  	if (argc != 3)
  		error("Usage: getopts optstring var");
+ 	p = lookupvar( "OPTIND" );
+ 	if ( ( p != NULL ) && ( *p == '1' ) && ( *( p + 1 ) == '\0' ) )
+ 		shellparam.optnext = NULL;
+ 	if ( argv[ 1 ][ 0 ] == ':' )
+ 		need_error_msg = 0;
  	if (shellparam.optnext == NULL) {
  		shellparam.optnext = shellparam.p;
  		shellparam.optptr = NULL;
***************
*** 369,375 ****
  	c = *p++;
  	for (q = argv[1] ; *q != c ; ) {
  		if (*q == '\0') {
! 			out1fmt("Illegal option -%c\n", c);
  			c = '?';
  			goto out;
  		}
--- 376,386 ----
  	c = *p++;
  	for (q = argv[1] ; *q != c ; ) {
  		if (*q == '\0') {
! 			tmp_ptr[ 0 ] = c;
! 			tmp_ptr[ 1 ] = '\0';
! 			setvar("OPTARG", tmp_ptr, 0);
! 			if ( need_error_msg )
! 				out1fmt("Illegal option -%c\n", c);
  			c = '?';
  			goto out;
  		}
***************
*** 378,391 ****
  	}
  	if (*++q == ':') {
  		if (*p == '\0' && (p = *shellparam.optnext++) == NULL) {
! 			out1fmt("No arg for -%c option\n", c);
! 			c = '?';
  			goto out;
  		}
  		setvar("OPTARG", p, 0);
  		p = NULL;
  	}
  out:
  	shellparam.optptr = p;
  	s[0] = c;
  	s[1] = '\0';
--- 389,409 ----
  	}
  	if (*++q == ':') {
  		if (*p == '\0' && (p = *shellparam.optnext++) == NULL) {
! 			shellparam.optnext--;
! 			tmp_ptr[ 0 ] = c;
! 			tmp_ptr[ 1 ] = '\0';
! 			setvar("OPTARG", tmp_ptr, 0);
! 			if ( need_error_msg )
! 				out1fmt("No arg for -%c option\n", c);
! 			c = ':';
  			goto out;
  		}
  		setvar("OPTARG", p, 0);
  		p = NULL;
  	}
  out:
+ 	fmtstr(s, 10, "%d", shellparam.optnext - shellparam.p + 1);
+ 	setvar("OPTIND", s, 0);
  	shellparam.optptr = p;
  	s[0] = c;
  	s[1] = '\0';

===========================================
-----
The static version's patch:
-----
===========================================
*** options.c.orig	Tue Oct 10 02:04:38 1995
--- options.c	Thu Jul 25 16:15:41 1996
***************
*** 346,355 ****
--- 346,362 ----
  	register char *p, *q;
  	char c;
  	char s[10];
+ 	char tmp_ptr[ 2 ];
+ 	static char need_error_msg = 1;
  
  	if (argc != 3)
  		error("Usage: getopts optstring var");
+ 	p = lookupvar( "OPTIND" );
+ 	if ( ( p != NULL ) && ( *p == '1' ) && ( *( p + 1 ) == '\0' ) )
+ 		shellparam.optnext = NULL;
  	if (shellparam.optnext == NULL) {
+ 		if ( argv[ 1 ][ 0 ] == ':' )
+ 			need_error_msg = 0;
  		shellparam.optnext = shellparam.p;
  		shellparam.optptr = NULL;
  	}
***************
*** 369,375 ****
  	c = *p++;
  	for (q = argv[1] ; *q != c ; ) {
  		if (*q == '\0') {
! 			out1fmt("Illegal option -%c\n", c);
  			c = '?';
  			goto out;
  		}
--- 376,386 ----
  	c = *p++;
  	for (q = argv[1] ; *q != c ; ) {
  		if (*q == '\0') {
! 			tmp_ptr[ 0 ] = c;
! 			tmp_ptr[ 1 ] = '\0';
! 			setvar("OPTARG", tmp_ptr, 0);
! 			if ( need_error_msg )
! 				out1fmt("Illegal option -%c\n", c);
  			c = '?';
  			goto out;
  		}
***************
*** 378,391 ****
  	}
  	if (*++q == ':') {
  		if (*p == '\0' && (p = *shellparam.optnext++) == NULL) {
! 			out1fmt("No arg for -%c option\n", c);
! 			c = '?';
  			goto out;
  		}
  		setvar("OPTARG", p, 0);
  		p = NULL;
  	}
  out:
  	shellparam.optptr = p;
  	s[0] = c;
  	s[1] = '\0';
--- 389,409 ----
  	}
  	if (*++q == ':') {
  		if (*p == '\0' && (p = *shellparam.optnext++) == NULL) {
! 			shellparam.optnext--;
! 			tmp_ptr[ 0 ] = c;
! 			tmp_ptr[ 1 ] = '\0';
! 			setvar("OPTARG", tmp_ptr, 0);
! 			if ( need_error_msg )
! 				out1fmt("No arg for -%c option\n", c);
! 			c = ':';
  			goto out;
  		}
  		setvar("OPTARG", p, 0);
  		p = NULL;
  	}
  out:
+ 	fmtstr(s, 10, "%d", shellparam.optnext - shellparam.p + 1);
+ 	setvar("OPTIND", s, 0);
  	shellparam.optptr = p;
  	s[0] = c;
  	s[1] = '\0';
>Release-Note:
>Audit-Trail:
Responsible-Changed-From-To: freebsd-bugs->joerg 
Responsible-Changed-By: joerg 
Responsible-Changed-When: Sat Jul 27 19:23:03 MET DST 1996 
Responsible-Changed-Why:  
I still intend to deal with all the sh(1) bugs some day. 
Responsible-Changed-From-To: joerg->steve 
Responsible-Changed-By: steve 
Responsible-Changed-When: Tue Oct 15 19:12:32 PDT 1996 
Responsible-Changed-Why:  
I am working on this one. 
State-Changed-From-To: open->feedback 
State-Changed-By: phk 
State-Changed-When: Tue Apr 14 12:10:31 PDT 1998 
State-Changed-Why:  
has this been fixed ? 
Responsible-Changed-From-To: steve->cracauer 
Responsible-Changed-By: steve 
Responsible-Changed-When: Tue Apr 28 05:57:32 PDT 1998 
Responsible-Changed-Why:  
Martin said he would take this one. 
State-Changed-From-To: feedback->closed 
State-Changed-By: cracauer 
State-Changed-When: Tue May 5 15:08:41 MEST 1998 
State-Changed-Why:  
I just checked this old one: 

Point 1) This has already been integrated in version 1.7 of var.c when 
Steve merged NetBSD changes in December 1996. 

Point 2) I have no idea why this should be useful. We currently don't 
do it. 

Point 3) and Point 4) We don't do what the PR suggests. I'm not sure 
the behavior Zahemszky describes is what current Posix says 
(Zahemszky, do you copy, can you give me an update?). Our current 
behavior is exactly the same as pdksh and bash2, so I leave it as it 
is until I get my Posix specs (that is - the money...). 

Point 5) This already works in our sh. Probably from the NetBSD merge 
as well. 

Point 6) Not implemented in our sh or in this PR. 
>Unformatted:

