Merge branch 'blender-v2.90-release'
[blender.git] / source / blender / blenkernel / intern / report.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup bke
22  */
23
24 #include <errno.h>
25 #include <stdarg.h>
26 #include <stdio.h>
27 #include <string.h>
28
29 #include "MEM_guardedalloc.h"
30
31 #include "BLI_blenlib.h"
32 #include "BLI_dynstr.h"
33 #include "BLI_utildefines.h"
34
35 #include "BLT_translation.h"
36
37 #include "BKE_global.h" /* G.background only */
38 #include "BKE_report.h"
39
40 const char *BKE_report_type_str(ReportType type)
41 {
42   switch (type) {
43     case RPT_DEBUG:
44       return TIP_("Debug");
45     case RPT_INFO:
46       return TIP_("Info");
47     case RPT_OPERATOR:
48       return TIP_("Operator");
49     case RPT_PROPERTY:
50       return TIP_("Property");
51     case RPT_WARNING:
52       return TIP_("Warning");
53     case RPT_ERROR:
54       return TIP_("Error");
55     case RPT_ERROR_INVALID_INPUT:
56       return TIP_("Invalid Input Error");
57     case RPT_ERROR_INVALID_CONTEXT:
58       return TIP_("Invalid Context Error");
59     case RPT_ERROR_OUT_OF_MEMORY:
60       return TIP_("Out Of Memory Error");
61     default:
62       return TIP_("Undefined Type");
63   }
64 }
65
66 void BKE_reports_init(ReportList *reports, int flag)
67 {
68   if (!reports) {
69     return;
70   }
71
72   memset(reports, 0, sizeof(ReportList));
73
74   reports->storelevel = RPT_INFO;
75   reports->printlevel = RPT_ERROR;
76   reports->flag = flag;
77 }
78
79 /**
80  * Only frees the list \a reports.
81  * To make displayed reports disappear, either remove window-manager reports
82  * (wmWindowManager.reports, or CTX_wm_reports()), or use #WM_report_banners_cancel().
83  */
84 void BKE_reports_clear(ReportList *reports)
85 {
86   Report *report, *report_next;
87
88   if (!reports) {
89     return;
90   }
91
92   report = reports->list.first;
93
94   while (report) {
95     report_next = report->next;
96     MEM_freeN((void *)report->message);
97     MEM_freeN(report);
98     report = report_next;
99   }
100
101   BLI_listbase_clear(&reports->list);
102 }
103
104 void BKE_report(ReportList *reports, ReportType type, const char *_message)
105 {
106   Report *report;
107   int len;
108   const char *message = TIP_(_message);
109
110   /* in background mode always print otherwise there are cases the errors wont be displayed,
111    * but still add to the report list since this is used for python exception handling */
112   if (G.background || !reports || ((reports->flag & RPT_PRINT) && (type >= reports->printlevel))) {
113     printf("%s: %s\n", BKE_report_type_str(type), message);
114     fflush(stdout); /* this ensures the message is printed before a crash */
115   }
116
117   if (reports && (reports->flag & RPT_STORE) && (type >= reports->storelevel)) {
118     char *message_alloc;
119     report = MEM_callocN(sizeof(Report), "Report");
120     report->type = type;
121     report->typestr = BKE_report_type_str(type);
122
123     len = strlen(message);
124     message_alloc = MEM_mallocN(sizeof(char) * (len + 1), "ReportMessage");
125     memcpy(message_alloc, message, sizeof(char) * (len + 1));
126     report->message = message_alloc;
127     report->len = len;
128     BLI_addtail(&reports->list, report);
129   }
130 }
131
132 void BKE_reportf(ReportList *reports, ReportType type, const char *_format, ...)
133 {
134   DynStr *ds;
135   Report *report;
136   va_list args;
137   const char *format = TIP_(_format);
138
139   if (G.background || !reports || ((reports->flag & RPT_PRINT) && (type >= reports->printlevel))) {
140     printf("%s: ", BKE_report_type_str(type));
141     va_start(args, _format);
142     vprintf(format, args);
143     va_end(args);
144     fprintf(stdout, "\n"); /* otherwise each report needs to include a \n */
145     fflush(stdout);        /* this ensures the message is printed before a crash */
146   }
147
148   if (reports && (reports->flag & RPT_STORE) && (type >= reports->storelevel)) {
149     report = MEM_callocN(sizeof(Report), "Report");
150
151     ds = BLI_dynstr_new();
152     va_start(args, _format);
153     BLI_dynstr_vappendf(ds, format, args);
154     va_end(args);
155
156     report->message = BLI_dynstr_get_cstring(ds);
157     report->len = BLI_dynstr_get_len(ds);
158     BLI_dynstr_free(ds);
159
160     report->type = type;
161     report->typestr = BKE_report_type_str(type);
162
163     BLI_addtail(&reports->list, report);
164   }
165 }
166
167 void BKE_reports_prepend(ReportList *reports, const char *_prepend)
168 {
169   Report *report;
170   DynStr *ds;
171   const char *prepend = TIP_(_prepend);
172
173   if (!reports) {
174     return;
175   }
176
177   for (report = reports->list.first; report; report = report->next) {
178     ds = BLI_dynstr_new();
179
180     BLI_dynstr_append(ds, prepend);
181     BLI_dynstr_append(ds, report->message);
182     MEM_freeN((void *)report->message);
183
184     report->message = BLI_dynstr_get_cstring(ds);
185     report->len = BLI_dynstr_get_len(ds);
186
187     BLI_dynstr_free(ds);
188   }
189 }
190
191 void BKE_reports_prependf(ReportList *reports, const char *_prepend, ...)
192 {
193   Report *report;
194   DynStr *ds;
195   va_list args;
196   const char *prepend = TIP_(_prepend);
197
198   if (!reports) {
199     return;
200   }
201
202   for (report = reports->list.first; report; report = report->next) {
203     ds = BLI_dynstr_new();
204     va_start(args, _prepend);
205     BLI_dynstr_vappendf(ds, prepend, args);
206     va_end(args);
207
208     BLI_dynstr_append(ds, report->message);
209     MEM_freeN((void *)report->message);
210
211     report->message = BLI_dynstr_get_cstring(ds);
212     report->len = BLI_dynstr_get_len(ds);
213
214     BLI_dynstr_free(ds);
215   }
216 }
217
218 ReportType BKE_report_print_level(ReportList *reports)
219 {
220   if (!reports) {
221     return RPT_ERROR;
222   }
223
224   return reports->printlevel;
225 }
226
227 void BKE_report_print_level_set(ReportList *reports, ReportType level)
228 {
229   if (!reports) {
230     return;
231   }
232
233   reports->printlevel = level;
234 }
235
236 ReportType BKE_report_store_level(ReportList *reports)
237 {
238   if (!reports) {
239     return RPT_ERROR;
240   }
241
242   return reports->storelevel;
243 }
244
245 void BKE_report_store_level_set(ReportList *reports, ReportType level)
246 {
247   if (!reports) {
248     return;
249   }
250
251   reports->storelevel = level;
252 }
253
254 char *BKE_reports_string(ReportList *reports, ReportType level)
255 {
256   Report *report;
257   DynStr *ds;
258   char *cstring;
259
260   if (!reports || !reports->list.first) {
261     return NULL;
262   }
263
264   ds = BLI_dynstr_new();
265   for (report = reports->list.first; report; report = report->next) {
266     if (report->type >= level) {
267       BLI_dynstr_appendf(ds, "%s: %s\n", report->typestr, report->message);
268     }
269   }
270
271   if (BLI_dynstr_get_len(ds)) {
272     cstring = BLI_dynstr_get_cstring(ds);
273   }
274   else {
275     cstring = NULL;
276   }
277
278   BLI_dynstr_free(ds);
279   return cstring;
280 }
281
282 void BKE_reports_print(ReportList *reports, ReportType level)
283 {
284   char *cstring = BKE_reports_string(reports, level);
285
286   if (cstring == NULL) {
287     return;
288   }
289
290   puts(cstring);
291   fflush(stdout);
292   MEM_freeN(cstring);
293 }
294
295 Report *BKE_reports_last_displayable(ReportList *reports)
296 {
297   Report *report;
298
299   for (report = reports->list.last; report; report = report->prev) {
300     if (ELEM(report->type, RPT_ERROR, RPT_WARNING, RPT_INFO)) {
301       return report;
302     }
303   }
304
305   return NULL;
306 }
307
308 bool BKE_reports_contain(ReportList *reports, ReportType level)
309 {
310   Report *report;
311   if (reports != NULL) {
312     for (report = reports->list.first; report; report = report->next) {
313       if (report->type >= level) {
314         return true;
315       }
316     }
317   }
318   return false;
319 }
320
321 bool BKE_report_write_file_fp(FILE *fp, ReportList *reports, const char *header)
322 {
323   Report *report;
324
325   if (header) {
326     fputs(header, fp);
327   }
328
329   for (report = reports->list.first; report; report = report->next) {
330     fprintf((FILE *)fp, "%s  # %s\n", report->message, report->typestr);
331   }
332
333   return true;
334 }
335
336 bool BKE_report_write_file(const char *filepath, ReportList *reports, const char *header)
337 {
338   FILE *fp;
339
340   errno = 0;
341   fp = BLI_fopen(filepath, "wb");
342   if (fp == NULL) {
343     fprintf(stderr,
344             "Unable to save '%s': %s\n",
345             filepath,
346             errno ? strerror(errno) : "Unknown error opening file");
347     return false;
348   }
349
350   BKE_report_write_file_fp(fp, reports, header);
351
352   fclose(fp);
353
354   return true;
355 }