1 #include "log.h"
2 #include "list.h"
3 #include "common.h"
4 #include "console.h"
5
6 #include <assert.h>
7 #include <stdlib.h>
8 #include <stdio.h>
9 #include <stdarg.h>
10
11 #define LOG_ENTRY_BUF_SZ 512
12
13 static struct list log_q;
14 static struct list log_stk;
15 static int log_group;
16 static int log_disabled;
17
18 struct log_entry {
19 struct list q_hook;
20 struct list stk_hook;
21 char buf[LOG_ENTRY_BUF_SZ];
22 char *ptr;
23 int room;
24 };
25
log_entry_new()26 struct log_entry *log_entry_new()
27 {
28 struct log_entry *entry = (struct log_entry*)calloc(1, sizeof(*entry));
29 assert(entry);
30 entry->ptr = entry->buf;
31 entry->room = sizeof(entry->buf);
32 return entry;
33 }
34
log_entry_del(struct log_entry * entry)35 static void log_entry_del(struct log_entry *entry)
36 {
37 free(entry);
38 }
39
log_entry_print(struct log_entry * entry,const char * fmt,va_list args)40 static void log_entry_print(struct log_entry *entry, const char *fmt, va_list args)
41 {
42 /* If fmt is NULL experimentation shows that vsnprintf returns -1,
43 * which is also the indication that we attempted to overflow the
44 * buffer. To prevent the ambiguity (we want to handle 'nothing
45 * written' differently than 'overflow') check for NULL here. */
46 if (!fmt) {
47 return;
48 }
49
50 int wrote = vsnprintf(entry->ptr, entry->room, fmt, args);
51 if (wrote >= 0) {
52 entry->room -= wrote;
53 entry->ptr += wrote;
54 } else {
55 entry->room = 0;
56 }
57 }
58
log_flush()59 void log_flush()
60 {
61 struct log_entry *entry;
62
63 if (list_empty(&log_q))
64 return;
65
66 entry = outcast(log_q.next, struct log_entry, q_hook);
67
68 if (entry->ptr != entry->buf) {
69 consolePrint(entry->buf);
70 consoleRepaint();
71 memset(entry->buf, 0, sizeof(entry->buf));
72 entry->ptr = entry->buf;
73 entry->room = sizeof(entry->buf);
74 }
75 }
76
log_print_queued_msgs()77 static inline void log_print_queued_msgs()
78 {
79 struct list *elem;
80
81 elem = log_q.next;
82
83 while (elem != &log_q) {
84
85 struct log_entry *entry;
86
87 entry = outcast(elem, struct log_entry, q_hook);
88 elem = elem->next;
89 consolePrint("%s\n", entry->buf);
90 list_remove(&entry->q_hook);
91 log_entry_del(entry);
92 }
93 }
94
log_flush_queued_msgs(struct log_entry * entry)95 static inline void log_flush_queued_msgs(struct log_entry *entry)
96 {
97 struct list *elem;
98
99 elem = &entry->q_hook;
100
101 while (elem != &log_q) {
102 entry = outcast(elem, struct log_entry, q_hook);
103 elem = elem->next;
104 list_remove(&entry->q_hook);
105 log_entry_del(entry);
106 }
107 }
108
log_push(struct log_entry * entry)109 static inline void log_push(struct log_entry *entry)
110 {
111 list_add_tail(&log_q, &entry->q_hook);
112 list_add(&log_stk, &entry->stk_hook);
113 log_begin_group();
114 }
115
log_pop()116 static inline void log_pop()
117 {
118 assert(! list_empty(&log_stk));
119
120 /* Pop the topmost entry from the msg stack */
121 list_remove(log_stk.next);
122
123 /* If the msg stack is now empty then log all msgs on the queue */
124 if (list_empty(&log_stk))
125 log_print_queued_msgs();
126 log_end_group();
127 }
128
log_abort(void)129 void log_abort(void)
130 {
131 struct log_entry *entry;
132
133 if (log_disabled)
134 return;
135
136 assert(! list_empty(&log_stk));
137 entry = outcast(log_stk.next, struct log_entry, stk_hook);
138
139 list_remove(log_stk.next);
140 log_flush_queued_msgs(entry);
141 log_end_group();
142 }
143
log_init(void)144 void log_init(void)
145 {
146 list_init(&log_q);
147 list_init(&log_stk);
148 log_group = 0;
149 log_disabled = 0;
150 }
151
log_begin(const char * fmt,...)152 void log_begin(const char *fmt, ...)
153 {
154 va_list args;
155
156 if (log_disabled)
157 return;
158
159 struct log_entry *entry = log_entry_new();
160 log_push(entry);
161 va_start(args, fmt);
162 log_entry_print(entry, fmt, args);
163 va_end(args);
164 }
165
log_continue(const char * fmt,...)166 void log_continue(const char *fmt, ...)
167 {
168 va_list args;
169 struct log_entry *entry;
170
171 if (log_disabled)
172 return;
173
174 assert(! list_empty(&log_stk));
175 entry = outcast(log_stk.next, struct log_entry, stk_hook);
176 va_start(args, fmt);
177 log_entry_print(entry, fmt, args);
178 va_end(args);
179 }
180
log_end(const char * fmt,...)181 void log_end(const char *fmt, ...)
182 {
183 va_list args;
184 struct log_entry *entry;
185
186 if (log_disabled)
187 return;
188
189 assert(! list_empty(&log_stk));
190 entry = outcast(log_stk.next, struct log_entry, stk_hook);
191 va_start(args, fmt);
192 log_entry_print(entry, fmt, args);
193 va_end(args);
194 log_pop();
195 }
196
log_msg(const char * fmt,...)197 void log_msg(const char *fmt, ...)
198 {
199 va_list args;
200 struct log_entry *entry = log_entry_new();
201
202 if (log_disabled)
203 return;
204
205 log_push(entry);
206 va_start(args, fmt);
207 log_entry_print(entry, fmt, args);
208 va_end(args);
209 log_pop();
210 }
211
log_banner(const char * fmt,...)212 void log_banner(const char *fmt, ...)
213 {
214 struct log_entry *entry;
215 va_list args;
216
217 if (log_disabled) {
218 return;
219 }
220
221 log_begin_group();
222 log_msg("^c+y*********************************^c-");
223 log_begin("^c+y*^c- ");
224
225
226 entry = outcast(log_stk.next, struct log_entry, stk_hook);
227 va_start(args, fmt);
228 log_entry_print(entry, fmt, args);
229 va_end(args);
230
231 log_end("");
232 log_msg("^c+y*********************************^c-");
233 log_end_group();
234 }
235
log_begin_group()236 void log_begin_group()
237 {
238 if (log_disabled)
239 return;
240
241 log_group++;
242 }
243
log_end_group()244 void log_end_group()
245 {
246 if (log_disabled)
247 return;
248
249 log_group--;
250 if (log_group == 0)
251 consolePrint("\n");
252 }
253
log_disable()254 void log_disable()
255 {
256 log_disabled++;
257 }
258
log_enable()259 void log_enable()
260 {
261 log_disabled--;
262 }
263