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