channel: Clear channel flag in error branch.
[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 /*** MODULEINFO
29         <support_level>core</support_level>
30  ***/
31
32 #include "asterisk.h"
33
34 #include <ctype.h>
35
36 #include "asterisk/module.h"
37 #include "asterisk/channel.h"
38 #include "asterisk/pbx.h"
39 #include "asterisk/utils.h"
40 #include "asterisk/app.h"
41
42 AST_THREADSTORAGE(result_buf);
43
44 /*** DOCUMENTATION
45         <function name="SPRINTF" language="en_US">
46                 <synopsis>
47                         Format a variable according to a format string.
48                 </synopsis>
49                 <syntax>
50                         <parameter name="format" required="true" />
51                         <parameter name="arg1" required="true" />
52                         <parameter name="arg2" multiple="true" />
53                         <parameter name="argN" />
54                 </syntax>
55                 <description>
56                         <para>Parses the format string specified and returns a string matching 
57                         that format. Supports most options found in <emphasis>sprintf(3)</emphasis>.
58                         Returns a shortened string if a format specifier is not recognized.</para>
59                 </description>
60                 <see-also>
61                         <ref type="manpage">sprintf(3)</ref>
62                 </see-also>
63         </function>
64  ***/
65 static int acf_sprintf(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
66 {
67 #define SPRINTF_FLAG    0
68 #define SPRINTF_WIDTH   1
69 #define SPRINTF_PRECISION       2
70 #define SPRINTF_LENGTH  3
71 #define SPRINTF_CONVERSION      4
72         int i, state = -1, argcount = 0;
73         char *formatstart = NULL, *bufptr = buf;
74         char formatbuf[256] = "";
75         int tmpi;
76         double tmpd;
77         AST_DECLARE_APP_ARGS(arg,
78                                 AST_APP_ARG(format);
79                                 AST_APP_ARG(var)[100];
80         );
81
82         AST_STANDARD_APP_ARGS(arg, data);
83
84         /* Scan the format, converting each argument into the requisite format type. */
85         for (i = 0; arg.format[i]; i++) {
86                 switch (state) {
87                 case SPRINTF_FLAG:
88                         if (strchr("#0- +'I", arg.format[i]))
89                                 break;
90                         state = SPRINTF_WIDTH;
91                 case SPRINTF_WIDTH:
92                         if (arg.format[i] >= '0' && arg.format[i] <= '9')
93                                 break;
94
95                         /* Next character must be a period to go into a precision */
96                         if (arg.format[i] == '.') {
97                                 state = SPRINTF_PRECISION;
98                         } else {
99                                 state = SPRINTF_LENGTH;
100                                 i--;
101                         }
102                         break;
103                 case SPRINTF_PRECISION:
104                         if (arg.format[i] >= '0' && arg.format[i] <= '9')
105                                 break;
106                         state = SPRINTF_LENGTH;
107                 case SPRINTF_LENGTH:
108                         if (strchr("hl", arg.format[i])) {
109                                 if (arg.format[i + 1] == arg.format[i])
110                                         i++;
111                                 state = SPRINTF_CONVERSION;
112                                 break;
113                         } else if (strchr("Lqjzt", arg.format[i])) {
114                                 state = SPRINTF_CONVERSION;
115                                 break;
116                         }
117                         state = SPRINTF_CONVERSION;
118                 case SPRINTF_CONVERSION:
119                         if (strchr("diouxXc", arg.format[i])) {
120                                 /* Integer */
121
122                                 /* Isolate this format alone */
123                                 ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
124                                 formatbuf[&arg.format[i] - formatstart + 1] = '\0';
125
126                                 /* Convert the argument into the required type */
127                                 if (arg.var[argcount]) {
128                                         if (sscanf(arg.var[argcount++], "%30d", &tmpi) != 1) {
129                                                 ast_log(LOG_ERROR, "Argument '%s' is not an integer number for format '%s'\n", arg.var[argcount - 1], formatbuf);
130                                                 goto sprintf_fail;
131                                         }
132                                 } else {
133                                         ast_log(LOG_ERROR, "SPRINTF() has more format specifiers than arguments!\n");
134                                         goto sprintf_fail;
135                                 }
136
137                                 /* Format the argument */
138                                 snprintf(bufptr, buf + len - bufptr, formatbuf, tmpi);
139
140                                 /* Update the position of the next parameter to print */
141                                 bufptr = strchr(buf, '\0');
142                         } else if (strchr("eEfFgGaA", arg.format[i])) {
143                                 /* Double */
144
145                                 /* Isolate this format alone */
146                                 ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
147                                 formatbuf[&arg.format[i] - formatstart + 1] = '\0';
148
149                                 /* Convert the argument into the required type */
150                                 if (arg.var[argcount]) {
151                                         if (sscanf(arg.var[argcount++], "%30lf", &tmpd) != 1) {
152                                                 ast_log(LOG_ERROR, "Argument '%s' is not a floating point number for format '%s'\n", arg.var[argcount - 1], formatbuf);
153                                                 goto sprintf_fail;
154                                         }
155                                 } else {
156                                         ast_log(LOG_ERROR, "SPRINTF() has more format specifiers than arguments!\n");
157                                         goto sprintf_fail;
158                                 }
159
160                                 /* Format the argument */
161                                 snprintf(bufptr, buf + len - bufptr, formatbuf, tmpd);
162
163                                 /* Update the position of the next parameter to print */
164                                 bufptr = strchr(buf, '\0');
165                         } else if (arg.format[i] == 's') {
166                                 /* String */
167
168                                 /* Isolate this format alone */
169                                 ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
170                                 formatbuf[&arg.format[i] - formatstart + 1] = '\0';
171
172                                 /* Format the argument */
173                                 snprintf(bufptr, buf + len - bufptr, formatbuf, arg.var[argcount++]);
174
175                                 /* Update the position of the next parameter to print */
176                                 bufptr = strchr(buf, '\0');
177                         } else if (arg.format[i] == '%') {
178                                 /* Literal data to copy */
179                                 *bufptr++ = arg.format[i];
180                         } else {
181                                 /* Not supported */
182
183                                 /* Isolate this format alone */
184                                 ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
185                                 formatbuf[&arg.format[i] - formatstart + 1] = '\0';
186
187                                 ast_log(LOG_ERROR, "Format type not supported: '%s' with argument '%s'\n", formatbuf, arg.var[argcount++]);
188                                 goto sprintf_fail;
189                         }
190                         state = -1;
191                         break;
192                 default:
193                         if (arg.format[i] == '%') {
194                                 state = SPRINTF_FLAG;
195                                 formatstart = &arg.format[i];
196                                 break;
197                         } else {
198                                 /* Literal data to copy */
199                                 *bufptr++ = arg.format[i];
200                         }
201                 }
202         }
203         *bufptr = '\0';
204         return 0;
205 sprintf_fail:
206         return -1;
207 }
208
209 static struct ast_custom_function sprintf_function = {
210         .name = "SPRINTF",
211         .read = acf_sprintf,
212 };
213
214 static int unload_module(void)
215 {
216         int res = 0;
217
218         res |= ast_custom_function_unregister(&sprintf_function);
219
220         return res;
221 }
222
223 static int load_module(void)
224 {
225         int res = 0;
226
227         res |= ast_custom_function_register(&sprintf_function);
228
229         return res;
230 }
231
232 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "SPRINTF dialplan function");