~ubuntu-branches/ubuntu/precise/latexila/precise

« back to all changes in this revision

Viewing changes to src/latex_output_filter.c

  • Committer: Bazaar Package Importer
  • Author(s): Tanguy Ortolo
  • Date: 2010-04-26 22:13:00 UTC
  • Revision ID: james.westby@ubuntu.com-20100426221300-6pa79a1yk5tino7y
Tags: upstream-0.2.0
ImportĀ upstreamĀ versionĀ 0.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * This file is part of LaTeXila.
 
3
 *
 
4
 * Copyright Ā© 2010 SĆ©bastien Wilmet
 
5
 *
 
6
 * LaTeXila is free software: you can redistribute it and/or modify
 
7
 * it under the terms of the GNU General Public License as published by
 
8
 * the Free Software Foundation, either version 3 of the License, or
 
9
 * (at your option) any later version.
 
10
 *
 
11
 * LaTeXila is distributed in the hope that it will be useful,
 
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 * GNU General Public License for more details.
 
15
 *
 
16
 * You should have received a copy of the GNU General Public License
 
17
 * along with LaTeXila.  If not, see <http://www.gnu.org/licenses/>.
 
18
 */
 
19
 
 
20
#include <stdlib.h>
 
21
#include <string.h>
 
22
#include <ctype.h> // for isspace()
 
23
#include <gtk/gtk.h>
 
24
 
 
25
#include "main.h"
 
26
#include "print.h"
 
27
#include "latex_output_filter.h"
 
28
#include "log.h"
 
29
 
 
30
static gboolean detect_badbox (const gchar *line);
 
31
static gboolean detect_badbox_line (const gchar *badbox,
 
32
                gboolean current_line_is_empty);
 
33
 
 
34
static gboolean detect_warning (const gchar *line);
 
35
static gboolean detect_warning_line (const gchar *warning,
 
36
                gboolean current_line_is_empty);
 
37
 
 
38
static gboolean detect_error (const gchar *line);
 
39
static gboolean detect_other (const gchar *line);
 
40
 
 
41
static void update_stack_file (const gchar *line);
 
42
static void update_stack_file_heuristic (const gchar *line);
 
43
 
 
44
static gboolean file_exists (const gchar *filename);
 
45
static gchar * get_path_if_file_exists (const gchar *filename);
 
46
static gchar * get_current_filename (void);
 
47
static void push_file_on_stack (gchar *filename, gboolean reliable);
 
48
static void pop_file_from_stack (void);
 
49
static gboolean top_file_on_stack_reliable (void);
 
50
static void print_msg (void);
 
51
 
 
52
// the current message
 
53
static message_t msg;
 
54
 
 
55
// if a message is splitted, we enter in a different status, so we fetch the end
 
56
// of the message
 
57
static enum filter_status status = START;
 
58
 
 
59
// if a message is splitted, the lines are concatenated in this buffer
 
60
static gchar line_buf[BUFFER_SIZE] = "";
 
61
static gint nb_lines = 0;
 
62
 
 
63
// the stack containing the files that TeX is processing
 
64
static GSList *stack_file = NULL;
 
65
 
 
66
// the directory where the document is compiled
 
67
static const gchar *path = NULL;
 
68
 
 
69
// some regex, initialised in latex_output_filter_init() and freed by
 
70
// latex_output_filter_free()
 
71
static GRegex *reg_badbox = NULL;
 
72
static GRegex *reg_badbox_lines = NULL;
 
73
static GRegex *reg_badbox_line = NULL;
 
74
static GRegex *reg_badbox_output = NULL;
 
75
 
 
76
static GRegex *reg_warning = NULL;
 
77
static GRegex *reg_warning_no_file = NULL;
 
78
static GRegex *reg_warning_line = NULL;
 
79
static GRegex *reg_warning_international_line = NULL;
 
80
 
 
81
static GRegex *reg_latex_error = NULL;
 
82
static GRegex *reg_pdflatex_error = NULL;
 
83
static GRegex *reg_tex_error = NULL;
 
84
static GRegex *reg_error_line = NULL;
 
85
 
 
86
static GRegex *reg_file_pop = NULL;
 
87
static GRegex *reg_other_bytes = NULL;
 
88
 
 
89
// for statistics
 
90
static gint nb_badboxes = 0;
 
91
static gint nb_warnings = 0;
 
