NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: bin/45390: more sh/$PWD follies



The following reply was made to PR bin/45390; it has been noted by GNATS.

From: Robert Elz <kre%munnari.OZ.AU@localhost>
To: gnats-bugs%netbsd.org@localhost
Cc: 
Subject: Re: bin/45390: more sh/$PWD follies
Date: Thu, 28 Oct 2021 04:53:57 +0700

 apb (back in 2011) replied to most of this PR.   Since then it has languished.
 
 The first and second reported follies were user finger/memory trouble.
 
 The third:
 
  - cd is supposed to print the name of the directory it changes into
    if it is not the name given, and the shell is interactive, but it
    does not:
 
 does not match what POSIX specifies, which is:
 
 STDOUT
     If a non-empty directory name from CDPATH is used, or if the operand '-'
     is used, an absolute pathname of the new working directory shall be
     written to the standard output as follows:
 
             "%s\n", <new directory>
 
     Otherwise, there shall be no output.
 
 Notice nothing about interactive, nor "not the name given" in the sense
 used in the example given in the PR.
 
 Our man page currently says:
 
     In an interactive shell, the cd command will print out the name of
     the directory that it actually switched to if this is different
     from the name that the user gave, or always if the cdprint option
     is set.  The destination may be different either because the
     CDPATH mechanism was used or if the replace argument was used.
 
 [Of course, nothing in POSIX about a cdprint option, that is an ash shell
 extension, and irrelevant here.  The "replace argument" we copied from ksh,
 or so the comments say, and is also not in POSIX, but printing in that case
 is consistent with the objective.]
 
 I plan on changing out man page to be:
 
      In an interactive shell, or if the posix option is set, the cd
      command will print out the name of the directory that it actually
      switched to if this is different from the name that the user gave,
      or if the cdprint option is set.  The destination may be different
      because a non-empty element of the CDPATH mechanism was used, or
      because the replace argument was used, or because the directory
      parameter was specified as "-".
 
 This will make sh posix conformant in this area, if in posix mode -- almost
 all other shells simply ignore "interactive" for this, and print, whether or
 not ... but for compat with our existing behaviour, I am currently planning
 on only adding the "|| posix" to the test for interactive, not simply removing
 the latter.
 
    [Aside: what cd really tests is the 'i' option, which gets set automatically
     in interactive shells, but can also be turned on and off, with very little
     other effect, any time at all.]
 
 There will also be a small change to the way that the "CDPATH mechanism
 was used" test, but it is quite unlikely that that difference will be
 noticed by almost anyone.   Aside from the man page not mentioning the "-"
 case (it is implemented like that already), adding the test of posix mode
 as an alternative to interactive, and the minor CDPATH use test change, this
 is what we have now.
 
 Note: I am not saying that the implmentation is as it was in 2011 when
 the PR was filed, there have been changes in this area in the past decade,
 but there don't seem to be any specific source changes required any more
 just to fix anything in the PR.
 
 There is one more issue here - note that POSIX requires "an absolute
 pathname" to be written - which is clearly not always possible (and POSIX
 needs to be fixed, I plan on filing a bug report with them about it).
 Most other shells simply write the (usually in this case) relative path
 name used (not the user's arg, if it was that, there would be no output,
 so we're in a case where CDPATH was used - the "-" and replace cases both
 result in absolute path names, so will rarely succeed, without allowing
 the path name to be discovered).   Some shells (sometimes) write an error
 or warning message, but no path name in that case (that's what we have
 been doing), zsh pretends a relative path is absolute, and writes that
 (which is beyond dumb, and probably just a bug) and yash hopes that PWD
 (if set) was correct, and uses that to deduce the new absolute path name
 (which is clever, though only a guess, and more work than this obscure
 case is worth).   I plan on copying most of the other shells and writing
 whatever path we have available (the path actually passed to chdir(2))
 when we cannot determine the absolute path.   My guess is (because of the
 divergence of what happens) POSIX will end up saying that it is unspecifed
 what, if anything, will be output in this case.
 
 
 The fourth folly:
 
  - pwd -L is not supposed to print the cached internal value (which is
    related to but separate from $PWD) unless it names the current
    directory. However, at least in some circumstances it will, such as
    this:
 
 As apb said, that one is a bug.   I have that fixed in my private sources
 (not yet committed).   As the example in the PR might indicate, it isn't
 a bug that anyone is going to encounter very often!
 
 Also on this one, relating to the note in the PR
 	the cached internal value (which is
         related to but separate from $PWD)
 that is correct, they are separate, but they're only ever different
 if the user (or script) has modified PWD - and POSIX says that if that
 happens, what cd and pwd end up doing is unspecified, so the difference
 isn't really material.   This paragraph isn't really material to the PR
 either of course, just an observation.
 
 And the fifth (and final?) folly:
 
  - cd is supposed to both change to the named directory and update
    $PWD with it, but it does not always do this correctly:
 
 This one was fixed as part of an earlier cd cleanup (sometime before
 NetBSD 8 I believe), and the example given seems to work correctly
 to me in all currently supported shells.
 
 
 There's one more issue in the PR, (perhaps not classified as a folly,
 that isn't clear):
 
 	Also, the man page is not explicit about what "update PWD with the
 	specified directory" actually means if the specified directory is not
 	absolute. The wording needs to be improved.
 
 Our man page currently says (I didn't check what it said in 2011):
 
      The -P option instructs the shell to update PWD with the specified
      physical directory path and change to that directory.  This is the
      default.
 
 I agree that is (still) not as clear as it could be, and propose changing
 it to be:
 
      The -P option (which is the unalterable default in this sh)
      instructs the shell to change to the directory specified (or
      determined) and if successful update PWD with the new physical
      directory path.  That is the path name, not traversing any
      symbolic links, of the altered working directory of the shell.
 
 (The man page also says that we do not support the -L option, I have no
 plans to alter that, either in the code or the man page, it also mentions
 that OLDPWD gets updated, which needs no alteration.)
 
 In the forthcoming edition, POSIX is going to add a new option, -e, to cd.
 This is already supported by a few shells (including FreeBSD's, but also
 bash and mksh), its purpose is to allow cd to exit(1) if it successfully
 changes directory, but is unable to determine the new path for PWD.
 Without this option, cd is required to exit(0) if the directory is changed
 (regardless of what happens to PWD, or any output that might be printed).
 
 I have implemented this new option, which will be documented as:
 
       The -e option alters the interpretation of the exit status.  cd
       will exit with status 0 upon complete success.  If the directory
       was successfully changed, but PWD was unable to be updated, cd
       will exit with status 1 if the -e option was given, and status 0
       otherwise.  Upon any other error, including usage errors, and
       failing to successfully change directory, cd will exit with status 2.
 
 POSIX is not as specific on what the status will be, without -e, our "2"
 is just required to be >0, with -e, it is required to be >1 - as we always
 use status 2 for internal shell detected errors, and 2>0 and 2>1 this all
 just magically works!
 
 Lastly, there has been some discussion on the austin group lists about
 forcing (most) utilities that write to stdout to actually check the status
 of that output and exit(1) on write failure.   cd would not be one such
 (errors writing the new directory name will continue to be ignored),  but
 pwd is included.
 
 The effect is that very few POSIX specified utilities which write to
 stdout will be able to simply
 	do { stuff; } while (!finished); printf(whatever); exit(0);
 they will be required to (if no other errors have occurred) insert
 	fflush(stdout);
 	if (ferror(stdout))
 		 fprintf(stderr, "output error\n"), exit(1);
 before the
 	exit(0);
 
 [I believe some versions of linux libc stdio install an atexit() routine
 which does the all this, so there is no need to modify the applications, but
 that kind of hidden alteration to the exit status is evil.   It obviously
 doesn't help with shell builtins, for which there is no real exit().]
 
 IMO this is going too far (I don't much care about pwd, having it exit(1)
 in case of a write error is easy, and it isn't hard to argue that
 echo and printf should be the same (the shell's builtin echo already does
 that, has for a long time, though /bin/echo doesn't).   But for most commands
 which do something, and merely incidentally write output, it isn't really.
 
 This is relevant (just barely) here, as it is related to the shell's pwd
 which is one of the issues of the PR...
 
 I did a (private) change to the shell's pwd to make this happen for it,
 when it was being discussed on the austin list (it's just a couple of lines)
 but as it is intended to apply to almost everything, I thought I'd make it
 more general, and apply to all of the sh builtin commands (if nothing is
 written to stdout, flushing it and checking for error is harmless).  But
 then I started seeing all the exceptions that would need to be handled for
 this to be sane ... I thought of modifying our (shell source) builtins
 description file (unless you look at sh sources you never see this) to add
 a new option for "check stdout" which itself would be easy - but that change
 then needs to be passed back to where the builtin is being executed, and
 doing that would require changes all over.   So I just gave up, and removed
 everything related.   I could put back the pwd check though if it is considered
 useful -- personally I have never seen a script check the exit status of
 any command which simply writes info to stdout, nor are write errors to
 stdout almost ever experienced (other than EPIPE, which usually turns into
 SIGPIPE, and so is rarely seen as an exit status).   This issue was raised
 by someone doing torture tests on a linux system running
 	pwd >/dev/full; echo $0
 and getting 0 (from mksh I think on the system in question).   (/dev/full
 simply returns ENOSPC to any write attempt, no idea what it does on reads).
 
 I will wait a day or two to see if there are any comments on all of this
 before committing the changes referred to above, or perhaps changing some
 of them first.
 
 kre
 


Home | Main Index | Thread Index | Old Index