- added patch to Improving progress/error output for telnet & GDB monitor
[fw/openocd] / src / helper / log.c
1 /***************************************************************************
2  *   Copyright (C) 2005 by Dominic Rath                                    *
3  *   Dominic.Rath@gmx.de                                                   *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
19  ***************************************************************************/
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include "log.h"
25 #include "configuration.h"
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <stdarg.h>
31
32 int debug_level = -1;
33
34 static FILE* log_output;
35
36
37 static void *privData;
38 static logCallback callback;
39
40 void log_setCallback(logCallback c, void *p)
41 {
42         callback=c;
43         privData=p;
44 }
45
46 static char *log_strings[4] = 
47 {
48         "Error:  ",
49         "Warning:",
50         "Info:   ",
51         "Debug:  ",
52 };
53
54 void log_printf(enum log_levels level, const char *file, int line, const char *function, const char *format, ...)
55 {
56         va_list args;
57         char buffer[512];
58
59         if (level > debug_level)
60                 return;
61
62         va_start(args, format);
63         vsnprintf(buffer, 512, format, args);
64
65         fprintf(log_output, "%s %s:%d %s(): %s\n", log_strings[level], file, line, function, buffer);
66         fflush(log_output);
67         
68         va_end(args);
69
70         if (callback)
71 {
72         va_start(args, format);
73
74                 callback(privData, file, line, function, format, args);
75
76         va_end(args);
77 }
78
79 }
80
81 /* change the current debug level on the fly
82  * 0: only ERRORS
83  * 1: + WARNINGS
84  * 2: + INFORMATIONAL MSGS
85  * 3: + DEBUG MSGS
86  */
87 int handle_debug_level_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
88 {
89         if (argc == 0)
90                 command_print(cmd_ctx, "debug_level: %i", debug_level);
91
92         if (argc > 0)
93                 debug_level = strtoul(args[0], NULL, 0);
94
95         if (debug_level < 0)
96                 debug_level = 0;
97
98         if (debug_level > 3)
99                 debug_level = 3;
100
101         return ERROR_OK;
102 }
103
104 int handle_log_output_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
105 {
106         if (argc == 1)
107         {
108                 FILE* file = fopen(args[0], "w");
109                 
110                 if (file)
111                 {
112                         log_output = file;
113                 }
114         }
115
116         return ERROR_OK;
117 }
118
119 int log_register_commands(struct command_context_s *cmd_ctx)
120 {
121         register_command(cmd_ctx, NULL, "log_output", handle_log_output_command,
122                 COMMAND_ANY, "redirect logging to <file> (default: stderr)");
123         register_command(cmd_ctx, NULL, "debug_level", handle_debug_level_command,
124                 COMMAND_ANY, "adjust debug level <0-3>");
125
126         return ERROR_OK;
127 }
128
129 int log_init(struct command_context_s *cmd_ctx)
130 {
131         /* set defaults for daemon configuration, if not set by cmdline or cfgfile */
132         if (debug_level == -1)
133                 debug_level = LOG_INFO;
134         
135         if (log_output == NULL)
136         {
137                 log_output = stderr;
138         }
139         
140         return ERROR_OK;
141 }
142         
143 int set_log_output(struct command_context_s *cmd_ctx, FILE *output)
144 {
145         log_output=output;
146         return ERROR_OK;
147 }
148
149 /* return allocated string w/printf() result */
150 char *allocPrintf(const char *fmt, va_list ap)
151 {
152         char *string=NULL;
153         int size=0; // start by 0 to exercise all the code paths. Need minimum 2 bytes to fit 1 char and 0 terminator.
154         int first=1;
155         for (;;)
156         {
157                 if ((string==NULL)||(!first))
158                 {
159                         size=size*2+2;
160                         char *t=string;
161                         string=realloc(string, size);
162                         if (string==NULL)
163                         {
164                                 if (t!=NULL)
165                                         free(t);
166                                 return NULL;
167                         }
168                 }
169         
170             int ret;
171             ret = vsnprintf(string, size, fmt, ap);
172             // NB! The result of the vsnprintf() might be an *EMPTY* string!
173             if ((ret>=0)&&((ret+1)<size))
174             {
175                 return string;
176             }
177             // there was just enough or not enough space, allocate more.
178             first=0;
179         }
180 }