92
static gint nb_errors = 0;
 
93
 
 
94
void
 
95
latex_output_filter (const gchar *line)
 
96
{
 
97
        if (line == NULL)
 
98
                return;
 
99
 
 
100
        switch (status)
 
101
        {
 
102
                case START:
 
103
                        if (strlen (line) == 0)
 
104
                                return;
 
105
 
 
106
                        if (! (detect_badbox (line) || detect_warning (line)
 
107
                                                || detect_error (line) || detect_other (line)))
 
108
                                update_stack_file (line);
 
109
                        break;
 
110
 
 
111
                case BADBOX:
 
112
                        detect_badbox (line);
 
113
                        break;
 
114
 
 
115
                case WARNING:
 
116
                        detect_warning (line);
 
117
                        break;
 
118
 
 
119
                case ERROR:
 
120
                case ERROR_SEARCH_LINE:
 
121
                        detect_error (line);
 
122
                        break;
 
123
 
 
124
                case FILENAME:
 
125
                case FILENAME_HEURISTIC:
 
126
                        update_stack_file (line);
 
127
 
 
128
                default:
 
129
                        status = START;
 
130
                        break;
 
131
        }
 
132
}
 
133
 
 
134
void
 
135
latex_output_filter_init (void)
 
136
{
 
137
        reg_badbox = g_regex_new ("^(Over|Under)full \\\\[hv]box", 0, 0, NULL);
 
138
        reg_badbox_lines = g_regex_new ("(.*) at lines ([0-9]+)--([0-9]+)", 0, 0, NULL);
 
139
        reg_badbox_line = g_regex_new ("(.*) at line ([0-9]+)", 0, 0, NULL);
 
140
        reg_badbox_output = g_regex_new ("(.*)has occurred while \\output is active", 0, 0, NULL);
 
141
 
 
142
        reg_warning = g_regex_new ("^(((! )?(La|pdf)TeX)|Package|Class) .*Warning.*:(.*)",
 
143
                        G_REGEX_CASELESS, 0, NULL);
 
144
        reg_warning_no_file = g_regex_new ("(No file .*)", 0, 0, NULL);
 
145
        reg_warning_line = g_regex_new ("(.*) on input line ([0-9]+)\\.$", 0, 0, NULL);
 
146
        reg_warning_international_line = g_regex_new ("(.*)([0-9]+)\\.$", 0, 0, NULL);
 
147
 
 
148
        reg_latex_error = g_regex_new ("^! LaTeX Error: (.*)$", 0, 0, NULL);
 
149
        reg_pdflatex_error = g_regex_new ("^Error: pdflatex (.*)$", 0, 0, NULL);
 
150
        reg_tex_error = g_regex_new ("^! (.*)\\.$", 0, 0, NULL);
 
151
        reg_error_line = g_regex_new ("^l\\.([0-9]+)(.*)", 0, 0, NULL);
 
152
 
 
153
        reg_file_pop = g_regex_new ("(\\) )?:<-$", 0, 0, NULL);
 
154
        reg_other_bytes = g_regex_new ("([0-9]+) bytes", 0, 0, NULL);
 
155
 
 
156
        msg.line = NO_LINE;
 
157
}
 
158
 
 
159
void
 
160
latex_output_filter_free (void)
 
161
{
 
162
        g_regex_unref (reg_badbox);
 
163
        g_regex_unref (reg_badbox_lines);
 
164
        g_regex_unref (reg_badbox_line);
 
165
        g_regex_unref (reg_badbox_output);
 
166
        g_regex_unref (reg_warning);
 
167
        g_regex_unref (reg_warning_no_file);
 
168
        g_regex_unref (reg_warning_line);
 
169
        g_regex_unref (reg_warning_international_line);
 
170
        g_regex_unref (reg_latex_error);
 
171
        g_regex_unref (reg_pdflatex_error);
 
172
        g_regex_unref (reg_tex_error);
 
173
        g_regex_unref (reg_error_line);
 
174
        g_regex_unref (reg_file_pop);
 
175
        g_regex_unref (reg_other_bytes);
 
176
        g_free ((gpointer) path);
 
177
}
 
178
 
 
179
void
 
180
latex_output_filter_set_path (const gchar *dir)
 
181
{
 
182
        path = g_strdup (dir);
 
183
}
 
184
 
 
185
void
 
186
latex_output_filter_print_stats (void)
 
