Edinburgh Speech Tools  2.4-release
 All Classes Functions Variables Typedefs Enumerations Enumerator Friends Pages
slib_format.cc
1 /*************************************************************************/
2 /* */
3 /* Centre for Speech Technology Research */
4 /* University of Edinburgh, UK */
5 /* Copyright (c) 1996,1997 */
6 /* All Rights Reserved. */
7 /* */
8 /* Permission is hereby granted, free of charge, to use and distribute */
9 /* this software and its documentation without restriction, including */
10 /* without limitation the rights to use, copy, modify, merge, publish, */
11 /* distribute, sublicense, and/or sell copies of this work, and to */
12 /* permit persons to whom this work is furnished to do so, subject to */
13 /* the following conditions: */
14 /* 1. The code must retain the above copyright notice, this list of */
15 /* conditions and the following disclaimer. */
16 /* 2. Any modifications must be clearly marked as such. */
17 /* 3. Original authors' names are not deleted. */
18 /* 4. The authors' names are not used to endorse or promote products */
19 /* derived from this software without specific prior written */
20 /* permission. */
21 /* */
22 /* THE UNIVERSITY OF EDINBURGH AND THE CONTRIBUTORS TO THIS WORK */
23 /* DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING */
24 /* ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT */
25 /* SHALL THE UNIVERSITY OF EDINBURGH NOR THE CONTRIBUTORS BE LIABLE */
26 /* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES */
27 /* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN */
28 /* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, */
29 /* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF */
30 /* THIS SOFTWARE. */
31 /* */
32 /*************************************************************************/
33 /* Author : Alan W Black */
34 /* Date : December 1996 */
35 /*-----------------------------------------------------------------------*/
36 /* */
37 /* A format function for formated output (like printf) */
38 /* */
39 /* Its amazing how much I have to write myself to get this to work when */
40 /* most people believe this a c library function. */
41 /* */
42 /*=======================================================================*/
43 
44 #include <cstdlib>
45 #include <cstdio>
46 #include "EST_cutils.h"
47 #include "siod.h"
48 #include "siodp.h"
49 
50 static int format_string(LISP fd,const char *formatstr, const char *str);
51 static int format_lisp(LISP fd,const char *formatstr, LISP a);
52 static int format_int(LISP fd,const char *formatstr, int i);
53 static int format_float(LISP fd,const char *formatstr, float f);
54 static int format_double(LISP fd,const char *formatstr, double d);
55 static int format_char(LISP fd, char c);
56 static int get_field_width(const char *directive);
57 static char *get_directive(const char *fstr);
58 static char directive_type(const char *fstr);
59 static void output_string(LISP fd,const char *str);
60 static int count_arg_places(const char *formatstring);
61 
62 static EST_String outstring;
63 static EST_Regex anumber_rx("[0-9]+");
64 
65 LISP l_format(LISP args)
66 {
67  // A format function for formated output
68  // Hmm not sure how to do this without writing lots myself
69  const char *formatstring = get_c_string(car(cdr(args)));
70  LISP lfd = car(args);
71  LISP fargs = cdr(cdr(args));
72  int i;
73  LISP a;
74 
75  if (count_arg_places(formatstring) != siod_llength(fargs))
76  err("format: wrong number of args for format string",NIL);
77 
78  outstring="";
79 
80  for (i=0,a=fargs; formatstring[i] != '\0'; i++)
81  {
82  if (formatstring[i] != '%')
83  format_char(lfd,formatstring[i]);
84  else if (formatstring[i+1] == '%')
85  {
86  format_char(lfd,formatstring[i]);
87  i++; // skip quoted %
88  }
89  else if (directive_type(formatstring+i) == 's')
90  {
91  i+= format_string(lfd,formatstring+i,get_c_string(car(a)));
92  a = cdr(a);
93  }
94  else if (directive_type(formatstring+i) == 'l')
95  {
96  i+= format_lisp(lfd,formatstring+i,car(a));
97  a = cdr(a);
98  }
99  else if ((directive_type(formatstring+i) == 'd') ||
100  (directive_type(formatstring+i) == 'x'))
101  {
102  i += format_int(lfd,formatstring+i,(int)get_c_int(car(a)));
103  a = cdr(a);
104  }
105  else if (directive_type(formatstring+i) == 'f')
106  {
107  i += format_float(lfd,formatstring+i,(float)get_c_double(car(a)));
108  a = cdr(a);
109  }
110  else if (directive_type(formatstring+i) == 'g')
111  {
112  i += format_double(lfd,formatstring+i,get_c_double(car(a)));
113  a = cdr(a);
114  }
115  else if (directive_type(formatstring+i) == 'c')
116  {
117  format_char(lfd,(char)get_c_int(car(a)));
118  i++;
119  a = cdr(a);
120  }
121  else
122  {
123  cerr << "SIOD format: unsupported format directive %"
124  << directive_type(formatstring+i) << endl;
125  err("",NIL);
126  }
127  }
128 
129  if (lfd == NIL)
130  return strintern(outstring);
131  else
132  return NIL;
133 }
134 
135 static int format_string(LISP fd,const char *formatstr, const char *str)
136 {
137  // Output str to fd using directive at start of formatstr
138  // Returns the number character in the format directive
139  char *directive = get_directive(formatstr);
140  int width = get_field_width(directive);
141  char *buff;
142 
143  if (width > (signed)strlen(str))
144  buff = walloc(char,width+10);
145  else
146  buff = walloc(char,strlen(str)+1);
147 
148  sprintf(buff,directive,str);
149 
150  output_string(fd,buff);
151  width = strlen(directive)-1;
152  wfree(buff);
153  wfree(directive);
154 
155  return width;
156 }
157 
158 static int format_lisp(LISP fd,const char *formatstr, LISP a)
159 {
160  // Output a as str to fd using directive at start of formatstr
161  // Returns the number character in the format directive
162  char *directive = get_directive(formatstr);
163  int width = get_field_width(directive);
164  EST_String buff;
165 
166  if (width != 0)
167  err("format: width in %l not supported",NIL);
168 
169  buff = siod_sprint(a);
170 
171  output_string(fd,buff);
172  width = strlen(directive)-1;
173  wfree(directive);
174 
175  return width;
176 }
177 
178 static int format_int(LISP fd, const char *formatstr, int i)
179 {
180  // Output i to fd using directive at start of formatstr
181  // Returns the number character in the format directive
182  char *directive = get_directive(formatstr);
183  int width = get_field_width(directive);
184  char *buff;
185 
186  if (width > 20)
187  buff = walloc(char,width+10);
188  else
189  buff = walloc(char,20);
190 
191  sprintf(buff,directive,i);
192 
193  output_string(fd,buff);
194  width = strlen(directive)-1;
195  wfree(buff);
196  wfree(directive);
197 
198  return width;
199 }
200 
201 static int format_float(LISP fd, const char *formatstr, float f)
202 {
203  // Output f to fd using directive at start of formatstr
204  // Returns the number character in the format directive
205  char *directive = get_directive(formatstr);
206  int width = get_field_width(directive);
207  char *buff;
208 
209  if (width > 20)
210  buff = walloc(char,width+10);
211  else
212  buff = walloc(char,20);
213 
214  sprintf(buff,directive,f);
215 
216  output_string(fd,buff);
217  width = strlen(directive)-1;
218  wfree(buff);
219  wfree(directive);
220 
221  return width;
222 }
223 
224 static int format_double(LISP fd, const char *formatstr, double d)
225 {
226  // Output f to fd using directive at start of formatstr
227  // Returns the number character in the format directive
228  char *directive = get_directive(formatstr);
229  int width = get_field_width(directive);
230  char *buff;
231 
232  if (width > 30)
233  buff = walloc(char,width+10);
234  else
235  buff = walloc(char,30);
236 
237  sprintf(buff,directive,d);
238 
239  output_string(fd,buff);
240  width = strlen(directive)-1;
241  wfree(buff);
242  wfree(directive);
243 
244  return width;
245 }
246 
247 static int format_char(LISP fd, char c)
248 {
249  // Output c to fd using directive at start of formatstr
250  // Returns the number character in the format directive
251  char buff[10];
252 
253  sprintf(buff,"%c",c);
254 
255  output_string(fd,buff);
256 
257  return 0;
258 }
259 
260 static int get_field_width(const char *directive)
261 {
262  // Look inside the directive for any explicit width info
263 
264  if (strlen(directive) == 2)
265  return 0;
266  else
267  {
268  EST_String nums = directive;
269  nums = nums.at(1,strlen(directive)-2);
270  if (nums.matches(anumber_rx))
271  return atoi(nums);
272  else if (nums.contains("."))
273  {
274  EST_String n1 = nums.before(".");
275  EST_String n2 = nums.after(".");
276  return atoi(n1) + atoi(n2);
277  }
278  else
279  {
280  cerr << "SIOD format: can't find width in directive "
281  << directive << endl;
282  err("",NIL);
283  }
284  }
285  return 0;
286 }
287 
288 static char *get_directive(const char *fstr)
289 {
290  // Copy the format directive from the start of this string
291  int i;
292 
293  for (i=0; fstr[i] != '\0'; i++)
294  if ((fstr[i] >= 'a') &&
295  (fstr[i] <= 'z'))
296  break;
297  if (fstr[i] == '\0')
298  err("format: premature end of format structure",NIL);
299  char *direct = walloc(char,i+2);
300  memmove(direct,fstr,i+1);
301  direct[i+1] = '\0';
302  return direct;
303 }
304 
305 static char directive_type(const char *fstr)
306 {
307  // return the next lower case character. This identifies the
308  // type of the argument to be inserted in the format string
309  int i;
310 
311  for (i=0; fstr[i] != '\0'; i++)
312  if ((fstr[i] >= 'a') &&
313  (fstr[i] <= 'z'))
314  {
315  return fstr[i];
316  }
317 
318  err("SIOD format: premature end of format structure",NIL);
319  return '\0';
320 
321 }
322 
323 static void output_string(LISP fd, const char *str)
324 {
325  if (fd == NIL)
326  outstring += str;
327  else if (fd == truth)
328  fprintf(stdout,"%s",str);
329  else if (TYPEP(fd,tc_c_file))
330  fprintf(get_c_file(fd,NULL),"%s",str);
331  else
332  err("format: not a file",fd);
333 }
334 
335 static int count_arg_places(const char *formatstring)
336 {
337  // count number of places in the format string.
338  int c,i;
339 
340  for (c=i=0; formatstring[i] != '\0'; i++)
341  if (formatstring[i] == '%')
342  {
343  if (formatstring[i+1] == '%')
344  i++;
345  else
346  c++;
347  }
348 
349  return c;
350 }
351 
352 void init_subrs_format()
353 {
354  init_lsubr("format",l_format,
355  "(format FD FORMATSTRING ARG0 ARG1 ...)\n\
356  Output ARGs to FD using FROMATSTRING. FORMATSTRING is like a printf\n\
357  formatstrng. FD may be a filedescriptor, or t (standard output) or\n\
358  nil (return as a string). Note not all printf format directive are\n\
359  supported. %l is additionally support for Lisp objects.\n\
360  [see Scheme I/O]");
361 }