Various updates to the unit test API.
[asterisk/asterisk.git] / funcs / func_sprintf.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2005-2006, Digium, Inc.
5  * Portions Copyright (C) 2005, Tilghman Lesher.  All rights reserved.
6  * Portions Copyright (C) 2005, Anthony Minessale II
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief String manipulation dialplan functions
22  *
23  * \author Tilghman Lesher
24  * \author Anothony Minessale II 
25  * \ingroup functions
26  */
27
28 #include "asterisk.h"
29
30 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
31
32 #include <ctype.h>
33
34 #include "asterisk/module.h"
35 #include "asterisk/channel.h"
36 #include "asterisk/pbx.h"
37 #include "asterisk/utils.h"
38 #include "asterisk/app.h"
39
40 AST_THREADSTORAGE(result_buf);
41
42 /*** DOCUMENTATION
43         <function name="SPRINTF" language="en_US">
44                 <synopsis>
45                         Format a variable according to a format string.
46                 </synopsis>
47                 <syntax>
48                         <parameter name="format" required="true" />
49                         <parameter name="arg1" required="true" />
50                         <parameter name="arg2" multiple="true" />
51                         <parameter name="argN" />
52                 </syntax>
53                 <description>
54                         <para>Parses the format string specified and returns a string matching 
55                         that format. Supports most options found in <emphasis>sprintf(3)</emphasis>.
56                         Returns a shortened string if a format specifier is not recognized.</para>
57                 </description>
58                 <see-also>
59                         <ref type="manpage">sprintf(3)</ref>
60                 </see-also>
61         </function>
62  ***/
63 static int acf_sprintf(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
64 {
65 #define SPRINTF_FLAG    0
66 #define SPRINTF_WIDTH   1
67 #define SPRINTF_PRECISION       2
68 #define SPRINTF_LENGTH  3
69 #define SPRINTF_CONVERSION      4
70         int i, state = -1, argcount = 0;
71         char *formatstart = NULL, *bufptr = buf;
72         char formatbuf[256] = "";
73         int tmpi;
74         double tmpd;
75         AST_DECLARE_APP_ARGS(arg,
76                                 AST_APP_ARG(format);
77                                 AST_APP_ARG(var)[100];
78         );
79
80         AST_STANDARD_APP_ARGS(arg, data);
81
82         /* Scan the format, converting each argument into the requisite format type. */
83         for (i = 0; arg.format[i]; i++) {
84                 switch (state) {
85                 case SPRINTF_FLAG:
86                         if (strchr("#0- +'I", arg.format[i]))
87                                 break;
88                         state = SPRINTF_WIDTH;
89                 case SPRINTF_WIDTH:
90                         if (arg.format[i] >= '0' && arg.format[i] <= '9')
91                                 break;
92
93                         /* Next character must be a period to go into a precision */
94                         if (arg.format[i] == '.') {
95                                 state = SPRINTF_PRECISION;
96                         } else {
97                                 state = SPRINTF_LENGTH;
98                                 i--;
99                         }
100                         break;
101                 case SPRINTF_PRECISION:
102                         if (arg.format[i] >= '0' && arg.format[i] <= '9')
103                                 break;
104                         state = SPRINTF_LENGTH;
105                 case SPRINTF_LENGTH:
106                         if (strchr("hl", arg.format[i])) {
107                                 if (arg.format[i + 1] == arg.format[i])
108                                         i++;
109                                 state = SPRINTF_CONVERSION;
110                                 break;
111                         } else if (strchr("Lqjzt", arg.format[i])) {
112                                 state = SPRINTF_CONVERSION;
113                                 break;
114                         }
115                         state = SPRINTF_CONVERSION;
116                 case SPRINTF_CONVERSION:
117                         if (strchr("diouxXc", arg.format[i])) {
118                                 /* Integer */
119
120                                 /* Isolate this format alone */
121                                 ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
122                                 formatbuf[&arg.format[i] - formatstart + 1] = '\0';
123
124                                 /* Convert the argument into the required type */
125                                 if (arg.var[argcount]) {
126                                         if (sscanf(arg.var[argcount++], "%30d", &tmpi) != 1) {
127                                                 ast_log(LOG_ERROR, "Argument '%s' is not an integer number for format '%s'\n", arg.var[argcount - 1], formatbuf);
128                                                 goto sprintf_fail;
129                                         }
130                                 } else {
131                                         ast_log(LOG_ERROR, "SPRINTF() has more format specifiers than arguments!\n");
132                                         goto sprintf_fail;
133                                 }
134
135                                 /* Format the argument */
136                                 snprintf(bufptr, buf + len - bufptr, formatbuf, tmpi);
137
138                                 /* Update the position of the next parameter to print */
139                                 bufptr = strchr(buf, '\0');
140                         } else if (strchr("eEfFgGaA", arg.format[i])) {
141                                 /* Double */
142
143                                 /* Isolate this format alone */
144                                 ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
145                                 formatbuf[&arg.format[i] - formatstart + 1] = '\0';
146
147                                 /* Convert the argument into the required type */
148                                 if (arg.var[argcount]) {
149                                         if (sscanf(arg.var[argcount++], "%30lf", &tmpd) != 1) {
150                                                 ast_log(LOG_ERROR, "Argument '%s' is not a floating point number for format '%s'\n", arg.var[argcount - 1], formatbuf);
151                                                 goto sprintf_fail;
152                                         }
153                                 } else {
154                                         ast_log(LOG_ERROR, "SPRINTF() has more format specifiers than arguments!\n");
155                                         goto sprintf_fail;
156                                 }
157
158                                 /* Format the argument */
159                                 snprintf(bufptr, buf + len - bufptr, formatbuf, tmpd);
160
161                                 /* Update the position of the next parameter to print */
162                                 bufptr = strchr(buf, '\0');
163                         } else if (arg.format[i] == 's') {
164                                 /* String */
165
166                                 /* Isolate this format alone */
167                                 ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
168                                 formatbuf[&arg.format[i] - formatstart + 1] = '\0';
169
170                                 /* Format the argument */
171                                 snprintf(bufptr, buf + len - bufptr, formatbuf, arg.var[argcount++]);
172
173                                 /* Update the position of the next parameter to print */
174                                 bufptr = strchr(buf, '\0');
175                         } else if (arg.format[i] == '%') {
176                                 /* Literal data to copy */
177                                 *bufptr++ = arg.format[i];
178                         } else {
179                                 /* Not supported */
180
181                                 /* Isolate this format alone */
182                                 ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
183                                 formatbuf[&arg.format[i] - formatstart + 1] = '\0';
184
185                                 ast_log(LOG_ERROR, "Format type not supported: '%s' with argument '%s'\n", formatbuf, arg.var[argcount++]);
186                                 goto sprintf_fail;
187                         }
188                         state = -1;
189                         break;
190                 default:
191                         if (arg.format[i] == '%') {
192                                 state = SPRINTF_FLAG;
193                                 formatstart = &arg.format[i];
194                                 break;
195                         } else {
196                                 /* Literal data to copy */
197                                 *bufptr++ = arg.format[i];
198                         }
199                 }
200         }
201         *bufptr = '\0';
202         return 0;
203 sprintf_fail:
204         return -1;
205 }
206
207 static struct ast_custom_function sprintf_function = {
208         .name = "SPRINTF",
209         .read = acf_sprintf,
210 };
211
212 static int unload_module(void)
213 {
214         int res = 0;
215
216         res |= ast_custom_function_unregister(&sprintf_function);
217
218         return res;
219 }
220
221 static int load_module(void)
222 {
223         int res = 0;
224
225         res |= ast_custom_function_register(&sprintf_function);
226
227         return res;
228 }
229
230 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "SPRINTF dialplan function");