Gammu internals  1.38.0
debug.c
Go to the documentation of this file.
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 
23  0,
24  NULL,
25  FALSE,
26  "",
27  FALSE,
28  FALSE,
29  NULL,
30  NULL
31  };
32 
34  0,
35  NULL,
36  FALSE,
37  "",
38  FALSE,
39  FALSE,
40  NULL,
41  NULL
42  };
43 
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)
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 #ifndef WIN32
119  if (d->df != NULL) {
120  fflush(d->df);
121  }
122 #endif
123 
124  return result;
125 }
126 
128 {
129  privdi->was_lf = TRUE;
130 
131  if (privdi->df != NULL
132  && fileno(privdi->df) != fileno(stderr)
133  && fileno(privdi->df) != fileno(stdout)
134  && privdi->closable) {
135  fclose(privdi->df);
136  }
137 
138  privdi->df = fd;
139  privdi->closable = closable;
140 
141  return ERR_NONE;
142 }
143 
144 GSM_Error GSM_SetDebugFile(const char *info, GSM_Debug_Info *privdi)
145 {
146  FILE *testfile;
147 
148  if (info == NULL || strlen(info) == 0) {
149  return GSM_SetDebugFileDescriptor(NULL, FALSE, privdi);
150  }
151 
152  switch (privdi->dl) {
153  case DL_BINARY:
154  testfile = fopen(info,"wb" COMMIT_FLAG);
155  break;
156  case DL_TEXTERROR:
157  case DL_TEXTERRORDATE:
158  testfile = fopen(info,"a" COMMIT_FLAG);
159  if (testfile != NULL) {
160  fseek(testfile, 0, SEEK_END);
161  if (ftell(testfile) > 5000000) {
162  fclose(testfile);
163  testfile = fopen(info,"w" COMMIT_FLAG);
164  }
165  }
166  break;
167  default:
168  testfile = fopen(info,"w" COMMIT_FLAG);
169  }
170 
171  if (testfile == NULL) {
172  dbgprintf(privdi, "Can't open debug file\n");
173  return ERR_CANTOPENFILE;
174  } else {
175  return GSM_SetDebugFileDescriptor(testfile, TRUE, privdi);
176  }
177 }
178 
180 {
181  privdi->log_function = info;
182  privdi->user_data = data;
183  return ERR_NONE;
184 }
185 
186 gboolean GSM_SetDebugLevel(const char *info, GSM_Debug_Info *privdi)
187 {
188  if (info == NULL) {
189  privdi->dl = DL_NONE;
190  return TRUE;
191  }
192  if (strcasecmp(info, "nothing") == 0) {
193  privdi->dl = DL_NONE;
194  return TRUE;
195  }
196  if (strcasecmp(info, "text") == 0) {
197  privdi->dl = DL_TEXT;
198  return TRUE;
199  }
200  if (strcasecmp(info, "textall") == 0) {
201  privdi->dl = DL_TEXTALL;
202  return TRUE;
203  }
204  if (strcasecmp(info, "binary") == 0) {
205  privdi->dl = DL_BINARY;
206  return TRUE;
207  }
208  if (strcasecmp(info, "errors") == 0) {
209  privdi->dl = DL_TEXTERROR;
210  return TRUE;
211  }
212  if (strcasecmp(info, "textdate") == 0) {
213  privdi->dl = DL_TEXTDATE;
214  return TRUE;
215  }
216  if (strcasecmp(info, "textalldate") == 0) {
217  privdi->dl = DL_TEXTALLDATE;
218  return TRUE;
219  }
220  if (strcasecmp(info, "errorsdate") == 0) {
221  privdi->dl = DL_TEXTERRORDATE;
222  return TRUE;
223  }
224  return FALSE;
225 }
226 
227 gboolean GSM_SetDebugCoding(const char *info, GSM_Debug_Info *privdi)
228 {
229  privdi->coding = info;
230  return TRUE;
231 }
232 
234 {
235  privdi->use_global = info;
236  return TRUE;
237 }
238 
239 PRINTF_STYLE(2, 3)
240 int smfprintf(GSM_Debug_Info *d, const char *format, ...)
241 {
242  va_list argp;
243  int result;
244  GSM_Debug_Info *tmpdi;
245 
246  if (d == NULL || d->use_global) {
247  tmpdi = &GSM_global_debug;
248  } else {
249  tmpdi = d;
250  }
251 
252  va_start(argp, format);
253  result = dbg_vprintf(tmpdi, format, argp);
254  va_end(argp);
255 
256  return result;
257 }
258 
259 
260 PRINTF_STYLE(2, 3)
261 int smprintf(GSM_StateMachine *s, const char *format, ...)
262 {
263  va_list argp;
264  int result=0;
265  GSM_Debug_Info *curdi;
266 
267  curdi = GSM_GetDI(s);
268 
269  va_start(argp, format);
270 
271  result = dbg_vprintf(curdi, format, argp);
272 
273  va_end(argp);
274  return result;
275 }
276 
277 PRINTF_STYLE(3, 4)
278 int smprintf_level(GSM_StateMachine * s, GSM_DebugSeverity severity, const char *format, ...)
279 {
280  va_list argp;
281  int result=0;
282  GSM_Debug_Info *curdi;
283 
284  curdi = GSM_GetDI(s);
285 
286  if (severity == D_TEXT) {
287  if (curdi->dl != DL_TEXT &&
288  curdi->dl != DL_TEXTALL &&
289  curdi->dl != DL_TEXTDATE &&
290  curdi->dl != DL_TEXTALLDATE) {
291  return 0;
292  }
293  } else if (severity == D_ERROR) {
294  if (curdi->dl != DL_TEXT &&
295  curdi->dl != DL_TEXTALL &&
296  curdi->dl != DL_TEXTDATE &&
297  curdi->dl != DL_TEXTALLDATE &&
298  curdi->dl != DL_TEXTERROR &&
299  curdi->dl != DL_TEXTERRORDATE) {
300  return 0;
301  }
302  }
303  va_start(argp, format);
304 
305  result = dbg_vprintf(curdi, format, argp);
306 
307  va_end(argp);
308  return result;
309 }
310 
311 #define CHARS_PER_LINE (16)
312 
313 /* Dumps a message */
314 void DumpMessage(GSM_Debug_Info *d, const unsigned char *message, const size_t messagesize)
315 {
316  size_t i, j = 0;
317  char buffer[(CHARS_PER_LINE * 5) + 1];
318 
319  smfprintf(d, "\n");
320 
321  if (messagesize == 0) return;
322 
323  memset(buffer, ' ', CHARS_PER_LINE * 5);
324  buffer[CHARS_PER_LINE * 5] = 0;
325 
326  for (i = 0; i < messagesize; i++) {
327  /* Write hex number */
328  snprintf(buffer + (j * 4), 3, "%02X", message[i]);
329  buffer[(j * 4) + 2] = ' '; /* wipe snprintf's \0 */
330 
331  /* Write char if possible */
332  if (isprint(message[i])
333  /* 0x09 = tab */
334  && message[i] != 0x09
335  /* 0x01 = beep in windows xp */
336  && message[i] != 0x01
337  /* these are empty in windows xp */
338  && message[i] != 0x85
339  && message[i] != 0x95
340  && message[i] != 0xA6
341  && message[i] != 0xB7) {
342  buffer[(j * 4) + 2] = message[i];
343  buffer[(CHARS_PER_LINE - 1) * 4 + j + 4] = message[i];
344  } else {
345  buffer[(CHARS_PER_LINE - 1) * 4 + j + 4] = '.';
346  }
347 
348  /* Write char separator */
349  if (j != CHARS_PER_LINE - 1 && i != messagesize - 1) {
350  buffer[j * 4 + 3] = '|';
351  }
352 
353  /* Print out buffer */
354  if (j == CHARS_PER_LINE - 1) {
355  smfprintf(d, "%s\n", buffer);
356  memset(buffer, ' ', CHARS_PER_LINE * 5);
357  j = 0;
358  } else {
359  j++;
360  }
361  }
362 
363  /* Anything remains to be printed? */
364  if (j != 0) {
365  smfprintf(d, "%s\n", buffer);
366  }
367 }
368 
369 #undef CHARS_PER_LINE
370 
371 void DumpMessageText(GSM_Debug_Info *d, const unsigned char *message, const size_t messagesize)
372 {
373  if (d == NULL || (d->dl != DL_TEXTALL && d->dl == DL_TEXTALLDATE)) return;
374  DumpMessage(d, message, messagesize);
375 
376 }
377 /* How should editor hadle tabs in this file? Add editor commands here.
378  * vim: noexpandtab sw=8 ts=8 sts=8:
379  */
GSM_Error GSM_SetDebugFunction(GSM_Log_Function info, void *data, GSM_Debug_Info *privdi)
Definition: debug.c:179
void DumpMessage(GSM_Debug_Info *d, const unsigned char *message, const size_t messagesize)
Definition: debug.c:314
GSM_Debug_Info * GSM_GetDI(GSM_StateMachine *s)
Definition: gsmstate.c:70
GSM_Log_Function log_function
Definition: debug.h:44
gboolean GSM_SetDebugCoding(const char *info, GSM_Debug_Info *privdi)
Definition: debug.c:227
Definition: debug.h:26
void DumpMessageText(GSM_Debug_Info *d, const unsigned char *message, const size_t messagesize)
Definition: debug.c:371
GSM_Error
Definition: gammu-error.h:23
FILE * df
Definition: debug.h:36
#define CHARS_PER_LINE
Definition: debug.c:311
char * DayOfWeek(unsigned int year, unsigned int month, unsigned int day)
Definition: misc.c:129
Debug_Level
Definition: debug.h:23
GSM_Error GSM_SetDebugFileDescriptor(FILE *fd, gboolean closable, GSM_Debug_Info *privdi)
Definition: debug.c:127
gboolean was_lf
Definition: debug.h:39
#define PRINTF_STYLE(f, a)
Definition: gammu-misc.h:92
void(* GSM_Log_Function)(const char *text, void *data)
int smprintf_level(GSM_StateMachine *s, GSM_DebugSeverity severity, const char *format,...)
Definition: debug.c:278
const char * coding
Definition: debug.h:38
gboolean GSM_SetDebugGlobal(gboolean info, GSM_Debug_Info *privdi)
Definition: debug.c:233
int gboolean
Definition: gammu-types.h:23
GSM_Debug_Info GSM_global_debug
Definition: debug.c:33
#define FALSE
Definition: gammu-types.h:25
void * user_data
Definition: debug.h:48
GSM_Error GSM_SetDebugFile(const char *info, GSM_Debug_Info *privdi)
Definition: debug.c:144
#define COMMIT_FLAG
Definition: debug.c:15
int dbg_vprintf(GSM_Debug_Info *d, const char *format, va_list argp)
Definition: debug.c:57
gboolean use_global
Definition: debug.h:37
Definition: debug.h:24
gboolean closable
Definition: debug.h:40
#define dbgprintf
Definition: debug.h:72
void GSM_GetCurrentDateTime(GSM_DateTime *Date)
Definition: misc.c:184
gboolean GSM_SetDebugLevel(const char *info, GSM_Debug_Info *privdi)
Definition: debug.c:186
Definition: debug.h:87
int smfprintf(GSM_Debug_Info *d, const char *format,...)
Definition: debug.c:240
#define TRUE
Definition: gammu-types.h:28
void dbg_write(GSM_Debug_Info *d, const char *text)
Definition: debug.c:47
GSM_DebugSeverity
Definition: debug.h:79
GSM_Debug_Info GSM_none_debug
Definition: debug.c:22
Debug_Level dl
Definition: debug.h:35
Definition: debug.h:91
int smprintf(GSM_StateMachine *s, const char *format,...)
Definition: debug.c:261