Update FSF's address in the preamble.
[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 #include "lisp.h"
28
29 extern long *xmalloc (), *xrealloc ();
30
31 static int doprnt1 ();
32
33 /* Generate output from a format-spec FORMAT,
34 terminated at position FORMAT_END.
35 Output goes in BUFFER, which has room for BUFSIZE chars.
36 If the output does not fit, truncate it to fit.
37 Returns the number of characters stored into BUFFER.
38 ARGS points to the vector of arguments, and NARGS says how many.
39 A double counts as two arguments.
40 String arguments are passed as C strings.
41 Integers are passed as C integers. */
42
43 doprnt (buffer, bufsize, format, format_end, nargs, args)
44 char *buffer;
45 register int bufsize;
46 char *format;
47 char *format_end;
48 int nargs;
49 char **args;
50 {
51 return doprnt1 (0, buffer, bufsize, format, format_end, nargs, args);
52 }
53
54 /* Like doprnt except that strings in ARGS are passed
55 as Lisp_Object. */
56
57 doprnt_lisp (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 (1, buffer, bufsize, format, format_end, nargs, args);
66 }
67
68 static int
69 doprnt1 (lispstrings, buffer, bufsize, format, format_end, nargs, args)
70 int lispstrings;
71 char *buffer;
72 register int bufsize;
73 char *format;
74 char *format_end;
75 int nargs;
76 char **args;
77 {
78 int cnt = 0; /* Number of arg to gobble next */
79 register char *fmt = format; /* Pointer into format string */
80 register char *bufptr = buffer; /* Pointer into output buffer.. */
81
82 /* Use this for sprintf unless we need something really big. */
83 char tembuf[100];
84
85 /* Size of sprintf_buffer. */
86 int size_allocated = 100;
87
88 /* Buffer to use for sprintf. Either tembuf or same as BIG_BUFFER. */
89 char *sprintf_buffer = tembuf;
90
91 /* Buffer we have got with malloc. */
92 char *big_buffer = 0;
93
94 register int tem;
95 char *string;
96 char fixed_buffer[20]; /* Default buffer for small formatting. */
97 char *fmtcpy;
98 int minlen;
99 int size; /* Field width factor; e.g., %90d */
100 char charbuf[2]; /* Used for %c. */
101
102 if (format_end == 0)
103 format_end = format + strlen (format);
104
105 if ((format_end - format + 1) < sizeof (fixed_buffer))
106 fmtcpy = fixed_buffer;
107 else
108 fmtcpy = (char *) alloca (format_end - format + 1);
109
110 bufsize--;
111
112 /* Loop until end of format string or buffer full. */
113 while (fmt != format_end && bufsize > 0)
114 {
115 if (*fmt == '%') /* Check for a '%' character */
116 {
117 int size_bound;
118
119 fmt++;
120 /* Copy this one %-spec into fmtcpy. */
121 string = fmtcpy;
122 *string++ = '%';
123 while (1)
124 {
125 *string++ = *fmt;
126 if (! (*fmt >= '0' && *fmt <= '9')
127 && *fmt != '-' && *fmt != ' '&& *fmt != '.')
128 break;
129 fmt++;
130 }
131 *string = 0;
132 /* Get an idea of how much space we might need. */
133 size_bound = atoi (&fmtcpy[1]);
134
135 /* Avoid pitfall of negative "size" parameter ("%-200d"). */
136 if (size_bound < 0)
137 size_bound = -size_bound;
138 size_bound += 50;
139
140 if (size_bound > (unsigned) (1 << (BITS_PER_INT - 1)))
141 error ("Format padding too large");
142
143 /* Make sure we have that much. */
144 if (size_bound > size_allocated)
145 {
146 if (big_buffer)
147 big_buffer = (char *) xrealloc (big_buffer, size_bound);
148 else
149 big_buffer = (char *) xmalloc (size_bound);
150 sprintf_buffer = big_buffer;
151 size_allocated = size_bound;
152 }
153 minlen = 0;
154 switch (*fmt++)
155 {
156 default:
157 error ("Invalid format operation %%%c", fmt[-1]);
158
159 /* case 'b': */
160 case 'd':
161 case 'o':
162 case 'x':
163 if (cnt == nargs)
164 error ("Not enough arguments for format string");
165 if (sizeof (int) == sizeof (EMACS_INT))
166 ;
167 else if (sizeof (long) == sizeof (EMACS_INT))
168 /* Insert an `l' the right place. */
169 string[1] = string[0],
170 string[0] = string[-1],
171 string[-1] = 'l',
172 string++;
173 else
174 abort ();
175 sprintf (sprintf_buffer, fmtcpy, args[cnt++]);
176 /* Now copy into final output, truncating as nec. */
177 string = sprintf_buffer;
178 goto doit;
179
180 case 'f':
181 case 'e':
182 case 'g':
183 {
184 union { double d; char *half[2]; } u;
185 if (cnt + 1 == nargs)
186 error ("not enough arguments for format string");
187 u.half[0] = args[cnt++];
188 u.half[1] = args[cnt++];
189 sprintf (sprintf_buffer, fmtcpy, u.d);
190 /* Now copy into final output, truncating as nec. */
191 string = sprintf_buffer;
192 goto doit;
193 }
194
195 case 'S':
196 string[-1] = 's';
197 case 's':
198 if (cnt == nargs)
199 error ("not enough arguments for format string");
200 if (fmtcpy[1] != 's')
201 minlen = atoi (&fmtcpy[1]);
202 if (lispstrings)
203 {
204 string = (char *) XSTRING (((Lisp_Object *) args)[cnt])->data;
205 tem = XSTRING (((Lisp_Object *) args)[cnt])->size;
206 cnt++;
207 }
208 else
209 {
210 string = args[cnt++];
211 tem = strlen (string);
212 }
213 goto doit1;
214
215 /* Copy string into final output, truncating if no room. */
216 doit:
217 tem = strlen (string);
218 doit1:
219 if (minlen > 0)
220 {
221 while (minlen > tem && bufsize > 0)
222 {
223 *bufptr++ = ' ';
224 bufsize--;
225 minlen--;
226 }
227 minlen = 0;
228 }
229 if (tem > bufsize)
230 tem = bufsize;
231 bcopy (string, bufptr, tem);
232 bufptr += tem;
233 bufsize -= tem;
234 if (minlen < 0)
235 {
236 while (minlen < - tem && bufsize > 0)
237 {
238 *bufptr++ = ' ';
239 bufsize--;
240 minlen++;
241 }
242 minlen = 0;
243 }
244 continue;
245
246 case 'c':
247 if (cnt == nargs)
248 error ("not enough arguments for format string");
249 *charbuf = (EMACS_INT) args[cnt++];
250 string = charbuf;
251 tem = 1;
252 if (fmtcpy[1] != 'c')
253 minlen = atoi (&fmtcpy[1]);
254 goto doit1;
255
256 case '%':
257 fmt--; /* Drop thru and this % will be treated as normal */
258 }
259 }
260 *bufptr++ = *fmt++; /* Just some characters; Copy 'em */
261 bufsize--;
262 };
263
264 /* If we had to malloc something, free it. */
265 if (big_buffer)
266 xfree (big_buffer);
267
268 *bufptr = 0; /* Make sure our string end with a '\0' */
269 return bufptr - buffer;
270 }