187
{
 
188
        print_output_stats (nb_errors, nb_warnings, nb_badboxes);
 
189
 
 
190
        // it's finish, we reset the counters
 
191
        nb_badboxes = 0;
 
192
        nb_warnings = 0;
 
193
        nb_errors = 0;
 
194
 
 
195
        // empty the stack file
 
196
        gint nb_files_in_stack = g_slist_length (stack_file);
 
197
        if (nb_files_in_stack > 0)
 
198
                print_warning ("the file stack was not empty!");
 
199
        for (int i = 0 ; i < nb_files_in_stack ; i++)
 
200
        {
 
201
                print_info ("%s", get_current_filename ());
 
202
                pop_file_from_stack ();
 
203
        }
 
204
}
 
205
 
 
206
static gboolean
 
207
detect_badbox (const gchar *line)
 
208
{
 
209
        gboolean current_line_is_empty;
 
210
 
 
211
        switch (status)
 
212
        {
 
213
                case START:
 
214
                        if (g_regex_match (reg_badbox, line, 0, NULL))
 
215
                        {
 
216
                                msg.message_type = MESSAGE_TYPE_BADBOX;
 
217
                                nb_badboxes++;
 
218
 
 
219
                                if (detect_badbox_line (line, FALSE))
 
220
                                        print_msg ();
 
221
                                else
 
222
                                {
 
223
                                        g_strlcpy (line_buf, line, BUFFER_SIZE);
 
224
                                        nb_lines++;
 
225
                                }
 
226
 
 
227
                                return TRUE;
 
228
                        }
 
229
                        return FALSE;
 
230
                        break;
 
231
                
 
232
                case BADBOX:
 
233
                        g_strlcat (line_buf, line, BUFFER_SIZE);
 
234
                        nb_lines++;
 
235
                        current_line_is_empty = strlen (line) == 0;
 
236
                        if (detect_badbox_line (line_buf, current_line_is_empty))
 
237
                        {
 
238
                                print_msg ();
 
239
                                nb_lines = 0;
 
240
                        }
 
241
 
 
242
                        // the return value is not important here
 
243
                        return TRUE;
 
244
                        break;
 
245
 
 
246
                default:
 
247
                        break;
 
248
        }
 
249
 
 
250
        return FALSE;
 
251
}
 
252
 
 
253
static gboolean
 
254
detect_badbox_line (const gchar *badbox, gboolean current_line_is_empty)
 
255
{
 
256
        if (g_regex_match (reg_badbox_lines, badbox, 0, NULL))
 
257
        {
 
258
                status = START;
 
259
                gchar **strings = g_regex_split (reg_badbox_lines, badbox, 0);
 
260
                msg.message = g_strdup (strings[1]);
 
261
                long n1 = strtol (strings[2], NULL, 10);
 
262
                long n2 = strtol (strings[3], NULL, 10);
 
263
                msg.line = n1 < n2 ? n1 : n2;
 
264
                g_strfreev (strings);
 
265
                return TRUE;
 
266
        }
 
267
 
 
268
        else if (g_regex_match (reg_badbox_line, badbox, 0, NULL))
 
269
        {
 
270
                status = START;
 
271
                gchar **strings = g_regex_split (reg_badbox_line, badbox, 0);
 
272
                msg.message = g_strdup (strings[1]);
 
273
                msg.line = strtol (strings[2], NULL, 10);
 
274
                g_strfreev (strings);
 
275
                return TRUE;
 
276
        }
 
277
 
 
278
        else if (g_regex_match (reg_badbox_output, badbox, 0, NULL))
 
279
        {
 
280
                status = START;
 
281
                gchar **strings = g_regex_split (reg_badbox_output, badbox, 0);
 
282
                msg.message = g_strdup (strings[1]);
 
283
                msg.line = NO_LINE;
 
284
                g_strfreev (strings);
 
285
                return TRUE;
 
286
        }
 
287
 
 
288
        else if (nb_lines > 4 || current_line_is_empty)
 
289
        {
 
290
                status = START;
 
291
                msg.message = g_strdup (badbox);
 
292
                msg.line = NO_LINE;
 
293
                return TRUE;
 
294
        }
 
295
 
 
296
        status = BADBOX;
 
297
        return FALSE;
 
298
}
 
299
 
 
300
static gboolean
 
301
detect_warning (const gchar *line)
 
