(fix_submap_inheritance, get_keyelt, store_in_keymap,
[bpt/emacs.git] / 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 doprnt (buffer, bufsize, format, format_end, nargs, args)
58 char *buffer;
59 register int bufsize;
60 char *format;
61 char *format_end;
62 int nargs;
63 char **args;
64 {
65 return doprnt1 (0, buffer, bufsize, format, format_end, nargs, args);
66 }
67
68 /* Like doprnt except that strings in ARGS are passed
69 as Lisp_Object. */
70
71 doprnt_lisp (buffer, bufsize, format, format_end, nargs, args)
72 char *buffer;
73 register int bufsize;
74 char *format;
75 char *format_end;
76 int nargs;
77 char **args;
78 {
79 return doprnt1 (1, buffer, bufsize, format, format_end, nargs, args);
80 }
81
82 static int
83 doprnt1 (lispstrings, buffer, bufsize, format, format_end, nargs, args)
84 int lispstrings;
85 char *buffer;
86 register int bufsize;
87 char *format;
88 char *format_end;
89 int nargs;
90 char **args;
91 {
92 int cnt = 0; /* Number of arg to gobble next */
93 register char *fmt = format; /* Pointer into format string */
94 register char *bufptr = buffer; /* Pointer into output buffer.. */
95
96 /* Use this for sprintf unless we need something really big. */
97 char tembuf[DBL_MAX_10_EXP + 100];
98
99 /* Size of sprintf_buffer. */
100 int size_allocated = sizeof (tembuf);
101
102 /* Buffer to use for sprintf. Either tembuf or same as BIG_BUFFER. */
103 char *sprintf_buffer = tembuf;
104
105 /* Buffer we have got with malloc. */
106 char *big_buffer = 0;
107
108 register int tem;
109 char *string;
110 char fixed_buffer[20]; /* Default buffer for small formatting. */
111 char *fmtcpy;
112 int minlen;
113 int size; /* Field width factor; e.g., %90d */
114 unsigned char charbuf[5]; /* Used for %c. */
115
116 if (format_end == 0)
117 format_end = format + strlen (format);
118
119 if ((format_end - format + 1) < sizeof (fixed_buffer))
120 fmtcpy = fixed_buffer;
121 else
122 fmtcpy = (char *) alloca (format_end - format + 1);
123
124 bufsize--;
125
126 /* Loop until end of format string or buffer full. */
127 while (fmt != format_end && bufsize > 0)
128 {
129 if (*fmt == '%') /* Check for a '%' character */
130 {
131 int size_bound = 0;
132 int width; /* Columns occupied by STRING. */
133
134 fmt++;
135 /* Copy this one %-spec into fmtcpy. */
136 string = fmtcpy;
137 *string++ = '%';
138 while (1)
139 {
140 *string++ = *fmt;
141 if ('0' <= *fmt && *fmt <= '9')
142 {
143 /* Get an idea of how much space we might need.
144 This might be a field width or a precision; e.g.
145 %1.1000f and %1000.1f both might need 1000+ bytes.
146 Parse the width or precision, checking for overflow. */
147 int n = *fmt - '0';
148 while ('0' <= fmt[1] && fmt[1] <= '9')
149 {
150 if (n * 10 / 10 != n
151 || (n = n * 10 + (fmt[1] - '0')) < 0)
152 error ("Format width or precision too large");
153 *string++ = *++fmt;
154 }
155
156 if (size_bound < n)
157 size_bound = n;
158 }
159 else if (*fmt == '-' || *fmt == ' ' || *fmt == '.')
160 ;
161 else
162 break;
163 fmt++;
164 }
165 *string = 0;
166
167 /* Make the size bound large enough to handle floating point formats
168 with large numbers. */
169 size_bound += DBL_MAX_10_EXP + 50;
170
171 if (size_bound < 0)
172 error ("Format width or precision too large");
173
174 /* Make sure we have that much. */
175 if (size_bound > size_allocated)
176 {
177 if (big_buffer)
178 big_buffer = (char *) xrealloc (big_buffer, size_bound);
179 else
180 big_buffer = (char *) xmalloc (size_bound);
181 sprintf_buffer = big_buffer;
182 size_allocated = size_bound;
183 }
184 minlen = 0;
185 switch (*fmt++)
186 {
187 default:
188 error ("Invalid format operation %%%c", fmt[-1]);
189
190 /* case 'b': */
191 case 'd':
192 case 'o':
193 case 'x':
194 if (cnt == nargs)
195 error ("Not enough arguments for format string");
196 if (sizeof (int) == sizeof (EMACS_INT))
197 ;
198 else if (sizeof (long) == sizeof (EMACS_INT))
199 /* Insert an `l' the right place. */
200 string[1] = string[0],
201 string[0] = string[-1],
202 string[-1] = 'l',
203 string++;
204 else
205 abort ();
206 sprintf (sprintf_buffer, fmtcpy, args[cnt++]);
207 /* Now copy into final output, truncating as nec. */
208 string = sprintf_buffer;
209 goto doit;
210
211 case 'f':
212 case 'e':
213 case 'g':
214 {
215 union { double d; char *half[2]; } u;
216 if (cnt + 1 == nargs)
217 error ("not enough arguments for format string");
218 u.half[0] = args[cnt++];
219 u.half[1] = args[cnt++];
220 sprintf (sprintf_buffer, fmtcpy, u.d);
221 /* Now copy into final output, truncating as nec. */
222 string = sprintf_buffer;
223 goto doit;
224 }
225
226 case 'S':
227 string[-1] = 's';
228 case 's':
229 if (cnt == nargs)
230 error ("not enough arguments for format string");
231 if (fmtcpy[1] != 's')
232 minlen = atoi (&fmtcpy[1]);
233 if (lispstrings)
234 {
235 string = (char *) ((struct Lisp_String *)args[cnt])->data;
236 tem = ((struct Lisp_String *)args[cnt])->size;
237 cnt++;
238 }
239 else
240 {
241 string = args[cnt++];
242 tem = strlen (string);
243 }
244 width = strwidth (string, tem);
245 goto doit1;
246
247 /* Copy string into final output, truncating if no room. */
248 doit:
249 /* Coming here means STRING contains ASCII only. */
250 width = tem = strlen (string);
251 doit1:
252 /* We have already calculated:
253 TEM -- length of STRING,
254 WIDTH -- columns occupied by STRING when displayed, and
255 MINLEN -- minimum columns of the output. */
256 if (minlen > 0)
257 {
258 while (minlen > width && bufsize > 0)
259 {
260 *bufptr++ = ' ';
261 bufsize--;
262 minlen--;
263 }
264 minlen = 0;
265 }
266 if (tem > bufsize)
267 {
268 /* Truncate the string at character boundary. */
269 tem = bufsize;
270 while (!CHAR_HEAD_P (string[tem - 1])) tem--;
271 bcopy (string, bufptr, tem);
272 /* We must calculate WIDTH again. */
273 width = strwidth (bufptr, tem);
274 }
275 else
276 bcopy (string, bufptr, tem);
277 bufptr += tem;
278 bufsize -= tem;
279 if (minlen < 0)
280 {
281 while (minlen < - width && bufsize > 0)
282 {
283 *bufptr++ = ' ';
284 bufsize--;
285 minlen++;
286 }
287 minlen = 0;
288 }
289 continue;
290
291 case 'c':
292 if (cnt == nargs)
293 error ("not enough arguments for format string");
294 tem = CHAR_STRING ((EMACS_INT) args[cnt], charbuf, string);
295 cnt++;
296 string[tem] = 0;
297 width = strwidth (string, tem);
298 if (fmtcpy[1] != 'c')
299 minlen = atoi (&fmtcpy[1]);
300 goto doit1;
301
302 case '%':
303 fmt--; /* Drop thru and this % will be treated as normal */
304 }
305 }
306
307 {
308 /* Just some character; Copy it if the whole multi-byte form
309 fit in the buffer. */
310 char *save_bufptr = bufptr;
311
312 do { *bufptr++ = *fmt++; }
313 while (--bufsize > 0 && !CHAR_HEAD_P (*fmt));
314 if (!CHAR_HEAD_P (*fmt))
315 {
316 bufptr = save_bufptr;
317 break;
318 }
319 }
320 };
321
322 /* If we had to malloc something, free it. */
323 if (big_buffer)
324 xfree (big_buffer);
325
326 *bufptr = 0; /* Make sure our string end with a '\0' */
327 return bufptr - buffer;
328 }