]> code.delx.au - gnu-emacs/blob - src/doprnt.c
(doprnt1): Cast arg to CHAR_STRING.
[gnu-emacs] / src / doprnt.c
1 /* Output like sprintf to a buffer of specified size.
2 Also takes args differently: pass one pointer to an array of strings
3 in addition to the format string which is separate.
4 Copyright (C) 1985 Free Software Foundation, Inc.
5
6 This file is part of GNU Emacs.
7
8 GNU Emacs is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2, or (at your option)
11 any later version.
12
13 GNU Emacs is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with GNU Emacs; see the file COPYING. If not, write to
20 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 Boston, MA 02111-1307, USA. */
22
23
24 #include <config.h>
25 #include <stdio.h>
26 #include <ctype.h>
27
28 #ifdef STDC_HEADERS
29 #include <float.h>
30 #endif
31
32 #include "lisp.h"
33
34 #ifndef DBL_MAX_10_EXP
35 #define DBL_MAX_10_EXP 308 /* IEEE double */
36 #endif
37
38 /* Since we use the macro CHAR_HEAD_P, we have to include this, but
39 don't have to include others because CHAR_HEAD_P does not contains
40 another macro. */
41 #include "charset.h"
42
43 extern long *xmalloc (), *xrealloc ();
44
45 static int doprnt1 ();
46
47 /* Generate output from a format-spec FORMAT,
48 terminated at position FORMAT_END.
49 Output goes in BUFFER, which has room for BUFSIZE chars.
50 If the output does not fit, truncate it to fit.
51 Returns the number of characters stored into BUFFER.
52 ARGS points to the vector of arguments, and NARGS says how many.
53 A double counts as two arguments.
54 String arguments are passed as C strings.
55 Integers are passed as C integers. */
56
57 int
58 doprnt (buffer, bufsize, format, format_end, nargs, args)
59 char *buffer;
60 register int bufsize;
61 char *format;
62 char *format_end;
63 int nargs;
64 char **args;
65 {
66 return doprnt1 (0, buffer, bufsize, format, format_end, nargs, args);
67 }
68
69 /* Like doprnt except that strings in ARGS are passed
70 as Lisp_Object. */
71
72 int
73 doprnt_lisp (buffer, bufsize, format, format_end, nargs, args)
74 char *buffer;
75 register int bufsize;
76 char *format;
77 char *format_end;
78 int nargs;
79 char **args;
80 {
81 return doprnt1 (1, buffer, bufsize, format, format_end, nargs, args);
82 }
83
84 static int
85 doprnt1 (lispstrings, buffer, bufsize, format, format_end, nargs, args)
86 int lispstrings;
87 char *buffer;
88 register int bufsize;
89 char *format;
90 char *format_end;
91 int nargs;
92 char **args;
93 {
94 int cnt = 0; /* Number of arg to gobble next */
95 register char *fmt = format; /* Pointer into format string */
96 register char *bufptr = buffer; /* Pointer into output buffer.. */
97
98 /* Use this for sprintf unless we need something really big. */
99 char tembuf[DBL_MAX_10_EXP + 100];
100
101 /* Size of sprintf_buffer. */
102 int size_allocated = sizeof (tembuf);
103
104 /* Buffer to use for sprintf. Either tembuf or same as BIG_BUFFER. */
105 char *sprintf_buffer = tembuf;
106
107 /* Buffer we have got with malloc. */
108 char *big_buffer = 0;
109
110 register int tem;
111 char *string;
112 char fixed_buffer[20]; /* Default buffer for small formatting. */
113 char *fmtcpy;
114 int minlen;
115 int size; /* Field width factor; e.g., %90d */
116 unsigned char charbuf[5]; /* Used for %c. */
117
118 if (format_end == 0)
119 format_end = format + strlen (format);
120
121 if ((format_end - format + 1) < sizeof (fixed_buffer))
122 fmtcpy = fixed_buffer;
123 else
124 fmtcpy = (char *) alloca (format_end - format + 1);
125
126 bufsize--;
127
128 /* Loop until end of format string or buffer full. */
129 while (fmt != format_end && bufsize > 0)
130 {
131 if (*fmt == '%') /* Check for a '%' character */
132 {
133 int size_bound = 0;
134 int width; /* Columns occupied by STRING. */
135
136 fmt++;
137 /* Copy this one %-spec into fmtcpy. */
138 string = fmtcpy;
139 *string++ = '%';
140 while (1)
141 {
142 *string++ = *fmt;
143 if ('0' <= *fmt && *fmt <= '9')
144 {
145 /* Get an idea of how much space we might need.
146 This might be a field width or a precision; e.g.
147 %1.1000f and %1000.1f both might need 1000+ bytes.
148 Parse the width or precision, checking for overflow. */
149 int n = *fmt - '0';
150 while ('0' <= fmt[1] && fmt[1] <= '9')
151 {
152 if (n * 10 / 10 != n
153 || (n = n * 10 + (fmt[1] - '0')) < 0)
154 error ("Format width or precision too large");
155 *string++ = *++fmt;
156 }
157
158 if (size_bound < n)
159 size_bound = n;
160 }
161 else if (*fmt == '-' || *fmt == ' ' || *fmt == '.')
162 ;
163 else
164 break;
165 fmt++;
166 }
167 *string = 0;
168
169 /* Make the size bound large enough to handle floating point formats
170 with large numbers. */
171 size_bound += DBL_MAX_10_EXP + 50;
172
173 if (size_bound < 0)
174 error ("Format width or precision too large");
175
176 /* Make sure we have that much. */
177 if (size_bound > size_allocated)
178 {
179 if (big_buffer)
180 big_buffer = (char *) xrealloc (big_buffer, size_bound);
181 else
182 big_buffer = (char *) xmalloc (size_bound);
183 sprintf_buffer = big_buffer;
184 size_allocated = size_bound;
185 }
186 minlen = 0;
187 switch (*fmt++)
188 {
189 default:
190 error ("Invalid format operation %%%c", fmt[-1]);
191
192 /* case 'b': */
193 case 'd':
194 case 'o':
195 case 'x':
196 if (cnt == nargs)
197 error ("Not enough arguments for format string");
198 if (sizeof (int) == sizeof (EMACS_INT))
199 ;
200 else if (sizeof (long) == sizeof (EMACS_INT))
201 /* Insert an `l' the right place. */
202 string[1] = string[0],
203 string[0] = string[-1],
204 string[-1] = 'l',
205 string++;
206 else
207 abort ();
208 sprintf (sprintf_buffer, fmtcpy, args[cnt++]);
209 /* Now copy into final output, truncating as nec. */
210 string = sprintf_buffer;
211 goto doit;
212
213 case 'f':
214 case 'e':
215 case 'g':
216 {
217 union { double d; char *half[2]; } u;
218 if (cnt + 1 == nargs)
219 error ("not enough arguments for format string");
220 u.half[0] = args[cnt++];
221 u.half[1] = args[cnt++];
222 sprintf (sprintf_buffer, fmtcpy, u.d);
223 /* Now copy into final output, truncating as nec. */
224 string = sprintf_buffer;
225 goto doit;
226 }
227
228 case 'S':
229 string[-1] = 's';
230 case 's':
231 if (cnt == nargs)
232 error ("not enough arguments for format string");
233 if (fmtcpy[1] != 's')
234 minlen = atoi (&fmtcpy[1]);
235 if (lispstrings)
236 {
237 string = (char *) ((struct Lisp_String *)args[cnt])->data;
238 tem = ((struct Lisp_String *)args[cnt])->size;
239 cnt++;
240 }
241 else
242 {
243 string = args[cnt++];
244 tem = strlen (string);
245 }
246 width = strwidth (string, tem);
247 goto doit1;
248
249 /* Copy string into final output, truncating if no room. */
250 doit:
251 /* Coming here means STRING contains ASCII only. */
252 width = tem = strlen (string);
253 doit1:
254 /* We have already calculated:
255 TEM -- length of STRING,
256 WIDTH -- columns occupied by STRING when displayed, and
257 MINLEN -- minimum columns of the output. */
258 if (minlen > 0)
259 {
260 while (minlen > width && bufsize > 0)
261 {
262 *bufptr++ = ' ';
263 bufsize--;
264 minlen--;
265 }
266 minlen = 0;
267 }
268 if (tem > bufsize)
269 {
270 /* Truncate the string at character boundary. */
271 tem = bufsize;
272 while (!CHAR_HEAD_P (string[tem - 1])) tem--;
273 bcopy (string, bufptr, tem);
274 /* We must calculate WIDTH again. */
275 width = strwidth (bufptr, tem);
276 }
277 else
278 bcopy (string, bufptr, tem);
279 bufptr += tem;
280 bufsize -= tem;
281 if (minlen < 0)
282 {
283 while (minlen < - width && bufsize > 0)
284 {
285 *bufptr++ = ' ';
286 bufsize--;
287 minlen++;
288 }
289 minlen = 0;
290 }
291 continue;
292
293 case 'c':
294 if (cnt == nargs)
295 error ("not enough arguments for format string");
296 tem = CHAR_STRING ((int) (EMACS_INT) args[cnt], charbuf, string);
297 cnt++;
298 string[tem] = 0;
299 width = strwidth (string, tem);
300 if (fmtcpy[1] != 'c')
301 minlen = atoi (&fmtcpy[1]);
302 goto doit1;
303
304 case '%':
305 fmt--; /* Drop thru and this % will be treated as normal */
306 }
307 }
308
309 {
310 /* Just some character; Copy it if the whole multi-byte form
311 fit in the buffer. */
312 char *save_bufptr = bufptr;
313
314 do { *bufptr++ = *fmt++; }
315 while (--bufsize > 0 && !CHAR_HEAD_P (*fmt));
316 if (!CHAR_HEAD_P (*fmt))
317 {
318 bufptr = save_bufptr;
319 break;
320 }
321 }
322 };
323
324 /* If we had to malloc something, free it. */
325 if (big_buffer)
326 xfree (big_buffer);
327
328 *bufptr = 0; /* Make sure our string end with a '\0' */
329 return bufptr - buffer;
330 }