rc-list - mailing list for the rc(1) shell
 help / color / mirror / Atom feed
From: Alan Watson <alan@oldp.astro.wisc.edu>
To: rc@hawkwind.utcs.toronto.edu
Subject: read
Date: Sat, 15 May 1993 00:14:16 -0400	[thread overview]
Message-ID: <9305150414.AA01457@oldp.astro.wisc.edu> (raw)

I know we've spent some time on this topic before, but I would like to
suggest that a read primitive should be added to rc.  The promise of one
over in the es list has thrown me into an evangelical mood.

(a) Boyd Roberts wrote in response to a suggestion to implement test
functionality in rc `The shell is there to run programs, not to have
programs built into it,' and I agree.  However, the shell should also
possess an appropriate set of primitives for linking programs together
in an efficient manner, and it is my opinion that the likes of echo and
read are essential for this capability.

(b) There have been many implementations of read posted to the list, and
most of them have been contained bugs.  Even the one in the EXAMPLES
file does not behave like a true sh read upon encountering EOF (it sets
the variable to '1', rather than unsetting it).

(c) There are reads and there are reads: some take only one argument,
others take multiple arguments; some split the input line according to
ifs, others don't; behaviour on EOF is almost entirely unpredictable.  A
builtin read, documented in the man page, would be completely portable.
(This alone justifies the builtin echo independently of performance
issues -- there's no uncertainty as to whether one is following SYSV or
BSD semantics when one uses echo in an rc script.  I might even go as
far as to say that the NOECHO option should be removed, and those people
with religeous objections can use a function to override the builtin and
get at /bin/echo, so that builtin echo will always be predicable.)

(d) External implementations of read are slow as hell, and this really
limits their usefulness.  (C'mon, Byron, you added echo just for this
reason!)

To me, the most important points are (c) and (d) (after all, it is a
simple matter to fix the read in the EXAMPLES file).  If we had an
agreed on definition of the semantics of read, I could implement it as
an addon and the rest of you could implement it as a function and we'd
all be happy.  For better or for worse, rc does not have a mechanism for
implementing `standard' functions, unlike es, so there is no method
available to universally instantiate such a definition other than as a
builtin.  

Code bload is not an issue -- this builtin adds about 80 very simple and
straightforward lines.  The only issues are whether read should take
multiple arguments, and whether it should perform ifs separation on the
line to make a list.

I know you're busy, Byron, so I'll append my implementation of a builtin
read at the end of this message, together with a trip.read.  This read
takes only one argument, does not perform ifs separation, does strip the
trailing newline, and unsets the variable when no input is read.
(Basically, it is a single-argument analogue of the v7 sh read.)

Alan.

; diff -e ORIG/addon.h addon.h
23a
#ifndef NOREAD
#undef  ADDONS
#define ADDONS { b_read,        "read" },
extern void b_read (char **av);
#endif

.
; diff -e ORIG/addon.c addon.c
5a
#include "rc.h"
#include "addon.h"

#ifndef NOREAD

/* 
 b_reads -- read a single line, terminated by \n or EOF, from the standard
 input, and assign the line without the terminator to av[1].
 */

static int
readchar (int fd)
{
   unsigned char c;

   if (read (fd, &c, 1) == 1)
      return c;
   else
      return EOF;
}

void
b_read (char **av)
{
   char *name;
   int c;
   char *line;
   SIZE_T len;
   SIZE_T max_len;
   SIZE_T max_len_quantum;

   /* check usage is "read name" */
   if (av[1] == NULL)
      rc_error ("missing variable name");
   if (av[2] != NULL)
      rc_error ("too many arguments to read");
   name = av[1];

   /* read a single line from stdin */
   line = NULL;
   len = 0;
   max_len = 0;
   max_len_quantum = 256;
   do {
      c = readchar (0);
      if (len == 0 && c == EOF)
	 break;
      if (c == '\n' || c == EOF)
	 c = 0;
      if (len == max_len) {
	 max_len += max_len_quantum;
	 line = erealloc (line, max_len);
      }
      line[len] = c;
      len++;
   } while (c);

   /* if we successfully read a line assign it to name and return TRUE, 
    * otherwise unset name and return FALSE */
   if (line != NULL) {
      assign (word (name, NULL), word (line, NULL), FALSE);
      efree (line);
      set (TRUE);
   } else {
      assign (word (name, NULL), NULL, FALSE);
      set (FALSE);
   }

   return;
}

#endif

.
; cat trip.read
# trip.read -- check a read implementation performs like a true v7 sh read

fn check {
   ./rc -c $2 >[2=]
   if ( ~ $1 0 ) {
      ~ $status 0 || echo failed: $2
   } else {
      ! ~ $status 0 || echo failed: $2
   }
}

check 0 true
check 1 false

nl = '
'
ifs = $nl

file = /etc/passwd
first = `{ cat $file }
first = $first(1)

echo 'start of trip.read'

# check read X and read '*' return 0
check 0 'exec <$file ; read X'
check 0 'exec <$file ; read ''*'''

# check read, read 1, read '' return !0
check 1 'exec <$file ; read'
check 1 'exec <$file ; read 1'
check 1 'exec <$file ; read '''''

# check read X correctly reads first line
check 0 'exec <$file ; read X ; ~ $X $first'

# check read '*' correctly reads first line, preserves $0
check 0 'exec <$file ; old0 = $0 ; read ''*'' ; ~ $0 $old0 && ~ $1 $first && ~ $#* 1'

# check read returns !0 on EOF and I/O error
check 1 'exec </dev/null ; read X'
check 1 'exec <[0=]      ; read X'

# check read sets variable to () on EOF and I/O error.
check 0 '{ exec </dev/null ; X = X ; read X ; ~ X () }'
check 0 '{ exec <[0=]      ; X = X ; read X ; ~ X () }'

echo 'end of trip.read'

# end-of-file


             reply	other threads:[~1993-05-15  4:14 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
1993-05-15  4:14 Alan Watson [this message]
1993-05-16  6:49 read Alan Watson
1993-05-16  7:12 ` read Chris Siebenmann
1993-05-16  7:51 read Alan Watson
1993-05-18  2:13 read Alan Watson

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=9305150414.AA01457@oldp.astro.wisc.edu \
    --to=alan@oldp.astro.wisc.edu \
    --cc=rc@hawkwind.utcs.toronto.edu \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).