1 /**
2 * @file latex.c
3 * @brief
4 *
5 * Copyright (C) 2009 Gummi Developers
6 * All Rights reserved.
7 *
8 * Permission is hereby granted, free of charge, to any person
9 * obtaining a copy of this software and associated documentation
10 * files (the "Software"), to deal in the Software without
11 * restriction, including without limitation the rights to use,
12 * copy, modify, merge, publish, distribute, sublicense, and/or sell
13 * copies of the Software, and to permit persons to whom the
14 * Software is furnished to do so, subject to the following
15 * conditions:
16 *
17 * The above copyright notice and this permission notice shall be
18 * included in all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 * OTHER DEALINGS IN THE SOFTWARE.
28 */
29
30 #include "latex.h"
31
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <unistd.h>
36
37 #include <gtk/gtk.h>
38 #include <glib.h>
39 #include <glib/gstdio.h>
40
41 #include "configfile.h"
42 #include "constants.h"
43 #include "editor.h"
44 #include "environment.h"
45 #include "external.h"
46 #include "gui/gui-preview.h"
47 #include "utils.h"
48
49 #include "compile/rubber.h"
50 #include "compile/latexmk.h"
51 #include "compile/texlive.h"
52
53 extern Gummi* gummi;
54
latex_init(void)55 GuLatex* latex_init (void) {
56 GuLatex* l = g_new0 (GuLatex, 1);
57 l->compilelog = NULL;
58 l->modified_since_compile = FALSE;
59
60 l->tex_version = texlive_init ();
61 rubber_init ();
62 latexmk_init ();
63 return l;
64 }
65
66
67
latex_method_active(gchar * method)68 gboolean latex_method_active (gchar* method) {
69 return config_value_as_str_equals ("Compile", "steps", method);
70 }
71
latex_update_workfile(GuEditor * ec)72 gchar* latex_update_workfile (GuEditor* ec) {
73 gchar *text;
74
75 text = editor_grab_buffer (ec);
76
77 // bit of a dirty hack, but only write the buffer content when
78 // there is not a recovery in progress, otherwise the workfile
79 // will be overwritten with empty text
80 if (!STR_EQU (text, "")) {
81 utils_set_file_contents (ec->workfile, text, -1);
82 }
83 return text;
84 }
85
latex_set_compile_cmd(GuEditor * ec)86 gchar* latex_set_compile_cmd (GuEditor* ec) {
87
88 const gchar* method = config_get_string ("Compile", "steps");
89 gchar* combined = NULL;
90 gchar* texcmd = NULL;
91
92 if (rubber_active()) {
93 texcmd = rubber_get_command (method, ec->workfile);
94 }
95 else if (latexmk_active()) {
96 texcmd = latexmk_get_command (method, ec->workfile, ec->basename);
97 }
98 else {
99 texcmd = texlive_get_command (method, ec->workfile, ec->basename);
100 }
101
102 combined = g_strdup_printf("%s %s", C_TEXSEC, texcmd);
103 g_free(texcmd);
104
105 return combined;
106 }
107
latex_analyse_log(gchar * log,gchar * filename,gchar * basename)108 gchar* latex_analyse_log (gchar* log, gchar* filename, gchar* basename) {
109 /* Rubber does not post the pdftex compilation output to tty, so we will
110 * have to open the log file and retrieve it I guess */
111 if (rubber_active()) {
112 gchar* logpath = NULL;
113 if (filename == NULL) {
114 logpath = g_strconcat (basename, ".log", NULL);
115 }
116 else {
117 logpath = g_strconcat (C_TMPDIR, C_DIRSEP,
118 g_path_get_basename(basename), ".log", NULL);
119 }
120 g_file_get_contents (logpath, &log, NULL, NULL);
121 }
122 return log;
123 }
124
125
126
127
latex_analyse_errors(GuLatex * lc)128 void latex_analyse_errors (GuLatex* lc) {
129 gchar* result = NULL;
130 GError* err = NULL;
131 GRegex* match_str = NULL;
132 GMatchInfo* match_info;
133
134 if (! (match_str = g_regex_new (":(\\d+):", G_REGEX_DOTALL, 0, &err))) {
135 slog (L_ERROR, "g_regex_new (): %s\n", err->message);
136 g_error_free (err);
137 return;
138 }
139
140 if (lc->compilelog == NULL) printf("null\n");
141
142 if (g_regex_match (match_str, lc->compilelog, 0, &match_info)) {
143 gint count = 0;
144 while (g_match_info_matches (match_info)) {
145 if (count + 1 == BUFSIZ) break;
146 result = g_match_info_fetch (match_info, 1);
147 lc->errorlines[count++] = atoi (result);
148 g_free (result);
149 g_match_info_next (match_info, NULL);
150 }
151 }
152 if (!lc->errorlines[0])
153 lc->errorlines[0] = -1;
154 g_match_info_free (match_info);
155 g_regex_unref (match_str);
156 }
157
latex_update_pdffile(GuLatex * lc,GuEditor * ec)158 gboolean latex_update_pdffile (GuLatex* lc, GuEditor* ec) {
159 static glong cerrors = 0;
160 gchar* basename = ec->basename;
161 gchar* filename = ec->filename;
162
163 if (!lc->modified_since_compile) return cerrors == 0;
164
165 const gchar* typesetter = config_get_string ("Compile", "typesetter");
166 if (!external_exists (typesetter)) {
167 // Set to default first detected typesetter
168 config_set_string ("Compile", "typesetter", "pdflatex");
169 }
170
171 /* create compile command */
172 gchar* curdir = g_path_get_dirname (ec->workfile);
173 gchar *command = latex_set_compile_cmd (ec);
174
175 g_free (lc->compilelog);
176 memset (lc->errorlines, 0, BUFSIZ * sizeof(gint));
177
178 /* run pdf compilation */
179 Tuple2 cresult = utils_popen_r (command, curdir);
180 cerrors = (glong)cresult.first;
181 gchar* coutput = (gchar*)cresult.second;
182
183 lc->compilelog = latex_analyse_log (coutput, filename, basename);
184 lc->modified_since_compile = FALSE;
185
186 /* find error line */
187 if (cerrors && (g_utf8_strlen (lc->compilelog, -1) != 0)) {
188 latex_analyse_errors (lc);
189 }
190
191 g_free (command);
192
193 return cerrors == 0;
194 }
195
latex_update_auxfile(GuEditor * ec)196 void latex_update_auxfile (GuEditor* ec) {
197 gchar* dirname = g_path_get_dirname (ec->workfile);
198 gchar* command = g_strdup_printf ("%s %s "
199 "--draftmode "
200 "-interaction=nonstopmode "
201 "--output-directory=\"%s\" \"%s\"",
202 C_TEXSEC,
203 config_get_string ("Compile", "typesetter"),
204 C_TMPDIR,
205 ec->workfile);
206 Tuple2 res = utils_popen_r (command, dirname);
207 g_free (dirname);
208 g_free (res.second);
209 g_free (command);
210 }
211
latex_remove_auxfile(GuEditor * ec)212 int latex_remove_auxfile (GuEditor* ec) {
213 gchar* auxfile = NULL;
214 int res = -1;
215 // TODO: merge this into function with several other instances.
216 // for instance the lines in analyse_log
217 if (ec->filename == NULL) {
218 auxfile = g_strconcat (ec->basename, ".aux", NULL);
219 }
220 else {
221 auxfile = g_strconcat (C_TMPDIR, C_DIRSEP,
222 g_path_get_basename(ec->basename), ".aux", NULL);
223 }
224
225 // TODO: extend for other build files
226 if (g_file_test (auxfile, G_FILE_TEST_EXISTS)) {
227 res = g_remove (auxfile);
228 }
229 g_free (auxfile);
230 return res;
231 }
232
latex_precompile_check(gchar * editortext)233 gboolean latex_precompile_check (gchar* editortext) {
234 /* both documentclass and documentstyle appear to be valid.
235 * http://pangea.stanford.edu/computing/unix/formatting/parts.php
236 * TOD: Improve and add document scan tags and make compatible with
237 * upcoming master/slave document system */
238
239 // TODO: see issue #269
240
241 gboolean class = utils_subinstr("\\documentclass", editortext, FALSE);
242 gboolean style = utils_subinstr("\\documentstyle", editortext, FALSE);
243 gboolean input = utils_subinstr("\\input", editortext, FALSE);
244
245 return (class || style || input);
246 }
247
latex_export_pdffile(GuLatex * lc,GuEditor * ec,const gchar * path,gboolean prompt_overrite)248 void latex_export_pdffile (GuLatex* lc, GuEditor* ec, const gchar* path,
249 gboolean prompt_overrite) {
250 gchar* savepath = NULL;
251 GError* err = NULL;
252 gint ret = 0;
253
254 if (!STR_EQU (path + strlen (path) -4, ".pdf"))
255 savepath = g_strdup_printf ("%s.pdf", path);
256 else
257 savepath = g_strdup (path);
258
259 if (prompt_overrite && utils_path_exists (savepath)) {
260 ret = utils_yes_no_dialog (_("The file already exists. Overwrite?"));
261 if (GTK_RESPONSE_YES != ret) {
262 g_free (savepath);
263 return;
264 }
265 }
266 if (!utils_copy_file (ec->pdffile, savepath, &err)) {
267 slog (L_G_ERROR, _("Unable to export PDF file.\n\n%s"),
268 err->message);
269 g_error_free (err);
270 }
271
272 g_free (savepath);
273 }
274
latex_run_makeindex(GuEditor * ec)275 gboolean latex_run_makeindex (GuEditor* ec) {
276 int retcode = 1;
277
278 if (g_find_program_in_path ("makeindex")) {
279
280 gchar* command = g_strdup_printf ("%s makeindex \"%s.idx\"",
281 C_TEXSEC,
282 g_path_get_basename(ec->basename));
283
284 Tuple2 res = utils_popen_r (command, C_TMPDIR);
285 retcode = (glong)res.first;
286 g_free (command);
287 }
288 if (retcode == 0) return TRUE;
289 return FALSE;
290 }
291
latex_can_synctex(void)292 gboolean latex_can_synctex (void) {
293 if (gummi->latex->tex_version >= 2008) {
294 return TRUE;
295 }
296 return FALSE;
297 }
298
299
latex_use_synctex(void)300 gboolean latex_use_synctex (void) {
301 return (config_get_boolean ("Compile", "synctex") &&
302 config_get_boolean ("Preview", "autosync"));
303 }
304
latex_use_shellescaping(void)305 gboolean latex_use_shellescaping (void) {
306 return config_get_boolean ("Compile", "shellescape");
307 }
308