[net.lang.c] Can anyone find a system on which this doesn't work?

rf@wu1.UUCP (11/20/84)

Does anyone know of any system on which the acat() routine in
the program test.c (attached) does not work?  Acat is part of a
string management package -- an allocating concatenate routine.
It takes up to 10 strings, allocates storage for their
concatenation and concatenates them.

The output of test.c is given.  Test.c will end with the error
message "test: More than 10 strings in acat()".


				Randolph Fritz
UUCPnet:			{ihnp4,decvax}!philabs!wu1!rf

"Rust and moth are the only true Critics; . . ."
				-- "The True Critics", Paul Edwin Zimmer


----------------- output of test.c

1
122
122333
1223334444
122333444455555
122333444455555666666
1223334444555556666667777777
122333444455555666666777777788888888
122333444455555666666777777788888888999999999
----------------- test.c

#include <stdio.h>

typedef int VOID;
typedef unsigned int COUNT;
typedef char TEXT;
typedef int BOOL;
#define EOS	'\0'		/* End-of-string */
#define NO	((BOOL) 0)	/* Logic false */
#define YES	((BOOL) 1)	/* Logic true */

TEXT *acat(), *stralloc();

main () {

   
   fprintf (stdout, "%s\n", acat("", NULL));
   fflush (stdout);
   fprintf (stdout, "%s\n", acat("","1", NULL));
   fflush (stdout);
   fprintf (stdout, "%s\n", acat("","1","22", NULL));
   fflush (stdout);
   fprintf (stdout, "%s\n", acat("","1","22","333", NULL));
   fflush (stdout);
   fprintf (stdout, "%s\n", acat("","1","22","333","4444", NULL));
   fflush (stdout);
   fprintf (stdout, "%s\n", acat("","1","22","333","4444","55555", NULL));
   fflush (stdout);
   fprintf (stdout, "%s\n", acat("","1","22","333","4444","55555","666666",
      NULL));
   fflush (stdout);
   fprintf (stdout, "%s\n", acat("","1","22","333","4444","55555","666666",
      "7777777", NULL));
   fflush (stdout);
   fprintf (stdout, "%s\n", acat("","1","22","333","4444","55555","666666",
      "7777777","88888888", NULL));
   fflush (stdout);
   fprintf (stdout, "%s\n", acat("","1","22","333","4444","55555","666666",
      "7777777","88888888","999999999", NULL));
   fflush (stdout);
   fprintf (stdout, "%s\n", acat("","1","22","333","4444","55555","666666",
      "7777777","88888888","999999999","10101010101010101010", NULL));
   fflush (stdout);

}


/* acat - allocating string concatenate (for up to 10 strings)

This (believed to be) portable routine will concatenate up to 10
strings.  Acat() is invoked by:

   newstring = acat (string1, string2 , . . . stringn, NULL);

  Acat is by no means elegant -- it even contains (horrors!) a
goto -- but it provides an amazingly useful function.

 */

TEXT *
acat (s0,s1,s2,s3,s4,s5,s6,s7,s8,s9,s10)
   TEXT *s0, *s1, *s2, *s3, *s4, *s5, *s6, *s7, *s8, *s9, *s10; {
   TEXT *instr[11];
   COUNT newlen, nstr, i;
   TEXT *news, *np, *op;
   
   nstr = 0;
   if (s0!=NULL)
      instr[nstr++] = s0;
   else
      goto got_em;
   if (s1!=NULL)
      instr[nstr++] = s1;
   else
      goto got_em;
   if (s2!=NULL)
      instr[nstr++] = s2;
   else
      goto got_em;
   if (s3!=NULL)
      instr[nstr++] = s3;
   else
      goto got_em;
   if (s4!=NULL)
      instr[nstr++] = s4;
   else
      goto got_em;
   if (s5!=NULL)
      instr[nstr++] = s5;
   else
      goto got_em;
   if (s6!=NULL)
      instr[nstr++] = s6;
   else
      goto got_em;
   if (s7!=NULL)
      instr[nstr++] = s7;
   else
      goto got_em;
   if (s8!=NULL)
      instr[nstr++] = s8;
   else
      goto got_em;
   if (s9!=NULL)
      instr[nstr++] = s9;
   else
      goto got_em;
   if (s10!=NULL)
      bugout ("More than 10 strings in acat()\n");
got_em:
   instr[nstr] = NULL;
   
   newlen = 0;
   for (i=0; i<nstr; i++)
      newlen += strlen(instr[i]);
   newlen += 1;		/* Allow space for an EOS */
   news = stralloc(newlen);
   np = news;
   for (i=0; i<nstr; i++) {
      op = instr[i];
      while ( (*np++ = *op++)!=EOS )
         ;
      np--;
      }

   return news;
   }


