gctl/tool/dsviewer/dsviewer.cpp
2024-12-28 13:47:25 +08:00

397 lines
12 KiB
C++

/********************************************************
* ██████╗ ██████╗████████╗██╗
* ██╔════╝ ██╔════╝╚══██╔══╝██║
* ██║ ███╗██║ ██║ ██║
* ██║ ██║██║ ██║ ██║
* ╚██████╔╝╚██████╗ ██║ ███████╗
* ╚═════╝ ╚═════╝ ╚═╝ ╚══════╝
* Geophysical Computational Tools & Library (GCTL)
*
* Copyright (c) 2023 Yi Zhang (yizhang-geo@zju.edu.cn)
*
* GCTL is distributed under a dual licensing scheme. You can redistribute
* it and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either version 2
* of the License, or (at your option) any later version. You should have
* received a copy of the GNU Lesser General Public License along with this
* program. If not, see <http://www.gnu.org/licenses/>.
*
* If the terms and conditions of the LGPL v.2. would prevent you from using
* the GCTL, please consider the option to obtain a commercial license for a
* fee. These licenses are offered by the GCTL's original author. As a rule,
* licenses are provided "as-is", unlimited in time for a one time fee. Please
* send corresponding requests to: yizhang-geo@zju.edu.cn. Please do not forget
* to include some description of your company and the realm of its activities.
* Also add information on how to contact you by electronic and paper mail.
******************************************************/
#include "dsviewer.h"
// We use the editline library to handle all inputs
#include "editline.h"
cmd_pair curr_cmd; // Executing command
gctl::dsv_io tc; // Grid object
extern "C" {
/*
* Strip whitespace from the start and end of STRING. Return a pointer
* into STRING.
*/
char *stripwhite(char *string)
{
char *s, *t;
for (s = string; isspace(*s); s++) ;
if (*s == 0)
return s;
t = s + strlen(s) - 1;
while (t > s && isspace(*t))
t--;
*++t = '\0';
return s;
}
/* Generator function for command completion. STATE lets us
know whether to start from scratch; without any state
(i.e. STATE == 0), then we start at the top of the list. */
char *command_generator(const char *text, int state)
{
static int list_index, len;
/* If this is a new word to complete, initialize now. This
includes saving the length of TEXT for efficiency, and
initializing the index variable to 0. */
if (!state)
{
list_index = 0;
len = strlen(text);
}
/* Return the next name which partially matches from the command list. */
while (list_index < CMD_NUM)
{
if (std::string(text) == commands[list_index].name.substr(0, len))
{
//const char* name = commands[list_index].name.data();
return strdup(commands[list_index].name.data());
}
else list_index++;
}
/* If no names matched, then return NULL. */
return nullptr;
}
/*
* Attempt to complete on the contents of TEXT. START and END
* bound the region of rl_line_buffer that contains the word to
* complete. TEXT is the word to complete. We can use the entire
* contents of rl_line_buffer in case we want to do some simple
* parsing. Return the array of matches, or NULL if there aren't any.
*/
char **gridmanager_completion(const char *text, int start, int end)
{
char **matches = nullptr;
/* If this word is at the start of the line, then it is a command
to complete. Otherwise it is the name of a file in the current
directory. */
if (start == 0) matches = rl_completion_matches(text, command_generator);
return matches;
}
void initialize_readline(void)
{
/* Allow conditional parsing of the ~/.inputrc file. */
rl_readline_name = "gridmanager";
/* Tell the completer that we want a crack first. */
rl_attempted_completion_function = gridmanager_completion;
}
} // End C section
void display_cmds()
{
std::clog << "Command:\n";
for (size_t i = 0; i < CMD_NUM - 1; i++)
{
std::clog << std::setw(12) << commands[i].name << ":\t" << commands[i].brief << "\n";
}
std::clog << "\nEnter \"<command>?\" to see detailed instructions.\n";
return;
}
void display_help(std::string input_cmd)
{
std::string install_dir = GCTL_INSTALL_PREFIX;
std::ifstream helpin;
open_infile(helpin, install_dir + "/sbin/share/dsviewer", ".md");
std::string tmp_l, tmp_help;
std::vector<std::string> cmds;
std::vector<std::string> helps;
while (getline(helpin, tmp_l))
{
if (tmp_l.substr(0, 4) == "####")
{
cmds.push_back(tmp_l.substr(5));
tmp_help = "";
while (getline(helpin, tmp_l))
{
if (tmp_l == "") break;
else tmp_help += tmp_l + "\n";
}
helps.push_back(tmp_help);
}
}
helpin.close();
std::string cmd_str;
for (size_t j = 0; j < cmds.size(); j++)
{
parse_string_to_value(cmds[j], ' ', true, cmd_str);
if (input_cmd == cmd_str)
{
replace_all(tmp_l, cmds[j], "\\", "");
std::cout << GCTL_BOLDGREEN << tmp_l << GCTL_RESET << "\n";
std::cout << helps[j] << "\n";
break;
}
}
return;
}
void exec_cmd(std::string cmd)
{
std::string cmd_name;
parse_string_to_value(cmd, ' ', true, cmd_name);
// show instruction if there is a question mark at end of the command
if (cmd_name.back() == '?')
{
cmd_name = cmd_name.substr(0, cmd_name.length() - 1);
display_help(cmd_name);
return;
}
// set default command to null
curr_cmd = commands[CMD_NUM - 1];
for (size_t i = 0; i < CMD_NUM - 1; i++)
{
if (cmd_name == commands[i].name)
{
curr_cmd = commands[i]; break;
}
}
if (curr_cmd.func_p == nullptr) throw gctl::runtime_error("Invalid command: " + cmd_name);
std::vector<std::string> cmd_units;
parse_string_with_quotes(cmd, cmd_units);
return curr_cmd.func_p(cmd_units);
}
// This function is defined to avoid potential breakdown while running in script mode.
void quit(const std::vector<std::string> &cmd_units)
{
return;
}
void info(const std::vector<std::string> &cmd_units)
{
if (cmd_units.size() == 1) // cmd_units[0] == info
{
tc.info();
return;
}
return;
}
void set_enable(const std::vector<std::string> &cmd_units)
{
// enable column|row <column> <column>...
if (cmd_units.size() < 3) throw std::runtime_error("enable: insufficient parameters.");
if (cmd_units[1] == "column")
{
for (size_t i = 2; i < cmd_units.size(); i++)
{
if (tc.name_index(cmd_units[i]) < 0) tc.column_output(atoi(cmd_units[i].c_str()), Enable);
else tc.column_output(cmd_units[i], Enable);
}
}
else if (cmd_units[1] == "row")
{
for (size_t i = 2; i < cmd_units.size(); i++)
{
if (tc.name_index(cmd_units[i], true) < 0) tc.row_output(atoi(cmd_units[i].c_str()), Enable);
else tc.row_output(cmd_units[i], Enable);
}
}
else throw std::runtime_error("enable: invalid parameters.");
return;
}
void set_disable(const std::vector<std::string> &cmd_units)
{
// disable column|row <column> <column>...
if (cmd_units.size() < 3) throw std::runtime_error("disable: insufficient parameters.");
if (cmd_units[1] == "column")
{
for (size_t i = 2; i < cmd_units.size(); i++)
{
if (tc.name_index(cmd_units[i]) < 0) tc.column_output(atoi(cmd_units[i].c_str()), Disable);
else tc.column_output(cmd_units[i], Enable);
}
}
else if (cmd_units[1] == "row")
{
for (size_t i = 2; i < cmd_units.size(); i++)
{
if (tc.name_index(cmd_units[i], true) < 0) tc.row_output(atoi(cmd_units[i].c_str()), Disable);
else tc.row_output(cmd_units[i], Enable);
}
}
else throw std::runtime_error("disable: invalid parameters.");
return;
}
void load_file(const std::vector<std::string> &cmd_units)
{
// load <file> [nohead|hashead] [<delimeter>] [tag_sym] [att_sym] [head_num]
if (cmd_units.size() < 2) throw std::runtime_error("open: insufficient parameters.");
gctl::array<std::string> copy_str(5, "null");
for (size_t i = 0; i < GCTL_MIN(cmd_units.size() - 2, 5); i++)
{
copy_str[i] = cmd_units[i + 2];
}
table_headtype_e ht = NoHead;
if (copy_str[0] == "nohead") ht = NoHead;
else if (copy_str[0] == "column") ht = ColumnHead;
else if (copy_str[0] == "row") ht = RowHead;
else if (copy_str[0] == "both") ht = BothHead;
if (copy_str[1] != "null") tc.set_delimeter(copy_str[1][0]);
if (copy_str[2] != "null") tc.set_tag_symbol(copy_str[2][0]);
if (copy_str[3] != "null") tc.set_annotation_symbol(copy_str[3][0]);
int hnum = 0;
if (copy_str[4] != "null") hnum = atoi(copy_str[4].c_str());
if (hnum != 0) tc.set_head_number(hnum);
std::string naked_name, exten_name;
parse_filename(cmd_units[1], naked_name, exten_name);
if (exten_name == ".csv") tc.load_csv(naked_name);
else tc.load_text(naked_name, exten_name, ht);
return;
}
void save_file(const std::vector<std::string> &cmd_units)
{
// save <file> [<delimeter>] [tag_sym] [att_sym]
if (cmd_units.size() < 2) throw std::runtime_error("save: insufficient parameters.");
gctl::array<std::string> copy_str(3, "null");
for (size_t i = 0; i < GCTL_MIN(cmd_units.size() - 2, 3); i++)
{
copy_str[i] = cmd_units[i + 2];
}
if (copy_str[0] != "null") tc.set_delimeter(copy_str[0][0]);
if (copy_str[1] != "null") tc.set_tag_symbol(copy_str[1][0]);
if (copy_str[2] != "null") tc.set_annotation_symbol(copy_str[2][0]);
std::string naked_name, exten_name;
parse_filename(cmd_units[1], naked_name, exten_name);
if (exten_name == ".csv") tc.save_csv(naked_name);
else tc.save_text(naked_name, exten_name);
return;
}
void statistic(const std::vector<std::string> &cmd_units)
{
// stats <column> <column>...
if (cmd_units.size() < 2) throw std::runtime_error("stats: insufficient parameters.");
_1d_array data;
for (size_t i = 1; i < cmd_units.size(); i++)
{
tc.get_column(cmd_units[i], data);
std::clog << "column: " << cmd_units[i]
<< " | " << data.min() << "/" << data.mean() << "/" << data.max()
<< " | STD: " << data.std() << "\n";
}
return;
}
int main(int argc, char *argv[])
{
if (argc >= 2)
{
std::string tmp_l;
std::ifstream cmdin;
// Run commands from files. Each line is a command.
// You can give more than one file
for (size_t i = 1; i < argc; i++) try
{
open_infile(cmdin, argv[i]);
while (getline(cmdin, tmp_l) && tmp_l != "" && tmp_l[0] != '#')
{
exec_cmd(tmp_l);
}
cmdin.close();
}
catch(std::exception& e)
{
GCTL_ShowWhatError(e.what(), GCTL_ERROR_ERROR, 0, "run dsviewer in the interactive mode for instructions.", 0);
}
}
else
{
setlocale(LC_CTYPE, "");
initialize_readline(); /* Bind our completer. */
display_logo();
std::clog << "dsviewer - read, manipulate and write dsv/csv files.\n";
std::clog << "Enter '?' to see all available commands.\n";
std::string cmd_str;
char *c_line = (char *)NULL;
char *c_line_s = (char *)NULL;
bool quit = false;
while (!quit) try
{
c_line = readline(">> ");
if (!c_line) break;
c_line_s = stripwhite(c_line);
cmd_str = c_line_s;
if (cmd_str == "quit") quit = true;
else if (cmd_str == "?") display_cmds();
else exec_cmd(cmd_str);
free(c_line);
}
catch(std::exception& e)
{
GCTL_ShowWhatError(e.what(), GCTL_ERROR_ERROR, 0, "Enter 'help' for instructions.", 0);
}
}
return 0;
}