[comp.lang.eiffel] formated string objects

fcaggian@kepler.com (Frank Caggiano) (01/05/91)

The following c routine is something I've wanted to write for
a while now.  It makes creating formatted string objects 
easier.  

Its an sprintf like routine but it returns a string object.

The call and arguments are the same as for the [sf]printf c routines.

Declare it as 

	external format_obj: STRING language "C"

Usage colud then be something like :

	local
		s: STRING
	do
		s := format_obj("%2d %-5s%6d%8.3f%5d%7d%6d%4d",
				n,
				stock_list.item.ticker.to_c,
				stock_list.item.amount, 
				price / 256.0,
				ktotal / 1000,
				stock_list.item.position,
				stock_list.item.pending,
				stock_list.item.form
				);
This was written and tested on a space running 4.0.3.  It should
be poratable.  Please let me know of any problems especially with
the varargs stuff. 


********* BEGIN INCLUDED CODE

 /*
  -- Copyright (c) 1990 Kepler Financial Mgmt. 
  --
  -- Permission is granted to anyone to make or distribute
  -- copies of this software  provided that the
  -- copyright notice and this permission notice are preserved.
  -- Ownership remains with Kepler Financial Mgmt.
  */
#ifndef lint
    static char *sccsid = "@(#)_objformat.c:1.1	1/4/91";
#endif

#include <varargs.h>
#include <_eiffel.h>

#ifndef BUFSIZ
#define BUFSIZ 512
#endif BUFSIZ

OBJPTR format_obj(fmt, va_alist)
    char *fmt;
    va_dcl		/* the missing ; IS RIGHT see varargs */
{
    va_list ap;
    register int n;
    register char *cp, c, *buf_end;
    char buf[BUFSIZ], f[20];

    OBJPTR strobj;
    ROUT_PTR from_c;

    cp = buf;
    f[0] = '%';
  
    buf_end = buf+BUFSIZ;

    va_start (ap);

    while((c = *fmt++) != '\0' && cp < buf_end) {
	if(c == '%') {
	    for(c = *fmt++,n = 1; c; n++) {
		switch(c) {
		    
		  case '%':	/* its %% put out the % */
		    *cp++ = '%';
		    c = '\0';
		    break;
		    
		  case 's':	/* normal string */
		    f[n++] = c;
		    f[n] = '\0';
		    sprintf(cp, f, va_arg(ap,char *));
		    cp += strlen(cp);
		    c = '\0';
		    break;
		    
		  case '*':	/* variable field width */
		{
		    char varwidth[10];
		    int x = 0;
		    sprintf(varwidth,"%d",va_arg(ap, int));
		    while((f[n++] = varwidth[x++]))
			;
		}
		    n -= 2;
		    c = *fmt++;
		    break;
		    
		  case 'f':	/* floats etc. */
		  case 'g':
		  case 'e':
		    f[n++] = c;
		    f[n] = '\0';
		    sprintf(cp, f,va_arg(ap, double));
		    cp += strlen(cp);
		    c = '\0';
		    break;
		    
		  case 'd':	/* ints and such */
		  case 'o':
		  case 'c':
		  case 'x':
		  case 'u':
		    f[n++] = c;
		    f[n] = '\0';
		    sprintf(cp, f,va_arg(ap, int));
		    cp += strlen(cp);
		    c = '\0';
		    break;
		    
		  default:	/* none of the above */
		    f[n] = c;
		    c = *fmt++;
		    break;
		}
	    }
	} else		/* c wasn't '%'; just copy it into buf */
	    *cp++ = c;
    }
    
    va_end(ap);

    *cp = '\0';

    strobj = (OBJPTR) eif_create("string", 0)
    from_c = (ROUT_PTR) eif_rout(strobj, "from_c");
    (*from_c) (strobj, buf);
    return (strobj);
}
							
By the way has anyone had a chance to tryout the RESOURCE managment
stuf I posted some time back? Anyone doing anything with X/GUI's
and Eiffel.  I would like to hear from anyone doing work along those
lines.  (if they are free to talk.  Those on Double Secret Probation 
need not apply :-) ).
-- 
Frank Caggiano                      INTERNET: fcaggian@kepler.com  
Kepler Financial Management, Ltd.       UUCP: ..!uunet!kepler1!fcaggian
100 North Country Rd.                    fax: (516) 751-8678
Sekauket, NY 11733                     voice: (516) 689-6300 