302
{
 
303
        gboolean current_line_is_empty;
 
304
        gchar **strings;
 
305
 
 
306
        switch (status)
 
307
        {
 
308
                case START:
 
309
                        if (g_regex_match (reg_warning, line, 0, NULL))
 
310
                        {
 
311
                                nb_warnings++;
 
312
                                msg.message_type = MESSAGE_TYPE_WARNING;
 
313
 
 
314
                                strings = g_regex_split (reg_warning, line, 0);
 
315
 
 
316
                                if (detect_warning_line (strings[5], FALSE))
 
317
                                        print_msg ();
 
318
                                else
 
319
                                {
 
320
                                        g_strlcpy (line_buf, strings[5], BUFFER_SIZE);
 
321
                                        nb_lines++;
 
322
                                }
 
323
 
 
324
                                g_strfreev (strings);
 
325
                                return TRUE;
 
326
                        }
 
327
 
 
328
                        else if (g_regex_match (reg_warning_no_file, line, 0, NULL))
 
329
                        {
 
330
                                nb_warnings++;
 
331
                                msg.message_type = MESSAGE_TYPE_WARNING;
 
332
                                strings = g_regex_split (reg_warning_no_file, line, 0);
 
333
                                msg.message = g_strdup (strings[1]);
 
334
                                g_strfreev (strings);
 
335
                                msg.line = NO_LINE;
 
336
                                print_msg ();
 
337
                                return TRUE;
 
338
                        }
 
339
 
 
340
                        return FALSE;
 
341
                        break;
 
342
 
 
343
                case WARNING:
 
344
                        g_strlcat (line_buf, line, BUFFER_SIZE);
 
345
                        nb_lines++;
 
346
                        current_line_is_empty = strlen (line) == 0;
 
347
                        if (detect_warning_line (line_buf, current_line_is_empty))
 
348
                        {
 
349
                                print_msg ();
 
350
                                nb_lines = 0;
 
351
                        }
 
352
 
 
353
                        // the return value is not important here
 
354
                        return TRUE;
 
355
                        break;
 
356
 
 
357
                default:
 
358
                        break;
 
359
        }
 
360
 
 
361
        return FALSE;
 
362
}
 
363
 
 
364
static gboolean
 
365
detect_warning_line (const gchar *warning, gboolean current_line_is_empty)
 
366
{
 
367
        if (g_regex_match (reg_warning_line, warning, 0, NULL))
 
368
        {
 
369
                status = START;
 
370
                gchar **strings = g_regex_split (reg_warning_line, warning, 0);
 
371
                msg.message = g_strdup (strings[1]);
 
372
                msg.line = strtol (strings[2], NULL, 10);
 
373
                g_strfreev (strings);
 
374
                return TRUE;
 
375
        }
 
376
        
 
377
        else if (g_regex_match (reg_warning_international_line, warning, 0, NULL))
 
378
        {
 
379
                status = START;
 
380
                gchar **strings = g_regex_split (reg_warning_international_line, warning, 0);
 
381
                msg.message = g_strdup (strings[1]);
 
382
                msg.line = strtol (strings[2], NULL, 10);
 
383
                g_strfreev (strings);
 
384
                return TRUE;
 
385
        }
 
386
 
 
387
        else if (warning[strlen (warning) - 1] == '.')
 
388
        {
 
389
                status = START;
 
390
                msg.message = g_strdup (warning);
 
391
                msg.line = NO_LINE;
 
392
                return TRUE;
 
393
        }
 
394
 
 
395
        else if (nb_lines > 5 || current_line_is_empty)
 
396
        {
 
397
                status = START;
 
398
                msg.message = g_strdup (warning);
 
399
                msg.line = NO_LINE;
 
400
                return TRUE;
 
401
        }
 
402
 
 
403
        status = WARNING;
 
404
        return FALSE;
 
405
}
 
406
 
 
407
static gboolean
 
408
detect_error (const gchar *line)
 
