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