marc@eiffel.UUCP (Jean-Marc Nerson) (01/06/91)

In article <457@kepler1.kepler.com>, fcaggian@kepler.com (Frank Caggiano) writes:
> The following c routine is something I've wanted to write for
> a while now.  It makes creating formatted string objects 
> easier.  

	Your contribution will be very helpful.

	I slightly updated your C code to include:
		1) Recognition of conversion characters E, G, X
		2) Use of specific Eiffel run-time routine MakeStr()
			instead of eif_create() and eif_rout()

	The diff -c	follows.

-------------------- CUT HERE ------------------
*** /tmp/,RCSt1a04581	Sat Jan  5 15:30:58 1991
--- _format.c	Sat Jan  5 15:28:13 1991
***************
*** 25,34 ****
      register int n;
      register char *cp, c, *buf_end;
      char buf[BUFSIZ], f[20];
  
-     OBJPTR strobj;
-     ROUT_PTR from_c;
- 
      cp = buf;
      f[0] = '%';
    
--- 25,32 ----
      register int n;
      register char *cp, c, *buf_end;
      char buf[BUFSIZ], f[20];
+ 	 extern OBJPTR MakeStr();
  
      cp = buf;
      f[0] = '%';
    
***************
*** 68,74 ****
--- 66,74 ----
  		    
  		  case 'f':	/* floats etc. */
  		  case 'g':
+ 		  case 'G':
  		  case 'e':
+ 		  case 'E':
  		    f[n++] = c;
  		    f[n] = '\0';
  		    sprintf(cp, f,va_arg(ap, double));
***************
*** 80,85 ****
--- 80,86 ----
  		  case 'o':
  		  case 'c':
  		  case 'x':
+ 		  case 'X':
  		  case 'u':
  		    f[n++] = c;
  		    f[n] = '\0';
***************
*** 102,109 ****
  
      *cp = '\0';
  
!     strobj = (OBJPTR) eif_create("string", 0)
!     from_c = (ROUT_PTR) eif_rout(strobj, "from_c");
!     (*from_c) (strobj, buf);
!     return (strobj);
  }
--- 103,107 ----
  
      *cp = '\0';
  
! 	 return (MakeStr (buf));
  }
-------------------- CUT HERE ------------------


-- Jean-Marc Nerson
marc@eiffel.com

rick@tetrauk.UUCP (Rick Jones) (01/07/91)

In article <457@kepler1.kepler.com> fcaggian@kepler.com (Frank Caggiano) writes:
>The following c routine is something I've wanted to write for
>a while now.  It makes creating formatted string objects 
>easier.  

I hate to devalue your work, but if your system has the vprintf() set of
functions, you can do this extremely simply.  It would look like:

#include <varargs.h>
#include <_eiffel.h>

#ifndef BUFSIZ
#define BUFSIZ 512
#endif BUFSIZ

OBJPTR format_obj(fmt, va_alist)
    char *fmt;
    va_dcl		/* the missing ; IS RIGHT see varargs */
{
    va_list ap;
    char buf[BUFSIZ];

    va_start (ap);

    if (vsprintf (buf, fmt, ap) >= BUFSIZ)
    {
	/* PANIC - buffer overrun, need some program abort code here */
    }

    va_end (ap);

    return (MakeStr (buf));
}

You might want to make BUFSIZ something really big to make the probability of
buffer overrun very low.

I'm not sure of the extent to which vsprintf() is supported.  It's standard in
System V, but may not be in BSD.  The neatest solution is to use this routine,
and then if you haven't got vsprintf() in your library write it using the code
Frank posted in his article.  The almost identical code was recently posted in
comp.lang.c for just this purpose.
-- 
Rick Jones
Tetra Ltd.  Maidenhead, 	Was it something important?  Maybe not
Berks, UK			What was it you wanted?  Tell me again I forgot
rick@tetrauk.uucp					-- Bob Dylan

fcaggian@kepler.com (Frank Caggiano) (01/08/91)

In article <475@eiffel.UUCP>, marc@eiffel.UUCP (Jean-Marc Nerson) writes:
> 	Your contribution will be very helpful.
> 
> 	I slightly updated your C code to include:

> 		2) Use of specific Eiffel run-time routine MakeStr()
> 			instead of eif_create() and eif_rout()
> 
> 	The diff -c	follows.
	[ Number of lines deleted ]
[from]
> !     strobj = (OBJPTR) eif_create("string", 0)
> !     from_c = (ROUT_PTR) eif_rout(strobj, "from_c");
> !     (*from_c) (strobj, buf);
> !     return (strobj);
[to]
> ! 	 return (MakeStr (buf));
>   }

Does this mean that the return from MakeStr() is an Eiffel string 
object ?  It would be legal to do something like:

	OBJPTR s;
	ROUT_PTR r;

	s = MakeStr (buf);
	r = (ROUT_PTR) (s, "append");
	(*r)(s,s);

[ Doesn't really do anything, just illustrates my question ]

This is good to know as I do a lot of passing of string objects
back from C.  Thanks for the input.


-- 
Frank Caggiano                      INTERNET: fcaggian@kepler.com  
Kepler Financial Management, Ltd.       UUCP: ..!uunet!kepler1!fcaggian
100 North Country Rd.                    fax: (516) 751-8678
Sekauket, NY 11733                     voice: (516) 689-6300 

fcaggian@kepler.com (Frank Caggiano) (01/12/91)

In article <1071@tetrauk.UUCP>, rick@tetrauk.UUCP (Rick Jones) writes:
> In article <457@kepler1.kepler.com> fcaggian@kepler.com (Frank Caggiano) writes:
> >The following c routine is something I've wanted to write for
> >a while now.  It makes creating formatted string objects 
> >easier.  
> 
> I hate to devalue your work, but if your system has the vprintf() set of
> functions, you can do this extremely simply.  It would look like:
	[ revised code deleted ]

I don't feel that your posting devalues my work in anyway.  It's a valid
solution to the problem. My code was stripped out of another routine I wrote
some years ago which is called eprintf. It acts just like the starndard printf
routines but had added % parameter recognition for error reporting.

I appreciate it when someone tales the time to look at and comment on code I 
have written. How else is one to learn in this buisiness?

> You might want to make BUFSIZ something really big to make the probability of
> buffer overrun very low.
The double check in the for loop prevents buffer overrun. If the format string
and expanded values are to large the returned string will be incomplete.  

> I'm not sure of the extent to which vsprintf() is supported.  
A real problem with the growth of UNIX. Just what is truely portable code these
days ? If anyone knows how universial the vsprintf() functions are prehaps they 
could let us know.

By the way according to the man page  for varargs on the sun my spliting 
of the format string from the rest of the varargs isn't right.   
It should be something like:

was:
OBJPTR format_obj(fmt,va_alist) 
    char *fmt;
should be:
OBJPTR format_obj(va_alist)  /* removed fmt as arg */
    va_dcl		/* the missing ; IS RIGHT see varargs */
{
    va_list ap;
    register int n;
    register char *cp, c, *buf_end;
    char buf[BUFSIZ], f[20], *fmt; /* added fmt as local var */
                            ^^^^^^^^^^^
    OBJPTR strobj;
    ROUT_PTR from_c;

    cp = buf;
    f[0] = '%';
  
    buf_end = buf+BUFSIZ;

    va_start (ap);

/* add */
    fmt = va_arg(ap, char *); /* set fmt from var arg list */
 
    while((c = *fmt++) != '\0' && cp < buf_end) {
[ THE REST is the same ]

The code as I orignally posted it works but may have problems with really
long arg lists.

> -- 
> Rick Jones
> Tetra Ltd.  Maidenhead, 	Was it something important?  Maybe not
> Berks, UK			What was it you wanted?  Tell me again I forgot
> rick@tetrauk.uucp					-- Bob Dylan

I like the Dylan quotes in your signature. How come I've never seen the
one: 
'You know something is happening but you don't know what it is, do you
Mr. ........  '   

[ It may be a bit off my Dylan is rusty ]
	:-) 

			Regards
PS. Did you do work for an American Law firm in London about 5 years ago?
-- 
Frank Caggiano                      INTERNET: fcaggian@kepler.com  
Kepler Financial Management, Ltd.       UUCP: ..!uunet!kepler1!fcaggian
100 North Country Rd.                    fax: (516) 751-8678
Sekauket, NY 11733                     voice: (516) 689-6300