409
{
 
410
        gboolean found = FALSE;
 
411
        gchar *tmp;
 
412
        gchar **strings;
 
413
 
 
414
        switch (status)
 
415
        {
 
416
                case START:
 
417
                        if (g_regex_match (reg_latex_error, line, 0, NULL))
 
418
                        {
 
419
                                found = TRUE;
 
420
                                strings = g_regex_split (reg_latex_error, line, 0);
 
421
                                tmp = g_strdup (strings[1]);
 
422
                                g_strfreev (strings);
 
423
                        }
 
424
 
 
425
                        else if (g_regex_match (reg_pdflatex_error, line, 0, NULL))
 
426
                        {
 
427
                                found = TRUE;
 
428
                                strings = g_regex_split (reg_pdflatex_error, line, 0);
 
429
                                tmp = g_strdup (strings[1]);
 
430
                                g_strfreev (strings);
 
431
                        }
 
432
 
 
433
                        else if (g_regex_match (reg_tex_error, line, 0, NULL))
 
434
                        {
 
435
                                found = TRUE;
 
436
                                strings = g_regex_split (reg_tex_error, line, 0);
 
437
                                tmp = g_strdup (strings[1]);
 
438
                                g_strfreev (strings);
 
439
                        }
 
440
 
 
441
                        if (found)
 
442
                        {
 
443
                                nb_errors++;
 
444
                                nb_lines++;
 
445
                                msg.message_type = MESSAGE_TYPE_ERROR;
 
446
 
 
447
                                // the message is complete
 
448
                                if (line[strlen (line) - 1] == '.')
 
449
                                {
 
450
                                        msg.message = tmp;
 
451
                                        status = ERROR_SEARCH_LINE;
 
452
                                }
 
453
                                // the message is splitted
 
454
                                else
 
455
                                {
 
456
                                        g_strlcpy (line_buf, tmp, BUFFER_SIZE);
 
457
                                        g_free (tmp);
 
458
                                        status = ERROR;
 
459
                                }
 
460
                                return TRUE;
 
461
                        }
 
462
 
 
463
                        return FALSE;
 
464
                        break;
 
465
 
 
466
                case ERROR:
 
467
                        g_strlcat (line_buf, line, BUFFER_SIZE);
 
468
                        nb_lines++;
 
469
 
 
470
                        if (line[strlen (line) - 1] == '.')
 
471
                        {
 
472
                                msg.message = g_strdup (line_buf);
 
473
                                status = ERROR_SEARCH_LINE;
 
474
                        }
 
475
                        else if (nb_lines > 4)
 
476
                        {
 
477
                                msg.message = g_strdup (line_buf);
 
478
                                msg.line = NO_LINE;
 
479
                                print_msg ();
 
480
                                nb_lines = 0;
 
481
                                status = START;
 
482
                        }
 
483
                        
 
484
                        // the return value is not important here
 
485
                        return TRUE;
 
486
                        break;
 
487
 
 
488
                case ERROR_SEARCH_LINE:
 
489
                        nb_lines++;
 
490
                        if (g_regex_match (reg_error_line, line, 0, NULL))
 
491
                        {
 
492
                                strings = g_regex_split (reg_error_line, line, 0);
 
493
                                msg.line = strtol (strings[1], NULL, 10);
 
494
                                g_strfreev (strings);
 
495
                                print_msg ();
 
496
                                nb_lines = 0;
 
497
                                status = START;
 
498
                                return TRUE;
 
499
                        }
 
500
                        else if (nb_lines > 11)
 
501
                        {
 
502
                                msg.line = NO_LINE;
 
503
                                print_msg ();
 
504
                                nb_lines = 0;
 
505
                                status = START;
 
506
                                return TRUE;
 
507
                        }
 
508
                        break;
 
509
 
 
510
                default:
 
511
                        break;
 
512
        }
 
513
 
 
514
        return FALSE;
 
515
}
 
516
 
 
517
static gboolean
 
518
detect_other (const gchar *line)
 
