]> git.xonotic.org Git - xonotic/gmqcc.git/blob - conout.c
1176e9bda81fc5a655c3508d6b3f0c80045505c9
[xonotic/gmqcc.git] / conout.c
1 /*
2  * Copyright (C) 2012, 2013
3  *     Dale Weiler
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy of
6  * this software and associated documentation files (the "Software"), to deal in
7  * the Software without restriction, including without limitation the rights to
8  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9  * of the Software, and to permit persons to whom the Software is furnished to do
10  * so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in all
13  * copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21  * SOFTWARE.
22  */
23 #define GMQCC_PLATFORM_HEADER
24 #include "gmqcc.h"
25 #include "platform.h"
26
27 #define GMQCC_IS_STDOUT(X) ((fs_file_t*)((void*)X) == (fs_file_t*)stdout)
28 #define GMQCC_IS_STDERR(X) ((fs_file_t*)((void*)X) == (fs_file_t*)stderr)
29 #define GMQCC_IS_DEFINE(X) (GMQCC_IS_STDERR(X) || GMQCC_IS_STDOUT(X))
30
31 typedef struct {
32     fs_file_t *handle_err;
33     fs_file_t *handle_out;
34     int        color_err;
35     int        color_out;
36 } con_t;
37
38 /*
39  * We use standard files as default. These can be changed at any time
40  * with con_change(F, F)
41  */
42 static con_t console;
43
44 /*
45  * Enables color on output if supported.
46  * NOTE: The support for checking colors is NULL.  On windows this will
47  * always work, on *nix it depends if the term has colors.
48  *
49  * NOTE: This prevents colored output to piped stdout/err via isatty
50  * checks.
51  */
52 static void con_enablecolor(void) {
53     if (console.handle_err == (fs_file_t*)stderr || console.handle_err == (fs_file_t*)stdout)
54         console.color_err = !!(platform_isatty(STDERR_FILENO));
55     if (console.handle_out == (fs_file_t*)stderr || console.handle_out == (fs_file_t*)stdout)
56         console.color_out = !!(platform_isatty(STDOUT_FILENO));
57 }
58
59 /*
60  * Does a write to the handle with the format string and list of
61  * arguments.  This colorizes for windows as well via translate
62  * step.
63  */
64 static int con_write(fs_file_t *handle, const char *fmt, va_list va) {
65     return vfprintf((FILE*)handle, fmt, va);
66 }
67
68 /**********************************************************************
69  * EXPOSED INTERFACE BEGINS
70  *********************************************************************/
71
72 void con_close() {
73     if (!GMQCC_IS_DEFINE(console.handle_err))
74         fs_file_close(console.handle_err);
75     if (!GMQCC_IS_DEFINE(console.handle_out))
76         fs_file_close(console.handle_out);
77 }
78
79 void con_color(int state) {
80     if (state)
81         con_enablecolor();
82     else {
83         console.color_err = 0;
84         console.color_out = 0;
85     }
86 }
87
88 void con_init() {
89     console.handle_err = (fs_file_t*)stderr;
90     console.handle_out = (fs_file_t*)stdout;
91     con_enablecolor();
92 }
93
94 void con_reset() {
95     con_close();
96     con_init ();
97 }
98
99 /*
100  * This is clever, say you want to change the console to use two
101  * files for out/err.  You pass in two strings, it will properly
102  * close the existing handles (if they're not std* handles) and
103  * open them.  Now say you want TO use stdout and stderr, this
104  * allows you to do that so long as you cast them to (char*).
105  * Say you need stdout for out, but want a file for error, you can
106  * do this too, just cast the stdout for (char*) and stick to a
107  * string for the error file.
108  */
109 int con_change(const char *out, const char *err) {
110     con_close();
111
112     if (!out) out = (const char *)((!console.handle_out) ? (fs_file_t*)stdout : console.handle_out);
113     if (!err) err = (const char *)((!console.handle_err) ? (fs_file_t*)stderr : console.handle_err);
114
115     if (GMQCC_IS_DEFINE(out)) {
116         console.handle_out = (fs_file_t*)(GMQCC_IS_STDOUT(out) ? stdout : stderr);
117         con_enablecolor();
118     } else if (!(console.handle_out = fs_file_open(out, "w"))) return 0;
119
120     if (GMQCC_IS_DEFINE(err)) {
121         console.handle_err = (fs_file_t*)(GMQCC_IS_STDOUT(err) ? stdout : stderr);
122         con_enablecolor();
123     } else if (!(console.handle_err = fs_file_open(err, "w"))) return 0;
124
125     return 1;
126 }
127
128 /*
129  * Defaultizer because stdio.h shouldn't be used anywhere except here
130  * and inside file.c To prevent mis-match of wrapper-interfaces.
131  */
132 fs_file_t *con_default_out() {
133     return (fs_file_t*)(console.handle_out = (fs_file_t*)stdout);
134 }
135 fs_file_t *con_default_err() {
136     return (fs_file_t*)(console.handle_err = (fs_file_t*)stderr);
137 }
138
139 int con_verr(const char *fmt, va_list va) {
140     return con_write(console.handle_err, fmt, va);
141 }
142 int con_vout(const char *fmt, va_list va) {
143     return con_write(console.handle_out, fmt, va);
144 }
145
146 /*
147  * Standard stdout/stderr printf functions used generally where they need
148  * to be used.
149  */
150 int con_err(const char *fmt, ...) {
151     va_list  va;
152     int      ln = 0;
153     va_start(va, fmt);
154     con_verr(fmt, va);
155     va_end  (va);
156     return   ln;
157 }
158 int con_out(const char *fmt, ...) {
159     va_list  va;
160     int      ln = 0;
161     va_start(va, fmt);
162     con_vout(fmt, va);
163     va_end  (va);
164     return   ln;
165 }
166
167 /*
168  * Utility console message writes for lexer contexts.  These will allow
169  * for reporting of file:line based on lexer context, These are used
170  * heavily in the parser/ir/ast.
171  */
172 static void con_vprintmsg_c(int level, const char *name, size_t line, size_t column, const char *msgtype, const char *msg, va_list ap, const char *condname) {
173     /* color selection table */
174     static int sel[] = {
175         CON_WHITE,
176         CON_CYAN,
177         CON_RED
178     };
179
180     int  err                             = !!(level == LVL_ERROR);
181     int  color                           = (err) ? console.color_err : console.color_out;
182     int (*print) (const char *, ...)     = (err) ? &con_err          : &con_out;
183     int (*vprint)(const char *, va_list) = (err) ? &con_verr         : &con_vout;
184
185     if (color)
186         print("\033[0;%dm%s:%d:%d: \033[0;%dm%s: \033[0m", CON_CYAN, name, (int)line, (int)column, sel[level], msgtype);
187     else
188         print("%s:%d:%d: %s: ", name, (int)line, (int)column, msgtype);
189
190     vprint(msg, ap);
191     if (condname)
192         print(" [%s]\n", condname);
193     else
194         print("\n");
195 }
196
197 void con_vprintmsg(int level, const char *name, size_t line, size_t column, const char *msgtype, const char *msg, va_list ap) {
198     con_vprintmsg_c(level, name, line, column, msgtype, msg, ap, NULL);
199 }
200
201 void con_printmsg(int level, const char *name, size_t line, size_t column, const char *msgtype, const char *msg, ...) {
202     va_list   va;
203     va_start(va, msg);
204     con_vprintmsg(level, name, line, column, msgtype, msg, va);
205     va_end  (va);
206 }
207
208 void con_cvprintmsg(lex_ctx_t ctx, int lvl, const char *msgtype, const char *msg, va_list ap) {
209     con_vprintmsg(lvl, ctx.file, ctx.line, ctx.column, msgtype, msg, ap);
210 }
211
212 void con_cprintmsg(lex_ctx_t ctx, int lvl, const char *msgtype, const char *msg, ...) {
213     va_list   va;
214     va_start(va, msg);
215     con_cvprintmsg(ctx, lvl, msgtype, msg, va);
216     va_end  (va);
217 }
218
219 #ifndef QCVM_EXECUTOR
220 /* General error interface */
221 size_t compile_errors   = 0;
222 size_t compile_warnings = 0;
223 size_t compile_Werrors  = 0;
224 static lex_ctx_t first_werror;
225
226 void compile_show_werrors()
227 {
228     con_cprintmsg(first_werror, LVL_ERROR, "first warning", "was here");
229 }
230
231 void vcompile_error(lex_ctx_t ctx, const char *msg, va_list ap)
232 {
233     ++compile_errors;
234     con_cvprintmsg(ctx, LVL_ERROR, "error", msg, ap);
235 }
236
237 void compile_error(lex_ctx_t ctx, const char *msg, ...)
238 {
239     va_list ap;
240     va_start(ap, msg);
241     vcompile_error(ctx, msg, ap);
242     va_end(ap);
243 }
244
245 bool GMQCC_WARN vcompile_warning(lex_ctx_t ctx, int warntype, const char *fmt, va_list ap)
246 {
247     const char *msgtype = "warning";
248     int         lvl     = LVL_WARNING;
249     char        warn_name[1024];
250
251     if (!OPTS_WARN(warntype))
252         return false;
253
254     warn_name[0] = '-';
255     warn_name[1] = 'W';
256     (void)util_strtononcmd(opts_warn_list[warntype].name, warn_name+2, sizeof(warn_name)-2);
257
258     ++compile_warnings;
259     if (OPTS_WERROR(warntype)) {
260         if (!compile_Werrors)
261             first_werror = ctx;
262         ++compile_Werrors;
263         msgtype = "Werror";
264         if (OPTS_FLAG(BAIL_ON_WERROR)) {
265             msgtype = "error";
266             ++compile_errors;
267         }
268         lvl = LVL_ERROR;
269     }
270
271     con_vprintmsg_c(lvl, ctx.file, ctx.line, ctx.column, msgtype, fmt, ap, warn_name);
272
273     return OPTS_WERROR(warntype) && OPTS_FLAG(BAIL_ON_WERROR);
274 }
275
276 bool GMQCC_WARN compile_warning(lex_ctx_t ctx, int warntype, const char *fmt, ...)
277 {
278     bool r;
279     va_list ap;
280     va_start(ap, fmt);
281     r = vcompile_warning(ctx, warntype, fmt, ap);
282     va_end(ap);
283     return r;
284 }
285 #endif