1 /*
2 	voutf.c		Process formatted text through an output function.
3 
4 	MODULE:	voutf
5 	FILES:	voutf.c (this one) and voutf.h.
6 
7 	This package implements a flexible variable argument list
8 	output function similar to the System V vprintf function;
9 	however, it differs by using a function as an output sink
10 	instead of a string buffer or an output file.  This increases
11 	the flexibility of the package by allowing the supplied
12 	function to post-process and redirect the output as required
13 	by the application.
14 
15 	Note:	This procedure was adapted from earlier projects and
16 		edited slightly to fit Medline project syle criteria.
17 
18 	Edit History:
19 
20 	    25 July 1991	Rand S. Huntzinger, NLM/NCBI
21 		Adapted to meet style criterial for modules in the
22 		Medline project.
23 
24 	    14 Dec 1989		Modified to permit unlimited-length
25 				format statements and output of
26 				super long strings with unqualified
27 				%s format (ie. %s not %30s).
28 
29 	Written: 5/21/87 by Rand S. Huntzinger
30 *
31 *
32 * RCS Modification History:
33 * $Log: voutf.c,v $
34 * Revision 6.0  1997/08/25 18:37:08  madden
35 * Revision changed to 6.0
36 *
37 * Revision 1.2  1995/05/17 17:56:14  epstein
38 * add RCS log revision history
39 *
40 */
41 
42 #include	<stdio.h>
43 #include	<string.h>
44 #include	"voutf.h"
45 
46 #define index(s,c) strchr(s,c)
47 
48 /* Local definitions and declarations */
49 
50 #define	MAX_ARG_EXPANSION	128	/* Largest single output field */
51 #define	MAX_FMT_ITEM		128	/* Largest single field format */
52 #define	EOS	'\0'			/* End of string marker */
53 #ifndef	TRUE
54 #define	TRUE	1			/* Boolean constants */
55 #define	FALSE	0
56 #endif
57 
58 typedef	int	ifunc();
59 
60 /* Macro to support error handling in the output function */
61 
62 #define	OUTPUT(S) if((rv = out_func(S)) != 0) return (rv)
63 
64 /* List of printf style format characters recognized by voutf */
65 
66 static	char *fmt_chars = "sdcfgGeEduoxX%";
67 
68 /* External functions required */
69 
70 
71 /*
72 	voutf		Variable argument list output function.
73 
74 	This routine implements interprets a variable length argument
75 	list with respect to a format in a manner similar to the
76 	printf functions in the standard I/O package.  As each output
77 	field is interpreted, the resulting string is passed to a
78 	supplied output function, which processes it as required by
79 	the application [usually collects it into a string or outputs
80 	it].
81 
82 	Parameters:
83 
84 		out_func	The address of the function to be used
85 				as an output sink.  It is called for
86 				each output field (including constant
87 				fields).
88 
89 		fmt		A printf-style format statement
90 				describing how to format the remaining
91 				arguments into strings to pass to
92 				out_func.
93 
94 		args		A variable of type va_list (from a
95 				variable argument list in the calling
96 				program).
97 
98 	Returns:
99 
100 		0		If no error.
101 		NOT 0		An error status from out_func.
102 				Interpretation depends upon out_func.
103 */
104 
105 /*
106 *		voutf(int(out_func)(),charfmt,voidargs)
107 */
voutf(int (* out_func)(),char * fmt,void * args)108 int	voutf(int (*out_func)(), char *fmt, void *args)
109 {
110 	char	buf[MAX_ARG_EXPANSION+1];
111 	char	afmt[MAX_FMT_ITEM];
112 	int	rv;
113 
114 	/* Scan the format and output text */
115 
116 	rv = 0;			/* Assume success for now */
117 	while(*fmt) {
118 		char	*p;		/* Advancing char pointer */
119 		char	f_char;		/* Formatting character */
120 		short	longvar;	/* TRUE if a long integer */
121 		short	shortvar;	/* TRUE if a long integer */
122 
123 		/* Variables to hold arguments of various types */
124 
125 		double	dval;	char *	sval;
126 		int	ival;	long	lval;
127 
128 		/* Non-formatting codes simply get output */
129 		if(*fmt != '%') {
130 			for(p = buf; *fmt && *fmt!= '%'; *p++ = *fmt++)
131 			    if(p == &buf[MAX_ARG_EXPANSION]) {
132 				*p = EOS;
133 				OUTPUT(buf);
134 				p = buf;
135 			    }
136 			*p = EOS;
137 			OUTPUT(buf);
138 		}
139 		/* Format items require conversion */
140 		if(*fmt == '%') {
141 			/* Extract the conversion specification */
142 			longvar = shortvar = FALSE;
143 			p = afmt;
144 			*p++ = *fmt++;
145 			while(*fmt && index(fmt_chars, *fmt) == NULL) {
146 				char ivstr[32], *q;
147 				if(*fmt == '*') {
148 					/* Get format width from data */
149 					ival= va_arg(args, int);
150 					sprintf(ivstr, "%d", ival);
151 					for(q=ivstr; *q; ) *p++ = *q++;
152 					fmt++;		/* Skip * */
153 					continue;
154 				}
155 				if(*fmt == 'l') longvar = TRUE;
156 				if(*fmt == 'h') shortvar = TRUE;
157 				*p++ = *fmt++;
158 			}
159 			*p++ = f_char = *fmt;
160 			*p = EOS;
161 			if(*fmt) fmt++;
162 			/* Convert next argument by format */
163 			switch(f_char) {
164 			  case '%':	/* Quoted % */
165 				strcpy(buf, "%");
166 				break;
167 			  case 'd':	/* Integer conversions */
168 			  case 'o':  case 'u':  case 'x':  case 'X':
169 			  case 'c':	/* char is an integer on stack */
170 				if(longvar) {
171 					lval = va_arg(args, long);
172 					sprintf(buf, afmt, lval);
173 				} else if(shortvar) {
174 					lval = va_arg(args, int);
175 					sprintf(buf, afmt, lval);
176 				} else {
177 					ival = va_arg(args, int);
178 					sprintf(buf, afmt, ival);
179 				}
180 				break;
181 			  case 'f':	/* Floating point conversions */
182 			  case 'e':  case 'E':  case 'g':  case 'G':
183 				/* NOTE - PROBLEMS if float <> double? */
184 				dval = va_arg(args, double);
185 				sprintf(buf, afmt, dval);
186 				break;
187 			  case 's':	/* String argument */
188 				sval = va_arg(args, char *);
189 				if(strcmp(afmt, "%s") == 0) {
190 				    OUTPUT( sval ); /* Unrestricted string */
191 				    continue;
192 				} else sprintf(buf, afmt, sval);
193 				break;
194 			  case EOS:	/* End of string */
195 			  default:	/* Unexpected character */
196 				strcpy(buf, afmt);
197 				break;
198 			}
199 			/* Output the resulting string */
200 			OUTPUT(buf);
201 		}
202 	}
203 
204 	/* Exit at this point */
205 
206 	return( rv );
207 }
208 
209 
210 /*
211 	voutput		Output with variable argument list.
212 
213 	Call voutf with a direct variable argument list.  This is
214 	used when the function is not called from a variable argument
215 	list function.
216 
217 	Parameters:
218 
219 		out_func	The address of the function to be used
220 				as an output sync.  It is called for
221 				each output field (including constant
222 				fields).
223 
224 		fmt		A printf-style format statement
225 				describing how to format the remaining
226 				arguments into strings to pass to
227 				out_func.
228 
229 		args ...	The remaining arguments are interpreted
230 				according to the format passed as 'fmt'.
231 
232 	Returns:
233 
234 		0		If no error.
235 		NOT 0		An error status from out_func.
236 				Interpretation depends upon out_func.
237 */
238 
239 /*
240 *		voutput(int(out_func)(),charfmt,...)
241 */
voutput(int (* out_func)(),char * fmt,...)242 int	voutput(int (*out_func)(), char *fmt, ...)
243 {
244 	va_list	args;
245 	int	rv;
246 
247 	/* Extract the fixed parameters */
248 
249 	va_start(args, fmt);
250 
251 	/* Now use voutf */
252 
253 	rv = voutf(out_func, fmt, args);
254 	va_end( args );
255 
256 	/* Done */
257 
258 	return( rv );
259 }
260