home *** CD-ROM | disk | FTP | other *** search
- Newsgroups: comp.sources.unix
- From: panos@anchor.cs.colorado.edu (Panos Tsirigotis)
- Subject: v27i125: clc - C Libraries Collection, Part19/20
- References: <1.754527080.23891@gw.home.vix.com>
- Sender: unix-sources-moderator@gw.home.vix.com
- Approved: vixie@gw.home.vix.com
-
- Submitted-By: panos@anchor.cs.colorado.edu (Panos Tsirigotis)
- Posting-Number: Volume 27, Issue 125
- Archive-Name: clc/part19
-
- #! /bin/sh
- # This is a shell archive. Remove anything before this line, then unpack
- # it by saving it into a file and typing "sh file". To overwrite existing
- # files, type "sh file -c". You can also feed this as standard input via
- # unshar, or by typing "sh <file", e.g.. If this archive is complete, you
- # will see the following message at the end:
- # "End of archive 19 (of 20)."
- # Contents: libs/src/sio/sprint.c
- # Wrapped by panos@eclipse on Sun Nov 28 14:48:17 1993
- PATH=/bin:/usr/bin:/usr/ucb ; export PATH
- if test -f 'libs/src/sio/sprint.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'libs/src/sio/sprint.c'\"
- else
- echo shar: Extracting \"'libs/src/sio/sprint.c'\" \(17542 characters\)
- sed "s/^X//" >'libs/src/sio/sprint.c' <<'END_OF_FILE'
- X/*
- X * (c) Copyright 1992, 1993 by Panagiotis Tsirigotis
- X * All rights reserved. The file named COPYRIGHT specifies the terms
- X * and conditions for redistribution.
- X */
- X
- X
- Xstatic char RCSid[] = "$Id: sprint.c,v 8.3 1993/03/30 21:33:44 panos Exp $" ;
- X
- X#include <ctype.h>
- X
- X#include "sio.h"
- X#include "impl.h"
- X
- X#ifndef WIDE_INT
- X#define WIDE_INT long
- X#endif
- X
- Xtypedef WIDE_INT wide_int ;
- Xtypedef unsigned WIDE_INT u_wide_int ;
- Xtypedef int bool_int ;
- X
- X#define S_NULL "(null)"
- X#define S_NULL_LEN 6
- X
- X#define FLOAT_DIGITS 6
- X#define EXPONENT_LENGTH 10
- X
- X/*
- X * NUM_BUF_SIZE is the size of the buffer used for arithmetic conversions
- X *
- X * XXX: this is a magic number; do not decrease it
- X */
- X#define NUM_BUF_SIZE 512
- X
- X/*
- X * The INS_CHAR macro inserts a character in the buffer and writes
- X * the buffer back to disk if necessary
- X * It uses the char pointers sp and bep:
- X * sp points to the next available character in the buffer
- X * bep points to the end-of-buffer+1
- X * While using this macro, note that the nextb pointer is NOT updated.
- X *
- X * No I/O is performed if fd is not positive. Negative fd values imply
- X * conversion with the output directed to a string. Excess characters
- X * are discarded if the string overflows.
- X *
- X * NOTE: Evaluation of the c argument should not have any side-effects
- X */
- X#define INS_CHAR( c, sp, bep, odp, cc, fd ) \
- X { \
- X if ( sp < bep ) \
- X { \
- X *sp++ = c ; \
- X cc++ ; \
- X } \
- X else \
- X { \
- X if ( fd >= 0 ) \
- X { \
- X odp->nextb = sp ; \
- X if ( __sio_writef( odp, fd ) != bep - odp->start ) \
- X return( ( cc != 0 ) ? cc : SIO_ERR ) ; \
- X sp = odp->nextb ; \
- X *sp++ = c ; \
- X cc++ ; \
- X } \
- X } \
- X if ( __SIO_MUST_FLUSH( *odp, c ) && fd >= 0 ) \
- X { \
- X int b_in_buffer = sp - odp->start ; \
- X \
- X odp->nextb = sp ; \
- X if ( __sio_writef( odp, fd ) != b_in_buffer ) \
- X return( cc ) ; \
- X sp = odp->nextb ; \
- X } \
- X }
- X
- X
- X
- X#define NUM( c ) ( c - '0' )
- X
- X#define STR_TO_DEC( str, num ) \
- X num = NUM( *str++ ) ; \
- X while ( isdigit( *str ) ) \
- X { \
- X num *= 10 ; \
- X num += NUM( *str++ ) ; \
- X }
- X
- X/*
- X * This macro does zero padding so that the precision
- X * requirement is satisfied. The padding is done by
- X * adding '0's to the left of the string that is going
- X * to be printed.
- X */
- X#define FIX_PRECISION( adjust, precision, s, s_len ) \
- X if ( adjust ) \
- X while ( s_len < precision ) \
- X { \
- X *--s = '0' ; \
- X s_len++ ; \
- X }
- X
- X/*
- X * Macro that does padding. The padding is done by printing
- X * the character ch.
- X */
- X#define PAD( width, len, ch ) do \
- X { \
- X INS_CHAR( ch, sp, bep, odp, cc, fd ) ; \
- X width-- ; \
- X } \
- X while ( width > len )
- X
- X/*
- X * Prefix the character ch to the string str
- X * Increase length
- X * Set the has_prefix flag
- X */
- X#define PREFIX( str, length, ch ) *--str = ch ; length++ ; has_prefix = YES
- X
- X
- X/*
- X * Sprint is the equivalent of printf for SIO.
- X * It returns the # of chars written
- X * Assumptions:
- X * - all floating point arguments are passed as doubles
- X */
- X/* VARARGS2 */
- Xint Sprint( fd, fmt, va_alist )
- X int fd ;
- X register char *fmt ;
- X va_dcl
- X{
- X __sio_descriptor_t *dp = &__sio_descriptors[ fd ] ;
- X register __sio_od_t *odp = ODP( dp ) ;
- X register int cc ;
- X va_list ap ;
- X
- X IO_SETUP( fd, dp, __SIO_OUTPUT_STREAM, SIO_ERR ) ;
- X
- X va_start( ap ) ;
- X cc = __sio_converter( odp, fd, fmt, ap ) ;
- X va_end( ap ) ;
- X return( cc ) ;
- X}
- X
- X
- X/*
- X * This is the equivalent of vfprintf for SIO
- X */
- Xint Sprintv( fd, fmt, ap )
- X int fd ;
- X char *fmt ;
- X va_list ap ;
- X{
- X __sio_descriptor_t *dp = &__sio_descriptors[ fd ] ;
- X register __sio_od_t *odp = ODP( dp ) ;
- X
- X IO_SETUP( fd, dp, __SIO_OUTPUT_STREAM, SIO_ERR ) ;
- X return( __sio_converter( odp, fd, fmt, ap ) ) ;
- X}
- X
- X
- X/*
- X * Convert a floating point number to a string formats 'f', 'e' or 'E'.
- X * The result is placed in buf, and len denotes the length of the string
- X * The sign is returned in the is_negative argument (and is not placed
- X * in buf).
- X */
- XPRIVATE char *conv_fp( format, num, add_dp, precision, is_negative, buf, len )
- X register char format ;
- X register double num ;
- X boolean_e add_dp ; /* always add decimal point if YES */
- X int precision ;
- X bool_int *is_negative ;
- X char buf[] ;
- X int *len ;
- X{
- X register char *s = buf ;
- X register char *p ;
- X int decimal_point ;
- X char *ecvt(), *fcvt() ;
- X char *conv_10() ;
- X char *strcpy() ;
- X
- X if ( format == 'f' )
- X p = fcvt( num, precision, &decimal_point, is_negative ) ;
- X else /* either e or E format */
- X p = ecvt( num, precision+1, &decimal_point, is_negative ) ;
- X
- X /*
- X * Check for Infinity and NaN
- X */
- X if ( isalpha( *p ) )
- X {
- X *len = strlen( strcpy( buf, p ) ) ;
- X *is_negative = FALSE ;
- X return( buf ) ;
- X }
- X
- X if ( format == 'f' )
- X if ( decimal_point <= 0 )
- X {
- X *s++ = '0' ;
- X if ( precision > 0 )
- X {
- X *s++ = '.' ;
- X while ( decimal_point++ < 0 )
- X *s++ = '0' ;
- X }
- X else if ( add_dp )
- X *s++ = '.' ;
- X }
- X else
- X {
- X while ( decimal_point-- > 0 )
- X *s++ = *p++ ;
- X if ( precision > 0 || add_dp ) *s++ = '.' ;
- X }
- X else
- X {
- X *s++ = *p++ ;
- X if ( precision > 0 || add_dp ) *s++ = '.' ;
- X }
- X
- X /*
- X * copy the rest of p, the NUL is NOT copied
- X */
- X while ( *p ) *s++ = *p++ ;
- X
- X if ( format != 'f' )
- X {
- X char temp[ EXPONENT_LENGTH ] ; /* for exponent conversion */
- X int t_len ;
- X bool_int exponent_is_negative ;
- X
- X *s++ = format ; /* either e or E */
- X decimal_point-- ;
- X if ( decimal_point != 0 )
- X {
- X p = conv_10( (wide_int)decimal_point, FALSE, &exponent_is_negative,
- X &temp[ EXPONENT_LENGTH ], &t_len ) ;
- X *s++ = exponent_is_negative ? '-' : '+' ;
- X
- X /*
- X * Make sure the exponent has at least 2 digits
- X */
- X if ( t_len == 1 )
- X *s++ = '0' ;
- X while ( t_len-- ) *s++ = *p++ ;
- X }
- X else
- X {
- X *s++ = '+' ;
- X *s++ = '0' ;
- X *s++ = '0' ;
- X }
- X }
- X
- X *len = s - buf ;
- X return( buf ) ;
- X}
- X
- X
- X/*
- X * Convert num to a base X number where X is a power of 2. nbits determines X.
- X * For example, if nbits is 3, we do base 8 conversion
- X * Return value:
- X * a pointer to a string containing the number
- X *
- X * The caller provides a buffer for the string: that is the buf_end argument
- X * which is a pointer to the END of the buffer + 1 (i.e. if the buffer
- X * is declared as buf[ 100 ], buf_end should be &buf[ 100 ])
- X */
- XPRIVATE char *conv_p2( num, nbits, format, buf_end, len )
- X register u_wide_int num ;
- X register int nbits ;
- X char format ;
- X char *buf_end ;
- X register int *len ;
- X{
- X register int mask = ( 1 << nbits ) - 1 ;
- X register char *p = buf_end ;
- X static char low_digits[] = "0123456789abcdef" ;
- X static char upper_digits[] = "0123456789ABCDEF" ;
- X register char *digits = ( format == 'X' ) ? upper_digits : low_digits ;
- X
- X do
- X {
- X *--p = digits[ num & mask ] ;
- X num >>= nbits ;
- X }
- X while( num ) ;
- X
- X *len = buf_end - p ;
- X return( p ) ;
- X}
- X
- X
- X
- X/*
- X * Convert num to its decimal format.
- X * Return value:
- X * - a pointer to a string containing the number (no sign)
- X * - len contains the length of the string
- X * - is_negative is set to TRUE or FALSE depending on the sign
- X * of the number (always set to FALSE if is_unsigned is TRUE)
- X *
- X * The caller provides a buffer for the string: that is the buf_end argument
- X * which is a pointer to the END of the buffer + 1 (i.e. if the buffer
- X * is declared as buf[ 100 ], buf_end should be &buf[ 100 ])
- X */
- XPRIVATE char *conv_10( num, is_unsigned, is_negative, buf_end, len )
- X register wide_int num ;
- X register bool_int is_unsigned ;
- X register bool_int *is_negative ;
- X char *buf_end ;
- X register int *len ;
- X{
- X register char *p = buf_end ;
- X register u_wide_int magnitude ;
- X
- X if ( is_unsigned )
- X {
- X magnitude = (u_wide_int) num ;
- X *is_negative = FALSE ;
- X }
- X else
- X {
- X *is_negative = ( num < 0 ) ;
- X
- X /*
- X * On a 2's complement machine, negating the most negative integer
- X * results in a number that cannot be represented as a signed integer.
- X * Here is what we do to obtain the number's magnitude:
- X * a. add 1 to the number
- X * b. negate it (becomes positive)
- X * c. convert it to unsigned
- X * d. add 1
- X */
- X if ( *is_negative )
- X {
- X wide_int t = num + 1 ;
- X
- X magnitude = ( (u_wide_int) -t ) + 1 ;
- X }
- X else
- X magnitude = (u_wide_int) num ;
- X }
- X
- X /*
- X * We use a do-while loop so that we write at least 1 digit
- X */
- X do
- X {
- X register u_wide_int new_magnitude = magnitude / 10 ;
- X
- X *--p = magnitude - new_magnitude*10 + '0' ;
- X magnitude = new_magnitude ;
- X }
- X while ( magnitude ) ;
- X
- X *len = buf_end - p ;
- X return( p ) ;
- X}
- X
- X
- X/*
- X * Do format conversion placing the output in odp
- X */
- Xint __sio_converter( odp, fd, fmt, ap )
- X register __sio_od_t *odp ;
- X int fd ;
- X register char *fmt ;
- X va_list ap ;
- X{
- X register char *sp ;
- X register char *bep ;
- X register int cc = 0 ;
- X register int i ;
- X
- X register char *s ;
- X char *q ;
- X int s_len ;
- X
- X register int min_width ;
- X int precision ;
- X enum { LEFT, RIGHT } adjust ;
- X char pad_char ;
- X char prefix_char ;
- X
- X double fp_num ;
- X wide_int i_num ;
- X u_wide_int ui_num ;
- X
- X char num_buf[ NUM_BUF_SIZE ] ;
- X char char_buf[ 2 ] ; /* for printing %% and %<unknown> */
- X
- X /*
- X * Flag variables
- X */
- X boolean_e is_long ;
- X boolean_e alternate_form ;
- X boolean_e print_sign ;
- X boolean_e print_blank ;
- X boolean_e adjust_precision ;
- X boolean_e adjust_width ;
- X bool_int is_negative ;
- X
- X char *conv_10(), *conv_p2(), *conv_fp() ;
- X char *gcvt() ;
- X char *strchr() ;
- X
- X
- X sp = odp->nextb ;
- X bep = odp->buf_end ;
- X
- X while ( *fmt )
- X {
- X if ( *fmt != '%' )
- X {
- X INS_CHAR( *fmt, sp, bep, odp, cc, fd ) ;
- X }
- X else
- X {
- X /*
- X * Default variable settings
- X */
- X adjust = RIGHT ;
- X alternate_form = print_sign = print_blank = NO ;
- X pad_char = ' ' ;
- X prefix_char = NUL ;
- X
- X fmt++ ;
- X
- X /*
- X * Try to avoid checking for flags, width or precision
- X */
- X if ( isascii( *fmt ) && ! islower( *fmt ) )
- X {
- X /*
- X * Recognize flags: -, #, BLANK, +
- X */
- X for ( ;; fmt++ )
- X {
- X if ( *fmt == '-' )
- X adjust = LEFT ;
- X else if ( *fmt == '+' )
- X print_sign = YES ;
- X else if ( *fmt == '#' )
- X alternate_form = YES ;
- X else if ( *fmt == ' ' )
- X print_blank = YES ;
- X else if ( *fmt == '0' )
- X pad_char = '0' ;
- X else
- X break ;
- X }
- X
- X /*
- X * Check if a width was specified
- X */
- X if ( isdigit( *fmt ) )
- X {
- X STR_TO_DEC( fmt, min_width ) ;
- X adjust_width = YES ;
- X }
- X else if ( *fmt == '*' )
- X {
- X min_width = va_arg( ap, int ) ;
- X fmt++ ;
- X adjust_width = YES ;
- X if ( min_width < 0 )
- X {
- X adjust = LEFT ;
- X min_width = -min_width ;
- X }
- X }
- X else
- X adjust_width = NO ;
- X
- X /*
- X * Check if a precision was specified
- X *
- X * XXX: an unreasonable amount of precision may be specified
- X * resulting in overflow of num_buf. Currently we
- X * ignore this possibility.
- X */
- X if ( *fmt == '.' )
- X {
- X adjust_precision = YES ;
- X fmt++ ;
- X if ( isdigit( *fmt ) )
- X {
- X STR_TO_DEC( fmt, precision ) ;
- X }
- X else if ( *fmt == '*' )
- X {
- X precision = va_arg( ap, int ) ;
- X fmt++ ;
- X if ( precision < 0 )
- X precision = 0 ;
- X }
- X else
- X precision = 0 ;
- X }
- X else
- X adjust_precision = NO ;
- X }
- X else
- X adjust_precision = adjust_width = NO ;
- X
- X /*
- X * Modifier check
- X */
- X if ( *fmt == 'l' )
- X {
- X is_long = YES ;
- X fmt++ ;
- X }
- X else
- X is_long = NO ;
- X
- X /*
- X * Argument extraction and printing.
- X * First we determine the argument type.
- X * Then, we convert the argument to a string.
- X * On exit from the switch, s points to the string that
- X * must be printed, s_len has the length of the string
- X * The precision requirements, if any, are reflected in s_len.
- X *
- X * NOTE: pad_char may be set to '0' because of the 0 flag.
- X * It is reset to ' ' by non-numeric formats
- X */
- X switch( *fmt )
- X {
- X case 'd':
- X case 'i':
- X case 'u':
- X if ( is_long )
- X i_num = va_arg( ap, wide_int ) ;
- X else
- X i_num = (wide_int) va_arg( ap, int ) ;
- X s = conv_10( i_num, (*fmt) == 'u', &is_negative,
- X &num_buf[ NUM_BUF_SIZE ], &s_len ) ;
- X FIX_PRECISION( adjust_precision, precision, s, s_len ) ;
- X
- X if ( *fmt != 'u' )
- X {
- X if ( is_negative )
- X prefix_char = '-' ;
- X else if ( print_sign )
- X prefix_char = '+' ;
- X else if ( print_blank )
- X prefix_char = ' ' ;
- X }
- X break ;
- X
- X
- X case 'o':
- X if ( is_long )
- X ui_num = va_arg( ap, u_wide_int ) ;
- X else
- X ui_num = (u_wide_int) va_arg( ap, unsigned int ) ;
- X s = conv_p2( ui_num, 3, *fmt,
- X &num_buf[ NUM_BUF_SIZE ], &s_len ) ;
- X FIX_PRECISION( adjust_precision, precision, s, s_len ) ;
- X if ( alternate_form && *s != '0' )
- X {
- X *--s = '0' ;
- X s_len++ ;
- X }
- X break ;
- X
- X
- X case 'x':
- X case 'X':
- X if ( is_long )
- X ui_num = (u_wide_int) va_arg( ap, u_wide_int ) ;
- X else
- X ui_num = (u_wide_int) va_arg( ap, unsigned int ) ;
- X s = conv_p2( ui_num, 4, *fmt,
- X &num_buf[ NUM_BUF_SIZE ], &s_len ) ;
- X FIX_PRECISION( adjust_precision, precision, s, s_len ) ;
- X if ( alternate_form && i_num != 0 )
- X {
- X *--s = *fmt ; /* 'x' or 'X' */
- X *--s = '0' ;
- X s_len += 2 ;
- X }
- X break ;
- X
- X
- X case 's':
- X s = va_arg( ap, char * ) ;
- X if ( s != NULL )
- X {
- X s_len = strlen( s ) ;
- X if ( adjust_precision && precision < s_len )
- X s_len = precision ;
- X }
- X else
- X {
- X s = S_NULL ;
- X s_len = S_NULL_LEN ;
- X }
- X pad_char = ' ' ;
- X break ;
- X
- X
- X case 'f':
- X case 'e':
- X case 'E':
- X fp_num = va_arg( ap, double ) ;
- X
- X s = conv_fp( *fmt, fp_num, alternate_form,
- X ( adjust_precision == NO ) ? FLOAT_DIGITS : precision,
- X &is_negative, &num_buf[ 1 ], &s_len ) ;
- X if ( is_negative )
- X prefix_char = '-' ;
- X else if ( print_sign )
- X prefix_char = '+' ;
- X else if ( print_blank )
- X prefix_char = ' ' ;
- X break ;
- X
- X
- X case 'g':
- X case 'G':
- X if ( adjust_precision == NO )
- X precision = FLOAT_DIGITS ;
- X else if ( precision == 0 )
- X precision = 1 ;
- X /*
- X * We use &num_buf[ 1 ], so that we have room for the sign
- X */
- X s = gcvt( va_arg( ap, double ), precision, &num_buf[ 1 ] ) ;
- X if ( *s == '-' )
- X prefix_char = *s++ ;
- X else if ( print_sign )
- X prefix_char = '+' ;
- X else if ( print_blank )
- X prefix_char = ' ' ;
- X
- X s_len = strlen( s ) ;
- X
- X if ( alternate_form && ( q = strchr( s, '.' ) ) == NULL )
- X s[ s_len++ ] = '.' ;
- X if ( *fmt == 'G' && ( q = strchr( s, 'e' ) ) != NULL )
- X *q = 'E' ;
- X break ;
- X
- X
- X case 'c':
- X char_buf[ 0 ] = (char) (va_arg( ap, int )) ;
- X s = &char_buf[ 0 ] ;
- X s_len = 1 ;
- X pad_char = ' ' ;
- X break ;
- X
- X
- X case '%':
- X char_buf[ 0 ] = '%' ;
- X s = &char_buf[ 0 ] ;
- X s_len = 1 ;
- X pad_char = ' ' ;
- X break ;
- X
- X
- X case 'n':
- X *(va_arg( ap, int * )) = cc ;
- X break ;
- X
- X /*
- X * Always extract the argument as a "char *" pointer. We
- X * should be using "void *" but there are still machines
- X * that don't understand it.
- X * If the pointer size is equal to the size of an unsigned
- X * integer we convert the pointer to a hex number, otherwise
- X * we print "%p" to indicate that we don't handle "%p".
- X */
- X case 'p':
- X ui_num = (u_wide_int) va_arg( ap, char * ) ;
- X
- X if ( sizeof( char * ) <= sizeof( u_wide_int ) )
- X s = conv_p2( ui_num, 4, 'x',
- X &num_buf[ NUM_BUF_SIZE ], &s_len ) ;
- X else
- X {
- X s = "%p" ;
- X s_len = 2 ;
- X }
- X pad_char = ' ' ;
- X break ;
- X
- X
- X case NUL:
- X /*
- X * The last character of the format string was %.
- X * We ignore it.
- X */
- X continue ;
- X
- X
- X /*
- X * The default case is for unrecognized %'s.
- X * We print %<char> to help the user identify what
- X * option is not understood.
- X * This is also useful in case the user wants to pass
- X * the output of __sio_converter to another function
- X * that understands some other %<char> (like syslog).
- X * Note that we can't point s inside fmt because the
- X * unknown <char> could be preceded by width etc.
- X */
- X default:
- X char_buf[ 0 ] = '%' ;
- X char_buf[ 1 ] = *fmt ;
- X s = char_buf ;
- X s_len = 2 ;
- X pad_char = ' ' ;
- X break ;
- X }
- X
- X if ( prefix_char != NUL )
- X {
- X *--s = prefix_char ;
- X s_len++ ;
- X }
- X
- X if ( adjust_width && adjust == RIGHT && min_width > s_len )
- X {
- X if ( pad_char == '0' && prefix_char != NUL )
- X {
- X INS_CHAR( *s, sp, bep, odp, cc, fd )
- X s++ ;
- X s_len-- ;
- X min_width-- ;
- X }
- X PAD( min_width, s_len, pad_char ) ;
- X }
- X
- X /*
- X * Print the string s.
- X */
- X for ( i = s_len ; i != 0 ; i-- )
- X {
- X INS_CHAR( *s, sp, bep, odp, cc, fd ) ;
- X s++ ;
- X }
- X
- X if ( adjust_width && adjust == LEFT && min_width > s_len )
- X PAD( min_width, s_len, pad_char ) ;
- X }
- X fmt++ ;
- X }
- X odp->nextb = sp ;
- X return( cc ) ;
- X}
- X
- END_OF_FILE
- if test 17542 -ne `wc -c <'libs/src/sio/sprint.c'`; then
- echo shar: \"'libs/src/sio/sprint.c'\" unpacked with wrong size!
- fi
- # end of 'libs/src/sio/sprint.c'
- fi
- echo shar: End of archive 19 \(of 20\).
- cp /dev/null ark19isdone
- MISSING=""
- for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ; do
- if test ! -f ark${I}isdone ; then
- MISSING="${MISSING} ${I}"
- fi
- done
- if test "${MISSING}" = "" ; then
- echo You have unpacked all 20 archives.
- rm -f ark[1-9]isdone ark[1-9][0-9]isdone
- else
- echo You still need to unpack the following archives:
- echo " " ${MISSING}
- fi
- ## End of shell archive.
- exit 0
-