1 /* Gammu logging/debugging functions */
2 /* Copyright (c) 2008-2009 by Michal Cihar <michal@cihar.com> */
3 /* Licensed under GPL2+ */
4
5 #include "debug.h"
6
7 #include <string.h>
8 #include <ctype.h>
9
10 /* Commit flag for opening files is MS extension, some other
11 * implementations (like BCC 5.5) don't like this flag at all */
12 #ifdef _MSC_VER
13 # define COMMIT_FLAG "c"
14 #else
15 # define COMMIT_FLAG ""
16 #endif
17
18 #if defined _WIN64 || defined _WIN32
19 #define strcasecmp _stricmp
20 #endif
21
22 GSM_Debug_Info GSM_none_debug = {
23 0,
24 NULL,
25 FALSE,
26 "",
27 FALSE,
28 FALSE,
29 NULL,
30 NULL
31 };
32
33 GSM_Debug_Info GSM_global_debug = {
34 0,
35 NULL,
36 FALSE,
37 "",
38 FALSE,
39 FALSE,
40 NULL,
41 NULL
42 };
43
44 /**
45 * Actually writes message to debuging file.
46 */
dbg_write(GSM_Debug_Info * d,const char * text)47 void dbg_write(GSM_Debug_Info *d, const char *text)
48 {
49 if (d->log_function != NULL) {
50 d->log_function(text, d->user_data);
51 } else if (d->df != NULL) {
52 fprintf(d->df, "%s", text);
53 }
54 }
55
56 PRINTF_STYLE(2, 0)
dbg_vprintf(GSM_Debug_Info * d,const char * format,va_list argp)57 int dbg_vprintf(GSM_Debug_Info *d, const char *format, va_list argp)
58 {
59 int result=0;
60 char buffer[3000], timestamp[60];
61 char *pos, *end;
62 char save = 0;
63 GSM_DateTime date_time;
64 Debug_Level l;
65
66 l = d->dl;
67
68 if (l == DL_NONE) return 0;
69
70 result = vsnprintf(buffer, sizeof(buffer) - 1, format, argp);
71 pos = buffer;
72
73 while (*pos != 0) {
74
75 /* Find new line in string */
76 end = strstr(pos, "\n");
77
78 /* Are we at start of line? */
79 if (d->was_lf) {
80 /* Show date? */
81 if (l == DL_TEXTALLDATE || l == DL_TEXTERRORDATE || l == DL_TEXTDATE) {
82 GSM_GetCurrentDateTime(&date_time);
83 sprintf(timestamp, "%s %4d/%02d/%02d %02d:%02d:%02d: ",
84 DayOfWeek(date_time.Year, date_time.Month, date_time.Day),
85 date_time.Year, date_time.Month, date_time.Day,
86 date_time.Hour, date_time.Minute, date_time.Second);
87 dbg_write(d, timestamp);
88 }
89 d->was_lf = FALSE;
90 }
91
92 /* Remember end char */
93 if (end != NULL) {
94 save = *end;
95 *end = 0;
96 }
97
98 /* Output */
99 dbg_write(d, pos);
100
101 if (end != NULL) {
102 /* We had new line */
103 dbg_write(d, "\n");
104 d->was_lf = TRUE;
105
106 /* Restore saved char */
107 *end = save;
108
109 /* Advance to next line */
110 pos = end + strlen("\n");
111 } else {
112 /* We hit end of string */
113 break;
114 }
115 }
116
117 /* Flush buffers, this might be configurable, but it could cause drop of last log messages */
118 if (d->df != NULL) {
119 fflush(d->df);
120 }
121
122 return result;
123 }
124
GSM_SetDebugFileDescriptor(FILE * fd,gboolean closable,GSM_Debug_Info * privdi)125 GSM_Error GSM_SetDebugFileDescriptor(FILE *fd, gboolean closable, GSM_Debug_Info *privdi)
126 {
127 privdi->was_lf = TRUE;
128
129 if (privdi->df != NULL
130 && fileno(privdi->df) != fileno(stderr)
131 && fileno(privdi->df) != fileno(stdout)
132 && privdi->closable) {
133 fclose(privdi->df);
134 }
135
136 privdi->df = fd;
137 privdi->closable = closable;
138
139 return ERR_NONE;
140 }
141
GSM_SetDebugFile(const char * info,GSM_Debug_Info * privdi)142 GSM_Error GSM_SetDebugFile(const char *info, GSM_Debug_Info *privdi)
143 {
144 FILE *testfile;
145
146 if (info == NULL || strlen(info) == 0) {
147 return GSM_SetDebugFileDescriptor(NULL, FALSE, privdi);
148 }
149
150 switch (privdi->dl) {
151 case DL_BINARY:
152 testfile = fopen(info,"wb" COMMIT_FLAG);
153 break;
154 case DL_TEXTERROR:
155 case DL_TEXTERRORDATE:
156 testfile = fopen(info,"a" COMMIT_FLAG);
157 if (testfile != NULL) {
158 fseek(testfile, 0, SEEK_END);
159 if (ftell(testfile) > 5000000) {
160 fclose(testfile);
161 testfile = fopen(info,"w" COMMIT_FLAG);
162 }
163 }
164 break;
165 default:
166 testfile = fopen(info,"w" COMMIT_FLAG);
167 }
168
169 if (testfile == NULL) {
170 dbgprintf(privdi, "Can't open debug file\n");
171 return ERR_CANTOPENFILE;
172 } else {
173 return GSM_SetDebugFileDescriptor(testfile, TRUE, privdi);
174 }
175 }
176
GSM_SetDebugFunction(GSM_Log_Function info,void * data,GSM_Debug_Info * privdi)177 GSM_Error GSM_SetDebugFunction(GSM_Log_Function info, void *data, GSM_Debug_Info * privdi)
178 {
179 privdi->log_function = info;
180 privdi->user_data = data;
181 return ERR_NONE;
182 }
183
GSM_SetDebugLevel(const char * info,GSM_Debug_Info * privdi)184 gboolean GSM_SetDebugLevel(const char *info, GSM_Debug_Info *privdi)
185 {
186 if (info == NULL) {
187 privdi->dl = DL_NONE;
188 return TRUE;
189 }
190 if (strcasecmp(info, "nothing") == 0) {
191 privdi->dl = DL_NONE;
192 return TRUE;
193 }
194 if (strcasecmp(info, "text") == 0) {
195 privdi->dl = DL_TEXT;
196 return TRUE;
197 }
198 if (strcasecmp(info, "textall") == 0) {
199 privdi->dl = DL_TEXTALL;
200 return TRUE;
201 }
202 if (strcasecmp(info, "binary") == 0) {
203 privdi->dl = DL_BINARY;
204 return TRUE;
205 }
206 if (strcasecmp(info, "errors") == 0) {
207 privdi->dl = DL_TEXTERROR;
208 return TRUE;
209 }
210 if (strcasecmp(info, "textdate") == 0) {
211 privdi->dl = DL_TEXTDATE;
212 return TRUE;
213 }
214 if (strcasecmp(info, "textalldate") == 0) {
215 privdi->dl = DL_TEXTALLDATE;
216 return TRUE;
217 }
218 if (strcasecmp(info, "errorsdate") == 0) {
219 privdi->dl = DL_TEXTERRORDATE;
220 return TRUE;
221 }
222 return FALSE;
223 }
224
GSM_SetDebugCoding(const char * info,GSM_Debug_Info * privdi)225 gboolean GSM_SetDebugCoding(const char *info, GSM_Debug_Info *privdi)
226 {
227 privdi->coding = info;
228 return TRUE;
229 }
230
GSM_SetDebugGlobal(gboolean info,GSM_Debug_Info * privdi)231 gboolean GSM_SetDebugGlobal(gboolean info, GSM_Debug_Info *privdi)
232 {
233 privdi->use_global = info;
234 return TRUE;
235 }
236
237 PRINTF_STYLE(2, 3)
smfprintf(GSM_Debug_Info * d,const char * format,...)238 int smfprintf(GSM_Debug_Info *d, const char *format, ...)
239 {
240 va_list argp;
241 int result;
242 GSM_Debug_Info *tmpdi;
243
244 if (d == NULL || d->use_global) {
245 tmpdi = &GSM_global_debug;
246 } else {
247 tmpdi = d;
248 }
249
250 va_start(argp, format);
251 result = dbg_vprintf(tmpdi, format, argp);
252 va_end(argp);
253
254 return result;
255 }
256
257
258 PRINTF_STYLE(2, 3)
smprintf(GSM_StateMachine * s,const char * format,...)259 int smprintf(GSM_StateMachine *s, const char *format, ...)
260 {
261 va_list argp;
262 int result=0;
263 GSM_Debug_Info *curdi;
264
265 curdi = GSM_GetDI(s);
266
267 va_start(argp, format);
268
269 result = dbg_vprintf(curdi, format, argp);
270
271 va_end(argp);
272 return result;
273 }
274
275 PRINTF_STYLE(3, 4)
smprintf_level(GSM_StateMachine * s,GSM_DebugSeverity severity,const char * format,...)276 int smprintf_level(GSM_StateMachine * s, GSM_DebugSeverity severity, const char *format, ...)
277 {
278 va_list argp;
279 int result=0;
280 GSM_Debug_Info *curdi;
281
282 curdi = GSM_GetDI(s);
283
284 if (severity == D_TEXT) {
285 if (curdi->dl != DL_TEXT &&
286 curdi->dl != DL_TEXTALL &&
287 curdi->dl != DL_TEXTDATE &&
288 curdi->dl != DL_TEXTALLDATE) {
289 return 0;
290 }
291 } else if (severity == D_ERROR) {
292 if (curdi->dl != DL_TEXT &&
293 curdi->dl != DL_TEXTALL &&
294 curdi->dl != DL_TEXTDATE &&
295 curdi->dl != DL_TEXTALLDATE &&
296 curdi->dl != DL_TEXTERROR &&
297 curdi->dl != DL_TEXTERRORDATE) {
298 return 0;
299 }
300 }
301 va_start(argp, format);
302
303 result = dbg_vprintf(curdi, format, argp);
304
305 va_end(argp);
306 return result;
307 }
308
309 #define CHARS_PER_LINE (16)
310
311 /* Dumps a message */
DumpMessage(GSM_Debug_Info * d,const unsigned char * message,const size_t messagesize)312 void DumpMessage(GSM_Debug_Info *d, const unsigned char *message, const size_t messagesize)
313 {
314 size_t i, j = 0;
315 char buffer[(CHARS_PER_LINE * 5) + 1];
316
317 smfprintf(d, "\n");
318
319 if (messagesize == 0) return;
320
321 memset(buffer, ' ', CHARS_PER_LINE * 5);
322 buffer[CHARS_PER_LINE * 5] = 0;
323
324 for (i = 0; i < messagesize; i++) {
325 /* Write hex number */
326 snprintf(buffer + (j * 4), 3, "%02X", message[i]);
327 buffer[(j * 4) + 2] = ' '; /* wipe snprintf's \0 */
328
329 /* Write char if possible */
330 if (isprint(message[i])
331 /* 0x09 = tab */
332 && message[i] != 0x09
333 /* 0x01 = beep in windows xp */
334 && message[i] != 0x01
335 /* these are empty in windows xp */
336 && message[i] != 0x85
337 && message[i] != 0x95
338 && message[i] != 0xA6
339 && message[i] != 0xB7) {
340 buffer[(j * 4) + 2] = message[i];
341 buffer[(CHARS_PER_LINE - 1) * 4 + j + 4] = message[i];
342 } else {
343 buffer[(CHARS_PER_LINE - 1) * 4 + j + 4] = '.';
344 }
345
346 /* Write char separator */
347 if (j != CHARS_PER_LINE - 1 && i != messagesize - 1) {
348 buffer[j * 4 + 3] = '|';
349 }
350
351 /* Print out buffer */
352 if (j == CHARS_PER_LINE - 1) {
353 smfprintf(d, "%s\n", buffer);
354 memset(buffer, ' ', CHARS_PER_LINE * 5);
355 j = 0;
356 } else {
357 j++;
358 }
359 }
360
361 /* Anything remains to be printed? */
362 if (j != 0) {
363 smfprintf(d, "%s\n", buffer);
364 }
365 }
366
367 #undef CHARS_PER_LINE
368
DumpMessageText(GSM_Debug_Info * d,const unsigned char * message,const size_t messagesize)369 void DumpMessageText(GSM_Debug_Info *d, const unsigned char *message, const size_t messagesize)
370 {
371 if (d == NULL || (d->dl != DL_TEXTALL && d->dl == DL_TEXTALLDATE)) return;
372 DumpMessage(d, message, messagesize);
373
374 }
375 /* How should editor hadle tabs in this file? Add editor commands here.
376 * vim: noexpandtab sw=8 ts=8 sts=8:
377 */
378