lnav/src/lnav.cc

3796 lines
117 KiB
C++
Raw Normal View History

2011-06-11 17:26:52 +00:00
/**
2013-05-03 06:02:03 +00:00
* Copyright (c) 2007-2012, Timothy Stack
*
* All rights reserved.
2013-05-28 04:35:00 +00:00
*
2013-05-03 06:02:03 +00:00
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
2013-05-28 04:35:00 +00:00
*
2013-05-03 06:02:03 +00:00
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Timothy Stack nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
2013-05-28 04:35:00 +00:00
*
2013-05-03 06:02:03 +00:00
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
2011-06-11 17:26:52 +00:00
* @file lnav.cc
*
* XXX This file has become a dumping ground for code and needs to be broken up
* a bit.
*/
2009-09-14 01:07:32 +00:00
#include "config.h"
#include <stdio.h>
#include <errno.h>
#include <math.h>
2009-09-14 01:07:32 +00:00
#include <time.h>
#include <glob.h>
#include <locale.h>
2009-09-14 01:07:32 +00:00
2013-05-17 21:53:01 +00:00
#include <fcntl.h>
2009-09-14 01:07:32 +00:00
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
2012-04-19 02:30:53 +00:00
#include <termios.h>
2009-09-14 01:07:32 +00:00
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
2012-04-24 21:31:35 +00:00
#include <sys/time.h>
2009-09-14 01:07:32 +00:00
#include <sys/socket.h>
2013-10-11 13:31:17 +00:00
#include <sys/wait.h>
2009-09-14 01:07:32 +00:00
#include <readline/readline.h>
#include <map>
2011-06-11 17:26:52 +00:00
#include <set>
2009-09-14 01:07:32 +00:00
#include <stack>
#include <vector>
#include <memory>
#include <fstream>
2009-09-14 01:07:32 +00:00
#include <sstream>
#include <iostream>
2009-09-14 01:07:32 +00:00
#include <algorithm>
#include <functional>
2009-10-06 21:14:49 +00:00
#include <sqlite3.h>
2012-07-13 16:26:47 +00:00
#include "lnav.hh"
2009-09-14 01:07:32 +00:00
#include "help.hh"
#include "init-sql.hh"
2009-09-14 01:07:32 +00:00
#include "logfile.hh"
#include "lnav_util.hh"
#include "ansi_scrubber.hh"
2009-09-14 01:07:32 +00:00
#include "listview_curses.hh"
#include "statusview_curses.hh"
#include "vt52_curses.hh"
#include "readline_curses.hh"
#include "textview_curses.hh"
#include "logfile_sub_source.hh"
#include "textfile_sub_source.hh"
2009-09-14 01:07:32 +00:00
#include "grep_proc.hh"
#include "bookmarks.hh"
#include "hist_source.hh"
#include "top_status_source.hh"
#include "bottom_status_source.hh"
#include "piper_proc.hh"
#include "log_vtab_impl.hh"
#include "db_sub_source.hh"
#include "pcrecpp.h"
2009-10-14 19:42:58 +00:00
#include "termios_guard.hh"
#include "data_parser.hh"
2012-04-24 21:31:35 +00:00
#include "xterm_mouse.hh"
2012-07-13 16:26:47 +00:00
#include "lnav_commands.hh"
#include "column_namer.hh"
#include "log_data_table.hh"
#include "log_format_loader.hh"
2013-05-31 15:01:31 +00:00
#include "session_data.hh"
#include "lnav_config.hh"
#include "sql_util.hh"
#include "sqlite-extension-func.h"
#include "log_data_helper.hh"
2013-05-31 15:01:31 +00:00
#include "yajlpp.hh"
2009-09-14 01:07:32 +00:00
using namespace std;
#define HELP_MSG_1(x, msg) \
"Press '" ANSI_BOLD(#x) "' " msg
#define HELP_MSG_2(x, y, msg) \
"Press " ANSI_BOLD(#x) "/" ANSI_BOLD(#y) " " msg
2009-09-14 01:07:32 +00:00
static multimap<lnav_flags_t, string> DEFAULT_FILES;
2012-07-13 16:26:47 +00:00
struct _lnav_data lnav_data;
2009-09-14 01:07:32 +00:00
struct hist_level {
int hl_bucket_size;
int hl_group_size;
};
static struct hist_level HIST_ZOOM_VALUES[] = {
{ 24 * 60 * 60, 7 * 24 * 60 * 60 },
{ 4 * 60 * 60, 24 * 60 * 60 },
{ 60 * 60, 24 * 60 * 60 },
{ 10 * 60, 60 * 60 },
{ 60, 60 * 60 },
};
2013-05-28 04:35:00 +00:00
static const int HIST_ZOOM_LEVELS = sizeof(HIST_ZOOM_VALUES) /
sizeof(struct hist_level);
2009-09-14 01:07:32 +00:00
2011-06-11 17:26:52 +00:00
static bookmark_type_t BM_EXAMPLE;
static bookmark_type_t BM_QUERY;
2011-06-11 17:26:52 +00:00
2013-05-31 15:01:31 +00:00
const char *lnav_view_strings[LNV__MAX] = {
"log",
"text",
"help",
"histogram",
"graph",
"db",
"example",
};
class field_overlay_source : public list_overlay_source {
public:
field_overlay_source(logfile_sub_source &lss)
2013-05-28 04:35:00 +00:00
: fos_active(false),
fos_active_prev(false),
fos_log_helper(lss) { };
2013-05-28 04:35:00 +00:00
size_t list_overlay_count(const listview_curses &lv)
{
logfile_sub_source &lss = lnav_data.ld_log_source;
if (!this->fos_active) {
return 0;
}
if (lss.text_line_count() == 0) {
return 0;
}
content_line_t cl = lss.at(lv.get_top());
if (!this->fos_log_helper.parse_line(cl)) {
return 0;
}
2013-05-28 04:35:00 +00:00
this->fos_key_size = 0;
for (std::vector<logline_value>::iterator iter =
this->fos_log_helper.ldh_line_values.begin();
iter != this->fos_log_helper.ldh_line_values.end();
2013-05-28 04:35:00 +00:00
++iter) {
this->fos_key_size = max(this->fos_key_size,
(int)iter->lv_name.length());
}
for (data_parser::element_list_t::iterator iter =
this->fos_log_helper.ldh_parser->dp_pairs.begin();
iter != this->fos_log_helper.ldh_parser->dp_pairs.end();
2013-05-28 04:35:00 +00:00
++iter) {
std::string colname = this->fos_log_helper.ldh_parser->get_element_string(
2013-05-28 04:35:00 +00:00
iter->e_sub_elements->front());
colname = this->fos_log_helper.ldh_namer->add_column(colname);
2013-05-28 04:35:00 +00:00
this->fos_key_size = max(this->fos_key_size,
(int)colname.length());
}
return 1 +
2013-07-10 03:52:20 +00:00
1 +
this->fos_log_helper.ldh_line_values.size() +
2013-05-28 04:35:00 +00:00
1 +
this->fos_log_helper.ldh_parser->dp_pairs.size();
2013-05-28 04:35:00 +00:00
};
bool list_value_for_overlay(const listview_curses &lv,
vis_line_t y,
attr_line_t &value_out)
{
if (!this->fos_active || this->fos_log_helper.ldh_parser.get() == NULL) {
2013-05-28 04:35:00 +00:00
return false;
}
size_t total_count = (1 +
2013-07-13 22:38:12 +00:00
1 +
this->fos_log_helper.ldh_line_values.size() +
2013-05-28 04:35:00 +00:00
1 +
this->fos_log_helper.ldh_parser->dp_pairs.size());
2013-05-28 04:35:00 +00:00
int row = (int)y - 1;
bool last_line = (row == (int)(total_count - 1));
if (row < 0 || row >= (int)total_count) {
return false;
}
std::string &str = value_out.get_string();
2013-07-10 03:52:20 +00:00
if (row == 0) {
char old_timestamp[64], curr_timestamp[64];
struct timeval curr_tv, offset_tv, orig_tv;
char log_time[256];
sql_strftime(curr_timestamp, sizeof(curr_timestamp),
this->fos_log_helper.ldh_line->get_time(),
this->fos_log_helper.ldh_line->get_millis());
curr_tv = this->fos_log_helper.ldh_line->get_timeval();
offset_tv = this->fos_log_helper.ldh_file->get_time_offset();
2013-07-10 03:52:20 +00:00
timersub(&curr_tv, &offset_tv, &orig_tv);
sql_strftime(old_timestamp, sizeof(old_timestamp),
orig_tv.tv_sec, orig_tv.tv_usec / 1000);
snprintf(log_time, sizeof(log_time),
" Current Time: %s Original Time: %s Offset: %+d.%03d",
curr_timestamp,
old_timestamp,
(int)offset_tv.tv_sec, offset_tv.tv_usec / 1000);
str = log_time;
return true;
}
row -= 1;
2013-05-28 04:35:00 +00:00
if (row == 0) {
if (this->fos_log_helper.ldh_line_values.empty()) {
2013-05-28 04:35:00 +00:00
str = " No known message fields";
}
else{
str = " Known message fields:";
}
return true;
}
row -= 1;
if (row == (int)this->fos_log_helper.ldh_line_values.size()) {
if (this->fos_log_helper.ldh_parser->dp_pairs.empty()) {
2013-05-28 04:35:00 +00:00
str = " No discovered message fields";
}
else{
str = " Discovered message fields:";
}
return true;
}
if (row < (int)this->fos_log_helper.ldh_line_values.size()) {
str = " " + this->fos_log_helper.ldh_line_values[row].lv_name;
2013-05-28 04:35:00 +00:00
int padding = this->fos_key_size - str.length() + 3;
str.append(padding, ' ');
str += " = " + this->fos_log_helper.ldh_line_values[row].to_string();
2013-05-28 04:35:00 +00:00
}
else {
data_parser::element_list_t::iterator iter;
row -= this->fos_log_helper.ldh_line_values.size() + 1;
2013-05-28 04:35:00 +00:00
iter = this->fos_log_helper.ldh_parser->dp_pairs.begin();
2013-05-28 04:35:00 +00:00
std::advance(iter, row);
str = " " + this->fos_log_helper.ldh_namer->cn_names[row];
2013-05-28 04:35:00 +00:00
int padding = this->fos_key_size - str.length() + 3;
str.append(padding, ' ');
str += " = " + this->fos_log_helper.ldh_parser->get_element_string(
2013-05-28 04:35:00 +00:00
iter->e_sub_elements->back());
}
string_attrs_t & sa = value_out.get_attrs();
struct line_range lr(1, 2);
2013-05-28 04:35:00 +00:00
sa[lr].insert(make_string_attr("graphic",
last_line ? ACS_LLCORNER : ACS_LTEE));
lr.lr_start = 3 + this->fos_key_size + 3;
lr.lr_end = -1;
sa[lr].insert(make_string_attr("style", A_BOLD));
return true;
};
bool fos_active;
bool fos_active_prev;
log_data_helper fos_log_helper;
2013-05-28 04:35:00 +00:00
int fos_key_size;
};
static int handle_collation_list(void *ptr,
int ncols,
char **colvalues,
char **colnames)
{
2013-05-28 04:35:00 +00:00
lnav_data.ld_rl_view->add_possibility(LNM_SQL, "*", colvalues[1]);
2013-05-28 04:35:00 +00:00
return 0;
}
static int handle_db_list(void *ptr,
int ncols,
char **colvalues,
char **colnames)
{
2013-05-28 04:35:00 +00:00
lnav_data.ld_rl_view->add_possibility(LNM_SQL, "*", colvalues[1]);
2013-05-28 04:35:00 +00:00
return 0;
}
static int handle_table_list(void *ptr,
int ncols,
char **colvalues,
char **colnames)
{
2013-05-28 04:35:00 +00:00
lnav_data.ld_rl_view->add_possibility(LNM_SQL, "*", colvalues[0]);
2013-05-28 04:35:00 +00:00
return 0;
}
static int handle_table_info(void *ptr,
int ncols,
char **colvalues,
char **colnames)
{
2013-05-28 04:35:00 +00:00
lnav_data.ld_rl_view->add_possibility(LNM_SQL, "*", colvalues[1]);
if (strcmp(colvalues[5], "1") == 0) {
lnav_data.ld_db_key_names.push_back(colvalues[1]);
}
return 0;
}
static int handle_foreign_key_list(void *ptr,
int ncols,
char **colvalues,
char **colnames)
{
2013-05-28 04:35:00 +00:00
lnav_data.ld_db_key_names.push_back(colvalues[3]);
lnav_data.ld_db_key_names.push_back(colvalues[4]);
return 0;
}
struct sqlite_metadata_callbacks lnav_sql_meta_callbacks = {
handle_collation_list,
handle_db_list,
handle_table_list,
handle_table_info,
handle_foreign_key_list,
};
2013-06-06 02:34:48 +00:00
bool setup_logline_table()
{
// Hidden columns don't show up in the table_info pragma.
static const char *hidden_table_columns[] = {
"log_path",
"log_text",
NULL
};
2013-06-06 02:34:48 +00:00
textview_curses &log_view = lnav_data.ld_views[LNV_LOG];
2013-06-16 01:07:50 +00:00
bool retval = false;
2013-06-06 02:34:48 +00:00
if (log_view.get_inner_height()) {
vis_line_t vl = log_view.get_top();
content_line_t cl = lnav_data.ld_log_source.at(vl);
lnav_data.ld_vtab_manager->unregister_vtab("logline");
lnav_data.ld_vtab_manager->register_vtab(new log_data_table(cl));
retval = true;
}
lnav_data.ld_db_key_names.clear();
lnav_data.ld_rl_view->clear_possibilities(LNM_SQL, "*");
lnav_data.ld_rl_view->add_possibility(LNM_SQL, "*", sql_keywords);
lnav_data.ld_rl_view->add_possibility(LNM_SQL, "*", sql_function_names);
lnav_data.ld_rl_view->add_possibility(LNM_SQL, "*", hidden_table_columns);
2013-06-22 14:55:49 +00:00
for (int lpc = 0; sqlite_registration_funcs[lpc]; lpc++) {
const struct FuncDef *basic_funcs;
const struct FuncDefAgg *agg_funcs;
sqlite_registration_funcs[lpc](&basic_funcs, &agg_funcs);
for (int lpc2 = 0; basic_funcs && basic_funcs[lpc2].zName; lpc2++) {
lnav_data.ld_rl_view->add_possibility(LNM_SQL,
"*",
basic_funcs[lpc2].zName);
}
for (int lpc2 = 0; agg_funcs && agg_funcs[lpc2].zName; lpc2++) {
lnav_data.ld_rl_view->add_possibility(LNM_SQL,
"*",
agg_funcs[lpc2].zName);
}
}
walk_sqlite_metadata(lnav_data.ld_db.in(), lnav_sql_meta_callbacks);
{
log_vtab_manager::iterator iter;
for (iter = lnav_data.ld_vtab_manager->begin();
iter != lnav_data.ld_vtab_manager->end();
++iter) {
iter->second->get_foreign_keys(lnav_data.ld_db_key_names);
}
}
stable_sort(lnav_data.ld_db_key_names.begin(),
lnav_data.ld_db_key_names.end());
2013-06-06 02:34:48 +00:00
return retval;
}
/**
* Observer for loading progress that updates the bottom status bar.
*/
2009-09-14 01:07:32 +00:00
class loading_observer
: public logfile_sub_source::observer {
public:
loading_observer()
2013-05-28 04:35:00 +00:00
: lo_last_offset(0),
lo_last_line(0) { };
2009-09-14 01:07:32 +00:00
void logfile_indexing(logfile &lf, off_t off, size_t total)
{
2013-05-28 04:35:00 +00:00
/* XXX assert(off <= total); */
if (off > (off_t)total) {
off = total;
}
if ((std::abs((long int)(off - this->lo_last_offset)) >
(off_t)(128 * 1024)) ||
(size_t)off == total) {
lnav_data.ld_bottom_source.update_loading(off, total);
this->do_update();
this->lo_last_offset = off;
}
if (!lnav_data.ld_looping) {
throw logfile::error(lf.get_filename(), EINTR);
}
2009-09-14 01:07:32 +00:00
};
void logfile_sub_source_filtering(logfile_sub_source &lss,
vis_line_t cl,
2013-05-28 04:35:00 +00:00
size_t total)
2009-09-14 01:07:32 +00:00
{
if (std::abs(cl - this->lo_last_line) > (4 * 1024) || (size_t)cl ==
2013-05-28 04:35:00 +00:00
(total - 1)) {
lnav_data.ld_bottom_source.update_loading(cl, (total - 1));
this->do_update();
this->lo_last_line = cl;
}
if (!lnav_data.ld_looping) {
throw logfile::error("", EINTR);
}
2009-09-14 01:07:32 +00:00
};
private:
void do_update(void)
{
2013-05-28 04:35:00 +00:00
lnav_data.ld_top_source.update_time();
lnav_data.ld_status[LNS_TOP].do_update();
lnav_data.ld_status[LNS_BOTTOM].do_update();
refresh();
2009-09-14 01:07:32 +00:00
};
off_t lo_last_offset;
vis_line_t lo_last_line;
2009-09-14 01:07:32 +00:00
};
static void rebuild_hist(size_t old_count, bool force)
{
2013-06-16 01:07:50 +00:00
textview_curses & hist_view = lnav_data.ld_views[LNV_HISTOGRAM];
logfile_sub_source &lss = lnav_data.ld_log_source;
size_t new_count = lss.text_line_count();
2013-05-28 04:35:00 +00:00
hist_source &hs = lnav_data.ld_hist_source;
int zoom_level = lnav_data.ld_hist_zoom;
time_t old_time;
int lpc;
2009-09-14 01:07:32 +00:00
old_time = hs.value_for_row(hist_view.get_top());
hs.set_bucket_size(HIST_ZOOM_VALUES[zoom_level].hl_bucket_size);
hs.set_group_size(HIST_ZOOM_VALUES[zoom_level].hl_group_size);
if (force) {
2013-05-28 04:35:00 +00:00
hs.clear();
2009-09-14 01:07:32 +00:00
}
2009-10-14 19:42:58 +00:00
for (lpc = old_count; lpc < (int)new_count; lpc++) {
2013-05-28 04:35:00 +00:00
logline *ll = lss.find_line(lss.at(vis_line_t(lpc)));
2009-09-14 01:07:32 +00:00
2013-05-28 04:35:00 +00:00
if (!(ll->get_level() & logline::LEVEL_CONTINUED)) {
hs.add_value(ll->get_time(),
bucket_type_t(ll->get_level() &
~logline::LEVEL__FLAGS));
}
2009-09-14 01:07:32 +00:00
}
hist_view.reload_data();
hist_view.set_top(hs.row_for_value(old_time));
}
2012-07-13 16:26:47 +00:00
void rebuild_indexes(bool force)
2009-09-14 01:07:32 +00:00
{
static loading_observer obs;
2013-05-28 04:35:00 +00:00
logfile_sub_source &lss = lnav_data.ld_log_source;
textview_curses & log_view = lnav_data.ld_views[LNV_LOG];
textview_curses & text_view = lnav_data.ld_views[LNV_TEXT];
vis_line_t old_bottom(0), height(0);
content_line_t top_content = content_line_t(-1);
2009-09-14 01:07:32 +00:00
unsigned long width;
bool scroll_down;
size_t old_count;
time_t old_time;
2012-07-13 16:26:47 +00:00
old_count = lss.text_line_count();
2013-05-28 04:35:00 +00:00
if (old_count) {
top_content = lss.at(log_view.get_top());
}
2012-07-13 16:26:47 +00:00
{
2013-05-28 04:35:00 +00:00
textfile_sub_source * tss = &lnav_data.ld_text_source;
std::list<logfile *>::iterator iter;
2013-09-14 19:30:57 +00:00
logfile *front_file = NULL;
int front_top = -1;
bool new_data = false;
2013-05-28 04:35:00 +00:00
size_t new_count;
2010-12-28 17:39:57 +00:00
2013-05-28 04:35:00 +00:00
text_view.get_dimensions(height, width);
old_bottom = text_view.get_top() + height;
scroll_down = (size_t)old_bottom > tss->text_line_count();
2013-05-28 04:35:00 +00:00
for (iter = tss->tss_files.begin();
iter != tss->tss_files.end(); ) {
try {
bool new_text_data = (*iter)->rebuild_index(&obs);
if ((*iter)->get_format() != NULL) {
logfile *lf = *iter;
if (lnav_data.ld_log_source.insert_file(lf)) {
iter = tss->tss_files.erase(iter);
force = true;
}
else {
++iter;
}
}
else {
new_data = new_data || new_text_data;
2013-09-14 19:30:57 +00:00
if (!lnav_data.ld_files_to_front.empty() &&
lnav_data.ld_files_to_front.front().first ==
(*iter)->get_filename()) {
front_file = *iter;
front_top = lnav_data.ld_files_to_front.front().second;
lnav_data.ld_files_to_front.pop_front();
}
2013-05-28 04:35:00 +00:00
++iter;
}
2013-05-28 04:35:00 +00:00
}
catch (const line_buffer::error &e) {
2013-06-22 14:55:49 +00:00
// TODO: log that we dropped this file.
iter = tss->tss_files.erase(iter);
2013-05-28 04:35:00 +00:00
}
}
2010-12-28 17:39:57 +00:00
2013-09-14 19:30:57 +00:00
if (front_file != NULL) {
ensure_view(&text_view);
if (tss->current_file() != front_file) {
tss->tss_files.remove(front_file);
tss->tss_files.push_front(front_file);
redo_search(LNV_TEXT);
text_view.reload_data();
old_bottom = vis_line_t(-1);
new_data = false;
}
if (front_top < text_view.get_inner_height()) {
text_view.set_top(vis_line_t(front_top));
scroll_down = false;
}
}
if (new_data && lnav_data.ld_search_child[LNV_TEXT].get() != NULL) {
lnav_data.ld_search_child[LNV_TEXT]->get_grep_proc()->reset();
lnav_data.ld_search_child[LNV_TEXT]->get_grep_proc()->
queue_request(grep_line_t(-1));
lnav_data.ld_search_child[LNV_TEXT]->get_grep_proc()->start();
}
2013-05-28 04:35:00 +00:00
text_view.reload_data();
2010-12-28 17:39:57 +00:00
2013-05-28 04:35:00 +00:00
new_count = tss->text_line_count();
if (scroll_down && new_count >= (size_t)old_bottom) {
text_view.set_top(vis_line_t(new_count - height + 1));
}
}
2009-09-14 01:07:32 +00:00
old_time = lnav_data.ld_top_time;
log_view.get_dimensions(height, width);
old_bottom = log_view.get_top() + height;
2012-07-13 16:26:47 +00:00
scroll_down = (size_t)old_bottom > old_count;
2009-09-14 01:07:32 +00:00
if (force) {
2013-05-28 04:35:00 +00:00
old_count = 0;
2009-09-14 01:07:32 +00:00
}
if (lss.rebuild_index(&obs, force)) {
2013-05-28 04:35:00 +00:00
size_t new_count = lss.text_line_count();
grep_line_t start_line;
int lpc;
2009-09-14 01:07:32 +00:00
2013-05-28 04:35:00 +00:00
log_view.reload_data();
2012-07-13 16:26:47 +00:00
2013-05-28 04:35:00 +00:00
if (scroll_down && new_count >= (size_t)old_bottom) {
log_view.set_top(vis_line_t(new_count - height + 1));
}
else if (!scroll_down && force) {
content_line_t new_top_content = content_line_t(-1);
2012-07-13 16:26:47 +00:00
2013-05-28 04:35:00 +00:00
if (new_count) {
new_top_content = lss.at(log_view.get_top());
}
2012-07-13 16:26:47 +00:00
2013-05-28 04:35:00 +00:00
if (new_top_content != top_content) {
log_view.set_top(lss.find_from_time(old_time));
}
}
2009-09-14 01:07:32 +00:00
2013-05-28 04:35:00 +00:00
rebuild_hist(old_count, force);
2009-09-14 01:07:32 +00:00
2013-05-28 04:35:00 +00:00
start_line = force ? grep_line_t(0) : grep_line_t(-1);
2009-09-14 01:07:32 +00:00
2013-05-28 04:35:00 +00:00
if (force) {
log_view.match_reset();
}
2010-11-22 06:01:57 +00:00
2013-05-28 04:35:00 +00:00
for (lpc = 0; lpc < LG__MAX; lpc++) {
if (lnav_data.ld_grep_child[lpc].get() != NULL) {
lnav_data.ld_grep_child[lpc]->get_grep_proc()->
queue_request(start_line);
lnav_data.ld_grep_child[lpc]->get_grep_proc()->start();
}
}
if (lnav_data.ld_search_child[LNV_LOG].get() != NULL) {
lnav_data.ld_search_child[LNV_LOG]->get_grep_proc()->reset();
lnav_data.ld_search_child[LNV_LOG]->get_grep_proc()->
queue_request(start_line);
lnav_data.ld_search_child[LNV_LOG]->get_grep_proc()->start();
}
2009-09-14 01:07:32 +00:00
}
lnav_data.ld_bottom_source.update_filtered(lss);
lnav_data.ld_scroll_broadcaster.invoke(lnav_data.ld_view_stack.top());
}
class plain_text_source
: public text_sub_source {
public:
plain_text_source(string text)
{
2013-05-28 04:35:00 +00:00
size_t start = 0, end;
2009-09-14 01:07:32 +00:00
2013-05-28 04:35:00 +00:00
while ((end = text.find('\n', start)) != string::npos) {
this->tds_lines.push_back(text.substr(start, end - start));
start = end + 1;
}
this->tds_lines.push_back(text.substr(start));
2009-09-14 01:07:32 +00:00
};
size_t text_line_count()
{
2013-05-28 04:35:00 +00:00
return this->tds_lines.size();
2009-09-14 01:07:32 +00:00
};
void text_value_for_line(textview_curses &tc,
2013-05-28 04:35:00 +00:00
int row,
string &value_out,
bool no_scrub)
2009-09-14 01:07:32 +00:00
{
2013-05-28 04:35:00 +00:00
value_out = this->tds_lines[row];
2009-09-14 01:07:32 +00:00
};
private:
vector<string> tds_lines;
};
class time_label_source
: public hist_source::label_source {
public:
time_label_source() { };
void hist_label_for_bucket(int bucket_start_value,
2013-05-28 04:35:00 +00:00
const hist_source::bucket_t &bucket,
string &label_out)
2009-09-14 01:07:32 +00:00
{
2013-05-28 04:35:00 +00:00
hist_source::bucket_t::const_iterator iter;
int total = 0, errors = 0, warnings = 0;
time_t bucket_time = bucket_start_value;
struct tm *bucket_tm;
char buffer[128];
int len;
bucket_tm = gmtime((time_t *)&bucket_time);
if (bucket_tm) {
strftime(buffer, sizeof(buffer),
" %a %b %d %H:%M ",
bucket_tm);
}
else {
fprintf(stderr, "bad time %d\n", bucket_start_value);
buffer[0] = '\0';
}
for (iter = bucket.begin(); iter != bucket.end(); iter++) {
total += (int)iter->second;
switch (iter->first) {
case logline::LEVEL_FATAL:
case logline::LEVEL_ERROR:
case logline::LEVEL_CRITICAL:
errors += (int)iter->second;
break;
case logline::LEVEL_WARNING:
warnings += (int)iter->second;
break;
}
}
len = strlen(buffer);
snprintf(&buffer[len], sizeof(buffer) - len,
" %8d total %8d errors %8d warnings",
total, errors, warnings);
label_out = string(buffer);
2009-09-14 01:07:32 +00:00
};
};
static bool append_default_files(lnav_flags_t flag)
{
bool retval = true;
if (lnav_data.ld_flags & flag) {
2013-05-28 04:35:00 +00:00
pair<multimap<lnav_flags_t, string>::iterator,
multimap<lnav_flags_t, string>::iterator> range;
for (range = DEFAULT_FILES.equal_range(flag);
range.first != range.second;
2013-05-28 04:35:00 +00:00
range.first++) {
string path = range.first->second;
struct stat st;
if (access(path.c_str(), R_OK) == 0) {
2013-06-02 21:20:15 +00:00
auto_mem<char> abspath;
2013-05-28 04:35:00 +00:00
2013-06-02 21:20:15 +00:00
path = get_current_dir() + range.first->second;
if ((abspath = realpath(path.c_str(), NULL)) == NULL) {
perror("Unable to resolve path");
}
else {
2013-06-16 01:07:50 +00:00
lnav_data.ld_file_names.insert(make_pair(abspath.in(),
-1));
2013-06-02 21:20:15 +00:00
}
2013-05-28 04:35:00 +00:00
}
else if (stat(path.c_str(), &st) == 0) {
fprintf(stderr,
"error: cannot read -- %s%s\n",
get_current_dir().c_str(),
path.c_str());
retval = false;
}
}
2009-09-14 01:07:32 +00:00
}
return retval;
}
static void sigint(int sig)
{
lnav_data.ld_looping = false;
}
static void sigwinch(int sig)
{
lnav_data.ld_winched = true;
}
static void sigchld(int sig)
{
lnav_data.ld_child_terminated = true;
}
2009-09-14 01:07:32 +00:00
static void back_ten(int ten_minute)
{
textview_curses * tc = lnav_data.ld_view_stack.top();
logfile_sub_source *lss;
lss = dynamic_cast<logfile_sub_source *>(tc->get_sub_source());
if (!lss)
return;
2009-09-14 01:07:32 +00:00
time_t hour = rounddown_offset(lnav_data.ld_top_time,
2013-05-28 04:35:00 +00:00
60 * 60,
ten_minute * 10 * 60);
vis_line_t line = lss->find_from_time(hour);
2009-09-14 01:07:32 +00:00
--line;
lnav_data.ld_view_stack.top()->set_top(line);
}
static void update_view_name(void)
{
static const char *view_names[LNV__MAX] = {
"LOG",
"TEXT",
"HELP",
"HIST",
"GRAPH",
"DB",
"EXAMPLE",
};
status_field &sf = lnav_data.ld_top_source.statusview_value_for_field(
top_status_source::TSF_VIEW_NAME);
2013-06-16 01:07:50 +00:00
textview_curses * tc = lnav_data.ld_view_stack.top();
struct line_range lr(0);
sf.set_value("% 5s ", view_names[tc - lnav_data.ld_views]);
sf.get_value().get_attrs()[lr].insert(make_string_attr(
2013-06-16 01:07:50 +00:00
"style", A_REVERSE |
view_colors::ansi_color_pair(COLOR_BLUE, COLOR_WHITE)));
}
bool toggle_view(textview_curses *toggle_tc)
2009-09-14 01:07:32 +00:00
{
2013-05-28 04:35:00 +00:00
textview_curses *tc = lnav_data.ld_view_stack.top();
bool retval = false;
2009-09-14 01:07:32 +00:00
if (tc == toggle_tc) {
2013-05-28 04:35:00 +00:00
lnav_data.ld_view_stack.pop();
2009-09-14 01:07:32 +00:00
}
else {
2013-05-28 04:35:00 +00:00
lnav_data.ld_view_stack.push(toggle_tc);
retval = true;
2009-09-14 01:07:32 +00:00
}
tc = lnav_data.ld_view_stack.top();
tc->set_needs_update();
lnav_data.ld_scroll_broadcaster.invoke(tc);
update_view_name();
2009-09-14 01:07:32 +00:00
return retval;
}
2013-09-14 19:30:57 +00:00
void redo_search(lnav_view_t view_index)
{
2013-06-26 13:14:09 +00:00
textview_curses *tc = &lnav_data.ld_views[view_index];
2013-05-28 04:35:00 +00:00
tc->reload_data();
2013-06-26 13:14:09 +00:00
if (lnav_data.ld_search_child[view_index].get() != NULL) {
grep_proc *gp = lnav_data.ld_search_child[view_index]->get_grep_proc();
2013-05-28 04:35:00 +00:00
tc->match_reset();
gp->reset();
gp->queue_request(grep_line_t(0));
gp->start();
}
lnav_data.ld_scroll_broadcaster.invoke(tc);
}
/**
* Ensure that the view is on the top of the view stack.
2013-05-28 04:35:00 +00:00
*
* @param expected_tc The text view that should be on top.
*/
void ensure_view(textview_curses *expected_tc)
{
2013-05-28 04:35:00 +00:00
textview_curses *tc = lnav_data.ld_view_stack.top();
if (tc != expected_tc) {
2013-05-28 04:35:00 +00:00
toggle_view(expected_tc);
}
}
2013-05-28 04:35:00 +00:00
static void moveto_cluster(vis_line_t(bookmark_vector<vis_line_t>::*f) (
vis_line_t),
bookmark_type_t *bt,
vis_line_t top)
2009-09-14 01:07:32 +00:00
{
textview_curses *tc = lnav_data.ld_view_stack.top();
2013-07-10 03:52:20 +00:00
vis_bookmarks &bm = tc->get_bookmarks();
vis_line_t last_top(top);
2009-09-14 01:07:32 +00:00
2013-07-10 03:52:20 +00:00
while ((top = (bm[bt].*f)(top)) != -1) {
int diff = top - last_top;
2013-05-28 04:35:00 +00:00
2013-07-10 03:52:20 +00:00
if (diff > 1) {
tc->set_top(top);
return;
}
2013-07-10 03:52:20 +00:00
else if (diff < -1) {
2013-05-28 04:35:00 +00:00
last_top = top;
2013-07-10 03:52:20 +00:00
while ((top = (bm[bt].*f)(top)) != -1) {
if (std::abs(last_top - top) > 1)
break;
last_top = top;
2013-05-28 04:35:00 +00:00
}
2013-07-10 03:52:20 +00:00
tc->set_top(last_top);
return;
2013-05-28 04:35:00 +00:00
}
2013-07-10 03:52:20 +00:00
last_top = top;
2009-09-14 01:07:32 +00:00
}
2013-07-10 03:52:20 +00:00
flash();
2009-09-14 01:07:32 +00:00
}
static void check_for_clipboard(FILE **pfile, const char *execstr)
{
2013-05-28 04:35:00 +00:00
if (execstr == NULL || pfile == NULL || *pfile != NULL) {
return;
}
2013-05-28 04:35:00 +00:00
if ((*pfile = popen(execstr, "w")) != NULL && pclose(*pfile) == 0) {
*pfile = popen(execstr, "w");
}
else {
*pfile = NULL;
}
return;
}
2009-09-14 01:07:32 +00:00
/* XXX For one, this code is kinda crappy. For two, we should probably link
* directly with X so we don't need to have xclip installed and it'll work if
* we're ssh'd into a box.
*/
static void copy_to_xclip(void)
{
textview_curses *tc = lnav_data.ld_view_stack.top();
2013-05-28 04:35:00 +00:00
bookmark_vector<vis_line_t> &bv =
tc->get_bookmarks()[&textview_curses::BM_USER];
bookmark_vector<vis_line_t>::iterator iter;
2013-06-16 01:07:50 +00:00
FILE * pfile = NULL;
int line_count = 0;
2009-09-14 01:07:32 +00:00
string line;
2013-05-28 04:35:00 +00:00
/* XXX : Check if this is linux or MAC. Probably not the best solution but */
/* better than traversing the PATH to stat for the binaries or trying to */
/* forkexec. */
check_for_clipboard(&pfile, "xclip -i > /dev/null 2>&1");
check_for_clipboard(&pfile, "pbcopy > /dev/null 2>&1");
2013-05-28 04:35:00 +00:00
if (!pfile) {
flash();
lnav_data.ld_rl_view->set_value(
"error: Unable to copy to clipboard. "
"Make sure xclip or pbcopy is installed.");
return;
2009-09-14 01:07:32 +00:00
}
for (iter = bv.begin(); iter != bv.end(); iter++) {
tc->grep_value_for_line(*iter, line);
fprintf(pfile, "%s\n", line.c_str());
2013-06-15 14:11:45 +00:00
line_count += 1;
2009-09-14 01:07:32 +00:00
}
pclose(pfile);
2009-09-14 01:07:32 +00:00
pfile = NULL;
2013-06-15 14:11:45 +00:00
char buffer[128];
snprintf(buffer, sizeof(buffer),
"Copied " ANSI_BOLD("%d") " lines to the clipboard",
line_count);
lnav_data.ld_rl_view->set_value(buffer);
2009-09-14 01:07:32 +00:00
}
static void handle_paging_key(int ch)
{
2013-05-28 04:35:00 +00:00
textview_curses * tc = lnav_data.ld_view_stack.top();
logfile_sub_source *lss = NULL;
2013-05-28 04:35:00 +00:00
vis_bookmarks & bm = tc->get_bookmarks();
2009-09-14 01:07:32 +00:00
if (tc->handle_key(ch)) {
2013-05-28 04:35:00 +00:00
return;
2009-09-14 01:07:32 +00:00
}
lss = dynamic_cast<logfile_sub_source *>(tc->get_sub_source());
2009-09-14 01:07:32 +00:00
/* process the command keystroke */
switch (ch) {
case 'q':
case 'Q':
2013-06-16 01:07:50 +00:00
{
string msg = "";
2013-06-16 01:07:50 +00:00
if (tc == &lnav_data.ld_views[LNV_DB]) {
msg = HELP_MSG_2(v, V, "to switch to the SQL result view");
}
else if (tc == &lnav_data.ld_views[LNV_HISTOGRAM]) {
msg = HELP_MSG_2(i, I, "to switch to the histogram view");
}
else if (tc == &lnav_data.ld_views[LNV_TEXT]) {
msg = HELP_MSG_1(t, "to switch to the text file view");
}
else if (tc == &lnav_data.ld_views[LNV_GRAPH]) {
msg = HELP_MSG_1(g, "to switch to the graph view");
}
2013-06-16 01:07:50 +00:00
lnav_data.ld_rl_view->set_alt_value(msg);
}
2013-05-28 04:35:00 +00:00
lnav_data.ld_view_stack.pop();
if (lnav_data.ld_view_stack.empty() ||
(lnav_data.ld_view_stack.size() == 1 &&
lnav_data.ld_log_source.text_line_count() == 0)) {
lnav_data.ld_looping = false;
}
else {
tc = lnav_data.ld_view_stack.top();
tc->set_needs_update();
lnav_data.ld_scroll_broadcaster.invoke(tc);
update_view_name();
2013-05-28 04:35:00 +00:00
}
break;
2009-09-14 01:07:32 +00:00
case KEY_F(2):
if (xterm_mouse::is_available()) {
lnav_data.ld_mouse.set_enabled(!lnav_data.ld_mouse.is_enabled());
}
else {
lnav_data.ld_rl_view->set_value(
"error: mouse support is not available, make sure your TERM is set to "
"xterm or xterm-256color");
}
break;
2009-09-14 01:07:32 +00:00
case 'c':
2013-05-28 04:35:00 +00:00
copy_to_xclip();
break;
2009-09-14 01:07:32 +00:00
case 'C':
2013-05-28 04:35:00 +00:00
if (lss) {
lss->get_user_bookmarks()[&textview_curses::BM_USER].clear();
}
tc->get_bookmarks()[&textview_curses::BM_USER].clear();
tc->reload_data();
lnav_data.ld_rl_view->set_value("Cleared bookmarks");
2013-05-28 04:35:00 +00:00
break;
2009-09-14 01:07:32 +00:00
case 'e':
2013-05-28 04:35:00 +00:00
moveto_cluster(&bookmark_vector<vis_line_t>::next,
&logfile_sub_source::BM_ERRORS,
tc->get_top());
lnav_data.ld_rl_view->set_alt_value(HELP_MSG_2(
2013-06-16 01:07:50 +00:00
w, W,
"to move forward/backward through warning messages"));
2013-05-28 04:35:00 +00:00
break;
2009-09-14 01:07:32 +00:00
case 'E':
2013-05-28 04:35:00 +00:00
moveto_cluster(&bookmark_vector<vis_line_t>::prev,
&logfile_sub_source::BM_ERRORS,
tc->get_top());
lnav_data.ld_rl_view->set_alt_value(HELP_MSG_2(
2013-06-16 01:07:50 +00:00
w, W,
"to move forward/backward through warning messages"));
2013-05-28 04:35:00 +00:00
break;
2009-09-14 01:07:32 +00:00
case 'w':
2013-05-28 04:35:00 +00:00
moveto_cluster(&bookmark_vector<vis_line_t>::next,
&logfile_sub_source::BM_WARNINGS,
tc->get_top());
lnav_data.ld_rl_view->set_alt_value(HELP_MSG_2(
2013-06-16 01:07:50 +00:00
o, O,
"to move forward/backward an hour"));
2013-05-28 04:35:00 +00:00
break;
2009-09-14 01:07:32 +00:00
case 'W':
2013-05-28 04:35:00 +00:00
moveto_cluster(&bookmark_vector<vis_line_t>::prev,
&logfile_sub_source::BM_WARNINGS,
tc->get_top());
lnav_data.ld_rl_view->set_alt_value(HELP_MSG_2(
2013-06-16 01:07:50 +00:00
o, O,
"to move forward/backward an hour"));
2013-05-28 04:35:00 +00:00
break;
2009-09-14 01:07:32 +00:00
case 'n':
2013-05-28 04:35:00 +00:00
tc->set_top(bm[&textview_curses::BM_SEARCH].next(tc->get_top()));
2013-06-02 21:20:15 +00:00
lnav_data.ld_rl_view->set_alt_value(
"Press '" ANSI_BOLD(">") "' or '" ANSI_BOLD("<")
"' to scroll horizontally to a search result");
2013-05-28 04:35:00 +00:00
break;
2009-09-14 01:07:32 +00:00
case 'N':
2013-05-28 04:35:00 +00:00
tc->set_top(bm[&textview_curses::BM_SEARCH].prev(tc->get_top()));
2013-06-02 21:20:15 +00:00
lnav_data.ld_rl_view->set_alt_value(
"Press '" ANSI_BOLD(">") "' or '" ANSI_BOLD("<")
"' to scroll horizontally to a search result");
2013-05-28 04:35:00 +00:00
break;
2009-09-14 01:07:32 +00:00
case 'y':
2013-05-28 04:35:00 +00:00
tc->set_top(bm[&BM_QUERY].next(tc->get_top()));
break;
case 'Y':
2013-05-28 04:35:00 +00:00
tc->set_top(bm[&BM_QUERY].prev(tc->get_top()));
break;
case '>':
2013-05-28 04:35:00 +00:00
{
std::pair<int, int> range;
tc->horiz_shift(tc->get_top(),
tc->get_bottom(),
tc->get_left(),
"$search",
2013-05-28 04:35:00 +00:00
range);
if (range.second != INT_MAX) {
tc->set_left(range.second);
2013-06-15 14:11:45 +00:00
lnav_data.ld_rl_view->set_alt_value(
HELP_MSG_1(m, "to bookmark a line"));
2013-05-28 04:35:00 +00:00
}
else{
flash();
}
}
break;
case '<':
2013-05-28 04:35:00 +00:00
if (tc->get_left() == 0) {
flash();
}
else {
std::pair<int, int> range;
tc->horiz_shift(tc->get_top(),
tc->get_bottom(),
tc->get_left(),
"$search",
2013-05-28 04:35:00 +00:00
range);
if (range.first != -1) {
tc->set_left(range.first);
}
else{
tc->set_left(0);
}
2013-06-15 14:11:45 +00:00
lnav_data.ld_rl_view->set_alt_value(
HELP_MSG_1(m, "to bookmark a line"));
2013-05-28 04:35:00 +00:00
}
break;
2009-09-14 01:07:32 +00:00
case 'f':
2013-05-28 04:35:00 +00:00
if (tc == &lnav_data.ld_views[LNV_LOG]) {
tc->set_top(bm[&logfile_sub_source::BM_FILES].next(tc->get_top()));
}
else if (tc == &lnav_data.ld_views[LNV_TEXT]) {
textfile_sub_source &tss = lnav_data.ld_text_source;
if (!tss.tss_files.empty()) {
tss.tss_files.push_front(tss.tss_files.back());
tss.tss_files.pop_back();
2013-06-26 13:14:09 +00:00
redo_search(LNV_TEXT);
2013-05-28 04:35:00 +00:00
}
}
break;
2009-09-14 01:07:32 +00:00
case 'F':
2013-05-28 04:35:00 +00:00
if (tc == &lnav_data.ld_views[LNV_LOG]) {
tc->set_top(bm[&logfile_sub_source::BM_FILES].prev(tc->get_top()));
}
else if (tc == &lnav_data.ld_views[LNV_TEXT]) {
textfile_sub_source &tss = lnav_data.ld_text_source;
if (!tss.tss_files.empty()) {
tss.tss_files.push_back(tss.tss_files.front());
tss.tss_files.pop_front();
2013-06-26 13:14:09 +00:00
redo_search(LNV_TEXT);
2013-05-28 04:35:00 +00:00
}
}
break;
2009-09-14 01:07:32 +00:00
case 'z':
2013-05-28 04:35:00 +00:00
if (tc == &lnav_data.ld_views[LNV_HISTOGRAM]) {
if ((lnav_data.ld_hist_zoom + 1) >= HIST_ZOOM_LEVELS) {
flash();
}
else {
lnav_data.ld_hist_zoom += 1;
rebuild_hist(0, true);
}
2013-06-02 21:20:15 +00:00
lnav_data.ld_rl_view->set_alt_value(HELP_MSG_1(
2013-06-16 01:07:50 +00:00
I,
"to switch to the log view at the top displayed time"));
2013-05-28 04:35:00 +00:00
}
break;
2009-09-14 01:07:32 +00:00
case 'Z':
2013-05-28 04:35:00 +00:00
if (tc == &lnav_data.ld_views[LNV_HISTOGRAM]) {
if (lnav_data.ld_hist_zoom == 0) {
flash();
}
else {
lnav_data.ld_hist_zoom -= 1;
rebuild_hist(0, true);
}
2013-06-02 21:20:15 +00:00
lnav_data.ld_rl_view->set_alt_value(HELP_MSG_1(
2013-06-16 01:07:50 +00:00
I,
"to switch to the log view at the top displayed time"));
2013-05-28 04:35:00 +00:00
}
break;
2009-09-14 01:07:32 +00:00
case 'u':
2013-07-01 04:19:31 +00:00
lnav_data.ld_rl_view->set_alt_value(HELP_MSG_1(c, "to copy marked lines to the clipboard; ")
HELP_MSG_1(C, "to clear marked lines"));
2013-07-10 03:52:20 +00:00
moveto_cluster(&bookmark_vector<vis_line_t>::next,
&textview_curses::BM_USER,
tc->get_top());
2013-05-28 04:35:00 +00:00
break;
2009-09-14 01:07:32 +00:00
case 'U':
2013-07-10 03:52:20 +00:00
moveto_cluster(&bookmark_vector<vis_line_t>::prev,
&textview_curses::BM_USER,
tc->get_top());
2013-05-28 04:35:00 +00:00
break;
2009-09-14 01:07:32 +00:00
case 'm':
lnav_data.ld_last_user_mark[tc] = tc->get_top();
tc->toggle_user_mark(&textview_curses::BM_USER,
vis_line_t(lnav_data.ld_last_user_mark[tc]));
tc->reload_data();
lnav_data.ld_rl_view->set_alt_value(HELP_MSG_2(
u, U,
"to move forward/backward through user bookmarks"));
2013-05-28 04:35:00 +00:00
break;
2013-05-28 04:35:00 +00:00
case 'J':
if (lnav_data.ld_last_user_mark.find(tc) ==
lnav_data.ld_last_user_mark.end() ||
!tc->is_visible(vis_line_t(lnav_data.ld_last_user_mark[tc]))) {
lnav_data.ld_last_user_mark[tc] = tc->get_top();
}
else {
vis_line_t height;
unsigned long width;
2009-09-14 01:07:32 +00:00
tc->get_dimensions(height, width);
if (lnav_data.ld_last_user_mark[tc] > tc->get_bottom() - 2 &&
tc->get_top() + height < tc->get_inner_height()) {
tc->shift_top(vis_line_t(1));
2013-05-28 04:35:00 +00:00
}
if (lnav_data.ld_last_user_mark[tc] + 1 >=
tc->get_inner_height()) {
break;
}
lnav_data.ld_last_user_mark[tc] += 1;
2013-05-28 04:35:00 +00:00
}
tc->toggle_user_mark(&textview_curses::BM_USER,
vis_line_t(lnav_data.ld_last_user_mark[tc]));
tc->reload_data();
lnav_data.ld_rl_view->set_alt_value(HELP_MSG_1(
c,
"to copy marked lines to the clipboard"));
2013-05-28 04:35:00 +00:00
break;
2013-05-28 04:35:00 +00:00
case 'K':
{
2013-05-28 04:35:00 +00:00
int new_mark;
if (lnav_data.ld_last_user_mark.find(tc) ==
lnav_data.ld_last_user_mark.end() ||
!tc->is_visible(vis_line_t(lnav_data.ld_last_user_mark[tc]))) {
lnav_data.ld_last_user_mark[tc] = -1;
new_mark = tc->get_top();
}
else {
new_mark = lnav_data.ld_last_user_mark[tc];
}
tc->toggle_user_mark(&textview_curses::BM_USER,
vis_line_t(new_mark));
2013-05-28 04:35:00 +00:00
if (new_mark == tc->get_top()) {
tc->shift_top(vis_line_t(-1));
}
if (new_mark > 0) {
lnav_data.ld_last_user_mark[tc] = new_mark - 1;
}
else {
lnav_data.ld_last_user_mark[tc] = new_mark;
flash();
}
tc->reload_data();
2013-06-02 21:20:15 +00:00
lnav_data.ld_rl_view->set_alt_value(HELP_MSG_1(
2013-06-16 01:07:50 +00:00
c,
"to copy marked lines to the clipboard"));
}
break;
2013-05-28 04:35:00 +00:00
case 'M':
if (lnav_data.ld_last_user_mark.find(tc) ==
lnav_data.ld_last_user_mark.end()) {
flash();
}
else {
int start_line = min((int)tc->get_top(),
lnav_data.ld_last_user_mark[tc] + 1);
int end_line = max((int)tc->get_top(),
lnav_data.ld_last_user_mark[tc] - 1);
tc->toggle_user_mark(&textview_curses::BM_USER,
vis_line_t(start_line),
vis_line_t(end_line));
tc->reload_data();
2013-05-28 04:35:00 +00:00
}
break;
2013-06-02 21:20:15 +00:00
case 'S':
{
bookmark_vector<vis_line_t>::iterator iter;
2013-05-28 04:35:00 +00:00
for (iter = bm[&textview_curses::BM_SEARCH].begin();
iter != bm[&textview_curses::BM_SEARCH].end();
++iter) {
tc->toggle_user_mark(&textview_curses::BM_USER, *iter);
}
2013-05-28 04:35:00 +00:00
lnav_data.ld_last_user_mark[tc] = -1;
tc->reload_data();
}
break;
2013-05-28 04:35:00 +00:00
2009-09-14 01:07:32 +00:00
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
2013-05-28 04:35:00 +00:00
if (lss) {
int ten_minute = (ch - '0') * 10 * 60;
time_t hour = rounddown(lnav_data.ld_top_time +
(60 * 60) -
ten_minute +
1,
60 * 60);
vis_line_t line = lss->find_from_time(hour + ten_minute);
tc->set_top(line);
}
break;
2009-09-14 01:07:32 +00:00
case '!':
2013-05-28 04:35:00 +00:00
back_ten(1);
break;
2009-09-14 01:07:32 +00:00
case '@':
2013-05-28 04:35:00 +00:00
back_ten(2);
break;
2009-09-14 01:07:32 +00:00
case '#':
2013-05-28 04:35:00 +00:00
back_ten(3);
break;
2009-09-14 01:07:32 +00:00
case '$':
2013-05-28 04:35:00 +00:00
back_ten(4);
break;
2009-09-14 01:07:32 +00:00
case '%':
2013-05-28 04:35:00 +00:00
back_ten(5);
break;
2009-09-14 01:07:32 +00:00
case '^':
2013-05-28 04:35:00 +00:00
back_ten(6);
break;
2009-09-14 01:07:32 +00:00
case '9':
if (lss) {
double tenth = ((double)tc->get_inner_height()) / 10.0;
tc->shift_top(vis_line_t(tenth));
}
break;
case '(':
if (lss) {
double tenth = ((double)tc->get_inner_height()) / 10.0;
tc->shift_top(vis_line_t(-tenth));
}
break;
2009-09-14 01:07:32 +00:00
case '0':
2013-05-28 04:35:00 +00:00
if (lss) {
time_t first_time = lnav_data.ld_top_time;
int step = 24 * 60 * 60;
vis_line_t line =
lss->find_from_time(roundup_size(first_time, step));
2009-09-14 01:07:32 +00:00
2013-05-28 04:35:00 +00:00
tc->set_top(line);
}
break;
2009-09-14 01:07:32 +00:00
case ')':
2013-05-28 04:35:00 +00:00
if (lss) {
time_t day = rounddown(lnav_data.ld_top_time, 24 * 60 * 60);
vis_line_t line = lss->find_from_time(day);
2009-09-14 01:07:32 +00:00
2013-05-28 04:35:00 +00:00
--line;
tc->set_top(line);
}
break;
2009-09-14 01:07:32 +00:00
case 'D':
case 'O':
2013-05-28 04:35:00 +00:00
if (tc->get_top() == 0) {
flash();
}
else if (lss) {
int step = ch == 'D' ? (24 * 60 * 60) : (60 * 60);
time_t top_time = lnav_data.ld_top_time;
vis_line_t line = lss->find_from_time(top_time - step);
if (line != 0) {
--line;
}
tc->set_top(line);
2013-06-15 14:11:45 +00:00
lnav_data.ld_rl_view->set_alt_value(HELP_MSG_1(/, "to search"));
2013-05-28 04:35:00 +00:00
}
break;
2009-09-14 01:07:32 +00:00
case 'd':
case 'o':
2013-05-28 04:35:00 +00:00
if (lss) {
int step = ch == 'd' ? (24 * 60 * 60) : (60 * 60);
vis_line_t line =
lss->find_from_time(lnav_data.ld_top_time + step);
2009-09-14 01:07:32 +00:00
2013-05-28 04:35:00 +00:00
tc->set_top(line);
2013-06-15 14:11:45 +00:00
lnav_data.ld_rl_view->set_alt_value(HELP_MSG_1(/, "to search"));
2013-05-28 04:35:00 +00:00
}
break;
2009-09-14 01:07:32 +00:00
case 's':
2013-05-28 04:35:00 +00:00
lnav_data.ld_log_source.toggle_scrub();
tc->reload_data();
break;
2009-09-14 01:07:32 +00:00
case ':':
2013-06-06 02:34:48 +00:00
if (lnav_data.ld_views[LNV_LOG].get_inner_height() > 0) {
2013-06-16 01:07:50 +00:00
logfile_sub_source &lss = lnav_data.ld_log_source;
textview_curses & log_view = lnav_data.ld_views[LNV_LOG];
content_line_t cl = lss.at(log_view.get_top());
logfile * lf = lss.find(cl);
2013-07-09 04:09:35 +00:00
logfile::iterator ll = lf->begin() + cl;
2013-06-06 02:34:48 +00:00
2013-09-14 19:30:57 +00:00
lnav_data.ld_rl_view->clear_possibilities(LNM_COMMAND, "colname");
2013-06-06 02:34:48 +00:00
2013-09-14 19:30:57 +00:00
if (!ll->is_continued()) {
std::string line = lf->read_line(ll);
struct line_range body;
string_attrs_t sa;
std::vector<logline_value> line_values;
2013-06-06 02:34:48 +00:00
2013-09-14 19:30:57 +00:00
lf->get_format()->annotate(line, sa, line_values);
2013-06-06 02:34:48 +00:00
2013-09-14 19:30:57 +00:00
body = find_string_attr_range(sa, "body");
if (body.lr_end != -1) {
line = line.substr(body.lr_start);
}
2013-06-06 02:34:48 +00:00
2013-09-14 19:30:57 +00:00
data_scanner ds(line);
data_parser dp(&ds);
dp.parse();
column_namer namer;
for (data_parser::element_list_t::iterator iter =
2013-06-16 01:07:50 +00:00
dp.dp_pairs.begin();
2013-09-14 19:30:57 +00:00
iter != dp.dp_pairs.end();
++iter) {
std::string colname = dp.get_element_string(iter->e_sub_elements->front());
2013-06-06 02:34:48 +00:00
2013-09-14 19:30:57 +00:00
colname = namer.add_column(colname);
lnav_data.ld_rl_view->add_possibility(LNM_COMMAND, "colname",
colname);
}
2013-06-06 02:34:48 +00:00
}
2013-07-09 04:09:35 +00:00
lnav_data.ld_rl_view->clear_possibilities(LNM_COMMAND, "line-time");
{
struct timeval tv = lf->get_time_offset();
char buffer[64];
sql_strftime(buffer, sizeof(buffer),
ll->get_time(), ll->get_millis());
lnav_data.ld_rl_view->add_possibility(LNM_COMMAND,
"line-time",
buffer);
sql_strftime(buffer, sizeof(buffer),
ll->get_time() - tv.tv_sec,
ll->get_millis() - (tv.tv_usec / 1000));
lnav_data.ld_rl_view->add_possibility(LNM_COMMAND,
"line-time",
buffer);
}
2013-06-06 02:34:48 +00:00
}
2013-05-28 04:35:00 +00:00
lnav_data.ld_mode = LNM_COMMAND;
lnav_data.ld_rl_view->focus(LNM_COMMAND, ":");
lnav_data.ld_bottom_source.set_prompt("Enter an lnav command: "
"(Press CTRL+] to abort)");
2013-05-28 04:35:00 +00:00
break;
2009-09-14 01:07:32 +00:00
case '/':
2013-05-28 04:35:00 +00:00
lnav_data.ld_mode = LNM_SEARCH;
2013-07-24 14:42:16 +00:00
lnav_data.ld_previous_search = lnav_data.ld_last_search[lnav_data.ld_view_stack.top() - lnav_data.ld_views];
2013-05-28 04:35:00 +00:00
lnav_data.ld_search_start_line = lnav_data.ld_view_stack.top()->
get_top();
lnav_data.ld_rl_view->focus(LNM_SEARCH, "/");
lnav_data.ld_bottom_source.set_prompt(
"Enter a regular expression to search for: "
"(Press CTRL+] to abort)");
2013-05-28 04:35:00 +00:00
break;
2009-09-14 01:07:32 +00:00
case ';':
if (tc == &lnav_data.ld_views[LNV_LOG] ||
tc == &lnav_data.ld_views[LNV_DB]) {
2013-05-28 04:35:00 +00:00
textview_curses &log_view = lnav_data.ld_views[LNV_LOG];
2013-05-28 04:35:00 +00:00
lnav_data.ld_mode = LNM_SQL;
2013-06-06 02:34:48 +00:00
setup_logline_table();
2013-05-28 04:35:00 +00:00
lnav_data.ld_rl_view->focus(LNM_SQL, ";");
2013-05-28 04:35:00 +00:00
lnav_data.ld_bottom_source.update_loading(0, 0);
lnav_data.ld_status[LNS_BOTTOM].do_update();
2013-05-28 04:35:00 +00:00
field_overlay_source *fos;
2013-05-28 04:35:00 +00:00
fos = (field_overlay_source *)log_view.get_overlay_source();
fos->fos_active_prev = fos->fos_active;
if (!fos->fos_active) {
fos->fos_active = true;
tc->reload_data();
}
lnav_data.ld_bottom_source.set_prompt(
"Enter an SQL query: (Press CTRL+] to abort)");
2013-05-28 04:35:00 +00:00
}
break;
2009-09-14 01:07:32 +00:00
case 'p':
field_overlay_source *fos;
2013-05-28 04:35:00 +00:00
fos =
(field_overlay_source *)lnav_data.ld_views[LNV_LOG].
get_overlay_source();
fos->fos_active = !fos->fos_active;
tc->reload_data();
break;
case 't':
if (lnav_data.ld_text_source.current_file() == NULL) {
flash();
2013-06-15 14:11:45 +00:00
lnav_data.ld_rl_view->set_value("No text files loaded");
}
else if (toggle_view(&lnav_data.ld_views[LNV_TEXT])) {
lnav_data.ld_rl_view->set_alt_value(HELP_MSG_2(
2013-06-16 01:07:50 +00:00
f, F,
"to switch to the next/previous file"));
}
2013-05-28 04:35:00 +00:00
break;
case 'T':
lnav_data.ld_log_source.toggle_time_offset();
tc->reload_data();
break;
2009-09-14 01:07:32 +00:00
case 'i':
if (toggle_view(&lnav_data.ld_views[LNV_HISTOGRAM])) {
lnav_data.ld_rl_view->set_alt_value(
HELP_MSG_2(z, Z, "to zoom in/out"));
}
else {
lnav_data.ld_rl_view->set_alt_value("");
}
2013-05-28 04:35:00 +00:00
break;
2009-09-14 01:07:32 +00:00
case 'I':
2013-05-28 04:35:00 +00:00
{
time_t log_top = lnav_data.ld_top_time;
time_t hist_top =
lnav_data.ld_hist_source.value_for_row(tc->get_top());
if (toggle_view(&lnav_data.ld_views[LNV_HISTOGRAM])) {
hist_source &hs = lnav_data.ld_hist_source;
tc = lnav_data.ld_view_stack.top();
tc->set_top(hs.row_for_value(log_top));
}
else {
tc = &lnav_data.ld_views[LNV_LOG];
lss = &lnav_data.ld_log_source;
tc->set_top(lss->find_from_time(hist_top));
tc->set_needs_update();
}
}
break;
2009-09-14 01:07:32 +00:00
case 'g':
2013-05-28 04:35:00 +00:00
toggle_view(&lnav_data.ld_views[LNV_GRAPH]);
break;
2009-09-14 01:07:32 +00:00
case '?':
2013-05-28 04:35:00 +00:00
toggle_view(&lnav_data.ld_views[LNV_HELP]);
break;
2009-09-14 01:07:32 +00:00
case 'v':
2013-05-28 04:35:00 +00:00
toggle_view(&lnav_data.ld_views[LNV_DB]);
break;
case 'V':
2013-05-28 04:35:00 +00:00
{
textview_curses *db_tc = &lnav_data.ld_views[LNV_DB];
db_label_source &dls = lnav_data.ld_db_rows;
hist_source & hs = lnav_data.ld_db_source;
if (toggle_view(db_tc)) {
unsigned int lpc;
for (lpc = 0; lpc < dls.dls_headers.size(); lpc++) {
if (dls.dls_headers[lpc] != "log_line") {
2013-05-28 04:35:00 +00:00
continue;
}
char linestr[64];
int line_number = (int)tc->get_top();
unsigned int row;
snprintf(linestr, sizeof(linestr), "%d", line_number);
for (row = 0; row < dls.dls_rows.size(); row++) {
if (strcmp(dls.dls_rows[row][lpc].c_str(),
linestr) == 0) {
vis_line_t db_line(hs.row_for_value(row));
db_tc->set_top(db_line);
db_tc->set_needs_update();
break;
}
}
break;
}
}
else {
int db_row = hs.value_for_row(db_tc->get_top());
unsigned int lpc;
for (lpc = 0; lpc < dls.dls_headers.size(); lpc++) {
if (dls.dls_headers[lpc] != "log_line") {
2013-05-28 04:35:00 +00:00
continue;
}
unsigned int line_number;
tc = &lnav_data.ld_views[LNV_LOG];
if (sscanf(dls.dls_rows[db_row][lpc].c_str(),
"%d",
&line_number) &&
line_number < tc->listview_rows(*tc)) {
tc->set_top(vis_line_t(line_number));
tc->set_needs_update();
}
break;
}
}
}
break;
2009-09-14 01:07:32 +00:00
case '\t':
if (tc == &lnav_data.ld_views[LNV_DB])
{
hist_source &hs = lnav_data.ld_db_source;
db_label_source &dls = lnav_data.ld_db_rows;
std::vector<bucket_type_t> &displayed = hs.get_displayed_buckets();
std::vector<bool>::iterator start_iter, iter;
start_iter = dls.dls_headers_to_graph.begin();
if (!displayed.empty()) {
advance(start_iter, (int)displayed[0] + 1);
}
displayed.clear();
iter = find(start_iter,
dls.dls_headers_to_graph.end(),
true);
if (iter != dls.dls_headers_to_graph.end()) {
bucket_type_t type;
type = bucket_type_t(distance(dls.dls_headers_to_graph.begin(), iter));
displayed.push_back(type);
}
tc->reload_data();
}
break;
// XXX I'm sure there must be a better way to handle the difference between
// iterator and reverse_iterator.
case KEY_BTAB:
if (tc == &lnav_data.ld_views[LNV_DB])
{
hist_source &hs = lnav_data.ld_db_source;
db_label_source &dls = lnav_data.ld_db_rows;
std::vector<bucket_type_t> &displayed = hs.get_displayed_buckets();
std::vector<bool>::reverse_iterator start_iter, iter;
start_iter = dls.dls_headers_to_graph.rbegin();
if (!displayed.empty()) {
advance(start_iter, dls.dls_headers_to_graph.size() - (int)displayed[0]);
}
displayed.clear();
iter = find(start_iter,
dls.dls_headers_to_graph.rend(),
true);
if (iter != dls.dls_headers_to_graph.rend()) {
bucket_type_t type;
type = bucket_type_t(distance(dls.dls_headers_to_graph.begin(), --iter.base()));
displayed.push_back(type);
}
tc->reload_data();
}
break;
2011-06-11 17:26:52 +00:00
case 'x':
2013-05-28 04:35:00 +00:00
if (tc == &lnav_data.ld_views[LNV_LOG]) {
lnav_data.ld_log_source.toggle_user_mark(&BM_EXAMPLE,
vis_line_t(tc->get_top()));
}
break;
2011-06-11 17:26:52 +00:00
case 'X':
execute_command("close");
break;
2011-06-11 17:26:52 +00:00
case '\\':
2013-05-28 04:35:00 +00:00
{
vis_bookmarks &bm = tc->get_bookmarks();
string ex;
for (bookmark_vector<vis_line_t>::iterator iter =
bm[&BM_EXAMPLE].begin();
iter != bm[&BM_EXAMPLE].end();
++iter) {
string line;
tc->get_sub_source()->text_value_for_line(*tc, *iter, line);
ex += line + "\n";
}
lnav_data.ld_views[LNV_EXAMPLE].set_sub_source(new plain_text_source(
ex));
ensure_view(&lnav_data.ld_views[LNV_EXAMPLE]);
}
break;
2011-06-11 17:26:52 +00:00
2013-06-02 21:20:15 +00:00
case 'r':
2013-06-16 01:07:50 +00:00
lnav_data.ld_session_file_index =
(lnav_data.ld_session_file_index + 1) %
2013-06-02 21:20:15 +00:00
lnav_data.ld_session_file_names.size();
reset_session();
load_session();
rebuild_indexes(true);
break;
2013-06-16 01:07:50 +00:00
2013-06-02 21:20:15 +00:00
case 'R':
2013-06-16 01:07:50 +00:00
if (lnav_data.ld_session_file_index == 0) {
lnav_data.ld_session_file_index =
lnav_data.ld_session_file_names.size() - 1;
}
else{
2013-06-02 21:20:15 +00:00
lnav_data.ld_session_file_index -= 1;
2013-06-16 01:07:50 +00:00
}
2013-06-02 21:20:15 +00:00
reset_session();
load_session();
rebuild_indexes(true);
break;
2013-06-16 01:07:50 +00:00
2013-06-02 21:20:15 +00:00
case KEY_CTRL_R:
reset_session();
rebuild_indexes(true);
lnav_data.ld_rl_view->set_alt_value(HELP_MSG_2(
2013-06-16 01:07:50 +00:00
r, R,
"to restore the next/previous session"));
2013-06-02 21:20:15 +00:00
break;
2009-09-14 01:07:32 +00:00
default:
2013-05-28 04:35:00 +00:00
fprintf(stderr, "unhandled %d\n", ch);
2013-06-15 14:11:45 +00:00
lnav_data.ld_rl_view->set_value("Unrecognized keystroke, press "
ANSI_BOLD("?")
" to view help");
2013-05-28 04:35:00 +00:00
flash();
break;
2009-09-14 01:07:32 +00:00
}
}
static void handle_rl_key(int ch)
{
switch (ch) {
case KEY_PPAGE:
case KEY_NPAGE:
2013-05-28 04:35:00 +00:00
handle_paging_key(ch);
break;
2009-09-14 01:07:32 +00:00
2013-07-24 14:42:16 +00:00
case KEY_CTRL_RBRACKET:
lnav_data.ld_rl_view->abort();
break;
2009-09-14 01:07:32 +00:00
default:
2013-05-28 04:35:00 +00:00
lnav_data.ld_rl_view->handle_key(ch);
break;
2009-09-14 01:07:32 +00:00
}
}
2012-07-13 16:26:47 +00:00
readline_context::command_map_t lnav_commands;
2009-09-14 01:07:32 +00:00
2013-06-02 21:20:15 +00:00
string execute_command(string cmdline)
{
stringstream ss(cmdline);
vector<string> args;
2013-05-28 04:35:00 +00:00
string buf, msg;
while (ss >> buf) {
2013-05-28 04:35:00 +00:00
args.push_back(buf);
}
if (args.size() > 0) {
2013-05-28 04:35:00 +00:00
readline_context::command_map_t::iterator iter;
2013-05-28 04:35:00 +00:00
if ((iter = lnav_commands.find(args[0])) ==
lnav_commands.end()) {
msg = "error: unknown command - " + args[0];
}
else {
msg = iter->second(cmdline, args);
}
}
return msg;
}
static void execute_file(string path)
{
ifstream cmd_file(path.c_str());
if (cmd_file.is_open()) {
2013-05-28 04:35:00 +00:00
int line_number = 0;
string line;
2013-05-28 04:35:00 +00:00
while (getline(cmd_file, line)) {
line_number += 1;
2013-05-28 04:35:00 +00:00
if (line.empty()) {
continue;
}
if (line[0] == '#') {
continue;
}
2013-05-28 04:35:00 +00:00
string rc = execute_command(line);
2013-05-28 04:35:00 +00:00
fprintf(stderr,
"%s:%d:execute result -- %s\n",
path.c_str(),
line_number,
rc.c_str());
}
}
}
2013-06-06 02:34:48 +00:00
int sql_callback(sqlite3_stmt *stmt)
2009-10-06 21:14:49 +00:00
{
logfile_sub_source &lss = lnav_data.ld_log_source;
2013-05-28 04:35:00 +00:00
db_label_source & dls = lnav_data.ld_db_rows;
hist_source & hs = lnav_data.ld_db_source;
int ncols = sqlite3_column_count(stmt);
2009-10-06 21:14:49 +00:00
int row_number;
int lpc, retval = 0;
row_number = dls.dls_rows.size();
dls.dls_rows.resize(row_number + 1);
if (dls.dls_headers.empty()) {
2013-05-28 04:35:00 +00:00
for (lpc = 0; lpc < ncols; lpc++) {
int type = sqlite3_column_type(stmt, lpc);
string colname = sqlite3_column_name(stmt, lpc);
2013-05-28 04:35:00 +00:00
bool graphable;
graphable = ((type == SQLITE_INTEGER || type == SQLITE_FLOAT) &&
!binary_search(lnav_data.ld_db_key_names.begin(),
lnav_data.ld_db_key_names.end(),
colname));
2013-05-28 04:35:00 +00:00
dls.push_header(colname, type, graphable);
if (graphable) {
2013-05-28 04:35:00 +00:00
hs.set_role_for_type(bucket_type_t(lpc),
view_colors::singleton().
next_plain_highlight());
}
}
2009-10-06 21:14:49 +00:00
}
for (lpc = 0; lpc < ncols; lpc++) {
2013-05-28 04:35:00 +00:00
const char *value = (const char *)sqlite3_column_text(stmt, lpc);
double num_value = 0.0;
if (value == NULL) {
value = "<NULL>";
}
dls.push_column(value);
if (dls.dls_headers[lpc] == "log_line") {
2013-05-28 04:35:00 +00:00
int line_number = -1;
if (sscanf(value, "%d", &line_number) == 1) {
lss.text_mark(&BM_QUERY, line_number, true);
}
}
if (dls.dls_headers_to_graph[lpc]) {
sscanf(value, "%lf", &num_value);
hs.add_value(row_number, bucket_type_t(lpc), num_value);
}
else {
2013-05-28 04:35:00 +00:00
hs.add_empty_value(row_number);
}
2009-10-06 21:14:49 +00:00
}
2009-10-06 21:14:49 +00:00
return retval;
}
void execute_search(lnav_view_t view, const std::string &regex)
{
auto_ptr<grep_highlighter> &gc = lnav_data.ld_search_child[view];
2013-06-16 01:07:50 +00:00
textview_curses & tc = lnav_data.ld_views[view];
if ((gc.get() == NULL) || (regex != lnav_data.ld_last_search[view])) {
const char *errptr;
pcre * code;
int eoff;
2013-06-26 13:14:09 +00:00
tc.match_reset();
if (regex.empty() && gc.get() != NULL) {
tc.grep_begin(*(gc->get_grep_proc()));
tc.grep_end(*(gc->get_grep_proc()));
}
gc.reset();
fprintf(stderr, "start search for: %s\n", regex.c_str());
if (regex.empty()) {
lnav_data.ld_bottom_source.grep_error("");
}
else if ((code = pcre_compile(regex.c_str(),
PCRE_CASELESS,
&errptr,
&eoff,
NULL)) == NULL) {
lnav_data.ld_bottom_source.
grep_error("regexp error: " + string(errptr));
}
else {
textview_curses::highlighter
hl(code, false, view_colors::VCR_SEARCH);
lnav_data.ld_bottom_source.set_prompt("");
textview_curses::highlight_map_t &hm = tc.get_highlights();
hm["$search"] = hl;
auto_ptr<grep_proc> gp(new grep_proc(code,
tc,
lnav_data.ld_max_fd,
lnav_data.ld_read_fds));
gp->queue_request(grep_line_t(tc.get_top()));
if (tc.get_top() > 0) {
gp->queue_request(grep_line_t(0),
grep_line_t(tc.get_top()));
}
gp->start();
2013-07-23 12:55:08 +00:00
gp->set_sink(&tc);
tc.set_follow_search(true);
2013-06-16 01:07:50 +00:00
auto_ptr<grep_highlighter> gh(new grep_highlighter(gp, "$search",
hm));
gc = gh;
}
}
2013-06-16 01:07:50 +00:00
lnav_data.ld_last_search[view] = regex;
}
static void rl_search_internal(void *dummy, readline_curses *rc, bool complete = false)
2009-09-14 01:07:32 +00:00
{
2013-07-24 14:42:16 +00:00
string term_val;
2009-09-14 01:07:32 +00:00
string name;
switch (lnav_data.ld_mode) {
case LNM_SEARCH:
name = "$search";
2013-05-28 04:35:00 +00:00
break;
2009-09-14 01:07:32 +00:00
case LNM_CAPTURE:
2013-05-28 04:35:00 +00:00
assert(0);
name = "$capture";
2013-05-28 04:35:00 +00:00
break;
2009-09-14 01:07:32 +00:00
case LNM_COMMAND:
2013-05-28 04:35:00 +00:00
return;
2009-09-14 01:07:32 +00:00
case LNM_SQL:
2013-07-24 14:42:16 +00:00
term_val = rc->get_value() + ";";
if (!sqlite3_complete(term_val.c_str())) {
2013-05-28 04:35:00 +00:00
lnav_data.ld_bottom_source.
grep_error("sql error: incomplete statement");
}
else {
auto_mem<sqlite3_stmt> stmt(sqlite3_finalize);
int retcode;
retcode = sqlite3_prepare_v2(lnav_data.ld_db,
rc->get_value().c_str(),
-1,
stmt.out(),
NULL);
if (retcode != SQLITE_OK) {
const char *errmsg = sqlite3_errmsg(lnav_data.ld_db);
lnav_data.ld_bottom_source.
grep_error(string("sql error: ") + string(errmsg));
}
else {
2013-06-16 01:07:50 +00:00
lnav_data.ld_bottom_source.grep_error("");
2013-05-28 04:35:00 +00:00
}
}
return;
2009-09-14 01:07:32 +00:00
default:
2013-05-28 04:35:00 +00:00
assert(0);
break;
2009-09-14 01:07:32 +00:00
}
2013-06-16 01:07:50 +00:00
textview_curses *tc = lnav_data.ld_view_stack.top();
lnav_view_t index = (lnav_view_t)(tc - lnav_data.ld_views);
2013-05-28 04:35:00 +00:00
if (!complete)
tc->set_top(lnav_data.ld_search_start_line);
execute_search(index, rc->get_value());
2009-09-14 01:07:32 +00:00
}
static void rl_search(void *dummy, readline_curses *rc)
{
rl_search_internal(dummy, rc);
}
2013-07-24 14:42:16 +00:00
static void rl_abort(void *dummy, readline_curses *rc)
{
textview_curses *tc = lnav_data.ld_view_stack.top();
lnav_view_t index = (lnav_view_t)(tc - lnav_data.ld_views);
lnav_data.ld_bottom_source.set_prompt("");
lnav_data.ld_bottom_source.grep_error("");
switch (lnav_data.ld_mode) {
case LNM_SEARCH:
tc->set_top(lnav_data.ld_search_start_line);
execute_search(index, lnav_data.ld_previous_search);
break;
case LNM_SQL:
{
field_overlay_source *fos;
fos =
(field_overlay_source *)lnav_data.ld_views[LNV_LOG].
get_overlay_source();
fos->fos_active = fos->fos_active_prev;
tc->reload_data();
break;
}
default:
break;
}
lnav_data.ld_mode = LNM_PAGING;
}
2009-09-14 01:07:32 +00:00
static void rl_callback(void *dummy, readline_curses *rc)
{
lnav_data.ld_bottom_source.set_prompt("");
2013-06-16 01:07:50 +00:00
2009-09-14 01:07:32 +00:00
switch (lnav_data.ld_mode) {
2009-10-14 19:42:58 +00:00
case LNM_PAGING:
2013-05-28 04:35:00 +00:00
assert(0);
break;
2009-09-14 01:07:32 +00:00
case LNM_COMMAND:
2013-05-28 04:35:00 +00:00
lnav_data.ld_mode = LNM_PAGING;
rc->set_value(execute_command(rc->get_value()));
rc->set_alt_value("");
2013-05-28 04:35:00 +00:00
break;
2009-09-14 01:07:32 +00:00
case LNM_SEARCH:
case LNM_CAPTURE:
rl_search_internal(dummy, rc, true);
2013-05-28 04:35:00 +00:00
if (rc->get_value().size() > 0) {
lnav_data.ld_view_stack.top()->set_follow_search(false);
lnav_data.ld_rl_view->
add_possibility(LNM_COMMAND, "filter", rc->get_value());
rc->set_value("search: " + rc->get_value());
rc->set_alt_value(HELP_MSG_2(
2013-06-16 01:07:50 +00:00
n, N,
"to move forward/backward through search results"));
2013-05-28 04:35:00 +00:00
}
lnav_data.ld_mode = LNM_PAGING;
break;
2009-09-14 01:07:32 +00:00
case LNM_SQL:
2013-05-28 04:35:00 +00:00
{
db_label_source & dls = lnav_data.ld_db_rows;
hist_source & hs = lnav_data.ld_db_source;
auto_mem<sqlite3_stmt> stmt(sqlite3_finalize);
int retcode;
lnav_data.ld_bottom_source.grep_error("");
hs.clear();
hs.get_displayed_buckets().clear();
2013-05-28 04:35:00 +00:00
dls.clear();
2013-06-06 02:34:48 +00:00
retcode = sqlite3_prepare_v2(lnav_data.ld_db.in(),
2013-05-28 04:35:00 +00:00
rc->get_value().c_str(),
-1,
stmt.out(),
NULL);
if (retcode != SQLITE_OK) {
const char *errmsg = sqlite3_errmsg(lnav_data.ld_db);
rc->set_value(errmsg);
rc->set_alt_value("");
2013-05-28 04:35:00 +00:00
}
else if (stmt == NULL) {
rc->set_value("");
rc->set_alt_value("");
2013-05-28 04:35:00 +00:00
}
else {
bool done = false;
lnav_data.ld_log_source.text_clear_marks(&BM_QUERY);
while (!done) {
retcode = sqlite3_step(stmt.in());
switch (retcode) {
case SQLITE_OK:
case SQLITE_DONE:
done = true;
break;
case SQLITE_ROW:
sql_callback(stmt.in());
break;
default:
{
const char *errmsg;
fprintf(stderr, "code %d\n", retcode);
errmsg = sqlite3_errmsg(lnav_data.ld_db);
rc->set_value(errmsg);
done = true;
}
break;
}
}
2013-05-28 04:35:00 +00:00
if (retcode == SQLITE_DONE) {
lnav_data.ld_views[LNV_LOG].reload_data();
lnav_data.ld_views[LNV_DB].reload_data();
lnav_data.ld_views[LNV_DB].set_left(0);
if (dls.dls_rows.size() > 0) {
char row_count[32];
2013-05-28 04:35:00 +00:00
ensure_view(&lnav_data.ld_views[LNV_DB]);
snprintf(row_count, sizeof(row_count),
2013-06-15 14:11:45 +00:00
ANSI_BOLD("%'d") " row(s) matched",
(int)dls.dls_rows.size());
rc->set_value(row_count);
rc->set_alt_value(HELP_MSG_2(
2013-06-16 01:07:50 +00:00
y, Y,
"to move forward/backward through query results "
"in the log view"));
}
else {
rc->set_value("No rows matched");
rc->set_alt_value("");
2013-05-28 04:35:00 +00:00
}
}
lnav_data.ld_bottom_source.update_loading(0, 0);
lnav_data.ld_status[LNS_BOTTOM].do_update();
}
field_overlay_source *fos;
fos =
(field_overlay_source *)lnav_data.ld_views[LNV_LOG].
get_overlay_source();
fos->fos_active = fos->fos_active_prev;
2013-06-26 13:14:09 +00:00
redo_search(LNV_DB);
2013-05-28 04:35:00 +00:00
lnav_data.ld_views[LNV_LOG].reload_data();
}
lnav_data.ld_mode = LNM_PAGING;
break;
2009-09-14 01:07:32 +00:00
}
}
static void usage(void)
{
const char *usage_msg =
2013-05-28 04:35:00 +00:00
"usage: %s [-hVsar] [logfile1 logfile2 ...]\n"
"\n"
"A curses-based log file viewer that indexes log messages by type\n"
"and time to make it easier to navigate through files quickly.\n"
"\n"
"Key bindings:\n"
" ? View/leave the online help text.\n"
" q Quit the program.\n"
"\n"
"Options:\n"
" -h Print this message, then exit.\n"
" -C Check configuration and then exit.\n"
" -d file Write debug messages to the given file.\n"
2013-05-28 04:35:00 +00:00
" -V Print version information.\n"
" -s Load the most recent syslog messages file.\n"
" -a Load all of the most recent log file types.\n"
" -r Load older rotated log files as well.\n"
" -t Prepend timestamps to the lines of data being read in\n"
" on the standard input.\n"
" -w file Write the contents of the standard input to this file.\n"
2013-05-28 04:35:00 +00:00
"\n"
"Optional arguments:\n"
" logfile1 The log files or directories to view. If a\n"
" directory is given, all of the files in the\n"
" directory will be loaded.\n"
"\n"
"Examples:\n"
" To load and follow the syslog file:\n"
" $ lnav -s\n"
"\n"
" To load all of the files in /var/log:\n"
" $ lnav /var/log\n"
"\n"
" To watch the output of make with timestamps prepended:\n"
" $ make 2>&1 | lnav -t\n"
"\n"
"Version: " PACKAGE_STRING "\n";
2009-09-14 01:07:32 +00:00
fprintf(stderr, usage_msg, lnav_data.ld_program_name);
}
static pcre *xpcre_compile(const char *pattern, int options = 0)
{
const char *errptr;
2013-05-28 04:35:00 +00:00
pcre * retval;
int eoff;
2009-09-14 01:07:32 +00:00
if ((retval = pcre_compile(pattern,
2013-05-28 04:35:00 +00:00
options,
&errptr,
&eoff,
NULL)) == NULL) {
fprintf(stderr, "internal error: failed to compile -- %s\n", pattern);
fprintf(stderr, "internal error: %s\n", errptr);
exit(1);
2009-09-14 01:07:32 +00:00
}
return retval;
}
/**
* Callback used to keep track of the timestamps for the top and bottom lines
* in the log view. This function is intended to be used as the callback
* function in a view_action.
*
2013-05-28 04:35:00 +00:00
* @param lv The listview object that contains the log
*/
2009-09-14 01:07:32 +00:00
static void update_times(void *, listview_curses *lv)
{
if (lv == &lnav_data.ld_views[LNV_LOG] && lv->get_inner_height() > 0) {
2013-05-28 04:35:00 +00:00
logfile_sub_source &lss = lnav_data.ld_log_source;
2013-06-22 14:55:49 +00:00
logline *ll;
ll = lss.find_line(lss.at(lv->get_top()));
lnav_data.ld_top_time = ll->get_time();
lnav_data.ld_top_time_millis = ll->get_millis();
ll = lss.find_line(lss.at(lv->get_bottom()));
lnav_data.ld_bottom_time = ll->get_time();
lnav_data.ld_bottom_time_millis = ll->get_millis();
2009-09-14 01:07:32 +00:00
}
if (lv == &lnav_data.ld_views[LNV_HISTOGRAM] &&
2013-05-28 04:35:00 +00:00
lv->get_inner_height() > 0) {
hist_source &hs = lnav_data.ld_hist_source;
2009-09-14 01:07:32 +00:00
2013-05-28 04:35:00 +00:00
lnav_data.ld_top_time = hs.value_for_row(lv->get_top());
2013-06-22 14:55:49 +00:00
lnav_data.ld_top_time_millis = 0;
2013-05-28 04:35:00 +00:00
lnav_data.ld_bottom_time = hs.value_for_row(lv->get_bottom());
2013-06-22 14:55:49 +00:00
lnav_data.ld_bottom_time_millis = 0;
2009-09-14 01:07:32 +00:00
}
}
/**
* Functor used to compare files based on their device and inode number.
*/
struct same_file {
same_file(const struct stat &stat) : sf_stat(stat) { };
/**
* Compare the given log file against the 'stat' given in the constructor.
* @param lf The log file to compare.
* @return True if the dev/inode values in the stat given in the
* constructor matches the stat in the logfile object.
*/
2013-05-28 04:35:00 +00:00
bool operator()(const logfile *lf) const
{
return this->sf_stat.st_dev == lf->get_stat().st_dev &&
this->sf_stat.st_ino == lf->get_stat().st_ino;
};
const struct stat &sf_stat;
};
/**
* Try to load the given file as a log file. If the file has not already been
* loaded, it will be loaded. If the file has already been loaded, the file
* name will be updated.
2013-05-28 04:35:00 +00:00
*
* @param filename The file name to check.
* @param fd An already-opened descriptor for 'filename'.
* @param required Specifies whether or not the file must exist and be valid.
*/
static void watch_logfile(string filename, int fd, bool required)
{
2012-07-13 16:26:47 +00:00
list<logfile *>::iterator file_iter;
struct stat st;
2013-05-28 04:35:00 +00:00
int rc;
if (fd != -1) {
2013-05-28 04:35:00 +00:00
rc = fstat(fd, &st);
}
else {
2013-05-28 04:35:00 +00:00
rc = stat(filename.c_str(), &st);
}
if (rc == 0) {
2013-05-28 04:35:00 +00:00
if (!S_ISREG(st.st_mode)) {
if (required) {
rc = -1;
errno = EINVAL;
}
else {
return;
}
}
}
if (rc == -1) {
2013-05-28 04:35:00 +00:00
if (required) {
throw logfile::error(filename, errno);
}
else{
return;
}
}
2012-07-13 16:26:47 +00:00
file_iter = find_if(lnav_data.ld_files.begin(),
2013-05-28 04:35:00 +00:00
lnav_data.ld_files.end(),
same_file(st));
2012-07-13 16:26:47 +00:00
if (file_iter == lnav_data.ld_files.end()) {
2013-05-28 04:35:00 +00:00
if (find(lnav_data.ld_other_files.begin(),
lnav_data.ld_other_files.end(),
filename) == lnav_data.ld_other_files.end()) {
file_format_t ff = detect_file_format(filename);
switch (ff) {
case FF_SQLITE_DB:
lnav_data.ld_other_files.push_back(filename);
attach_sqlite_db(lnav_data.ld_db.in(), filename);
2013-05-28 04:35:00 +00:00
break;
default:
/* It's a new file, load it in. */
logfile *lf = new logfile(filename, fd);
lnav_data.ld_files.push_back(lf);
lnav_data.ld_text_source.tss_files.push_back(lf);
break;
}
}
}
2012-07-13 16:26:47 +00:00
else {
2013-05-28 04:35:00 +00:00
/* The file is already loaded, but has been found under a different
* name. We just need to update the stored file name.
*/
2013-05-28 04:35:00 +00:00
(*file_iter)->set_filename(filename);
2012-07-13 16:26:47 +00:00
}
}
/**
* Expand a glob pattern and call watch_logfile with the file names that match
* the pattern.
* @param path The glob pattern to expand.
* @param required Passed to watch_logfile.
*/
static void expand_filename(string path, bool required)
{
2013-05-31 00:46:41 +00:00
static_root_mem<glob_t, globfree> gl;
2013-05-31 00:46:41 +00:00
if (glob(path.c_str(), GLOB_NOCHECK, NULL, gl.inout()) == 0) {
2013-05-28 04:35:00 +00:00
int lpc;
2013-05-31 00:46:41 +00:00
if (gl->gl_pathc == 1 /*&& gl.gl_matchc == 0*/) {
2013-05-28 04:35:00 +00:00
/* It's a pattern that doesn't match any files
* yet, allow it through since we'll load it in
* dynamically.
*/
required = false;
}
2013-05-31 00:46:41 +00:00
if (gl->gl_pathc > 1 ||
strcmp(path.c_str(), gl->gl_pathv[0]) != 0) {
2013-05-28 04:35:00 +00:00
required = false;
}
2013-05-31 00:46:41 +00:00
for (lpc = 0; lpc < (int)gl->gl_pathc; lpc++) {
2013-09-14 19:30:57 +00:00
auto_mem<char> abspath;
if ((abspath = realpath(gl->gl_pathv[lpc], NULL)) == NULL) {
perror("Cannot find file");
}
else {
watch_logfile(abspath.in(), -1, required);
}
2013-05-28 04:35:00 +00:00
}
}
}
static bool rescan_files(bool required = false)
{
2013-05-28 04:35:00 +00:00
set<pair<string, int> >::iterator iter;
list<logfile *>::iterator file_iter;
bool retval = false;
for (iter = lnav_data.ld_file_names.begin();
2013-05-28 04:35:00 +00:00
iter != lnav_data.ld_file_names.end();
iter++) {
if (iter->second == -1) {
expand_filename(iter->first, required);
if (lnav_data.ld_flags & LNF_ROTATED) {
string path = iter->first + ".*";
expand_filename(path, false);
}
}
else {
watch_logfile(iter->first, iter->second, required);
}
}
for (file_iter = lnav_data.ld_files.begin();
2013-05-28 04:35:00 +00:00
file_iter != lnav_data.ld_files.end(); ) {
2013-09-14 19:30:57 +00:00
logfile *lf = *file_iter;
if (!lf->exists() || lf->is_closed()) {
std::list<logfile *>::iterator tss_iter;
2013-09-14 19:30:57 +00:00
fprintf(stderr, "file has been deleted/closed -- %s\n",
lf->get_filename().c_str());
lnav_data.ld_file_names.erase(make_pair(lf->get_filename(), lf->get_fd()));
2013-09-14 19:30:57 +00:00
lnav_data.ld_text_source.tss_files.remove(lf);
lnav_data.ld_log_source.remove_file(lf);
file_iter = lnav_data.ld_files.erase(file_iter);
delete lf;
retval = true;
}
else {
++file_iter;
}
}
return retval;
}
static string execute_action(log_data_helper &ldh,
int value_index,
const string &action_name)
{
std::map<string, log_format::action_def>::const_iterator iter;
logline_value &lv = ldh.ldh_line_values[value_index];
logfile *lf = ldh.ldh_file;
const log_format *format = lf->get_format();
pid_t child_pid;
string retval;
iter = format->lf_action_defs.find(action_name);
const log_format::action_def &action = iter->second;
auto_pipe in_pipe(STDIN_FILENO);
auto_pipe out_pipe(STDOUT_FILENO);
auto_pipe err_pipe(STDERR_FILENO);
in_pipe.open();
if (action.ad_capture_output)
out_pipe.open();
err_pipe.open();
child_pid = fork();
in_pipe.after_fork(child_pid);
out_pipe.after_fork(child_pid);
err_pipe.after_fork(child_pid);
switch (child_pid) {
case -1:
retval = "error: unable to fork child process -- " + string(strerror(errno));
break;
case 0: {
const char *args[action.ad_cmdline.size() + 1];
set<std::string> path_set(format->get_source_path());
char env_buffer[64];
int value_line;
string path;
setenv("LNAV_ACTION_FILE", lf->get_filename().c_str(), 1);
snprintf(env_buffer, sizeof(env_buffer),
"%ld",
(ldh.ldh_line - lf->begin()) + 1);
setenv("LNAV_ACTION_FILE_LINE", env_buffer, 1);
snprintf(env_buffer, sizeof(env_buffer), "%d", ldh.ldh_y_offset + 1);
setenv("LNAV_ACTION_MSG_LINE", env_buffer, 1);
setenv("LNAV_ACTION_VALUE_NAME", lv.lv_name.c_str(), 1);
value_line = ldh.ldh_y_offset - ldh.get_value_line(lv) + 1;
snprintf(env_buffer, sizeof(env_buffer), "%d", value_line);
setenv("LNAV_ACTION_VALUE_LINE", env_buffer, 1);
for (set<string>::iterator path_iter = path_set.begin();
path_iter != path_set.end();
++path_iter) {
if (!path.empty()) {
path += ":";
}
path += *path_iter;
}
path += ":" + string(getenv("PATH"));
setenv("PATH", path.c_str(), 1);
for (size_t lpc = 0; lpc < action.ad_cmdline.size(); lpc++) {
args[lpc] = action.ad_cmdline[lpc].c_str();
}
args[action.ad_cmdline.size()] = NULL;
execvp(args[0], (char *const *) args);
fprintf(stderr,
"error: could not exec process -- %s:%s\n",
args[0],
strerror(errno));
exit(0);
}
break;
default: {
static int exec_count = 0;
string value = lv.to_string();
const char *line;
line_buffer lb;
size_t len;
off_t off = 0;
lnav_data.ld_children.push_back(child_pid);
if (write(in_pipe.write_end(), value.c_str(), value.size()) == -1) {
perror("execute_action write");
}
in_pipe.close();
lb.set_fd(err_pipe.read_end());
line = lb.read_line(off, len);
if (out_pipe.read_end() != -1) {
piper_proc *pp = new piper_proc(out_pipe.read_end(), false);
char desc[128];
lnav_data.ld_pipers.push_back(pp);
snprintf(desc,
sizeof(desc), "[%d] Output of %s",
exec_count++,
action.ad_cmdline[0].c_str());
lnav_data.ld_file_names.insert(make_pair(
desc,
pp->get_fd()));
lnav_data.ld_files_to_front.push_back(make_pair(desc, 0));
}
retval = string(line, len);
}
break;
}
return retval;
}
class action_delegate : public text_delegate {
public:
action_delegate(logfile_sub_source &lss) : ad_log_helper(lss), ad_press_line(-1) { };
virtual bool text_handle_mouse(textview_curses &tc, mouse_event &me) {
bool retval = false;
if (me.me_button != BUTTON_LEFT) {
return false;
}
vis_line_t mouse_line = vis_line_t(tc.get_top() + me.me_y);
int mouse_left = tc.get_left() + me.me_x;
switch (me.me_state) {
case BUTTON_STATE_PRESSED:
if (mouse_line >= vis_line_t(0) && mouse_line <= tc.get_bottom()) {
size_t line_end_index = 0;
int x_offset;
this->ad_press_line = mouse_line;
this->ad_log_helper.parse_line(mouse_line, true);
this->ad_log_helper.get_line_bounds(this->ad_line_index, line_end_index);
struct line_range lr(this->ad_line_index, line_end_index);
this->ad_press_value = -1;
x_offset = this->ad_line_index + mouse_left;
if (lr.contains(x_offset)) {
for (size_t lpc = 0;
lpc < this->ad_log_helper.ldh_line_values.size();
lpc++) {
logline_value &lv = this->ad_log_helper.ldh_line_values[lpc];
if (lv.lv_origin.contains(x_offset)) {
this->ad_press_value = lpc;
retval = true;
break;
}
}
}
}
break;
case BUTTON_STATE_DRAGGED:
if (this->ad_press_value != -1) {
retval = true;
}
break;
case BUTTON_STATE_RELEASED:
if (this->ad_press_value != -1 && this->ad_press_line == mouse_line) {
logline_value &lv = this->ad_log_helper.ldh_line_values[this->ad_press_value];
int x_offset = this->ad_line_index + mouse_left;
if (lv.lv_origin.contains(x_offset)) {
logfile *lf = this->ad_log_helper.ldh_file;
const vector<string> *actions;
actions = lf->get_format()->get_actions(lv);
if (actions != NULL && !actions->empty()) {
string rc = execute_action(
this->ad_log_helper, this->ad_press_value, actions->at(0));
lnav_data.ld_rl_view->set_value(rc);
}
}
retval = true;
}
break;
}
return retval;
};
log_data_helper ad_log_helper;
vis_line_t ad_press_line;
int ad_press_value;
size_t ad_line_index;
};
2012-04-24 21:31:35 +00:00
class lnav_behavior : public mouse_behavior {
public:
2013-05-28 04:35:00 +00:00
enum lb_mode_t {
LB_MODE_NONE,
LB_MODE_DOWN,
LB_MODE_UP,
LB_MODE_DRAG
};
lnav_behavior()
: lb_selection_start(-1),
lb_selection_last(-1),
lb_scrollbar_y(-1),
lb_scroll_repeat(0),
lb_mode(LB_MODE_NONE) {};
int scroll_polarity(int button)
{
return button == xterm_mouse::XT_SCROLL_UP ? -1 : 1;
};
void mouse_event(int button, bool release, int x, int y)
2013-05-28 04:35:00 +00:00
{
textview_curses * tc = lnav_data.ld_view_stack.top();
struct mouse_event me;
2013-05-28 04:35:00 +00:00
switch (button & xterm_mouse::XT_BUTTON__MASK) {
2013-05-28 04:35:00 +00:00
case xterm_mouse::XT_BUTTON1:
me.me_button = BUTTON_LEFT;
2013-05-28 04:35:00 +00:00
break;
case xterm_mouse::XT_BUTTON2:
me.me_button = BUTTON_MIDDLE;
break;
case xterm_mouse::XT_BUTTON3:
me.me_button = BUTTON_RIGHT;
2013-05-28 04:35:00 +00:00
break;
case xterm_mouse::XT_SCROLL_UP:
me.me_button = BUTTON_SCROLL_UP;
break;
2013-05-28 04:35:00 +00:00
case xterm_mouse::XT_SCROLL_DOWN:
me.me_button = BUTTON_SCROLL_DOWN;
2013-05-28 04:35:00 +00:00
break;
}
if (button & xterm_mouse::XT_DRAG_FLAG) {
me.me_state = BUTTON_STATE_DRAGGED;
}
else if (release) {
me.me_state = BUTTON_STATE_RELEASED;
}
else {
me.me_state = BUTTON_STATE_PRESSED;
}
gettimeofday(&me.me_time, NULL);
me.me_x = x - 1;
me.me_y = y - tc->get_y() - 1;
tc->handle_mouse(me);
2013-05-28 04:35:00 +00:00
};
2012-04-24 21:31:35 +00:00
private:
2013-05-28 04:35:00 +00:00
struct timeval lb_last_event_time;
vis_line_t lb_selection_start;
vis_line_t lb_selection_last;
bool lb_selection_cleared;
2012-04-24 21:31:35 +00:00
2013-05-28 04:35:00 +00:00
int lb_scrollbar_y;
2012-04-24 21:31:35 +00:00
2013-05-28 04:35:00 +00:00
struct timeval lb_last_scroll_time;
int lb_scroll_repeat;
2013-05-28 04:35:00 +00:00
lb_mode_t lb_mode;
2012-04-24 21:31:35 +00:00
};
2009-09-14 01:07:32 +00:00
static void looper(void)
{
int fd;
2013-05-28 04:35:00 +00:00
fd =
open(lnav_data.ld_debug_log_name, O_WRONLY | O_CREAT | O_APPEND, 0666);
2009-09-14 01:07:32 +00:00
dup2(fd, STDERR_FILENO);
close(fd);
fprintf(stderr, "startup\n");
try {
2013-05-28 04:35:00 +00:00
readline_context command_context("cmd", &lnav_commands);
2009-09-14 01:07:32 +00:00
2013-05-28 04:35:00 +00:00
readline_context search_context("search");
readline_context index_context("capture");
readline_context sql_context("sql", NULL, false);
textview_curses *tc;
readline_curses rlc;
int lpc;
2009-09-14 01:07:32 +00:00
2013-05-28 04:35:00 +00:00
listview_curses::action::broadcaster &sb =
lnav_data.ld_scroll_broadcaster;
2009-09-14 01:07:32 +00:00
2013-05-28 04:35:00 +00:00
rlc.add_context(LNM_COMMAND, command_context);
rlc.add_context(LNM_SEARCH, search_context);
rlc.add_context(LNM_CAPTURE, index_context);
rlc.add_context(LNM_SQL, sql_context);
rlc.start();
2009-09-14 01:07:32 +00:00
2013-05-28 04:35:00 +00:00
lnav_data.ld_rl_view = &rlc;
2009-09-14 01:07:32 +00:00
2013-05-28 04:35:00 +00:00
lnav_data.ld_rl_view->
add_possibility(LNM_COMMAND, "graph", "\\d+(?:\\.\\d+)?");
lnav_data.ld_rl_view->
add_possibility(LNM_COMMAND, "graph", "([:= \\t]\\d+(?:\\.\\d+)?)");
2009-09-14 01:07:32 +00:00
2013-05-28 04:35:00 +00:00
(void)signal(SIGINT, sigint);
(void)signal(SIGTERM, sigint);
(void)signal(SIGWINCH, sigwinch);
(void)signal(SIGCHLD, sigchld);
2009-09-14 01:07:32 +00:00
screen_curses sc;
2013-05-28 04:35:00 +00:00
lnav_behavior lb;
lnav_data.ld_mouse.set_behavior(&lb);
lnav_data.ld_mouse.set_enabled(check_experimental("mouse"));
2013-05-28 04:35:00 +00:00
lnav_data.ld_window = sc.get_window();
keypad(stdscr, TRUE);
(void)nonl();
(void)cbreak();
(void)noecho();
(void)nodelay(lnav_data.ld_window, 1);
define_key("\033Od", KEY_BEG);
define_key("\033Oc", KEY_END);
view_colors::singleton().init();
rlc.set_window(lnav_data.ld_window);
rlc.set_y(-1);
rlc.set_perform_action(readline_curses::action(rl_callback));
rlc.set_timeout_action(readline_curses::action(rl_search));
2013-07-24 14:42:16 +00:00
rlc.set_abort_action(readline_curses::action(rl_abort));
rlc.set_alt_value(HELP_MSG_2(
2013-06-16 01:07:50 +00:00
e, E,
"to move forward/backward through error messages"));
2013-05-28 04:35:00 +00:00
(void)curs_set(0);
lnav_data.ld_view_stack.push(&lnav_data.ld_views[LNV_LOG]);
update_view_name();
2013-05-28 04:35:00 +00:00
tc = lnav_data.ld_view_stack.top();
for (lpc = 0; lpc < LNV__MAX; lpc++) {
lnav_data.ld_views[lpc].set_window(lnav_data.ld_window);
lnav_data.ld_views[lpc].set_y(1);
lnav_data.ld_views[lpc].
set_height(vis_line_t(-(rlc.get_height() + 1 + 1)));
lnav_data.ld_views[lpc].
set_scroll_action(sb.get_functor());
lnav_data.ld_views[lpc].
set_search_action(&lnav_data.ld_bottom_source.hits_wire);
}
lnav_data.ld_status[LNS_TOP].set_top(0);
for (lpc = 0; lpc < LNS__MAX; lpc++) {
lnav_data.ld_status[lpc].set_window(lnav_data.ld_window);
}
lnav_data.ld_status[LNS_TOP].
set_data_source(&lnav_data.ld_top_source);
lnav_data.ld_status[LNS_BOTTOM].
set_data_source(&lnav_data.ld_bottom_source);
sb.push_back(view_action<listview_curses>(update_times));
sb.push_back(&lnav_data.ld_top_source.filename_wire);
sb.push_back(&lnav_data.ld_bottom_source.line_number_wire);
sb.push_back(&lnav_data.ld_bottom_source.percent_wire);
sb.push_back(&lnav_data.ld_bottom_source.marks_wire);
{
vis_line_t top(0), height(0);
unsigned long width;
tc->get_dimensions(height, width);
top = vis_line_t(tc->get_inner_height()) - height + vis_line_t(1);
if (top > 0) {
tc->set_top(top);
}
}
{
hist_source &hs = lnav_data.ld_hist_source;
lnav_data.ld_hist_zoom = 2;
hs.set_role_for_type(bucket_type_t(logline::LEVEL_FATAL),
view_colors::VCR_ERROR);
hs.set_role_for_type(bucket_type_t(logline::LEVEL_CRITICAL),
view_colors::VCR_ERROR);
hs.set_role_for_type(bucket_type_t(logline::LEVEL_ERROR),
view_colors::VCR_ERROR);
hs.set_role_for_type(bucket_type_t(logline::LEVEL_WARNING),
view_colors::VCR_WARNING);
hs.set_label_source(new time_label_source());
}
{
hist_source &hs = lnav_data.ld_graph_source;
hs.set_bucket_size(1);
hs.set_group_size(100);
}
{
hist_source &hs = lnav_data.ld_db_source;
hs.set_bucket_size(1);
hs.set_group_size(10);
hs.set_label_source(&lnav_data.ld_db_rows);
}
FD_ZERO(&lnav_data.ld_read_fds);
FD_SET(STDIN_FILENO, &lnav_data.ld_read_fds);
lnav_data.ld_max_fd =
max(STDIN_FILENO, rlc.update_fd_set(lnav_data.ld_read_fds));
lnav_data.ld_status[0].window_change();
lnav_data.ld_status[1].window_change();
execute_file(dotlnav_path("session"));
lnav_data.ld_scroll_broadcaster.invoke(lnav_data.ld_view_stack.top());
2013-05-31 15:01:31 +00:00
bool session_loaded = false;
2013-05-28 04:35:00 +00:00
while (lnav_data.ld_looping) {
fd_set ready_rfds = lnav_data.ld_read_fds;
struct timeval to = { 0, 330000 };
int rc;
lnav_data.ld_top_source.update_time();
if (rescan_files()) {
rebuild_indexes(true);
}
2013-05-28 04:35:00 +00:00
for (lpc = 0; lpc < LNV__MAX; lpc++) {
lnav_data.ld_views[lpc]
.set_height(vis_line_t(-(rlc.get_height() + 1)));
}
lnav_data.ld_status[LNS_BOTTOM].set_top(-(rlc.get_height() + 1));
lnav_data.ld_view_stack.top()->do_update();
lnav_data.ld_status[LNS_TOP].do_update();
lnav_data.ld_status[LNS_BOTTOM].do_update();
rlc.do_update();
refresh();
rc = select(lnav_data.ld_max_fd + 1,
&ready_rfds, NULL, NULL,
&to);
if (rc < 0) {
switch (errno) {
case EINTR:
break;
case EBADF:
{
int lpc, fd_flags;
fprintf(stderr, "bad file descriptor\n");
for (lpc = 0; lpc < FD_SETSIZE; lpc++) {
if (fcntl(lpc, F_GETFD, &fd_flags) == -1 &&
FD_ISSET(lpc, &lnav_data.ld_read_fds)) {
fprintf(stderr, "bad fd %d\n", lpc);
}
}
lnav_data.ld_looping = false;
}
break;
2013-05-28 04:35:00 +00:00
default:
fprintf(stderr, "select %s\n", strerror(errno));
lnav_data.ld_looping = false;
break;
}
}
else if (rc == 0) {
static bool initial_build = false;
rebuild_indexes(false);
if (!initial_build &&
lnav_data.ld_log_source.text_line_count() == 0 &&
lnav_data.ld_text_source.text_line_count() > 0) {
toggle_view(&lnav_data.ld_views[LNV_TEXT]);
lnav_data.ld_views[LNV_TEXT].set_top(vis_line_t(0));
lnav_data.ld_rl_view->set_alt_value(
2013-06-16 01:07:50 +00:00
HELP_MSG_2(f, F,
"to switch to the next/previous file"));
2013-05-28 04:35:00 +00:00
}
if (lnav_data.ld_log_source.text_line_count() > 0 ||
lnav_data.ld_text_source.text_line_count() > 0) {
initial_build = true;
}
if (!session_loaded) {
load_session();
if (!lnav_data.ld_session_file_names.empty()) {
std::string ago;
ago = time_ago(lnav_data.ld_session_save_time);
lnav_data.ld_rl_view->set_value(
("restored session from " ANSI_BOLD_START) +
ago +
(ANSI_NORM "; press Ctrl-R to reset session"));
}
rebuild_indexes(true);
session_loaded = true;
}
2013-05-28 04:35:00 +00:00
}
else {
if (FD_ISSET(STDIN_FILENO, &ready_rfds)) {
static size_t escape_index = 0;
static char escape_buffer[32];
2013-05-28 04:35:00 +00:00
int ch;
while ((ch = getch()) != ERR) {
2013-07-27 19:07:05 +00:00
alerter::singleton().new_input(ch);
if (escape_index > sizeof(escape_buffer) - 1) {
escape_index = 0;
}
else if (escape_index > 0) {
escape_buffer[escape_index++] = ch;
escape_buffer[escape_index] = '\0';
if (strcmp("\x1b[<", escape_buffer) == 0) {
lnav_data.ld_mouse.handle_mouse(ch);
escape_index = 0;
}
continue;
}
2013-05-28 04:35:00 +00:00
switch (ch) {
case CEOF:
case KEY_RESIZE:
break;
case '\x1b':
escape_index = 0;
escape_buffer[escape_index++] = ch;
escape_buffer[escape_index] = '\0';
break;
2013-05-28 04:35:00 +00:00
case KEY_MOUSE:
lnav_data.ld_mouse.handle_mouse(ch);
2013-05-28 04:35:00 +00:00
break;
default:
switch (lnav_data.ld_mode) {
case LNM_PAGING:
handle_paging_key(ch);
break;
case LNM_COMMAND:
case LNM_SEARCH:
case LNM_CAPTURE:
case LNM_SQL:
handle_rl_key(ch);
break;
default:
assert(0);
break;
}
break;
}
}
}
for (lpc = 0; lpc < LG__MAX; lpc++) {
auto_ptr<grep_highlighter> &gc =
lnav_data.ld_grep_child[lpc];
if (gc.get() != NULL) {
gc->get_grep_proc()->check_fd_set(ready_rfds);
if (lpc == LG_GRAPH) {
lnav_data.ld_views[LNV_GRAPH].reload_data();
/* XXX */
}
}
}
for (lpc = 0; lpc < LNV__MAX; lpc++) {
auto_ptr<grep_highlighter> &gc =
lnav_data.ld_search_child[lpc];
if (gc.get() != NULL) {
gc->get_grep_proc()->check_fd_set(ready_rfds);
if (!lnav_data.ld_view_stack.empty()) {
lnav_data.ld_bottom_source.
update_hits(lnav_data.ld_view_stack.top());
}
}
}
rlc.check_fd_set(ready_rfds);
}
if (lnav_data.ld_winched) {
struct winsize size;
lnav_data.ld_winched = false;
2013-05-28 04:35:00 +00:00
if (ioctl(fileno(stdout), TIOCGWINSZ, &size) == 0) {
resizeterm(size.ws_row, size.ws_col);
}
rlc.window_change();
lnav_data.ld_status[0].window_change();
lnav_data.ld_status[1].window_change();
lnav_data.ld_view_stack.top()->set_needs_update();
}
if (lnav_data.ld_child_terminated) {
lnav_data.ld_child_terminated = false;
for (std::list<pid_t>::iterator iter = lnav_data.ld_children.begin();
iter != lnav_data.ld_children.end();
++iter) {
int rc, child_stat;
rc = waitpid(*iter, &child_stat, WNOHANG);
if (rc == -1 || rc == 0)
continue;
iter = lnav_data.ld_children.erase(iter);
}
for (std::list<piper_proc *>::iterator iter = lnav_data.ld_pipers.begin();
iter != lnav_data.ld_pipers.end();
++iter) {
if ((*iter)->has_exited()) {
delete *iter;
iter = lnav_data.ld_pipers.erase(iter);
}
}
2013-05-28 04:35:00 +00:00
}
}
2009-09-14 01:07:32 +00:00
}
catch (readline_curses::error & e) {
2013-05-28 04:35:00 +00:00
fprintf(stderr, "error: %s\n", strerror(e.e_err));
2009-09-14 01:07:32 +00:00
}
}
class strace_log_table : public log_vtab_impl {
public:
2009-09-14 01:07:32 +00:00
strace_log_table()
2013-05-28 04:35:00 +00:00
: log_vtab_impl("strace_log") {};
2013-05-28 04:35:00 +00:00
void get_columns(vector<vtab_column> &cols)
{
cols.push_back(vtab_column("funcname", SQLITE_TEXT));
cols.push_back(vtab_column("args", SQLITE_TEXT));
cols.push_back(vtab_column("result", SQLITE_TEXT));
cols.push_back(vtab_column("duration", SQLITE_TEXT));
#if 0
2013-05-28 04:35:00 +00:00
cols.push_back(vtab_column("arg0", SQLITE_TEXT));
cols.push_back(vtab_column("arg1", SQLITE_TEXT));
cols.push_back(vtab_column("arg2", SQLITE_TEXT));
cols.push_back(vtab_column("arg3", SQLITE_TEXT));
cols.push_back(vtab_column("arg4", SQLITE_TEXT));
cols.push_back(vtab_column("arg5", SQLITE_TEXT));
cols.push_back(vtab_column("arg6", SQLITE_TEXT));
cols.push_back(vtab_column("arg7", SQLITE_TEXT));
cols.push_back(vtab_column("arg8", SQLITE_TEXT));
cols.push_back(vtab_column("arg9", SQLITE_TEXT));
#endif
2009-09-14 01:07:32 +00:00
};
#if 0
void extract(logfile *lf,
const std::string &line,
2013-05-28 04:35:00 +00:00
int column,
sqlite3_context *ctx)
{
string function, args, result, duration = "0";
if (!this->slt_regex.FullMatch(line,
&function,
&args,
&result,
&duration)) {
fprintf(stderr, "bad match! %s\n", line.c_str());
}
switch (column) {
case 0:
sqlite3_result_text(ctx,
function.c_str(),
function.length(),
SQLITE_TRANSIENT);
break;
case 1:
sqlite3_result_text(ctx,
result.c_str(),
result.length(),
SQLITE_TRANSIENT);
break;
case 2:
sqlite3_result_text(ctx,
duration.c_str(),
duration.length(),
SQLITE_TRANSIENT);
break;
default:
{
const char *arg_start = args.c_str();
int in_struct = 0, in_list = 0;
int lpc, argnum, curarg = 0;
bool in_quote = false;
argnum = column - 3;
for (lpc = 0; lpc < (int)args.length(); lpc++) {
switch (args[lpc]) {
case '{':
if (!in_quote) {
in_struct += 1;
}
break;
case '}':
if (!in_quote) {
in_struct -= 1;
}
break;
case '[':
if (!in_quote) {
in_list += 1;
}
break;
case ']':
if (!in_quote) {
in_list -= 1;
}
break;
case '"':
if (!in_quote) {
in_quote = true;
}
else if (lpc > 0 && args[lpc - 1] != '\\') {
in_quote = false;
}
break;
case ',':
if (!in_quote && !in_struct && !in_list) {
if (curarg == argnum) {
sqlite3_result_text(ctx,
arg_start,
&(args.c_str()[lpc]) -
arg_start,
SQLITE_TRANSIENT);
return;
}
curarg += 1;
arg_start = &(args.c_str()[lpc + 1]);
}
break;
}
}
if (curarg == argnum) {
sqlite3_result_text(ctx,
arg_start,
&(args.c_str()[lpc]) -
arg_start,
SQLITE_TRANSIENT);
}
else {
sqlite3_result_text(ctx,
"",
0,
SQLITE_TRANSIENT);
}
}
break;
}
2009-09-14 01:07:32 +00:00
};
private:
pcrecpp::RE slt_regex;
#endif
};
2011-10-20 22:38:37 +00:00
static void setup_highlights(textview_curses::highlight_map_t &hm)
{
hm["$kw"] = textview_curses::highlighter(xpcre_compile(
"(?:"
"\\balter |"
"\\band\\b|"
"\\bas |"
"\\bbetween\\b|"
"\\bbool\\b|"
"\\bboolean\\b|"
"\\bbreak\\b|"
"\\bcase\\b|"
"\\bcatch\\b|"
"\\bchar\\b|"
"\\bclass\\b|"
"\\bconst\\b|"
"\\bcontinue\\b|"
"\\bcreate |"
"\\bdef |"
"\\bdefault[:\\s]|"
"\\bdo\\b|"
"\\bdone\\b|"
"\\bdouble\\b|"
"\\bdrop\\b|"
"\\belif |"
"\\belse\\b|"
"\\benum\\b|"
"\\bendif\\b|"
"\\besac\\b|"
"\\bexcept[\\s:]|"
"\\bexists\\b|"
"\\bexport\\b|"
"\\bextends\\b|"
"\\bextern\\b|"
"\\bfalse\\b|"
"\\bfi\\b|"
"\\bfloat\\b|"
"\\bfor\\b|"
"\\bfrom |"
"\\bgoto\\b|"
"\\bgroup by |"
"\\bif\\b|"
"\\bimport |"
"\\bimplements\\b|"
"\\bin\\b|"
"\\binline\\b|"
"\\binsert |"
"\\bint\\b|"
"\\binto\\b|"
"\\binterface\\b|"
"\\bjoin\\b|"
"\\blambda\\b|"
2013-07-26 03:41:42 +00:00
"\\blet\\b|"
"\\blong\\b|"
"\\bnamespace\\b|"
"\\bnew\\b|"
"\\bnot\\b|"
"\\bnull\\b|"
"\\boperator\\b|"
"\\bor\\b|"
"\\border by |"
"\\bpackage\\b|"
"\\bprivate\\b|"
"\\bprotected\\b|"
"\\bpublic\\b|"
"\\braise\\b|"
"\\b(?<!@)return\\b|"
"\\bselect |"
"\\bself\\b|"
"\\bshift\\b|"
"\\bshort\\b|"
"\\bsizeof\\b|"
"\\bstatic\\b|"
"\\bstruct\\b|"
"\\bswitch\\b|"
"\\btable\\b|"
"\\btemplate\\b|"
"\\bthen\\b|"
"\\bthis\\b|"
"\\b(?<!@)throws?\\b|"
"\\btrue\\b|"
"\\btry\\b|"
"\\btypedef |"
"\\btypename |"
"\\bunion\\b|"
"\\bunsigned |"
"\\bupdate |"
"\\busing |"
"\\bvar\\b|"
"\\bvoid\\b|"
"\\bvolatile\\b|"
"\\bwhere |"
"\\bwhile\\b|"
"\\b[a-zA-Z][\\w]+_t\\b"
")", PCRE_CASELESS),
false, view_colors::VCR_KEYWORD);
2013-06-02 21:20:15 +00:00
hm["$srcfile"] = textview_curses::
2013-05-28 04:35:00 +00:00
highlighter(xpcre_compile(
"[\\w\\-_]+\\."
"(?:java|a|o|so|c|cc|cpp|cxx|h|hh|hpp|hxx|py|pyc|rb):"
"\\d+"));
2013-06-02 21:20:15 +00:00
hm["$xml"] = textview_curses::
highlighter(xpcre_compile("<(/?[^ >=]+)[^>]*>"));
2013-06-02 21:20:15 +00:00
hm["$stringd"] = textview_curses::
highlighter(xpcre_compile("\"(?:\\\\.|[^\"])*\""),
false, view_colors::VCR_STRING);
2013-06-02 21:20:15 +00:00
hm["$strings"] = textview_curses::
2013-05-28 04:35:00 +00:00
highlighter(xpcre_compile(
"(?<![A-WY-Za-qstv-z])\'(?:\\\\.|[^'])*\'"),
false, view_colors::VCR_STRING);
hm["$stringb"] = textview_curses::
highlighter(xpcre_compile("`(?:\\\\.|[^`])*`"),
false, view_colors::VCR_STRING);
2013-06-02 21:20:15 +00:00
hm["$diffp"] = textview_curses::
2013-05-28 04:35:00 +00:00
highlighter(xpcre_compile(
"^\\+.*"), false,
view_colors::VCR_DIFF_ADD);
2013-06-02 21:20:15 +00:00
hm["$diffm"] = textview_curses::
2013-05-28 04:35:00 +00:00
highlighter(xpcre_compile(
"^(?:--- .*|-$|-[^-].*)"), false,
2013-05-28 04:35:00 +00:00
view_colors::VCR_DIFF_DELETE);
2013-06-02 21:20:15 +00:00
hm["$diffs"] = textview_curses::
2013-05-28 04:35:00 +00:00
highlighter(xpcre_compile(
"^\\@@ .*"), false,
view_colors::VCR_DIFF_SECTION);
2013-06-02 21:20:15 +00:00
hm["$ip"] = textview_curses::
2013-05-28 04:35:00 +00:00
highlighter(xpcre_compile("\\d+\\.\\d+\\.\\d+\\.\\d+"));
hm["$comment"] = textview_curses::highlighter(xpcre_compile(
2013-07-26 03:41:42 +00:00
"(?<!:)//.*|/\\*.*\\*/|\\(\\*.*\\*\\)|^#.*|\\s+#.*|dnl.*"), false, view_colors::VCR_COMMENT);
hm["$javadoc"] = textview_curses::highlighter(xpcre_compile(
"@(?:author|deprecated|exception|file|param|return|see|since|throws|todo|version)"));
hm["$var"] = textview_curses::highlighter(xpcre_compile(
"(?:"
2013-07-23 12:55:08 +00:00
"(?:var\\s+)?([\\-\\w]+)\\s*=|"
"(?<!\\$)\\$(\\w+)|"
"(?<!\\$)\\$\\((\\w+)\\)|"
"(?<!\\$)\\$\\{(\\w+)\\}"
")"),
false, view_colors::VCR_VARIABLE);
}
int sql_progress(const struct log_cursor &lc)
{
static int sub_count = 0;
2013-05-28 04:35:00 +00:00
size_t total = lnav_data.ld_log_source.text_line_count();
off_t off = lc.lc_curr_line;
2013-05-28 04:35:00 +00:00
if (lnav_data.ld_window == NULL) {
return 0;
}
2013-05-28 04:35:00 +00:00
if (!lnav_data.ld_looping) {
return 1;
}
if ((off < (total - 1)) || (sub_count % 64 == 0)) {
lnav_data.ld_bottom_source.update_loading(off, total);
lnav_data.ld_top_source.update_time();
lnav_data.ld_status[LNS_TOP].do_update();
lnav_data.ld_status[LNS_BOTTOM].do_update();
refresh();
}
sub_count += 1;
2013-05-28 04:35:00 +00:00
return 0;
2011-10-20 22:38:37 +00:00
}
2009-09-14 01:07:32 +00:00
int main(int argc, char *argv[])
{
2013-06-29 18:00:34 +00:00
std::vector<std::string> loader_errors;
2009-09-14 01:07:32 +00:00
int lpc, c, retval = EXIT_SUCCESS;
2013-05-28 04:35:00 +00:00
2009-09-14 01:07:32 +00:00
auto_ptr<piper_proc> stdin_reader;
2013-05-28 04:35:00 +00:00
const char * stdin_out = NULL;
2009-10-06 21:14:49 +00:00
setlocale(LC_NUMERIC, "");
2013-08-29 04:22:04 +00:00
ensure_dotlnav();
2013-06-29 18:00:34 +00:00
load_formats(loader_errors);
if (!loader_errors.empty()) {
for (std::vector<std::string>::iterator iter = loader_errors.begin();
iter != loader_errors.end();
++iter) {
2013-07-23 12:55:08 +00:00
fprintf(stderr, "%s%s", iter->c_str(),
(*iter)[iter->size() - 1] == '\n' ? "" : "\n");
2013-06-29 18:00:34 +00:00
}
return EXIT_FAILURE;
}
/* If we statically linked against an ncurses library that had a non-
* standard path to the terminfo database, we need to set this variable
* so that it will try the default path.
*/
setenv("TERMINFO_DIRS",
"/usr/share/terminfo:/lib/terminfo:/usr/share/lib/terminfo",
0);
2009-10-06 21:14:49 +00:00
if (sqlite3_open(":memory:", lnav_data.ld_db.out()) != SQLITE_OK) {
fprintf(stderr, "error: unable to create sqlite memory database\n");
2013-05-28 04:35:00 +00:00
exit(EXIT_FAILURE);
2009-10-06 21:14:49 +00:00
}
{
2013-05-28 04:35:00 +00:00
int register_collation_functions(sqlite3 * db);
register_sqlite_funcs(lnav_data.ld_db.in(), sqlite_registration_funcs);
2013-05-28 04:35:00 +00:00
register_collation_functions(lnav_data.ld_db.in());
}
2009-09-14 01:07:32 +00:00
lnav_data.ld_program_name = argv[0];
lnav_data.ld_vtab_manager =
2013-05-28 04:35:00 +00:00
new log_vtab_manager(lnav_data.ld_db,
2013-06-26 03:43:27 +00:00
lnav_data.ld_views[LNV_LOG],
2013-05-28 04:35:00 +00:00
lnav_data.ld_log_source,
sql_progress);
2009-09-14 01:07:32 +00:00
{
auto_mem<char, sqlite3_free> errmsg;
if (sqlite3_exec(lnav_data.ld_db.in(),
init_sql,
NULL,
NULL,
errmsg.out()) != SQLITE_OK) {
fprintf(stderr,
"error: unable to execute DB init -- %s\n",
errmsg.in());
}
}
lnav_data.ld_vtab_manager->register_vtab(new log_vtab_impl("generic_log"));
lnav_data.ld_vtab_manager->register_vtab(new strace_log_table());
for (std::vector<log_format *>::iterator iter = log_format::get_root_formats().begin();
iter != log_format::get_root_formats().end();
++iter) {
log_vtab_impl *lvi = (*iter)->get_vtab_impl();
if (lvi != NULL) {
lnav_data.ld_vtab_manager->register_vtab(lvi);
}
}
2009-09-14 01:07:32 +00:00
DEFAULT_FILES.insert(make_pair(LNF_SYSLOG, string("var/log/messages")));
2011-06-20 05:30:10 +00:00
DEFAULT_FILES.insert(make_pair(LNF_SYSLOG, string("var/log/system.log")));
DEFAULT_FILES.insert(make_pair(LNF_SYSLOG, string("var/log/syslog")));
DEFAULT_FILES.insert(make_pair(LNF_SYSLOG, string("var/log/syslog.log")));
2009-09-14 01:07:32 +00:00
2012-07-13 16:26:47 +00:00
init_lnav_commands(lnav_commands);
2009-09-14 01:07:32 +00:00
lnav_data.ld_views[LNV_HELP].
set_sub_source(new plain_text_source(help_txt));
2009-09-14 01:07:32 +00:00
lnav_data.ld_views[LNV_LOG].
set_sub_source(&lnav_data.ld_log_source);
lnav_data.ld_views[LNV_LOG].
set_delegate(new action_delegate(lnav_data.ld_log_source));
lnav_data.ld_views[LNV_TEXT].
set_sub_source(&lnav_data.ld_text_source);
2009-09-14 01:07:32 +00:00
lnav_data.ld_views[LNV_HISTOGRAM].
set_sub_source(&lnav_data.ld_hist_source);
lnav_data.ld_views[LNV_GRAPH].
set_sub_source(&lnav_data.ld_graph_source);
lnav_data.ld_views[LNV_DB].
set_sub_source(&lnav_data.ld_db_source);
lnav_data.ld_db_overlay.dos_labels = &lnav_data.ld_db_rows;
lnav_data.ld_views[LNV_DB].
set_overlay_source(&lnav_data.ld_db_overlay);
lnav_data.ld_views[LNV_LOG].
set_overlay_source(new field_overlay_source(lnav_data.ld_log_source));
lnav_data.ld_db_overlay.dos_hist_source = &lnav_data.ld_db_source;
2009-09-14 01:07:32 +00:00
{
2013-05-28 04:35:00 +00:00
setup_highlights(lnav_data.ld_views[LNV_LOG].get_highlights());
2011-10-20 22:38:37 +00:00
setup_highlights(lnav_data.ld_views[LNV_TEXT].get_highlights());
2009-09-14 01:07:32 +00:00
}
2013-05-28 04:35:00 +00:00
lnav_data.ld_looping = true;
lnav_data.ld_mode = LNM_PAGING;
lnav_data.ld_debug_log_name = "/dev/null";
while ((c = getopt(argc, argv, "harsCd:tw:V")) != -1) {
2013-05-28 04:35:00 +00:00
switch (c) {
case 'h':
usage();
exit(retval);
break;
2009-09-14 01:07:32 +00:00
case 'C':
exit(0);
break;
2013-05-28 04:35:00 +00:00
case 'd':
lnav_data.ld_debug_log_name = optarg;
break;
2009-09-14 01:07:32 +00:00
2013-05-28 04:35:00 +00:00
case 'a':
lnav_data.ld_flags |= LNF__ALL;
break;
2009-09-14 01:07:32 +00:00
2013-05-28 04:35:00 +00:00
case 'r':
lnav_data.ld_flags |= LNF_ROTATED;
break;
2009-09-14 01:07:32 +00:00
2013-05-28 04:35:00 +00:00
case 's':
lnav_data.ld_flags |= LNF_SYSLOG;
break;
2009-09-14 01:07:32 +00:00
2013-05-28 04:35:00 +00:00
case 't':
lnav_data.ld_flags |= LNF_TIMESTAMP;
break;
case 'w':
stdin_out = optarg;
break;
2013-05-28 04:35:00 +00:00
case 'V':
printf("%s\n", PACKAGE_STRING);
exit(0);
break;
2009-10-15 16:18:30 +00:00
2013-05-28 04:35:00 +00:00
default:
retval = EXIT_FAILURE;
break;
}
2009-09-14 01:07:32 +00:00
}
argc -= optind;
argv += optind;
2013-05-28 04:35:00 +00:00
if (isatty(STDIN_FILENO) && argc == 0 &&
!(lnav_data.ld_flags & LNF__ALL)) {
lnav_data.ld_flags |= LNF_SYSLOG;
2009-09-14 01:07:32 +00:00
}
if (lnav_data.ld_flags != 0) {
2013-05-28 04:35:00 +00:00
char start_dir[FILENAME_MAX];
if (getcwd(start_dir, sizeof(start_dir)) == NULL) {
perror("getcwd");
}
else {
do {
for (lpc = 0; lpc < LNB__MAX; lpc++) {
if (!append_default_files((lnav_flags_t)(1L << lpc))) {
retval = EXIT_FAILURE;
}
}
} while (lnav_data.ld_file_names.empty() &&
change_to_parent_dir());
if (chdir(start_dir) == -1) {
perror("chdir(start_dir)");
}
}
2009-09-14 01:07:32 +00:00
}
for (lpc = 0; lpc < argc; lpc++) {
2013-05-31 00:46:41 +00:00
auto_mem<char> abspath;
2013-06-16 01:07:50 +00:00
struct stat st;
2013-09-14 19:30:57 +00:00
if (is_glob(argv[lpc])) {
lnav_data.ld_file_names.insert(make_pair(argv[lpc], -1));
}
else if (stat(argv[lpc], &st) == -1) {
2013-05-31 00:46:41 +00:00
fprintf(stderr,
"Cannot stat file: %s -- %s\n",
argv[lpc],
strerror(errno));
retval = EXIT_FAILURE;
}
else if ((abspath = realpath(argv[lpc], NULL)) == NULL) {
perror("Cannot find file");
2013-05-28 04:35:00 +00:00
retval = EXIT_FAILURE;
}
else if (S_ISDIR(st.st_mode)) {
2013-05-31 00:46:41 +00:00
string dir_wild(abspath.in());
2013-05-28 04:35:00 +00:00
if (dir_wild[dir_wild.size() - 1] == '/') {
dir_wild.resize(dir_wild.size() - 1);
}
lnav_data.ld_file_names.insert(make_pair(dir_wild + "/*", -1));
}
else {
2013-05-31 00:46:41 +00:00
lnav_data.ld_file_names.insert(make_pair(abspath.in(), -1));
2013-05-28 04:35:00 +00:00
}
2009-09-14 01:07:32 +00:00
}
if (!isatty(STDOUT_FILENO)) {
2013-05-28 04:35:00 +00:00
fprintf(stderr, "error: stdout is not a tty.\n");
retval = EXIT_FAILURE;
2009-09-14 01:07:32 +00:00
}
if (!isatty(STDIN_FILENO)) {
2013-05-28 04:35:00 +00:00
stdin_reader =
auto_ptr<piper_proc>(new piper_proc(STDIN_FILENO,
lnav_data.ld_flags &
LNF_TIMESTAMP, stdin_out));
lnav_data.ld_file_names.insert(make_pair("stdin",
stdin_reader->get_fd()));
if (dup2(STDOUT_FILENO, STDIN_FILENO) == -1) {
perror("cannot dup stdout to stdin");
}
2009-09-14 01:07:32 +00:00
}
2009-09-14 01:07:32 +00:00
if (lnav_data.ld_file_names.empty()) {
2013-05-28 04:35:00 +00:00
fprintf(stderr, "error: no log files given/found.\n");
retval = EXIT_FAILURE;
2009-09-14 01:07:32 +00:00
}
if (retval != EXIT_SUCCESS) {
2013-05-28 04:35:00 +00:00
usage();
2009-09-14 01:07:32 +00:00
}
else {
2013-05-28 04:35:00 +00:00
try {
rescan_files(true);
2013-06-16 01:07:50 +00:00
2013-05-31 15:01:31 +00:00
init_session();
scan_sessions();
2013-05-28 04:35:00 +00:00
guard_termios gt(STDIN_FILENO);
looper();
2013-05-31 15:01:31 +00:00
save_session();
2013-05-28 04:35:00 +00:00
}
catch (line_buffer::error & e) {
fprintf(stderr, "error: %s\n", strerror(e.e_err));
}
catch (logfile::error & e) {
if (e.e_err != EINTR) {
fprintf(stderr,
"error: %s -- '%s'\n",
strerror(e.e_err),
e.e_filename.c_str());
}
}
// When reading from stdin, dump out the last couple hundred lines so
// the user can have the text in their terminal history.
if (stdin_reader.get() != NULL) {
list<logfile *>::iterator file_iter;
struct stat st;
fstat(stdin_reader->get_fd(), &st);
file_iter = find_if(lnav_data.ld_files.begin(),
lnav_data.ld_files.end(),
same_file(st));
if (file_iter != lnav_data.ld_files.end()) {
logfile::iterator line_iter;
logfile *lf = *file_iter;
int offset;
string str;
offset = std::max((int)0, (int)lf->size() - 200);
for (line_iter = lf->begin();
line_iter != lf->end();
++line_iter) {
lf->read_line(line_iter, str);
write(STDOUT_FILENO, str.c_str(), str.size());
write(STDOUT_FILENO, "\n", 1);
}
}
}
2009-09-14 01:07:32 +00:00
}
return retval;
}