519
{
 
520
        if (strstr (line, "Output written on"))
 
521
        {
 
522
                /* try to show the file size in a human readable format */
 
523
                if (g_regex_match (reg_other_bytes, line, 0, NULL))
 
524
                {
 
525
                        gchar **strings = g_regex_split (reg_other_bytes, line, 0);
 
526
                        gint nb_bytes = strtol (strings[1], NULL, 10);
 
527
                        g_strfreev (strings);
 
528
 
 
529
                        gboolean replace = FALSE;
 
530
                        gchar *human_size = NULL;
 
531
 
 
532
                        // do nothing
 
533
                        if (nb_bytes < 1024)
 
534
                                msg.message = g_strdup (line);
 
535
 
 
536
                        // size in KB (less than 1 MB)
 
537
                        else if (nb_bytes < 1024 * 1024)
 
538
                        {
 
539
                                gint nb_kb = nb_bytes / 1024;
 
540
                                human_size = g_strdup_printf ("%d KB", nb_kb);
 
541
                                replace = TRUE;
 
542
                        }
 
543
 
 
544
                        // size in MB (with one decimal)
 
545
                        else
 
546
                        {
 
547
                                gdouble nb_mb = (gdouble) nb_bytes / (1024.0 * 1024.0);
 
548
                                human_size = g_strdup_printf ("%.1f MB", nb_mb);
 
549
                                replace = TRUE;
 
550
                        }
 
551
 
 
552
                        if (replace)
 
553
                        {
 
554
                                GError *error = NULL;
 
555
                                gchar *new_line = g_regex_replace_literal (reg_other_bytes,
 
556
                                                line, -1, 0, human_size, 0, &error);
 
557
                                g_free (human_size);
 
558
 
 
559
                                // shit!
 
560
                                if (error != NULL)
 
561
                                {
 
562
                                        g_error_free (error);
 
563
                                        g_free (new_line);
 
564
                                        msg.message = g_strdup (line);
 
565
                                }
 
566
                                else
 
567
                                        msg.message = new_line;
 
568
                        }
 
569
                }
 
570
                
 
571
                // no "bytes" found
 
572
                else
 
573
                        msg.message = g_strdup (line);
 
574
 
 
575
                msg.line = NO_LINE;
 
576
                msg.message_type = MESSAGE_TYPE_OTHER;
 
577
                print_msg ();
 
578
                return TRUE;
 
579
        }
 
580
 
 
581
        return FALSE;
 
582
}
 
583
 
 
584
// There are basically two ways to detect the current file TeX is processing:
 
585
//      1) Use \Input (srctex or srcltx package) and \include exclusively. This will
 
586
//      cause (La)TeX to print the line ":<+ filename"  in the log file when opening a file, 
 
587
//      ":<-" when closing a file. Filenames pushed on the stack in this mode are marked
 
588
//      as reliable.
 
589
//
 
590
//      2) Since people will probably also use the \input command, we also have to be
 
591
//      to detect the old-fashioned way. TeX prints '(filename' when opening a file and a ')'
 
592
//      when closing one. It is impossible to detect this with 100% certainty (TeX prints many messages
 
593
//      and even text (a context) from the TeX source file, there could be unbalanced parentheses), 
 
594
//      so we use a heuristic algorithm. In heuristic mode a ')' will only be considered as a signal that 
 
595
//      TeX is closing a file if the top of the stack is not marked as "reliable". 
 
596
//      
 
597
// The method used here is almost the same as in Kile.
 
598
static void
 
599
update_stack_file (const gchar *line)
 
600
{
 
601
        static gchar filename[BUFFER_SIZE] = "";
 
602
        gchar *tmp;
 
603
 
 
604
        switch (status)
 
605
        {
 
606
                case START:
 
607
                case FILENAME_HEURISTIC:
 
608
                        // TeX is opening a file
 
609
                        if (g_str_has_prefix (line, ":<+ "))
 
610
                        {
 
611
                                g_strlcpy (filename, line + 4, BUFFER_SIZE);
 
612
                                g_strstrip (filename);
 
613
                                status = FILENAME;
 
614
                        }
 
615
                        // TeX closed a file
 
616
                        else if (g_regex_match (reg_file_pop, line, 0, NULL)
 
617
                                        || g_str_has_prefix (line, ":<-"))
 
618
                                pop_file_from_stack ();
 
619
                        // fallback to the heuristic detection of filenames
 
620
                        else
 
621
                                update_stack_file_heuristic (line);
 
622
                        break;
 
623
 
 
624
                case FILENAME:
 
625
                        // The partial filename was followed by '(', this means that TeX is
 
626
                        // signalling it is opening the file. We are sure the filename is
 
627
                        // complete now. Don't call update_stack_file_heuristic()
 
628
                        // since we don't want the filename on the stack twice.
 
629
                        if (line[0] == '(' || g_str_has_prefix (line, "\\openout"))
 
630
                        {
 
631
                                push_file_on_stack (filename, TRUE);
 
632
                                status = START;
 
633
                        }
 
634
 
 
635
                        // The partial filename was followed by a TeX error, meaning the
 
636
                        // file doesn't exist. Don't push it on the stack, instead try to
 
637
                        // detect the error.
 
638
                        else if (line[0] == '!')
 
639
                        {
 
640
                                status = START;
 
641
                                detect_error (line);
 
642
                        }
 
643
                        else if (g_str_has_prefix (line, "No file"))
 
644
                        {
 
645
                                status = START;
 
646
                                detect_warning (line);
 
647
                        }
 
648
 
 
649
                        // the filename is not complete
 
650
                        else
 
651
                        {
 
652
                                tmp = g_strdup (line);
 
653
                                g_strstrip (tmp);
 
654
                                g_strlcat (filename, tmp, BUFFER_SIZE);
 
655
                                g_free (tmp);
 
656
                        }
 
657
                        break;
 
658
 
 
659
                default:
 
660
                        break;
 
661
        }
 
662
}
 
