source: git/src/cavernlog.cc @ 77b8654

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

src/cavernlog.cc: Don't double escape the contents of href and target
in links in the log.

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

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