source: git/src/cavernlog.cc @ f175de2

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

src/cavernlog.cc: Remove superfluous nested declaration.

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

  • Property mode set to 100644
File size: 9.3 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 "mainfrm.h"
29#include "message.h"
30
31#include <errno.h>
32#include <stdio.h>
33#include <stdlib.h>
34
35// For select():
36#ifdef HAVE_SYS_SELECT_H
37#include <sys/select.h>
38#endif
39#include <sys/time.h>
40#include <sys/types.h>
41#include <unistd.h>
42
43#ifdef HAVE_GETC_UNLOCKED
44# define GETC(F) getc_unlocked(F)
45#else
46# define GETC(F) getc(F)
47#endif
48
49enum { LOG_REPROCESS = 1234 };
50
51BEGIN_EVENT_TABLE(CavernLogWindow, wxHtmlWindow)
52    EVT_BUTTON(LOG_REPROCESS, CavernLogWindow::OnReprocess)
53    EVT_BUTTON(wxID_OK, CavernLogWindow::OnOK)
54END_EVENT_TABLE()
55
56static wxString escape_for_shell(wxString s, bool protect_dash = false)
57{
58    size_t p = 0;
59#ifdef __WXMSW__
60    bool needs_quotes = false;
61    while (p < s.size()) {
62        wxChar ch = s[p];
63        if (ch < 127) {
64            if (ch == wxT('"')) {
65                s.insert(p, wxT('\\'));
66                ++p;
67                needs_quotes = true;
68            } else if (strchr(" <>&|^", ch)) {
69                needs_quotes = true;
70            }
71        }
72        ++p;
73    }
74    if (needs_quotes) {
75        s.insert(0, wxT('"'));
76        s += wxT('"');
77    }
78#else
79    if (protect_dash && !s.empty() && s[0u] == '-') {
80        // If the filename starts with a '-', protect it from being
81        // treated as an option by prepending "./".
82        s.insert(0, wxT("./"));
83        p = 2;
84    }
85    while (p < s.size()) {
86        // Exclude a few safe characters which are common in filenames
87        if (!isalnum(s[p]) && strchr("/._-", s[p]) == NULL) {
88            s.insert(p, wxT('\\'));
89            ++p;
90        }
91        ++p;
92    }
93#endif
94    return s;
95}
96
97CavernLogWindow::CavernLogWindow(MainFrm * mainfrm_, wxWindow * parent)
98    : wxHtmlWindow(parent), mainfrm(mainfrm_)
99{
100    int fsize = parent->GetFont().GetPointSize();
101    int sizes[7] = { fsize, fsize, fsize, fsize, fsize, fsize, fsize };
102    SetFonts(wxString(), wxString(), sizes);
103}
104
105void
106CavernLogWindow::OnLinkClicked(const wxHtmlLinkInfo &link)
107{
108    wxString href = link.GetHref();
109    wxString title = link.GetTarget();
110    size_t colon = href.rfind(wxT(':'));
111    if (colon != wxString::npos) {
112#ifdef __WXMSW__
113        wxString cmd = wxT("notepad $f");
114#else
115        wxString cmd = wxT("x-terminal-emulator -title $t -e vim -c $l $f");
116        // wxString cmd = "x-terminal-emulator -title $t -e emacs +$l $f";
117        // wxString cmd = "x-terminal-emulator -title $t -e nano +$l $f";
118        // wxString cmd = "x-terminal-emulator -title $t -e jed -g $l $f";
119#endif
120        const char * p = getenv("SURVEXEDITOR");
121        if (p) {
122            cmd = wxString(p, wxConvUTF8);
123            if (!cmd.find(wxT("$f"))) {
124                cmd += wxT(" $f");
125            }
126        }
127        size_t i = 0;
128        while ((i = cmd.find(wxT('$'), i)) != wxString::npos) {
129            if (++i >= cmd.size()) break;
130            switch (cmd[i]) {
131                case wxT('$'):
132                    cmd.erase(i, 1);
133                    break;
134                case wxT('f'): {
135                    wxString f = escape_for_shell(href.substr(0, colon), true);
136                    cmd.replace(i - 1, 2, f);
137                    i += f.size() - 1;
138                    break;
139                }
140                case wxT('t'): {
141                    wxString t = escape_for_shell(title);
142                    cmd.replace(i - 1, 2, t);
143                    i += t.size() - 1;
144                    break;
145                }
146                case wxT('l'): {
147                    wxString l = escape_for_shell(href.substr(colon + 1));
148                    cmd.replace(i - 1, 2, l);
149                    i += l.size() - 1;
150                    break;
151                }
152                default:
153                    ++i;
154            }
155        }
156#ifdef __WXMSW__
157        _wsystem(cmd.c_str());
158#else
159        system(cmd.mb_str());
160#endif
161    }
162}
163
164int
165CavernLogWindow::process(const wxString &file)
166{
167    SetFocus();
168    filename = file;
169
170#ifdef __WXMSW__
171    SetEnvironmentVariable(wxT("SURVEX_UTF8"), wxT("1"));
172#else
173    setenv("SURVEX_UTF8", "1", 1);
174#endif
175
176    wxString escaped_file = escape_for_shell(file, true);
177#ifdef __WXMSW__
178    wxString cmd;
179    {
180        wchar_t * argv0 = _wpgmptr;
181        wchar_t * slash = wcsrchr(argv0, L'\\');
182        if (slash) {
183            cmd.assign(argv0, slash - argv0 + 1);
184        }
185    }
186    cmd += L"cavern";
187    cmd = escape_for_shell(cmd, false);
188#else
189    char *cavern = use_path(msg_exepth(), "cavern");
190    wxString cmd = escape_for_shell(wxString(cavern, wxConvUTF8), false);
191    osfree(cavern);
192#endif
193    cmd += wxT(" -o ");
194    cmd += escaped_file;
195    cmd += wxT(' ');
196    cmd += escaped_file;
197
198#ifdef __WXMSW__
199    FILE * cavern_out = _wpopen(cmd.c_str(), L"r");
200#else
201    FILE * cavern_out = popen(cmd.mb_str(), "r");
202#endif
203    if (!cavern_out) {
204        wxString m;
205        m.Printf(wmsg(/*Couldn't open pipe: `%s'*/17), cmd.c_str());
206        m += wxT(" (");
207        m += wxString(strerror(errno), wxConvUTF8);
208        m += wxT(')');
209        wxGetApp().ReportError(m);
210        return -2;
211    }
212
213    int cavern_fd;
214#ifdef __WXMSW__
215    cavern_fd = _fileno(cavern_out);
216#else
217    cavern_fd = fileno(cavern_out);
218#endif
219    assert(cavern_fd < FD_SETSIZE); // FIXME we shouldn't just assert, but what else to do?
220    wxString cur;
221    int link_count = 0;
222    // We're only guaranteed one character of pushback by ungetc() but we
223    // need two so for portability we implement the second ourselves.
224    int left = EOF;
225    while (!feof(cavern_out)) {
226        fd_set rfds, efds;
227        FD_ZERO(&rfds);
228        FD_SET(cavern_fd, &rfds);
229        FD_ZERO(&efds);
230        FD_SET(cavern_fd, &efds);
231        // Timeout instantly.
232        struct timeval timeout;
233        timeout.tv_sec = 0;
234        timeout.tv_usec = 0;
235        if (select(cavern_fd + 1, &rfds, NULL, &efds, &timeout) == 0) {
236            Update();
237            FD_SET(cavern_fd, &rfds);
238            FD_SET(cavern_fd, &efds);
239            // Set timeout to 0.1 seconds.
240            timeout.tv_sec = 0;
241            timeout.tv_usec = 100000;
242            if (select(cavern_fd + 1, &rfds, NULL, &efds, &timeout) == 0) {
243                wxYield();
244                continue;
245            }
246        }
247        if (!FD_ISSET(cavern_fd, &rfds)) {
248            // Error, which pclose() should report.
249            break;
250        }
251        int ch;
252        if (left == EOF) {
253            ch = GETC(cavern_out);
254            if (ch == EOF) break;
255        } else {
256            ch = left;
257            left = EOF;
258        }
259        // Decode UTF-8 first to avoid security issues with <, >, &, etc
260        // encoded using multibyte encodings.
261        if (ch >= 0xc0 && ch < 0xf0) {
262            int ch1 = GETC(cavern_out);
263            if ((ch1 & 0xc0) != 0x80) {
264                left = ch1;
265            } else if (ch < 0xe0) {
266                /* 2 byte sequence */
267                ch = ((ch & 0x1f) << 6) | (ch1 & 0x3f);
268            } else {
269                /* 3 byte sequence */
270                int ch2 = GETC(cavern_out);
271                if ((ch2 & 0xc0) != 0x80) {
272                    ungetc(ch2, cavern_out);
273                    left = ch1;
274                } else {
275                    ch = ((ch & 0x1f) << 12) | ((ch1 & 0x3f) << 6) | (ch2 & 0x3f);
276                }
277            }
278        }
279
280        switch (ch) {
281            case '\r':
282                // Ignore.
283                break;
284            case '\n': {
285                if (cur.empty()) continue;
286#ifndef __WXMSW__
287                size_t colon = cur.find(':');
288#else
289                // If the path is "C:\path\to\file.svx" then don't split at the
290                // : after the drive letter!  FIXME: better to look for ": "?
291                size_t colon = cur.find(':', 2);
292#endif
293                if (colon != wxString::npos && colon < cur.size() - 1) {
294                    ++colon;
295                    size_t i = colon;
296                    while (i < cur.size() - 1 &&
297                           cur[i] >= wxT('0') && cur[i] <= wxT('9')) {
298                        ++i;
299                    }
300                    if (i > colon && cur[i] == wxT(':') ) {
301                        colon = i;
302                        wxString tag = wxT("<a href=\"");
303                        tag.append(cur, 0, colon);
304                        while (cur[++i] == wxT(' ')) { }
305                        tag += wxT("\" target=\"");
306                        tag.append(cur, i, wxString::npos);
307                        tag += wxT("\">");
308                        cur.insert(0, tag);
309                        cur.insert(colon + tag.size(), wxT("</a>"));
310                        ++link_count;
311                    }
312                }
313
314                // Save the scrollbar positions.
315                int scroll_x = 0, scroll_y = 0;
316                GetViewStart(&scroll_x, &scroll_y);
317
318                cur += wxT("<br>\n");
319                AppendToPage(cur);
320
321                if (!link_count) {
322                    // Auto-scroll the window until we've reported a warning or
323                    // error.
324                    int x, y;
325                    GetVirtualSize(&x, &y);
326                    int xs, ys;
327                    GetClientSize(&xs, &ys);
328                    y -= ys;
329                    int xu, yu;
330                    GetScrollPixelsPerUnit(&xu, &yu);
331                    Scroll(scroll_x, y / yu);
332                } else {
333                    // Restore the scrollbar positions.
334                    Scroll(scroll_x, scroll_y);
335                }
336
337                cur.clear();
338                break;
339            }
340            case '<':
341                cur += wxT("&lt;");
342                break;
343            case '>':
344                cur += wxT("&gt;");
345                break;
346            case '&':
347                cur += wxT("&amp;");
348                break;
349            case '"':
350                cur += wxT("&#22;");
351                continue;
352            default:
353                if (ch >= 128) {
354                    cur += wxString::Format(wxT("&#%u;"), ch);
355                } else {
356                    cur += (char)ch;
357                }
358        }
359    }
360
361    int retval = pclose(cavern_out);
362    if (retval) {
363        AppendToPage(wxT("<avenbutton default id=1234 name=\"Reprocess\">"));
364        if (retval == -1) {
365            wxString m = wxT("Problem running cavern: ");
366            m += wxString(strerror(errno), wxConvUTF8);
367            wxGetApp().ReportError(m);
368            return -2;
369        }
370        return -1;
371    }
372    if (link_count) {
373        AppendToPage(wxT("<avenbutton id=1234 name=\"Reprocess\">"));
374        AppendToPage(wxString::Format(wxT("<avenbutton default id=%d>"), (int)wxID_OK));
375        Update();
376    }
377    return link_count;
378}
379
380void
381CavernLogWindow::OnReprocess(wxCommandEvent & e)
382{
383    SetPage(wxString());
384    if (process(filename) == 0) {
385        OnOK(e);
386    }
387}
388
389void
390CavernLogWindow::OnOK(wxCommandEvent &)
391{
392    mainfrm->InitialiseAfterLoad(filename);
393}
Note: See TracBrowser for help on using the repository browser.