663
 
 
664
static void
 
665
update_stack_file_heuristic (const gchar *line)
 
666
{
 
667
        static gchar filename[BUFFER_SIZE] = "";
 
668
        gboolean expect_filename = status == FILENAME_HEURISTIC;
 
669
        gint index = 0;
 
670
        int length = strlen (line);
 
671
 
 
672
        // handle special case
 
673
        if (expect_filename && length > 0 && line[0] == ')')
 
674
        {
 
675
                push_file_on_stack (filename, FALSE);
 
676
                expect_filename = FALSE;
 
677
                status = START;
 
678
        }
 
679
 
 
680
        // scan for parentheses and grab filenames
 
681
        for (int i = 0 ; i < length ; i++)
 
682
        {
 
683
                /*
 
684
                We're expecting a filename. If a filename really ends at this position,
 
685
                one of the following must be true:
 
686
                        1) Next character is a space, indicating the end of a filename
 
687
                          (yes, there can't spaces in the path, this is a TeX limitation).
 
688
                        2) We're at the end of the line, the filename is probably
 
689
                           continued on the next line.
 
690
                        3) The file was closed already, signalled by the ')'.
 
691
                */
 
692
 
 
693
                gboolean is_last_char = i + 1 == length;
 
694
                gboolean next_is_terminator =
 
695
                        is_last_char ? FALSE : (isspace (line[i+1]) || line[i+1] == ')');
 
696
 
 
697
                if (expect_filename && (is_last_char || next_is_terminator))
 
698
                {
 
699
                        g_strlcat (filename, line + index, strlen (filename) + i - index + 2);
 
700
 
 
701
                        if (strlen (filename) == 0)
 
702
                                continue;
 
703
 
 
704
                        // by default, an output line is 79 characters max
 
705
                        if ((is_last_char && i < 78) || next_is_terminator || file_exists (filename))
 
706
                        {
 
707
                                push_file_on_stack (filename, FALSE);
 
708
                                expect_filename = FALSE;
 
709
                                status = START;
 
710
                        }
 
711
 
 
712
                        // Guess the filename is continued on the next line, only if the
 
713
                        // current filename does not exist
 
714
                        else if (is_last_char)
 
715
                        {
 
716
                                if (file_exists (filename))
 
717
                                {
 
718
                                        push_file_on_stack (filename, FALSE);
 
719
                                        expect_filename = FALSE;
 
720
                                        status = START;
 
721
                                }
 
722
                                else
 
723
                                        status = FILENAME_HEURISTIC;
 
724
                        }
 
725
 
 
726
                        // filename not detected
 
727
                        else
 
728
                        {
 
729
                                status = START;
 
730
                                filename[0] = '\0';
 
731
                                expect_filename = FALSE;
 
732
                        }
 
733
                }
 
734
 
 
735
                // TeX is opening a file
 
736
                else if (line[i] == '(')
 
737
                {
 
738
                        status = START;
 
739
                        filename[0] = '\0';
 
740
                        // we need to extract the filename
 
741
                        expect_filename = TRUE;
 
742
                        // this is where the filename is supposed to start
 
743
                        index = i + 1;
 
744
                }
 
745
 
 
746
                // TeX is closing a file
 
747
                // If this filename was pushed on the stack by the reliable ":<+-"
 
748
                // method, don't pop, a ":<-" will follow. This helps in preventing
 
749
                // unbalanced ')' from popping filenames from the stack too soon.
 
750
                else if (line[i] == ')' && g_slist_length (stack_file) > 0
 
751
                                && ! top_file_on_stack_reliable ())
 
752
                        pop_file_from_stack ();
 
753
        }
 
754
}
 
