source: git/src/cavernlog.cc @ a75f5a1

RELEASE/1.2debug-cidebug-ci-sanitisersstereowalls-data
Last change on this file since a75f5a1 was a75f5a1, checked in by Olly Betts <olly@…>, 14 years ago

src/cavernlog.cc: Set the select timeout before calling wxYield()
when waiting for output from cavern to 0.1s instead of 0.00001s to
avoid yielding excessively on slower machines where this is likely
to really slow things down. Call Scroll() only once per line in
the case where we are updating the scrollbar position. Call
Update() instead of wxYield() after each line, since that's enough
to get the window updated. Remove unused assignment "ch = left;"
at end of loop.

git-svn-id: file:///home/survex-svn/survex/trunk@3479 4b37db11-9a0c-4f06-9ece-9ab7cdaee568

  • Property mode set to 100644
File size: 8.5 KB
Line 
1/* cavernlog.cc
2 * Run cavern inside an Aven window
3 *
4 * Copyright (C) 2005,2006,2010 Olly Betts
5 *
6 * This program 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 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program 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 this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
19 */
20
21#ifdef HAVE_CONFIG_H
22# include <config.h>
23#endif
24
25#include "aven.h"
26#include "cavernlog.h"
27#include "filename.h"
28#include "message.h"
29
30#include <errno.h>
31#include <stdio.h>
32
33// For select():
34#ifdef HAVE_SYS_SELECT_H
35#include <sys/select.h>
36#endif
37#include <sys/time.h>
38#include <sys/types.h>
39#include <unistd.h>
40
41static wxString escape_for_shell(wxString s, bool protect_dash = false)
42{
43    size_t p = 0;
44#ifdef __WXMSW__
45    bool needs_quotes = false;
46    while (p < s.size()) {
47        if (s[p] == wxT('"')) {
48            s.insert(p, wxT('\\'));
49            ++p;
50            needs_quotes = true;
51        }
52        ++p;
53    }
54    if (needs_quotes) {
55        s.insert(0, wxT('"'));
56        s += wxT('"');
57    }
58#else
59    if (protect_dash && !s.empty() && s[0u] == '-') {
60        // If the filename starts with a '-', protect it from being
61        // treated as an option by prepending "./".
62        s.insert(0, wxT("./"));
63        p = 2;
64    }
65    while (p < s.size()) {
66        // Exclude a few safe characters which are common in filenames
67        if (!isalnum(s[p]) && strchr("/._-", s[p]) == NULL) {
68            s.insert(p, wxT('\\'));
69            ++p;
70        }
71        ++p;
72    }
73#endif
74    return s;
75}
76
77static wxString
78html_escape(const wxString &str)
79{
80    wxString res;
81    size_t p = 0;
82    while (p < str.size()) {
83        char ch = str[p++];
84        switch (ch) {
85            case '<':
86                res += wxT("&lt;");
87                continue;
88            case '>':
89                res += wxT("&gt;");
90                continue;
91            case '&':
92                res += wxT("&amp;");
93                continue;
94            case '"':
95                res += wxT("&quot;");
96                continue;
97            default:
98                res += ch;
99        }
100    }
101    return res;
102}
103
104CavernLogWindow::CavernLogWindow(wxWindow * parent) : wxHtmlWindow(parent) {
105    //GetParser()->SetInputEncoding(wxFONTENCODING_UTF8);
106    int fsize = parent->GetFont().GetPointSize();
107    int sizes[7] = { fsize, fsize, fsize, fsize, fsize, fsize, fsize };
108    SetFonts(wxString(), wxString(), sizes);
109    //GetParser()->SetInputEncoding(wxFONTENCODING_UTF8);
110}
111
112void
113CavernLogWindow::OnLinkClicked(const wxHtmlLinkInfo &link)
114{
115    wxString href = link.GetHref();
116    wxString title = link.GetTarget();
117    size_t colon = href.rfind(wxT(':'));
118    if (colon != wxString::npos) {
119#ifdef __WXMSW__
120        wxString cmd = wxT("notepad $f");
121#else
122        wxString cmd = wxT("x-terminal-emulator -title $t -e vim -c $l $f");
123        // wxString cmd = "x-terminal-emulator -title $t -e emacs +$l $f";
124        // wxString cmd = "x-terminal-emulator -title $t -e nano +$l $f";
125        // wxString cmd = "x-terminal-emulator -title $t -e jed -g $l $f";
126#endif
127        const char * p = getenv("SURVEXEDITOR");
128        if (p) {
129            cmd = wxString(p, wxConvUTF8);
130            if (!cmd.find(wxT("$f"))) {
131                cmd += wxT(" $f");
132            }
133        }
134        size_t i = 0;
135        while ((i = cmd.find(wxT('$'), i)) != wxString::npos) {
136            if (++i >= cmd.size()) break;
137                switch (cmd[i]) {
138                    case wxT('$'):
139                        cmd.erase(i, 1);
140                        break;
141                    case wxT('f'): {
142                        wxString f = escape_for_shell(href.substr(0, colon), true);
143                        cmd.replace(i - 1, 2, f);
144                        i += f.size() - 1;
145                        break;
146                    }
147                    case wxT('t'): {
148                        wxString t = escape_for_shell(title);
149                        cmd.replace(i - 1, 2, t);
150                        i += t.size() - 1;
151                        break;
152                    }
153                    case wxT('l'): {
154                        wxString l = escape_for_shell(href.substr(colon + 1));
155                        cmd.replace(i - 1, 2, l);
156                        i += l.size() - 1;
157                        break;
158                    }
159                    default:
160                        ++i;
161                }
162        }
163        // FIXME: This should really use fn_str() - currently we probably can't
164        // process a Unicode path on wxmsw.
165        system(cmd.char_str());
166    }
167}
168
169int
170CavernLogWindow::process(const wxString &file)
171{
172    char *cavern = use_path(msg_exepth(), "cavern");
173#ifdef __WXMSW__
174    SetEnvironmentVariable(wxT("SURVEX_UTF8"), wxT("1"));
175#else
176    setenv("SURVEX_UTF8", "1", 1);
177#endif
178
179    wxString escaped_file = escape_for_shell(file, true);
180    wxString cmd = escape_for_shell(wxString(cavern, wxConvUTF8), false);
181    osfree(cavern);
182    cmd += wxT(" -o ");
183    cmd += escaped_file;
184    cmd += wxT(' ');
185    cmd += escaped_file;
186
187    // FIXME: This should really use fn_str() - currently we probably can't
188    // process a Unicode path on wxmsw.
189    FILE * cavern_out = popen(cmd.char_str(), "r");
190    if (!cavern_out) {
191        wxString m;
192        m.Printf(wmsg(/*Couldn't open pipe: `%s'*/17), cmd.c_str());
193        m += wxT(" (");
194        m += wxString(strerror(errno), wxConvUTF8);
195        m += wxT(')');
196        wxGetApp().ReportError(m);
197        return -2;
198    }
199
200    int cavern_fd;
201#ifdef __WXMSW__
202    cavern_fd = _fileno(cavern_out);
203#else
204    cavern_fd = fileno(cavern_out);
205#endif
206    assert(cavern_fd < FD_SETSIZE); // FIXME we shouldn't just assert, but what else to do?
207    wxString cur;
208    int link_count = 0;
209    // We're only guaranteed one character of pushback by ungetc() but we
210    // need two so for portability we implement the second ourselves.
211    int left = EOF;
212    while (!feof(cavern_out)) {
213        fd_set rfds, efds;
214        FD_ZERO(&rfds);
215        FD_SET(cavern_fd, &rfds);
216        FD_ZERO(&efds);
217        FD_SET(cavern_fd, &efds);
218        // Set timeout to 0.1 seconds.
219        struct timeval timeout;
220        timeout.tv_sec = 0;
221        timeout.tv_usec = 100000;
222        if (select(cavern_fd + 1, &rfds, NULL, &efds, &timeout) == 0) {
223            wxYield();
224            continue;
225        }
226        if (!FD_ISSET(cavern_fd, &rfds)) {
227            // Error, which pclose() should report.
228            break;
229        }
230        int ch;
231        if (left == EOF) {
232            ch = getc(cavern_out);
233            if (ch == EOF) break;
234        } else {
235            ch = left;
236            left = EOF;
237        }
238        // Decode UTF-8 first to avoid security issues with <, >, &, etc
239        // encoded using multibyte encodings.
240        if (ch >= 0xc0 && ch < 0xf0) {
241            int ch1 = getc(cavern_out);
242            if ((ch1 & 0xc0) != 0x80) {
243                left = ch1;
244            } else if (ch < 0xe0) {
245                /* 2 byte sequence */
246                ch = ((ch & 0x1f) << 6) | (ch1 & 0x3f);
247            } else {
248                /* 3 byte sequence */
249                int ch2 = getc(cavern_out);
250                if ((ch2 & 0xc0) != 0x80) {
251                    ungetc(ch2, cavern_out);
252                    left = ch1;
253                } else {
254                    ch = ((ch & 0x1f) << 12) | ((ch1 & 0x3f) << 6) | (ch2 & 0x3f);
255                }
256            }
257        }
258
259        switch (ch) {
260            case '\r':
261                // Ignore.
262                break;
263            case '\n': {
264                if (cur.empty()) continue;
265                size_t colon = cur.find(':');
266#ifdef __WXMSW__
267                // If the path is "C:\path\to\file.svx" then don't split at the
268                // : after the drive letter!  FIXME: better to look for ": "?
269                if (colon == 1) colon = cur.find(':', 2);
270#endif
271                if (colon != wxString::npos) {
272                    size_t colon2 = cur.find(':', colon + 1);
273                    if (colon2 != wxString::npos && colon2 != cur.size() - 1) {
274                        wxString href = cur.substr(0, colon2);
275                        while (++colon2 < cur.size()) {
276                            if (cur[colon2] != ' ') break;
277                        }
278                        wxString title = cur.substr(colon2);
279                        cur.insert(colon2, wxT("</a>"));
280                        wxString tag = wxT("<a href=\"");
281                        tag += html_escape(href);
282                        tag += wxT("\" target=\"");
283                        tag += html_escape(title);
284                        tag += wxT("\">");
285                        cur.insert(0, tag);
286                        ++link_count;
287                    }
288                }
289
290                // Save the scrollbar positions.
291                int scroll_x = 0, scroll_y = 0;
292                GetViewStart(&scroll_x, &scroll_y);
293
294                cur += wxT("<br>\n");
295                AppendToPage(cur);
296
297                if (!link_count) {
298                    // Auto-scroll the window until we've reported a warning or
299                    // error.
300                    int x, y;
301                    GetVirtualSize(&x, &y);
302                    int xs, ys;
303                    GetClientSize(&xs, &ys);
304                    y -= ys;
305                    int xu, yu;
306                    GetScrollPixelsPerUnit(&xu, &yu);
307                    Scroll(scroll_x, y / yu);
308                } else {
309                    // Restore the scrollbar positions.
310                    Scroll(scroll_x, scroll_y);
311                }
312
313                cur.clear();
314                Update();
315                break;
316            }
317            case '<':
318                cur += wxT("&lt;");
319                break;
320            case '>':
321                cur += wxT("&gt;");
322                break;
323            case '&':
324                cur += wxT("&amp;");
325                break;
326            default:
327                if (ch >= 128) {
328                    cur += wxString::Format(wxT("&#%u;"), ch);
329                } else {
330                    cur += (char)ch;
331                }
332        }
333    }
334    int retval = pclose(cavern_out);
335    if (retval) {
336        if (retval == -1) {
337            wxString m = wxT("Problem running cavern: ");
338            m += wxString(strerror(errno), wxConvUTF8);
339            wxGetApp().ReportError(m);
340            return -2;
341        }
342        wxGetApp().ReportError(wxT("Failed to process survey data - see log window for details"));
343        return -1;
344    }
345    return link_count;
346}
Note: See TracBrowser for help on using the repository browser.