00d88b4b38d7cdc68493e15017be88a73233a64a
[retroray] / src / logger.c
1 /*
2 RetroRay - integrated standalone vintage modeller/renderer
3 Copyright (C) 2023  John Tsiombikas <nuclear@mutantstargoat.com>
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 3 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, see <https://www.gnu.org/licenses/>.
17 */
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <stdarg.h>
21 #include "logger.h"
22
23 #if defined(__MSDOS__) || defined(MSDOS)
24 static int setup_serial(int sdev);
25 #else
26 #define USE_STD
27 #endif
28
29 enum { LOG_FILE, LOG_STREAM, LOG_CON, LOG_CB };
30 enum { LOG_DBG, LOG_INFO, LOG_WARN, LOG_ERR };
31
32 struct log_callback {
33         void (*func)(const char*, void*);
34         void *cls;
35 };
36
37 struct log_output {
38         int type, level;
39         union {
40                 FILE *fp;
41                 int con;
42                 struct log_callback cb;
43         } out;
44 };
45
46 #define MAX_OUTPUTS     8
47 static struct log_output outputs[MAX_OUTPUTS];
48 static int num_outputs;
49
50 void init_logger(void)
51 {
52         num_outputs = 0;
53 }
54
55 void cleanup_logger(void)
56 {
57         int i;
58
59         for(i=0; i<num_outputs; i++) {
60                 if(outputs[i].type == LOG_FILE) {
61                         fclose(outputs[i].out.fp);
62                 }
63         }
64         num_outputs = 0;
65 }
66
67 int add_log_file(const char *fname)
68 {
69         FILE *fp;
70         int idx;
71
72         if(num_outputs >= MAX_OUTPUTS) {
73                 return -1;
74         }
75         if(!(fp = fopen(fname, "w"))) {
76                 return -1;
77         }
78         idx = num_outputs++;
79
80         outputs[idx].type = LOG_FILE;
81         outputs[idx].out.fp = fp;
82         return 0;
83 }
84
85 int add_log_stream(FILE *fp)
86 {
87         int idx;
88
89         if(num_outputs >= MAX_OUTPUTS) {
90                 return -1;
91         }
92         idx = num_outputs++;
93
94         outputs[idx].type = LOG_STREAM;
95         outputs[idx].out.fp = fp;
96         return 0;
97 }
98
99 int add_log_console(const char *devname)
100 {
101 #if defined(MSDOS) || defined(__MSDOS__)
102         int i, comport;
103         if(sscanf(devname, "COM%d", &comport) != 1 || comport < 1 || comport > 2) {
104                 return -1;
105         }
106         comport--;
107
108         if(num_outputs >= MAX_OUTPUTS) {
109                 return -1;
110         }
111         for(i=0; i<num_outputs; i++) {
112                 if(outputs[i].type == LOG_CON && outputs[i].out.con == comport) {
113                         return -1;
114                 }
115         }
116         if(setup_serial(comport) == -1) {
117                 return -1;
118         }
119
120         i = num_outputs++;
121         outputs[i].type = LOG_CON;
122         outputs[i].out.con = comport;
123         return 0;
124
125 #elif defined(unix) || defined(__unix__)
126         /* TODO? */
127         return -1;
128 #endif
129 }
130
131 int add_log_callback(void (*cbfunc)(const char*, void*), void *cls)
132 {
133         int idx;
134
135         if(num_outputs >= MAX_OUTPUTS) {
136                 return -1;
137         }
138         idx = num_outputs++;
139
140         outputs[idx].type = LOG_CB;
141         outputs[idx].out.cb.func = cbfunc;
142         outputs[idx].out.cb.cls = cls;
143         return 0;
144 }
145
146 #if defined(__WATCOMC__)
147 #ifndef vsnprintf
148 #define vsnprintf _vsnprintf
149 #endif
150 #endif
151
152 static void logmsg(int type, const char *fmt, va_list ap)
153 {
154         static char *buf;
155         static int bufsz;
156         int i, ret, newsz;
157         char *tmp;
158
159         while((ret = vsnprintf(buf, bufsz, fmt, ap)) > bufsz || ret < 0) {
160                 if(ret > bufsz) {
161                         newsz = ret + 1;
162                 } else {
163                         newsz = bufsz ? bufsz * 2 : 256;
164                 }
165                 if(!(tmp = realloc(buf, newsz))) {
166                         if(buf) {
167                                 buf[bufsz - 1] = 0;
168                         }
169                         break;
170                 }
171                 buf = tmp;
172                 bufsz = newsz;
173         }
174
175         if(!buf) return;
176
177         for(i=0; i<num_outputs; i++) {
178                 switch(outputs[i].type) {
179                 case LOG_FILE:
180                 case LOG_STREAM:
181                         fputs(buf, outputs[i].out.fp);
182                         break;
183
184 #if defined(MSDOS) || defined(__MSDOS__)
185                 case LOG_CON:
186                         ser_puts(buf);
187                         break;
188 #endif
189                 case LOG_CB:
190                         outputs[i].out.cb.func(buf, outputs[i].out.cb.cls);
191                         break;
192
193                 default:
194                         break;
195                 }
196         }
197 }
198
199 void errormsg(const char *fmt, ...)
200 {
201         va_list ap;
202         va_start(ap, fmt);
203         logmsg(LOG_ERR, fmt, ap);
204         va_end(ap);
205 }
206
207 void warnmsg(const char *fmt, ...)
208 {
209         va_list ap;
210         va_start(ap, fmt);
211         logmsg(LOG_WARN, fmt, ap);
212         va_end(ap);
213 }
214
215 void infomsg(const char *fmt, ...)
216 {
217         va_list ap;
218         va_start(ap, fmt);
219         logmsg(LOG_INFO, fmt, ap);
220         va_end(ap);
221 }
222
223 void dbgmsg(const char *fmt, ...)
224 {
225         va_list ap;
226         va_start(ap, fmt);
227         logmsg(LOG_DBG, fmt, ap);
228         va_end(ap);
229 }
230
231 void verrormsg(const char *fmt, va_list ap)
232 {
233         logmsg(LOG_ERR, fmt, ap);
234 }
235
236 void vwarnmsg(const char *fmt, va_list ap)
237 {
238         logmsg(LOG_ERR, fmt, ap);
239 }
240
241 void vinfomsg(const char *fmt, va_list ap)
242 {
243         logmsg(LOG_ERR, fmt, ap);
244 }
245
246 void vdbgmsg(const char *fmt, va_list ap)
247 {
248         logmsg(LOG_ERR, fmt, ap);
249 }
250
251
252 #if defined(MSDOS) || defined(__MSDOS__)
253 #define UART1_BASE      0x3f8
254 #define UART2_BASE      0x2f8
255
256 #define UART_DATA       0
257 #define UART_DIVLO      0
258 #define UART_DIVHI      1
259 #define UART_FIFO       2
260 #define UART_LCTL       3
261 #define UART_MCTL       4
262 #define UART_LSTAT      5
263
264 #define DIV_9600                        (115200 / 9600)
265 #define DIV_38400                       (115200 / 38400)
266 #define LCTL_8N1                        0x03
267 #define LCTL_DLAB                       0x80
268 #define FIFO_ENABLE_CLEAR       0x07
269 #define MCTL_DTR_RTS_OUT2       0x0b
270 #define LST_TRIG_EMPTY          0x20
271
272 static unsigned int iobase;
273
274 static int setup_serial(int sdev)
275 {
276         if(sdev < 0 || sdev > 1) {
277                 return -1;
278         }
279         iobase = sdev == 0 ? UART1_BASE : UART2_BASE;
280
281         /* set clock divisor */
282         outp(iobase | UART_LCTL, LCTL_DLAB);
283         outp(iobase | UART_DIVLO, DIV_9600 & 0xff);
284         outp(iobase | UART_DIVHI, DIV_9600 >> 8);
285         /* set format 8n1 */
286         outp(iobase | UART_LCTL, LCTL_8N1);
287         /* assert RTS and DTR */
288         outp(iobase | UART_MCTL, MCTL_DTR_RTS_OUT2);
289         return 0;
290 }
291
292 void ser_putchar(int c)
293 {
294         if(c == '\n') {
295                 ser_putchar('\r');
296         }
297
298         while((inp(iobase | UART_LSTAT) & LST_TRIG_EMPTY) == 0);
299         outp(iobase | UART_DATA, c);
300 }
301
302 void ser_puts(const char *s)
303 {
304         while(*s) {
305                 ser_putchar(*s++);
306         }
307 }
308
309 void ser_printf(const char *fmt, ...)
310 {
311         va_list ap;
312         char buf[512];
313
314         va_start(ap, fmt);
315         vsprintf(buf, fmt, ap);
316         va_end(ap);
317
318         ser_puts(buf);
319 }
320 #endif