755
 
 
756
static gboolean
 
757
file_exists (const gchar *filename)
 
758
{
 
759
        gchar *full_path = get_path_if_file_exists (filename);
 
760
        if (full_path != NULL)
 
761
        {
 
762
                g_free (full_path);
 
763
                return TRUE;
 
764
        }
 
765
        return FALSE;
 
766
}
 
767
 
 
768
// return NULL if the filename does not exist
 
769
// return the path of the filename if it exists (must be freed)
 
770
static gchar *
 
771
get_path_if_file_exists (const gchar *filename)
 
772
{
 
773
        if (g_path_is_absolute (filename))
 
774
        {
 
775
                if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
 
776
                        return g_strdup (filename);
 
777
                else
 
778
                        return NULL;
 
779
        }
 
780
 
 
781
        gchar *full_path;
 
782
        if (g_str_has_prefix (filename, "./"))
 
783
                full_path = g_build_filename (path, filename + 2, NULL);
 
784
        else
 
785
                full_path = g_build_filename (path, filename, NULL);
 
786
 
 
787
        if (g_file_test (full_path, G_FILE_TEST_IS_REGULAR))
 
788
                return full_path;
 
789
 
 
790
        // try to add various extensions on the filename to see if the file exists
 
791
        gchar *extensions[] = {".tex", ".ltx", ".latex", ".dtx", ".ins"};
 
792
        guint nb_ext = G_N_ELEMENTS (extensions);
 
793
        for (guint i = 0 ; i < nb_ext ; i++)
 
794
        {
 
795
                gchar *tmp = g_strdup_printf ("%s%s", full_path, extensions[i]);
 
796
                if (g_file_test (tmp, G_FILE_TEST_IS_REGULAR))
 
797
                {
 
798
                        g_free (full_path);
 
799
                        return tmp;
 
800
                }
 
801
                g_free (tmp);
 
802
        }
 
803
 
 
804
        g_free (full_path);
 
805
        return NULL;
 
806
}
 
807
 
 
808
static gchar *
 
809
get_current_filename (void)
 
810
{
 
811
        if (stack_file != NULL)
 
812
        {
 
813
                file_in_stack_t *file = stack_file->data;
 
814
                return file->filename;
 
815
        }
 
816
 
 
817
        return NULL;
 
818
}
 
819
 
 
820
static void
 
821
push_file_on_stack (gchar *filename, gboolean reliable)
 
822
{
 
823
        //print_info ("***\tpush\t\"%s\" (%s)", filename, reliable ? "reliable" : "not reliable");
 
824
        file_in_stack_t *file = g_malloc (sizeof (file_in_stack_t));
 
825
 
 
826
        gchar *path = get_path_if_file_exists (filename);
 
827
        if (path != NULL)
 
828
                file->filename = path;
 
829
        else
 
830
                file->filename = g_strdup (filename);
 
831
 
 
832
        file->reliable = reliable;
 
833
        stack_file = g_slist_prepend (stack_file, file);
 
834
}
 
835
 
 
836
static void
 
837
pop_file_from_stack (void)
 
838
{
 
839
        if (stack_file == NULL)
 
840
                return;
 
841
 
 
842
        file_in_stack_t *file = stack_file->data;
 
843
        //print_info ("***\tpop\t\"%s\" (%s)", file->filename, file->reliable ? "reliable" : "not reliable");
 
844
        stack_file = g_slist_remove (stack_file, file);
 
845
        g_free (file->filename);
 
846
        g_free (file);
 
847
}
 
848
 
 
849
static gboolean
 
850
top_file_on_stack_reliable (void)
 
851
{
 
852
        // stack_file must contain at least one file
 
853
        g_assert (stack_file != NULL);
 
854
 
 
855
        file_in_stack_t *file = stack_file->data;
 
856
        return file->reliable;
 
857
}
 
858
 
 
859
static void
 
860
print_msg (void)
 
861
{
 
862
        gchar *filename = get_current_filename ();
 
863
        print_output_message (filename, msg.line, msg.message, msg.message_type);
 
864
        
 
865
        msg.line = NO_LINE;
 
866
        msg.message_type = MESSAGE_TYPE_OTHER;
 
867
        g_free (msg.message);
 
868
}