source: git/src/cavernlog.cc @ 813919e

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

src/cavernlog.cc: Remove commented out calls to set the input
encoding of the HTML parser - we now always escape non-ASCII
characters as HTML entities.

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

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