(describe_abbrev): Return void.
[bpt/emacs.git] / src / doprnt.c
CommitLineData
24f98398
JA
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
6This file is part of GNU Emacs.
7
8GNU Emacs is free software; you can redistribute it and/or modify
9it under the terms of the GNU General Public License as published by
7c938215 10the Free Software Foundation; either version 2, or (at your option)
24f98398
JA
11any later version.
12
13GNU Emacs is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16GNU General Public License for more details.
17
18You should have received a copy of the GNU General Public License
19along with GNU Emacs; see the file COPYING. If not, write to
3b7ad313
EN
20the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21Boston, MA 02111-1307, USA. */
24f98398
JA
22
23
6d291527 24#include <config.h>
24f98398
JA
25#include <stdio.h>
26#include <ctype.h>
27
cf09633a 28#ifdef STDC_HEADERS
be65c2f4
PE
29#include <float.h>
30#endif
31
523e9291
RS
32#include "lisp.h"
33
be65c2f4
PE
34#ifndef DBL_MAX_10_EXP
35#define DBL_MAX_10_EXP 308 /* IEEE double */
36#endif
37
a0ca925c
KH
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
1077c789
RS
43extern long *xmalloc (), *xrealloc ();
44
1513af9e
RS
45static int doprnt1 ();
46
f4c730d3
RS
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.
1513af9e
RS
53 A double counts as two arguments.
54 String arguments are passed as C strings.
55 Integers are passed as C integers. */
f4c730d3 56
24f98398
JA
57doprnt (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;
1513af9e
RS
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
71doprnt_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
82static int
83doprnt1 (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;
24f98398
JA
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.. */
03383aaf 95
f4c730d3 96 /* Use this for sprintf unless we need something really big. */
be65c2f4 97 char tembuf[DBL_MAX_10_EXP + 100];
03383aaf 98
f4c730d3 99 /* Size of sprintf_buffer. */
be65c2f4 100 int size_allocated = sizeof (tembuf);
03383aaf 101
f4c730d3
RS
102 /* Buffer to use for sprintf. Either tembuf or same as BIG_BUFFER. */
103 char *sprintf_buffer = tembuf;
03383aaf 104
f4c730d3
RS
105 /* Buffer we have got with malloc. */
106 char *big_buffer = 0;
03383aaf 107
24f98398
JA
108 register int tem;
109 char *string;
03383aaf
BF
110 char fixed_buffer[20]; /* Default buffer for small formatting. */
111 char *fmtcpy;
24f98398
JA
112 int minlen;
113 int size; /* Field width factor; e.g., %90d */
e9aea0ef 114 unsigned char charbuf[5]; /* Used for %c. */
24f98398
JA
115
116 if (format_end == 0)
117 format_end = format + strlen (format);
118
03383aaf
BF
119 if ((format_end - format + 1) < sizeof (fixed_buffer))
120 fmtcpy = fixed_buffer;
121 else
91098d38 122 fmtcpy = (char *) alloca (format_end - format + 1);
03383aaf 123
24f98398 124 bufsize--;
03383aaf
BF
125
126 /* Loop until end of format string or buffer full. */
127 while (fmt != format_end && bufsize > 0)
24f98398
JA
128 {
129 if (*fmt == '%') /* Check for a '%' character */
130 {
be65c2f4 131 int size_bound = 0;
a0ca925c 132 int width; /* Columns occupied by STRING. */
f4c730d3 133
24f98398 134 fmt++;
d427b66a 135 /* Copy this one %-spec into fmtcpy. */
24f98398
JA
136 string = fmtcpy;
137 *string++ = '%';
03383aaf 138 while (1)
24f98398
JA
139 {
140 *string++ = *fmt;
be65c2f4
PE
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
24f98398
JA
162 break;
163 fmt++;
164 }
165 *string = 0;
03383aaf 166
be65c2f4
PE
167 /* Make the size bound large enough to handle floating point formats
168 with large numbers. */
169 size_bound += DBL_MAX_10_EXP + 50;
03383aaf 170
be65c2f4
PE
171 if (size_bound < 0)
172 error ("Format width or precision too large");
6e951728 173
f4c730d3
RS
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 }
24f98398
JA
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)
6e951728 195 error ("Not enough arguments for format string");
f9fa352f
RS
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 ();
f4c730d3
RS
206 sprintf (sprintf_buffer, fmtcpy, args[cnt++]);
207 /* Now copy into final output, truncating as nec. */
208 string = sprintf_buffer;
24f98398
JA
209 goto doit;
210
f4c730d3
RS
211 case 'f':
212 case 'e':
213 case 'g':
214 {
215 union { double d; char *half[2]; } u;
216 if (cnt + 1 == nargs)
ee0c28e3 217 error ("not enough arguments for format string");
f4c730d3
RS
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
24f98398
JA
226 case 'S':
227 string[-1] = 's';
228 case 's':
229 if (cnt == nargs)
ee0c28e3 230 error ("not enough arguments for format string");
24f98398
JA
231 if (fmtcpy[1] != 's')
232 minlen = atoi (&fmtcpy[1]);
1513af9e
RS
233 if (lispstrings)
234 {
857c4c67
RS
235 string = (char *) ((struct Lisp_String *)args[cnt])->data;
236 tem = ((struct Lisp_String *)args[cnt])->size;
1513af9e
RS
237 cnt++;
238 }
239 else
240 {
241 string = args[cnt++];
242 tem = strlen (string);
243 }
a0ca925c 244 width = strwidth (string, tem);
1513af9e
RS
245 goto doit1;
246
24f98398
JA
247 /* Copy string into final output, truncating if no room. */
248 doit:
a0ca925c
KH
249 /* Coming here means STRING contains ASCII only. */
250 width = tem = strlen (string);
35a65fce 251 doit1:
a0ca925c
KH
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. */
24f98398
JA
256 if (minlen > 0)
257 {
a0ca925c 258 while (minlen > width && bufsize > 0)
24f98398
JA
259 {
260 *bufptr++ = ' ';
261 bufsize--;
262 minlen--;
263 }
264 minlen = 0;
265 }
266 if (tem > bufsize)
a0ca925c
KH
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);
24f98398
JA
277 bufptr += tem;
278 bufsize -= tem;
279 if (minlen < 0)
280 {
a0ca925c 281 while (minlen < - width && bufsize > 0)
24f98398
JA
282 {
283 *bufptr++ = ' ';
284 bufsize--;
285 minlen++;
286 }
287 minlen = 0;
288 }
289 continue;
290
291 case 'c':
292 if (cnt == nargs)
ee0c28e3 293 error ("not enough arguments for format string");
a0ca925c
KH
294 tem = CHAR_STRING ((EMACS_INT) args[cnt], charbuf, string);
295 cnt++;
296 string[tem] = 0;
297 width = strwidth (string, tem);
35a65fce
RS
298 if (fmtcpy[1] != 'c')
299 minlen = atoi (&fmtcpy[1]);
300 goto doit1;
24f98398
JA
301
302 case '%':
303 fmt--; /* Drop thru and this % will be treated as normal */
304 }
305 }
a0ca925c
KH
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 }
24f98398
JA
320 };
321
f4c730d3
RS
322 /* If we had to malloc something, free it. */
323 if (big_buffer)
9ac0d9e0 324 xfree (big_buffer);
f4c730d3 325
24f98398
JA
326 *bufptr = 0; /* Make sure our string end with a '\0' */
327 return bufptr - buffer;
328}