TEXT *progname = "test";

/* bugout - what to do when totally stuck or confused

Writes s to standard error, prefixed by the program name, then
exits with the "bomb" return code, 3.

 */

VOID
bugout (s)
   TEXT *s; {
   extern TEXT *progname;
   
   fprintf (stderr, "%s: %s", progname, s);
   exit (3);
   }

/* stralloc - string allocate

   Allocate n characters using calloc.  Bug out if there's no storage to get.
This routine really ought not use calloc.

 */

TEXT *
stralloc(n)
   COUNT n; {
   TEXT *news;
   
   news = (TEXT *) calloc(n,sizeof(TEXT));
   if (news==NULL)
      bugout ("Out of main memory in stralloc()\n");
   return news;
   }

henry@utzoo.UUCP (Henry Spencer) (11/22/84)

> Does anyone know of any system on which the acat() routine in
> the program test.c (attached) does not work?

Dozens of them.  Acat is taking variable-length argument lists, which
is the kiss of death for portability.  Declaring lots of parameters
and then using them in order is highly machine-dependent; it makes
major assumptions about how the argument list is laid out in memory.
Using <varargs.h> would be a lot better, but even that is not perfect.
There exist machines on which <varargs.h> is unimplementable.
-- 
				Henry Spencer @ U of Toronto Zoology
				{allegra,ihnp4,linus,decvax}!utzoo!henry

john@physiol.OZ (John Mackin) (11/30/84)

In article <4669@utzoo.UUCP>, henry@utzoo.UUCP writes:

> There exist machines on which <varargs.h> is unimplementable.

I'd be very interested in some specific examples.  Machines
already running UNIX, please, or likely to run it: not
antiques.

John Mackin, Physiology Department, University of Sydney, Sydney, Australia
...!decvax!mulga!physiol.su.oz!john

henry@utzoo.UUCP (Henry Spencer) (12/02/84)

> > There exist machines on which <varargs.h> is unimplementable.
> 
> I'd be very interested in some specific examples.  Machines
> already running UNIX, please, or likely to run it: not
> antiques.

The PERQ, on which the parameter list of a function must have a constant
length, the same length for all calls.  I studied compiling C for the
PERQ as part of my M.Sc. thesis, and friends of mine actually carried
this to a full implementation for a commercial UNIX port.  It was a
memorable bad experience for them, by the way, and the company would
rather forget it ever did that port (which is why I'm not identifying
either the people or the company).

<varargs.h> is also rather hard on machines like the Z8000, where the
most common compilers pass parameters in registers.  It's not utterly
impossible on the Z8000, but the principle easily generalizes to
almost arbitrary difficulty.  (E.g., what if different datatypes go in
different register sets?)
-- 
				Henry Spencer @ U of Toronto Zoology
				{allegra,ihnp4,linus,decvax}!utzoo!henry

jeff@gatech.UUCP (Jeff Lee) (12/06/84)

> In article <4669@utzoo.UUCP>, henry@utzoo.UUCP writes:
> 
> > There exist machines on which <varargs.h> is unimplementable.
> 
> I'd be very interested in some specific examples.  Machines
> already running UNIX, please, or likely to run it: not
> antiques.
> 
> John Mackin, Physiology Department, University of Sydney, Sydney, Australia
> ...!decvax!mulga!physiol.su.oz!john

In a C compiler that was implemented on our Primes, the only way to provide
it was to have the <varargs.h> package declare as many arguments as (hopefully)
would ever be used and hope that was enough. I think this is a hack. It works
(barely) but if any other machines have any more restrictions then it would be
impossible. The problem is that procedure entry handles the stack allocation
for you (thanks a lot). It has the number of parameters is kept in the entry
control block and it will not transfer any more arguments than are accounted
for in the control block. This is actually more serious than it may seem,
because the parameter pointers are kept in procedure space following the
procedure call which means that the return program counter (not accessible,
really) is not bumped past the arguments and you end up executing data
pointers. Ok, so we can allocate a possible 50 arguments (we actually use 10)
but then you run into the problem that it takes 2.4 microseconds per actual
argument EXPECTED (on a P400, past the initial 12.6 microseconds for the PCL).
This makes those calls seem REAL LONG. The result is that <varargs.h> is
supplied but you pay for it and it isn't a general purpose facility.

-- 
Jeff Lee
CSNet:	Jeff @ GATech		ARPA:	Jeff.GATech @ CSNet-Relay
uucp:	...!{akgua,allegra,rlgvax,sb1,unmvax,ulysses,ut-sally}!gatech!jeff