pax_global_header00006660000000000000000000000064146566351140014525gustar00rootroot0000000000000052 comment=247e7ff727d9a8bed410f1bbf86f247c059546d9 multitail-7.1.5/000077500000000000000000000000001465663511400135435ustar00rootroot00000000000000multitail-7.1.5/.github/000077500000000000000000000000001465663511400151035ustar00rootroot00000000000000multitail-7.1.5/.github/FUNDING.yml000066400000000000000000000000711465663511400167160ustar00rootroot00000000000000github: [ folkertvanheusden ] patreon: folkertvanheusden multitail-7.1.5/.github/workflows/000077500000000000000000000000001465663511400171405ustar00rootroot00000000000000multitail-7.1.5/.github/workflows/build.yml000066400000000000000000000005651465663511400207700ustar00rootroot00000000000000name: build and run tests on: push: branches: [ master ] pull_request: branches: [ master ] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Install dependencies run: sudo apt install libncursesw5-dev - name: cmake build run: cmake -Bbuild . - name: cmake make run: cmake --build build/ multitail-7.1.5/.gitignore000066400000000000000000000004211465663511400155300ustar00rootroot00000000000000# dependency files *.d # Object files *.o *.ko *.obj *.elf # Libraries *.lib *.a # Shared objects (inc. Windows DLLs) *.dll *.so *.so.* *.dylib # Executables *.exe *.out *.app *.i*86 *.x86_64 *.hex # Ignore the built binary multitail ccmultitail cppcheck cppcheck.xml multitail-7.1.5/CMakeLists.txt000066400000000000000000000144061465663511400163100ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.6) ## use ccache if found find_program(CCACHE_EXECUTABLE "ccache" HINTS /usr/local/bin /opt/local/bin) if(CCACHE_EXECUTABLE AND NOT CMAKE_TOOLCHAIN_FILE) message(STATUS "use ccache") set(CMAKE_C_COMPILER_LAUNCHER "${CCACHE_EXECUTABLE}" CACHE PATH "ccache" FORCE) endif() #===================== project(multitail LANGUAGES C) # set VERSION using version file file (STRINGS "version" VERSION_CONTENT) if(VERSION_CONTENT MATCHES "VERSION=([0-9]+\\.[0-9]+\\.[0-9]+)") set(VERSION "${CMAKE_MATCH_1}") message(STATUS "Project version: ${VERSION}") else() message(FATAL_ERROR "Unable to extract version from ./version") endif() #===================== # usage: #$ mkdir build && cd build #$ cmake .. #$ make DESTDIR=/tmp/multitail install # #--------------------------------------------------------------------------------------- # Compiler config #--------------------------------------------------------------------------------------- set(CMAKE_C_STANDARD 99) set(CMAKE_C_STANDARD_REQUIRED OFF) set(CMAKE_C_EXTENSIONS ON) option(COMPILER_WARNINGS_ARE_ERRORS "To be pedantic! ;-)" OFF) if(COMPILER_WARNINGS_ARE_ERRORS) if(MSVC) # warning level 4 and all warnings as errors add_compile_options(/W4 /WX) else() # lots of warnings and all warnings as errors add_compile_options(-Wall -Wextra -Wpedantic -Werror # XXX -Wno-gnu-zero-variadic-macro-arguments # XXX -Wno-gnu-conditional-omitted-operand -Wno-unused-parameter # FIXME! -Wno-sign-compare # FIXME! ) endif() endif() if(NOT CMAKE_SYSTEM_NAME STREQUAL "Darwin") option(UTF8_SUPPORT "Build with UTF8 support" ON) endif() option(USE_CPPCHECK "Looking for cppcheck program ..." ON) add_definitions(-D_FORTIFY_SOURCE=2) add_definitions(-DVERSION=\"${VERSION}\") add_definitions(-D${CMAKE_C_PLATFORM_ID}) message(STATUS "CMAKE_C_PLATFORM_ID=${CMAKE_C_PLATFORM_ID}") # Set ``CURSES_NEED_WIDE`` to ``TRUE`` before the # ``find_package(Curses)`` call if unicode functionality is required. if(UTF8_SUPPORT) set(CURSES_NEED_WIDE TRUE) find_library(PANEL_LIBRARY panelw REQUIRED) add_definitions(-DUTF8_SUPPORT) add_definitions(-DNCURSES_WIDECHAR) else() find_library(PANEL_LIBRARY panel REQUIRED) endif() message(STATUS "PANEL_LIBRARY=${PANEL_LIBRARY}") # use the lib to build bin add_executable(multitail clipboard.c cmdline.c color.c config.c cv.c diff.c error.c exec.c globals.c help.c history.c mem.c misc.c mt.c my_pty.c scrollback.c selbox.c stripstring.c term.c ui.c utils.c clipboard.h cmdline.h color.h config.h cv.h diff.h doassert.h error.h exec.h globals.h help.h history.h mem.h misc.h mt.h my_pty.h scrollback.h selbox.h stripstring.h term.h ui.h utils.h version.h ) set(EXTRA_LIBS "-lutil -lm") if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") set(CURSES_INCLUDE_PATH "/usr/include") add_definitions(-DCONFIG_FILE=\"/usr/local/etc/multitail.conf\") if (EXISTS "/usr/local/include/gnu/regex.h") add_library(gnuregex SHARED IMPORTED) set_target_properties( gnuregex PROPERTIES IMPORTED_LOCATION "/usr/local/lib/libgnuregex.so" INTERFACE_INCLUDE_DIRECTORIES "/usr/local/include/gnu") else() message(FATAL_ERROR "/usr/local/include/gnu/regex.h missing, did you install libgnuregex?") endif() else () add_definitions(-DCONFIG_FILE=\"/etc/multitail.conf\") endif() # Set ``CURSES_NEED_NCURSES`` to ``TRUE`` before the # ``find_package(Curses)`` call if NCurses functionality is required. set(CURSES_NEED_NCURSES TRUE) find_file(CURSES_INCLUDE_FILE ncurses.h) find_package(Curses REQUIRED) if(CURSES_FOUND) message(STATUS "CURSES_NCURSES_LIBRARY=${CURSES_NCURSES_LIBRARY}") include_directories(${CURSES_INCLUDE_DIRS}) endif() if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") target_link_libraries(multitail gnuregex ${CURSES_LIBRARIES} ${PANEL_LIBRARY} ${EXTRA_LIBS}) else() target_link_libraries(multitail ${CURSES_LIBRARIES} ${PANEL_LIBRARY} ${EXTRA_LIBS}) endif() if(CMAKE_SYSTEM_NAME MATCHES "CYGWIN") include(FindPkgConfig) pkg_check_modules(PCREPOSIX REQUIRED libpcreposix) target_link_libraries(multitail ${PCREPOSIX_LIBRARIES}) target_include_directories(multitail PUBLIC ${PCREPOSIX_INCLUDE_DIRS}) target_compile_options(multitail PUBLIC ${PCREPOSIX_CFLAGS_OTHER}) if(PCREPOSIX_FOUND) message(STATUS "libpcreposix found") include_directories(${CURSES_INCLUDE_DIRS}) else() message(FATAL_ERROR "libpcreposix NOT found") endif() endif() # # Where to put all the RUNTIME targets when built. This variable is used to # initialize the RUNTIME_OUTPUT_DIRECTORY property on all the targets. # set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) # install the bin install(TARGETS multitail DESTINATION bin) # install the config file install(FILES multitail.conf DESTINATION etc RENAME multitail.conf.new) # install the manual files install(FILES multitail.1 DESTINATION share/man/man1) # install doc files install(FILES manual.html DESTINATION share/doc/multitail-${VERSION}) install(FILES LICENSE DESTINATION share/doc/multitail-${VERSION}) install(FILES README.md DESTINATION share/doc/multitail-${VERSION}) install(FILES thanks.txt DESTINATION share/doc/multitail-${VERSION}) # cp conversion-scripts/* etc/multitail/ install(DIRECTORY conversion-scripts DESTINATION etc/multitail) if(USE_CPPCHECK) find_program(CPPCHECK cppcheck) find_program(HTMLREPORT cppcheck-htmlreport) if(CPPCHECK AND HTMLREPORT) message(STATUS "cppchek found at '${CPPCHECK}'; you may use target 'cppcheck' to run it!") add_custom_target(cppcheck ${CPPCHECK} --std=c99 --verbose --force --enable=all --inconclusive --template=gcc --suppress=variableScope --xml --xml-version=2 . 2> cppcheck.xml COMMAND ${HTMLREPORT} --file=cppcheck.xml --report-dir=cppcheck WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} VERBATIM) endif() endif() include(CMakePrintSystemInformation) message("CMAKE_C_LIBRARY_ARCHITECTURE ${CMAKE_C_LIBRARY_ARCHITECTURE}") set(CMAKE_BUILD_TYPE RelWithDebInfo) # vim: ft=cmake:sw=4:ts=4:smarttab:expandtab multitail-7.1.5/GNUmakefile000066400000000000000000000046221465663511400156210ustar00rootroot00000000000000# # Standard stuff # .SUFFIXES: # Disable the built-in implicit rules. MAKEFLAGS+= --no-builtin-rules .PHONY: setup all test check cppcheck install format clean distclean # see https://www.kdab.com/clang-tidy-part-1-modernize-source-code-using-c11c14/ # and https://github.com/llvm-mirror/clang-tools-extra/blob/master/clang-tidy/tool/run-clang-tidy.py # ### checkAllHeader:='include/spdlog/[acdlstv].*' ## checkAllHeader?='include/spdlog/[^f].*' checkAllHeader?='$(CURDIR)/.*' # NOTE: to many errors with boost::test # CHECKS:='-*,cppcoreguidelines-*' ## CHECKS?='-*,portability-*,readability-*,-readability-braces-around-statements,-readability-implicit-bool-conversion,-readability-named-parameter' CHECKS?='-*,misc-*,boost-*,cert-*,-misc-unused-parameters' PROJECT:=$(shell basename $$PWD) # XXX CC:=$(shell which clang) BUILD_TYPE?=Debug # GENERATOR:=Xcode GENERATOR?=Ninja BUILD_DIR:=../.build-$(PROJECT)-$(BUILD_TYPE) all: setup .configure cmake --build $(BUILD_DIR) test: all cd $(BUILD_DIR) && ctest -C $(BUILD_TYPE) --rerun-failed --output-on-failure . cd $(BUILD_DIR) && ctest -C $(BUILD_TYPE) . check: setup .configure compile_commands.json run-clang-tidy.py -header-filter=$(checkAllHeader) -checks=$(CHECKS) | tee run-clang-tidy.log 2>&1 egrep '\b(warning|error):' run-clang-tidy.log | perl -pe 's/(^.*) (warning|error):/\2/' | sort -u .configure: CMakeLists.txt cd $(BUILD_DIR) && cmake -G $(GENERATOR) -Wdev -Wdeprecated \ -DCMAKE_BUILD_TYPE=$(BUILD_TYPE) -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_C_COMPILER=${CC} $(CURDIR) touch $@ setup: $(BUILD_DIR) .clang-tidy compile_commands.json compile_commands.json: .configure ln -sf $(CURDIR)/$(BUILD_DIR)/compile_commands.json . $(BUILD_DIR): GNUmakefile mkdir -p $@ format: .clang-format find . -type f -name '*.h' -o -name '*.c' | xargs clang-format -style=file -i cppcheck: $(BUILD_DIR) cmake --build $(BUILD_DIR) --target $@ install: $(BUILD_DIR) cmake --build $(BUILD_DIR) --target $@ clean: $(BUILD_DIR) cmake --build $(BUILD_DIR) --target $@ distclean: rm -rf $(BUILD_DIR) .configure compile_commands.json *~ .*~ tags find . -name '*~' -delete # These rules keep make from trying to use the match-anything rule below # to rebuild the makefiles--ouch! ## CMakeLists.txt :: ; GNUmakefile :: ; .clang-tidy :: ; .clang-format :: ; # Anything we don't know how to build will use this rule. The command is # a do-nothing command. % :: ; multitail-7.1.5/INSTALL000066400000000000000000000027201465663511400145750ustar00rootroot00000000000000Type: make install That's it! Make sure you have the ncursesw development libraries installed. Please note: ncursesW. Also see readme.txt. * * +-------------------------------+ |Press q' to exit the program. | |For help at any time, press F1.| +-------------------------------+ * * Since version 2.8.4 you can automatically send an e-mail with information of the system you're using. That way I have some statistics on the usage of MultiTail. It basically sends the output of 'uname -a'. To invoke all of this, enter: make thanks Is your company using MultiTail and you would like to be mentioned on http://www.vanheusden.com/multitail/usedby.html ? Then please send me a logo (not too big) and a link and I'll add it to that page. IRIX users ---------- Use: make -f makefile.irix install AIX users --------- Make sure you have the GNU c-compiler installed. Then run: make install HP-UX users ----------- You need the ncurses library! Then: make -f makefile.hpux install Solaris users ------------- One needs at least version 7 of Solaris. Use: make -f makefile.solaris_gcc install or make -f makefile.solaris_sunwspro install (depending on the compiler you use). Mac OS X users: -------------- Use: make -f makefile.macosx install IRIX users: -------------- Use: make -f makefile.irix install SCO users: --------- MultiTail has been tested to run on SCO OpenServer v6. Use: make -f makefile.sco-openserver6 multitail-7.1.5/LICENSE000066400000000000000000000020641465663511400145520ustar00rootroot00000000000000MIT License Copyright (c) 2023 Folkert van Heusden Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. multitail-7.1.5/Makefile000066400000000000000000000106371465663511400152120ustar00rootroot00000000000000include version PLATFORM:=$(shell uname) CPPFLAGS:=$(shell pkg-config --cflags ncurses) NCURSES_LIB:=$(shell pkg-config --libs ncurses) DEBUG:=#XXX -g -D_DEBUG ###-pg -Wpedantic ## -pg #-fprofile-arcs # pkg-config --libs --cflags ncurses # -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 -lncurses -ltinfo UTF8_SUPPORT:=yes DESTDIR= PREFIX=/usr/local BINDIR=$(PREFIX)/bin SHAREDIR=$(PREFIX)/share MANDIR=$(SHAREDIR)/man MAN1DIR=$(MANDIR)/man1 DOCDIR=$(SHAREDIR)/doc/multitail-$(VERSION) ifeq ($(PLATFORM),FreeBSD) SYSCONFDIR=$(PREFIX)/etc else SYSCONFDIR=/etc endif CONFIG_FILE=$(SYSCONFDIR)/multitail.conf CONFIG_DIR=$(SYSCONFDIR)/multitail INSTALL = install INSTALL_DATA = $(INSTALL) -m 0644 INSTALL_EXEC = $(INSTALL) -m 0755 INSTALL_DIR = $(INSTALL) -m 0755 -d CC?=gcc CFLAGS+=-Wall -Wno-unused-parameter -funsigned-char -O3 CPPFLAGS+=-D$(PLATFORM) -DVERSION=\"$(VERSION)\" $(DEBUG) -DCONFIG_FILE=\"$(CONFIG_FILE)\" -D_FORTIFY_SOURCE=2 # build dependency files while compile (*.d) CPPFLAGS+= -MMD -MP ifeq ($(PLATFORM),Darwin) LDFLAGS+=-lpanel $(NCURSES_LIB) -lutil -lm else ifeq ($(UTF8_SUPPORT),yes) LDFLAGS+=-lpanelw -lncursesw -lutil -lm CPPFLAGS+=-DUTF8_SUPPORT else LDFLAGS+=-lpanel -lncurses -lutil -lm endif endif OBJS:=utils.o mt.o error.o my_pty.o term.o scrollback.o help.o mem.o cv.o selbox.o stripstring.o color.o misc.o ui.o exec.o diff.o config.o cmdline.o globals.o history.o clipboard.o DEPENDS:= $(OBJS:%.o=%.d) .PHONY: all check install uninstall coverity clean distclean package thanks all: multitail multitail: $(OBJS) $(CC) $(OBJS) $(LDFLAGS) -o multitail ccmultitail: $(OBJS) ccmalloc --no-wrapper -Wextra $(CC) $(OBJS) $(LDFLAGS) -o ccmultitail install: multitail $(INSTALL_DIR) $(DESTDIR)$(BINDIR) $(INSTALL_DIR) $(DESTDIR)$(MAN1DIR) $(INSTALL_DIR) $(DESTDIR)$(DOCDIR) $(INSTALL_EXEC) multitail $(DESTDIR)$(BINDIR) $(INSTALL_DATA) multitail.1 $(DESTDIR)$(MAN1DIR)/multitail.1 $(INSTALL_DATA) *.txt INSTALL manual*.html $(DESTDIR)$(DOCDIR) # ### COPIED multitail.conf.new, YOU NEED TO REPLACE THE multitail.conf ### YOURSELF WITH THE NEW FILE # $(INSTALL_DIR) $(DESTDIR)$(CONFIG_DIR) $(INSTALL_DATA) multitail.conf $(DESTDIR)$(CONFIG_FILE).new $(INSTALL_EXEC) conversion-scripts/* $(DESTDIR)$(CONFIG_DIR) #rm -f $(DESTDIR)$(MAN1DIR)/multitail.1.gz #gzip -9 $(DESTDIR)$(MAN1DIR)/multitail.1 # # There's a mailinglist! # Send an e-mail to minimalist@vanheusden.com with in the subject # 'subscribe multitail' to subscribe. # # you might want to run 'make thanks' now :-) # http://www.vanheusden.com/wishlist.php # # How do YOU use multitail? Please send me an e-mail so that I can # update the examples page. uninstall: clean rm -f $(DESTDIR)$(BINDIR)/multitail rm -f $(DESTDIR)$(MAN1DIR)/multitail.1 # rm -f $(DESTDIR)$(CONFIG_FILE) rm -rf $(DESTDIR)$(CONFIG_DIR) rm -rf $(DESTDIR)$(DOCDIR) clean: rm -f $(OBJS) multitail core gmon.out *.da ccmultitail package: clean # source package rm -rf multitail-$(VERSION)* mkdir multitail-$(VERSION) cp -a conversion-scripts *.conf *.c *.h multitail.1 manual*.html Makefile makefile.* INSTALL license.txt readme.txt thanks.txt version multitail-$(VERSION) tar czf multitail-$(VERSION).tgz multitail-$(VERSION) rm -rf multitail-$(VERSION) thanks: echo Automatic thank you e-mail for multitail $(VERSION) on a `uname -a` | mail -s "multitail $(VERSION)" folkert@vanheusden.com echo Is your company using MultiTail and you would like to be echo mentioned on http://www.vanheusden.com/multitail/usedby.html ? echo Then please send me a logo -not too big- and a link and I will echo add it to that page. echo echo Oh, blatant plug: http://keetweej.vanheusden.com/wishlist.html ### cppcheck: unusedFunction check can't be used with '-j' option. Disabling unusedFunction check. check: #XXX TBD to use cppechk --check-config $(CPPFLAGS) -I/usr/include cppcheck --std=c99 --verbose --force --enable=all --inconclusive --template=gcc \ '--suppress=variableScope' --xml --xml-version=2 . 2> cppcheck.xml cppcheck-htmlreport --file=cppcheck.xml --report-dir=cppcheck make clean -scan-build make coverity: make clean rm -rf cov-int CC=gcc cov-build --dir cov-int make all tar vczf ~/site/coverity/multitail.tgz README cov-int/ putsite -q /home/folkert/.coverity-mt.sh distclean: clean rm -rf cov-int cppcheck cppcheck.xml *.d *~ tags # include dependency files for any other rule: ifneq ($(filter-out clean distclean,$(MAKECMDGOALS)),) -include $(DEPENDS) endif multitail-7.1.5/README000066400000000000000000000000651465663511400144240ustar00rootroot00000000000000Folkert van Heusden folkert@vanheusden.com multitail multitail-7.1.5/README.md000066400000000000000000000025621465663511400150270ustar00rootroot00000000000000What is it? ----------- MultiTail allows you to monitor logfiles and command output in multiple windows in a terminal, colorize, filter and merge. http://vanheusden.com/multitail/ Check http://www.vanheusden.com/multitail/ for screenshots and more info. Help? ----- For help at any time, press F1. How to compile -------------- By default multitail requires the ncursesw library (ncursesW!) to compile and run. You can choose to use the non-wide char version (ncurses) by setting UTF8_SUPPORT to "no" in the makefile. Note that this disables UTF8 support. On Cygwin, you need the libpcre-devel package. Tips ---- You can also use MultiTail to view logfiles on other hosts! How? Like this: multitail -l "ssh username@host tail -f file" Q: but then I cannot enter the password! A1: use authentication via keys A2: or use "ssh-agent": then you only once have to enter your passphrase (so login once to that host manually, and then start MultiTail) Q & A ----- Q: the program fails then resizing the terminal-window A: solution: upgrade ncursesw to version 5.3 (or more recent) Q: when I use the -l option on some program, I get nothing in the window A: now that is strange! please tell me what program you're trying to interface to MultiTail. please do: any help is appreciated! License ------- See the LICENSE file. For fixes, updates and tweaks please send a PR via github. multitail-7.1.5/clipboard.c000066400000000000000000000041671465663511400156560ustar00rootroot00000000000000#define _LARGEFILE64_SOURCE #include #include #include #include #include #include #include #include #include "mt.h" #include "error.h" #include "config.h" #include "utils.h" #include "term.h" #include "ui.h" #include "clipboard.h" char *clipboard = "/usr/bin/" CLIPBOARD_NAME; void send_to_clipboard_binary(char *what) { int fds[2] = { 0 }; pid_t pid = -1; if (pipe(fds) == -1) error_exit(TRUE, TRUE, "error creating pipe\n"); pid = fork(); if (pid == -1) error_exit(TRUE, TRUE, "error forking\n"); if (pid == 0) { int loop; for(loop=0; loop<1024; loop++) { if (fds[0] != loop) close(loop); } signal(SIGHUP, SIG_DFL); if (dup(fds[0]) == -1) error_exit(TRUE, TRUE, "dup() failed\n"); setsid(); #ifndef __minix setpgid(0, 0); #endif if (execl(clipboard, clipboard, NULL) == -1) error_exit(TRUE, FALSE, "execl of %s failed\n", clipboard); exit(1); } WRITE(fds[1], what, strlen(what), CLIPBOARD_NAME); close(fds[1]); close(fds[0]); } void send_to_clipboard(buffer *pb) { if (file_exist(clipboard) == -1) error_popup("Copy to clipboard", -1, CLIPBOARD_NAME " program not found"); else if (getenv("DISPLAY") == NULL) error_popup("Copy to clipboard", -1, "DISPLAY environment variable not set"); else { char *data = NULL; int len_out = 0; int loop = 0; NEWWIN *mywin = create_popup(9, 40); #ifdef __APPLE__ win_header(mywin, "Copy buffer to clipboard"); #else win_header(mywin, "Copy buffer to X clipboard"); #endif mydoupdate(); for(loop=0; loop curpos; loop++) { int len = 0; if ((pb -> be)[loop].Bline == NULL) continue; len = strlen((pb -> be)[loop].Bline); data = (char *)realloc(data, len_out + len + 1); memcpy(&data[len_out], (pb -> be)[loop].Bline, len + 1); len_out += len; } send_to_clipboard_binary(data); free(data); mvwprintw(mywin -> win, 3, 2, "Finished!"); mvwprintw(mywin -> win, 4, 2, "Press any key to continue..."); mydoupdate(); (void)wait_for_keypress(-1, 0, mywin, 0); delete_popup(mywin); } } multitail-7.1.5/clipboard.h000066400000000000000000000003061465663511400156520ustar00rootroot00000000000000extern char *clipboard; #ifdef __APPLE__ #define CLIPBOARD_NAME "pbcopy" #else #define CLIPBOARD_NAME "xclip" #endif void send_to_clipboard_binary(char *what); void send_to_clipboard(buffer *pb); multitail-7.1.5/cmdline.c000066400000000000000000000741711465663511400153340ustar00rootroot00000000000000#define _LARGEFILE64_SOURCE /* required for GLIBC to enable stat64 and friends */ #include "doassert.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mt.h" #include "mem.h" #include "error.h" #include "color.h" #include "term.h" #include "utils.h" #include "config.h" #include "cv.h" #include "exec.h" #include "globals.h" #include "help.h" void add_redir_to_file(char mode, char *file, redirect_t **predir, int *n_redirect) { int cur_index = (*n_redirect)++; *predir = (redirect_t *)myrealloc(*predir, (*n_redirect) * sizeof(redirect_t)); assert(mode == 'A' || mode == 'a'); if (mode == 'a') (*predir)[cur_index].type = REDIRECTTO_FILE_FILTERED; else (*predir)[cur_index].type = REDIRECTTO_FILE; (*predir)[cur_index].redirect = mystrdup(file); (*predir)[cur_index].fd = open((*predir)[cur_index].redirect, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR); if ((*predir)[cur_index].fd == -1) error_exit(TRUE, FALSE, "%s: cannot open file %s for write access.\n", file, (*predir)[cur_index].redirect); } void add_redir_to_proc(char mode, char *proc, redirect_t **predir, int *n_redirect) { int fds_to_proc[2], fds_from_proc[2]; assert(mode == 'G' || mode == 'g'); *predir = (redirect_t *)myrealloc(*predir, (*n_redirect + 1) * sizeof(redirect_t)); memset(&(*predir)[*n_redirect], 0x00, sizeof(redirect_t)); if ((*predir)[*n_redirect].type != REDIRECTTO_NONE) error_exit(FALSE, FALSE, "One can only set one redirection-type per (sub-)window.\n"); if (mode == 'g') (*predir)[*n_redirect].type = REDIRECTTO_PIPE_FILTERED; else (*predir)[*n_redirect].type = REDIRECTTO_PIPE; (*predir)[*n_redirect].redirect = mystrdup(proc); (*predir)[*n_redirect].pid = exec_with_pipe((*predir)[*n_redirect].redirect, fds_to_proc, fds_from_proc); myclose(fds_to_proc[0]); myclose(fds_from_proc[0]); myclose(fds_from_proc[1]); (*predir)[*n_redirect].fd = fds_to_proc[1]; (*n_redirect)++; } void add_redir_to_socket(char filtered, char *prio, char *fac, char *address, redirect_t **predir, int *n_redirect) { char *local_address = mystrdup(address); char *colon = strchr(local_address, ':'); int prio_nr = -1, fac_nr = -1; int loop; char* node; char* service; struct addrinfo hints; struct addrinfo* result; struct addrinfo* rp; int s, sfd = -1; *predir = (redirect_t *)myrealloc(*predir, (*n_redirect + 1) * sizeof(redirect_t)); memset(&(*predir)[*n_redirect], 0x00, sizeof(redirect_t)); assert(filtered == 1 || filtered == 0); if (filtered) (*predir)[*n_redirect].type = REDIRECTTO_SOCKET_FILTERED; else (*predir)[*n_redirect].type = REDIRECTTO_SOCKET; (*predir)[*n_redirect].redirect = mystrdup(address); if (colon) { *colon = 0x00; node = local_address; service = colon + 1; } else { node = local_address; service = "syslog"; } memset(&hints, 0x00, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; hints.ai_flags = 0; hints.ai_protocol = 0; s = getaddrinfo(node, service, &hints, &result); if (s != 0) error_exit(TRUE, FALSE, "Cannot create socket for redirecting via syslog protocol: %s.\n", gai_strerror(s)); for (rp = result; rp != NULL; rp = rp -> ai_next) { sfd = socket(rp -> ai_family, rp -> ai_socktype, rp -> ai_protocol); if (sfd == -1) continue; if (connect(sfd, rp -> ai_addr, rp -> ai_addrlen) != -1) break; close(sfd); } freeaddrinfo(result); if (rp == NULL) error_exit(FALSE, FALSE, "Cannot create socket for redirecting via syslog protocol.\n"); (*predir)[*n_redirect].fd = sfd; for(loop=0; loop<8; loop++) { if (strcasecmp(severities[loop], prio) == 0) { prio_nr = loop; break; } } if (prio_nr == -1) error_exit(FALSE, FALSE, "Priority '%s' is not recognized.\n", prio); for(loop=0; loop<24; loop++) { if (strcasecmp(facilities[loop], fac) == 0) { fac_nr = loop; break; } } if (fac_nr == -1) error_exit(FALSE, FALSE, "Facility '%s' is not known.\n", fac); (*predir)[*n_redirect].prio_fac = (fac_nr * 8) + prio_nr; myfree(local_address); (*n_redirect)++; } void argv_set_window_widths(char *widths) { if (split != 0) error_exit(FALSE, FALSE, "-s and -sw are mutual exclusive.\n"); split = 0; for(;;) { int cur_width; char *pnt; pnt = strtok(widths, ","); if (!pnt) break; split++; vertical_split = (int *)myrealloc(vertical_split, split * sizeof(int)); cur_width = get_value_arg("-sw", pnt, VAL_ZERO_POSITIVE); widths = NULL; if (cur_width < 4) { if (cur_width == 0) cur_width = -1; else error_exit(FALSE, FALSE, "The width of a column must be 4 or greater (or '0' for automatic size).\n"); } vertical_split[split - 1] = cur_width; } if (split == 1) error_exit(FALSE, FALSE, "You have to give the width for each window or set it to 0 (=auto width).\n"); } void argv_set_n_windows_per_column(char *pars) { int index = 0; if (split == 0) error_exit(FALSE, FALSE, "First use -s or -sw to define the number of columns.\n"); for(;;) { int cur_n; char *pnt; pnt = strtok(pars, ","); if (!pnt) break; index++; n_win_per_col = (int *)myrealloc(n_win_per_col, index * sizeof(int)); cur_n = get_value_arg("-sn", pnt, VAL_ZERO_POSITIVE); pars = NULL; if (cur_n < 0) error_exit(FALSE, FALSE, "The number of windows must be either 0 (=auto) or >= 1.\n"); n_win_per_col[index - 1] = cur_n; } } int argv_add_re(char *mode, char *pars[], char invert_regex, re **pre_cur, int *n_re_cur, re **pre_all, int *n_re_all) { char *cmd = NULL, *expr = NULL; char regex_mode = 0; int n_pars_used = 0; re **pre = pre_cur; int *n_re = n_re_cur; /* -e => only for this file, -E => for all following files */ if (mode[1] == 'E') { pre = pre_all; n_re = n_re_all; } /* c/C/m define colors, x says: execute */ if (toupper(mode[2]) == 'C') regex_mode = mode[2]; else if (toupper(mode[2]) == 'B') regex_mode = mode[2]; else if (mode[2] == 'm' || mode[2] == 0x00) regex_mode = 'm'; /* m = match, only print when matches */ else if (mode[2] == 'v') regex_mode = 'v'; /* v = !match, only print when not matching */ else if (toupper(mode[2]) == 'X') regex_mode = mode[2]; /* x = execute */ else error_exit(FALSE, FALSE, "%s is an unknown switch.\n", mode); /* get expression */ expr = pars[n_pars_used++]; /* and if there's anything to execute, get commandline */ if (toupper(regex_mode) == 'X') { cmd = pars[n_pars_used++]; if (regex_mode == 'X' && (strchr(expr, '(') == NULL || strchr(expr, ')') == NULL)) error_exit(FALSE, FALSE, "Filterscheme rule: -eX requires a regular expression which selects a substring using '(' and ')'.\n"); } /* compile & set expression */ /* allocate new structure */ *pre = (re *)myrealloc(*pre, sizeof(re) * (*n_re + 1)); /* initialize structure */ memset(&(*pre)[*n_re], 0x00, sizeof(re)); /* compile */ compile_re(&(*pre)[*n_re].regex, expr); /* remember string for later edit */ (*pre)[*n_re].regex_str = mystrdup(expr); /* set flag on current file */ (*pre)[*n_re].use_regex = regex_mode; if (mode[1] == 'E') regex_mode = 0; /* wether to invert the reg exp or not */ if ((regex_mode == 'v' || regex_mode == 'm') && invert_regex) error_exit(FALSE, FALSE, "-e[m] / -ev cannot be used together with -v\n"); (*pre)[*n_re].invert_regex = invert_regex; /* what to execute (if...) */ if (cmd) (*pre)[*n_re].cmd = mystrdup(cmd); else (*pre)[*n_re].cmd = NULL; (*n_re)++; return n_pars_used; } int argv_color_settings(char *mode, char *pars[], char *allcolor, char *curcolor, int *field_index, char **field_delimiter, myattr_t *cdef, term_t *cur_term_emul, int_array_t *cur_color_schemes, myattr_t *alt_col_cdev1, myattr_t *alt_col_cdev2, char *doall) { int n_pars_used = 0; char cur_mode = mode[2]; *doall = 0; if (mode[1] == 'C') *doall = 1; if (cur_mode == 's') /* syslog-file coloring? */ { } else if (cur_mode == 'a') /* alternating colors */ { *alt_col_cdev1 = parse_attributes(pars[n_pars_used++]); *alt_col_cdev2 = parse_attributes(pars[n_pars_used++]); } else if (cur_mode == 'i') /* use one specific color */ { *cdef = parse_attributes(pars[n_pars_used++]); } else if (cur_mode == 'T') /* terminal mode */ { if (pars[n_pars_used] != NULL && (strcasecmp(pars[n_pars_used], "ANSI") == 0 || strcasecmp(pars[n_pars_used], "vt100") == 0)) *cur_term_emul = TERM_ANSI; else error_exit(FALSE, FALSE, "Terminal emulation '%s' is not known.\n", pars[n_pars_used]); n_pars_used++; } else if (cur_mode == 'S') /* use colorscheme */ { int cur_scheme_index; char *cur_cscheme = pars[n_pars_used++]; if (!cur_cscheme) error_exit(FALSE, FALSE, "%s requires a color scheme name.\n", mode); if ((cur_scheme_index = find_colorscheme(cur_cscheme)) == -1) { if (use_colors) error_exit(FALSE, FALSE, "Color scheme %s not found! Please check your configuration file.\n", cur_cscheme); else error_exit(FALSE, FALSE, "Color schemes are not supported on monochrome terminals.\n"); } add_color_scheme(cur_color_schemes, cur_scheme_index); } else if (cur_mode == '-') /* do not color current */ { cur_mode = 'n'; } else if (cur_mode == 'f') /* select field for coloring */ { *field_index = get_value_arg(mode, pars[n_pars_used++], VAL_ZERO_POSITIVE); *field_delimiter = pars[n_pars_used++]; } else if (cur_mode == 0x00) /* use complete line for coloring */ { cur_mode = 'm'; } else { error_exit(FALSE, FALSE, "Invalid -c mode: '%c'.\n", cur_mode); } if (*doall) { *allcolor = cur_mode; *curcolor = 'n'; } else { *curcolor = cur_mode; *allcolor = 'n'; } return n_pars_used; } int argv_add_stripper(char *mode, char *pars[], strip_t **pstrip, int *n_strip) { int n_pars_used = 0; if (mode[2] == 'e' || mode[2] == 'S') { char *re = pars[n_pars_used++]; *pstrip = myrealloc(*pstrip, (*n_strip + 1) * sizeof(strip_t)); if (mode[2] == 'e') (*pstrip)[*n_strip].type = STRIP_TYPE_REGEXP; else if (mode[2] == 'S') (*pstrip)[*n_strip].type = STRIP_KEEP_SUBSTR; (*pstrip)[*n_strip].regex_str = mystrdup(re); (*pstrip)[*n_strip].del = NULL; compile_re(&(*pstrip)[*n_strip].regex, re); (*n_strip)++; } else if (mode[2] == 'r') { *pstrip = myrealloc(*pstrip, (*n_strip + 1) * sizeof(strip_t)); (*pstrip)[*n_strip].type = STRIP_TYPE_RANGE; (*pstrip)[*n_strip].start = get_value_arg(mode, pars[n_pars_used++], VAL_ZERO_POSITIVE); (*pstrip)[*n_strip].end = get_value_arg(mode, pars[n_pars_used++], VAL_ZERO_POSITIVE); (*pstrip)[*n_strip].del = NULL; if ((*pstrip)[*n_strip].end <= (*pstrip)[*n_strip].start) error_exit(FALSE, FALSE, "'-kr start end': end must be higher then start.\n"); (*n_strip)++; } else if (mode[2] == 'c') { *pstrip = myrealloc(*pstrip, (*n_strip + 1) * sizeof(strip_t)); (*pstrip)[*n_strip].type = STRIP_TYPE_COLUMN; (*pstrip)[*n_strip].del = mystrdup(pars[n_pars_used++]); (*pstrip)[*n_strip].col_nr = get_value_arg(mode, pars[n_pars_used++], VAL_ZERO_POSITIVE); (*n_strip)++; } else if (mode[2] == 's') { char *scheme = pars[n_pars_used++]; int editscheme_index = find_editscheme(scheme); if (editscheme_index == -1) error_exit(FALSE, FALSE, "-ks %s: scheme not found.\n", scheme); duplicate_es_array(pes[editscheme_index].strips, pes[editscheme_index].n_strips, pstrip, n_strip); } else error_exit(FALSE, FALSE, "'%s' is not recognized.\n", mode); return n_pars_used; } void add_glob_check(const char *check_glob, int check_interval, char merge, char new_only, const char *cs, const char *label) { cdg = (check_dir_glob *)myrealloc(cdg, sizeof(check_dir_glob) * (n_cdg + 1)); cdg[n_cdg].glob_str = mystrdup(check_glob); cdg[n_cdg].check_interval = check_interval; cdg[n_cdg].in_one_window = merge; cdg[n_cdg].new_only = new_only; cdg[n_cdg].window_nr = -1; cdg[n_cdg].last_check = (dtime_t)0.0; cdg[n_cdg].color_scheme = cs; cdg[n_cdg].label = label ? mystrdup(label) : NULL; n_cdg++; } void do_commandline(int argc, char *argv[]) { int loop; char curcolor = 'n', allcolor = 'n'; int field_index = 0; char *field_delimiter = NULL; char follow_filename = -1, retry = 0, invert_regex = 0; char retry_all = 0; int maxlines = -1; char setallmaxlines = 0; int restart = -1; char restart_clear = 0; char do_diff = 0; char cur_line_wrap = -1; int cur_line_wrap_offset = -1; char all_line_wrap = 0; char *label = NULL; re *pre = NULL; int n_re = 0; re *pre_all = NULL; int n_re_all = 0; int_array_t cur_color_schemes = { NULL, 0, 0 }; myattr_t cdef = { -1, -1 }; int window_height = -1; int initial_n_lines_tail = min_n_bufferlines; char *win_title = NULL; term_t cur_term_emul = TERM_IGNORE; strip_t *pstrip = NULL; int n_strip = 0; int n_redirect = 0; redirect_t *predir = NULL; mybool_t used_stdin = MY_FALSE; char merge_all = 0; char merge_in_new_first = 0; int close_idle = 0; char setallmaxbytes = 0; int maxbytes = -1; myattr_t alt_col_cdev1 = { -1, -1 }, alt_col_cdev2 = { -1, -1 }; time_field_t new_only = 0; char no_repeat = 0; int mark_interval = 0; char syslog_noreverse = 0; char cont = 0; char marker_of_other_window = 0; char no_marker_of_other_window = 0; char bufferwhat = -1; int cur_beep_interval = -1; char doallterm = 0; char do_add_timestamp = 0; int_array_t conversions = { NULL, 0, 0 }; char all_label = 0; /* first, before we load the main configfile, see if we should load the global * file or not */ for(loop=1; loop= 0.\n"); } else error_exit(FALSE, FALSE, "-H requires a parameter.\n"); } else if (strcasecmp(argv[loop], "-a") == 0) { char mode = argv[loop][1]; add_redir_to_file(mode, argv[++loop], &predir, &n_redirect); } else if (strcasecmp(argv[loop], "-g") == 0) { char mode = argv[loop][1]; add_redir_to_proc(mode, argv[++loop], &predir, &n_redirect); } else if (argv[loop][0] == '-' && argv[loop][1] == 'U') { char *prio, *fac, *addr; char filtered; /* -U[af][as] host[:port] */ if (argv[loop][2] != 'a' && argv[loop][2] != 'f') error_exit(FALSE, FALSE, "-Ux where x needs to be either 'a' or 'f'"); filtered = argv[loop][2] == 'f'; prio = argv[++loop]; fac = argv[++loop]; addr = argv[++loop]; add_redir_to_socket(filtered, prio, fac, addr, &predir, &n_redirect); } else if (strcmp(argv[loop], "-F") == 0 || strcmp(argv[loop], "--config") == 0) { config_file = argv[++loop]; if (file_exist(config_file) == -1) error_exit(FALSE, FALSE, "Configuration file %s does not exist.\n", config_file); (void)do_load_config(-1, NULL, config_file); } else if (strcmp(argv[loop], "-Z") == 0) { markerline_attrs = parse_attributes(argv[++loop]); } else if (strcmp(argv[loop], "-T") == 0) { timestamp_in_markerline = 1; } else if (strcmp(argv[loop], "-S") == 0) { show_subwindow_id = 1; } else if (strcmp(argv[loop], "-t") == 0) { ++loop; if (!argv[loop]) error_exit(FALSE, FALSE, "-t requires a parameter.\n"); win_title = mystrdup(argv[loop]); } else if (strcmp(argv[loop], "-x") == 0) { ++loop; if (!argv[loop]) error_exit(FALSE, FALSE, "-x requires a parameter.\n"); set_title = mystrdup(argv[loop]); } else if (argv[loop][0] == '-' && toupper(argv[loop][1]) == 'P') { char *lw = argv[++loop]; if (lw) { if (argv[loop - 1][1] == 'P') all_line_wrap = 1; else all_line_wrap = 0; cur_line_wrap = lw[0]; if (cur_line_wrap == 'o') cur_line_wrap_offset = get_value_arg("-p", argv[++loop], VAL_ZERO_POSITIVE); else if (cur_line_wrap != 'a' && cur_line_wrap != 'l' && cur_line_wrap != 'r' && toupper(cur_line_wrap) != 'S' && cur_line_wrap != 'w') error_exit(FALSE, FALSE, "Invalid mode for -p\n"); } else error_exit(FALSE, FALSE, "-p requires a parameter.\n"); } else if (strcmp(argv[loop], "--retry") == 0) { retry = 1; } else if (strcmp(argv[loop], "--retry-all") == 0) { retry_all = 1; retry = 1; } else if (strcmp(argv[loop], "-n") == 0) { initial_n_lines_tail = get_value_arg("-n", argv[++loop], VAL_ZERO_POSITIVE); } else if (strcmp(argv[loop], "-N") == 0) { initial_n_lines_tail = min_n_bufferlines = get_value_arg("-n", argv[++loop], VAL_ZERO_POSITIVE); } else if (strcmp(argv[loop], "-b") == 0) { tab_width = get_value_arg("-b", argv[++loop], VAL_POSITIVE); } else if (strcmp(argv[loop], "-u") == 0) { update_interval = get_value_arg("-u", argv[++loop], VAL_ZERO_POSITIVE); } else if (argv[loop][0] == '-' && toupper(argv[loop][1]) == 'R') { if (argv[loop][1] == 'R') do_diff = 1; if (argv[loop][2] == 'c') restart_clear = 1; restart = get_value_arg("-r/R", argv[++loop], VAL_ZERO_POSITIVE); } else if (strcmp(argv[loop], "-s") == 0) { split = get_value_arg("-s", argv[++loop], VAL_POSITIVE_NOT_1); } else if (strcmp(argv[loop], "-sw") == 0) { argv_set_window_widths(argv[++loop]); } else if (strcmp(argv[loop], "-sn") == 0) { argv_set_n_windows_per_column(argv[++loop]); } else if (strcmp(argv[loop], "-wh") == 0) { window_height = get_value_arg("-wh", argv[++loop], VAL_POSITIVE); } else if (strcmp(argv[loop], "-fr") == 0) { char *par = argv[++loop]; for(;;) { int filter; char *komma = strchr(par, ','); if (komma) *komma = 0x00; filter = find_filterscheme(par); if (filter == -1) error_exit(FALSE, FALSE, "'%s' is not a known filter scheme.\n", par); duplicate_re_array(pfs[filter].pre, pfs[filter].n_re, &pre, &n_re); if (!komma) break; par = komma + 1; } } else if (strcmp(argv[loop], "-cv") == 0) { char *par = argv[++loop]; for(;;) { char *komma = strchr(par, ','); if (komma) *komma = 0x00; add_conversion_scheme(&conversions, par); if (!komma) break; par = komma + 1; } } else if (argv[loop][0] == '-' && toupper(argv[loop][1]) == 'E') { loop += argv_add_re(argv[loop], &argv[loop + 1], invert_regex, &pre, &n_re, &pre_all, &n_re_all); } else if (strcmp(argv[loop], "-v") == 0) { invert_regex = 1; } else if (strcmp(argv[loop], "-csn") == 0) { syslog_noreverse = 1; } else if (argv[loop][0] == '-' && toupper(argv[loop][1]) == 'C') { loop += argv_color_settings(argv[loop], &argv[loop + 1], &allcolor, &curcolor, &field_index, &field_delimiter, &cdef, &cur_term_emul, &cur_color_schemes, &alt_col_cdev1, &alt_col_cdev2, &doallterm); } else if (strcmp(argv[loop], "-f") == 0) { follow_filename = 1; } else if (strcmp(argv[loop], "--follow-all") == 0) { default_follow_filename = 1; follow_filename = 1; } else if (strcmp(argv[loop], "-w") == 0) { use_colors = 0; } else if (argv[loop][0] == '-' && argv[loop][1] == 'k') { loop += argv_add_stripper(argv[loop], &argv[loop + 1], &pstrip, &n_strip); } else if (strcasecmp(argv[loop], "-m") == 0) { if (argv[loop][1] == 'M') setallmaxlines = 1; maxlines = get_value_arg("-m/M", argv[++loop], VAL_ZERO_POSITIVE); } else if (strcasecmp(argv[loop], "-mb") == 0) { if (argv[loop][1] == 'M') setallmaxbytes = 1; maxbytes = get_value_arg("-m/M", argv[++loop], VAL_ZERO_POSITIVE); } else if (strcasecmp(argv[loop], "--label") == 0) { label = mystrdup(argv[++loop]); all_label = 0; } else if (strcasecmp(argv[loop], "--all-label") == 0) { label = mystrdup(argv[++loop]); all_label = 1; } else if (strcasecmp(argv[loop], "-i") == 0 || argv[loop][0] != '-' || strcasecmp(argv[loop], "-l") == 0 || strcasecmp(argv[loop], "-j") == 0 || strcasecmp(argv[loop], "-iw") == 0 || strcasecmp(argv[loop], "--listen") == 0) { struct stat64 buf; char *dummy; char is_cmd = 0; char is_sub = 0; char is_giw = 0; char is_sock = 0; char is_stdin = 0; int check_interval = 0; proginfo *cur; if (strcasecmp(argv[loop], "-l") == 0) { is_cmd = 1; } else if (strcasecmp(argv[loop], "-j") == 0) { if (used_stdin == MY_TRUE) error_exit(FALSE, FALSE, "One can use %s only once.\n", argv[loop]); is_stdin = used_stdin = 1; } else if (strcasecmp(argv[loop], "-iw") == 0) { if (argv[loop + 2]) { if ((argc - loop) > 2 && isdigit(argv[loop+2][0])) { is_giw = 1; check_interval = atoi(argv[loop + 2]); } else { check_interval = 5; } } else error_exit(FALSE, FALSE, "-iw requires 2 parameters.\n"); } else if (strcasecmp(argv[loop], "--listen") == 0) { is_sock = 1; } if (strcmp(argv[loop], "-L") == 0 || strcmp(argv[loop], "-I") == 0 || strcmp(argv[loop], "-Iw") == 0 || strcmp(argv[loop], "-J") == 0 || strcmp(argv[loop], "--Listen") == 0 || merge_all) { is_sub = 1; if (merge_all & (merge_in_new_first == 1)) { is_sub = 0; merge_in_new_first = 0; } } if (argv[loop][0] == '-' && toupper(argv[loop][1]) != 'J') { loop++; } dummy = argv[loop]; if (is_sub == 1 && nfd > 0) { cur = &pi[nfd - 1]; while(cur -> next) { cur = cur -> next; } cur -> next = (proginfo *)mymalloc(sizeof(proginfo)); cur = cur -> next; nsubwindows[nfd-1]++; } else { pi = (proginfo *)myrealloc(pi, (nfd + 1) * sizeof(proginfo)); lb = (buffer *)myrealloc(lb, (nfd + 1) * sizeof(buffer)); nsubwindows = (char *)myrealloc(nsubwindows, (nfd + 1) * sizeof(char)); nsubwindows[nfd] = 1; memset(&lb[nfd], 0x00, sizeof(buffer)); lb[nfd].maxnlines = maxlines; if (!setallmaxlines) maxlines = -1; lb[nfd].bufferwhat = bufferwhat; bufferwhat = default_bufferwhat; lb[nfd].maxbytes = maxbytes; if (!setallmaxbytes) maxbytes = -1; if (marker_of_other_window || lb[nfd].marker_of_other_window) lb[nfd].marker_of_other_window = 1; else if (no_marker_of_other_window == 1) lb[nfd].marker_of_other_window = -1; /* override global configfile setting */ no_marker_of_other_window = marker_of_other_window = 0; cur = &pi[nfd]; nfd++; } memset(cur, 0x00, sizeof(proginfo)); /* see if file exists */ if (check_interval == 0 && is_cmd == 0 && is_stdin == 0 && is_sock == 0 && retry == 0 && stat64(dummy, &buf) == -1) { fprintf(stderr, "Error opening file %s (%s)\n", dummy, strerror(errno)); exit(EXIT_FAILURE); } /* init. struct. for this file */ if (is_stdin == 0) { if (!dummy) error_exit(FALSE, FALSE, "No filename given.\n"); cur -> filename = mystrdup(dummy); } else { cur -> filename = mystrdup("STDIN"); } if (is_cmd) cur -> wt = WT_COMMAND; else if (is_stdin) cur -> wt = WT_STDIN; else if (is_sock) cur -> wt = WT_SOCKET; else cur -> wt = WT_FILE; cur -> check_interval = check_interval; cur -> win_title = win_title; win_title = NULL; cur -> close_idle = close_idle; close_idle = 0; /* initial number of lines to tail */ cur -> initial_n_lines_tail = initial_n_lines_tail; initial_n_lines_tail = min_n_bufferlines; /* default window height */ cur -> win_height = window_height; window_height = -1; /* store regular expression(s) */ cur -> pre = pre; cur -> n_re = n_re; pre = NULL; n_re = 0; if (n_re_all > 0) duplicate_re_array(pre_all, n_re_all, &cur -> pre, &cur -> n_re); /* hide this window? */ cur -> hidden = 0; /* add timestamp in front of each line? */ cur -> add_timestamp = do_add_timestamp; do_add_timestamp = 0; /* strip */ cur -> pstrip = pstrip; cur -> n_strip = n_strip; pstrip = NULL; n_strip = 0; cur -> conversions = conversions; init_iat(&conversions); /* line wrap */ cur -> line_wrap = cur_line_wrap; cur -> line_wrap_offset = cur_line_wrap_offset; if (!all_line_wrap) { cur_line_wrap = default_linewrap; cur_line_wrap_offset = default_line_wrap_offset; } cur -> retry_open = retry; cur -> follow_filename = follow_filename; follow_filename = default_follow_filename; cur -> cont = cont; cont = 0; /* 'watch' functionality configuration (more or less) */ cur -> restart.restart = restart; cur -> restart.restart_clear = restart_clear; restart = -1; /* invalidate for next parameter */ restart_clear = 0; cur -> restart.first = 1; cur -> restart.do_diff = do_diff; do_diff = 0; cur -> repeat.suppress_repeating_lines = no_repeat; cur -> mark_interval = mark_interval; /* colors */ cur -> cdef.attributes = cdef; cur -> cdef.alt_col_cdev1 = alt_col_cdev1; cur -> cdef.alt_col_cdev2 = alt_col_cdev2; cur -> cdef.syslog_noreverse = syslog_noreverse; if (curcolor != 'n' && curcolor != 0) { cur -> cdef.colorize = curcolor; cur -> cdef.color_schemes = cur_color_schemes; init_iat(&cur_color_schemes); } else { if (allcolor == 'n' && default_color_scheme != -1) { cur -> cdef.colorize = 'S'; add_color_scheme(&cur -> cdef.color_schemes, default_color_scheme); } else if (allcolor == 'n') { cur -> cdef.colorize = 0; } else if (allcolor == 'S') { cur -> cdef.colorize = 'S'; cur -> cdef.color_schemes = cur_color_schemes; } else { cur -> cdef.colorize = allcolor; } } cur -> cdef.field_nr = field_index; cur -> cdef.field_del = field_delimiter ? mystrdup(field_delimiter) : NULL; cur -> statistics.sccfirst = 1; curcolor = 'n'; if (!retry_all) retry = 0; if (is_giw) /* skip over the checkinterval */ loop++; cur -> cdef.term_emul = cur_term_emul; if (doallterm == 0) cur_term_emul = TERM_IGNORE; /* redirect input also to a file or pipe */ cur -> n_redirect = n_redirect; n_redirect = 0; cur -> predir = predir; predir = NULL; cur -> beep.beep_interval = cur_beep_interval; cur_beep_interval = -1; cur -> label = label ? strdup(label) : NULL; if (!all_label) label = NULL; } else if (strcmp(argv[loop], "-du") == 0) { statusline_above_data = 1; } else if (strcmp(argv[loop], "-d") == 0) { mode_statusline = 0; } else if (strcmp(argv[loop], "-D") == 0) { mode_statusline = -1; } else if (strcmp(argv[loop], "-z") == 0) { warn_closed = 0; } else if (strcmp(argv[loop], "--new-only") == 0) { char *type = argv[++loop]; if (type) { if (strcasecmp(type, "atime") == 0) new_only = TT_ATIME; else if (strcasecmp(type, "mtime") == 0) new_only = TT_MTIME; else if (strcasecmp(type, "ctime") == 0) new_only = TT_CTIME; else error_exit(FALSE, FALSE, "--new-only requires either atime, mtime or ctime as parameter, not '%s'.\n", type); } else error_exit(FALSE, FALSE, "--new-only requires a parameter.\n"); } else if (strcasecmp(argv[loop], "-q") == 0 || strcasecmp(argv[loop], "-qs") == 0) { int cmd_index = loop; int merge = argv[cmd_index][1] == 'Q'; int check_interval = get_value_arg("-q[s]/-Q[s]", argv[++loop], VAL_ZERO_POSITIVE); const char *default_color_scheme = NULL, *check_glob = NULL; if (argv[cmd_index][2] == 's') default_color_scheme = argv[++loop]; check_glob = argv[++loop]; if (default_color_scheme && find_colorscheme(default_color_scheme) == -1) error_exit(FALSE, FALSE, "Color scheme '%s' is not known.\n", default_color_scheme); add_glob_check(check_glob, check_interval, merge, new_only, default_color_scheme, label); } else if (strcmp(argv[loop], "--mark-change") == 0) { marker_of_other_window = 1; } else if (strcmp(argv[loop], "--no-mark-change") == 0) { no_marker_of_other_window = 1; } else if (strcmp(argv[loop], "-o") == 0) { loop++; if (!argv[loop]) error_exit(FALSE, FALSE, "-o requires a parameter.\n"); else config_file_entry(-1, argv[loop]); } else if (strcmp(argv[loop], "--beep-interval") == 0) { beep_interval = get_value_arg("--beep-interval", argv[++loop], VAL_POSITIVE); } else if (strcmp(argv[loop], "--bi") == 0) { cur_beep_interval = get_value_arg("--bi", argv[++loop], VAL_POSITIVE); } else { if (strcmp(argv[loop], "-h") != 0) fprintf(stderr, "\nunknown parameter '%s'\n\n", argv[loop]); usage(); exit(1); } } for(loop=0; loop #include #include #include #include #include #include #include "mt.h" #include "error.h" #include "mem.h" #include "term.h" #include "exec.h" #include "utils.h" #include "globals.h" #include "ui.h" color_offset_in_line *realloc_color_offset_in_line(color_offset_in_line *oldp, int n_entries) { assert(n_entries > 0); return (color_offset_in_line *)myrealloc(oldp, n_entries * sizeof(color_offset_in_line)); } void add_color_scheme(int_array_t *schemes, int cur_scheme) { assert(cur_scheme >= 0); add_to_iat(schemes, cur_scheme); } int gen_color(char *start, char *end) { char *loop; int chk = 0; assert(end != NULL); for(loop=start; loop= 16) { string = strchr(&string[16], ' '); } if (string) { char *end1, *end2, *end3, *end = NULL; while(isspace(*string)) string++; end1 = strchr(string, '['); end2 = strchr(string, ' '); end3 = strchr(string, ':'); end = end1; if ((end2 && end2 < end) || (end == NULL)) end = end2; if ((end3 && end3 < end) || (end == NULL)) end = end3; if (end) cdev.colorpair_index = gen_color(string, end); else cdev.colorpair_index = gen_color(string, &string[strlen(string)]); } } return cdev; } myattr_t gen_color_from_field(char *string, char *field_del, int field_nr) { myattr_t cdev = { -1, -1 }; assert(field_nr >= 0); if (use_colors) { int field_del_len = strlen(field_del), loop; char *dummy = NULL; for(loop=0; loop fd_w, line_buf, line_len + 1, pscript -> script); myfree(line_buf); for(;!finished;) { char *workpnt = iobuffer; int rc = READ(pscript -> fd_r, iobuffer, SCRIPT_IO_BUFFER_SIZE - 1, pscript -> script); if (rc <= 1) break; iobuffer[rc] = 0x00; for(;;) { char *komma; char *lf = strchr(workpnt, '\n'); if (!lf) break; *lf = 0x00; if (workpnt[0] == 0x00) { finished = 1; break; } if (*cur_n_cmatches == *n_cmatches) { *cur_n_cmatches = (*cur_n_cmatches + 8) * 2; *cmatches = realloc_color_offset_in_line(*cmatches, *cur_n_cmatches); } komma = strchr(workpnt, ','); if (!komma) { error_popup("Color scheme script", -1, "Malformed line returned by color selection script '%s': first ',' missing.\n", pscript -> script); break; } memset(&(*cmatches)[*n_cmatches], 0x00, sizeof(color_offset_in_line)); (*cmatches)[*n_cmatches].start = atoi(workpnt); (*cmatches)[*n_cmatches].end = atoi(komma + 1); komma = strchr(komma + 1, ','); if (!komma) { error_popup("Color scheme script", -1, "Malformed line returned by color selection script '%s': second ',' missing.\n", pscript -> script); break; } (*cmatches)[*n_cmatches].attrs = parse_attributes(komma + 1); (*n_cmatches)++; workpnt = lf + 1; } } myfree(iobuffer); } void get_colors_from_colorscheme(char *line, int_array_t *color_schemes, color_offset_in_line **cmatches, int *n_cmatches, mybool_t *has_merge_colors) { int cs_index, loop; regmatch_t colormatches[MAX_N_RE_MATCHES]; int cur_n_cmatches = 0; int len = strlen(line); *n_cmatches = 0; *cmatches = NULL; *has_merge_colors = MY_FALSE; for(loop=0; loop 0) memcpy(valstr, &line[this_start_offset], val_size); valstr[val_size] = 0x00; value = atof(valstr); myfree(valstr); if (flags == CSREFLAG_CMP_VAL_LESS && value < cschemes[get_iat_element(color_schemes, loop)].pentries[cs_index].cmp_value) value_match = 1; else if (flags == CSREFLAG_CMP_VAL_BIGGER && value > cschemes[get_iat_element(color_schemes, loop)].pentries[cs_index].cmp_value) value_match = 1; else if (flags == CSREFLAG_CMP_VAL_EQUAL && value == cschemes[get_iat_element(color_schemes, loop)].pentries[cs_index].cmp_value) value_match = 1; if (!value_match) continue; } if (cur_n_cmatches == *n_cmatches) { cur_n_cmatches = (cur_n_cmatches + 8) * 2; *cmatches = realloc_color_offset_in_line(*cmatches, cur_n_cmatches); } memset(&(*cmatches)[*n_cmatches], 0x00, sizeof(color_offset_in_line)); (*cmatches)[*n_cmatches].start = this_start_offset; (*cmatches)[*n_cmatches].end = this_end_offset; if (cschemes[get_iat_element(color_schemes, loop)].pentries[cs_index].cdef.ac_index == 0) (*cmatches)[*n_cmatches].attrs = cschemes[get_iat_element(color_schemes, loop)].pentries[cs_index].cdef.attrs1; else (*cmatches)[*n_cmatches].attrs = cschemes[get_iat_element(color_schemes, loop)].pentries[cs_index].cdef.attrs2; if (cschemes[get_iat_element(color_schemes, loop)].pentries[cs_index].merge_color == MY_TRUE) *has_merge_colors = MY_TRUE; (*cmatches)[*n_cmatches].merge_color = cschemes[get_iat_element(color_schemes, loop)].pentries[cs_index].merge_color; (*n_cmatches)++; } /* iterate all substringmatches or just the first which is the globl one */ /* in case we have alternating colors, alternate them */ if (cschemes[get_iat_element(color_schemes, loop)].pentries[cs_index].cdef.use_alternating_colors == MY_TRUE) { cschemes[get_iat_element(color_schemes, loop)].pentries[cs_index].cdef.ac_index = 1 - cschemes[get_iat_element(color_schemes, loop)].pentries[cs_index].cdef.ac_index; } } while(offset < len); /* do the whole line */ } /* go through all lines of the current colorscheme */ } /* go through all colorschemes for this (sub-)window */ } myattr_t choose_color(char *string, proginfo *cur, color_offset_in_line **cmatches, int *n_cmatches, mybool_t *has_merge_colors, char **new_string) { myattr_t cdev = { -1, -1 }; *new_string = NULL; if (cur -> cdef.term_emul != TERM_IGNORE) { *new_string = emulate_terminal(string, cmatches, n_cmatches); return find_attr(COLOR_WHITE, -1, -1); } /* find color */ switch(cur -> cdef.colorize) { case 'i': cdev = cur -> cdef.attributes; break; case 'a': /* alternating colors */ if (string[0] != 0x00) { if (cur -> cdef.alt_col) cdev = cur -> cdef.alt_col_cdev1; else cdev = cur -> cdef.alt_col_cdev2; cur -> cdef.alt_col = !cur -> cdef.alt_col; } break; case 's': /* use the program name for the color */ cdev = gen_syslog_progname_color(string); break; case 'f': /* user selected field for coloring */ cdev = gen_color_from_field(string, cur -> cdef.field_del, cur -> cdef.field_nr); break; case 'm': /* complete string */ if (use_colors) cdev.colorpair_index = gen_color(string, &string[strlen(string)]); break; case 'S': /* colorscheme or colorscripts */ get_colors_from_colorscheme(string, &cur -> cdef.color_schemes, cmatches, n_cmatches, has_merge_colors); break; default: assert(0); break; } if (cdev.colorpair_index == -1) cdev.colorpair_index = 0; if (cdev.attrs == -1) cdev.attrs = A_NORMAL; if (cur -> cdef.syslog_noreverse) cdev.colorpair_index %= DEFAULT_COLORPAIRS; return cdev; } int find_colorscheme(const char *name) { int loop; for(loop=0; loop #include #include #include #include #include #include #include #include #include #include #include #include #include "mt.h" #include "mem.h" #include "error.h" #include "utils.h" #include "color.h" #include "term.h" #include "exec.h" #include "globals.h" #include "config.h" #include "clipboard.h" /* "local global" */ int cur_colorscheme_nr = -1; int cur_filterscheme_nr = -1; int cur_editscheme_nr = -1; mybool_t config_yes_no(char *what) { if (what[0] == '1' || strcasecmp(what, "yes") == 0 || strcasecmp(what, "y") == 0 || strcasecmp(what, "on") == 0) { return MY_TRUE; } return MY_FALSE; } long long int kb_str_to_value(char *field, char *str) { char *mult; long long int bytes = atoll(str); if (bytes < -1) error_exit(FALSE, FALSE, "%s: value cannot be < -1\n", field); mult = &str[strlen(str) - 2]; if (strcasecmp(mult, "kb") == 0) bytes *= 1024; else if (strcasecmp(mult, "mb") == 0) bytes *= 1024 * 1024; else if (strcasecmp(mult, "gb") == 0) bytes *= 1024 * 1024 * 1024; return bytes; } void config_error_exit(int linenr, char *format, ...) { va_list ap; fprintf(stderr, version_str, VERSION); fprintf(stderr, "\n\n"); if (linenr != -1) fprintf(stderr, "Error while processing configuration file at line %d:\n", linenr); va_start(ap, format); (void)vfprintf(stderr, format, ap); va_end(ap); exit(EXIT_FAILURE); } void add_cs_re(int linenr, char *incmd, char *par) { if (use_colors) { char *re = NULL, *val = NULL; char *cmd = &incmd[5]; char *colon; if (strncmp(cmd, "_val", 4) == 0) { val = find_next_par(par); if (!val) config_error_exit(linenr, "cs_re_val...-entry malformed: value missing.\n"); re = find_next_par(val); } else re = find_next_par(par); if (re == NULL) config_error_exit(linenr, "cs_re-entry malformed: color or regular expression missing.\n"); if (cur_colorscheme_nr == -1) config_error_exit(linenr, "For cs_re one needs to define a color scheme name first.\n"); /* find colorscheme */ if (cschemes[cur_colorscheme_nr].color_script.script) config_error_exit(linenr, "One cannot let a color script have the same name has a color scheme."); /* grow/create list */ cschemes[cur_colorscheme_nr].pentries = (color_scheme_entry *)myrealloc(cschemes[cur_colorscheme_nr].pentries, (cschemes[cur_colorscheme_nr].n + 1) * sizeof(color_scheme_entry)); /* add to list */ if (cmd[0] == 0x00) cschemes[cur_colorscheme_nr].pentries[cschemes[cur_colorscheme_nr].n].flags = 0; else if (strcmp(cmd, "_s") == 0) cschemes[cur_colorscheme_nr].pentries[cschemes[cur_colorscheme_nr].n].flags = CSREFLAG_SUB; else if (strcmp(cmd, "_val_less") == 0) cschemes[cur_colorscheme_nr].pentries[cschemes[cur_colorscheme_nr].n].flags = CSREFLAG_CMP_VAL_LESS; else if (strcmp(cmd, "_val_bigger") == 0) cschemes[cur_colorscheme_nr].pentries[cschemes[cur_colorscheme_nr].n].flags = CSREFLAG_CMP_VAL_BIGGER; else if (strcmp(cmd, "_val_equal") == 0) cschemes[cur_colorscheme_nr].pentries[cschemes[cur_colorscheme_nr].n].flags = CSREFLAG_CMP_VAL_EQUAL; /* sanity check */ if (cmd[0] != 0x00 && strchr(re, '(') == NULL) config_error_exit(linenr, "%s is missing substring selections! ('(' and ')')\n", cmd); if (val) cschemes[cur_colorscheme_nr].pentries[cschemes[cur_colorscheme_nr].n].cmp_value = atof(val); cschemes[cur_colorscheme_nr].pentries[cschemes[cur_colorscheme_nr].n].merge_color = incmd[0] == 'm' ? MY_TRUE : MY_FALSE; cschemes[cur_colorscheme_nr].pentries[cschemes[cur_colorscheme_nr].n].cdef.ac_index = 0; colon = strchr(par, '|'); if (colon) { *colon = 0x00; cschemes[cur_colorscheme_nr].pentries[cschemes[cur_colorscheme_nr].n].cdef.use_alternating_colors = MY_TRUE; cschemes[cur_colorscheme_nr].pentries[cschemes[cur_colorscheme_nr].n].cdef.attrs2 = parse_attributes(colon + 1); } else { cschemes[cur_colorscheme_nr].pentries[cschemes[cur_colorscheme_nr].n].cdef.use_alternating_colors = MY_FALSE; } cschemes[cur_colorscheme_nr].pentries[cschemes[cur_colorscheme_nr].n].cdef.attrs1 = parse_attributes(par); /* compile regular expression */ compile_re(&cschemes[cur_colorscheme_nr].pentries[cschemes[cur_colorscheme_nr].n].regex, re); cschemes[cur_colorscheme_nr].n++; } } void add_colorscheme(int linenr, char *cmd, char *par) { if (use_colors) { char *descr = find_next_par(par); cur_colorscheme_nr = n_cschemes++; cschemes = (color_scheme *)myrealloc(cschemes, n_cschemes * sizeof(color_scheme)); memset(&cschemes[cur_colorscheme_nr], 0x00, sizeof(color_scheme)); cschemes[cur_colorscheme_nr].descr = mystrdup(USE_IF_SET(descr, "")); cschemes[cur_colorscheme_nr].name = mystrdup(par); } } void add_colorscript(int linenr, char *cmd, char *par) { char *dummy1, *dummy2; dummy1 = find_next_par(par); /* find script */ dummy2 = find_next_par(dummy1); /* find description */ cur_colorscheme_nr = n_cschemes++; cschemes = (color_scheme *)myrealloc(cschemes, n_cschemes * sizeof(color_scheme)); memset(&cschemes[cur_colorscheme_nr], 0x00, sizeof(color_scheme)); cschemes[cur_colorscheme_nr].name = mystrdup(par); cschemes[cur_colorscheme_nr].descr = mystrdup(dummy2); cschemes[cur_colorscheme_nr].color_script.script = mystrdup(dummy1); cur_colorscheme_nr = -1; } void add_editscheme(int linenr, char *cmd, char *par) { char *descr = find_next_par(par); pes = (editscheme *)myrealloc(pes, sizeof(editscheme) * (n_es + 1)); memset(&pes[n_es], 0x00, sizeof(editscheme)); pes[n_es].es_name = mystrdup(par); pes[n_es].es_desc = mystrdup(USE_IF_SET(descr, "")); cur_editscheme_nr = n_es++; } void add_editrule(int linenr, char *cmd, char *par) { char *type_str = par; char *par1 = find_next_par(type_str); char *par2 = NULL; striptype_t type = STRIP_TYPE_REGEXP; int rule_index = -1; if (!par1) config_error_exit(linenr, "editrule:%s requires a parameter.\n", type_str); if (strcmp(type_str, "kr") == 0) type = STRIP_TYPE_RANGE; else if (strcmp(type_str, "ke") == 0) type = STRIP_TYPE_REGEXP; else if (strcmp(type_str, "kc") == 0) type = STRIP_TYPE_COLUMN; else if (strcmp(type_str, "kS") == 0) type = STRIP_KEEP_SUBSTR; else config_error_exit(linenr, "editrule requirs either ke, kr, kS or kc.\n"); if (type == STRIP_TYPE_RANGE || type == STRIP_TYPE_COLUMN) { par2 = find_next_par(par1); if (!par2) config_error_exit(linenr, "editrule:%s requires another parameter.\n", type_str); } rule_index = pes[cur_editscheme_nr].n_strips; pes[cur_editscheme_nr].strips = (strip_t *)myrealloc(pes[cur_editscheme_nr].strips, (pes[cur_editscheme_nr].n_strips + 1) * sizeof(strip_t)); memset(&pes[cur_editscheme_nr].strips[pes[cur_editscheme_nr].n_strips], 0x00, sizeof(strip_t)); pes[cur_editscheme_nr].n_strips++; pes[cur_editscheme_nr].strips[rule_index].type = type; if (type == STRIP_TYPE_RANGE) { pes[cur_editscheme_nr].strips[rule_index].start = atoi(par1); pes[cur_editscheme_nr].strips[rule_index].end = atoi(par2); } else if (type == STRIP_TYPE_REGEXP || type == STRIP_KEEP_SUBSTR) { pes[cur_editscheme_nr].strips[rule_index].regex_str = mystrdup(par1); compile_re(&pes[cur_editscheme_nr].strips[rule_index].regex, par1); } else if (type == STRIP_TYPE_COLUMN) { pes[cur_editscheme_nr].strips[rule_index].del = mystrdup(par1); pes[cur_editscheme_nr].strips[rule_index].col_nr = atoi(par2); } } void add_filterscheme(int linenr, char *cmd, char *par) { char *descr = find_next_par(par); pfs = (filterscheme *)myrealloc(pfs, sizeof(filterscheme) * (n_fs + 1)); memset(&pfs[n_fs], 0x00, sizeof(filterscheme)); pfs[n_fs].fs_name = mystrdup(par); pfs[n_fs].fs_desc = mystrdup(USE_IF_SET(descr, "")); cur_filterscheme_nr = n_fs++; } void add_filterscheme_rule(int linenr, char *cmd, char *par) { char *type = par; int use_regex = 0x00; char *re_cmd = NULL; char *re_str = find_next_par(par); if (!re_str) config_error_exit(linenr, "Missing regular expression in rule-line for scheme %s.\n", pfs[cur_filterscheme_nr].fs_name); if (type[0] != 'e') config_error_exit(linenr, "Regular expression type '%s' is not recognized.\n", type); if (type[1] == 0x00 || type[1] == 'm') use_regex = 'm'; else if (type[1] == 'v') use_regex = 'v'; else if (type[1] == 'c') use_regex = 'c'; else if (type[1] == 'C') use_regex = 'C'; else if (toupper(type[1]) == 'X') { char *dummy = find_next_par(re_str); if (!dummy) config_error_exit(linenr, "Missing command for rule of type 'e%c' for scheme %s.\n", type[1], pfs[cur_filterscheme_nr].fs_name); re_cmd = mystrdup(dummy); use_regex = type[1]; if (use_regex == 'X' && (strchr(re_str, '(') == NULL || strchr(re_str, ')') == NULL)) config_error_exit(linenr, "Filter scheme rule: -eX requires a regular expression which selects a substring using '(' and ')'.\n"); } pfs[cur_filterscheme_nr].pre = (re *)myrealloc(pfs[cur_filterscheme_nr].pre, (pfs[cur_filterscheme_nr].n_re + 1) * sizeof(re)); memset(&pfs[cur_filterscheme_nr].pre[pfs[cur_filterscheme_nr].n_re], 0x00, sizeof(re)); pfs[cur_filterscheme_nr].pre[pfs[cur_filterscheme_nr].n_re].use_regex = use_regex; pfs[cur_filterscheme_nr].pre[pfs[cur_filterscheme_nr].n_re].regex_str = mystrdup(re_str); compile_re(&pfs[cur_filterscheme_nr].pre[pfs[cur_filterscheme_nr].n_re].regex, re_str); pfs[cur_filterscheme_nr].pre[pfs[cur_filterscheme_nr].n_re].cmd = re_cmd; pfs[cur_filterscheme_nr].n_re++; } void add_convert(int linenr, char *cmd, char *par) { char *conv_name = par; char *conv_type = NULL; conversion_t type = 0; char *conv_script = NULL; char *conv_re = NULL; int loop; /* parse type */ conv_type = find_next_par(conv_name); if (!conv_type) config_error_exit(linenr, "'convert'-entry malformed: conversion type missing.\n"); if (strncmp(conv_type, "script:", 7) == 0) { conv_script = find_next_par(conv_type); if (conv_script) conv_re = find_next_par(conv_script); else config_error_exit(linenr, "Convert: script filename missing.\n"); } else conv_re = find_next_par(conv_type); if (!conv_re) config_error_exit(linenr, "'convert'-entry malformed: type or regular expression missing.\n"); /* find this conversion: is it from an already known group? */ for(loop=0; loop 2) config_error_exit(linenr, "bind parameter malformed: unknown keyselection.\n"); keybindings = (keybinding *)myrealloc(keybindings, sizeof(keybinding) * (n_keybindings + 1)); if (par[0] == '^') keybindings[n_keybindings].key = toupper(par[1]) - 'A' + 1; else keybindings[n_keybindings].key = par[0]; keybindings[n_keybindings].command = mystrdup(proc); n_keybindings++; } void set_suppress_empty_lines(int linenr, char *cmd, char *par) { suppress_empty_lines = config_yes_no(par); } void set_close_closed_windows(int linenr, char *cmd, char *par) { do_not_close_closed_windows = !config_yes_no(par); } void set_follow_filename(int linenr, char *cmd, char *par) { default_follow_filename = config_yes_no(par); } void set_default_linewrap(int linenr, char *cmd, char *par) { default_linewrap = par[0]; if (default_linewrap == 'o') { char *dummy = find_next_par(par); if (!dummy) config_error_exit(linenr, "default_linewrap: 'o' needs a wrap offset parameter.\n"); default_line_wrap_offset = atoi(dummy); } } void set_umask(int linenr, char *cmd, char *par) { def_umask = strtol(par, NULL, 0); } void set_shell(int linenr, char *cmd, char *par) { myfree(shell); shell = mystrdup(par); } void set_statusline_above_data(int linenr, char *cmd, char *par) { statusline_above_data = config_yes_no(par); } void set_caret_notation(int linenr, char *cmd, char *par) { caret_notation = config_yes_no(par); } void set_searches_case_insensitive(int linenr, char *cmd, char *par) { re_case_insensitive = MY_TRUE; } void set_beep_method(int linenr, char *cmd, char *par) { if (strcmp(par, "beep") == 0) beep_method = BEEP_BEEP; else if (strcmp(par, "flash") == 0) beep_method = BEEP_FLASH; else if (strcmp(par, "popup") == 0) beep_method = BEEP_POPUP; else if (strcmp(par, "none") == 0) beep_method = BEEP_NONE; else config_error_exit(linenr, "'%s' is an unknown beep method.\n"); } void set_beep_popup_length(int linenr, char *cmd, char *par) { beep_popup_length = atof(par); if (beep_popup_length < 0.0) config_error_exit(linenr, "beep_popup_length must be >= 0.0\n"); } void set_allow_8bit(int linenr, char *cmd, char *par) { allow_8bit = config_yes_no(par); } void set_dsblwm(int linenr, char *cmd, char *par) { no_linewrap = 1 - config_yes_no(par); } void set_warn_closed(int linenr, char *cmd, char *par) { warn_closed = config_yes_no(par); } void set_basename(int linenr, char *cmd, char *par) { filename_only = config_yes_no(par); } void set_bright(int linenr, char *cmd, char *par) { bright_colors = config_yes_no(par); } void set_ts_format(int linenr, char *cmd, char *par) { myfree(ts_format); ts_format = mystrdup(par); } void set_cnv_ts_format(int linenr, char *cmd, char *par) { myfree(cnv_ts_format); cnv_ts_format = mystrdup(par); } void set_statusline_ts_format(int linenr, char *cmd, char *par) { myfree(statusline_ts_format); statusline_ts_format = mystrdup(par); } void set_markerline_attrs(int linenr, char *cmd, char *par) { markerline_attrs = parse_attributes(par); } void set_idleline_color(int linenr, char *cmd, char *par) { idleline_attrs = parse_attributes(par); } void set_msgline_color(int linenr, char *cmd, char *par) { msgline_attrs = parse_attributes(par); } void set_changeline_color(int linenr, char *cmd, char *par) { changeline_attrs = parse_attributes(par); } void set_statusline_attrs(int linenr, char *cmd, char *par) { statusline_attrs = parse_attributes(par); } void set_splitline_attrs(int linenr, char *cmd, char *par) { splitline_attrs = parse_attributes(par); } void set_inverse_attrs(int linenr, char *cmd, char *par) { inverse_attrs = attrstr_to_nr(par); } void set_splitline(int linenr, char *cmd, char *par) { if (strcmp(par, "none") == 0) splitline_mode = SL_NONE; else if (strcmp(par, "regular") == 0) splitline_mode = SL_REGULAR; else if (strcmp(par, "attributes") == 0) splitline_mode = SL_ATTR; else config_error_exit(linenr, "Parameter '%s' for 'splitline' not recognized.\n", par); } void set_show_subwindow_id(int linenr, char *cmd, char *par) { show_subwindow_id = config_yes_no(par); } void set_markerline_timestamp(int linenr, char *cmd, char *par) { timestamp_in_markerline = config_yes_no(par); } void set_global_default_nlines(int linenr, char *cmd, char *par) { default_maxnlines = atoi(par); } void set_global_default_nkb(int linenr, char *cmd, char *par) { default_maxbytes = kb_str_to_value(cmd, par); } void set_default_nlines(int linenr, char *cmd, char *par) { char *re; int n_lines; if ((re = find_next_par(par)) == NULL) /* format: cs_re:color:regular expression */ config_error_exit(linenr, "Scheme entry malformed: scheme name or regular expression missing.\n"); /* find colorscheme */ n_lines = atoi(par); if (n_lines < -1) config_error_exit(linenr, "default_nlines: value cannot be lower then -1.\n"); add_pars_per_file(re, NULL, n_lines, -1, -1, -1, -1, NULL); } void set_default_mark_change(int linenr, char *cmd, char *par) { char *re; if ((re = find_next_par(par)) == NULL) /* format: cs_re:color:regular expression */ config_error_exit(linenr, "Scheme entry malformed: scheme name or regular expression missing.\n"); add_pars_per_file(re, NULL, -1, -1, config_yes_no(par), -1, -1, NULL); } void set_default_bytes(int linenr, char *cmd, char *par) { int bytes; char *re; if ((re = find_next_par(par)) == NULL) /* format: cs_re:color:regular expression */ config_error_exit(linenr, "Scheme entry malformed: scheme name or regular expression missing.\n"); bytes = kb_str_to_value(cmd, par); add_pars_per_file(re, NULL, -1, bytes, -1, -1, -1, NULL); } void set_check_mail(int linenr, char *cmd, char *par) { check_for_mail = get_value_arg("check_mail", par, VAL_ZERO_POSITIVE); } void set_tab_stop(int linenr, char *cmd, char *par) { tab_width = atoi(par); if (tab_width == 0) config_error_exit(linenr, "tab_stop: value cannot be 0.\n"); } void set_tail(int linenr, char *cmd, char *par) { myfree(tail); tail = mystrdup(par); } void set_titlebar(int linenr, char *cmd, char *par) { myfree(set_title); set_title = mystrdup(par); } void set_abbreviate_filesize(int linenr, char *cmd, char *par) { afs = config_yes_no(par); } void set_replace_by_markerline(int linenr, char *cmd, char *par) { myfree(replace_by_markerline); replace_by_markerline = mystrdup(par); } void set_popup_refresh_interval(int linenr, char *cmd, char *par) { popup_refresh_interval = get_value_arg("popup_refresh_interval", par, VAL_POSITIVE); } void set_msgline_char(int linenr, char *cmd, char *par) { msgline_char = par[0]; } void set_idleline_char(int linenr, char *cmd, char *par) { idleline_char = par[0]; } void set_changeline_char(int linenr, char *cmd, char *par) { changeline_char = par[0]; } void set_markerline_char(int linenr, char *cmd, char *par) { markerline_char = par[0]; } void set_global_mark_change(int linenr, char *cmd, char *par) { global_marker_of_other_window = config_yes_no(par); } void set_default_bufferwhat(int linenr, char *cmd, char *par) { char what = par[0]; if (what != 'a' && what != 'f') config_error_exit(linenr, "default_bufferwhat expects either 'a' or 'f'. Got: '%c'.\n", what); default_bufferwhat = what; } void set_abort_key(int linenr, char *cmd, char *par) { int dummy = atoi(par); if (dummy < 0 || dummy > 255) config_error_exit(linenr, "abort_key expects an ascii value which is >= 0 && <= 255.\n"); abort_key = dummy; } void set_exit_key(int linenr, char *cmd, char *par) { int dummy = atoi(par); if (dummy < 0 || dummy > 255) config_error_exit(linenr, "exit_key expects an ascii value which is >= 0 && <= 255.\n"); exit_key = dummy; } void set_line_ts_format(int linenr, char *cmd, char *par) { myfree(line_ts_format); line_ts_format = mystrdup(par); } void set_default_min_shrink(int linenr, char *cmd, char *par) { default_min_shrink = get_value_arg("min_shrink", par, VAL_POSITIVE); } void set_scrollback_show_winnrs(int linenr, char *cmd, char *par) { default_sb_showwinnr = config_yes_no(par); } void set_wordwrapmaxlength(int linenr, char *cmd, char *par) { wordwrapmaxlength = get_value_arg("wordwrapmaxlength", par, VAL_POSITIVE); } void set_xclip(int linenr, char *cmd, char *par) { if (file_exist(par) == -1) error_exit(TRUE, FALSE, "xclip binary '%s' does not exist"); clipboard = strdup(par); } void set_pbcopy(int linenr, char *cmd, char *par) { if (file_exist(par) == -1) error_exit(TRUE, FALSE, "pbcopy binary '%s' does not exist"); clipboard = strdup(par); } void set_map_delete_as_backspace(int linenr, char *cmd, char *par) { map_delete_as_backspace = config_yes_no(par); } void set_searchhistory_file(int linenr, char *cmd, char *par) { if (par[0] == 0x00) { search_h.history_file = NULL; search_h.history_size = 0; } else { search_h.history_file = myrealpath(par); } } void set_searchhistory_size(int linenr, char *cmd, char *par) { int hs = atoi(par); if (hs <= 0) { search_h.history_file = NULL; search_h.history_size = 0; } else { search_h.history_size = hs; } } void set_cmdfile_history_file(int linenr, char *cmd, char *par) { if (par[0] == 0x00) { cmdfile_h.history_file = NULL; cmdfile_h.history_size = 0; } else { cmdfile_h.history_file = myrealpath(par); } } void set_cmdfile_history_size(int linenr, char *cmd, char *par) { int hs = atoi(par); if (hs <= 0) { cmdfile_h.history_file = NULL; cmdfile_h.history_size = 0; } else { cmdfile_h.history_size = hs; } } void set_default_background_color(int linenr, char *cmd, char *par) { if (use_colors) { default_bg_color = colorstr_to_nr(par); if (default_bg_color == -1) config_error_exit(linenr, "default_background_color: '%s' is not a recognized color.", par); } } void set_reuse_searchstring(int linenr, char *cmd, char *par) { reuse_searchstring = config_yes_no(par); } void set_min_n_bufferlines(int linenr, char *cmd, char *par) { min_n_bufferlines = atoi(par); if (min_n_bufferlines < 0) config_error_exit(linenr, "min_n_bufferlines must have a value >= 0."); } void set_box_bottom_left_hand_corner(int linenr, char *cmd, char *par) { box_bottom_left_hand_corner = par[0]; } void set_box_bottom_right_hand_corner(int linenr, char *cmd, char *par) { box_bottom_right_hand_corner = par[0]; } void set_box_bottom_side(int linenr, char *cmd, char *par) { box_bottom_side = par[0]; } void set_box_left_side(int linenr, char *cmd, char *par) { box_left_side = par[0]; } void set_box_right_side(int linenr, char *cmd, char *par) { box_right_side = par[0]; } void set_box_top_left_hand_corner(int linenr, char *cmd, char *par) { box_top_left_hand_corner = par[0]; } void set_box_top_right_hand_corner(int linenr, char *cmd, char *par) { box_top_right_hand_corner = par[0]; } void set_box_top_side(int linenr, char *cmd, char *par) { box_top_side = par[0]; } void set_window_number(int linenr, char *cmd, char *par) { myfree(window_number); window_number = mystrdup(par); } void set_subwindow_number(int linenr, char *cmd, char *par) { myfree(subwindow_number); subwindow_number = mystrdup(par); } void set_posix_tail(int linenr, char *cmd, char *par) { posix_tail = config_yes_no(par); } void set_syslog_ts_format(int linenr, char *cmd, char *par) { myfree(syslog_ts_format); syslog_ts_format = mystrdup(par); } void set_resolv_ip_addresses(int linenr, char *cmd, char *par) { resolv_ip_addresses = config_yes_no(par); } void set_show_severity_facility(int linenr, char *cmd, char *par) { show_severity_facility = config_yes_no(par); } void set_scrollback_fullscreen_default(int linenr, char *cmd, char *par) { scrollback_fullscreen_default = config_yes_no(par); } void set_scrollback_no_colors(int linenr, char *cmd, char *par) { scrollback_no_colors = config_yes_no(par); } void set_scrollback_search_new_window(int linenr, char *cmd, char *par) { scrollback_search_new_window = config_yes_no(par); } void set_gnu_tail(int linenr, char *cmd, char *par) { gnu_tail = config_yes_no(par); } config_file_keyword cf_entries[] = { { "abbreviate_filesize", set_abbreviate_filesize }, { "abort_key", set_abort_key }, { "allow_8bit", set_allow_8bit }, { "basename", set_basename }, { "beep_method", set_beep_method }, { "beep_popup_length", set_beep_popup_length }, { "bind", bind_char }, { "box_bottom_left_hand_corner", set_box_bottom_left_hand_corner }, { "box_bottom_right_hand_corner", set_box_bottom_right_hand_corner }, { "box_bottom_side", set_box_bottom_side }, { "box_left_side", set_box_left_side }, { "box_right_side", set_box_right_side }, { "box_top_left_hand_corner", set_box_top_left_hand_corner }, { "box_top_right_hand_corner", set_box_top_right_hand_corner }, { "box_top_side", set_box_top_side }, { "bright", set_bright }, { "caret_notation", set_caret_notation }, { "changeline_char", set_changeline_char }, { "changeline_color", set_changeline_color }, { "check_mail", set_check_mail }, { "close_closed_windows", set_close_closed_windows }, { "cmdfile_history_file", set_cmdfile_history_file }, { "cmdfile_history_size", set_cmdfile_history_size }, { "cnv_ts_format", set_cnv_ts_format }, { "colorscheme", add_colorscheme }, { "colorscript", add_colorscript }, { "convert", add_convert }, { "cs_re", add_cs_re }, { "cs_re_s", add_cs_re }, { "cs_re_val_bigger", add_cs_re }, { "cs_re_val_equal", add_cs_re }, { "cs_re_val_less", add_cs_re }, { "default_background_color", set_default_background_color }, { "default_bufferwhat", set_default_bufferwhat }, { "default_bytes", set_default_bytes }, { "default_convert", set_default_convert }, { "default_linewrap", set_default_linewrap }, { "default_mark_change", set_default_mark_change }, { "default_nlines", set_default_nlines }, { "defaultcscheme", set_defaultcscheme }, { "dsblwm", set_dsblwm }, { "editrule", add_editrule }, { "editscheme", add_editscheme }, { "exit_key", set_exit_key }, { "filterscheme", add_filterscheme }, { "follow_filename", set_follow_filename }, { "global_default_nkb", set_global_default_nkb }, { "global_default_nlines", set_global_default_nlines }, { "global_mark_change", set_global_mark_change }, { "gnu_tail", set_gnu_tail }, { "idleline_char", set_idleline_char }, { "idleline_color", set_idleline_color }, { "include", do_load_config }, { "inverse", set_inverse_attrs }, { "line_ts_format", set_line_ts_format }, { "map_delete_as_backspace", set_map_delete_as_backspace }, { "markerline_char", set_markerline_char }, { "markerline_color", set_markerline_attrs }, { "markerline_timestamp", set_markerline_timestamp }, { "mcsre", add_cs_re }, { "mcsre_s", add_cs_re }, { "mcsre_val_bigger", add_cs_re }, { "mcsre_val_equal", add_cs_re }, { "mcsre_val_less", add_cs_re }, { "min_n_bufferlines", set_min_n_bufferlines }, { "min_shrink", set_default_min_shrink }, { "msgline_char", set_msgline_char }, { "msgline_color", set_msgline_color }, { "popup_refresh_interval", set_popup_refresh_interval }, { "posix_tail", set_posix_tail }, { "replace_by_markerline", set_replace_by_markerline }, { "resolv_ip_addresses", set_resolv_ip_addresses }, { "reuse_searchstring", set_reuse_searchstring }, { "rule", add_filterscheme_rule }, { "scheme", scheme }, { "scrollback_fullscreen_default", set_scrollback_fullscreen_default }, { "scrollback_no_colors", set_scrollback_no_colors }, { "scrollback_search_new_window", set_scrollback_search_new_window }, { "scrollback_show_winnrs", set_scrollback_show_winnrs }, { "searches_case_insensitive", set_searches_case_insensitive }, { "searchhistory_file", set_searchhistory_file }, { "searchhistory_size", set_searchhistory_size }, { "shell", set_shell }, { "show_severity_facility", set_show_severity_facility }, { "show_subwindow_id", set_show_subwindow_id }, { "splitline", set_splitline }, { "splitline_attrs", set_splitline_attrs }, { "statusline_above_data", set_statusline_above_data }, { "statusline_attrs", set_statusline_attrs }, { "statusline_ts_format", set_statusline_ts_format }, { "subwindow_number", set_subwindow_number }, { "suppress_empty_lines", set_suppress_empty_lines }, { "syslog_ts_format", set_syslog_ts_format }, { "tab_stop", set_tab_stop }, { "tail", set_tail }, { "titlebar", set_titlebar }, { "ts_format", set_ts_format }, { "umask", set_umask }, { "useeditscheme", use_editscheme }, { "usefilterscheme", use_filterscheme }, { "warn_closed", set_warn_closed }, { "window_number", set_window_number }, { "wordwrapmaxlength", set_wordwrapmaxlength }, { "xclip", set_xclip }, #ifdef __APPLE__ { "pbcopy", set_pbcopy }, #endif }; int find_config_entry_in_dispatch_table(char *cmd_name) { int left = 0; int right = (sizeof(cf_entries) / sizeof(config_file_keyword)) - 1; while(left <= right) { int mid = (left + right) / 2; int compare = strcmp(cmd_name, cf_entries[mid].config_keyword); if (compare > 0) left = mid + 1; else if (compare < 0) right = mid - 1; else return mid; } return -1; } int config_file_entry(int linenr, char *cmd) { int function_nr; char *par = NULL; /* remove spaces at the beginning of the line */ while (isspace(*cmd)) cmd++; /* skip empty lines and comments */ if (cmd[0] == 0x00 || cmd[0] == '#' || cmd[0] == ';') return 0; /* lines are in the format of command:parameter */ if ((par = find_next_par(cmd)) == NULL) config_error_exit(linenr, "Malformed configuration line found: %s (command delimiter (':') missing).\n", cmd); function_nr = find_config_entry_in_dispatch_table(cmd); if (function_nr == -1) return -1; cf_entries[function_nr].function(linenr, cmd, par); return 0; } int sort_colorschemes_compare(const void *a, const void *b) { color_scheme *pa = (color_scheme *)a; color_scheme *pb = (color_scheme *)b; return strcmp(pa -> name, pb -> name); } /* returns the default color scheme or -1 if none */ void do_load_config(int dummynr, char *dummy, char *file) { static char sorted = 0; FILE *fh; int linenr = 0; /* given file */ fh = fopen(file, "r"); if (fh == NULL) { if (errno == ENOENT) /* file does not exist, not an error */ return; error_exit(TRUE, FALSE, "do_load_config: error loading configfile '%s'\n", file); } for(;;) { char read_buffer[CONFIG_READ_BUFFER]; char *dummy; char *cmd = fgets(read_buffer, sizeof(read_buffer) - 1, fh); if (!cmd) break; linenr++; /* strip LF */ dummy = strchr(cmd, '\n'); if (dummy) *dummy = 0x00; else error_exit(FALSE, FALSE, "line %d of file '%s' is too long!\n", linenr, file); /* LOG("%d: %s\n", linenr, cmdin); */ if (config_file_entry(linenr, cmd) == -1) error_exit(FALSE, FALSE, "Configuration parameter '%s' is unknown (file: %s, line: %d).\n", read_buffer, file, linenr); } fclose(fh); if (!sorted) { sorted = 1; qsort(cschemes, n_cschemes, sizeof(color_scheme), sort_colorschemes_compare); } } void load_configfile_wrapper(char *config_file) { /* load configurationfile (if any) */ if (load_global_config) { DIR *dir = opendir("/etc/multitail/cfg.d"); int path_max = find_path_max(); char *path = mymalloc(path_max + 1); do_load_config(-1, NULL, CONFIG_FILE); for(;dir;) { struct dirent *de = readdir(dir); if (!de) break; snprintf(path, path_max, "/etc/multitail/cfg.d/%s", de->d_name); do_load_config(-1, NULL, path); } free(path); if (dir) closedir(dir); } if (config_file) { do_load_config(-1, NULL, config_file); } else { int path_max = find_path_max(); char *path = mymalloc(path_max + 1); char *xdg_config = getenv("XDG_CONFIG_HOME"); char *home = getenv("HOME"); struct passwd *pp = getuserinfo(); if (xdg_config) snprintf(path, path_max, "%s/multitail/config", xdg_config); else if (home) snprintf(path, path_max, "%s/.multitailrc", home); else if (pp) snprintf(path, path_max, "%s/.multitailrc", pp -> pw_dir); if (xdg_config || home || pp) do_load_config(-1, NULL, path); myfree(path); } } char load_configfile(char *config_file) { static char config_loaded = 0; if (config_loaded == 0) { /* load configurationfile (if any) */ load_configfile_wrapper(config_file); config_loaded = 1; return 1; } return 0; } multitail-7.1.5/config.h000066400000000000000000000003731465663511400151640ustar00rootroot00000000000000typedef struct { char *config_keyword; void (*function)(int, char *, char *); } config_file_keyword; int config_file_entry(int dummy, char *line); void do_load_config(int dummynr, char *dummy, char *file); char load_configfile(char *config_file); multitail-7.1.5/conversion-scripts/000077500000000000000000000000001465663511400174155ustar00rootroot00000000000000multitail-7.1.5/conversion-scripts/colors-example.pl000077500000000000000000000002511465663511400227050ustar00rootroot00000000000000#!/usr/bin/perl # disable I/O buffering (this is essential) $| = 1; while(<>) { # 2 colors print "2,4,red,yellow,bold\n"; print "6,7,green,white\n"; print "\n"; } multitail-7.1.5/conversion-scripts/colors-example.sh000077500000000000000000000001361465663511400227060ustar00rootroot00000000000000#!/bin/sh while read line ; do echo 2,4,red,yellow,bold echo 6,7,green,white echo done multitail-7.1.5/conversion-scripts/convert-geoip.pl000077500000000000000000000003371465663511400225410ustar00rootroot00000000000000#!/usr/bin/perl use Geo::IP; my $gi = Geo::IP->new(GEOIP_STANDARD); $| = 1; while(<>) { chomp($_); $country = $gi->country_code_by_addr($_); if ($country eq '') { $country = '?'; } print "$_ ($country)\n"; } multitail-7.1.5/conversion-scripts/convert-simple.pl000077500000000000000000000002111465663511400227160ustar00rootroot00000000000000#!/usr/bin/perl # disable I/O buffering (this is essential) $| = 1; while(<>) { # add 'bla' in front of the string print "bla".$_; } multitail-7.1.5/cv.c000066400000000000000000000171241465663511400143240ustar00rootroot00000000000000#define _LARGEFILE64_SOURCE /* required for GLIBC to enable stat64 and friends */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mt.h" #include "mem.h" #include "error.h" #include "utils.h" #include "exec.h" #include "globals.h" int cv_offsets_compare(const void *a, const void *b) { cv_off *pa = (cv_off *)a, *pb = (cv_off *)b; if (pa -> start > pb -> start) return -1; else if (pa -> start == pb -> start) { if (pa -> end > pb -> end) return -1; } return 0; } char * epoch_to_str(time_t epoch) { char *new_string; struct tm *ptm = localtime(&epoch); if (!ptm) return NULL; new_string = mymalloc(4096); if (!strftime(new_string, 4096, cnv_ts_format, ptm)) error_exit(FALSE, FALSE, "An error occured whilte converting timestamp format '%s'.\n", cnv_ts_format); return new_string; } char *do_convert(char *what, int what_len, int type, script *pscript) { switch(type) { case CONVTYPE_SIGNRTOSTRING: { int signr = atoi(what); if (signr > n_known_sigs || signr < 1) return mystrdup("unknown signal"); return mystrdup(sigs[signr]); } case CONVTYPE_TAI64NTODATE: { long long int v2_62 = (long long int)1 << (long long int)62; long long int val = 0; int loop; if (what[0] == '@') what++; /* http://cr.yp.to/libtai/tai64.html#tai64n */ /* convert to 64 bit integer */ for(loop=0; loop<(8 * 2); loop++) { int c = tolower(what[loop]); val <<= (long long int)4; if (c >= 'a') val += 10 + c - 'a'; else val += c - '0'; } if (val >= v2_62) /* FIXME: 2^63 are reserved, not checking for that, sorry */ { char *new_str = epoch_to_str((time_t)(val - v2_62)); if (new_str) return new_str; else return mystrdup("cannot convert current 'TAI64N'-date to string"); } else { /* before 1970/1/1 now what should I do with that? */ return mystrdup("cannot convert 'TAI64N'-dates before the epoch"); } } case CONVTYPE_IP4TOHOST: { if (resolv_ip_addresses) { struct hostent *ht; in_addr_t addr = inet_addr(what); if ((int)addr == -1) return mystrdup(what); if ((ht = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET)) == NULL) return mystrdup(what); return mystrdup(ht -> h_name); } return mystrdup(what); } break; case CONVTYPE_EPOCHTODATE: { char *new_str = epoch_to_str((time_t)atoll(what)); if (new_str) return new_str; else return mystrdup("cannot convert current epoch value"); } break; case CONVTYPE_ERRNO: { return mystrdup(strerror(atoi(what))); } case CONVTYPE_HEXTODEC: { long long int result = strtoll(what, NULL, 16); char result_str[128]; snprintf(result_str, sizeof(result_str), "%lld", result); return mystrdup(result_str); } case CONVTYPE_DECTOHEX: { long long int result = atoll(what); char result_str[128]; snprintf(result_str, sizeof(result_str), "%llx", result); return mystrdup(result_str); } case CONVTYPE_SCRIPT: { int rc; char *send_buffer = mymalloc(what_len + 1 + 1); char *result_str = mymalloc(SCRIPT_IO_BUFFER_SIZE); exec_script(pscript); memcpy(send_buffer, what, what_len); send_buffer[what_len] = '\n'; send_buffer[what_len + 1] = 0x00; WRITE(pscript -> fd_w, send_buffer, what_len + 1, "conversion script (is it still running?)"); myfree(send_buffer); rc = READ(pscript -> fd_r, result_str, SCRIPT_IO_BUFFER_SIZE - 1, pscript -> script); result_str[rc > 0?rc - 1:rc] = 0x00; return result_str; } case CONVTYPE_ABBRTOK: return amount_to_str(atoll(what)); default: error_exit(FALSE, FALSE, "Internal error: unknown conversion type %d.\n", type); } return "do_convert: INTERNAL ERROR"; } char *convert(int_array_t *pconversions, char *line) { conversion *cur_conv = NULL; cv_off *cv_offsets = NULL; int conv_index; int conv_req; int new_len = 0; int cur_n_cv_matches = 0; char *new_string = NULL; int offset_old = 0, offset_new = 0; int old_len = strlen(line); int n_conversions = get_iat_size(pconversions); if (n_conversions == 0) return line; for(conv_req=0; conv_req < n_conversions; conv_req++) { cur_conv = &conversions[get_iat_element(pconversions, conv_req)]; /* find where they match */ for(conv_index=0; conv_index n; conv_index++) { int offset = 0; do { int cur_match_index; int cur_offset = offset; regmatch_t matches[MAX_N_RE_MATCHES]; /* FIXME: what to do with regexp errors? */ if (regexec(&(cur_conv -> pcb)[conv_index].regex, &line[offset], MAX_N_RE_MATCHES, matches, offset?REG_NOTBOL:0) != 0) break; for(cur_match_index=1; cur_match_index pcb)[conv_index].match_count++; cv_offsets = (cv_off *)myrealloc(cv_offsets, sizeof(cv_off) * (cur_n_cv_matches + 1)); cv_offsets[cur_n_cv_matches].start = match_start; cv_offsets[cur_n_cv_matches].end = match_end; dummylen = match_end - match_start; dummy = mymalloc(dummylen + 1); memcpy(dummy, &line[match_start], dummylen); dummy[dummylen] = 0x00; cv_offsets[cur_n_cv_matches].newstr = do_convert(dummy, dummylen, (cur_conv -> pcb)[conv_index].type, &(cur_conv -> pcs)[conv_index]); myfree(dummy); cur_n_cv_matches++; } } while (offset < old_len); } } if (cur_n_cv_matches) { int n_copy; /* sort */ if (cur_n_cv_matches > 1) qsort(cv_offsets, cur_n_cv_matches, sizeof(cv_off), cv_offsets_compare); /* create new string */ for(conv_index=0; conv_index < cur_n_cv_matches; conv_index++) { n_copy = cv_offsets[conv_index].start - offset_old; if (n_copy > 0) { new_string = myrealloc(new_string, new_len + n_copy + 1); memcpy(&new_string[offset_new], &line[offset_old], n_copy); new_string[offset_new + n_copy] = 0x00; new_len += n_copy; offset_new += n_copy; } offset_old = cv_offsets[conv_index].end; n_copy = strlen(cv_offsets[conv_index].newstr); new_string = myrealloc(new_string, new_len + n_copy + 1); memcpy(&new_string[offset_new], cv_offsets[conv_index].newstr, n_copy); new_string[offset_new + n_copy] = 0x00; myfree(cv_offsets[conv_index].newstr); new_len += n_copy; offset_new += n_copy; } n_copy = old_len - offset_old; if (n_copy) { new_string = myrealloc(new_string, new_len + n_copy + 1); memcpy(&new_string[offset_new], &line[offset_old], n_copy); new_string[offset_new + n_copy] = 0x00; } } else { new_string = line; } myfree(cv_offsets); return new_string; } int find_conversion_scheme(char *name) { int loop; for(loop=0; loop #include #include #include #include #include "mt.h" #include "mem.h" #include "utils.h" #include "globals.h" void store_for_diff(diff_t *diff, char *string) { diff -> bcur = (char **)myrealloc(diff -> bcur, sizeof(char *) * (diff -> ncur + 1)); (diff -> bcur)[diff -> ncur] = mystrdup(string); diff -> ncur++; } void generate_diff(int winnr, proginfo *cur) { int loop_cur; int search_offset = 0; /* calculate & print difference */ for(loop_cur=0; loop_cur < cur -> restart.diff.ncur; loop_cur++) { int loop_prev; char found = 0; for(loop_prev=0; loop_prev < cur -> restart.diff.nprev; loop_prev++) { if (strcmp((cur -> restart.diff.bprev)[(search_offset + loop_prev) % cur -> restart.diff.nprev], (cur -> restart.diff.bcur)[loop_cur]) == 0) { found = 1; break; } } if (!found) { /* output cur[loop] */ emit_to_buffer_and_term(winnr, cur, cur -> restart.diff.bcur[loop_cur]); search_offset = 0; } else { search_offset = loop_prev + 1; } } update_panels(); /* free up previous */ delete_array(cur -> restart.diff.bprev, cur -> restart.diff.nprev); /* remember current */ cur -> restart.diff.bprev = cur -> restart.diff.bcur; cur -> restart.diff.nprev = cur -> restart.diff.ncur; cur -> restart.diff.bcur = NULL; cur -> restart.diff.ncur = 0; } multitail-7.1.5/diff.h000066400000000000000000000001371465663511400146250ustar00rootroot00000000000000void store_for_diff(diff_t *diff, char *string); void generate_diff(int winnr, proginfo *cur); multitail-7.1.5/doassert.h000066400000000000000000000001161465663511400155360ustar00rootroot00000000000000#ifdef _DEBUG #define NDEBUG #else #undef NDEBUG #endif #include multitail-7.1.5/error.c000066400000000000000000000030661465663511400150450ustar00rootroot00000000000000#define _LARGEFILE64_SOURCE /* required for GLIBC to enable stat64 and friends */ #include #include #include #include #include #include #include #include #include #if defined(__GLIBC__) #include #endif #include #include #include "mt.h" #include "utils.h" #include "version.h" #include "error.h" void error_exit_(BOOL show_errno, BOOL show_st, char *file, const char *function, int line, char *format, ...) { int e = errno; va_list ap; #if defined(__GLIBC__) int index; void *trace[128]; int trace_size = backtrace(trace, 128); char **messages = backtrace_symbols(trace, trace_size); #endif (void)endwin(); fprintf(stderr, version_str, VERSION); fprintf(stderr, "\n\n"); fprintf(stderr, "The following problem occured:\n"); fprintf(stderr, "-----------------------------\n"); va_start(ap, format); (void)vfprintf(stderr, format, ap); va_end(ap); if (show_errno || show_st) fprintf(stderr, "If this is a bug, please report the following information:\n"); if (e && show_errno) fprintf(stderr, "The last system call returned: %d which means \"%s\"\n", e, strerror(e)); if (show_st) { #if defined(__GLIBC__) fprintf(stderr, "Execution path:\n"); for(index=0; index #include #include #include #include #include #include #include #include #include #include #include #include "mt.h" #include "error.h" #include "utils.h" #include "mem.h" #include "term.h" #include "my_pty.h" #include "globals.h" #include "ui.h" int start_tail(char *filename, char retry_open, char follow_filename, int initial_tail, int *pipefd) { pid_t pid = -1; /* start child process */ if ((pid = fork()) == 0) { char *pars[16], *posix_version = NULL; int npars = 0; char nlines_buffer[32]; #ifndef __minix setpgid(0, 0); #endif setup_for_childproc(pipefd[1], 0, "dumb"); /* create command for take last n lines & follow and start tail */ /* the command to start */ pars[npars++] = tail; /* Linux' tail has the --retry option, but not all * other UNIX'es have this, so I implemented it * myself */ if (retry_open) { int rc; struct stat64 buf; for(;;) { rc = stat64(filename, &buf); if (rc == -1) { if (errno != ENOENT) { fprintf(stderr, "Error while looking for file %s: %d\n", filename, errno); exit(EXIT_FAILURE); } } else if (rc == 0) break; usleep(WAIT_FOR_FILE_DELAY * 1000); } #if defined(linux) || defined(__CYGWIN__) || defined(__GNU__) pars[npars++] = "--retry"; #endif } /* get the posix compliance level */ posix_version = getenv("_POSIX2_VERSION"); /* follow filename is only supported on *BSD and Linux */ #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(linux) || defined(__CYGWIN__) || defined(__APPLE__) || defined(__GNU__) if (follow_filename) { #if defined(linux) || defined(__CYGWIN__) || defined(__GNU__) pars[npars++] = "--follow=name"; #elif defined(__OpenBSD__) pars[npars++] = "-f"; #else pars[npars++] = "-F"; #endif /* number of lines to tail initially */ pars[npars++] = "-n"; snprintf(nlines_buffer, sizeof(nlines_buffer), "%d", initial_tail); pars[npars++] = nlines_buffer; } else #endif { #if !defined(linux) && !defined(__CYGWIN__) && !defined(__GNU__) if (follow_filename && gnu_tail) pars[npars++] = "--follow=name"; #endif /* check the posix compliance level */ if ((posix_version && atoi(posix_version) >= 200112) || posix_tail == MY_TRUE) { pars[npars++] = "-f"; pars[npars++] = "-n"; snprintf(nlines_buffer, sizeof(nlines_buffer), "%d", initial_tail); pars[npars++] = nlines_buffer; } else { /* number of lines to tail initially and 'follow file' ('f') */ snprintf(nlines_buffer, sizeof(nlines_buffer), "-%dlf", initial_tail); pars[npars++] = nlines_buffer; } } /* add the filename to monitor */ pars[npars++] = filename; pars[npars] = NULL; /* run tail! */ if (-1 == execvp(pars[0], pars)) error_exit(TRUE, FALSE, "Error while starting process %s.\n", pars[0]); /* if execlp returns, an error occured */ error_exit(FALSE, FALSE, "An error occured while starting process %s!\n", pars[0]); } return pid; } void start_proc_signal_handler(int sig) { if (sig != SIGTERM) error_popup("Signal handler(2)", -1, "Unexpected signal %d.\n", sig); stop_process(tail_proc); exit(1); } int start_proc(proginfo *cur, int initial_tail) { cur -> n_runs++; if (cur -> wt == WT_COMMAND) { int fd_master = 1, fd_slave = 1; /* allocate pseudo-tty & fork*/ cur -> pid = get_pty_and_fork(&fd_master, &fd_slave); if (-1 == cur -> pid) error_exit(TRUE, FALSE, "An error occured while invoking get_pty_and_fork.\n"); /* child? */ if (cur -> pid == 0) { #ifndef __minix setpgid(0, 0); #endif /* reset signal handler for SIGTERM */ signal(SIGTERM, SIG_DFL); /* sleep if requested and only when 2nd or 3d (etc.) execution time */ if (cur -> restart.restart && cur -> restart.first == 0) sleep(cur -> restart.restart); /* connect slave-fd to stdin/out/err */ setup_for_childproc(fd_slave, 1, term_t_to_string(cur -> cdef.term_emul)); /* start process */ if (-1 == execlp(shell, shell, "-c", cur -> filename, (void *)NULL)) error_exit(TRUE, FALSE, "Error while starting \"%s -c '%s'\".\n", shell, cur -> filename); /* if execlp returns, an error occured */ error_exit(FALSE, FALSE, "Error while starting process %s!\n", shell); } #if defined(sun) || defined(__sun) || defined(AIX) || defined(_HPUX_SOURCE) || defined(OSF1) || defined(scoos) /* these platforms only have the slave-fd available in the childprocess * * so don't try to close it as the parent process doesn't have it * */ #else if (myclose(fd_slave) == -1) error_exit(TRUE, FALSE, "An error occured while closing the slave fd (pseudo tty, fd %d)\n", fd_slave); #endif /* remember master-fd (we'll read from that one) */ cur -> fd = fd_master; cur -> wfd = fd_master; /* next time, sleep */ cur -> restart.first = 0; } else if (cur -> wt == WT_FILE) { int pipefd[2] = { -1 }; /* create a pipe, will be to child-process */ if (-1 == pipe(pipefd)) error_exit(TRUE, FALSE, "Error creating pipe.\n"); if (cur -> check_interval) { /* start the process that will check every 'interval' seconds * if a matching file with a more recent mod-time is available */ cur -> pid = fork(); if (cur -> pid == 0) { char *cur_file = NULL, *new_file; #ifndef __minix setpgid(0, 0); #endif signal(SIGTERM, start_proc_signal_handler); for(;;) { /* find (new) file */ new_file = find_most_recent_file(cur -> filename, cur_file); /* if find_most_recent_file returned NOT null, a file was found * which is more recent */ if (new_file != NULL) { /* if there was a previous file, see if it is different * from the new filename. this should always be the case! */ if (cur_file && strcmp(new_file, cur_file) != 0) { stop_process(tail_proc); } /* remember new filename */ myfree(cur_file); cur_file = new_file; /* and start a proc process */ tail_proc = start_tail(cur_file, cur -> retry_open, cur -> follow_filename, initial_tail, pipefd); if (tail_proc == -1) { break; } } else { /* LOG("no file found for pattern %s\n", cur -> filename); */ } sleep(cur -> check_interval); } /* LOG("stopped checking for file pattern %s\n", cur -> filename); */ exit(1); } } else { cur -> pid = start_tail(cur -> filename, cur -> retry_open, cur -> follow_filename, initial_tail, pipefd); } cur -> fd = pipefd[0]; cur -> wfd = pipefd[1]; } if (cur -> pid > -1) return 0; return -1; } int execute_program(char *execute, char bg) { int status; pid_t child; if (bg) { /* to prevent meltdowns, only a limited number of * processes can be executed */ if (n_children >= MAX_N_SPAWNED_PROCESSES) return 0; } else endwin(); child = fork(); if (child == 0) { #ifndef __minix setpgid(0, 0); #endif if (bg) setup_for_childproc(open_null(), 1, "dumb"); /* start process */ if (-1 == execlp(shell, shell, "-c", execute, (void *)NULL)) error_exit(TRUE, FALSE, "Error while starting \"%s -c '%s'\".\n", execute); /* if execlp returns, an error occured */ error_exit(FALSE, FALSE, "Error while starting process!\n"); } else if (child == -1) { error_exit(TRUE, FALSE, "Failed to fork child process.\n"); } if (bg) { /* remember this childprocess: we'll see if it has * died in the main-loop */ children_list[n_children++] = child; } else { /* wait for the childprocess to exit */ if (waitpid(child, &status, 0) == -1) error_exit(TRUE, FALSE, "Error while waiting for process to exit.\n"); /* restore (n)curses */ mydoupdate(); } return 0; } void init_children_reaper(void) { /* init list of pids to watch for exit */ memset(children_list, 0x00, sizeof(children_list)); } pid_t exec_with_pipe(char *command, int pipe_to_proc[], int pipe_from_proc[]) { pid_t pid = -1; if (pipe(pipe_to_proc) == -1) error_exit(TRUE, FALSE, "Error creating pipe.\n"); if (pipe(pipe_from_proc) == -1) error_exit(TRUE, FALSE, "Error creating pipe.\n"); if ((pid = fork()) == -1) error_exit(TRUE, FALSE, "fork() failed.\n"); if (pid == 0) { myclose(0); if (mydup(pipe_to_proc[0]) == -1) error_exit(TRUE, FALSE, "dup() failed.\n"); myclose(pipe_to_proc[1]); /* will not write to itself, only parent writes to it */ myclose(1); myclose(2); if (mydup(pipe_from_proc[1]) == -1) error_exit(TRUE, FALSE, "dup() failed.\n"); if (mydup(pipe_from_proc[1]) == -1) error_exit(TRUE, FALSE, "dup() failed.\n"); myclose(pipe_from_proc[0]); /* start process */ /* if (-1 == execlp(shell, shell, "-c", command, (void *)NULL)) error_exit("execlp of %s failed\n", command); */ if (-1 == execlp(command, command, (void *)NULL)) error_exit(TRUE, FALSE, "Error while starting '%s'.\n", command); /* if execlp returns, an error occured */ error_exit(FALSE, FALSE, "Error while starting process '%s'.\n", command); } return pid; } pid_t exec_with_pty(char *command, int *fd) { int fd_master = -1, fd_slave = -1; pid_t pid = get_pty_and_fork(&fd_master, &fd_slave); if (-1 == pid) error_exit(TRUE, FALSE, "An error occured while invoking get_pty_and_fork.\n"); if (pid == 0) { #ifndef __minix setpgid(0, 0); #endif myclose(fd_master); /* connect slave-fd to stdin/out/err */ setup_for_childproc(fd_slave, 1, "dumb"); /* start process */ if (-1 == execlp(command, command, (void *)NULL)) error_exit(TRUE, FALSE, "Error while starting '%s'.\n", command); } *fd = fd_master; #if defined(sun) || defined(__sun) || defined(AIX) || defined(_HPUX_SOURCE) || defined(OSF1) || defined(scoos) /* see start_proc */ #else if (myclose(fd_slave) == -1) error_exit(TRUE, FALSE, "Error closing slave-fd (pseudo tty, fd %d)\n", fd_slave); #endif return pid; } void exec_script(script *pscript) { if (pscript -> pid == 0) { int to[2], from[2]; pscript -> pid = exec_with_pipe(pscript -> script, to, from); pscript -> fd_r = from[0]; pscript -> fd_w = to[1]; /* int fd; pscript -> pid = exec_with_pty(pscript -> script, &fd); pscript -> fd_r = pscript -> fd_w = fd; */ } } multitail-7.1.5/exec.h000066400000000000000000000005271465663511400146440ustar00rootroot00000000000000int start_tail(char *filename, char retry_open, char follow_filename, int initial_tail, int *pipefd); int start_proc(proginfo *cur, int initial_tail); int execute_program(char *execute, char bg); void init_children_reaper(void); pid_t exec_with_pipe(char *command, int pipe_to_proc[], int pipe_from_proc[]); void exec_script(script *pscript); multitail-7.1.5/globals.c000066400000000000000000000123011465663511400153270ustar00rootroot00000000000000#define _LARGEFILE64_SOURCE /* required for GLIBC to enable stat64 and friends */ #include #include #include #include #include #include #include #include #include "mt.h" #include "mem.h" NEWWIN **splitlines = NULL; NEWWIN *menu_win = NULL; buffer *lb = NULL; char *config_file = NULL; char *mail_spool_file = NULL; char *nsubwindows = NULL; char *set_title = NULL; char *shell = NULL; char *tail = NULL; char *ts_format = NULL; char *statusline_ts_format = NULL; char *syslog_ts_format = NULL; char *cnv_ts_format = NULL; char *line_ts_format = NULL; char *window_number = NULL; char *subwindow_number = NULL; char **color_names = NULL; char *replace_by_markerline = NULL; const char *F1 = "For help at any time press F1."; int *n_win_per_col = NULL; int *vertical_split = NULL; color_scheme *cschemes = NULL; const char *version_str = " --*- multitail " VERSION " (C) 2003-2022 by folkert@vanheusden.com -*--"; conversion *conversions = NULL; keybinding *keybindings = NULL; pars_per_file *ppf = NULL; proginfo *pi = NULL; check_dir_glob *cdg = NULL; int n_cdg = 0; editscheme *pes = NULL; int n_es = 0; char *defaultcscheme = NULL; proginfo *terminal_index = NULL; char *global_find = NULL; int terminal_main_index = -1; int default_color_scheme = -1; int max_y, max_x; int min_n_bufferlines = -1; int mode_statusline = 1; int n_children = 0; int n_conversions = 0; int n_cschemes = 0; int n_keybindings = 0; int n_pars_per_file = 0; int n_splitlines = 0; int nfd = 0; int update_interval = 0; int n_colors_defined = 0; int check_for_mail = 5; /* check for mail every 5 seconds */ int default_maxnlines = 0; int default_maxbytes = 0; int popup_refresh_interval = 5; /* every 5 seconds */ int default_line_wrap_offset = 0; int inverse_attrs = A_REVERSE; int beep_interval = -1; int linecounter_for_beep = 0; int did_n_beeps = 0; int default_min_shrink = 10; int wordwrapmaxlength = 10; int default_bg_color = -1; int total_wakeups = 0; int split = 0; int syslog_port = 514; double heartbeat_interval = 0.0; double heartbeat_t = 0.0; off64_t msf_prev_size = 0; dtime_t msf_last_check = 0; time_t mt_started; pid_t children_list[MAX_N_SPAWNED_PROCESSES]; pid_t tail_proc = 0; /* process used by checker-proc */ struct stat64 msf_info; term_t term_type = TERM_IGNORE; myattr_t markerline_attrs = { -1, -1 }; myattr_t changeline_attrs = { -1, -1 }; myattr_t idleline_attrs = { -1, -1 }; myattr_t msgline_attrs = { -1, -1 }; myattr_t statusline_attrs = { -1, -1 }; myattr_t splitline_attrs = { -1, -1 }; colorpairs cp; mode_t def_umask = 0022; history_t search_h = { NULL, 0, NULL }; history_t cmdfile_h = { NULL, 0, NULL }; mybool_t re_case_insensitive = MY_FALSE; chtype box_bottom_left_hand_corner=0; chtype box_bottom_right_hand_corner=0; chtype box_bottom_side=0; chtype box_left_side=0; chtype box_right_side=0; chtype box_top_left_hand_corner=0; chtype box_top_right_hand_corner=0; chtype box_top_side=0; beeb_t beep_method = BEEP_BEEP; double beep_popup_length = 0.0; char afs = 0; /* abbreviate filesize */ char allow_8bit = 0; /* allow 8 bits ascii*/ char banner = 1; char bright_colors = 0; char do_refresh = 0; char filename_only = 0; char got_sigusr1 = 0; char mail = 0; char no_linewrap = 0; char prev_term_char = -1; char show_subwindow_id = 0; char tab_width = 4; /* some people use 8 */ char terminal_changed = 0; char timestamp_in_markerline = 0; char use_colors = 0; char warn_closed = 1; char caret_notation = 0; char statusline_above_data = 0; char global_marker_of_other_window = 0; char markerline_char = '-'; char changeline_char = '-'; char idleline_char = '-'; char msgline_char = '-'; char default_bufferwhat = 'f'; char load_global_config = 1; char default_linewrap = 'a'; char default_follow_filename = 0; char do_not_close_closed_windows = 0; char suppress_empty_lines = 1; char splitline_mode = SL_REGULAR; char abort_key = 7; char exit_key = 3; char default_sb_showwinnr = 0; char reuse_searchstring = 1; char need_died_procs_check = 0; char scrollback_fullscreen_default = 0; char scrollback_no_colors = 0; char scrollback_search_new_window = 1; mybool_t posix_tail = 0; mybool_t resolv_ip_addresses = 1; mybool_t show_severity_facility = 1; mybool_t gnu_tail = 0; mybool_t map_delete_as_backspace = MY_FALSE; regex_t global_highlight_re; char *global_highlight_str = NULL; filterscheme *pfs = NULL; int n_fs = 0; char *sigs[] = { NULL, "SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGTRAP", "SIGABRT/SIGIOT", "SIGIOT", "SIGBUS", "SIGFPE", "SIGKILL", "SIGUSR1", "SIGSEGV", "SIGUSR2", "SIGPIPE", "SIGALRM", "SIGTERM", "SIGSTKFLT", "SIGCHLD", "SIGCONT", "SIGSTOP", "SIGTSTP", "SIGTTIN", "SIGTTOU", "SIGURG", "SIGXCPU", "SIGXFSZ", "SIGVTALRM", "SIGPROF", "SIGWINCH", "SIGIO/SIGPOLL", "SIGPWR", "SIGSYS/SIGUNUSED" }; int n_known_sigs = 32; char *severities[8] = { "emerg", "alert", "crit", "err", "warning", "notice", "info", "debug" }; char *facilities[24] = { "kern", "user", "mail", "daemon", "auth", "syslog", "lpr", "news", "uucp", "cron", "authpriv", "ftp", "?", "?", "?", "?", "local0", "local1", "local2", "local3", "local4", "local5", "local6", "local7" }; void set_do_refresh(char val) { do_refresh = val; } char get_do_refresh() { return do_refresh; } multitail-7.1.5/globals.h000066400000000000000000000102731465663511400153420ustar00rootroot00000000000000extern proginfo *pi; extern buffer *lb; extern int nfd; extern int max_y, max_x; extern char terminal_changed; extern int split; extern char banner; extern NEWWIN **splitlines; extern int n_splitlines; extern NEWWIN *menu_win; extern int mode_statusline; extern char warn_closed; extern char use_colors; extern int min_n_bufferlines; extern color_scheme *cschemes; extern int n_cschemes; extern pars_per_file *ppf; extern int n_pars_per_file; extern char mail; extern int check_for_mail; extern char tab_width; extern time_t mt_started; extern int *vertical_split; extern int *n_win_per_col; extern proginfo *terminal_index; extern int terminal_main_index; extern char prev_term_char; extern int n_keybindings; extern keybinding *keybindings; extern char *set_title; extern pid_t tail_proc; extern char bright_colors; extern char *mail_spool_file; extern struct stat64 msf_info; extern off64_t msf_prev_size; extern dtime_t msf_last_check; extern int update_interval; extern char *tail; extern char afs; extern colorpairs cp; extern char show_subwindow_id; extern myattr_t markerline_attrs; extern myattr_t changeline_attrs; extern myattr_t idleline_attrs; extern myattr_t statusline_attrs; extern myattr_t msgline_attrs; extern char timestamp_in_markerline; extern double heartbeat_t; extern double heartbeat_interval; extern const char *version_str; extern char *ts_format; extern char *statusline_ts_format; extern char *cnv_ts_format; extern conversion *conversions; extern int n_conversions; extern char *shell; extern char no_linewrap; extern char *config_file; extern char got_sigusr1; extern char filename_only; extern int default_color_scheme; extern char *nsubwindows; extern char allow_8bit; extern char **color_names; extern int n_colors_defined; extern beeb_t beep_method; extern double beep_popup_length; extern char caret_notation; extern mode_t def_umask; extern check_dir_glob *cdg; extern int n_cdg; extern char statusline_above_data; extern char *F1; extern int default_maxnlines; extern int default_maxbytes; extern int popup_refresh_interval; extern char global_marker_of_other_window; extern char markerline_char; extern char changeline_char; extern char idleline_char; extern char msgline_char; extern char *replace_by_markerline; extern char default_bufferwhat; extern color_scheme *cschemes; extern int n_cschemes; extern colorpairs cp; extern pid_t children_list[MAX_N_SPAWNED_PROCESSES]; extern int n_children; extern term_t term_type; extern regex_t global_highlight_re; extern char *global_highlight_str; extern mybool_t re_case_insensitive; extern filterscheme *pfs; extern int n_fs; extern char load_global_config; extern char default_linewrap; extern int default_line_wrap_offset; extern char default_follow_filename; extern char do_not_close_closed_windows; extern char suppress_empty_lines; extern editscheme *pes; extern int n_es; extern char splitline_mode; extern myattr_t splitline_attrs; extern int inverse_attrs; extern char abort_key; extern char exit_key; extern int beep_interval; extern int linecounter_for_beep; extern int did_n_beeps; extern char *defaultcscheme; extern char *line_ts_format; extern int default_min_shrink; extern char default_sb_showwinnr; extern int wordwrapmaxlength; extern history_t search_h; extern history_t cmdfile_h; extern int default_bg_color; extern char reuse_searchstring; extern char *sigs[]; extern int n_known_sigs; extern char need_died_procs_check; extern chtype box_bottom_left_hand_corner; extern chtype box_bottom_right_hand_corner; extern chtype box_bottom_side; extern chtype box_left_side; extern chtype box_right_side; extern chtype box_top_left_hand_corner; extern chtype box_top_right_hand_corner; extern chtype box_top_side; extern char *window_number; extern char *subwindow_number; extern int total_wakeups; extern mybool_t posix_tail; extern mybool_t resolv_ip_addresses; extern mybool_t show_severity_facility; extern char *severities[]; extern char *facilities[]; extern char *syslog_ts_format; extern char scrollback_fullscreen_default; extern char scrollback_no_colors; extern int syslog_port; extern char scrollback_search_new_window; extern char *global_find; extern mybool_t gnu_tail; extern mybool_t map_delete_as_backspace; void set_do_refresh(char val); char get_do_refresh(); multitail-7.1.5/help.c000066400000000000000000001427311465663511400146470ustar00rootroot00000000000000#define _LARGEFILE64_SOURCE /* required for GLIBC to enable stat64 and friends */ #include #include #include #include #include #include #include #include #include "error.h" #include "mt.h" #include "mem.h" #include "help.h" #include "term.h" #include "utils.h" #include "globals.h" char *help_main_menu[] = { "MultiTail lets you create one or more windows in", "one terminal. In each window one can monitor one", "or more logfiles and(!)/or the output of external", "programs: when using multiple inputs, they get", "automatically merged.", "", "In the main menu one can do add/delete windows,", "move them, swap them, set regular expressions, set", "color(-schemes) etc.", "", "One can press the following keys:", " ^q^ quit the program", " ^F1^ this help", " ^/^ search in all windows", " ^shift^ + ^/^ highlight in all windows", " ^b^ scroll back", " ^B^ scroll back in ALL windows merged into", " one window", " ^I^ toggle case sensitivity for searches", " ^a^ add a new window or add another file/", " program to an existing window", " ^d^ delete a window (or a file/program from a", " window)", " ^s^ swap the location of 2 windows", " ^e^ enter/edit regular expressions", " ^f^ enter/edit in line filter regexps", " ^c^ set/change colors (only works if your", " terminal supports colors)", " ^C^ edit the r/g/b components of a color", " ^b^ scrollback in the buffer of a window", " ^v^ toggle vertical split", " ^0^...^9^ set a mark in a window so that you can", " easily see what has changed since the last", " time you looked", " ^R^ restart a window", " ^ctrl^ + ^v^ select conversion scheme(s)", " ^ctrl^ + ^t^ toggle subwindow before lines", " ^Y^ send a signal to a window (for commands)", " ^y^ set linewrap", " ^o^ wipe (clear) a window", " ^O^ wipe all windows", " ^T^ truncate a logfile", " ^g^ make screendump", " ^r^ redraw screen", " ^I^ toggle case sensitivity for searches", " ^l^ list keybindings", " ^i^ info", " ^t^ statistics", " ^j^ set window sizes", " ^z^ hide/unhide a window", " ^u^ hide all windows but the one selected", " ^U^ unhide all windows", " ^w^ write a script which starts multitail", " with the current settings (the files that", " are monitored, regular expressions, etc.)", " ^n^ clear the buffer of a window", " ^N^ clear all buffers of all windows", " ^m^ set the size of a buffer", " ^k^ enter a (limited) terminal-mode", " ^p^ pause all windows", " ^P^ (un-)pause one window", "", "", "", "^MultiTail^ " VERSION " was written by", "_folkert@vanheusden.com_.", "Check: _http://www.vanheusden.com/multitail/_", "for new versions.", NULL }; char *help_add_file_or_cmd[] = { "MultiTail lets you display logfiles and the", "output of commands. At this point you can select", "wether you want to add to the new window a file", "or the output of a command.", " ^f^ selects a file", " ^c^ selects a commands", " ^ctrl+g^ abort", "", "Commands are executed in their own virtual tty to", "force line-buffered output. They are executed by", "starting up */bin/sh -c* with as parameter the", "command you'll enter here.", NULL }; char *help_list_keybindings[] = { "This screen lists the keybindings you defined in", "the MultiTail configurationfile which is located", "in "CONFIG_FILE". The keybindings only", "work in the main screen.", "In this screen, you can press the followking", "keys:", " ^UP^ (cursor key UP) scroll up", " ^DOWN^ (cursor key DOWN) scroll down", " ^q^ abort", NULL }; char *help_enter_filename_to_monitor[] = { "Please enter the filename of the file you wish to", "monitor. If the file is not in the directory you", "started MultiTail from, please enter the complete", "pathname. As with bash, you can press the *TAB*-", "key for filename completion: if more then one file", "exists beginning with what you typed at that", "point, you'll get a list of files that match. With", "the cursor keys and the enter key you can then", "select the filename to use.", NULL }; char *help_enter_cmd_to_monitor[] = { "Please enter the command that you want to monitor", "with MultiTail. If the command is not in the", "directory you started MultiTail from, please enter", "the complete pathname. As with bash, you can press", "the *TAB* key for filename completion: if more", "then one file exists beginning with what you typed", "at that point, you'll get a list of files that", "match. With the cursor keys and the enter key you", "can then select the filename to use.", "", "Commands are executed in their own virtual tty to", "force line-buffered output. They are executed by", "starting up */bin/sh -c* with as parameter the", "command you'll enter here.", NULL }; char *help_add_window_merge_subwin[] = { "At this point there are already one or more", "windows on the screen. You can now indicate if", "you want the new file/command to be *merged* in an", "existing window. Lines from the logfiles/commands", "in a window are then interleaved as they are", "generated", "Enter ^y^ or ^n^ here or ^ctrl+g^ to abort.", NULL }; char *help_add_window_select_merge_window[] = { "At this point there are already one or more", "windows on the screen. You can now indicate if", "you want the new file/command to be *merged* in an", "existing window. Lines from the logfiles/commands", "in a window are then interleaved as they are", "generated", "Here, select the window to merge with.", "Press ^ctrl+g^ to abort.", NULL }; char *help_add_file_follow_filename[] = { "Here you can select wether to follow the filename", "(^y^) or the filedescriptor (^n^). When the file that", "you want to monitor gets, for example, \"rotated\"", "by *logrotate* every night, you want to select to", "follow the filename here. What this means is: if", "the filename gets renamed (or deleted) and a new", "file with the original filename is created, that", "new file will automatically be monitored instead", "of the original file.", NULL, }; char *help_ask_colors[] = { "Now you can select how MultiTail colors the text", "it displays.", " ^n^ do *not* use any coloring at all", " ^s^ this option expects that you're monitoring", " a file in the syslog format, e.g: date", " hostname, program, pid, message. It uses", " the name of the program which logged the", " message to select a color", " ^m^ this option evaluates the complete logged", " line to select a color", " ^S^ this option lets you select a pre-defined", " color scheme. That color scheme should be", " defined in *multitail.conf* which can be", " stored in your home directory or in */etc*", " See the example file for details.", " ^f^ when selecting this option, you'll be", " asked to enter the number of the field to", " use for selecting a color. You'll also be", " asked to enter the character that seperates", " each field in a line", " ^ctrl+g^ abort", "", "Using colors only works if your terminal supports", "this.", NULL, }; char *help_colors_field_nr[] = { "Selecting a field works like the *cut* command:", "you select a delimiter and a field-nummer. The", "delimiter specifies where a field ends and where", "the next field starts. There's one important", "difference between MultiTail and *cut* field-", "delimiters can be more then one character in size.", "", "Here, please enter the number of the field which", "you want to use for the color-selection. The first", "field on a line has number 0.", NULL }; char *help_colors_field_delimiter[] = { "Selecting a field works like the *cut* command:", "you select a delimiter and a field-nummer. The", "delimiter specifies where a field ends and where", "the next field starts. There's one important", "difference between MultiTail and *cut* field-", "delimiters can be more then one character in size.", "", "Here, please enter the string that delimits each", "field. This delimiter must be at least one", "character in size.", NULL }; char *help_ask_colors_select_scheme[] = { "Here you can select a predefined color scheme.", "These color schemes are defined in *multitail.conf*", "which can be located in your homedirectory and in", "*/etc*.", "In this configuration file you select using", "regular expressions what color to use for what", "patterns.", "See the example *multitail.conf* which should've", "been included with the MultiTail when you", "installed it.", "Press ^ctrl+g^ to abort.", NULL }; char *help_add_window_repeat_interval[] = { "In the case that the external command you're", "monitoring with MultiTail exits, the window gets", "automatically closed. Press enter to indicate that", "that is ok with you. If you want the command to be", "automatically repeated instead, enter here the", "delay before restarting that command. The minimum", "is 0: in that case the program gets repeated", "immediately (that is: when MultiTail notices that", "it died!).", "Press enter on an empty line to abort.", NULL }; char *help_add_file_display_diff[] = { "In some situations (for example: when monitoring", "the output of '*netstat -t tcp*') you may only", "want to see the difference between each run. In", "that case press ^y^ here. To see all output every", "time, press ^n^.", "Press ^ctrl+g^ to abort.", "", "On the commandline you can select this behaviour", "with the ^-R^ switch.", NULL }; #define help_merge_another_window help_add_window_select_merge_window char *help_enter_regexp_select_window[] = { "In this menu one can enter/edit/delete regular", "expressions which are used to filter what is", "displayed in a window." "First you need to select the main-window. If more", "then one file/command is displayed in a window,", "you'll get another selection box after this one", "where you can select a \"sub-window\".", "Press ^ctrl+g^ to abort.", NULL }; char *help_enter_regexp_select_subwindow[] = { "The window you selected contains the output of", "more then one file/command. You now have to", "select the \"sub-window\" to edit the regular", "expressions for.", "Press ^ctrl+g^ to abort.", NULL }; char *help_regexp_menu[] = { "This menu has a couple of options for editing", "regexps ('regular expressions'):", " ^a^ add a new regexp (one can have a maximum", " of 10 per file/command)", " ^e^ edit an existing regexp", " ^d^ delete one of the existing regexps", " ^D^ move an entry down, this also changes", " priority of this reg.exp.", " ^U^ move an entry up", " ^r^ reset counter", " ^q^/^x^ leave this menu", NULL }; char *help_enter_regexp[] = { "Enter a regular expression. This expression will", "be used for filtering.", "A regular expression is a method for defining", "text-patterns. See wikipedia for more info:", "_http://en.wikipedia.org/wiki/Regular__expression_", "or read my (Dutch!) article:", "_http://www.vanheusden.com/misc/regexp.html_", "Press enter on an empty line to abort.", NULL }; char *help_negate_regexp[] = { "Here you select how to handle the outcome of the", "regular expression processing: should it match", "when the regexp matches, or should it match when", "the regexp does *not* match?", "Press ^ctrl+g^ to abort.", NULL }; char *help_regexp_usage[] = { "Here you select what to do when a regexp matches.", " ^m^ show the line if the regexp matches. If", " selected to \"negate\" the outcome of the", " regexp, ^m^ tells MultiTail to only display", " the line if the regexp does *not* match.", " ^c^ if the regexp matches (see also ^m^)," " the line is displayed and colored", " ^C^ the lines are always printed but if the", " regexp matches, colors are used", " ^B^ if a regexp matches for a line, a bell-", " sound is produced", " ^b^ combination of ^C^ and ^B^", " ^x^ if a regexp matches, a command is", " executed", " ^ctrl+g^ abort", NULL }; char *help_enter_cmd[] = { "Enter the command which should be executed when a", "line matches a regular expression.", "The command is executed in the background and its", "output is redirected to */dev/null*.", "The command is started as a parameter of */bin/sh -c*", "Press enter on an empty line to abort.", NULL }; char *help_compile_regexp_failed[] = { "Compilation (\"pre-processing\") of the regular", "expression failed.", "Please check the wikipedia page for the correct", "syntax:", "_http://en.wikipedia.org/wiki/Regular__expression_", "or read my (Dutch!) article:", "_http://www.vanheusden.com/misc/regexp.html_", NULL }; char *help_delete_select_window[] = { "You've indicated that you want to delete a window.", "Select the window that you want to remove.", "If a window consists of more then one logfile or", "command, you'll be asked next if you want to", "remove the complete window including all", "\"subwindows\" or just the first one (the first", "file/command).", "Press ^ctrl+g^ to abort.", NULL }; char *help_delete_window_delete_all_subwin[] = { "This window contains more then one logfile /", "command. Here you select if you want to remove all", "\"subwindows\" or just the first file/command.", "Press ^ctrl+g^ to abort.", NULL }; char *help_delete_select_subwindow[] = { "This window contains more then one logfile /", "command. Select the file / command you wish to", "remove from this window.", "Press ^ctrl+g^ to abort.", NULL }; char *help_swap_win1[] = { "You have indicated that you want to swap the", "location (on the screen) of 2 windows.", "Please select the first window.", "Press ^ctrl+g^ to abort.", NULL }; char *help_swap_win2[] = { "You have indicated that you want to swap the", "location (on the screen) of 2 windows.", "Please select the second window.", "Press ^ctrl+g^ to abort.", NULL }; char *help_toggle_colors_select_window[] = { "You've indicated that you want to change the", "colors of a window. Please select that window.", "If a window consists of more then one logfile or", "command, you'll be asked to select the file or", "command in that window to change the colors for.", "Press ^ctrl+g^ to abort.", NULL }; char *help_toggle_colors_select_subwindow[] = { "This window contains more then one logfile /", "command. Select the file / command you wish to", "change the colors for.", "Press ^ctrl+g^ to abort.", NULL }; char *help_failed_to_start_tail[] = { "There was a problem starting the '*tail*'-process", "(in case you wanted to monitor a logfile) or", "there was a problem starting the command you", "wanted to monitor.", NULL }; char *help_window_closed[] = { "A window has closed. This happens when you're", "monitoring the output of an external program. That", "program has died and when that happens the", "window is automatically closed.", "On the commandline you can set with the ^-r^ or", "the ^-R^ that a command should be repeated when it", "exits. This behaviour can also be set when you add", "a window by pressing the ^a^-key in the main menu.", NULL }; char *help_write_script[] = { "This function writes a script to disk which will", "start MultiTail in the same state it now is. This", "is useful in case you did a lot of configuring", "with the menu-interface, you then don't have to", "find out what commandline switches to set.", "Here you have to enter the filename for the script", "to generate.", "Press enter on an empty line to abort.", NULL }; char *help_statistics[] = { "In this menu one can select the window or sub-", "window to display the statistics off.", " ^r^ reset ALL counters for ALL windows", " ^ctrl+g^ exit window", NULL, }; char *help_statistics_popup[] = { "Apart from how long MultiTail has been running, it", "also shows a couple of statistics for each window.", "It also shows some statistics on the interval", "between each show line in each window.", " *average* average interval", " *standard deviation* speaks for itself", "", " ^r^ reset the counters for this window", " ^ctrl+g^ exit window", NULL }; char *help_set_buffering[] = { "In this window you can control the buffering for", "each window. The first question lets you select", "on what window you want to set the buffering", "parameters:" " ^a^ set on all windows", " ^o^ set on one window", "Press ^ctrl+g^ to abort.", NULL }; char *help_set_buffering_store_what[] = { "Here you set what should be buffered.", " ^a^ buffer all lines that are monitored", " ^m^ only buffer the lines that match the", " configured regular expressions. If you have", " not set any regexps, everything is stored.", "Press ^ctrl+g^ to abort.", NULL }; char *help_enter_number_of_lines_to_store[] = { "Set the size of the buffer. The default size is", "100 lines per window.", "Press enter on an empty line to abort.", NULL }; char *help_set_buffering_select_window[] = { "Here you select the window on which you want to", "set the buffer limits.", "Press ^ctrl+g^ to abort.", NULL }; char *help_hide_window[] = { "Select the window that you want to (un-)hide. When", "a window is hidden, it is not displayed: logging", "and filtering continues as usual.", "Press ^ctrl+g^ to abort.", NULL }; char *help_set_windowsizes[] = { "Here you can set the sizes of windows. The width", "can only set for *all* windows, the height can be", "set for each individual window.", " ^m^ manage columns: add a column, set width", " ^h^ set the height for a window", "Press ^ctrl+g^ to abort.", NULL }; char *help_set_window_width[] = { "When setting the window width, you actually set", "the width of the windows on the left. So if you", "set it to 20, the windows on the left are 20", "characters in width. The windows on the right are", "then what is left in width. Beware: one column is", "used for the dividing line.", "This option can only be used when you've switched", "on \"vertical split\" in the main menu (^v^).", "Press enter on an empty line to abort.", NULL }; char *help_set_window_height[] = { "The height of a window must be at least 2 lines.", "That is excluding the statusline. The statusline", "can be switched off using the '^-D^' commandline", "switch.", "One can only set the window height if there are at", "least 2 windows on the screen.", "When you enter '0' MultiTail will automatically", "set the height. The height must be either 0 or", "more then one.", "Press enter on an empty line to abort.", NULL }; char *help_set_window_height_select_window[] = { "You're about to set the height of a window. The", "first step is to select the window which you want", "to resize.", "Press ^ctrl+g^ to abort.", NULL }; char *help_terminal_mode_select_window[] = { "MultiTail features a small terminal. With this", "terminal you can send commando's to a program you", "are monitoring. That way, one can put a telnet-", "session in a window, login and start a tail-", "process on the remote host.", "Warning: this doesn't work for ssh as ssh accesses", "the tty you've been logged on directly.", NULL }; char *help_scrollback_help[] = { "The scrollback menu lets you scrollback in the", "buffer. The default size of the buffer is 100", "lines. This size can be set with the ^-m^ command-", "line parameter or the ^m^-key in the main menu.", " ^c^ set colors", #ifdef __APPLE__ " ^x^ copy contents to clipboard (pbcopy)", #else " ^x^ copy contents to X clipboard (xclip)", #endif " ^f^/^/^ search for a string in the buffer", " ^n^ find the next occurence", " ^Y^ toggle linewrap. if linewrap is disabled,", " one can scroll left/right with the cursor-", " keys.", " ^ctrl^+^t^ show (sub-)window number in front of", " each line", " ^t^ show a list of window-nrs and what they", " contain. can also display statistics.", " ^s^ save the buffer to a file", " ^F9^/^ctrl+w^ switch to full screen mode (and back)", "Press ^ctrl+g^ to abort.", NULL }; char *help_scrollback_savefile[] = { "Save a buffer to a file.", " ^a^ write all lines to the file", " ^m^ write only the lines to the file which", " match the regular expression(s) (if any)", NULL }; char *help_scrollback_savefile_enter_filename[] = { "Save a buffer to a file. Enter the name of the", "file to write to.", "Press enter on an empty line to abort.", NULL }; char *help_scrollback_no_mark[] = { "You cannot scrollback in this window as it has no", "buffer. Press ^m^ in the main-menu to set the size", "of the buffer or use the ^-m^ commandline switch.", NULL }; char *help_scrollback_select_window[] = { "Select the window in which you want to scrollback.", "Press ^ctrl+g^ to abort.", NULL }; char *help_scrollback_edit_search_string[] = { "Enter the string to search for in the scrollback-", "buffer. The search-string can be a regular", "expression.", "Press enter on an empty line to abort.", NULL }; char *help_pause_a_window[] = { "Select the window to (un-)pause. When a window is", "paused, it won't be updated until the window is", "un-paused. NOTHING will be discarded.", NULL }; char *help_ask_colors_select_color[] = { "Select the color or attribute to use.", NULL }; char *help_set_vertical_split_n_win[] = { "Enter the number of columns to split the terminal", "in. If you press 'j' in the menu, you can also", "select the number of windows per column.", NULL }; char *help_wipe_window[] = { "Press the number of the window to wipe. The number", "can be found in the status-line of the windows.", "Press 'r' to redraw the windows so that you can", "see their contents again.", NULL }; char *help_select_colorschemes[] = { "Move the cursor with the cursor keys to the a", "color scheme. Then press space to select and again", "to unselect. Press enter to submit or ^g to abort.", NULL }; char *help_manage_cols[] = { "Press ^a^ to add a column, ^d^ to delete one and press", "^e^ to set the number of windows in a column.", NULL }; char *help_set_linewrap[] = { "In this menu one can set how the wrap the text in", "windows.", " ^l^ start at the left and cut of at the right", " ^a^ try to display everything", " ^r^ cut off at the left", " ^s^ cut right before the syslog process name", " ^S^ cut right after the syslog process name", " ^o^ enter a position where to cut", " Press ctrl+g to abort.", NULL }; char *help_enter_stripper_select_window[] = { "Select window to apply the stripping to.", NULL }; char *help_enter_stripper_select_subwindow[] = { "Select the sub-window to apply the stripping to.", NULL }; char *help_redirect_failed[] = { "Sending the data which was retrieved from a tail-", "process to some other process or file failed.", "Maybe the disk is full or the process died?", NULL }; char *help_stripper_type[] = { " ^e^ filter by using a regular expression", " ^r^ filter by specifying the character-range", " ^c^ specify a column to strip - you'll be asked", " to enter a delimiter as well", NULL }; char *help_stripper_start_offset[] = { "Please enter the offset from where to strip the", "string. The offset starts at 0.", NULL }; char *help_stripper_end_offset[] = { "Please enter the offset upto where the string will", "be filtered. Upto, not including!", NULL }; char *help_stripper_delimiter[] = { "When selecting stripping by column-number you need", "to enter the delimiter for each column. Usually", "you would enter a space here.", NULL }; char *help_stripper_col_nr[] = { "Enter the number of the column here. The first one", "is at offset 0.", NULL }; char *help_error_write_script_create_file[] = { "There was an error writing the scriptfile. Maybe", "the filesystem is full or the media is damaged?", NULL }; char *help_column_width[] = { "Enter the number of characters that will fit in a", "line for this column.", NULL }; char *help_n_win_per_col[] = { "Enter the number of windows that will fit in this", "column (vertically).", NULL }; char *help_send_signal_select_window[] = { "Select the window to send the signal to. Please", "note that sending a signal may cause the process", "(for example the tail-process when monitoring a", "logfile) to exit and thereby closing the window.", NULL }; char *help_send_signal_window_send_to_all_subwin[] = { "Would you like to send the signal to all sub-", "windows?", NULL }; char *help_send_signal_select_subwindow[] = { "Select the subwindow to send the signal to.", NULL }; char *help_send_signal_failed[] = { "Sending the signal to the process failed. This", "should not happen.", NULL }; char *help_select_signal[] = { "Select the signal to send. Please note that some", "signals may cause the process to exit and closing", "the window", NULL }; char *help_screendump_select_file[] = { "Select the file to write the screendump to.", "Existing files will be overwritten.", NULL }; char *help_screendump_select_win[] = { "Select the window to dump to a screendump-file.", "The output file will be plain ascii.", NULL }; char *help_truncate_file_select_window[] = { "Select window. By selecting a window you select", "the file to truncate.", NULL }; char *help_truncate_file_select_subwindow[] = { "As the window you selected contains several sub-", "windows, you need to select now the subwindow to", "truncate.", NULL }; char *help_truncate_areyousure[] = { "Are you really sure you want to truncate this", "file? This cannot be undone!", NULL }; char *help_truncate_failed[] = { "There was an error truncating the file. Please", "check the permissions on the file.", NULL }; char *help_truncate_only_logfiles[] = { "One can only truncate logfiles and not processes.", NULL }; char *help_select_color_and_attributes[] = { "Select the foreground and background color to use.", "You can select one foreground and one background-", "color but you can select multiple attributes.", "Press the space-bar to select or un-select.", NULL }; char *help_cannot_change_color[] = { "Your terminal doesn't support chaning the red,", "green and blue values of a color.", NULL }; char *help_edit_color_edit[] = { "Depending on the range you selected (by pressing", "the cursor up and down key), you can enter a value", "for either red, green or blue in the range of", "0-1000, 0-255 or 00-ff (hex value).", NULL }; char *help_edit_color[] = { "In this menu one can edit the values for the red,", "the green and the blue component of a color.", "Move the '>' with the up and down cursorkeys in", "front of the range in which you want to enter the", "value. The uppermost range being 0 upto 1000, the", "middle 0-255 and the lowest also 0-255 but entered", "in hexadecimal values.", " ^r^ edit the red component", " ^g^ edit the green component", " ^b^ edit the blue component", " ^c^ give the color a name/description", NULL }; char *help_edit_color_change_name[] = { "One can give this specific red, green and blue", "values a name. Like roses-red or so. This name can", "then be used in a color scheme.", NULL }; char *help_hide_but_window[] = { "Select which window to keep open. All other", "windows will be closed.", NULL }; char *help_search_in_all_windows[] = { "This functionality lets you search in all windows.", "The result will be merged into one new window.", NULL }; char *help_highlight_in_all_windows[] = { "This functionality lets you search in all windows.", "All lines with the search-string will be", "highlighted (displayed in reverse).", NULL }; char *help_select_conversionschemes[] = { "With a conversionscheme one can let MultiTail", "automatically convert e.g. ip-addresses, time-", "stamps, etc. to something more readable. In this", "menu you can select what conversionscheme(s) to", "apply to a (sub-)window.", NULL }; char *help_select_restart_window[] = { "Restarting a window stops- and restarts the tail-", "or command running underneath a (sub-)window.", NULL }; char *help_select_restart_window_all[] = { "This chooses wether to select all sub-windows or", "just one specific.", NULL }; char *help_select_terminal_emulation[] = { "MultiTail can emulate a terminal. That way it can", "display color (escape-)codes that sometimes are", "in (log-)files.", NULL }; char *help_clear_buffer[] = { "Select window for which to clear the scrollback", "buffer.", NULL }; char *help_history[] = { /*2345678901234567890123456789012345678901234567890 */ "Select a string from this list. These strings have", "been used in the past in MultiTail. They are", "stored in a file. Check the 'history_file' entry", "in the configuration-file to see in what file.", NULL }; #if 0 char *help_bla[] = { /*2345678901234567890123456789012345678901234567890 */ "FIXME", /* FIXME */ NULL }; #endif help_t help[] = { { HELP_MAIN_MENU, help_main_menu }, { HELP_ADD_FILE_OR_CMD, help_add_file_or_cmd }, { HELP_LIST_KEYBINDINGS, help_list_keybindings }, { HELP_ENTER_FILENAME_TO_MONITOR, help_enter_filename_to_monitor }, { HELP_ENTER_CMD_TO_MONITOR, help_enter_cmd_to_monitor }, { HELP_ADD_WINDOW_MERGE_SUBWIN, help_add_window_merge_subwin }, { HELP_ADD_WINDOW_SELECT_MERGE_WINDOW, help_add_window_select_merge_window }, { HELP_ADD_FILE_FOLLOW_FILENAME, help_add_file_follow_filename }, { HELP_ASK_COLORS, help_ask_colors }, { HELP_COLORS_FIELD_NR, help_colors_field_nr }, { HELP_COLORS_FIELD_DELIMITER, help_colors_field_delimiter }, { HELP_ASK_COLORS_SELECT_SCHEME, help_ask_colors_select_scheme }, { HELP_ADD_WINDOW_REPEAT_INTERVAL, help_add_window_repeat_interval }, { HELP_ADD_FILE_DISPLAY_DIFF, help_add_file_display_diff }, { HELP_MERGE_ANOTHER_WINDOW, help_merge_another_window }, { HELP_ENTER_REGEXP_SELECT_WINDOW, help_enter_regexp_select_window }, { HELP_ENTER_STRIPPER_SELECT_WINDOW, help_enter_stripper_select_window }, { HELP_ENTER_REGEXP_SELECT_SUBWINDOW, help_enter_regexp_select_subwindow }, { HELP_ENTER_STRIPPER_SELECT_SUBWINDOW, help_enter_stripper_select_subwindow }, { HELP_REGEXP_MENU, help_regexp_menu }, { HELP_ENTER_REGEXP, help_enter_regexp }, { HELP_NEGATE_REGEXP, help_negate_regexp }, { HELP_REGEXP_USAGE, help_regexp_usage }, { HELP_ENTER_CMD, help_enter_cmd }, { HELP_COMPILE_REGEXP_FAILED, help_compile_regexp_failed }, { HELP_DELETE_SELECT_WINDOW, help_delete_select_window }, { HELP_DELETE_SELECT_SUBWINDOW, help_delete_select_subwindow }, { HELP_DELETE_WINDOW_DELETE_ALL_SUBWIN, help_delete_window_delete_all_subwin }, { HELP_SWAP_WIN1, help_swap_win1 }, { HELP_SWAP_WIN2, help_swap_win2 }, { HELP_TOGGLE_COLORS_SELECT_WINDOW, help_toggle_colors_select_window }, { HELP_TOGGLE_COLORS_SELECT_SUBWINDOW, help_toggle_colors_select_subwindow }, { HELP_FAILED_TO_START_TAIL, help_failed_to_start_tail }, { HELP_WINDOW_CLOSED, help_window_closed }, { HELP_WRITE_SCRIPT, help_write_script }, { HELP_STATISTICS, help_statistics }, { HELP_SET_BUFFERING, help_set_buffering }, { HELP_SET_BUFFERING_STORE_WHAT, help_set_buffering_store_what }, { HELP_ENTER_NUMBER_OF_LINES_TO_STORE, help_enter_number_of_lines_to_store }, { HELP_SET_BUFFERING_SELECT_WINDOW, help_set_buffering_select_window }, { HELP_HIDE_WINDOW, help_hide_window }, { HELP_SET_WINDOWSIZES, help_set_windowsizes }, { HELP_SET_WINDOW_WIDTH, help_set_window_width }, { HELP_SET_WINDOW_HEIGHT, help_set_window_height }, { HELP_SET_WINDOW_HEIGHT_SELECT_WINDOW, help_set_window_height_select_window }, { HELP_TERMINAL_MODE_SELECT_WINDOW, help_terminal_mode_select_window }, { HELP_SCROLLBACK_HELP, help_scrollback_help }, { HELP_SCROLLBACK_SAVEFILE, help_scrollback_savefile }, { HELP_SCROLLBACK_NO_MARK, help_scrollback_no_mark }, { HELP_SCROLLBACK_SAVEFILE_ENTER_FILENAME, help_scrollback_savefile_enter_filename }, { HELP_SCROLLBACK_SELECT_WINDOW, help_scrollback_select_window }, { HELP_SCROLLBACK_EDIT_SEARCH_STRING, help_scrollback_edit_search_string }, { HELP_PAUSE_A_WINDOW, help_pause_a_window }, { HELP_ASK_COLORS_SELECT_COLOR, help_ask_colors_select_color }, { HELP_SET_VERTICAL_SPLIT_N_WIN, help_set_vertical_split_n_win }, { HELP_MANAGE_COLS, help_manage_cols }, { HELP_WIPE_WINDOW, help_wipe_window }, { HELP_SELECT_COLORSCHEMES, help_select_colorschemes }, { HELP_SET_LINEWRAP, help_set_linewrap }, { HELP_REDIRECT_FAILED, help_redirect_failed }, { HELP_STRIPPER_TYPE, help_stripper_type }, { HELP_STRIPPER_START_OFFSET, help_stripper_start_offset }, { HELP_STRIPPER_END_OFFSET, help_stripper_end_offset }, { HELP_STRIPPER_DELIMITER, help_stripper_delimiter }, { HELP_STRIPPER_COL_NR, help_stripper_col_nr }, { HELP_ERROR_WRITE_SCRIPT_CREATE_FILE, help_error_write_script_create_file }, { HELP_COLUMN_WIDTH, help_column_width }, { HELP_N_WIN_PER_COL, help_n_win_per_col }, { HELP_SEND_SIGNAL_SELECT_WINDOW, help_send_signal_select_window }, { HELP_SEND_SIGNAL_WINDOW_SEND_TO_ALL_SUBWIN, help_send_signal_window_send_to_all_subwin }, { HELP_SEND_SIGNAL_SELECT_SUBWINDOW, help_send_signal_select_subwindow }, { HELP_SEND_SIGNAL_FAILED, help_send_signal_failed }, { HELP_SELECT_SIGNAL, help_select_signal }, { HELP_SCREENDUMP_SELECT_FILE, help_screendump_select_file }, { HELP_SCREENDUMP_SELECT_WIN, help_screendump_select_win }, { HELP_TRUNCATE_FILE_SELECT_WINDOW, help_truncate_file_select_window }, { HELP_TRUNCATE_FILE_SELECT_SUBWINDOW, help_truncate_file_select_subwindow }, { HELP_TRUNCATE_AREYOUSURE, help_truncate_areyousure }, { HELP_TRUNCATE_FAILED, help_truncate_failed }, { HELP_TRUNCATE_ONLY_LOGFILES, help_truncate_only_logfiles }, { HELP_SELECT_COLOR_AND_ATTRIBUTES, help_select_color_and_attributes }, { HELP_CANNOT_EDIT_COLOR, help_cannot_change_color }, { HELP_EDIT_COLOR_EDIT, help_edit_color_edit }, { HELP_EDIT_COLOR, help_edit_color }, { HELP_EDIT_COLOR_CHANGE_NAME, help_edit_color_change_name }, { HELP_HIDE_BUT_WINDOW, help_hide_but_window }, { HELP_SEARCH_IN_ALL_WINDOWS, help_search_in_all_windows }, { HELP_HIGHLIGHT_IN_ALL_WINDOWS, help_highlight_in_all_windows }, { HELP_SELECT_CONVERSIONSCHEMES, help_select_conversionschemes }, { HELP_SELECT_RESTART_WINDOW, help_select_restart_window }, { HELP_SELECT_RESTART_WINDOW_ALL, help_select_restart_window_all }, { HELP_SELECT_TERMINAL_EMULATION, help_select_terminal_emulation }, { HELP_CLEAR_BUFFER, help_clear_buffer }, { HELP_HISTORY, help_history }, { -1, NULL } }; void show_help(int what_help) { help_t *php = NULL; int index = 0; int line_cnt = 0; int ppos = -1, pos = 0; while(help[index].nr != -1) { if (help[index].nr == what_help) { php = &help[index]; break; } index++; } if (php) { NEWWIN *winb = create_popup(15 + 2, 50 + 4); NEWWIN *win = create_popup(15 , 50 ); while(php -> text[line_cnt]) line_cnt++; wattron(winb -> win, A_STANDOUT); if (line_cnt > 15) mvwprintw(winb -> win, 0, 2, "Use cursor UP/DOWN to scroll, ctrl+g to exit"); else mvwprintw(winb -> win, 0, 2, "Press ctrl+g to exit"); wattroff(winb -> win, A_STANDOUT); mydoupdate(); for(;;) { int c; if (ppos != pos) { int loop; werase(win -> win); for(loop=pos; loop 0) { pos--; } else if ((c == KEY_DOWN || c == 13) && pos < (line_cnt - 1)) { pos++; } else if (c == KEY_PPAGE && pos >= 15) { pos -= 15; } else if ((c == KEY_NPAGE || c == ' ') && (pos + 15) < (line_cnt - 1)) { pos += 15; } else if (c == KEY_HOME && pos > 0) { pos = 0; } else if (tolower(c) == 'q' || c == abort_key) { break; } else if (c == -1) { /* a window got closed */ } else { wrong_key(); } } delete_popup(win); delete_popup(winb); } else { LOG("no help found for: %d\n", what_help); wrong_key(); } } void format_help(const char *short_str, const char *long_str, const char *descr) { int par_width = SWITCHES_COLUMN_WIDTH, max_wrap_width = par_width * 2 / 3, cur_par_width = 0; int descr_width = max_x - (par_width + 1); char *line = NULL, *p = (char *)descr; char first = 1; if (long_str && short_str) str_add(&line, "%-4s / %s", short_str, long_str); else if (long_str) str_add(&line, "%s", long_str); else str_add(&line, "%s", short_str); cur_par_width = fprintf(stderr, "%-*s ", par_width, line); free(line); if (par_width + 1 >= max_x || cur_par_width >= max_x) { fprintf(stderr, "%s\n", descr); return; } for(;strlen(p);) { char *n = NULL, *kn = NULL, *copy = NULL; int n_len = 0, len_after_ww = 0, len_before_ww = 0; int str_len = 0, cur_descr_width = first ? max_x - cur_par_width : descr_width; while(*p == ' ') p++; str_len = strlen(p); if (str_len == 0) break; len_before_ww = min(str_len, cur_descr_width); n = &p[len_before_ww]; kn = n; if (str_len > cur_descr_width) { while (*n != ' ' && n_len < max_wrap_width) { n--; n_len++; } if (n_len >= max_wrap_width) n = kn; } len_after_ww = (int)(n - p); if (len_after_ww <= 0) break; copy = (char *)malloc(len_after_ww + 1); memcpy(copy, p, len_after_ww); copy[len_after_ww] = 0x00; if (first) first = 0; else fprintf(stderr, "%*s ", par_width, ""); fprintf(stderr, "%s\n", copy); free(copy); p = n; } } char ansi_terminal(void) { const char *term = getenv("TERM"); if (!isatty(1) || !isatty(2)) return 0; if (!term) return 0; if (strcasecmp(term, "ansi") == 0) return 1; if (strcasecmp(term, "console") == 0 || strcasecmp(term, "con80x25") == 0 || strcasecmp(term, "linux") == 0 || strcasecmp(term, "linux-80x25") == 0) return 1; if (strcasecmp(term, "screen") == 0) return 1; if (strcasecmp(term, "xterm") == 0 || strcasecmp(term, "xterm-color") == 0 || strcasecmp(term, "xterm-256color") == 0 || strcasecmp(term, "xterm-16color") == 0) return 1; if (strcasecmp(term, "rxvt") == 0 || strcasecmp(term, "konsole") == 0 || strcasecmp(term, "linux") == 0 || strcasecmp(term, "linux-80x25") == 0 || strcasecmp(term, "konsole-16color") == 0 || strcasecmp(term, "rxvt-16color") == 0) return 1; return 0; } void set_bold(char on) { if (ansi_terminal()) { if (on) fprintf(stderr, "\x1b[1m"); else fprintf(stderr, "\x1b[22m"); } } void set_underline(char on) { if (ansi_terminal()) { if (on) fprintf(stderr, "\x1b[4m"); else fprintf(stderr, "\x1b[24m"); } } void reset_term() { if (ansi_terminal()) fprintf(stderr, "\x1b[0m\x1b[2K\r"); } void help_header(const char *str) { set_bold(1); fprintf(stderr, " *** "); set_underline(1); fprintf(stderr, "%s", str); set_underline(0); fprintf(stderr, " ***\n"); set_bold(0); } void usage(void) { reset_term(); fprintf(stderr, "%s", version_str); fprintf(stderr, "\n\nmultitail [-cs|-Cs|-c-] [-i] inputfile [-i anotherinputfile] [...]\n\n"); help_header("selecting files to follow"); format_help("-i x", NULL, "the following parameter is a filename (in case it starts with a dash)"); format_help("-I x", NULL, "like -i only this one merges this logfile into the previous window"); fprintf(stderr, "\n"); format_help("-q x z", NULL, "check every x seconds for new files by search criteria z, create a new window for those"); format_help("-qs x y z", NULL, "check every x seconds for new files by search criteria z, create a new window for those, use color scheme y"); format_help("-Q x z", NULL, "check every x seconds for new files by search criteria z, put them all in the same window (using subwindows)"); format_help("-Qs x y z", NULL, "check every x seconds for new files by search criteria z, put them all in the same window (using subwindows), use color scheme y"); format_help("-iw file i", NULL, "check every 'i' seconds if 'file' appeared in the filesystem"); format_help(NULL, "--new-only", "(for -q/-Q) only create windows for files created after multitail was started, existing files are ignored"); fprintf(stderr, "\n"); format_help("-f", NULL, "follow the following filename, not the descriptor (e.g. when logrotate archives logfiles)"); format_help(NULL, "--follow-all", "see -f: for all files after this switch"); format_help(NULL, "--retry", "keep trying to open the following file until it is accessible"); format_help(NULL, "--retry-all", "like --retry but for all following files"); fprintf(stderr, "\n"); help_header("selecting command output to follow"); format_help("-l x", NULL, "parameter is a command to be executed"); format_help("-L x", NULL, "see -l but merge the output to a previously created window"); format_help("-r interval", NULL, "restart the command when it terminated after `interval' seconds"); format_help("-R interval", NULL, "like -r, but only show the differences"); format_help("-Rc/-rc interval", NULL, "like -r/-R but clean the window before each iteration"); fprintf(stderr, "\n"); format_help("-j", NULL, "read from STDIN (can be used only once)"); format_help("-J", NULL, "like -j but merge into previous window"); fprintf(stderr, "\n"); format_help(NULL, "--listen [interface]:port", "behave like a syslog server. port is normally 514"); format_help(NULL, "--Listen [interface]:port", "like --listen but merge into previous window"); fprintf(stderr, "\n"); help_header("merge parameters"); format_help(NULL, "--mergeall", "merge all of the following files into the same window (in the previous window)"); format_help(NULL, "--mergeall-new", "merge all of the following files into the same window (in a new window)"); format_help(NULL, "--no-mergeall", "stop merging all files into one window"); format_help(NULL, "--no-repeat", "suppress repeating lines and replace them with a \"last message repeated x times\""); fprintf(stderr, "\n"); help_header("markers"); format_help(NULL, "--mark-interval x", "when nothing comes in, print a '---mark---' line every 'x' seconds"); format_help(NULL, "--mark-change", "when multiple files are merged an multitail switches between two windows, print a markerline with the filename"); format_help(NULL, "--no-mark-change", "do NOT print the markerline when the file changes (overrides the configuration file)"); fprintf(stderr, "\n"); help_header("initial tail / scrollback parameters"); format_help("-n x", NULL, "initial number of lines to tail"); format_help("-m x", NULL, "set scrollback buffer size (# lines)"); format_help("-mb x", NULL, "set scrollback buffer size (in bytes, use xKB/MB/GB)"); format_help("-bw a/f", NULL, "what to buffer: 'a'll or what went through the 'f'ilter"); fprintf(stderr, "\n"); help_header("\"tee\" functionality"); format_help("-a x", NULL, "like 'tee': write (filtered) input to file 'x'"); format_help("-A x", NULL, "see -a: but write the unfiltered(!) input to file 'x'"); format_help("-g x", NULL, "redirect the input also (filtered) to command/process 'x'"); format_help("-G x", NULL, "redirect the unfiltered input also to command/process 'x'"); fprintf(stderr, "\n"); help_header("screen layout"); format_help("-s x", NULL, "vertical split screen (in 'x' columns)"); format_help("-sw x,x,...", NULL, "at what columns to split the screen, use '0' for automatic size"); format_help("-sn x,x,...", NULL, "number of windows per column"); format_help("-wh x", NULL, "height of window"); fprintf(stderr, "\n"); help_header("filtering"); format_help("-fr scheme", NULL, "use the predefined filter from the configuration file"); format_help("-e[m]", NULL, "print only when matching with this regexp"); format_help("-ev", NULL, "print only when NOT matching with this regexp"); format_help("-ec", NULL, "use regular expression but display the matches inverted on following file"); format_help("-eC", NULL, "use regexp, display everything but matches inverted on following file"); format_help("-ex", NULL, "execute command ('-ex regexp command') when matches, matching line is given as commandline parameter"); format_help("-eX", NULL, "like -ex but only give the matching substring as commandline parameter to the command"); format_help("-E", NULL, "use regular expression on following files"); format_help("-Ec", NULL, "use regular expression but display the matches inverted on following files"); format_help("-EC", NULL, "use regexp, display everything but matches inverted on following files"); format_help("-ke x", NULL, "strip parts of the input using regular expression 'x'"); format_help("-kr x y", NULL, "strip parts of the input starting at offset x and ending (not including!) offset y"); format_help("-kc x y", NULL, "strip parts of the input: strip column 'y' with delimiter 'x'"); format_help("-ks x", NULL, "use edit scheme 'x' (defined in configuration file)"); format_help("-kS x", NULL, "only show the substrings matched by the substring-selects (the parts between '(' and ')') in the regular epxression 'x'"); format_help("-v", NULL, "invert next regular expression (do not use with -ev/em)"); fprintf(stderr, "\n"); help_header("colors"); format_help("-cv x", NULL, "use conversion scheme 'x' (see multitail.conf)"); format_help("-c", NULL, "colorize current"); format_help("-cS scheme", NULL, "use color scheme 'scheme' (as defined in multitail.conf)"); format_help("-csn", NULL, "extra switch for the following switches; do not use reverse (inverted) colors"); format_help("-Cs", NULL, "colorize all following files with syslog-scheme"); format_help("-C", NULL, "colorize all following files"); format_help("-Cf/-cf field delimiter", NULL, "colorize next/all file(s) depending on the given field number. fields are delimited with the given field-delimiter"); format_help("-ci color", NULL, "use 'color' (red, green, etc), useful when merging multiple inputs"); format_help("-c-", NULL, "do NOT colorize the following file"); format_help("-C-", NULL, "do NOT colorize the following files"); format_help("-cT term", NULL, "interpret terminal-codes from file/command (for terminal type 'term')"); format_help("-Z color", NULL, "set color for markerline"); format_help("-w", NULL, "do not use colors"); fprintf(stderr, "\n"); help_header("timestamps"); format_help("-ts", NULL, "add a timestamp (format configurable in multitail.conf) before each line"); format_help("-T", NULL, "put a timestamp in markerlines"); fprintf(stderr, "\n"); help_header("status line parameters"); format_help("-d", NULL, "do NOT update the status-line"); format_help("-D", NULL, "do not display a status-line at all"); format_help("-du", NULL, "put the statusline above the data window"); fprintf(stderr, "\n"); help_header("status line parameters"); format_help("-z", NULL, "do not show \"window closed\" pop-ups"); format_help("-x str", NULL, "show \"str\" in the xterm title bar"); format_help("-t x", NULL, "display 'x' in the window-title (when MultiTail runs in an xterm)"); format_help("-u", NULL, "set update interval (for slow links)"); fprintf(stderr, "\n"); help_header("input text handling"); format_help("-p x [y]", NULL, "set linewrap (l=left/a=all/r=right/s=syslog,S=syslog w/o procname,o=offset -> 'y',w=wordwrap)"); format_help("-P", NULL, "like -p but for all following files"); format_help("-b n", NULL, "set TAB-width"); format_help(NULL, "--cont", "reconnect lines with a '\' at the end"); fprintf(stderr, "\n"); help_header("line prefixes"); format_help(NULL, "--basename", "only display the filename (and not the path) in the statusline"); format_help(NULL, "--label x", "put in front of each line"); format_help(NULL, "--all-label x", "put in front of each line for all following windows"); format_help("-S prepend", NULL, "show subwindow number in merged output"); fprintf(stderr, "\n"); help_header("configuration file"); #ifndef S_SPLINT_S format_help("-F file", NULL, "use 'file' as configuration file (instead of " CONFIG_FILE ")"); format_help(NULL, "--no-load-global-config", "do not read " CONFIG_FILE ""); #endif format_help("-o config_file_parameter", NULL, "do a setting which would normally be set in the configuration file"); fprintf(stderr, "\n"); help_header("monitoring"); format_help("-H x", NULL, "show heartbeat (to keep your sessions alive)"); format_help(NULL, "--beep-interval x", "beep every x lines processed"); format_help(NULL, "--bi x", "like '--beep-interval' but only for current (sub-)window"); format_help(NULL, "--closeidle x", "close windows when more then 'x' seconds no new data was processed"); fprintf(stderr, "\n"); help_header("miscellaneous"); format_help("-V", NULL, "show version and exit"); format_help("-h", NULL, "this help"); fprintf(stderr, "\n"); fprintf(stderr, "You can have multiple regular expressions per file/command. Be warned: if\n"); fprintf(stderr, "you define multiple and one of them is specified with '-E' (=for every\n"); fprintf(stderr, "following file), _all_ of the current regular expressions are for all\n"); fprintf(stderr, "following files!\n"); fprintf(stderr, "\n"); fprintf(stderr, "%s\n", F1); } multitail-7.1.5/help.h000066400000000000000000000073601465663511400146520ustar00rootroot00000000000000#define SWITCHES_COLUMN_WIDTH 16 typedef struct { int nr; char **text; } help_t; #define HELP_MAIN_MENU 0 #define HELP_ADD_FILE_OR_CMD 100 #define HELP_LIST_KEYBINDINGS 200 #define HELP_NEGATE_REGEXP 300 #define HELP_SWAP_WIN1 800 #define HELP_SWAP_WIN2 900 #define HELP_DELETE_WINDOW_DELETE_ALL_SUBWIN 1000 #define HELP_ASK_COLORS 1100 #define HELP_COLORS_FIELD_NR 1200 #define HELP_COLORS_FIELD_DELIMITER 1300 #define HELP_REGEXP_USAGE 1400 #define HELP_ADD_WINDOW_MERGE_SUBWIN 1500 #define HELP_ENTER_FILENAME_TO_MONITOR 1600 #define HELP_ENTER_CMD_TO_MONITOR 1700 #define HELP_ADD_WINDOW_REPEAT_INTERVAL 1800 #define HELP_FAILED_TO_START_TAIL 1900 #define HELP_MERGE_ANOTHER_WINDOW 2000 #define HELP_REGEXP_MENU 2100 #define HELP_ENTER_REGEXP 2200 #define HELP_ENTER_CMD 2300 #define HELP_COMPILE_REGEXP_FAILED 2400 #define HELP_SET_BUFFERING 2600 #define HELP_SET_BUFFERING_STORE_WHAT 2700 #define HELP_ENTER_NUMBER_OF_LINES_TO_STORE 2800 #define HELP_WINDOW_CLOSED 2900 #define HELP_WRITE_SCRIPT 3000 #define HELP_STATISTICS 3100 #define HELP_STATISTICS_POPUP 3150 #define HELP_SET_WINDOWSIZES 3200 #define HELP_SET_WINDOW_WIDTH 3300 #define HELP_SET_WINDOW_HEIGHT 3400 #define HELP_ASK_COLORS_SELECT_SCHEME 3500 #define HELP_DELETE_SELECT_WINDOW 3600 #define HELP_DELETE_SELECT_SUBWINDOW 3700 #define HELP_HIDE_WINDOW 3800 #define HELP_ADD_WINDOW_SELECT_MERGE_WINDOW 3900 #define HELP_TOGGLE_COLORS_SELECT_WINDOW 4000 #define HELP_TOGGLE_COLORS_SELECT_SUBWINDOW 4100 #define HELP_ENTER_REGEXP_SELECT_WINDOW 4200 #define HELP_ENTER_STRIPPER_SELECT_WINDOW 4250 #define HELP_ENTER_REGEXP_SELECT_SUBWINDOW 4300 #define HELP_ENTER_STRIPPER_SELECT_SUBWINDOW 4350 #define HELP_SET_BUFFERING_SELECT_WINDOW 4400 #define HELP_SET_WINDOW_HEIGHT_SELECT_WINDOW 4500 #define HELP_TERMINAL_MODE_SELECT_WINDOW 4600 #define HELP_SCROLLBACK_SAVEFILE 4700 #define HELP_SCROLLBACK_NO_MARK 4800 #define HELP_SCROLLBACK_HELP 4900 #define HELP_SCROLLBACK_SAVEFILE_ENTER_FILENAME 5100 #define HELP_SCROLLBACK_SELECT_WINDOW 5200 #define HELP_SCROLLBACK_EDIT_SEARCH_STRING 5300 #define HELP_ADD_FILE_FOLLOW_FILENAME 5500 #define HELP_ADD_FILE_DISPLAY_DIFF 5600 #define HELP_PAUSE_A_WINDOW 5700 #define HELP_ASK_COLORS_SELECT_COLOR 5800 #define HELP_SET_VERTICAL_SPLIT_N_WIN 5900 #define HELP_MANAGE_COLS 6000 #define HELP_WIPE_WINDOW 6100 #define HELP_SELECT_COLORSCHEMES 6200 #define HELP_SET_LINEWRAP 6300 #define HELP_REDIRECT_FAILED 6400 #define HELP_STRIPPER_TYPE 6500 #define HELP_STRIPPER_START_OFFSET 6600 #define HELP_STRIPPER_END_OFFSET 6700 #define HELP_STRIPPER_DELIMITER 6800 #define HELP_STRIPPER_COL_NR 6900 #define HELP_ERROR_WRITE_SCRIPT_CREATE_FILE 7000 #define HELP_COLUMN_WIDTH 7100 #define HELP_N_WIN_PER_COL 7200 #define HELP_SEND_SIGNAL_SELECT_WINDOW 7300 #define HELP_SEND_SIGNAL_WINDOW_SEND_TO_ALL_SUBWIN 7400 #define HELP_SEND_SIGNAL_SELECT_SUBWINDOW 7500 #define HELP_SEND_SIGNAL_FAILED 7600 #define HELP_SELECT_SIGNAL 7700 #define HELP_SCREENDUMP_SELECT_FILE 7800 #define HELP_SCREENDUMP_SELECT_WIN 7900 #define HELP_TRUNCATE_FILE_SELECT_WINDOW 8000 #define HELP_TRUNCATE_FILE_SELECT_SUBWINDOW 8100 #define HELP_TRUNCATE_AREYOUSURE 8200 #define HELP_TRUNCATE_FAILED 8300 #define HELP_TRUNCATE_ONLY_LOGFILES 8400 #define HELP_SELECT_COLOR_AND_ATTRIBUTES 8500 #define HELP_CANNOT_EDIT_COLOR 8600 #define HELP_EDIT_COLOR_EDIT 8700 #define HELP_EDIT_COLOR 8800 #define HELP_EDIT_COLOR_CHANGE_NAME 8900 #define HELP_HIDE_BUT_WINDOW 9000 #define HELP_SEARCH_IN_ALL_WINDOWS 9100 #define HELP_HIGHLIGHT_IN_ALL_WINDOWS 9200 #define HELP_SELECT_CONVERSIONSCHEMES 9300 #define HELP_SELECT_RESTART_WINDOW 9400 #define HELP_SELECT_RESTART_WINDOW_ALL 9500 #define HELP_SELECT_TERMINAL_EMULATION 9600 #define HELP_CLEAR_BUFFER 9700 #define HELP_HISTORY 9800 void show_help(int what_help); void usage(void); multitail-7.1.5/history.c000066400000000000000000000060221465663511400154100ustar00rootroot00000000000000#define _LARGEFILE64_SOURCE #include #include #include #include #include #include #include #include #include #include "mt.h" #include "error.h" #include "utils.h" #include "mem.h" #include "selbox.h" #include "ui.h" #include "help.h" #include "globals.h" void init_history(history_t *ph) { } void load_history(history_t *ph) { if (ph -> history == NULL) { int array_in_bytes = sizeof(char *) * ph -> history_size; ph -> history = (char **)mymalloc(array_in_bytes); memset(ph -> history, 0x00, array_in_bytes); if (file_exist(ph -> history_file) == 0) { int loop; FILE *fh = fopen(ph -> history_file, "r"); if (!fh) error_popup("Load history", -1, "Failed to open history file %s.\n", ph -> history_file); else { for(loop=0; loop history_size; loop++) { char *lf; char buffer[HISTORY_IO_BUFFER]; if (!fgets(buffer, sizeof(buffer), fh)) break; lf = strchr(buffer, '\n'); if (lf) *lf = 0x00; (ph -> history)[loop] = mystrdup(buffer); } fclose(fh); } } } } int history_find_null_entry(history_t *ph) { int loop; for(loop=0; loop history_size; loop++) { if (!(ph -> history)[loop]) { return loop; } } return -1; } void save_history(history_t *ph) { int loop; int last_entry = history_find_null_entry(ph); int n_entries = last_entry == -1 ? ph -> history_size : last_entry; FILE *fh = fopen(ph -> history_file, "w+"); if (!fh) { error_popup("Error saving history", -1, "Error creating/opening file %s: %s", ph -> history_file, strerror(errno)); return; } for(loop=0; loop history)[loop]); } fclose(fh); } void history_add(history_t *ph, char *string) { if (ph -> history_size > 0) { int loop; int found = -1; load_history(ph); /* bail out if this string is already in the history */ for(loop=0; loop history_size; loop++) { if ((ph -> history)[loop] != NULL && strcmp((ph -> history)[loop], string) == 0) return ; } /* find free spot */ found = history_find_null_entry(ph); /* when no free spot was found, free-up an entry */ if (found == -1) { myfree((ph -> history)[ph -> history_size - 1]); memmove(&(ph -> history)[1], &(ph -> history)[0], sizeof(char *) * (ph -> history_size - 1)); found = 0; } (ph -> history)[found] = mystrdup(string); save_history(ph); } } char * search_history(history_t *ph, char *search_string) { if (ph -> history_size > 0) { int n_entries, free_entry; load_history(ph); free_entry = history_find_null_entry(ph); n_entries = free_entry == -1 ? ph -> history_size : free_entry; if (n_entries > 0) { int sel_index = selection_box((void **)ph -> history, NULL, n_entries, SEL_HISTORY, HELP_HISTORY, NULL); if (sel_index >= 0) return mystrdup((ph -> history)[sel_index]); } else error_popup("Search history", -1, "The history list is empty."); } return NULL; } multitail-7.1.5/history.h000066400000000000000000000002141465663511400154120ustar00rootroot00000000000000void init_history(history_t *ph); void history_add(history_t *ph, char *string); char * search_history(history_t *ph, char *search_string); multitail-7.1.5/makefile.aix000066400000000000000000000033471465663511400160320ustar00rootroot00000000000000include version CONFIG_FILE=$(DESTDIR)/etc/multitail.conf DEBUG=#-g -D_DEBUG #-pg #-fprofile-arcs LDFLAGS=-L/usr/local/lib/ -lpanel -lncurses -lm $(DEBUG) CFLAGS=-I/usr/local/include/ -fsigned-char -DAIX -O2 -DVERSION=\"$(VERSION)\" $(DEBUG) -DCONFIG_FILE=\"$(CONFIG_FILE)\" OBJS=utils.o mt.o error.o my_pty.o term.o scrollback.o help.o mem.o cv.o selbox.o stripstring.o color.o misc.o ui.o exec.o diff.o config.o cmdline.o globals.o history.o clipboard.o all: multitail multitail: $(OBJS) $(CC) $(OBJS) $(LDFLAGS) -o multitail install: multitail cp multitail $(DESTDIR)/usr/bin cp multitail.1 $(DESTDIR)/usr/share/man/man1/multitail.1 # ### COPIED multitail.conf.new, YOU NEED TO REPLACE THE multitail.conf ### YOURSELF WITH THE NEW FILE # cp multitail.conf $(DESTDIR)/etc/multitail.conf.new mkdir -p $(DESTDIR)/usr/share/doc/multitail-$(VERSION) cp *.txt INSTALL manual*.html $(DESTDIR)/usr/share/doc/multitail-$(VERSION) # # +-=-------------------------------------------------------------=-+ # | There's a mailinglist! | # | Send an e-mail to minimalist@vanheusden.com with in the subject | # | 'subscribe multitail' to subscribe. | # +-=-------------------------------------------------------------=-+ # # you might want to run 'make thanks' now :-) # # http://www.vanheusden.com/wishlist.php uninstall: clean rm -f $(DESTDIR)/usr/bin/multitail rm -f $(DESTDIR)/usr/share/man/man1/multitail.1.gz rm -f $(DESTDIR)/etc/multitail.conf rm -rf $(DESTDIR)/usr/share/doc/multitail-$(VERSION) clean: rm -f $(OBJS) multitail core thanks: echo Automatic thank you e-mail for multitail $(VERSION) on a `uname -a` | mail -s "multitail $(VERSION)" folkert@vanheusden.com multitail-7.1.5/makefile.cross-arm-linux000066400000000000000000000022051465663511400203040ustar00rootroot00000000000000include version CONFIG_FILE=$(DESTDIR)/etc/multitail.conf DEBUG=#-g -D_DEBUG #-pg #-fprofile-arcs LDFLAGS=-lpanel -lncurses -lutil -lm $(DEBUG) CFLAGS=-D$(shell uname) -O2 -Wall -DVERSION=\"$(VERSION)\" $(DEBUG) -DCONFIG_FILE=\"$(CONFIG_FILE)\" OBJS=utils.o mt.o error.o my_pty.o term.o scrollback.o help.o mem.o cv.o selbox.o stripstring.o color.o misc.o ui.o exec.o diff.o config.o cmdline.o globals.o history.o clipboard.o all: multitail multitail: $(OBJS) $(CC) -Wall -W $(OBJS) $(LDFLAGS) -o multitail # # +-=-------------------------------------------------------------=-+ # | There's a mailinglist! | # | Send an e-mail to minimalist@vanheusden.com with in the subject | # | 'subscribe multitail' to subscribe. | # +-=-------------------------------------------------------------=-+ # # you might want to run 'make thanks' now :-) # # http://www.vanheusden.com/wishlist.php clean: rm -f $(OBJS) multitail core gmon.out *.da thanks: echo Automatic thank you e-mail for multitail $(VERSION) on a `uname -a` | mail -s "multitail $(VERSION)" folkert@vanheusden.com multitail-7.1.5/makefile.cygwin000066400000000000000000000036371465663511400165530ustar00rootroot00000000000000include version CONFIG_FILE=/etc/multitail.conf DEBUG=#-g -D_DEBUG -W -pedantic #-pg #-fprofile-arcs LDFLAGS=-lpanel -lncurses -lm $(DEBUG) CFLAGS=-D__CYGWIN__ -O2 -Wall -DVERSION=\"$(VERSION)\" $(DEBUG) -DCONFIG_FILE=\"$(CONFIG_FILE)\" OBJS=utils.o mt.o error.o my_pty.o term.o scrollback.o help.o mem.o cv.o selbox.o stripstring.o color.o misc.o ui.o exec.o diff.o config.o cmdline.o globals.o history.o clipboard.o all: multitail multitail: $(OBJS) $(CC) -Wall -W $(OBJS) $(LDFLAGS) -o multitail install: multitail mkdir -p $(DESTDIR)/usr/bin mkdir -p $(DESTDIR)/usr/share/man/man1 cp multitail $(DESTDIR)/usr/bin cp multitail.1 $(DESTDIR)/usr/share/man/man1/multitail.1 # ### COPIED multitail.conf.new, YOU NEED TO REPLACE THE multitail.conf ### YOURSELF WITH THE NEW FILE # mkdir -p $(DESTDIR)/etc mkdir -p $(DESTDIR)/usr/share/doc/multitail-$(VERSION) cp *.txt INSTALL manual*.html $(DESTDIR)/usr/share/doc/multitail-$(VERSION) cp multitail.conf $(DESTDIR)/etc/multitail.conf.new rm -f $(DESTDIR)/usr/share/man/man1/multitail.1.gz gzip -9 $(DESTDIR)/usr/share/man/man1/multitail.1 # # +-=-------------------------------------------------------------=-+ # | There's a mailinglist! | # | Send an e-mail to minimalist@vanheusden.com with in the subject | # | 'subscribe multitail' to subscribe. | # +-=-------------------------------------------------------------=-+ # # you might want to run 'make thanks' now :-) # # http://www.vanheusden.com/wishlist.php uninstall: clean rm -f $(DESTDIR)/usr/bin/multitail rm -f $(DESTDIR)/usr/share/man/man1/multitail.1.gz rm -f $(DESTDIR)/etc/multitail.conf rm -rf $(DESTDIR)/usr/share/doc/multitail-$(VERSION) clean: rm -f $(OBJS) multitail core gmon.out *.da thanks: echo Automatic thank you e-mail for multitail $(VERSION) on a `uname -a` | email -s "multitail $(VERSION)" folkert@vanheusden.com multitail-7.1.5/makefile.hpux000066400000000000000000000033051465663511400162270ustar00rootroot00000000000000include version CONFIG_FILE=$(DESTDIR)/etc/multitail.conf LDFLAGS=-L/usr/local/lib/ -lpanel -lncurses -lm CFLAGS=-Ae -I/usr/local/include/ -O -DVERSION=\"$(VERSION)\" -DCONFIG_FILE=\"$(CONFIG_FILE)\" OBJS=utils.o mt.o error.o my_pty.o term.o scrollback.o help.o mem.o cv.o selbox.o stripstring.o color.o misc.o ui.o exec.o diff.o config.o cmdline.o globals.o history.o clipboard.o all: multitail multitail: $(OBJS) $(CC) $(OBJS) $(LDFLAGS) -o multitail install: multitail cp multitail $(DESTDIR)/usr/bin cp multitail.1 $(DESTDIR)/usr/man/man1/multitail.1 # ### COPIED multitail.conf.new, YOU NEED TO REPLACE THE multitail.conf ### YOURSELF WITH THE NEW FILE # cp multitail.conf $(DESTDIR)/etc/multitail.conf.new mkdir -p $(DESTDIR)/usr/share/doc/multitail-$(VERSION) cp *.txt INSTALL manual.html $(DESTDIR)/usr/share/doc/multitail-$(VERSION) rm -f $(DESTDIR)/usr/man/man1/multitail.1.gz gzip -9 $(DESTDIR)/usr/man/man1/multitail.1 # # +-=-------------------------------------------------------------=-+ # | There's a mailinglist! | # | Send an e-mail to minimalist@vanheusden.com with in the subject | # | 'subscribe multitail' to subscribe. | # +-=-------------------------------------------------------------=-+ # # you might want to run 'make thanks' now :-) # # http://www.vanheusden.com/wishlist.php uninstall: clean rm -f $(DESTDIR)/usr/bin/multitail rm -f $(DESTDIR)/usr/man/man1/multitail.1.gz rm -rf $(DESTDIR)/usr/share/doc/multitail-$(VERSION) clean: rm -f $(OBJS) multitail core thanks: echo Automatic thank you e-mail for multitail $(VERSION) on a `uname -a` | mail -s "multitail $(VERSION)" folkert@vanheusden.com multitail-7.1.5/makefile.icc000066400000000000000000000010321465663511400157740ustar00rootroot00000000000000include version CONFIG_FILE=$(DESTDIR)/etc/multitail.conf CC=icc DEBUG=-g # -D_DEBUG LDFLAGS=-lpanel -lncurses -lutil -lm $(DEBUG) CFLAGS+=-D$(shell uname) -O2 -DVERSION=\"$(VERSION)\" $(DEBUG) -DCONFIG_FILE=\"$(CONFIG_FILE)\" OBJS=utils.o mt.o error.o my_pty.o term.o scrollback.o help.o mem.o cv.o selbox.o stripstring.o color.o misc.o ui.o exec.o diff.o config.o cmdline.o globals.o history.o clipboard.o all: multitail multitail: $(OBJS) $(CC) $(OBJS) $(LDFLAGS) -o multitail clean: rm -f $(OBJS) multitail core gmon.out *.da multitail-7.1.5/makefile.irix000066400000000000000000000033271465663511400162220ustar00rootroot00000000000000include version CONFIG_FILE=$(DESTDIR)/etc/multitail.conf DEBUG=#-g -D_DEBUG #-pg #-fprofile-arcs LDFLAGS=-lpanel -lncurses -lm $(DEBUG) CFLAGS=-I/usr/freeware/include/ncurses/ -DIRIX -DVERSION=\"$(VERSION)\" -signed $(DEBUG) -DCONFIG_FILE=\"$(CONFIG_FILE)\" OBJS=utils.o mt.o error.o my_pty.o term.o scrollback.o help.o mem.o cv.o selbox.o stripstring.o color.o misc.o ui.o exec.o diff.o config.o cmdline.o globals.o history.o clipboard.o all: multitail multitail: $(OBJS) $(CC) $(OBJS) $(LDFLAGS) -o multitail install: multitail cp multitail $(DESTDIR)/usr/bin cp multitail.1 $(DESTDIR)/usr/share/man/man1/multitail.1 # ### COPIED multitail.conf.new, YOU NEED TO REPLACE THE multitail.conf ### YOURSELF WITH THE NEW FILE # cp multitail.conf $(DESTDIR)/etc/multitail.conf.new mkdir -p $(DESTDIR)/usr/share/doc/multitail-$(VERSION) cp *.txt INSTALL manual*.html $(DESTDIR)/usr/share/doc/multitail-$(VERSION) # # +-=-------------------------------------------------------------=-+ # | There's a mailinglist! | # | Send an e-mail to minimalist@vanheusden.com with in the subject | # | 'subscribe multitail' to subscribe. | # +-=-------------------------------------------------------------=-+ # # you might want to run 'make thanks' now :-) # # http://www.vanheusden.com/wishlist.php uninstall: clean rm -f $(DESTDIR)/usr/bin/multitail rm -f $(DESTDIR)/usr/share/man/man1/multitail.1.gz rm -f $(DESTDIR)/etc/multitail.conf rm -rf $(DESTDIR)/usr/share/doc/multitail-$(VERSION) clean: rm -f $(OBJS) multitail core thanks: echo Automatic thank you e-mail for multitail $(VERSION) on a `uname -a` | mail -s "multitail $(VERSION)" folkert@vanheusden.com multitail-7.1.5/makefile.macosx000066400000000000000000000044171465663511400165420ustar00rootroot00000000000000include version CONFIG_FILE=$(DESTDIR)/usr/local/etc/multitail.conf DEBUG:=#XXX -g -D_DEBUG #-pg -Wpedantic # -pg #-fprofile-arcs LDFLAGS=-lpanel -lm $(DEBUG) CPPFLAGS:=$(shell pkg-config --cflags ncurses) NCURSES_LIB:=$(shell pkg-config --libs ncurses) # pkg-config --libs --cflags ncurses # -D_DARWIN_C_SOURCE -I/opt/local/include -L/opt/local/lib -lncurses PLATFORM:=$(shell uname) CFLAGS=-O2 -Wall -Wextra -std=c99 -D$(PLATFORM) -DVERSION=\"$(VERSION)\" $(DEBUG) -DCONFIG_FILE=\"$(CONFIG_FILE)\" OBJS=utils.o mt.o error.o my_pty.o term.o scrollback.o help.o mem.o cv.o selbox.o stripstring.o color.o misc.o ui.o exec.o diff.o config.o cmdline.o globals.o history.o clipboard.o all: multitail multitail: $(OBJS) $(CC) -Wall -W $(OBJS) $(NCURSES_LIB) $(LDFLAGS) -o multitail install: multitail cp multitail $(DESTDIR)/usr/local/bin cp multitail.1 $(DESTDIR)/usr/local/share/man/man1/multitail.1 # ### COPIED multitail.conf.new, YOU NEED TO REPLACE THE multitail.conf ### YOURSELF WITH THE NEW FILE # cp multitail.conf $(DESTDIR)/usr/local/etc/multitail.conf.new mkdir -p $(DESTDIR)/usr/local/share/doc/multitail-$(VERSION) cp *.txt INSTALL manual.html $(DESTDIR)/usr/local/share/doc/multitail-$(VERSION) # # +-=-------------------------------------------------------------=-+ # | There's a mailinglist! | # | Send an e-mail to minimalist@vanheusden.com with in the subject | # | 'subscribe multitail' to subscribe. | # +-=-------------------------------------------------------------=-+ # # you might want to run 'make thanks' now :-) # # http://www.vanheusden.com/wishlist.php uninstall: clean rm -f $(DESTDIR)/usr/local/bin/multitail rm -f $(DESTDIR)/usr/local/share/man/man1/multitail.1 rm -rf $(DESTDIR)/usr/local/share/doc/multitail-$(VERSION) clean: rm -f $(OBJS) multitail core package: clean # source package rm -rf multitail-$(VERSION)* mkdir multitail-$(VERSION) cp *.c *.h multitail.1 manual.html Makefile makefile.* Changes INSTALL license.txt readme.txt multitail-$(VERSION) tar czf multitail-$(VERSION).tgz multitail-$(VERSION) rm -rf multitail-$(VERSION) thanks: echo Automatic thank you e-mail for multitail $(VERSION) on a `uname -a` | mail -s "multitail $(VERSION)" folkert@vanheusden.com multitail-7.1.5/makefile.minix000066400000000000000000000054701465663511400163740ustar00rootroot00000000000000include version DESTDIR=/ CONFIG_FILE=$(DESTDIR)/etc/multitail.conf DEBUG=-g LDFLAGS+=-L/usr/pkg/lib -lpanel -lncurses -lutil -lm $(DEBUG) -rdynamic CFLAGS+=-funsigned-char -D`uname` -O2 -Wall -DVERSION=\"$(VERSION)\" $(DEBUG) -DCONFIG_FILE=\"$(CONFIG_FILE)\" -I/usr/pkg/include OBJS=utils.o mt.o error.o my_pty.o term.o scrollback.o help.o mem.o cv.o selbox.o stripstring.o color.o misc.o ui.o exec.o diff.o config.o cmdline.o globals.o history.o clipboard.o all: multitail multitail: $(OBJS) $(CC) -Wall -W $(OBJS) $(LDFLAGS) -o multitail multitail_ccmalloc: $(OBJS) ccmalloc --no-wrapper $(CC) -Wall -W $(OBJS) $(LDFLAGS) -o ccmultitail install: multitail cp multitail $(DESTDIR)/usr/bin cp multitail.1 $(DESTDIR)/usr/share/man/man1/multitail.1 mkdir -p $(DESTDIR)/usr/share/doc/multitail-$(VERSION) cp *.txt INSTALL manual*.html $(DESTDIR)/usr/share/doc/multitail-$(VERSION) # ### COPIED multitail.conf.new, YOU NEED TO REPLACE THE multitail.conf ### YOURSELF WITH THE NEW FILE # cp multitail.conf $(CONFIG_FILE).new mkdir -p $(DESTDIR)/etc/multitail/ cp convert-* colors-* $(DESTDIR)/etc/multitail/ rm -f $(DESTDIR)/usr/share/man/man1/multitail.1.gz gzip -9 $(DESTDIR)/usr/share/man/man1/multitail.1 # # There's a mailinglist! # Send an e-mail to minimalist@vanheusden.com with in the subject # 'subscribe multitail' to subscribe. # # you might want to run 'make thanks' now :-) # http://www.vanheusden.com/wishlist.php # # How do YOU use multitail? Please send me an e-mail so that I can # update the examples page. uninstall: clean rm -f $(DESTDIR)/usr/bin/multitail rm -f $(DESTDIR)/usr/share/man/man1/multitail.1.gz rm -f $(CONFIG_FILE) rm -rf $(DESTDIR)/usr/share/doc/multitail-$(VERSION) clean: rm -f $(OBJS) multitail core gmon.out *.da ccmultitail package: clean # source package rm -rf multitail-$(VERSION)* mkdir multitail-$(VERSION) cp conversion-scripts/* *.conf *.c *.h multitail.1 manual*.html Makefile makefile.* Changes INSTALL license.txt readme.txt thanks.txt version multitail-$(VERSION) tar czf multitail-$(VERSION).tgz multitail-$(VERSION) rm -rf multitail-$(VERSION) thanks: echo Automatic thank you e-mail for multitail $(VERSION) on a `uname -a` | mail -s "multitail $(VERSION)" folkert@vanheusden.com echo Is your company using MultiTail and you would like to be echo mentioned on http://www.vanheusden.com/multitail/usedby.html ? echo Then please send me a logo -not too big- and a link and I will echo add it to that page. echo echo Oh, blatant plug: http://keetweej.vanheusden.com/wishlist.html check: cppcheck -v --force -j 3 --enable=all --inconclusive -I. . 2> err.txt # make clean scan-build make coverity: make clean rm -rf cov-int CC=gcc cov-build --dir cov-int make all tar vczf ~/site/coverity/multitail.tgz README cov-int/ putsite -q /home/folkert/.coverity-mt.sh multitail-7.1.5/makefile.sco-openserver6000066400000000000000000000047111465663511400203050ustar00rootroot00000000000000include version CONFIG_FILE=$(DESTDIR)/etc/multitail.conf DEBUG=-g # -D_DEBUG # -pedantic #-pg #-fprofile-arcs LDFLAGS=-L/usr/local/lib/ -lpanel -lncurses -lm -lsocket $(DEBUG) CFLAGS=-Dscoos -O2 -I/usr/local/include/ -DVERSION=\"$(VERSION)\" $(DEBUG) -DCONFIG_FILE=\"$(CONFIG_FILE)\" OBJS=utils.o mt.o error.o my_pty.o term.o scrollback.o help.o mem.o cv.o selbox.o stripstring.o color.o misc.o ui.o exec.o diff.o config.o cmdline.o globals.o history.o clipboard.o all: multitail multitail: $(OBJS) $(CC) $(OBJS) $(LDFLAGS) -o multitail install: multitail cp multitail $(DESTDIR)/usr/bin cp multitail.1 $(DESTDIR)/usr/share/man/man1/multitail.1 mkdir -p $(DESTDIR)/usr/share/doc/multitail-$(VERSION) cp *.txt INSTALL manual*.html $(DESTDIR)/usr/share/doc/multitail-$(VERSION) # ### COPIED multitail.conf.new, YOU NEED TO REPLACE THE multitail.conf ### YOURSELF WITH THE NEW FILE # cp multitail.conf $(DESTDIR)/etc/multitail.conf.new rm -f $(DESTDIR)/usr/share/man/man1/multitail.1.gz gzip -9 $(DESTDIR)/usr/share/man/man1/multitail.1 # # +-=-------------------------------------------------------------=-+ # | There's a mailinglist! | # | Send an e-mail to minimalist@vanheusden.com with in the subject | # | 'subscribe multitail' to subscribe. | # +-=-------------------------------------------------------------=-+ # # you might want to run 'make thanks' now :-) # # http://www.vanheusden.com/wishlist.php uninstall: clean rm -f $(DESTDIR)/usr/bin/multitail rm -f $(DESTDIR)/usr/share/man/man1/multitail.1.gz rm -f $(DESTDIR)/etc/multitail.conf rm -rf $(DESTDIR)/usr/share/doc/multitail-$(VERSION) clean: rm -f $(OBJS) multitail core gmon.out *.da package: clean # source package rm -rf multitail-$(VERSION)* mkdir multitail-$(VERSION) cp *.conf *.c *.h multitail.1 manual*.html Makefile makefile.* Changes INSTALL license.txt readme.txt version multitail-$(VERSION) tar czf multitail-$(VERSION).tgz multitail-$(VERSION) rm -rf multitail-$(VERSION) thanks: echo Automatic thank you e-mail for multitail $(VERSION) on a `uname -a` | mail -s "multitail $(VERSION)" folkert@vanheusden.com echo Is your company using MultiTail and you would like to be echo mentioned on http://www.vanheusden.com/multitail/usedby.html ? echo Then please send me a logo -not too big- and a link and I will echo add it to that page. echo echo Oh, blatant plug: http://keetweej.vanheusden.com/wishlist.html multitail-7.1.5/makefile.solaris_gcc000066400000000000000000000052601465663511400175350ustar00rootroot00000000000000include version DESTDIR=/ CONFIG_FILE=$(DESTDIR)/etc/multitail.conf CC=gcc DEBUG=#-g -D_DEBUG #-pg #-fprofile-arcs CFLAGS=${EXTRA_CFLAGS} -O2 -I/usr/local/include/ -DVERSION=\"$(VERSION)\" $(DEBUG) -DCONFIG_FILE=\"$(CONFIG_FILE)\" LDFLAGS=${EXTRA_LDFLAGS} -L/usr/local/lib/ -lsocket -lpanel -lncurses -lnsl -lm $(DEBUG) OBJS=utils.o mt.o error.o my_pty.o term.o scrollback.o help.o mem.o cv.o color.o stripstring.o selbox.o misc.o ui.o exec.o diff.o config.o cmdline.o globals.o history.o clipboard.o all: multitail multitail: $(OBJS) $(CC) $(OBJS) $(LDFLAGS) -o multitail install: multitail /usr/sbin/install -m 0755 -u root -g sys -d $(DESTDIR)/usr /usr/sbin/install -m 0755 -u root -g bin -d $(DESTDIR)/usr/bin cp multitail $(DESTDIR)/usr/bin /usr/sbin/install -m 0755 -u root -g sys -d $(DESTDIR)/usr/share /usr/sbin/install -m 0755 -u root -g bin -d $(DESTDIR)/usr/share/man /usr/sbin/install -m 0755 -u root -g bin -d $(DESTDIR)/usr/share/man/man1 cp multitail.1 $(DESTDIR)/usr/share/man/man1/multitail.1 # ### COPIED multitail.conf.new, YOU NEED TO REPLACE THE multitail.conf ### YOURSELF WITH THE NEW FILE # /usr/sbin/install -m 0755 -u root -g sys -d $(DESTDIR)/etc cp multitail.conf $(DESTDIR)/etc/multitail.conf.new /usr/sbin/install -m 0755 -u root -g other -d $(DESTDIR)/usr/share/doc /usr/sbin/install -m 0755 -u root -g other -d $(DESTDIR)/usr/share/doc/multitail-$(VERSION) #mkdir -p $(DESTDIR)/usr/share/doc/multitail-$(VERSION) cp *.txt INSTALL manual.html $(DESTDIR)/usr/share/doc/multitail-$(VERSION) # # +-=-------------------------------------------------------------=-+ # | There's a mailinglist! | # | Send an e-mail to minimalist@vanheusden.com with in the subject | # | 'subscribe multitail' to subscribe. | # +-=-------------------------------------------------------------=-+ # # you might want to run 'make thanks' now :-) # # http://www.vanheusden.com/wishlist.php solarisbinpackage: multitail rm -rf usr etc mkdir -p etc cp multitail.conf etc mkdir -p usr/bin mkdir -p usr/share/man/man1 mkdir -p usr/share/doc/multitail-$(VERSION) cp multitail usr/bin cp multitail.1 usr/share/man/man1 cp *.txt INSTALL manual.html usr/share/doc/multitail-$(VERSION) tar cvf multitail-$(VERSION)-solaris.tar usr etc rm -rf usr etc gzip -9 multitail-$(VERSION)-solaris.tar uninstall: clean rm -f $(DESTDIR)/usr/bin/multitail rm -f $(DESTDIR)/usr/man/man1/multitail.1.gz rm -rf $(DESTDIR)/usr/share/doc/multitail-$(VERSION) clean: rm -f $(OBJS) multitail core thanks: echo Automatic thank you e-mail for multitail $(VERSION) on a `uname -a` | mail -s "multitail $(VERSION)" folkert@vanheusden.com multitail-7.1.5/makefile.solaris_sunwspro000066400000000000000000000052641465663511400207050ustar00rootroot00000000000000include version DESTDIR=/ CONFIG_FILE=$(DESTDIR)/etc/multitail.conf DEBUG=#-g -D_DEBUG #-pg #-fprofile-arcs CFLAGS=${EXTRA_CFLAGS} -O2 -I/usr/local/include/ -D_STDC_C99 -DVERSION=\"$(VERSION)\" $(DEBUG) -DCONFIG_FILE=\"$(CONFIG_FILE)\" LDFLAGS=${EXTRA_LDFLAGS} -L/usr/local/lib/ -lsocket -lpanel -lncurses -lnsl -lm $(DEBUG) OBJS=utils.o mt.o error.o my_pty.o term.o scrollback.o help.o mem.o cv.o color.o stripstring.o selbox.o misc.o ui.o exec.o diff.o config.o cmdline.o globals.o history.o clipboard.o all: multitail multitail: $(OBJS) $(CC) $(OBJS) $(LDFLAGS) -o multitail install: multitail /usr/sbin/install -m 0755 -u root -g sys -d $(DESTDIR)/usr /usr/sbin/install -m 0755 -u root -g bin -d $(DESTDIR)/usr/bin cp multitail $(DESTDIR)/usr/bin /usr/sbin/install -m 0755 -u root -g sys -d $(DESTDIR)/usr/share /usr/sbin/install -m 0755 -u root -g bin -d $(DESTDIR)/usr/share/man /usr/sbin/install -m 0755 -u root -g bin -d $(DESTDIR)/usr/share/man/man1 cp multitail.1 $(DESTDIR)/usr/share/man/man1/multitail.1 # ### COPIED multitail.conf.new, YOU NEED TO REPLACE THE multitail.conf ### YOURSELF WITH THE NEW FILE # /usr/sbin/install -m 0755 -u root -g sys -d $(DESTDIR)/etc cp multitail.conf $(DESTDIR)/etc/multitail.conf.new /usr/sbin/install -m 0755 -u root -g other -d $(DESTDIR)/usr/share/doc /usr/sbin/install -m 0755 -u root -g other -d $(DESTDIR)/usr/share/doc/multitail-$(VERSION) mkdir -p $(DESTDIR)/usr/share/doc/multitail-$(VERSION) cp *.txt INSTALL manual.html $(DESTDIR)/usr/share/doc/multitail-$(VERSION) # # +-=-------------------------------------------------------------=-+ # | There's a mailinglist! | # | Send an e-mail to minimalist@vanheusden.com with in the subject | # | 'subscribe multitail' to subscribe. | # +-=-------------------------------------------------------------=-+ # # you might want to run 'make thanks' now :-) # # http://www.vanheusden.com/wishlist.php solarisbinpackage: multitail rm -rf usr etc mkdir -p etc cp multitail.conf etc mkdir -p usr/bin mkdir -p usr/share/man/man1 mkdir -p usr/share/doc/multitail-$(VERSION) cp multitail usr/bin cp multitail.1 usr/share/man/man1 cp *.txt INSTALL manual.html usr/share/doc/multitail-$(VERSION) tar cvf multitail-$(VERSION)-solaris.tar usr etc rm -rf usr etc gzip -9 multitail-$(VERSION)-solaris.tar uninstall: clean rm -f $(DESTDIR)/usr/bin/multitail rm -f $(DESTDIR)/usr/man/man1/multitail.1.gz rm -rf $(DESTDIR)/usr/share/doc/multitail-$(VERSION) clean: rm -f $(OBJS) multitail core thanks: echo Automatic thank you e-mail for multitail $(VERSION) on a `uname -a` | mail -s "multitail $(VERSION)" folkert@vanheusden.com multitail-7.1.5/makefile.tru64000066400000000000000000000032531465663511400162310ustar00rootroot00000000000000include version CONFIG_FILE=$(DESTDIR)/etc/multitail.conf LDFLAGS=-lutil -lm -lpanel -lncurses CFLAGS=-Ae -DOSF1 -O -DVERSION=\"$(VERSION)\" -DCONFIG_FILE=\"$(CONFIG_FILE)\" OBJS=utils.o mt.o error.o my_pty.o term.o scrollback.o help.o mem.o cv.o selbox.o stripstring.o color.o misc.o ui.o exec.o diff.o config.o cmdline.o globals.o history.o clipboard.o all: multitail multitail: $(OBJS) $(CC) $(OBJS) $(LDFLAGS) -o multitail install: multitail cp multitail $(DESTDIR)/usr/bin cp multitail.1 $(DESTDIR)/usr/man/man1/multitail.1 # ### COPIED multitail.conf.new, YOU NEED TO REPLACE THE multitail.conf ### YOURSELF WITH THE NEW FILE # cp multitail.conf $(DESTDIR)/etc/multitail.conf.new mkdir -p $(DESTDIR)/usr/share/doc/multitail-$(VERSION) cp *.txt INSTALL manual.html $(DESTDIR)/usr/share/doc/multitail-$(VERSION) rm -f $(DESTDIR)/usr/man/man1/multitail.1.gz gzip -9 $(DESTDIR)/usr/man/man1/multitail.1 # # +-=-------------------------------------------------------------=-+ # | There's a mailinglist! | # | Send an e-mail to minimalist@vanheusden.com with in the subject | # | 'subscribe multitail' to subscribe. | # +-=-------------------------------------------------------------=-+ # # you might want to run 'make thanks' now :-) # # http://www.vanheusden.com/wishlist.php uninstall: clean rm -f $(DESTDIR)/usr/bin/multitail rm -f $(DESTDIR)/usr/man/man1/multitail.1.gz rm -rf $(DESTDIR)/usr/share/doc/multitail-$(VERSION) clean: rm -f $(OBJS) multitail core thanks: echo Automatic thank you e-mail for multitail $(VERSION) on a `uname -a` | mail -s "multitail $(VERSION)" folkert@vanheusden.com multitail-7.1.5/manual-nl.html000066400000000000000000000033731465663511400163230ustar00rootroot00000000000000 Een introductie tot MultiTail

Een introductie tot MultiTail

Introductie

Wat is MultiTail?

Met MultiTail kan men een of meerdere logfiles bekijken netzoals bij tail. Het verschil met het originele tail-programma is dat het meerdere vensters in het console-venster maakt (met ncurses) met in ieder venster een logfile. Verder kan het 2 of meer logfiles samenvoegen in 'n venster. Logfiles kunnen ook met kleur getoond worden (selectie m.b.h.v. reguliere expressies) om zo sneller te herkennen wat belangrijk is en wat niet. Logregels kunnen ook weggefiltert worden, wederom met reguliere expressies. Deze reguliere expressies kunnen via interactieve menu's toegevoegd en verwijderd worden. Ook extra vensters laten tonen kan zonder het programma te verlaten. Niet alleen logfiles kunnen bekeken worden, ook de uitvoer van shellscripts en programma's kunnen worden getoond. MultiTail kan daarbij de functionaliteit van watch nadoen.

Waarom deze tekst?

Als men MultiTail start zonder parameters, dan verschijnt er een leeg scherm met alleen een lijstje van de toetsen die men kan indrukken op dat moment of wanneer het programma loopt. Men kan er ook voor kiezen MultiTail te starten met de '-h' parameter, dat geeft een overzicht van alle mogelijke parameters. Het zijn geen kleine lijsten en dat kan wat intimiderend zijn. Ook is de help in het programma en in de man-page misschien niet voldoende, daarom is deze tekst geschreven. Mocht er dan toch nog wat onduidelijk zijn, schrijf me dan op het volgende e-mail adres:

De Basis

multitail-7.1.5/manual.html000066400000000000000000000251601465663511400157120ustar00rootroot00000000000000 An introduction to MultiTail

An introduction to MultiTail

Introduction

What is MultiTail?

MultiTail lets you view one or multiple files like the original tail program. The difference is that it creates multiple windows on your console (with ncurses). Merging of 2 or even more logfiles is possible. It can also use colors while displaying the logfiles (through regular expressions), for faster recognition of what is important and what not. It can also filter lines (again with regular expressions). It has interactive menus for editing given regular expressions and deleting and adding windows. One can also have windows with the output of shell scripts and other software. When viewing the output of external software, MultiTail can mimic the functionality of tools like 'watch' and such.

Why this text?

When you start multitail without any parameters, it clears the screen and shows a couple of keys you can press together with a short explanation what they do. You can then press any of those keys or 'x', 'q' or 'CTRL'+'C' to exit the program. If you like to know what commandlineparameters can be given, start multitail with the '-h' parameter.
The "help" that is given with the methods described above might not be sufficient, that's why this text was written. If there is still anything not clear after reading this, do not hesitate to contact me at the following e-mail address: folkert@vanheusden.com

The Basics

The most trivial use of MultiTail is as follows:
multitail [-i] file
This shows the file 'file' in your terminal-window. At the bottom, a line (the statusline) is displayed with the name of the file, its size and the date/time of when the last time the file changed. You can make this status- line static (not updated) with the '-d' commandline parameter. With '-D' no statusline is displayed at all.
You only need to specify the '-i' when the filename starts with a dash ('-'). Something a little more complex is this:
multitail [-i] file1 [-i] file2
This splits your terminalwindow in two windows. In each window, one file is displayed. Both windows have a statusline giving info. The window with 'file1' is displayed above the window with 'file2'. Instead of above each other, you can also have them displayed side by side. For that, you can enter the parameter '-s' on the commandline or press the 'v' key while the program is running.
Press keys 0 to 9 for adding a thick red line at the current position to window 0 to 9.

Scrolling

Of course you can scroll in the shown files. For that, press the 'b' key. When you're viewing multiple files, you'll first get a fileselector. Then a window is displayed with the buffered contents of the selected file (=window). You can then scroll with the cursorkeys and the page-up and pagedown key. Press 'x' or 'q' to exit this window. You cannot scroll the whole file, only the last 100 lines. To reset this limit to something bigger (or smaller), press the 'm' key. You will then be asked to enter a new value, e.g. 1000. This parameter can also be set from the commandline with the '-m value' parameter. With '-m' you set the limit for the next file, with '-M value' you'll set this parameter for all following files on the commandline. When you press the 'm'-key, the current buffer is cleared. So it is also a replacement for pressing the enter key a few times when using 'tail -f' to view a file.

Merging Files

Then there's the '-I' commandline parameter. It is the same as '-i', only '-I' merges two or more files together. For example:
multitail [-i] file1 -I file2
a reallife example:
multitail /var/log/apache/access.log -I /var/log/apache/error.log
These two examples will merge the output of the given files into one window. This can be useful with, for example, the files given in the second example: with this example you see what happend just before an error appeared in the Apache errorlog.

Viewing Output of External Programs

As I mentioned in the foreword, one can not only view files with MultiTail, also the output of other programs (scripts/executables/etc.) can be put inside one or more windows. To make this happen, you need to use the '-l' switch. For example:
multitail -l ls
another example:
multitail -l "ping localhost"
As you can see, you need to add doublequotes around the command when it needs parameters, otherwhise MultiTail would not be able to recognize what parameters are intended for the selected program or for MultiTail itself.
You might have tried the example with the ls-command. You then saw that MultiTail automatically closes the window when the external command has finished. There are a few options you can use to control this behaviour. For example the '-z' parameter: when given, the window is just closed, the screen redrawed and MultiTail goes on without the popup window telling you that the program ended.
Another option is: '-r interval': this will cause the command to be run every 'interval' seconds. Instead of '-r interval' also the '-R interval' option is available: when fed to MultiTail, it makes it run the next command with an interval of 'interval' seconds displaying only the difference with the previous run of the command! So if you run MultiTail like this:
multitail -R 3 -l "netstat -p tcp"
you will see state-changes for every TCP-connection: new connections and connections getting closed.
As with '-I file', '-L command' also merges the output of the external command to the previous file or command. Yes: output of commands can be safely merged with logfiles. Multiple commands, multiple logfiles, most things you can think of are possible.

Colors

When you have been watching logfiles scrolling by, it can get a little though after a while to still recognize what is important and what not. Because of that, MultiTail has the ability to display logfiles in color. When you give the '-c' parameter, the next given file or command is showed in color. It decides what color to use by looking at the whole log-line. If you want it to only use at the programname causing that logline (when monitoring syslog logfiles for example), you can use the '-cs' switch. The last option is the '-cS colorscheme' switch. As parameter it needs the name of a colorscheme. The colorschemes are read from multitail.conf. In multitail.conf you set by entering regular expressions what color to use for what "patterns". By default, MultiTail looks for multitail.conf in the current directory and in the /etc directory. With the '-F' parameter you can explicitly define what file it should use.
An example:
colorscheme:postfix
cs_re:yellow:status=sent
cs_re:magenta:queue active
The first line names the current colorscheme. The 'cs_re'-lines define combinations of regular expressions and a color. With the first 'cs_re'- line you define that if MultiTail encounters the string 'status=sent' in a logline that it should print it in the color yellow. The next line defines that the string 'queue active' must be printed in magenta. Another example, a little more complex:
colorscheme:syslog
cs_re:green:\[|\]
cs_re:blue:^... .. ..:..:..
The first 'cs_re'-line sets all occurences of '[' or ']' to green and all lines starting with a date in the format 'Mon DD HH:MM:SS' in blue. For more details on regular expressions: o'Reilly has few books on this topic.
One last thing on colors: if you use '-C' (uppercase 'C') instead of '-c', all following files will use the parameters you specify at that time, unless you override them with a new '-cx' or '-Cx' parameter.

Filtering using regular expressions

For filtering MultiTail uses regular expressions. To keep things simple, it uses them the exact same way as 'grep' does: '-e' says: a regular expression follows and '-v' says: invert it. Since version 3.5.0 this has changed somewhat: if you want to match the lines that do NOT have a certain pattern, use -ev.
Examples:
multitail -e "gnu-pop3d" /var/log/messages
multitail -v -e "ssh" -v -e "gnu-pop3d" -e "localhost" /var/log/messages
The first example shows only lines from /var/log/messages which have the string "gnu-pop3d" somewhere in them. The second example only shows lines which do not have the string "ssh" and not have the string "gnu-pop3d" and DO HAVE the string "localhost" in them.

Miscellaneous Options

There are a few other options not fitting elsewhere, these are:
-fThis makes MultiTail follow the file. In case the original file gets renamed and a new file is created with the original filename, MultiTail will start watching the file with the original filename (the one you entered).
-u secondsWhen using MultiTail over a slow link (a modem connection or maybe even over HAM) you might want to have a little less frequent updates. With this parameter you set how frequently MultiTail updates the screen. The default is immediately.
-H intervalIf you have a connection to some host (on which you're using MultiTail) which gets automatically disconnected when nothing happens for a while, you can use '-H'. When used, MultiTail moves the cursor around the screen generating traffic, keeping your line up.
-VIn case you're wondering what version of MultiTail you're using, you can start it with the '-V' option. It'll then display its version and exit. You can also press the 'i' key while it is running.

Is that all?

Not everything was covered in this manual. For a complete list of options and keys you can press while MultiTail runs, have a look at the man-page, the output of the '-h' commandline parameter and the help when you press 'h'-key while the program runs.
And let's not forget the sourcecode!



The latest version of MultiTail can always be found here: http://www.vanheusden.com/multitail/ multitail-7.1.5/mem.c000066400000000000000000000124251465663511400144710ustar00rootroot00000000000000#define _LARGEFILE64_SOURCE /* required for GLIBC to enable stat64 and friends */ #include #include #include #include #include #include #include #include #include #include #include #include "mt.h" #include "error.h" #include "globals.h" #include "term.h" #include "utils.h" #include "mem.h" void myfree(void *p) { free(p); } void * myrealloc(void *oldp, int new_size) { /* ---------------------------------------------------- * add code for repeatingly retry? -> then configurable * via configurationfile with number of retries and/or * sleep * ---------------------------------------------------- */ void *newp = realloc(oldp, new_size); if (!newp) error_exit(TRUE, TRUE, "Failed to reallocate a memory block to %d bytes.\n", new_size); return newp; } void * mymalloc(int size) { return myrealloc(NULL, size); } char * mystrdup(const char *in) { char *newp = strdup(in); if (!newp) error_exit(TRUE, TRUE, "Failed to duplicate a string: out of memory?\n"); return newp; } void clean_memory(void) { int loop; /* color schemes */ for(loop=0; loop n_strip; loop2++) { if ((cur -> pstrip)[loop2].type == STRIP_TYPE_REGEXP || (cur -> pstrip)[loop2].type == STRIP_KEEP_SUBSTR) { regfree(&(cur -> pstrip)[loop2].regex); myfree ((cur -> pstrip)[loop2].regex_str); } myfree ((cur -> pstrip)[loop2].del); } myfree(cur -> pstrip); myfree(cur -> filename); for(loop2=0; loop2 n_redirect; loop2++) myfree((cur -> predir)[loop2].redirect); myfree(cur -> predir); myfree(cur -> label); free_iat(&cur -> conversions); myfree(cur -> incomplete_line); myfree(cur -> win_title); delete_array(cur -> restart.diff.bcur, cur -> restart.diff.ncur); delete_array(cur -> restart.diff.bprev, cur -> restart.diff.nprev); myfree(cur -> repeat.last_line); free_iat(&cur -> cdef.color_schemes); delete_popup(cur -> status); delete_popup(cur -> data); for(loop2=0; loop2 n_re; loop2++) free_re(&(cur -> pre)[loop2]); myfree(cur -> pre); cur = cur -> next; if (pcur != &pi[loop]) myfree(pcur); } } myfree(pi); /* parameters per file */ for(loop=0; loop script); myfree(conversions[loop].pcs); } myfree(conversions); /* filterschemes */ for(loop=0; loop #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mt.h" #include "term.h" #include "utils.h" #include "error.h" #include "mem.h" #include "help.h" #include "globals.h" #include "ui.h" void info(void) { NEWWIN *mywin = create_popup(19, 60); int line = 7; struct utsname uinfo; int proc_u_line; char *term = getenv("TERM"); mvwprintw(mywin -> win, 1, 2, "-=* MultiTail " VERSION " *=-"); mvwprintw(mywin -> win, 3, 2, "Written by folkert@vanheusden.com"); mvwprintw(mywin -> win, 4, 2, "Website: http://www.vanheusden.com/multitail/"); if (!use_colors) mvwprintw(mywin -> win, line++, 2, "Your terminal doesn't support colors"); if (uname(&uinfo) == -1) error_popup("Retrieving system information", -1, "uname() failed\n"); else { line++; mvwprintw(mywin -> win, line++, 2, "Running on:"); #if defined(_GNU_SOURCE) && !defined(__APPLE__) mvwprintw(mywin -> win, line++, 2, "%s/%s %s %s", uinfo.nodename, uinfo.sysname, uinfo.machine, uinfo.domainname); #else mvwprintw(mywin -> win, line++, 2, "%s/%s %s", uinfo.nodename, uinfo.sysname, uinfo.machine); #endif mvwprintw(mywin -> win, line++, 2, "%s %s", uinfo.release, uinfo.version); line++; } if (has_colors()) mvwprintw(mywin -> win, line++, 2, "colors: %d, colorpairs: %d (%d), change colors: %s", COLORS, COLOR_PAIRS, cp.n_def, can_change_color()?"yes":"no"); else mvwprintw(mywin -> win, line++, 2, "Terminal does not support colors."); if (term) mvwprintw(mywin -> win, line++, 2, "Terminal size: %dx%d, terminal: %s", max_x, max_y, term); else mvwprintw(mywin -> win, line++, 2, "Terminal size: %dx%d", max_x, max_y); if (beep_interval > 0) mvwprintw(mywin -> win, line++, 2, "Did %d beeps.", did_n_beeps); proc_u_line = line++; escape_print(mywin, 16, 2, "_Press any key to exit this screen_"); #if defined(__FreeBSD__) || defined(linux) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) || defined(sun) || defined(__sun) || defined(__GNU__) || defined(__CYGWIN__) for(;;) { dtime_t run_time = get_ts() - mt_started; #ifndef __CYGWIN__ double v1, v2, v3; get_load_values(&v1, &v2, &v3); mvwprintw(mywin -> win, 6, 2, "Current load of system: %f %f %f", v1, v2, v3); #endif if (run_time) { struct rusage usage; if (getrusage(RUSAGE_SELF, &usage) == -1) error_exit(TRUE, FALSE, "getrusage() failed\n"); mvwprintw(mywin -> win, proc_u_line, 2, "Runtime: %02d:%02d:%02d, avg.proc.usage: %.2f%% ints/s: %.1f", (int)(run_time / 3600), ((int)run_time / 60) % 60, (int)run_time % 60, ((double)usage.ru_utime.tv_sec + (double)usage.ru_utime.tv_usec / 1000000.0 + (double)usage.ru_stime.tv_sec + (double)usage.ru_stime.tv_usec / 1000000.0) * 100.0 / run_time, (double)total_wakeups / run_time); } mydoupdate(); if (wait_for_keypress(-1, popup_refresh_interval, mywin, 0) != -1) break; } #else mydoupdate(); wait_for_keypress(-1, 0, mywin, 0); #endif delete_popup(mywin); } void reset_counters(statistics_t *ps) { memset(ps, 0x0, sizeof(*ps)); ps -> sccfirst = 1; } void statistics_popup(int f_index, proginfo *cur) { NEWWIN *popup = create_popup(16, 68); const char *title = "Statistics for "; char *abbr_fname = shorten_filename(cur -> filename, 54 - strlen(title)); char buffer[54 + 1]; snprintf(buffer, sizeof(buffer), "%s%s", title, abbr_fname); for(;;) { dtime_t time_running = get_ts() - cur -> statistics.start_ts; time_t start_ts = (time_t)cur -> statistics.start_ts; char *start_ts_str = mystrdup(ctime(&start_ts)); time_t lastevent = (time_t)cur -> statistics.lastevent; char *last_ts_str = mystrdup(ctime(&lastevent)); char *dummy; char *vmsize_str = NULL; char *fsize_str = NULL; char *total_data_processed_str = amount_to_str(cur -> statistics.bytes_processed); char *buffer_kb; off64_t fsize = -1; int c; int total_re = 0; int loop; if (cur -> wt == WT_COMMAND) { vmsize_str = amount_to_str(get_vmsize(cur -> pid)); } else if (cur -> wt == WT_FILE) { (void)file_info(cur -> filename, &fsize, 0, NULL, NULL); fsize_str = amount_to_str(fsize); } dummy = strchr(start_ts_str, '\n'); if (dummy) *dummy = 0x00; dummy = strchr(last_ts_str, '\n'); if (dummy) *dummy = 0x00; werase(popup -> win); win_header(popup, buffer); ui_inverse_on(popup); mvwprintw(popup -> win, 3, 2, "# lines :"); mvwprintw(popup -> win, 3, 27, "#l/s :"); mvwprintw(popup -> win, 3, 44, "Avg len:"); mvwprintw(popup -> win, 4, 2, "Data interval :"); if (cur -> wt == WT_COMMAND) mvwprintw(popup -> win, 5, 2, "VM size :"); else if (cur -> wt == WT_FILE) mvwprintw(popup -> win, 5, 2, "File size :"); mvwprintw(popup -> win, 9, 2, "Data processed:"); mvwprintw(popup -> win, 9, 27, "Bps :"); mvwprintw(popup -> win, 6, 2, "Started at :"); mvwprintw(popup -> win, 7, 2, "Last event :"); mvwprintw(popup -> win, 8, 2, "Next expected@:"); mvwprintw(popup -> win, 10, 2, "# matched r.e.:"); mvwprintw(popup -> win, 11, 2, "Buffered lines:"); mvwprintw(popup -> win, 11, 27, "Bytes:"); mvwprintw(popup -> win, 11, 44, "Limit :"); mvwprintw(popup -> win, 12, 2, "# of beeps: "); if (cur -> wt == WT_COMMAND) { mvwprintw(popup -> win, 13, 2, "Number of runs:"); mvwprintw(popup -> win, 13, 27, "Last rc:"); } ui_inverse_off(popup); mvwprintw(popup -> win, 3, 18, "%d", cur -> statistics.n_events); mvwprintw(popup -> win, 6, 18, "%s", start_ts_str); if (cur -> statistics.lastevent != (dtime_t)0.0) mvwprintw(popup -> win, 7, 18, "%s", last_ts_str); else mvwprintw(popup -> win, 7, 18, "---"); if (cur -> statistics.n_events == 0) { mvwprintw(popup -> win, 4, 18, "Not yet available"); } else { double avg = cur -> statistics.med / (double)cur -> statistics.n_events; double dev = sqrt((cur -> statistics.dev / (double)cur -> statistics.n_events) - pow(avg, 2.0)); /* serial correlation coefficient */ double scct1 = cur -> statistics.scct1 + cur -> statistics.scclast * cur -> statistics.sccu0; double med = cur -> statistics.med * cur -> statistics.med; double scc = (double)cur -> statistics.n_events * cur -> statistics.dev - med; if (scc != 0.0) { scc = ((double)cur -> statistics.n_events * scct1 - med) / scc; mvwprintw(popup -> win, 4, 18, "average: %.2f, std.dev.: %.2f, SCC: %1.6f", avg, dev, scc); } else mvwprintw(popup -> win, 4, 18, "average: %.2f, std.dev.: %.2f, not correlated", avg, dev); if (avg) { double dummy_d = (double)(time(NULL) - cur -> statistics.lastevent) / avg; time_t next_event = cur -> statistics.lastevent + (ceil(dummy_d) * avg); char *ne_str = mystrdup(ctime(&next_event)); char *dummy_str = strchr(ne_str, '\n'); if (dummy_str) *dummy_str = 0x00; mvwprintw(popup -> win, 8, 18, "%s", ne_str); myfree(ne_str); } mvwprintw(popup -> win, 3, 53, "%.1f", (double)cur -> statistics.bytes_processed / (double)cur -> statistics.n_events); } if (cur -> wt == WT_COMMAND) mvwprintw(popup -> win, 5, 18, "%s", vmsize_str); else if (cur -> wt == WT_STDIN || cur -> wt == WT_SOCKET) mvwprintw(popup -> win, 5, 18, "n.a."); else if (cur -> wt == WT_FILE) mvwprintw(popup -> win, 5, 18, "%s", fsize_str); myfree(vmsize_str); myfree(fsize_str); mvwprintw(popup -> win, 9, 18, "%s", total_data_processed_str); myfree(total_data_processed_str); if (time_running > 0) { char *bps_str = amount_to_str((double)cur -> statistics.bytes_processed / (double)time_running); mvwprintw(popup -> win, 9, 34, "%s", bps_str); myfree(bps_str); mvwprintw(popup -> win, 3, 34, "%.4f", (double)cur -> statistics.n_events / (double)time_running); } buffer_kb = amount_to_str(lb[f_index].curbytes); mvwprintw(popup -> win, 11, 18, "%d", lb[f_index].curpos); mvwprintw(popup -> win, 11, 34, "%s", buffer_kb); myfree(buffer_kb); mvwprintw(popup -> win, 12, 18, "%d", cur -> beep.did_n_beeps); escape_print(popup, 14, 2, "Press ^r^ to reset counters, ^q^ to exit"); myfree(start_ts_str); myfree(last_ts_str); for(loop=0; loop n_re; loop++) total_re += (cur -> pre)[loop].match_count; if (cur -> statistics.n_events) mvwprintw(popup -> win, 10, 18, "%d (%.2f%%)", total_re, (total_re * 100.0) / (double)cur -> statistics.n_events); else mvwprintw(popup -> win, 10, 18, "%d", total_re); if (cur -> wt == WT_COMMAND) { mvwprintw(popup -> win, 13, 18, "%d", cur -> n_runs); mvwprintw(popup -> win, 13, 36, "%d", cur -> last_exit_rc); } if (lb[f_index].maxnlines > 0) { mvwprintw(popup -> win, 11, 53, "%d lines", lb[f_index].maxnlines); } else if (lb[f_index].maxbytes > 0) { char *str = amount_to_str(lb[f_index].maxbytes); mvwprintw(popup -> win, 11, 53, "%s", str); myfree(str); } draw_border(popup); mydoupdate(); c = toupper(wait_for_keypress(HELP_STATISTICS_POPUP, popup_refresh_interval, popup, 0)); if (c == 'Q' || c == abort_key) { break; } else if (c == 'R') { reset_counters(&cur -> statistics); } else if (c != -1) { wrong_key(); } } delete_popup(popup); } void statistics_menu(void) { NEWWIN *mywin = create_popup(23, 65); int offset = 0, cur_line = 0; for(;;) { int c; int vmsize = get_vmsize(getpid()); time_t now = time(NULL); struct tm *tmnow = localtime(&now); proginfo **plist = NULL; char *issub = NULL; int *winnr = NULL; int loop, nwin = 0; /* create list of (sub-)windows */ for(loop=0; loop next; } } werase(mywin -> win); win_header(mywin, "Statistics"); for(loop=0; loop<18; loop++) { int cur_index = loop + offset; int is_sub_indent; if (cur_index >= nwin) break; is_sub_indent = issub[cur_index]; if (loop == cur_line) ui_inverse_on(mywin); if (is_sub_indent) mvwprintw(mywin -> win, 2 + loop, 7, "%s", shorten_filename(plist[cur_index] -> filename, 54)); else mvwprintw(mywin -> win, 2 + loop, 2, "[%02d] %s", winnr[cur_index], shorten_filename(plist[cur_index] -> filename, 56)); if (loop == cur_line) ui_inverse_off(mywin); } mvwprintw(mywin -> win, 20, 2, "Run-time: %.2f hours %02d:%02d", (get_ts() - mt_started) / 3600.0, tmnow -> tm_hour, tmnow -> tm_min); if (vmsize != -1) { char *vmsize_str = amount_to_str(vmsize); mvwprintw(mywin -> win, 20, 35, "Memory usage: %s", vmsize_str); myfree(vmsize_str); } escape_print(mywin, 21, 2, "Press ^r^ to reset counters, ^q^ to exit"); draw_border(mywin); mydoupdate(); c = toupper(wait_for_keypress(HELP_STATISTICS, popup_refresh_interval, mywin, 1)); if (c == 'R') { for(loop=0; loop statistics); cur = cur -> next; } } } else if (c == KEY_UP) { if (cur_line) cur_line--; else if (offset) offset--; else wrong_key(); } else if (c == KEY_DOWN) { if ((cur_line + offset) < (nwin - 1)) { if (cur_line < (18 - 1)) cur_line++; else offset++; } else wrong_key(); } else if (c == 13 || c == ' ') { statistics_popup(winnr[cur_line + offset], plist[cur_line + offset]); } else if (c == 'Q' || c == abort_key) { myfree(plist); myfree(issub); myfree(winnr); break; } else if (c != -1) { wrong_key(); } myfree(plist); myfree(issub); myfree(winnr); } delete_popup(mywin); } void heartbeat(void) { time_t now = time(NULL); struct tm *ptm = localtime(&now); static int x = 0, y = 0, dx = 1, dy = 1; static NEWWIN *hb_win = NULL; x += dx; y += dy; if (x >= (max_x - 8)) { dx = -(myrand(1) + 1); x = max_x - (8 + 1); } else if (x < 0) { dx = (myrand(2) + 1); x = 0; } if (y >= max_y) { dy = -(myrand(2) + 1); y = max_y - 1; } else if (y < 0) { dy = (myrand(2) + 1); y = 0; } if (dx == 0 && dy == 0) { dy = 1; dy = -1; } if (!hb_win) { hb_win = create_popup(1, 8); } move_panel(hb_win -> pwin, y, x); ui_inverse_on(hb_win); mvwprintw(hb_win -> win, 0, 0, "%02d:%02d:%02d", ptm -> tm_hour, ptm -> tm_min, ptm -> tm_sec); ui_inverse_off(hb_win); mydoupdate(); } void do_check_for_mail(dtime_t time) { if (check_for_mail > 0 && mail_spool_file != NULL) { /* get current filesize */ if (stat64(mail_spool_file, &msf_info) == -1) { if (errno != ENOENT) { check_for_mail = 0; error_popup("Check for new e-mail", -1, "Error doing stat64() on file %s.\ne-Mail check disabled.\n", mail_spool_file); } } /* filesize changed? */ if (msf_info.st_size != msf_prev_size) { /* file became bigger: new mail * if it became less, the file changed because * mail was deleted or so */ if (msf_info.st_size > msf_prev_size) { mail = 1; redraw_statuslines(); if (get_do_refresh() != 2) set_do_refresh(1); } msf_prev_size = msf_info.st_size; } } } void store_statistics(proginfo *cur, dtime_t now) { if (cur -> statistics.lastevent) { dtime_t cur_deltat = now - cur -> statistics.lastevent; if (cur -> statistics.n_events == 1) { cur -> statistics.total_deltat += (cur_deltat - cur -> statistics.prev_deltat); cur -> statistics.prev_deltat = cur_deltat; } cur -> statistics.med += cur_deltat; cur -> statistics.dev += pow(cur_deltat, 2.0); cur -> statistics.n_events++; /* Update calculation of serial correlation coefficient */ /* (also uses cur -> med/dev) */ if (cur -> statistics.sccfirst) { cur -> statistics.sccfirst = 0; cur -> statistics.scclast = 0; cur -> statistics.sccu0 = cur_deltat; } else cur -> statistics.scct1 = cur -> statistics.scct1 + cur -> statistics.scclast * cur_deltat; cur -> statistics.scclast = cur_deltat; } cur -> statistics.lastevent = now; } multitail-7.1.5/misc.h000066400000000000000000000002551465663511400146510ustar00rootroot00000000000000#include "mt.h" void info(void); void statistics_menu(void); void heartbeat(void); void do_check_for_mail(dtime_t time); void store_statistics(proginfo *cur, dtime_t now); multitail-7.1.5/mt.c000066400000000000000000002466651465663511400143520ustar00rootroot00000000000000#define _GNU_SOURCE #define _LARGEFILE64_SOURCE /* required for GLIBC to enable stat64 and friends */ #include #include #include #include #include #include #include #include #if !defined(__APPLE__) && !defined(__CYGWIN__) #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #ifndef AIX #if defined(__FreeBSD__) #include #else #include /* needed on Solaris 8 */ #endif #endif #include #include #include #if defined(__GLIBC__) && ( __GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 2)) #include #endif /* syslog receive */ #include #include #include #include #include #ifdef UTF8_SUPPORT #include #include #endif #include "mt.h" #include "globals.h" #include "error.h" #include "mem.h" #include "utils.h" #include "scrollback.h" #include "help.h" #include "term.h" #include "history.h" #include "cv.h" #include "selbox.h" #include "stripstring.h" #include "color.h" #include "misc.h" #include "ui.h" #include "exec.h" #include "diff.h" #include "config.h" #include "cmdline.h" /* #define KEYB_DEBUG */ void LOG(char *s, ...) { #ifdef _DEBUG va_list ap; FILE *fh = fopen("log.log", "a+"); if (!fh) { endwin(); error_exit(TRUE, TRUE, "error logging\n"); } va_start(ap, s); vfprintf(fh, s, ap); va_end(ap); fclose(fh); #endif } typedef void (*sh_t)(int); void set_signal(int sig, sh_t func, char *signame) { if (SIG_ERR == signal(sig, func)) error_exit(TRUE, FALSE, "Setting of handler for signal %s failed.\n", signame); } void free_subentry(proginfo *entry) { int loop; /* free up filters */ for(loop=0; loop n_strip; loop++) myfree((entry -> pstrip)[loop].del); myfree(entry -> pstrip); /* free all those allocated memory blocks */ myfree(entry -> filename); myfree(entry -> cdef.field_del); if (entry -> status) delete_popup(entry -> status); if (entry -> data) delete_popup(entry -> data); /* free buffers for diff (if any) */ if (entry -> restart.diff.bcur) delete_array(entry -> restart.diff.bcur, entry -> restart.diff.ncur); if (entry -> restart.diff.bprev) delete_array(entry -> restart.diff.bprev, entry -> restart.diff.nprev); /* delete regular expressions */ for(loop=0; loop n_re; loop++) free_re(&(entry -> pre)[loop]); myfree(entry -> pre); /* stop process */ stop_process(entry -> pid); /* close pipe to (tail) process */ myclose(entry -> fd); if (entry -> wfd != entry -> fd) myclose(entry -> wfd); } void buffer_replace_pi_pointers(int f_index, proginfo *org, proginfo *new) { int loop; for(loop=0; loop curpos; loop++) myfree((pb -> be)[loop].Bline); myfree(pb -> be); pb -> be = NULL; pb -> curpos = 0; } char delete_entry(int f_index, proginfo *sub) { char delete_all = 0; /* no children? then sub must be pointing to current */ if (pi[f_index].next == NULL) { sub = NULL; } /* stop the process(es) we're watching ('tail' most of the time) */ if (sub == NULL) /* delete all? */ { proginfo *cur = &pi[f_index]; do { free_subentry(cur); cur = cur -> next; } while(cur); /* free the subwindows (if any) */ cur = pi[f_index].next; while(cur) { proginfo *dummy = cur -> next; myfree(cur); cur = dummy; } delete_all = 1; } else { free_subentry(sub); } /* remove entry from array */ if (sub == NULL) /* delete entry in the main array */ { int n_to_move = (nfd - f_index) - 1; /* free buffers */ delete_be_in_buffer(&lb[f_index]); if (n_to_move > 0) { int loop; /* update buffer proginfo-pointers */ for(loop=f_index + 1; loop pi[loop] met pi[loop-1] */ buffer_replace_pi_pointers(loop, &pi[loop], &pi[loop - 1]); } /* prog info */ memmove(&pi[f_index], &pi[f_index+1], sizeof(proginfo) * n_to_move); /* buffers */ memmove(&lb[f_index], &lb[f_index+1], sizeof(buffer) * n_to_move); } nfd--; /* shrink array */ if (nfd == 0) /* empty? */ { myfree(pi); pi = NULL; myfree(lb); lb = NULL; } else /* not empty, just shrink */ { pi = (proginfo *)myrealloc(pi, nfd * sizeof(proginfo)); lb = (buffer *) myrealloc(lb, nfd * sizeof(buffer)); } } else /* delete sub */ { if (sub != &pi[f_index]) /* not in main array? */ { proginfo *parent = &pi[f_index]; /* find parent of 'sub' */ while (parent -> next != sub) parent = parent -> next; parent -> next = sub -> next; buffer_replace_pi_pointers(f_index, sub, NULL); myfree(sub); } else /* main array, find next */ { proginfo *oldnext = pi[f_index].next; /* first, delete the entries of that entry (which is in the array) */ buffer_replace_pi_pointers(f_index, &pi[f_index], NULL); /* then, 'rename' the pointers (original address -> address in array)... */ buffer_replace_pi_pointers(f_index, oldnext, &pi[f_index]); /* and move the object to the array */ memmove(&pi[f_index], oldnext, sizeof(proginfo)); /* free the obsolete entry */ myfree(oldnext); } } return delete_all; } /** do_exit * - in: int sig * - returns: nothing (doesn't return!) * this function is called when MultiTail receives the TERM-signal. it is also * called by, for example, wait_for_keypress when ^c is pressed. it stops all * child-processes, ends the curses-library and exits with SUCCESS status */ void do_exit(void) { proginfo *cur; int loop; set_signal(SIGCHLD, SIG_IGN, "do_exit"); /* kill tail processes */ for(loop=0; loop n_redirect; r_i++) { if ((cur -> predir)[r_i].type != 0) { myclose((cur -> predir)[r_i].fd); if ((cur -> predir)[r_i].type == REDIRECTTO_PIPE_FILTERED || (cur -> predir)[r_i].type == REDIRECTTO_PIPE) stop_process((cur -> predir)[r_i].pid); } } myclose(cur -> fd); if (cur -> wfd != -1 && cur -> wfd != cur -> fd) myclose(cur -> wfd); if (cur -> pid != -1) stop_process(cur -> pid); cur = cur -> next; } while(cur); } /* kill convesion scripts */ for(loop=0; loop 16) *prt_start = find_char_offset(&string[16], ' '); else *prt_start = -1; if (*prt_start == -1) *prt_start = 0; *disp_end = nbytes - *prt_start; } else if (mode == 'o') { *prt_start = min(wrap_offset, nbytes); *disp_end = nbytes - *prt_start; } } int draw_tab(NEWWIN *win) { if (tab_width) { int curx = getcurx(win -> win), loop; int move = (((curx / tab_width) + 1) * tab_width) - curx; for(loop=0; loop win, ' '); } return 1; } char find_highlight_matches(regmatch_t *matches, char use_regex, int offset) { int match_offset; if (!matches) return 0; for(match_offset=0; match_offset= matches[match_offset].rm_so && offset < matches[match_offset].rm_eo; if ((use_regex == 'c' && matching) || (use_regex == 'C' && !matching)) { return 1; } } return 0; } myattr_t * find_cmatches_index(color_offset_in_line *cmatches, int n_cmatches, mybool_t has_merge_colors, int offset) { static myattr_t final_color; int cmatches_index = 0; int fg_composed = -1, bg_composed = -1; int attrs = -1; char first_set = 1; for(cmatches_index=0; cmatches_index= cmatches[cmatches_index].start && offset < cmatches[cmatches_index].end) { if (has_merge_colors == MY_FALSE) { return &cmatches[cmatches_index].attrs; } if (cmatches[cmatches_index].merge_color == MY_TRUE) { /* merge these colors */ if (cmatches[cmatches_index].attrs.colorpair_index != -1) { int fg_fc, bg_fc; fg_fc = cp.fg_color[cmatches[cmatches_index].attrs.colorpair_index]; bg_fc = cp.bg_color[cmatches[cmatches_index].attrs.colorpair_index]; if (fg_fc != -1 && fg_composed == -1) fg_composed = fg_fc; if (bg_fc != -1 && bg_composed == -1) bg_composed = bg_fc; } if (cmatches[cmatches_index].attrs.attrs != -1) { if (attrs == -1) attrs = cmatches[cmatches_index].attrs.attrs; else attrs |= cmatches[cmatches_index].attrs.attrs; } } else if (first_set) { first_set = 0; fg_composed = cp.fg_color[cmatches[cmatches_index].attrs.colorpair_index]; bg_composed = cp.bg_color[cmatches[cmatches_index].attrs.colorpair_index]; attrs = cmatches[cmatches_index].attrs.attrs; } } } if (fg_composed != -1 || bg_composed != -1 || final_color.attrs != -1) { final_color.attrs = attrs; final_color.colorpair_index = find_or_init_colorpair(fg_composed, bg_composed, 1); return &final_color; } return NULL; } void gen_wordwrap_offsets(char *string, int start_offset, int end_offset, int win_width, int **offsets) { int check_x = start_offset; int n_ww = 0; for(;;) { int max_len = wordwrapmaxlength < win_width ? wordwrapmaxlength : win_width - 2; check_x += win_width - 1; if (check_x >= end_offset) break; while(max_len >= 0 && (!isspace(string[check_x])) && check_x > 0) { check_x--; max_len--; } if (max_len >= 0) { *offsets = (int *)myrealloc(*offsets, (n_ww + 1) * sizeof(int)); (*offsets)[n_ww++] = check_x; } else { check_x += wordwrapmaxlength; } } *offsets = (int *)myrealloc(*offsets, (n_ww + 1) * sizeof(int)); (*offsets)[n_ww] = -1; } int count_utf_bytes(int c) { if ((c & 0xe0) == 0xc0) return 2; else if ((c & 0xf0) == 0xe0) return 3; else if ((c & 0xf8) == 0xf0) return 4; return 1; } void do_color_print(proginfo *cur, char *use_string, int prt_start, int prt_end, int disp_end, color_offset_in_line *cmatches, int n_cmatches, mybool_t has_merge_colors, char start_reverse, regmatch_t *matches, int matching_regex, char use_regex, NEWWIN *win) { int offset; myattr_t cdev = { -1, -1 }; char default_reverse_state = 0; int disp_offset = 0; char highlight_part = toupper(use_regex) == 'C'; char use_colorschemes = cur -> cdef.colorize == 'S' || cur -> cdef.colorize == 'T' || highlight_part; int *ww = NULL; int ww_offset = 0; if (global_highlight_str) { if (regexec(&global_highlight_re, use_string, 0, NULL, 0) == 0) default_reverse_state = 1; } if (cur -> line_wrap == 'w') { gen_wordwrap_offsets(use_string, prt_start, prt_end, getmaxx(win -> win), &ww); } /* print text */ for(offset=prt_start; offset win, "\n"); ww_offset++; #ifdef UTF8_SUPPORT if (iswspace(wcur)) #else if (isspace(wcur)) #endif { offset++; continue; } } /* find things to colorize */ if (use_colorschemes) { if (highlight_part && find_highlight_matches(matches, use_regex, offset)) { re_inv = 1; } else { /* if there's a list of offsets where colors should be displayed, check if the * current offset in the string (the one we're going to display) is somewhere * in that list off offsets */ myattr_t *pa = find_cmatches_index(cmatches, n_cmatches, has_merge_colors, offset); if (pa != NULL) new_cdev = *pa; } } /* control-characters will be displayed as an inverse '.' */ #ifdef UTF8_SUPPORT if (iswcntrl(wcur)) #else if (iscntrl(wcur)) #endif { is_control_or_extended_ascii = 1; #ifdef UTF8_SUPPORT if (!iswspace(wcur)) #else if (!isspace(wcur)) #endif re_inv = 1; } if (re_inv) new_cdev.attrs = inverse_attrs; /* new color selected? then switch off previous color */ if (cdev.colorpair_index != new_cdev.colorpair_index || cdev.attrs != new_cdev.attrs) { myattr_off(win, cdev); myattr_on(win, new_cdev); cdev = new_cdev; } if (!is_control_or_extended_ascii) { #if defined(UTF8_SUPPORT) && defined(NCURSES_WIDECHAR) // FIXME warning: implicit declaration of function ‘waddnwstr’ is invalid in C99 [-Wimplicit-function-declaration] // see /usr/include/ncurses.h waddnwstr(win -> win, &wcur, 1); #else wprintw(win -> win, "%c", wcur); #endif disp_offset++; } else { /* error_exit("> 126 %d: %02x %02x %02x", wcur, use_string[offset+0], use_string[offset+1], use_string[offset+2]); */ if (wcur == 9 && tab_width > 0) /* TAB? */ { disp_offset += draw_tab(win); } /* 13 (CR) is just silently ignored */ else if (wcur != 13) { if (caret_notation) { wprintw(win -> win, "^%c", 'a' + wcur - 1); disp_offset++; } else waddch(win -> win, '.'); disp_offset++; } } if (disp_offset >= disp_end && disp_end != -1) break; offset += count_utf_bytes(use_string[offset]); } if (prt_start == prt_end) /* scrolled out line */ wprintw(win -> win, "\n"); if (cdev.attrs != -1) myattr_off(win, cdev); myfree(ww); } char *escape(const char *what, const proginfo *const cur) { char *temp = strdup(what ? what : ""); for(;;) { char *new_str = NULL; char *p = strstr(temp, "%f"); if (!p) break; *p = 0x00; asprintf(&new_str, "%s%s%s", temp, cur -> filename, p + 2); free(temp); temp = new_str; } return temp; } void color_print(int f_index, NEWWIN *win, proginfo *cur, char *string, regmatch_t *matches, int matching_regex, mybool_t force_to_winwidth, int start_at_offset, int end_at_offset, double ts, char show_window_nr) { char reverse = 0; myattr_t cdev = { -1, -1 }; int prt_start = 0, prt_end = 0, disp_end = 0; int mx = -1; char use_regex = 0; color_offset_in_line *cmatches = NULL; int n_cmatches = 0; mybool_t has_merge_colors = MY_FALSE; char *use_string = NULL; int x; /* stop if there's no window tou output too */ if (!win) return; /* check if the cursor is not at the beginning of the line * if it is not and it is also not at the most right position, move it * to the left (when it is already at the right most position, it'll move * to the left itself when emitting text) */ mx = getmaxx(win -> win); x = getcurx(win -> win); if ((x != 0 && x != mx) || !suppress_empty_lines) wprintw(win -> win, "\n"); /* cur == NULL? Markerline! */ if (IS_MARKERLINE(cur)) { draw_marker_line(win, string, cur); return; } if (f_index >= 0) { /* add a window-id [xx] in front of each line */ if (show_window_nr) { mx -= wprintw(win -> win, window_number, f_index); } /* add a subwindow-ID: [xx] in front of each line */ if (show_subwindow_id || (show_window_nr && pi[f_index].next != NULL)) { proginfo *ppi = &pi[f_index]; int subwin_nr = 0; while(ppi != cur && ppi -> next != NULL) { subwin_nr++; ppi = ppi -> next; } mx -= wprintw(win -> win, subwindow_number, subwin_nr); } } /* add a timestamp in front of each line */ if (cur -> add_timestamp) { char timestamp[1024]; double_ts_to_str(ts, line_ts_format, timestamp, sizeof(timestamp)); mx -= wprintw(win -> win, "%s ", timestamp); } /* add a label */ if (cur -> label != NULL && (cur -> label)[0]) { char *temp = escape(cur -> label, cur); mx -= wprintw(win -> win, "%s", temp); free(temp); } if (mx <= 0) mx = 4; /* had a matching regexp? */ if (matching_regex != -1) use_regex = (cur -> pre)[matching_regex].use_regex; /* select color or generate list of offset for colors */ if (cur -> cdef.colorize) /* no need to do this for b/w terminals */ { /* choose_color not only generates a list of colors but if there are terminal- * codes in that string which set a color those are stripped as well * the stripping part should be moved to a seperate function */ cdev = choose_color(string, cur, &cmatches, &n_cmatches, &has_merge_colors, &use_string); /* if not using colorschemes (which can have more then one color * per line), set the color */ if (cur -> cdef.colorize != 'S' && cur -> cdef.colorize != 'T') { myattr_on(win, cdev); if (cdev.attrs != -1 && cdev.attrs & A_REVERSE) reverse = 1; } } /* select start and end of string to display */ LOG("lwo: %d, mx: %d, ps: %d, pe: %d, de: %d\n", cur -> line_wrap_offset, mx, prt_start, prt_end, disp_end); select_display_start_and_end(USE_IF_SET(use_string, string), force_to_winwidth == MY_TRUE?'l':cur -> line_wrap, cur -> line_wrap_offset, mx, &prt_start, &prt_end, &disp_end); if (prt_start == 0 && start_at_offset > 0) { prt_start = start_at_offset; prt_end = end_at_offset; } /* and display on terminal */ LOG("ps: %d, pe: %d, de: %d\n", prt_start, prt_end, disp_end); LOG("%s\n", USE_IF_SET(use_string, string)); do_color_print(cur, USE_IF_SET(use_string, string), prt_start, prt_end, disp_end, cmatches, n_cmatches, has_merge_colors, reverse, matches, matching_regex, use_regex, win); myattr_off(win, cdev); myfree(use_string); myfree(cmatches); } void check_filter_exec(char *cmd, char *matching_string) { int str_index, par_len = strlen(matching_string); int cmd_len = strlen(cmd); char *command = mymalloc(cmd_len + 1/* cmd + space */ + 1 + (par_len * 2) + 1 + 1); /* "string"\0 */ int loop; memcpy(command, cmd, cmd_len); str_index = cmd_len; command[str_index++] = ' '; command[str_index++] = '\"'; for(loop=0; loop n_re; loop++) { int rc; char cmd = (cur -> pre)[loop].use_regex; char invert = (cur -> pre)[loop].invert_regex; /* SKIP DISABLED REGEXPS */ if (!cmd) continue; /* EXECUTE THE REGEXPS */ if (pmatch) { if (*pmatch == NULL) *pmatch = (regmatch_t *)mymalloc(sizeof(regmatch_t) * MAX_N_RE_MATCHES); rc = regexec(&(cur -> pre)[loop].regex, string, MAX_N_RE_MATCHES, *pmatch, 0); } else { rc = regexec(&(cur -> pre)[loop].regex, string, MAX_N_RE_MATCHES, local_matches, 0); } /* IF ERROR, ABORT EVERYTHING */ if (rc != 0 && rc != REG_NOMATCH) { *error = convert_regexp_error(rc, &(cur -> pre)[loop].regex); re_exec_ok = 0; break; } /* MATCHED? */ if (rc == 0) { if (!invert) { *matching_regex = loop; if (cmd == 'm') { *display = 1; (cur -> pre)[loop].match_count++; return 1; } else if (cmd == 'v') { *display = 0; (cur -> pre)[loop].match_count++; return 1; } else if (cmd == 'x' && do_re) { check_filter_exec((cur -> pre)[loop].cmd, string); (cur -> pre)[loop].match_count++; } else if (cmd == 'X' && do_re) { regmatch_t *usep = USE_IF_SET(*pmatch, local_matches); int len = usep[1].rm_eo - usep[1].rm_so; char *dummy = (char *)mymalloc(len + 1); memcpy(dummy, &string[usep[1].rm_so], len); dummy[len] = 0x00; check_filter_exec((cur -> pre)[loop].cmd, dummy); (cur -> pre)[loop].match_count++; myfree(dummy); } else if (toupper(cmd) == 'B' && do_re) { beep(); (cur -> pre)[loop].match_count++; } } else { if (cmd == 'm') { *display = 0; } } } else { myfree(*pmatch); *pmatch = NULL; if (invert) { *matching_regex = loop; if (cmd == 'm') { *display = 1; (cur -> pre)[loop].match_count++; return 1; } else if (cmd == 'v') { *display = 0; (cur -> pre)[loop].match_count++; return 1; } else if (cmd == 'x' && do_re) { check_filter_exec((cur -> pre)[loop].cmd, string); (cur -> pre)[loop].match_count++; } else if (cmd == 'X' && do_re) { int len = *pmatch ? (*pmatch)[1].rm_eo - (*pmatch)[1].rm_so : local_matches[1].rm_eo - local_matches[1].rm_so; char *dummy = (char *)mymalloc(len + 1); memcpy(dummy, &string[local_matches[1].rm_so], len); dummy[len] = 0x00; check_filter_exec((cur -> pre)[loop].cmd, dummy); (cur -> pre)[loop].match_count++; myfree(dummy); } else if (toupper(cmd) == 'B' && do_re) { beep(); (cur -> pre)[loop].match_count++; } } else { if (cmd == 'm') { *display = 0; } } } } return re_exec_ok; } void do_print(int f_index, proginfo *cur, char *string, regmatch_t *matches, int matching_regex, double ts) { /* check filter */ /* if cur == NULL, then marker line: don't check against filter */ if (IS_MARKERLINE(cur)) { color_print(f_index, pi[f_index].data, cur, string, NULL, -1, MY_FALSE, 0, 0, ts, 0); } else { color_print(f_index, pi[f_index].data, cur, string, matches, matching_regex, MY_FALSE, 0, 0, ts, 0); } } char is_buffer_too_full(buffer *lb, int n_bytes_to_add) { return (lb -> curpos >= lb -> maxnlines && lb -> maxnlines > 0) || ((lb -> curbytes + n_bytes_to_add) >= lb -> maxbytes && lb -> maxbytes > 0 && n_bytes_to_add < lb -> maxbytes); } void do_buffer(int f_index, proginfo *cur, char *string, char filter_match, dtime_t now) { /* remember string */ if (lb[f_index].bufferwhat == 'a' || filter_match == 1) { int cur_line; int line_len = 0; int move_index = 0, old_curpos = lb[f_index].curpos; int min_shrink = 0; if (string) line_len = strlen(string); if (is_buffer_too_full(&lb[f_index], line_len)) min_shrink = default_min_shrink; /* remove enough lines untill there's room */ if (lb[f_index].curpos > 0) { while(is_buffer_too_full(&lb[f_index], line_len) || min_shrink > 0) { /* delete oldest */ if (lb[f_index].be[move_index].Bline) { lb[f_index].curbytes -= strlen(lb[f_index].be[move_index].Bline); myfree(lb[f_index].be[move_index].Bline); } move_index++; lb[f_index].curpos--; if (lb[f_index].curpos == 0) break; min_shrink--; } } if (move_index > 0) { /* move entries over the deleted one */ memmove(&lb[f_index].be[0], &lb[f_index].be[move_index], (old_curpos - move_index) * sizeof(buffered_entry)); } else { /* grow array */ lb[f_index].be = (buffered_entry *)myrealloc(lb[f_index].be, sizeof(buffered_entry) * (lb[f_index].curpos + 1)); } cur_line = lb[f_index].curpos++; /* add the logline itself */ if (string) { lb[f_index].be[cur_line].Bline = mystrdup(string); lb[f_index].curbytes += line_len; } else lb[f_index].be[cur_line].Bline = NULL; /* remember pointer to subwindow (required for setting colors etc.) */ lb[f_index].be[cur_line].pi = cur; /* remember when this string was buffered */ lb[f_index].be[cur_line].ts = now; } } void do_redirect(redirect_t *predir, char *buffer, int nbytes, char add_lf) { int failed = 0; if (predir -> type == REDIRECTTO_SOCKET_FILTERED || predir -> type == REDIRECTTO_SOCKET) { int msg_len = nbytes + 6; char *msg = (char *)mymalloc(msg_len + 1);; snprintf(msg, msg_len, "<%d>%s\n", predir -> prio_fac, buffer); if (sendto(predir -> fd, msg, msg_len, 0, (const struct sockaddr *)&(predir -> sai), sizeof(predir -> sai)) == -1) failed = 1; myfree(msg); } else { if (WRITE(predir -> fd, buffer, nbytes, "redirect") != nbytes) failed = 1; if (add_lf == MY_TRUE && WRITE(predir -> fd, "\n", 1, "redirect") != 1) failed = 1; } if (failed) { error_popup("Error redirecting output", HELP_REDIRECT_FAILED, "Redirecting the output failed: %s\nRedirection stopped.", strerror(errno)); /* stop redirecting */ myclose(predir -> fd); predir -> fd = -1; predir -> type = REDIRECTTO_NONE; } } void redirect(proginfo *cur, char *data, int n_bytes, mybool_t is_filtered) { int r_i; for(r_i=0; r_i < cur -> n_redirect; r_i++) { if (is_filtered == MY_TRUE && ((cur -> predir)[r_i].type == REDIRECTTO_FILE_FILTERED || (cur -> predir)[r_i].type == REDIRECTTO_PIPE_FILTERED || (cur -> predir)[r_i].type == REDIRECTTO_SOCKET_FILTERED)) { do_redirect(&(cur -> predir)[r_i], data, n_bytes, 1); } else if (is_filtered == MY_FALSE && ((cur -> predir)[r_i].type == REDIRECTTO_FILE || (cur -> predir)[r_i].type == REDIRECTTO_PIPE || (cur -> predir)[r_i].type == REDIRECTTO_SOCKET)) { do_redirect(&(cur -> predir)[r_i], data, n_bytes, 0); } } } void delete_all_markerlines(void) { int index; for(index=0; index conversions, line); (void)check_filter(cur, new_line, &pmatch, &error, &matching_regex, 1, &display); /* error happened while processing regexp? */ if (error) { if (!marker_added) { marker_added = 1; add_marker_if_changed(f_index, cur); } add_markerline(f_index, cur, MARKER_MSG, error); do_print_and_buffer = 1; something_got_displayed = 1; myfree(error); } if (cur -> repeat.suppress_repeating_lines) { if ((!cur -> repeat.last_line && !new_line) || (cur -> repeat.last_line != NULL && new_line != NULL && strcmp(cur -> repeat.last_line, new_line) == 0)) { cur -> repeat.n_times_repeated++; } else { do_print_and_buffer = 1; if (new_line) cur -> repeat.last_line = mystrdup(new_line); else cur -> repeat.last_line = NULL; } } else do_print_and_buffer = 1; if (do_print_and_buffer) { if ((cur -> repeat.n_times_repeated > 0 || display) && !marker_added) { marker_added = 1; something_got_displayed |= add_marker_if_changed(f_index, cur); } if (cur -> repeat.n_times_repeated) { char message[128]; snprintf(message, sizeof(message), "Last message repeated %d times", cur -> repeat.n_times_repeated); do_print(f_index, cur, message, NULL, -1, now); do_buffer(f_index, cur, message, 1, now); something_got_displayed = 1; cur -> repeat.n_times_repeated = 0; myfree(cur -> repeat.last_line); cur -> repeat.last_line = NULL; } if (display) { char *stripped = do_strip(cur, new_line); /* output new text */ do_print(f_index, cur, USE_IF_SET(stripped, new_line), pmatch, matching_regex, now); myfree(stripped); something_got_displayed = 1; redirect(cur, new_line, strlen(new_line), MY_TRUE); } do_buffer(f_index, cur, new_line, display, now); } if (pmatch) myfree(pmatch); if (new_line != line) myfree(new_line); return something_got_displayed; } void update_statusline(NEWWIN *status, int win_nr, proginfo *cur) { if (mode_statusline > 0 && status != NULL && cur != NULL) { myattr_t attrs = statusline_attrs; int statusline_len = 0; off64_t fsize = (off64_t)-1; time_t ts = time(NULL); char show_f1 = 0; int help_str_offset = 0; int total_info_len = 0; int win_width; char *fname = cur -> filename; char timestamp[TIMESTAMP_EXTEND_BUFFER]; if (win_nr == terminal_main_index) attrs.colorpair_index = find_colorpair(COLOR_RED, -1, 0); else if (mail) attrs.colorpair_index = find_colorpair(COLOR_GREEN, -1, 0); if ((ts - mt_started) < 5) show_f1 = 1; myattr_on(status, attrs); draw_line(status, LINE_BOTTOM); if (filename_only) { char *dummy = strrchr(cur -> filename, '/'); if (dummy) fname = dummy + 1; } win_width = getmaxx(status -> win); mvwprintw(status -> win, 0, 0, "%02d] %s", win_nr, shorten_filename(USE_IF_SET(cur -> win_title, fname), win_width - 4)); if (cur -> wt == WT_FILE) (void)file_info(cur -> filename, &fsize, TT_MTIME, &ts, NULL); get_now_ts(statusline_ts_format, timestamp, sizeof(timestamp)); help_str_offset = 4 + strlen(USE_IF_SET(cur -> win_title, fname)); /* 4: '%02d] '!! (window nr.) */ statusline_len = help_str_offset + strlen(timestamp) + 1; /* 4: '%02d] '!! (window nr.) */ if (win_nr == terminal_main_index) wprintw(status -> win, ", press +, to exit"); else if (mail) wprintw(status -> win, " You've got mail!"); if (win_width >= (statusline_len + 13)) { if (cur -> paused) { color_on(status, find_colorpair(COLOR_YELLOW, -1, 0)); mvwprintw(status -> win, 0, win_width - 10, " Paused "); color_off(status, find_colorpair(COLOR_YELLOW, -1, 0)); } else if (cur -> wt == WT_COMMAND) { int vmsize = get_vmsize(cur -> pid); total_info_len = statusline_len + 12; if (vmsize != -1 && win_width >= (statusline_len + 30)) { int str_x = win_width - strlen(timestamp) - 30; char *vmsize_str = amount_to_str(vmsize); mvwprintw(status -> win, 0, str_x, "%6s (VMsize) %5d (PID) - %s", vmsize_str, cur -> pid, timestamp); myfree(vmsize_str); total_info_len = statusline_len + 30; } else { if (cur -> last_exit_rc != 0) { mvwprintw(status -> win, 0, win_width - strlen(timestamp) - 26, "Last rc: %d, %5d (PID) - %s", WEXITSTATUS(cur -> last_exit_rc), cur -> pid, timestamp); } else mvwprintw(status -> win, 0, win_width - strlen(timestamp) - 12, "%5d (PID) - %s", cur -> pid, timestamp); } } else if (fsize == -1) { if (cur -> wt == WT_STDIN || cur -> wt == WT_SOCKET) mvwprintw(status -> win, 0, win_width - strlen(timestamp), "%s", timestamp); else { mvwprintw(status -> win, 0, win_width - strlen(timestamp) - 6, "??? - %s", timestamp); total_info_len = statusline_len + 6; } } else { int cur_len = 0; if (fsize < cur -> last_size) add_markerline(win_nr, cur, MARKER_MSG, " file got truncated"); cur -> last_size = fsize; if (afs) { char *filesize = amount_to_str(fsize); cur_len = 3 + strlen(filesize); mvwprintw(status -> win, 0, win_width - (strlen(timestamp) + cur_len), "%s - %s", filesize, timestamp); myfree(filesize); } else { cur_len = 13; /* Accomodate for both 32-bit and 64-bit off_t. */ mvwprintw(status -> win, 0, win_width - (strlen(timestamp) + cur_len), "%10lld - %s", (long long)fsize, timestamp); } total_info_len = statusline_len + cur_len; } } if (show_f1) { if (use_colors) color_on(status, find_colorpair(COLOR_YELLOW, -1, 0)); if (win_width >= (total_info_len + 32)) mvwprintw(status -> win, 0, help_str_offset, " *Press F1/+ for help* "); else if (win_width >= (total_info_len + 21)) mvwprintw(status -> win, 0, help_str_offset, " F1/+: help "); else if (win_width >= (total_info_len + 13)) mvwprintw(status -> win, 0, help_str_offset, " F1/^h: help "); if (use_colors) color_off(status, find_colorpair(COLOR_YELLOW, -1, 0)); } myattr_off(status, attrs); update_panels(); } } void create_window_set(int startx, int width, int nwindows, int indexoffset) { int loop; int term_offset = 0; int n_not_hidden = 0; int n_height_not_set = 0; int n_lines_requested = 0, n_with_nlines_request = 0; int n_lines_available = 0; int n_lines_available_per_win = 0; int reserved_lines = mode_statusline >= 0?1:0; int *lines_per_win = (int *)mymalloc(nwindows * sizeof(int)); set_signal(SIGWINCH, SIG_IGN, "SIGWINCH"); memset(lines_per_win, 0x00, sizeof(int) * nwindows); /* count the number of windows that are not hidden */ for(loop=0; loop= nfd) break; if (pi[cur_win_index].hidden == 0) n_not_hidden++; } if (n_not_hidden == 0) { free(lines_per_win); return; } /* count the number of lines needed by the windows for which * the height is explicitly set * also count the number of windows with this request set */ for(loop=0; loop= nfd) break; if (pi[cur_win_index].hidden == 0 && pi[cur_win_index].win_height != -1) { n_lines_requested += (pi[cur_win_index].win_height + reserved_lines); n_with_nlines_request++; } } /* fill in the array with heights */ /* not enough lines? simple fill-in */ n_lines_available = max_y; n_height_not_set = n_not_hidden - n_with_nlines_request; if (n_height_not_set > 0) n_lines_available_per_win = (max_y - n_lines_requested) / n_height_not_set; if (n_height_not_set > 0 && n_lines_available_per_win < 3) { int lost_lines; int n_windows_that_will_fit; n_lines_available_per_win = max(3, max_y / n_not_hidden); n_windows_that_will_fit = max_y / n_lines_available_per_win; lost_lines = max_y - (n_lines_available_per_win * n_windows_that_will_fit); for(loop=0; loop= nfd) break; if (pi[cur_win_index].hidden == 0) { lines_per_win[loop] = n_lines_available_per_win - reserved_lines; if (lost_lines) { lines_per_win[loop]++; lost_lines--; } n_lines_available -= n_lines_available_per_win; if (n_lines_available < n_lines_available_per_win) break; } } } else /* enough lines */ { int lost_lines = (max_y - n_lines_requested) - (n_lines_available_per_win * (n_not_hidden - n_with_nlines_request)); /* not enough lines available? then ignore all requests */ for(loop=0; loop= nfd) break; if (!pi[cur_win_index].hidden) { if (pi[cur_win_index].win_height != -1) { lines_per_win[loop] = pi[cur_win_index].win_height; n_lines_available -= (pi[cur_win_index].win_height + reserved_lines); } else { lines_per_win[loop] = n_lines_available_per_win - reserved_lines; if (lost_lines) { lines_per_win[loop]++; lost_lines--; } n_lines_available -= (n_lines_available_per_win + reserved_lines); } } } } /* re-create windows */ for(loop=0; loop= nfd) break; if (pi[cur_index].hidden) continue; if (lines_per_win[loop] == 0) continue; /* create data window, clear en set scroll ok */ if (statusline_above_data) pi[cur_index].data = mynewwin(lines_per_win[loop], width, term_offset + 1, startx); else pi[cur_index].data = mynewwin(lines_per_win[loop], width, term_offset, startx); bottom_panel(pi[cur_index].data -> pwin); werase(pi[cur_index].data -> win); scrollok(pi[cur_index].data -> win, TRUE);/* supposed to always return OK, according to the manpage */ /* create status window */ if (mode_statusline >= 0) { if (statusline_above_data) pi[cur_index].status = mynewwin(1, width, term_offset, startx); else pi[cur_index].status = mynewwin(1, width, term_offset + lines_per_win[loop], startx); bottom_panel(pi[cur_index].status -> pwin); werase(pi[cur_index].status -> win); /* create status-line */ update_statusline(pi[cur_index].status, cur_index, &pi[cur_index]); } term_offset += lines_per_win[loop] + (mode_statusline >= 0?1:0); /* display old strings */ for(redraw_loop=max(0, lb[cur_index].curpos - lines_per_win[loop]); redraw_loop win); wprintw(menu_win -> win, "%s", version_str); wprintw(menu_win -> win, "\n\n"); wprintw(menu_win -> win, "%s\n", F1); } else { if (split > 0 && nfd > 1 && n_not_hidden >= split) { int cols_per_col = max_x / split; int win_per_col = nfd / split; int x = 0, indexoffset = 0; int *vs = (int *)mymalloc(sizeof(int) * split); int *vn = (int *)mymalloc(sizeof(int) * split); if (vertical_split) { int cols_in_use = 0; int cols_set = 0; int other_cols_size; for(loop=0; loop= 4) { vs[loop] = vertical_split[loop]; cols_in_use += vertical_split[loop]; cols_set++; } } /* determine width of other (=not set) columns */ if (cols_set < split) { other_cols_size = (max_x - cols_in_use) / (split - cols_set); /* doesn't fit? */ if (other_cols_size < 4) { /* then use the predetermined size */ for(loop=0; loop 0) { vn[loop] = n_win_per_col[loop]; win_in_use += n_win_per_col[loop]; win_set++; } } if (win_set < split) { other_win_n = (nfd - win_in_use) / (split - win_set); if (other_win_n == 0) { for(loop=0; loop 0) splitlines = (NEWWIN **)mymalloc(sizeof(NEWWIN *) * n_splitlines); } for(loop=0; loop pwin); myattr_on(splitlines[loop], sl_attrs); draw_line(splitlines[loop], LINE_LEFT); myattr_off(splitlines[loop], sl_attrs); } else { create_window_set(x, vs[loop], vn[loop], indexoffset); } x += vs[loop]; indexoffset += vn[loop]; } } myfree(vn); myfree(vs); } else if (nfd != 0) { create_window_set(0, max_x, nfd, 0); } } mydoupdate(); } void do_set_bufferstart(int f_index, char store_what_lines, int maxnlines) { lb[f_index].bufferwhat = store_what_lines; if (maxnlines < lb[f_index].maxnlines) { delete_be_in_buffer(&lb[f_index]); lb[f_index].curpos = 0; } lb[f_index].maxnlines = maxnlines; } char close_window(int winnr, proginfo *cur, mybool_t stop_proc) { if (winnr == terminal_main_index) { terminal_index = NULL; terminal_main_index = -1; } /* make sure it is really gone */ if (stop_proc) stop_process(cur -> pid); /* restart window? */ if (cur -> restart.restart >= 0) { int initial_n_lines_tail; /* close old fds */ if (myclose(cur -> fd) == -1) error_exit(TRUE, FALSE, "Closing file descriptor of read-end of pipe failed.\n"); if (cur -> fd != cur -> wfd) { if (myclose(cur -> wfd) == -1) error_exit(TRUE, FALSE, "Closing file descriptor of write-end of pipe failed.\n"); } /* do diff */ if (cur -> restart.do_diff) { generate_diff(winnr, cur); /* update statusline */ update_statusline(pi[winnr].status, winnr, cur); } if (cur -> initial_n_lines_tail == -1) initial_n_lines_tail = max_y / (nfd + 1); else initial_n_lines_tail = cur -> initial_n_lines_tail; /* restart tail-process */ if (start_proc(cur, initial_n_lines_tail) != -1) { cur -> restart.is_restarted = 1; return -1; } /* ...and if that fails, go on with the closing */ } if (warn_closed) { NEWWIN *mywin; int subwinnr = 0; proginfo *dummy = &pi[winnr]; int win_width = 18 + 4; int win_name_len = strlen(cur -> filename); while(dummy -> next) { subwinnr++; dummy = dummy -> next; } if (win_name_len > win_width && (win_name_len + 4) < max_x) win_width = win_name_len; mywin = create_popup(5, win_width); color_on(mywin, 1); mvwprintw(mywin -> win, 1, 2, "Window %d/%d closed", winnr, subwinnr); mvwprintw(mywin -> win, 2, 2, "%s", cur -> filename); mvwprintw(mywin -> win, 3, 2, "Exit code: %d", WEXITSTATUS(cur -> last_exit_rc)); draw_border(mywin); color_off(mywin, 1); wmove(mywin -> win, 1, 2); mydoupdate(); /* wait_for_keypress(HELP_WINDOW_CLOSED, 0); */ (void)getch(); delete_popup(mywin); } if (do_not_close_closed_windows) { cur -> closed = 1; add_markerline(winnr, cur, MARKER_MSG, " end of file reached"); return 127; } else { /* file no longer exists (or so) */ return delete_entry(winnr, cur); } } void do_restart_window(proginfo *cur) { /* stop process */ stop_process(cur -> pid); /* close pipes */ myclose(cur -> fd); myclose(cur -> wfd); /* re-start tail */ cur -> restart.first = 1; if (start_proc(cur, 1) == -1) error_exit(FALSE, FALSE, "Failed to start process %s.\n", cur -> filename); } char * key_to_keybinding(char what) { static char buffer[3]; buffer[1] = buffer[2] = 0x00; if (what < 32) { buffer[0] = '^'; buffer[1] = 'a' + what - 1; } else { buffer[0] = what; } return buffer; } void write_escape_str(FILE *fh, char *string) { int loop, len = strlen(string); fprintf(fh, "\""); for(loop=0; loop 0) { if (what.colorpair_index == -1) fprintf(fh, ",,"); else fprintf(fh, ","); fprintf(fh, "%s", attr_to_str(what.attrs)); } } void add_pars_per_file(char *re, char *colorscheme, int n_buffer_lines, int buffer_bytes, char change_win_marker, int fs, int es, char *conversion_scheme) { int loop; for(loop=0; loop 0) { chosen = (lb[loop].be)[lb[loop].curpos - 1].pi; /* chosen = lb[loop].pi[lb[loop].curpos - 1]; */ if (IS_MARKERLINE(chosen)) chosen = NULL; } if (!chosen) chosen = &pi[loop]; update_statusline(pi[loop].status, loop, chosen); } } int exec_bind(char key) { int executed = 0; int loop; for(loop=0; loop wt == WT_COMMAND || cur -> wt == WT_STDIN || cur -> wt == WT_SOCKET || cur -> check_interval != 0 || cur -> retry_open) { strncpy(real_fname, cur -> filename, pm); real_fname[pm - 1] = 0x00; } else { if (!realpath(cur -> filename, real_fname)) error_exit(TRUE, FALSE, "Problem obtaining complete (real) path of file %s.\n", cur -> filename); } /* check if any default parameters are given in the configfile */ for(ppf_index=0; ppf_index pre, &cur -> n_re); } for(es_loop=0; es_loop pstrip, &cur -> n_strip); } /* set default colorscheme if not already given */ if (ppf[ppf_index].n_colorschemes > 0 && (get_iat_size(&cur -> cdef.color_schemes) == 0 && cur -> wt != WT_COMMAND && (cur -> cdef.colorize == 'n' || cur -> cdef.colorize == 0))) { for(c_nr=0; c_nr cdef.color_schemes, cs_index); } cur -> cdef.colorize = 'S'; } for(c_nr=0; c_nr conversions, ppf[ppf_index].conversion_schemes[c_nr]); if (ppf[ppf_index].buffer_maxnlines != -1) maxnlines = ppf[ppf_index].buffer_maxnlines; if (ppf[ppf_index].buffer_bytes != -1) { maxbytes = ppf[ppf_index].buffer_bytes; } } /* we should inform the user of any errors while executing * the regexp! */ else { char *error = convert_regexp_error(rc, &ppf[ppf_index].regex); if (error) error_popup("Set default parameters", -1, "Execution of regular expression failed with error:\n%s\n", error); } } cur = cur -> next; } while(cur); if (pi_index != -1) { if (lb[pi_index].maxnlines == -1 && lb[pi_index].maxbytes == -1) { if (maxbytes) { lb[pi_index].maxbytes = maxbytes; lb[pi_index].maxnlines = 0; } else { lb[pi_index].maxnlines = maxnlines; lb[pi_index].maxbytes = 0; } } else if (lb[pi_index].maxnlines == -1) { lb[pi_index].maxnlines = maxnlines; } else /* if (lb[pi_index].maxbytes == -1) */ { lb[pi_index].maxbytes = maxbytes; } } myfree(real_fname); } void set_default_parameters_if_not_given(void) { int pi_index; for(pi_index=0; pi_index statistics.start_ts = time(NULL); if (cur -> wt == WT_STDIN) { int old_0 = mydup(0); if (old_0 == -1) error_exit(TRUE, FALSE, "Cannot dup(0).\n"); if (myclose(0) == -1) error_exit(TRUE, FALSE, "Error closing fd 0.\n"); if (myopen("/dev/tty", O_RDONLY) != 0) error_exit(TRUE, FALSE, "New fd != 0\n"); cur -> fd = old_0; cur -> wfd = -1; cur -> pid = -1; } else if (cur -> wt == WT_SOCKET) { char *dummy = mystrdup(cur -> filename); char *colon = strchr(dummy, ':'); struct addrinfo hints; struct addrinfo* result; struct addrinfo* rp; int sfd = -1, s; char *host = NULL; char *service = "syslog"; if (colon) { service = colon + 1; *colon = 0x00; if (colon > dummy) host = dummy; } memset(&hints, 0x00, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; hints.ai_flags = AI_PASSIVE; hints.ai_protocol = 0; hints.ai_canonname = NULL; hints.ai_addr = NULL; hints.ai_next = NULL; s = getaddrinfo(host, service, &hints, &result); if (s != 0) error_exit(TRUE, FALSE, "Failed to create socket for receiving syslog data on %s: %s.\n", cur -> filename, gai_strerror(s)); for (rp = result; rp != NULL; rp = rp -> ai_next) { sfd = socket(rp -> ai_family, rp -> ai_socktype, rp -> ai_protocol); if (sfd == -1) continue; if (bind(sfd, rp -> ai_addr, rp -> ai_addrlen) == 0) break; close(sfd); } freeaddrinfo(result); if (rp == NULL) error_exit(FALSE, FALSE, "Failed to create socket for receiving syslog data on %s.\n", cur -> filename); cur -> wfd = cur -> fd = sfd; cur -> wfd = cur -> fd = socket(AF_INET, SOCK_DGRAM, 0); if (cur -> fd == -1) myfree(dummy); cur -> pid = -1; } else { if (cur -> initial_n_lines_tail == -1) cur -> initial_n_lines_tail = cur_win_size; /* start the tail process for this file/command */ if (start_proc(cur, cur -> initial_n_lines_tail) == -1) error_exit(FALSE, FALSE, "Failed to start process for %s.\n", cur -> filename); } cur = cur -> next; } while(cur); } } void version(void) { printf("%s", version_str); printf("\n\nThank you for using MultiTail.\n"); printf("If you have any suggestion on how I can improve this program,\n"); printf("do not hesitate to contact me at folkert@vanheusden.com\n"); printf("Website is available at: http://www.vanheusden.com/multitail/\n\n\n"); } void init_check_for_mail(void) { if (check_for_mail > 0 && mail_spool_file != NULL) { if (stat64(mail_spool_file, &msf_info) == -1) { check_for_mail = 0; printf("Could not determine size of file '%s' (which is supposed to be ", mail_spool_file); printf("your mailfile): mail-check is disabled.\n"); printf("You can prevent this message by adding the line 'check_mail:0' "); printf("in "CONFIG_FILE" or in .multitailrc in your home-directory.\n\n"); printf("Press enter to continue..."); fflush(NULL); getchar(); } else { msf_prev_size = msf_info.st_size; msf_last_check = get_ts(); } } } void main_loop(void) { /* create windows */ set_do_refresh(2); for(;;) { int c = wait_for_keypress(HELP_MAIN_MENU, 0, NULL, 0); if (terminal_main_index == -1) { int uc = toupper(c); set_do_refresh(1); if (mail) { mail = 0; redraw_statuslines(); } if (c == -1) { set_do_refresh(2); continue; } else if (uc == 'Q' || uc == 'X') { break; } else if (c == 'Z') /* clear all marker lines */ { delete_all_markerlines(); set_do_refresh(2); continue; } else if (uc == 'A') { if (add_window()) set_do_refresh(2); continue; } else if (uc == 'H' || c == 8) { show_help(HELP_MAIN_MENU); continue; } else if (c == 'r' || c == 12) { set_do_refresh(2); continue; } else if (c == 'R') { restart_window(); continue; } else if (c == 'i') { info(); continue; } else if (c == 't') { statistics_menu(); continue; } else if (c == 'T') { truncate_file(); continue; } else if (uc == 'J') { if (set_window_sizes()) set_do_refresh(2); continue; } else if (uc == 'L') { list_keybindings(); continue; } if (nfd == 0) { wrong_key(); set_do_refresh(0); continue; } else if (uc == 'E' || uc == '\\') { if (edit_regexp()) set_do_refresh(2); } else if (uc == 'F') { if (edit_strippers()) set_do_refresh(2); } else if (uc == 'D') { if (delete_window()) set_do_refresh(2); continue; } else if (uc == 'V') { if (toggle_vertical_split()) set_do_refresh(2); } else if (c == 'c') { if (toggle_colors()) set_do_refresh(2); } else if (c == 'C' && use_colors) { if (can_change_color()) (void)color_management(NULL, NULL); else error_popup("Edit colors", HELP_CANNOT_EDIT_COLOR, "Your terminal doesn't support editing of colors."); } else if (uc == 'S') { if (swap_window()) set_do_refresh(2); continue; } else if (c == 'z') { if (hide_window()) set_do_refresh(2); } else if (c == 'u') { if (hide_all_but()) set_do_refresh(2); } else if (c == 'U') { if (unhide_all_windows()) set_do_refresh(2); } else if (uc == 'G') { screendump(); } else if (uc == 'W') { write_script(); } else if (uc == 'M') { set_buffering(); } else if (c == 'b') { scrollback(); } else if (c == 'B') { merged_scrollback_with_search(NULL, MY_FALSE); } else if (c == 'p') { do_pause(); } else if (c == 'P') /* pause an individual screen */ { selective_pause(); } else if (uc >= '0' && uc <= '9') { int index = uc - '0'; if (index < nfd) add_markerline(index, NULL, MARKER_REGULAR, NULL); else wrong_key(); } else if (uc == 13) { int loop; for(loop=0; loop s_port; cp.n_def = cp.size = 0; cp.fg_color = cp.bg_color = NULL; /* removed because if gives regexp compilation problems for some * LANG=... environment variables settings * setlocale(LC_ALL, ""); */ time(&mt_started); set_signal(SIGTERM, signal_handler, "SIGTERM"); set_signal(SIGINT, signal_handler, "SIGINT" ); set_signal(SIGHUP, signal_handler, "SIGHUP" ); set_signal(SIGCHLD, signal_handler, "SIGCHLD"); shell = mystrdup("/bin/sh"); tail = mystrdup("tail"); ts_format = mystrdup("%b %d %H:%M:%S"); window_number = mystrdup("[%02d]"); cnv_ts_format = mystrdup("%b %d %H:%M:%S %Y"); line_ts_format = mystrdup("%b %d %H:%M:%S %Y "); subwindow_number = mystrdup("[%02d]"); statusline_ts_format = mystrdup("%b %d %H:%M:%S %Y"); syslog_ts_format = mystrdup("%b %d %H:%M:%S %Y"); mail_spool_file = getenv("MAIL"); if (dummy) check_for_mail = atoi(dummy); /* calc. buffer length (at least a complete terminal screen) */ initscr(); min_n_bufferlines = max(MIN_N_BUFFERLINES, LINES); if (has_colors()) { start_color(); use_default_colors(); use_colors = 1; } init_colors(); init_colornames(); /* start curses library */ init_ncurses(); /* verify size of terminal window */ if (LINES < 23 || COLS < 78) error_popup("Terminal too small", -1, "Your terminal(-window) is %dx%d. That is too small\nfor MultiTail: at least 78x23 is required.\nSome menus will look garbled!", COLS, LINES); /* default parameters for the statusline */ statusline_attrs.colorpair_index = find_or_init_colorpair(COLOR_WHITE, COLOR_BLACK, 0); statusline_attrs.attrs = A_REVERSE; endwin(); do_commandline(argc, argv); cmdfile_h.history = search_h.history = NULL; load_configfile(config_file); init_history(&search_h); init_history(&cmdfile_h); if (defaultcscheme) { default_color_scheme = find_colorscheme(defaultcscheme); if (default_color_scheme == -1) error_exit(FALSE, FALSE, "Default color scheme '%s' is not defined. Please check the MultiTail configuration file %s.\n", defaultcscheme, config_file); } if (markerline_attrs.colorpair_index == -1 && markerline_attrs.attrs == -1) markerline_attrs = find_attr(COLOR_RED, COLOR_BLACK, A_REVERSE); init_children_reaper(); /* initialise mail-stuff */ init_check_for_mail(); /* it seems that on at least FreeBSD ncurses forgets all defined * colorpairs after an endwin() * so here were explicitly re-setting them */ for(loop=0; loop wt == WT_FILE) { int pipefd[2]; stop_process(cur -> pid); if (myclose(cur -> fd) == -1) error_exit(TRUE, FALSE, "Closing read filedescriptor failed.\n"); if (cur -> fd != cur -> wfd) { if (myclose(cur -> wfd) == -1) error_exit(TRUE, FALSE, "Closing write filedescriptor failed.\n"); } /* create a pipe, will be to child-process */ if (-1 == pipe(pipefd)) error_exit(TRUE, FALSE, "Error while creating pipe.\n"); cur -> pid = start_tail(cur -> filename, cur -> retry_open, cur -> follow_filename, min_n_bufferlines, pipefd); cur -> fd = pipefd[0]; cur -> wfd = pipefd[1]; } cur = cur -> next; } while(cur); } } int check_for_died_processes(void) { int a_window_got_closed = 0; for(;;) { int f_index; char found = 0; /* see if any processed stopped */ int exit_code = 0; pid_t died_proc = waitpid(-1, &exit_code, WNOHANG | WUNTRACED); if (died_proc == 0) { break; } else if (died_proc == -1) { if (errno == ECHILD) break; error_exit(TRUE, FALSE, "waitpid failed.\n"); } /* now check every window for died processes */ for(f_index=0; f_index pid && cur -> wt != WT_STDIN && cur -> wt != WT_SOCKET) { int refresh_info = close_window(f_index, cur, MY_FALSE); found = a_window_got_closed = 1; /* is an entry deleted? (>=0) or restarted? (==-1) */ if (refresh_info >= 0) { set_do_refresh(2); } else if (cur -> restart.do_diff && get_do_refresh() == 0) { if (get_do_refresh() != 2) set_do_refresh(1); } break; } cur = cur -> next; } while(cur); } /* check child processes */ if (!found) { int loop; for(loop=0; loop 0) memmove(&children_list[loop], &children_list[loop + 1], sizeof(pid_t) * n_pids_to_move); n_children--; break; } } } } return a_window_got_closed; } int find_window(char *filename, int *index, proginfo **cur) { int loop; for(loop=0; loop filename, filename) == 0) { if (index) *index = loop; if (cur) *cur = pc; return 0; } pc = pc -> next; } while(pc); } return -1; } void create_new_win(proginfo **cur, int *nr) { int loop; proginfo *newpi = (proginfo *)myrealloc(pi, (nfd + 1) * sizeof(proginfo)); lb = (buffer *)myrealloc(lb, (nfd + 1) * sizeof(buffer)); /* 'lb' has pointers to 'pi'-structures. now since we've realloced the pi- * array, we need to update to pointers to pi in the lb-array */ for(loop=0; loop= cdg[loop].check_interval) { int fi = 0; glob_t files = { 0 }; /* get list of files that match */ if (glob(cdg[loop].glob_str, GLOB_ERR | GLOB_NOSORT, NULL, &files) != 0) continue; /* check each found file */ for(fi=0; fi= cdg[loop].last_check || last_mod == 0) && find_window(files.gl_pathv[fi], NULL, NULL) == -1 && S_ISREG(ftype) && ((cdg[loop].new_only && last_mod >= mt_started) || !cdg[loop].new_only) ) { proginfo *cur = NULL; int win_nr = -1; /* create new structure containing all info and such */ if (!cdg[loop].in_one_window) { create_new_win(&cur, &win_nr); } else if (cdg[loop].window_nr == -1) { create_new_win(&cur, &cdg[loop].window_nr); win_nr = cdg[loop].window_nr; } else { cur = &pi[cdg[loop].window_nr]; /* skip to end of chain */ while (cur -> next) cur = cur -> next; /* allocate new entry */ cur -> next = (proginfo *)calloc(1, sizeof(proginfo)); cur = cur -> next; } /* fill in */ cur -> wt = WT_FILE; cur -> label = mystrdup(cdg[loop].label ? cdg[loop].label : ""); cur -> filename = mystrdup(files.gl_pathv[fi]); cur -> line_wrap = default_linewrap; cur -> win_height = -1; cur -> statistics.sccfirst = 1; set_default_parameters_if_not_given_do(cur, win_nr); if (cdg[loop].color_scheme) { int cs_index = find_colorscheme(cdg[loop].color_scheme); add_color_scheme(&cur -> cdef.color_schemes, cs_index); cur -> cdef.colorize = 'S'; } /* start the tail process for this file/command */ if (start_proc(cur, max_y / nfd) == -1) error_exit(FALSE, FALSE, "Failed to start tail process for %s.\n", cur -> filename); if (cur -> pid < 2) error_exit(FALSE, FALSE, "pid is %d, %d\n", cur -> pid, cur -> wt); new_wins = 1; } } /* check all matched files */ cdg[loop].last_check = now; globfree(&files); } /* time for a check? */ } /* check all search patterns */ return new_wins; } void resize_terminal_do(NEWWIN *popup) { determine_terminal_size(&max_y, &max_x); if (ERR == resizeterm(max_y, max_x)) error_exit(FALSE, FALSE, "An error occured while resizing terminal(-window).\n"); endwin(); refresh(); /* <- as specified by ncurses faq, was: doupdate(); */ create_windows(); if (popup) { if ((popup -> x_off + popup -> width) > max_x || (popup -> y_off + popup -> height) > max_y) { popup -> x_off = max(max_x - (popup -> width + 1), 0); popup -> y_off = max(max_y - (popup -> height + 1), 0); move_panel(popup -> pwin, popup -> y_off, popup -> x_off); mydoupdate(); } } } int process_global_keys(int what_help, NEWWIN *popup, char cursor_shift) { int c = getch(); if (c == KEY_RESIZE) return -1; redraw_statuslines(); if (c == KEY_F(5)) { resize_terminal_do(popup); set_do_refresh(2); } else if (c == exit_key) /* ^C */ { do_exit(); error_exit(FALSE, FALSE, "This should not be reached.\n"); } else if (c == 8 || c == KEY_F(1)) /* HELP (^h / F1) */ { show_help(what_help); } else if (popup != NULL && ((cursor_shift == 0 && c == KEY_LEFT) || (cursor_shift == 1 && c == KEY_SLEFT))) { if (popup -> x_off > 0) { popup -> x_off--; move_panel(popup -> pwin, popup -> y_off, popup -> x_off); mydoupdate(); } else wrong_key(); } else if (cursor_shift == 0 && c == KEY_UP) { if (popup != NULL && popup -> y_off > 0) { popup -> y_off--; move_panel(popup -> pwin, popup -> y_off, popup -> x_off); mydoupdate(); } else wrong_key(); } else if (popup != NULL && ((cursor_shift == 0 && c == KEY_RIGHT) || (cursor_shift == 1 && c == KEY_SRIGHT))) { if ((popup -> x_off + popup -> width) < max_x) { popup -> x_off++; move_panel(popup -> pwin, popup -> y_off, popup -> x_off); mydoupdate(); } else wrong_key(); } else if (cursor_shift == 0 && c == KEY_DOWN) { if (popup != NULL && (popup -> y_off + popup -> height) < max_y) { popup -> y_off++; move_panel(popup -> pwin, popup -> y_off, popup -> x_off); mydoupdate(); } else wrong_key(); } return c; } char process_input_data(int win_nr, proginfo *cur, char *data_in, int new_data_offset, int n_bytes_added, double now) { char *pnt = data_in; char statusline_update_needed = 0; /* statistics */ cur -> statistics.bytes_processed += n_bytes_added; data_in[new_data_offset + n_bytes_added] = 0x00; if (strchr(&data_in[new_data_offset], '\n')) { if (cur -> cont) /* reconnect lines with '\' */ { char *contsearch = pnt; while((contsearch = strstr(contsearch, "\\\n"))) { memmove(contsearch, contsearch + 2, strlen(contsearch + 2) + 1); } } if (cur -> restart.is_restarted && cur -> restart.restart_clear) { delete_be_in_buffer(&lb[win_nr]); cur -> restart.is_restarted = 0; werase(pi[win_nr].data -> win); } update_panels(); /* display lines */ for(;*pnt;) { char *end = strchr(pnt, '\n'); if (!end) break; *end = 0x00; /* redirect to some other file/pipe? */ redirect(cur, pnt, (int)(end - pnt), MY_FALSE); /* gen stats */ store_statistics(cur, now); /* see if we need to beep already */ if (beep_interval > 0) { if (++linecounter_for_beep == beep_interval) { linecounter_for_beep = 0; did_n_beeps++; beep(); } } /* beep per (sub-)window */ if (cur -> beep.beep_interval > 0) { if (++cur -> beep.linecounter_for_beep == cur -> beep.beep_interval) { cur -> beep.linecounter_for_beep = 0; cur -> beep.did_n_beeps++; beep(); } } /* is this the output of a program which we should diff and such? */ if (cur -> restart.do_diff) { store_for_diff(&cur -> restart.diff, pnt); } else /* no, just output */ { if (get_do_refresh() == 0) set_do_refresh(1); /* after update interval, update screen */ statusline_update_needed |= emit_to_buffer_and_term(win_nr, cur, pnt); } pnt = end + 1; } } if (*pnt != 0x00) { int line_len = strlen(pnt) + 1; cur -> incomplete_line = mymalloc(line_len); memcpy(cur -> incomplete_line, pnt, line_len); } return statusline_update_needed; } int recv_from_fd(int fd, char **buffer, int new_data_offset, int read_size) { struct sockaddr sa; socklen_t ssa_len = sizeof(sa); char *recv_buffer = mymalloc(read_size + 1); char time_buffer[TIMESTAMP_EXTEND_BUFFER]; char *host = ""; char *facility = ""; char *severity = ""; int added_bytes; int nbytes; get_now_ts(syslog_ts_format, time_buffer, sizeof(time_buffer)); nbytes = recvfrom(fd, recv_buffer, read_size, 0, &sa, &ssa_len); recv_buffer[nbytes] = 0x00; if (resolv_ip_addresses == MY_TRUE) { struct sockaddr_in *sai = (struct sockaddr_in *)&sa; struct hostent *he = gethostbyaddr(&(sai -> sin_addr.s_addr), sizeof(sai -> sin_addr.s_addr), AF_INET); if (he != NULL && he -> h_name != NULL) host = he -> h_name; else host = "?"; } else { host = inet_ntoa(((struct sockaddr_in *)&sa) -> sin_addr); } if (show_severity_facility == MY_TRUE) { char *gt = strchr(recv_buffer, '>'); if (recv_buffer[0] == '<' && (recv_buffer[2] == '>' || recv_buffer[3] == '>' || recv_buffer[4] == '>') && gt != NULL) { int value = atoi(recv_buffer + 1); int severity_nr = value & 7; int facility_nr = value / 8; severity = severities[severity_nr]; if (facility_nr >= 24) facility = "?"; else facility = facilities[facility_nr]; memmove(recv_buffer, gt + 1, strlen(gt)); } } added_bytes = strlen(time_buffer) + 1 + strlen(host) + 1 + strlen(facility) + 1 + strlen(severity) + 1 + 1 + nbytes + 1; *buffer = myrealloc(*buffer, new_data_offset + added_bytes); nbytes = snprintf(&(*buffer)[new_data_offset], added_bytes, "%s%s%s%s%s%s%s%s %s\n", time_buffer, time_buffer[0]?" ":"", host, host[0] ?" ":"", facility, facility[0] ?" ":"", severity, severity[0] ?" ":"", recv_buffer); myfree(recv_buffer); return nbytes; } void get_markerline_sleep(double *const s, char *have_any) { int loop = 0; dtime_t now = get_ts(); *have_any = 0; for(loop=0; loop mark_interval) { double t_until_next = cur -> statistics.lastevent + cur -> mark_interval - now; *have_any = 1; if (t_until_next <= 0) { *s = 0; return; } *s = min(*s, t_until_next); } cur = cur -> next; } while(cur); } } int wait_for_keypress(int what_help, double max_wait, NEWWIN *popup, char cursor_shift) { int c = -1; dtime_t max_wait_start = get_ts(); for(;;) { int deleted_entry_in_array = -1; int last_fd = 0, rc, loop; fd_set rfds; struct timeval tv; dtime_t now = get_ts(); proginfo *last_changed_window = NULL; char prev_mail_status = mail; static double lastupdate = 0; double sleep = 32767.0; char any_markers = 0; /* need to check any paths? */ if (check_paths()) set_do_refresh(2); sleep = min(sleep, check_for_mail > 0 ? max((msf_last_check + check_for_mail) - now, 0) : 32767); sleep = min(sleep, heartbeat_interval > 0 ? max((heartbeat_t + heartbeat_interval) - now, 0) : 32767); sleep = min(sleep, max_wait > 0.0 ? max((max_wait_start + max_wait) - now, 0) : 32767); get_markerline_sleep(&sleep, &any_markers); for(loop=0; loop paused) continue; if (cur -> closed) continue; do { if (cur -> fd != -1) { FD_SET(cur -> fd, &rfds); last_fd = max(last_fd, cur -> fd); } cur = cur -> next; } while(cur); } /* update screen? */ if (get_do_refresh()) { if (get_do_refresh() == 2) create_windows(); if (now - lastupdate >= update_interval) { set_do_refresh(0); lastupdate = now; mydoupdate(); } } /* wait for any data or key press */ if ((rc = select(last_fd + 1, &rfds, NULL, NULL, tv.tv_sec != 32767 ? &tv : NULL)) == -1) { if (errno != EINTR && errno != EAGAIN) error_exit(TRUE, TRUE, "select() returned an error."); } now = get_ts(); if (heartbeat_interval > 0 && now - heartbeat_t >= heartbeat_interval) { heartbeat(); heartbeat_t = now; } /* check for mail */ if (now - msf_last_check >= check_for_mail) { do_check_for_mail(now); msf_last_check = now; } total_wakeups++; if (terminal_changed) { terminal_changed = 0; resize_terminal_do(popup); } if (max_wait != 0.0 && now - max_wait_start >= max_wait) break; if (got_sigusr1) { got_sigusr1 = 0; sigusr1_restart_tails(); } if (any_markers) { for(loop=0; loop paused || cur -> closed) continue; do { if (cur -> mark_interval && now - cur -> statistics.lastevent >= cur -> mark_interval) { if (cur -> statistics.lastevent) { add_markerline(loop, cur, MARKER_IDLE, NULL); if (get_do_refresh() == 0) set_do_refresh(1); } cur -> statistics.lastevent = now; } cur = cur -> next; } while(cur); } } if (rc > 0) { /* any key pressed? */ if (FD_ISSET(0, &rfds)) { mail = 0; c = process_global_keys(what_help, popup, cursor_shift); if (c != -1) break; } /* go through all fds */ for(loop=0; loop paused) continue; if (cur -> closed) continue; do { if (cur -> fd != -1 && FD_ISSET(cur -> fd, &rfds)) { char *buffer = NULL; int read_size = min(SSIZE_MAX, 65536); int nbytes, new_data_offset = cur -> incomplete_line ? strlen(cur -> incomplete_line) : 0; buffer = myrealloc(cur -> incomplete_line, new_data_offset + read_size + 1); cur -> incomplete_line = NULL; if (cur -> wt == WT_SOCKET) { nbytes = recv_from_fd(cur -> fd, &buffer, new_data_offset, read_size); } else { nbytes = read(cur -> fd, &buffer[new_data_offset], read_size); } /* read error or EOF? */ if ((nbytes == -1 && errno != EINTR && errno != EAGAIN) || nbytes == 0) { if (nbytes == 0 && cur -> wt == WT_STDIN) { pi[loop].paused = 1; update_statusline(status, loop, cur); set_do_refresh(2); } else { if (last_changed_window == cur) last_changed_window = NULL; deleted_entry_in_array = close_window(loop, cur, MY_TRUE); if (deleted_entry_in_array >= 0) { set_do_refresh(2); free(buffer); goto closed_window; } else if (cur -> restart.do_diff && get_do_refresh() == 0) { if (get_do_refresh() != 2) set_do_refresh(1); } } } else if (nbytes != -1) /* if nbytes == -1 it must be an interrupt while READ */ { /* display statusline? */ if (process_input_data(loop, cur, buffer, new_data_offset, nbytes, now)) { update_statusline(status, loop, cur); } /* remember this window as it might be displayed in the GUI * terminal window */ last_changed_window = cur; } myfree(buffer); } /* close idle, if requested */ if (cur -> close_idle > 0 && now - cur -> statistics.lastevent > cur -> close_idle) { if (last_changed_window == cur) last_changed_window = NULL; deleted_entry_in_array = close_window(loop, cur, MY_TRUE); if (deleted_entry_in_array >= 0) { set_do_refresh(2); goto closed_window; } } cur = cur -> next; } while(cur); if (deleted_entry_in_array > 0) break; } } /* see if any processes have died (processes started * by matchin regular expressions) */ if (need_died_procs_check) { need_died_procs_check = 0; if (check_for_died_processes()) { c = -1; break; } } closed_window: /* any window changed? then we may want to update the terminal window header */ if ((last_changed_window != NULL || mail != prev_mail_status) && set_title != NULL) { prev_mail_status = mail; draw_gui_window_header(last_changed_window); } if (deleted_entry_in_array >= 0) { c = -1; break; } } return c; } multitail-7.1.5/mt.h000066400000000000000000000260071465663511400143410ustar00rootroot00000000000000#ifndef __MT_H__ #define __MT_H__ #define _LARGEFILE64_SOURCE /* required for GLIBC to enable stat64 and friends */ #include #include #include #include #include #include #define WAIT_FOR_FILE_DELAY (250) /* sleep when waiting for a file to become available */ #define MAX_N_RE_MATCHES (80) /* max. number of regex matches: used for coloring matches */ #define MIN_N_BUFFERLINES (100) /* number of lines to buffer at minimum */ #define MAX_N_SPAWNED_PROCESSES (16) /* max. nr. of processes executed by matching regexps */ #define MAX_N_COLUMNS (15) /* max number of columns */ #define DEFAULT_TAB_WIDTH (4) #define DEFAULT_COLORPAIRS (8) #define MAX_BACKTRACE_LENGTH (256) #define SCRIPT_IO_BUFFER_SIZE (4096) #define CONFIG_READ_BUFFER (4096) #define HISTORY_IO_BUFFER (4096) #define TIMESTAMP_EXTEND_BUFFER (1024) #define SL_REGULAR (1) #define SL_NONE (0) #define SL_ATTR (2) #define LOADAVG_STR_LEN (20) #define AMOUNT_STR_LEN (3 + 2 + 1) #ifndef __GNUC__ #define __PRETTY_FUNCTION__ "(unknown)" #define USE_IF_SET(x, y) ((x)?(x):(y)) #else #define USE_IF_SET(x, y) ((x)?:(y)) #endif typedef enum { MY_FALSE = 0, MY_TRUE = 1 } mybool_t; typedef enum { VAL_ZERO_POSITIVE = 1, VAL_POSITIVE_NOT_1, VAL_POSITIVE } valcheck_t; #define M_KB (1024) #define M_MB (M_KB * 1024) #define M_GB (M_MB * 1024) typedef enum { BEEP_FLASH = 1, BEEP_BEEP, BEEP_POPUP, BEEP_NONE } beeb_t; typedef enum { LINE_LEFT = 1, LINE_RIGHT, LINE_TOP, LINE_BOTTOM } linepos_t; typedef enum { TERM_IGNORE = 0, TERM_XTERM, TERM_ANSI /* or vt100 */} term_t; typedef enum { SCHEME_TYPE_EDIT = 0, SCHEME_TYPE_FILTER } filter_edit_scheme_t; #ifndef _BSD_SOURCE #define _BSD_SOURCE /* don't worry: it'll still work if you don't have a BSD system */ #endif #ifndef __USE_BSD #define __USE_BSD /* manpage says _BSD_SOURCE, stdlib.h says __USE_BSD */ #endif #if defined(UTF8_SUPPORT) && !defined(__APPLE__) #if defined(__FreeBSD__) || defined (__linux__) #include #include #else #include #include #endif #else #if defined(__APPLE__) #include #include #elif defined(sun) || defined(__sun) || defined(scoos) || defined(_HPUX_SOURCE) || defined(AIX) || defined(__CYGWIN__) #include #include #else #include #include #endif #endif /* it seems the default HP-UX c-compiler doesn't understand atoll and * strtoll while it does understand 'long long' */ #if defined(_HPUX_SOURCE) || defined(__APPLE__) #ifndef atoll #define atoll(x) atol(x) #endif #endif #ifndef strtoll #define strtoll(x, y, z) strtol(x, y, z) #endif #ifndef atoll #define atoll(a) strtoll((a), (char **)NULL, 10) #endif /* Tru64 workaround */ #if defined(OSF1) #undef getmaxyx #define getmaxyx(w,y,x) y = w->_maxy; x = w->_maxx #endif #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) || defined(__CYGWIN__) || defined(__minix) #define off64_t off_t #define stat64 stat #define open64 open #endif #define MARKER_REGULAR (NULL) #define MARKER_CHANGE ((proginfo *)-1) #define MARKER_IDLE ((proginfo *)-2) #define MARKER_MSG ((proginfo *)-3) #define IS_MARKERLINE(x) ((x) == MARKER_REGULAR || (x) == MARKER_CHANGE || (x) == MARKER_IDLE || (x) == MARKER_MSG) typedef enum { SEL_WIN = 1, SEL_SUBWIN, SEL_FILES, SEL_CSCHEME, SEL_HISTORY } selbox_type_t; typedef enum { TT_ATIME = 1, TT_MTIME, TT_CTIME } time_field_t; typedef double dtime_t; typedef struct { char *history_file; int history_size; char **history; } history_t; typedef struct { int *elements; int n; int size; } int_array_t; typedef struct { char *glob_str; int check_interval; dtime_t last_check; time_field_t new_only; const char *color_scheme; const char *label; char in_one_window; int window_nr; /* if 'in_one_window' is set, merge into the window 'window_nr' */ } check_dir_glob; typedef struct { WINDOW *win; PANEL *pwin; int x_off, y_off; int width, height; } NEWWIN; typedef struct { char *regex_str; regex_t regex; char invert_regex; char use_regex; int match_count; /* command to run if matches */ char *cmd; } re; typedef struct { char *fs_name; char *fs_desc; int n_re; re *pre; } filterscheme; typedef enum { STRIP_TYPE_REGEXP = 1, STRIP_TYPE_RANGE, STRIP_TYPE_COLUMN, STRIP_KEEP_SUBSTR } striptype_t; typedef struct { striptype_t type; regex_t regex; char *regex_str; int start, end; int col_nr; char *del; int match_count; } strip_t; typedef struct { char *es_name; char *es_desc; int n_strips; strip_t *strips; } editscheme; #define MAX_COLORS_PER_LINE (80) typedef struct { int *fg_color; int *bg_color; int size; /* COLOR_PAIRS */ int n_def; /* n defined, at least 1 as color_pair(0) is the default * terminal colors (white on black mostly) which also * cannot be changed */ } colorpairs; typedef struct { int colorpair_index; int attrs; } myattr_t; typedef enum { REDIRECTTO_NONE = 0, REDIRECTTO_PIPE_FILTERED = 1, REDIRECTTO_PIPE, REDIRECTTO_FILE_FILTERED, REDIRECTTO_FILE, REDIRECTTO_SOCKET_FILTERED, REDIRECTTO_SOCKET } redirecttype_t; typedef struct { char *redirect; redirecttype_t type; int fd; pid_t pid; struct sockaddr_in sai; int prio_fac; } redirect_t; typedef struct { dtime_t lastevent; double prev_deltat, total_deltat; double med; double dev; char sccfirst; double scc, sccu0, scclast, scct1, scct2, scct3; int n_events; dtime_t start_ts; long long int bytes_processed; } statistics_t; typedef struct { char colorize; char field_nr; char *field_del; int_array_t color_schemes; myattr_t attributes; char alt_col; myattr_t alt_col_cdev1, alt_col_cdev2; char syslog_noreverse; term_t term_emul; } cdef_t; typedef struct { char **bcur, **bprev; int ncur, nprev; } diff_t; typedef struct { int restart; char restart_clear; /* clear after each iteration? */ char is_restarted; char first; char do_diff; diff_t diff; } restart_t; typedef struct { char suppress_repeating_lines; char *last_line; int n_times_repeated; } repeatinglines_t; typedef enum { WT_FILE=1, WT_COMMAND, WT_STDIN, WT_SOCKET } windowtype_t; typedef struct _subwindow_ { char *filename; windowtype_t wt; int last_exit_rc; int n_runs; int check_interval; int fd; /* read */ int wfd; /* write */ pid_t pid; int n_redirect; redirect_t *predir; off64_t last_size; char cont; /* "re-connect" lines with \ at the end */ char add_timestamp; char *label; /* put in front of each line */ int_array_t conversions; char paused; char closed; char *incomplete_line; char *win_title; char line_wrap; int line_wrap_offset; int win_height; /* repeatingly start a program */ restart_t restart; int initial_n_lines_tail; int mark_interval; repeatinglines_t repeat; cdef_t cdef; char hidden; char follow_filename; char retry_open; int close_idle; statistics_t statistics; struct { int beep_interval; int linecounter_for_beep; int did_n_beeps; } beep; NEWWIN *status; NEWWIN *data; int n_re; re *pre; int n_strip; strip_t *pstrip; struct _subwindow_ *next; } proginfo; typedef struct { char *Bline; proginfo *pi; double ts; } buffered_entry; typedef struct { buffered_entry *be; int curpos; char bufferwhat; int maxnlines; int maxbytes, curbytes; proginfo *last_win; char marker_of_other_window; } buffer; typedef enum { CSREFLAG_SUB = 1, /* substring matching */ CSREFLAG_CMP_VAL_LESS, /* compare with value: value less then what is configured? */ CSREFLAG_CMP_VAL_BIGGER, /* compare with value: value higher then what is configured? */ CSREFLAG_CMP_VAL_EQUAL /* compare with value: value equal to what is configured? */ } csreflag_t; typedef struct { char *script; pid_t pid; int fd_r, fd_w; } script; typedef struct { myattr_t attrs1; myattr_t attrs2; mybool_t use_alternating_colors; int ac_index; } cse_main; typedef struct { cse_main cdef; regex_t regex; csreflag_t flags; double cmp_value; mybool_t merge_color; } color_scheme_entry; typedef struct { char *name; char *descr; script color_script; int n; color_scheme_entry *pentries; } color_scheme; typedef enum { CONVTYPE_IP4TOHOST = 1, CONVTYPE_EPOCHTODATE, CONVTYPE_ERRNO, CONVTYPE_HEXTODEC, CONVTYPE_DECTOHEX, CONVTYPE_TAI64NTODATE, CONVTYPE_SCRIPT, CONVTYPE_ABBRTOK, CONVTYPE_SIGNRTOSTRING } conversion_t; typedef struct { conversion_t type; regex_t regex; int match_count; } conversion_bundle_t; typedef struct { char *name; int n; conversion_bundle_t *pcb; /* conversion script */ script *pcs; } conversion; typedef enum { SCHEME_CONVERSION = 1, SCHEME_COLOR } scheme_t; typedef struct cv_off { int start, end; char *newstr; } cv_off; typedef struct { int n_colorschemes; char **colorschemes; int buffer_maxnlines; int buffer_bytes; char change_win_marker; regex_t regex; char *re_str; int_array_t filterschemes; int_array_t editschemes; int n_conversion_schemes; char **conversion_schemes; } pars_per_file; typedef struct { regoff_t start; regoff_t end; myattr_t attrs; mybool_t merge_color; } color_offset_in_line; typedef struct { char key; char *command; } keybinding; void do_exit(void); char * select_file(char *input, int what_help); char check_no_suppress_lines_filter(proginfo *cur); void color_print(int f_index, NEWWIN *win, proginfo *cur, char *string, regmatch_t *matches, int matching_regex, mybool_t force_to_winwidth, int start_at_offset, int end_at_offset, double ts, char show_window_nr); int select_window(int what_help, char *heading); char check_filter(proginfo *cur, char *string, regmatch_t **pmatch, char **error, int *matching_regex, char do_re, char *display); int wait_for_keypress(int what_help, double max_wait, NEWWIN *popup, char shift_cursor); int toggle_colors(void); void regexp_error_popup(int rc, regex_t *pre); void redraw_statuslines(void); void buffer_replace_pi_pointers(int f_index, proginfo *org, proginfo *new); char delete_entry(int f_index, proginfo *sub); char * key_to_keybinding(char what); void do_buffer(int f_index, proginfo *cur, char *string, char filter_match, double now); void do_print(int f_index, proginfo *cur, char *string, regmatch_t *matches, int matching_regex, double ts); void do_set_bufferstart(int f_index, char store_what_lines, int maxnlines); void emit_myattr_t(FILE *fh, myattr_t what); void update_statusline(NEWWIN *status, int win_nr, proginfo *cur); void write_escape_str(FILE *fh, char *string); void check_proc_sigh(int sig); void info(void); void statistics(void); int emit_to_buffer_and_term(int f_index, proginfo *cur, char *line); void add_pars_per_file(char *re, char *colorscheme, int n_buffer_lines, int buffer_bytes, char change_win_marker, int fs, int es, char *conversion_scheme); void version(void); void usage(void); void create_new_win(proginfo **cur, int *nr); void delete_be_in_buffer(buffer *pb); void do_restart_window(proginfo *cur); void LOG(char *str, ...); #endif multitail-7.1.5/multitail.1000066400000000000000000000315451465663511400156410ustar00rootroot00000000000000.\" Copyright Folkert van Heusden, 2007 .\" .\" This file may be copied under the conditions described .\" in the GNU GENERAL PUBLIC LICENSE, Version 1, September 1998 .\" that should have been distributed together with this file. .\" .TH MULTITAIL 1 2007-02 "MultiTail" .SH NAME MultiTail \- browse through several files at once .SH SYNOPSIS .BI "multitail [" options "] .sp options: .BI "[\-cs|\-Cs|\-c\-] [\-s] [\-i] inputfile [\-i anotherinputfile] [...]" .sp .SH DESCRIPTION The program .B MultiTail lets you view one or multiple files like the original tail program. The difference is that it creates multiple windows on your console (with ncurses). It can also monitor wildcards: if another file matching the wildcard has a more recent modification date, it will automatically switch to that file. That way you can, for example, monitor a complete directory of files. Merging of 2 or even more logfiles is possible. It can also use colors while displaying the logfiles (through regular expressions), for faster recognition of what is important and what not. It can also filter lines (again with regular expressions). It has interactive menus for editing given regular expressions and deleting and adding windows. One can also have windows with the output of shell scripts and other software. When viewing the output of external software, MultiTail can mimic the functionality of tools like 'watch' and such. When new mail arrives for the current user, the statuslines will become green. To reset this "mail has arrived"-state, press ' ' (a space). For help at any time, press F1. .PP .SH OPTIONS .TP .B "\-i" file Select a file to monitor. You can have multiple .BI "\-i " file parameters. You only need to add .BI "\-i " file in front of a filename if the filename starts with a dash ('-'). .TP .B "\-I" file Same as .BI "\-i " file but add the output to the previous window (so the output is merged). .TP .B "-iw" file interval .B "-Iw" file interval Like '-i'/'-I' but expects the parameter to be a wildcard and the second(!) an interval. Initially MultiTail will start monitoring the first file with the most recent modification time. Every interval it will check if any new files were created (or modified) and start tailing that one. *Don't forget* to put quotation marks around the filename as otherwhise the shell will try to substitute them! .TP .B "\-l" command Command to execute in a window. Parameter is the command. Do not forget to use "'s if the external command needs parameter! (e.g. -l "ping host"). .TP .B "\-L" command Same as .BI "\-l" but add the output to the previous window (so the output is merged). .TP .B "\-j" Read from stdin (can be used only once as there is only 1 stdin). .TP .B "\-J" Same as .BI "-j" but add the output to the previous window (so the output is merged). .TP .B "\-\-mergeall" Merge all of the following files into the same window (see '--no-mergeall'). .TP .B "\-\-no\-mergeall" Stop merging all files into one window (see '--mergeall'); .TP .B "\-\-no\-repeat" When the same line is repeated, it will be suppressed while printing a "Last message repeated x times" message. .TP .B "\-\-mark\-interval x" Print every 'x' seconds a mark-line when nothing else was printed. .TP .B "\-q i path" Check path for new files with interval 'i', all in new windows. One can enter paths here understood by the shell. E.g. "/tmp/*". Note: do not forget to add quotes around the pathname to prevent the shell from parsing it! .TP .B "\-Q i path" Like -q: but merge them all in one window. .TP .B "\-\-new\-only" For -q/-Q: only create windows for files created after MultiTail was started. .TP .B "\-\-closeidle x Close windows when more then 'x' seconds no new data was processed. .TP .B "\-a x" Write the output also to file 'x' (like 'tee') AFTER it was filtered by MultiTail. Note: you need to put "-a file" BEFORE to the file you're monitoring! .TP .B "\-A x" Write the output also to file 'x' (like 'tee') BEFORE it was filtered by MultiTail. Also see the note for "-a". .TP .B "-g x" Send the output also to command 'x' AFTER it was filtered by MultiTail. .TP .B "-G x" Send the output also to command 'x' BEFORE it was filtered by MultiTail. .TP .B "\-S" Prepend merged output with subwindow-number. .TP .B "\-t" title With this switch, "title" is displayed in the statusline instead of the filename or commandline. .TP .B "\-n" number_of_lines Number of lines to tail initially. The default depends on the size of the terminal-window. .TP .B "\-N" number_of_lines Like -n but this parameter will be used for all files/commands you tail after this parameter. .TP .B "\-r" interval Restart the command (started with -l/-L) after it has exited. With interval you can set how long to sleep before restarting. .TP .B "\-R" interval Restarts a command like -r only this one shows the difference in output compared to the previous run. .TP .B "\-rc" / "\-Rc" interval Like \-r / \-R but clears the window before each iteration. .TP .B "\-h" The help. .TP .B "\-f" Follow the following filename, not the descriptor. .TP .B "\--follow-all" For all files after this switch: follow the following filename, not the descriptor. .TP .B "\-fr" filter Use the predefined filter(s) from the configfile. .TP .B "\-e" Use the next regular expression on the following file. .TP .B "\-ex" Use regular expression on the following file and execute the command when it matches. The command gets as commandline parameter the whole matching line. .TP .B "\-eX" Like '-ex' but only give the matching substring as parameter. This requires a regular expression with '(' and ')'. .TP .B "\-ec" Use regular expression on the following file and display the matches. .TP .B "\-eC" Use regular expression on the following file but display everything and display the matches inverted. .TP .B "\-E" Use the next regular expression on the following files. .TP .B "\-v" Negate the next regular expression. .TP .B "\-s x" Splits the screen vertically in 'x' columns. .TP .B "\-sw x" At what position to split the screen. e.g. '-sw 20,40,,10' (=4 columns) .TP .B "\-sn x" How many windows per column for vertical split (use with -s or -sw). e.g. '-sn 3,,2'. .TP .B "\-wh x" Sets the height of a window (advisory: if it won't fit, the height is adjusted). .TP .B "\-cS" scheme Show the next given file using the colorscheme selected with 'scheme' (as defined in multitail.conf). .TP .B "\-CS" scheme Show all following files using the colorscheme selected with 'scheme' (as defined in multitail.conf). .TP .B "\-csn" Extra switch for the following switches; do not use reverse (inverted) colors. .TP .B "\-cs" Show the next given file in colors (syslog). .TP .B "\-c" Show the next given file in colors. .TP .B "\-Cs" Show all following files in color (through syslog-scheme). .TP .B "\-C" Show all following files in color. .TP .B "\-Cf field_index delimiter" Show all following files in color depending on field selected with field_index. Fields are delimited by the defined delimiter. .TP .B "\-cf field_index delimiter" Show the next file in color depending on field selected with field_index. Fields are delimited by the defined delimiter. .TP .B "\-ci color" Use a specific color. Useful when merging multiple outputs. .TP .B "\-cT terminalmode" Interpret terminal codes. Only ANSI supported at this time. .TP .B "\-c\-" Do NOT colorize the following file. .TP .B "\-C\-" Do NOT colorize the following files. .TP .B "\-ts" Add a timestamp to each line (format is configurable in multitail.conf). .TP .B "\-Z color" Specify the color-attributes for the markerline. .TP .B "\-T" A timestamp will be placed in the markerline. .TP .B "\-d" Do NOT update statusline. .TP .B "\-D" Do not display a statusline at all. .TP .B "\-du" Put the statusline above the data window. .TP .B "\-z" Do not display "window closed" windows. .TP .B "\-u" Set screen updateinterval (for slow links). .TP .B "\-m nlines" Set buffersize Set .BI "nlines" to 0 (zero) if you want no limits on the buffering. .TP .B "\-mb x" Set scrollback buffer size (in bytes, use xKB/MB/GB). .TP .B "\-M nlines" Set the buffersize on ALL following files. .TP .B "\-p x [y]" Set linewrap: a = print everything including linewrap. l = just show everything starting at the left until the rightside of the window is reached. r = show everything starting from the right of the line. s = show everything starting with the processname. S = show everything starting after the processname. o = show everything starting at offset 'y'. .TP .B "\-P x [y]" Like -p but for all following windows. .TP .B "\-ke x" Strip parts of the input using regular expression 'x'. .TP .B "\-kr x y" Strip parts of the input starting at offset x and ending (not including!) offset y. .TP .B "\-kc x y" Strip parts of the input: strip column 'y' with delimiter 'x'. .TP .B "\-ks x" Use editscheme 'x' from configfile. .TP .B "\-w" Do not use colors. .TP .B "\-b" n Sets the TAB-width. .TP .B "\--config" filename Load the configuration from given filename. .TP .B "\-x" Set xterm-title: %f will be replaced with the last changed file, %h with the hostname, %l with the load of the system, %m with "New mail!" when the current user has new mail, %u with the current effective user, %t timestamp of last changed file, %% with a % .TP .B "\-o" configfile-item Proces a configurationfile item via the commandline in case you cannot edit the default configfile. .TP .B "\--cont" Reconnect lines with a '\' at the end. .TP .B "\--mark-interval interval" When nothing comes in, print a '---mark---' line every 'interval' seconds. .TP .B "\--mark-change" When multiple files are merged an multitail switches between two windows, print a markerline with the filename. .TP .B "\--no-mark-change" Do NOT print the markerline when the file changes (overrides the configfile). .TP .B "\--label text" Put "text" in front of each line. Useful when merging multiple files and/or commands. .TP .B "\--retry Keep trying to open the following file if it is inaccessible. .TP .B "\--retry-all Like --retry but for all following files. .TP .B "\-cv x" Use conversion scheme 'x' (see multitail.conf). .TP .B "\--basename" Only display the filename (and not the path) in the statusline. .TP .B "\-F file" Use 'file' as configfile (instead of default configfile). .TP .B "\--no-load-global-config" Do NOT load the global configfile. .TP .B "\--beep-interval x" Let the terminal beep for every x-th line processed. Press 'i' in the main menu to see how many times it beeped. .TP .B "\--bi x" Like '\--beep-interval' but only for current (sub-)window. Statistics on the number of beeps can be found in the statistics for this (sub-)window. Press 't' in the main menu. .TP .B "\-H" Show heartbeat (to keep your sessions alive). .TP .B "\-V" Show the version and exit. .SH KEYS You can press a couple of keys while the program runs. To see a list of them, press F1 (or ^h). You can press F1 (or ^h) at any time: it gives you context related information. Press 'q' to exit the program. .SH EXAMPLES See http://www.vanheusden.com/multitail/examples.html for more and other examples. .TP .B "multitail /var/log/apache/access_log logfile \-i \-filestartingwithdatsh" This creates three windows. One with the contents of /var/log/apache/access_log, one with the contents of logfile and so on. .TP .B "multitail -R 2 -l \(dqnetstat -t\(dq" This runs netstat every 2 seconds and then shows what has changed since the previous run. That way one can see new connections being made and closed connections fading away. .TP .B "multitail logfile \-l \(dqping 192.168.1.3\(dq This creates two windows. One with the contents of logfile, one with with the output of 'ping 192.168.1.3'. .TP .B "multitail /var/log/apache/access_log \-I /var/log/apache/error_log" This creates .BI "one" window with the contents of /var/log/apache/access_log .BI "merged" with the contents of /var/log/apache/error_log. .TP .B "multitail \-M 0 /var/log/apache/access_log \-I /var/log/apache/error_log" Same as previous example. This example will store all logged entries in a buffer so that you can later on browse through them (by pressing ' .BI "b" '). .SH FILES .TP .B "${XDG_CONFIG_HOME}/multitail/config" If .BI "${XDG_CONFIG_HOME}" is defined. .TP .B "${HOME}/.multitailrc" If .BI "${HOME}" is defined. .TP .B "~/.multitailrc" When either .BI "${XDG_CONFIG_HOME}" nor .BI "${HOME}" is defined, which will use .BI "(struct passwd*)pp->pw_dir" from the .BI "pwd.h" standard header. .SH BUGS As this program grew larger and larger over the time with new functionality sometimes added ad-hoc, some bugs may have been introduced. Please notify folkert@vanheusden.com if you find any. .PP Well, except for the resizing of your terminal window. The program might crash when doing such things. Upgrading the ncurses library to at least version 5.3 might help in that case. .SH "SEE ALSO" .BR http://www.vanheusden.com/multitail/ .SH NOTES This page describes .B MultiTail as found in the installed multitail package; other versions may differ slightly. Mail corrections and additions to folkert@vanheusden.com. Report bugs in the program to folkert@vanheusden.com. multitail-7.1.5/multitail.conf000066400000000000000000001525351465663511400164310ustar00rootroot00000000000000# Format of this file: # # include:configfile # Also parse 'configfile'. # # defaultcscheme: # Selects the default color scheme to use. If this one is set, you # no longer need -cS/-CS. # # colorscheme: # This name can be given for the commandline-parameter -cS. That # way, one can select what colorscheme to use for the next # logfile. # # cs_re:: # This defines a regular expression to find a particular string. # # color: [fg],[bg],[attribute[/otherattribute]][|other colorpair+attribute] # e.g.: red,,bold|red would give bold red for line 1 and just red for line 2, etc. # Possible colors: red, green, yellow, blue, magenta, cyan and white. # For 256 colors support, you just have to use the color number of the ANSI 256 color scheme. # # cs_re_s:: # Like cs_re but only the substrings are used(!). E.g.: # ^....(...)...(...) # In the example above only what matches between '(' and ')' is # assigned a color. See the 'acctail' colorscheme for an example. # # cs_re_val_less::: # cs_re_val_bigger::: # cs_re_val_equal::: # Like cs_re_s(!) but here the value of the sub(!)-string is # compared against the given value. e.g.: # cs_re_val_less: if "" less then the value matched with # the regular expression, then use the selected color. Please # note that you have to select the value in the string with a # substring match; put '(' and ')' around it. # # mcsre:color:regexp # mcsre_s:color:regexp # mcsre_val_less/bigger/equal:color:regexp # These work like their cs_re* sisters only they merge their # attributes (colors, bold, bright, etc.) with the previous # merging one. # # scheme:: # This defines what colorscheme to use when the filename matches # the given regular expression. This way, one can automatically # use the correct colorscheme for certain files. # # check_mail: # How often MultiTail should check for new e-mail. You can switch # mail-checking off by setting this value to 0. # # tab_stop: # Specifies the width of TAB characters. Default-value is 4. # # bind:key:program # Binds a key to a external command. For example: # bind:^j:/bin/bash - binds ^j (control + j) to bash. While the # selected program is running, MultiTail is suspended. MultiTail # will automatically come back when the external command ends. # # titlebar: # what to put in the titlebar of the xterm in which MultiTail is # running. see below for details # # tail:tail_program # path to the tail-program (in case you want to use turbotail or # so instead of the regular tail) # # bright:(1|0) # use bright colors (1) or not (0) # # abbreviate_filesize # wether to abbreviate filesizes to xKB/MB/GB (1) or not (0) # # show_subwindow_id # when set to 1 and merging multiple inputs (e.g. logfiles) the # output show is prepended with a number indicating the input # # markerline_color # with this one you can set how a markerline looks. e.g. yellow on # red or so # # markerline_timestamp # sets whether to put a timestamp in the markerline (1) or not (0) # # ts_format: # format of timestamps. see the man-page of 'strftime' for a list # of the fields that can be used # # shell: # shell to use when invoking programs. default is /bin/sh # # umask: # umask used when creating files # #defaultcscheme:syslog # # /var/log/messages & .../syslog colorscheme:syslog:kernel and unsorted messages # segfaults cs_re_s:red,,bold:kernel: ([^:]*): segfault at [^ ]* rip [^ ]* rsp [^ ]* error cs_re:yellow:kernel: .*: segfault at [^ ]* rip [^ ]* rsp [^ ]* error # misc cs_re:red,,inverse/underline:kernel cs_re:green:Detected.*$ cs_re:green:\[ cs_re:green:\] cs_re:cyan:: cs_re:blue|blue,,bold:^... .. ..:..:.. cs_re_s:,,bold:^... .. ..:..:.. [^ ]* ([A-z0-9]*) cs_re:red:scanlogd.* cs_re:yellow:Did.not # # Zarafa colorscheme:zarafa:www.zarafa.com cs_re:magenta:none cs_re:blue,,bold:debug cs_re:green,,bold:info cs_re:yellow:notice cs_re:yellow,,bold:warning cs_re:red:error cs_re:red,,blink:fatal cs_re:yellow:: cs_re:green:^... ... .. ..:..:.. .... cs_re:cyan,,bold:/ cs_re:red,,bold:\( cs_re:red,,bold:\) cs_re_s:yellow,,underline:User *([^ ]*) cs_re:magenta,,bold: *[a-z]*[0-9]+[a-z]* cs_re:red:\[ cs_re:red:\] # # Oracle WebLogic colorscheme:weblogic cs_re_s:red,,bold:.*().* cs_re_s:yellow:.*().* cs_re_s:blue:.*().* cs_re_s:green:.*(<.*>).* # # Oracle GoldenGate colorscheme:goldengate cs_re_s:red,,bold:.*( ERROR ).* cs_re_s:yellow:.*( WARNING ).* cs_re_s:green:.*( INFO ).* # # ssh colorscheme:ssh:www.openssh.org cs_re:yellow:error: Could not get shadow information for.* cs_re:yellow:fatal: Timeout before authentication for.* cs_re_s:red,,bold:error: PAM: Authentication failure for(.*) cs_re:red:error: PAM: Authentication failure for cs_re:red,,blink:error: Bind to port [0-9]* on [^ ]* failed: Address already in use. cs_re_s:red,,bold:error: PAM: Authentication failure for ([^ ]*) from (.*) cs_re:red:error: PAM: Authentication failure for ([^ ]*) from (.*) cs_re_s:green,,bold:Accepted [^ ]* for ([^ ]*) from ([^ ]*) port ([0-9]*) ssh2 cs_re:green:Accepted [^ ]* for [^ ]* from [^ ]* port.* cs_re:red:PAM session setup failed\[[0-9]*\]:.* cs_re_s:yellow,,bold:Failed ([^ ]*) for ([^ ]*) from ([^ ]*) port ([0-9]*).* cs_re:yellow:Failed [^ ]* for [^ ]* from [^ ]* port [0-9]* .* cs_re:red:Disconnecting: Too many authentication failures for.* # # PowerDNS colorscheme:powerdns:www.powerdns.com cs_re_s:green:Remote\ (.*)\ wants cs_re:blue:'.*' cs_re:red:MISS cs_re:green,,bold:HIT # # UUCP colorscheme:uucp:UUCP Log files cs_re:yellow:uucico cs_re:cyan:uuxqt cs_re:magenta:Receiving cs_re:magenta:Executing cs_re:magenta:Sending cs_re:cyan:Queuing cs_re:cyan:Calling system cs_re:green:Login successful cs_re:green:Handshake successful cs_re:green:Call complete cs_re:red:ERROR:.* # # ADB logcat colorscheme:logcat cs_re_s:blue,,bold:^./(dalvikvm)\( cs_re_s:blue,,bold:^./(Process)\( cs_re_s:cyan:^./(ActivityManager)\( cs_re_s:cyan:^./(ActivityThread)\( cs_re_s:white,,bold:^./([^\(]*)\( cs_re_s:green:^[^\(]*(\()[^\)]*(\)) cs_re:red,,inverse:[Pp]ermission [Dd]eni[ae][dl] cs_re:red,,inverse:Caused by: cs_re:cyan:: #cs_re:red,,inverse:^F #cs_re:red,,bold:^E #cs_re:yellow,,bold:^W #cs_re:cyan,,bold:^I #cs_re:green,,bold:^V #cs_re:white:^D cs_re_s:red,,inverse:^(F)/[^:]*: (.*)$ cs_re_s:red:^(E)/[^:]*: (.*)$ cs_re_s:yellow:^(W).[^:]*: (.*)$ #cs_re_s:green:^(I).[^:]*: (.*)$ cs_re_s:green:^(V)/[^:]*: (.*)$ cs_re_s:black,,bold:^(D)/[^:]*: (.*)$ # # linux iptables firewall colorscheme:liniptfw:Linux IPtables (2.6.x kernel) cs_re:cyan:: cs_re:blue|blue,,bold:^... .. ..:..:.. cs_re_s:red:kernel: .*(DPT=[0-9]*) cs_re_s:yellow:kernel: (IN=[^ ]*) cs_re_s:cyan:kernel: .*(SRC=[^ ]*) *(DST=[^ ]*) cs_re_s:green:kernel: .*(PROTO=[^ ]*) # # postfix log colorscheme:postfix:www.postfix.org cs_re:cyan:: cs_re:yellow:status=sent cs_re:magenta:queue.active cs_re:green:from=.*> cs_re:red:to=.*> cs_re:blue|blue,,bold:^... .. ..:..:.. cs_re:green:\[ cs_re:green:\] # # apache colorscheme:apache:default Apache logging (webserver) cs_re_s:yellow:"[ ](1[0-9][0-9])[ ] cs_re_s:green:"[ ]([2-3][0-9][0-9])[ ] cs_re_s:red:"[ ]([4-5][0-9][0-9])[ ] cs_re:cyan:: cs_re:green:\[ cs_re:green:\] # ip-adresses in the format x.x.x.x cs_re:yellow:^[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\} # hostnames cs_re:yellow:^[^ ]* ### Apache errorlog colorscheme:apache_error:default Apache error logging cs_re:yellow:\[client .*\] cs_re:red: [^ ]*$ cs_re:blue:^\[... ... .. ..:..:.. ....\] # # rsstail (http://www.vanheusden.com/rsstail/) colorscheme:rsstail:RSSTail output (RSS feed reader) cs_re:cyan:: cs_re:cyan:/ cs_re:blue:^.......... ..:..:.. cs_re:green:Title:.* cs_re:red:^Link:.* cs_re:yellow:^Description: # # acctail (http://www.vanheusden.com/acctail/) colorscheme:acctail:(BSD-) process accounting reader cs_re:green:^................ cs_re_s:red:^.................(....) cs_re_s:cyan:^......................(........) cs_re_s:yellow:^...............................(........) cs_re:blue:\. # # wtmptail (http://www.vanheusden.com/wtmptail/) colorscheme:wtmptail:www.vanheusden.com/wtmptail/ cs_re:blue:\. cs_re:blue:: cs_re:yellow:^USER.* cs_re:green:^........ cs_re:cyan:[0-9]*:[0-9]*[ap]m cs_re:red:^.................................. cs_re:black,red,blink:BOOT cs_re:black,yellow:DEAD # # squid/squid3 colorscheme:squid:http proxy server cs_re:blue:^[^ ]* cs_re_s:yellow:^[^ ]* *[0-9]* *([^ ]*) cs_re_s:green:^[^ ]* *[0-9]* *[^ ]* ([^ ]*) cs_re_s:cyan:^[^ ]* *[0-9]* *[^ ]* [^ ]* *[0-9]* *[^ ]* *([^ ]*) # # asterisk colorscheme:asterisk:software PBX cs_re:blue:: cs_re:cyan:^............... cs_re:red:ERROR cs_re:yellow:WARNING cs_re:green:NOTICE cs_re:magenta:DEBUG cs_re:magenta:VERBOSE cs_re:red,black,underline:Unable to.* # # sendmail colorscheme:sendmail cs_re:blue|blue,,bold:^............... cs_re:cyan:to=<[^>]*> cs_re:yellow:stat=Deferred.* cs_re:red:stat=User unknown cs_re:green:stat=Sent.* cs_re:magenta:relay=[^ ]* cs_re:cyan:: cs_re:red:verify=FAIL # # MailScanner colorscheme:mailscanner:wrapper around sendmail/clamav/spamassassin cs_re:red:Detected HTML-specific exploits in.* cs_re:magenta:Delivered [0-9]* warnings to virus senders cs_re:yellow:Spam Checks: Found [0-9]* spam messages cs_re:yellow:Content Checks: Detected and will disarm HTML message in.* cs_re:green:Uninfected: Delivered [0-9]* messages cs_re:red:Infected message [^ ]* came from.* cs_re:yellow:Saved infected "[^"]*" to.* cs_re:blue|blue,,bold:^............... cs_re:cyan:: # # SpamAssassin colorscheme:spamassassin cs_re:magenta:: cs_re:blue|blue,,bold:^... .. ..:..:.. cs_re:yellow:autolearn=failed cs_re:red,,blink:server killed by [^,]*, shutting down cs_re_s:yellow,,bold:identified spam ([^ ]*) for ([^ ]*) in ([^ ]*) seconds, ([^ ]*) bytes. cs_re:yellow:identified spam [^ ]* for [^ ]* in [^ ]* seconds, [^ ]* bytes. cs_re_s:green,,bold:server successfully spawned child process, pid (.*) cs_re:green:server successfully spawned child process, pid # # ClamAV colorscheme:clamav:clamd logging cs_re:magenta:: cs_re:blue:^... ... [0-9]* ..:..:.. .... cs_re_s:red,,bold:ERROR:(.*) cs_re:red:ERROR: cs_re_s:green,,bold:Protecting against ([0-9]*) viruses. cs_re:green:Protecting against [0-9]* viruses. cs_re:red,black,inverse:Exiting.* cs_re_s:yellow,,bold:^(.*): Unable to open file or directory ERROR cs_re:yellow:^.*: Unable to open file or directory ERROR cs_re:red,black,inverse:LOGGING DISABLED.* # # samba colorscheme:samba cs_re_s:blue:^.([0-9]*/[0-9]*/[0-9]* [0-9]*:[0-9]*:[0-9]*, [0-9]*) cs_re:blue:\. cs_re_s:green:^ *([^ ]*).*(connect to service folkert initially as user [^ ]*) cs_re:yellow:closed connection to service.* cs_re:red:Error =.* cs_re:red:ERRNO =.* cs_re:red:^.*does not exist or is not a directory, when connecting to.* cs_re:red:Ignoring unknown parameter.* cs_re:green,,bold:smbd version.*started. cs_re:green,,bold:Netbios nameserver version.*started. cs_re:green:Samba name server.*is now a local master browser for workgroup.*on subnet.* cs_re:yellow:Attempting to become domain master browser on workgroup.*, subnet.* cs_re:green:Samba is now a logon server for workgroup.*on subnet.* cs_re:yellow,,bold:Server.*at IP.*is announcing itself as a local master browser for workgroup.*and we think we are master. Forcing election. # # audit.log colorscheme:audit cs_re:cyan:: cs_re:blue|blue,,bold:^... .. ..:..:.. cs_re:red:Illegal user.* cs_re:green:session opened for user.* cs_re:green:Accepted publickey for cs_re:yellow:Did not receive identification string from.* # # exim colorscheme:exim cs_re:cyan:: cs_re:blue|blue,,bold:^....-..-.. ..:..:.. cs_re:magenta,,bold:<= cs_re:magenta,,bold:=> cs_re:green,,bold:=> *[^ ]* cs_re:green:<= *[^ ]* cs_re:green,,bold:=> *[^ ]* <[^>]*> cs_re:green:<= *[^ ]* <[^>]*> cs_re:yellow:H=[^ ]* cs_re:red:verify failed for SMTP recipient.* cs_re:red: *[^ ]* \[[0-9\.:]*\]: Connection refused cs_re:red:SMTP.*timeout.* cs_re:yellow:Spool file is locked (another process is handling this message) # # httping colorscheme:httping:ping for HTTP cs_re:green:^PING .*: cs_re:magenta:time=[0-9\.]* ms cs_re:yellow:seq=[0-9]* cs_re:green:^---.* cs_re_s:green:^([0-9]*) connects, ([0-9]*) ok, ([^%]*) cs_re_s:green:^round-trip.*= ([0-9\.]*)/([0-9\.]*)/([0-9\.]*) cs_re:red:404 Not Found cs_re:blue:[0-9]*KB/s cs_re:red:could not connect # # netstat (use for example with multitail -R 1 -l "netstat") colorscheme:netstat:see www.vanheusden.com/multitail/examples.html cs_re:green:ESTABLISHED cs_re:yellow:SYN_SENT cs_re:magenta:CLOSE_WAIT cs_re:blue:FIN_WAIT1 cs_re:blue,,underline:FIN_WAIT2 cs_re_s:red::([^ ]*) cs_re:cyan:^[^ ]* # # tcpdump colorscheme:tcpdump cs_re:blue:^[^ ]* cs_re:magenta:: cs_re_s:red:^[^ ]* [^ ]* *[^ ]* > [^:]*: *([^ ]*) cs_re_s:green:^[^ ]* [^ ]* *[^ ]*\.([^ ]*) > [^ ]*\.([^:]*): cs_re:magenta:\. cs_re_s:yellow|yellow,,bold: IP (.*) > .*: cs_re_s:yellow,,bold|yellow: IP [^ ]* > ([^:]*): #17:44:07.363010 IP muur.intranet.vanheusden.com.49584 > keetweej.intranet.vanheusden.com.ssh: # # dhcpd colorscheme:dhcpd cs_re:magenta:: cs_re:blue|blue,,bold:^... .. ..:..:.. cs_re:green,,bold:DHCPACK cs_re_s:green:DHCPACK on ([^ ]*) to ([^ ]*) to (.*) cs_re_s:red,,bold:DHCPINFORM from ([^ ]*) via cs_re:red:DHCPINFORM from.*via.*not authoritative for subnet.* cs_re_s:yellow,,bold:DHCPDECLINE of ([^ ]*) from ([^ ]*) via cs_re:yellow:DHCPDECLINE of.*from.*via.* cs_re:cyan:DHCPNAK # # bind colorscheme:bind cs_re:magenta:: cs_re:blue|blue,,bold:^... .. ..:..:.. cs_re_s:yellow,,bold:lame server resolving ([^ ]*).*in ([^?]*).*: (.*$) cs_re:yellow:lame server resolving [^ ]* .in [^:]*..: cs_re_s:red,,bold:client ([^#]*)#[0-9]*: update forwarding denied cs_re:red:client [^:]*: update forwarding denied cs_re_s:cyan,,bold:received notify for zone (.*) cs_re:cyan:received notify for zone cs_re:cyan:zone [^:]*: transferred serial.* cs_re:cyan:zone [^:]*: sending notifies .serial [0-9]*. cs_re_s:green,,bold:transfer of [^ ]* from ([^#]*)#[0-9]*: end of transfer cs_re:green:transfer of [^ ]* from [^:]*: end of transfer cs_re:yellow:loading configuration from.* cs_re:yellow:no IPv6 interfaces found cs_re:red:logging channel [^ ]* file [^:]*: permission denied cs_re:red:isc_log_open [^ ]* failed: permission denied cs_re:red:zone [^:]*: loading master file [^:]*: file not found cs_re:green,,bold:named reload succeeded # # smartd colorscheme:smartd cs_re_s:red,,bold:Device: [^,]*, SMART Usage Attribute: [0-9]* ([^ ]*) changed from [0-9]* to (.*) cs_re:red:Device: [^,]*, SMART Usage Attribute: [0-9]* [^ ]* changed from [0-9]* to.* # # kerberos colorscheme:kerberos cs_re:magenta:: cs_re:blue:^... [0-9]* ..:..:.. cs_re_s:yellow,,bold:klogind.*: Authentication failed from ([^:]*): Software caused connection abort cs_re:yellow:klogind.*: Authentication failed from [^:]*: Software caused connection abort cs_re:red:klogind.*: Kerberos authentication failed cs_re:red,,bold:klogind.*: User ([^ ]*) is not authorized to login to account(.*) cs_re:red:klogind.*: User [^ ]* is not authorized to login to account.* cs_re_s:red,,bold:ksu.*:.*authentication failed for ([^ ]*) on cs_re:red:ksu.*:.*authentication failed for [^ ]* on.* cs_re:green:commencing operation cs_re_s:,,bold:AS_REQ [^}]*}, ([^ ]*) for (.*) cs_re:magenta:Ticket expired cs_re:red:Client not found in Kerberos database # # Oracle colorscheme:oracle cs_re:red,,bold:^ORA-0*600.* cs_re:red,,bold:^ORA-0*7445.* # ORA-07745:? cs_re_s:yellow:^ORA-([^:]*): # # ntpd colorscheme:ntpd cs_re:magenta:: cs_re:blue:^... .. ..:..:.. cs_re_s:red,,bold:configure: keyword "([^"]*)" unknown, line ignored cs_re:red:configure: keyword "([^"]*)" unknown, line ignored cs_re:yellow,,blink:ntpd\[.*\]: ntpd exiting on signal.* cs_re:green,,bold:ntpd\[.*\]: ntpd [^e].* cs_re_s:green,,bold:synchronized to ([^,]*) cs_re:green:synchronized to.* cs_re:red:check receiver configuration / cableling # # nagtail colorscheme:nagtail:www.nagios.org status viewer cs_re:magenta:: cs_re:magenta:/ cs_re:blue:^..../../.. ..:.. cs_re_s:red,,bold:^................ (CRIT) cs_re_s:yellow,,bold:^................ (WARN) cs_re_s:green:^................ ( OK ) cs_re_s:white,,bold:^................ ( \?\? ) cs_re_s:green:^..../../.. ..:.. ..... *([^ ]*) cs_re_s:yellow:^..../../.. ..:.. ..... *[^ ]* *(.*) # # WebSphere errorlog colorscheme:websphere:WebSphere error-log cs_re:magenta:: cs_re:magenta:/ cs_re:blue,,bold:\. cs_re:blue:^.[0-9]*/[0-9]*/[0-9]* *[0-9]*:..:..:[0-9]* [^ ]* cs_re_s:yellow:^.*\(([^:\)]*) cs_re:red:Reason:.* cs_re:red,,bold:Unable to cs_re_s:red:Unable to(.*) cs_re:red,,bold:Failed to cs_re_s:red:Failed to(.*) cs_re_s:green:^[^ ]* *[^ ]* *[^ ]* *[^ ]* *([^ ]*) cs_re_s:red,,bold:^[^ ]* *[^ ]* *[^ ]* *[^ ]* *[^ ]* *( F ) cs_re_s:red:^[^ ]* *[^ ]* *[^ ]* *[^ ]* *[^ ]* *( E ) cs_re_s:yellow,,bold:^[^ ]* *[^ ]* *[^ ]* *[^ ]* *[^ ]* *( W ) cs_re_s:yellow:^[^ ]* *[^ ]* *[^ ]* *[^ ]* *[^ ]* *( A ) cs_re_s:green,,bold:^[^ ]* *[^ ]* *[^ ]* *[^ ]* *[^ ]* *(I) cs_re_s:green:^[^ ]* *[^ ]* *[^ ]* *[^ ]* *[^ ]* *( C ) cs_re_s:magenta,,bold:^[^ ]* *[^ ]* *[^ ]* *[^ ]* *[^ ]* *( R ) cs_re_s:magenta:^[^ ]* *[^ ]* *[^ ]* *[^ ]* *[^ ]* *( O ) cs_re_s:red,,bold:^[^ ]* *[^ ]* *[^ ]* *[^ ]* *[^ ]* *( Z ) cs_re:yellow:Next Linked Exception: cs_re:magenta,,bold:Queue manager security exit rejected connection with error code [0-9]* cs_re:red,,bold:com.ibm.mq.MQException: [^:]*: An MQException occurred: Completion Code [0-9]*, Reason [0-9]* cs_re:yellow:Begin backtrace for nested exception cs_re:yellow:Socket connection attempt refused cs_re:yellow:Other data: cs_re:yellow:Exception data follows: cs_re:green:Target name: .* # # NNTPcache colorscheme:nntpcache cs_re:magenta:: cs_re:magenta:/ cs_re:blue|blue,,bold:^... .. ..:..:.. cs_re:yellow:nntpcache-expire.*: clean shutdown cs_re:green:nntpcache-expire.*: expire task awakening cs_re_s:red,,bold:Connection timed out: could.*t connect to ([^ ]*) as (.*) cs_re:red:Connection timed out: could.*t connect to [^ ]* as cs_re:yellow:^.*dropped connection during rebuild of.*$ cs_re:red,,bold:'501.* cs_re:red,,bold:'480.* # # Veritas Netbackup restore log colorscheme:vnetbr:Veritas Netbackup backup/restore logs cs_re:magenta:: cs_re:magenta:\( cs_re:magenta:\) cs_re:blue:^..:..:.. .[0-9]*.[^\)]*. cs_re_s:green:Restore job id ([0-9]*) will require ([0-9]*) image cs_re:green:Restore job id [0-9]* will require [0-9]* image.* cs_re_s:yellow,,bold:Media id ([^ ]*) is needed for the restore. cs_re:yellow:Media id ([^ ]*) is needed for the restore. cs_re:green:INF - Beginning restore from server [^ ]* to client [^ ]*. cs_re_s:yellow,,bold:Changed ([^ ]*) to ([^ ]*) cs_re:yellow:Changed [^ ]* to [^ ]* cs_re_s:red,,bold:Directory ([^ ]*) already exists. cs_re:red:Directory [^ ]* already exists. cs_re_s:green,,bold:Added ([^ ]*) permission to directory (.*) cs_re:green:Added [^ ]* permission to directory.* cs_re_s:yellow,,blink:INF - Media id ([^ ]*) is not in a robotic library; administrative interaction may be required to satisfy a mount request. cs_re:yellow:INF - Media id [^ ]* is not in a robotic library; administrative interaction may be required to satisfy a mount request. cs_re:red,black,inverse:INF - Status = termination requested by administrator. cs_re:red,,blink:media read error cs_re:red,,bold:Status of restore from image created.*media read error cs_re:red:INF - Status = the restore failed to recover the requested files. cs_re:green,,bold:INF - Status = the requested operation was successfully completed. # # procmail colorscheme:procmail cs_re:magenta:: cs_re:magenta:/ cs_re_s:blue,,bold:^procmail: \[[0-9]*\] ([^ ]* [^ ]* *[^ ]* ..:..:.. [^ ]*) cs_re:blue:^procmail: \[[0-9]*\] [^ ]* [^ ]* *[^ ]* ..:..:.. [^ ]* cs_re_s:green,,bold:^procmail: Match on "(.*)" cs_re:green:^procmail: Match on.* cs_re:red:^procmail: Executing.* cs_re:magenta,,bold:^procmail: Assigning cs_re:yellow:warning:.* cs_re:yellow:Couldn't determine implicit lockfile from.* # # checkpoint colorscheme:checkpoint:Checkpoint Firewall-1 cs_re:magenta:: cs_re:blue|blue,,bold:^[0-9]*:[0-9]*:[0-9]* cs_re:red,,bold:^..:..:.. *drop cs_re_s:red,,bold:^..:..:.. *drop.*(service: *[^;]*) cs_re:green:^..:..:.. *accept cs_re:yellow,,bold:^..:..:.. *reject cs_re_s:yellow,,bold:^..:..:.. *reject.*(service: *[^;]*) cs_re:green,,bold:rule: *[^;]* cs_re:yellow:service: *[^;]* cs_re:magenta:src: *[^;]* cs_re:magenta,,bold:dst: *[^;]* cs_re:blue,,bold:>[^ ]* # # pppd colorscheme:pppd:PPP daemon cs_re:magenta:: cs_re:blue|blue,,bold:^... .. ..:..:.. cs_re_s:red,,bold:Hangup(.*) cs_re:red:Hangup cs_re_s:yellow,,bold:Terminating on signal(.*) cs_re:yellow:Terminating on signal.* cs_re_s:green,,bold:Connect time ([^ ]*) minutes. cs_re:green:Connect time [^ ]* minutes. cs_re_s:magenta,,bold:Sent ([0-9]*) bytes, received ([0-9]*) bytes. cs_re:magenta:Sent [0-9]* bytes, received [0-9]* bytes. cs_re:green:pppd [^ ]* started by [^,]*, uid [0-9]* cs_re_s:blue,,bold:Using interface (.*) cs_re:blue:Using interface.* cs_re_s:green,,bold:local *IP address (.*) cs_re:green:local *IP address.* cs_re:red,black,inverse:Couldn't detach (fork failed:.*) cs_re_s:yellow,,bold:Unsupported protocol (.*) received cs_re:yellow,black,inverse:Unsupported protocol .* received cs_re:yellow,,blink:Peer not responding cs_re:,,inverse:DNS address.* # # INN colorscheme:inn cs_re:magenta:: cs_re:blue|blue,,bold:^... .. ..:..:.. cs_re:yellow:nnrpd.*: .* timeout # # Netscape Directory server (LDAP) colorscheme:netscapeldap:Netscape Directory server (LDAP) cs_re:magenta:: cs_re:magenta:/ cs_re:blue|blue,,bold:^.../.../....:..:..:.. ...... cs_re:green:Netscape-Directory.*starting up cs_re:yellow:All database threads now stopped cs_re:green:Backing up file.* cs_re:red:Detected Disorderly Shutdown last time Directory Server was running, recovering database. cs_re_s:yellow,,bold:Entry ([^ ]*) unknown object class ([^ ]*) cs_re:yellow:Entry [^ ]* unknown object class [^ ]* cs_re:red,,blink:Shutting down due to possible conflicts with other slapd processes cs_re:red,,bold:Unable to start slapd because it is already running as process ([0-9]*) cs_re:red,,blink:Unable to start slapd because it is already running as process [0-9]* cs_re:yellow:slapd got [^ ]* signal # # vmstat colorscheme:vmstat:vmstat is part of sysstat cs_re_s:magenta:^(procs) cs_re_s:red:^procs *(-*memory-*) cs_re_s:green:^procs *-*memory-* *(-*swap-*) cs_re_s:yellow:^procs *-*memory-* *-*swap-* *(-*io-*) cs_re_s:blue:^procs *-*memory-* *-*swap-* *-*io-* *(-*system-*) cs_re_s:magenta,,bold:^ *(r *b) cs_re_s:red,,bold:^ *r *b *(swpd *free *buff *cache) cs_re_s:green,,bold:^ *r *b *swpd *free *buff *cache *(si *so) cs_re_s:yellow,,bold:^ *r *b *swpd *free *buff *cache *si *so *(bi *bo) cs_re_s:blue,,bold:^ *r *b *swpd *free *buff *cache *si *so *bi *bo *(in *cs) cs_re_s:,,bold:^ *r *b *swpd *free *buff *cache *si *so *bi *bo *in *cs *(us *sy *id *wa) cs_re_val_bigger:yellow:0:([0-9]{1,}) # # mpstat colorscheme:mpstat:mpstat is part of systat cs_re:magenta:: cs_re:blue:^..:..:.. .. cs_re:blue,,bold:CPU.* cs_re_val_bigger:yellow:0:([0-9]{1,}\.[0-9]{1,}) # # log4j colorscheme:log4j cs_re:magenta:: cs_re:magenta:/ cs_re:blue:^[0-9]*-[0-9]*-[0-9]* [0-9]*:[0-9]*:[0-9]*,[0-9]* cs_re_s:blue,,bold:^[^ ]* *[^,]*,[^ ]* *[0-9]* *(DEBUG) *[^ ]* [^ ]* *(.*)$ cs_re_s:green:^[^ ]* *[^,]*,[0-9]* *[0-9]* *(INFO) *[^ ]* [^ ]* *(.*)$ cs_re_s:yellow:^[^ ]* *[^,]*,[0-9]* *[0-9]* *(WARN) *[^ ]* [^ ]* *(.*)$ cs_re_s:red:^[^ ]* *[^,]*,[0-9]* *[0-9]* *(ERROR) *[^ ]* [^ ]* *(.*)$ cs_re_s:red,,bold:^[^ ]* *[^,]*,[0-9]* *[0-9]* *(FATAL) *[^ ]* [^ ]* *(.*)$ cs_re_s:white,,bold:^[^ ]* *[^,]*,[0-9]* *[0-9]* *[A-Z]* *(.*) #log4jnew colorscheme:log4jnew cs_re:red,yellow,bold:ERROR cs_re:red,yellow,bold:FATAL cs_re:yellow:INFO cs_re:green:WARN #Date cs_re_s:yellow:([0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2},[0-9]{3}) #Thread cs_re_s:cyan: (\[.*\]) #Class cs_re:cyan:[INFO|WARNING|ERROR|DEBUG|FATAL]\ ([a-zA-Z0-9\.]*)\ #Numbers cs_re:magenta: ([0-9]{2,}) #Keywords cs_re_s:green:(UNKNOWN|localhost|handling) # # LambdaMOO colorscheme:lambdamoo:MUD/MOO server http://www.moo.mud.org/ cs_re:magenta:: cs_re:blue:^... [0-9]* ..:..:..: cs_re:green:STARTING: Version [^ ]* of the LambdaMOO server cs_re:yellow:DUMPING:.* cs_re:red:Can't create initial connection point cs_re_s:red,,bold/blink:Trying to dump database:(.*) cs_re:red,,bold:Trying to dump database: # # BOINCTail colorscheme:boinctail:BOINCTail http://www.vanheusden.com/boinctail/ cs_re:magenta:: cs_re_s:blue:^---> (... ... .. ..:..:.. ....) cs_re_s:blue,,bold:^---> ........................ .([0-9]* seconds.*) cs_re:yellow:report deadline cs_re_s:red:^................ ([^,]*) cs_re_s:green:, (estimated.*) cs_re:green,,bold:workunit is ready cs_re:red,,bold:not cs_re_s:white,,bold:^name of workunit: (.*) # # p0f colorscheme:p0f:p0f http://lcamtuf.coredump.cx/p0f.shtml cs_re:magenta:: cs_re:blue,,bold:^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3} cs_re_s:blue:^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}:([0-9]*) cs_re_s:yellow:^[^ ]* - ([^(]*) cs_re:green:\(older, [0-9]*\) cs_re:green,,bold:\(newer, [0-9]*\) cs_re_val_bigger:cyan,,bold:501:\(up: ([0-9]*).*\) cs_re:red:\(up: .*\) cs_re:red,,bold:distance [0-9]* cs_re_s:cyan:^ -> [^:]*:([0-9]*) cs_re_s:white:^>> Masquerade at ([^:]*): cs_re:white,,bold:^>> Masquerade at [^:]*: cs_re_val_bigger:green,,bold:50:indicators at ([0-9]*)% # # portsentry colorscheme:portsentry:http://sourceforge.net/projects/sentrytools/ cs_re:magenta:: cs_re_s:red,,bold:attackalert: ([^/]*)/Normal scan from host: ([^/]*)/([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}) to TCP port: ([0-9]*) cs_re:red:attackalert: [^/]*/Normal scan from host: [^/]*/[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3} to TCP port: [0-9]* cs_re:cyan:attackalert: ERROR: cannot open ignore file. Blocking host anyway. cs_re_s:yellow,,bold:attackalert: Host: ([^/]*)/([^ ]*) is already blocked Ignoring cs_re:yellow:attackalert: Host: [^ ]* is already blocked Ignoring # # strace colorscheme:strace:strace is the truss of Linux # comments cs_re:blue:/\*.*\*/ # call cs_re:yellow:^[a-z_]*[0-9]* # parenthesis around parameters cs_re_s:yellow:^[a-z_]*[0-9]*(\()[^)]*(\)) # errno details cs_re_s:blue:^[a-z]*\([^)]*\).*(\(.*\)) # return value cs_re_s:green:^.*(= *-*[a-fx0-9]*)[^=]*$ # errno cs_re_s:cyan:^.*= *-*[a-fx0-9]* *([A-Z]*)[^=]*$ # escapes cs_re:magenta:\\[a-z] cs_re:magenta:\\[0-9]* # parameters (1) cs_re:red:"[^"]*" # names cs_re:white:[a-z]*= cs_re:white:[a-z0-9_]*: # parameters (2) cs_re:red:[0-9a-fx]* cs_re:cyan:[A-Z_]* # {}[] cs_re:yellow:[\[\]]* # # Argus colorscheme:argus:Argus http://qosient.com/argus/ mcsre_s:,,bold:[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+\.([0-9]+).*[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+\.([0-9]+) mcsre_val_bigger:red,,bold:20000:([0-9]+)[ ]+([0-9]+)[ ]+[A-Z][A-Z][A-Z] mcsre_s:magenta,,bold: (<->) mcsre_s:cyan,,bold: (<[-?]) mcsre_s:yellow,,bold: ([-?]>) mcsre:,blue:.*tcp.* mcsre:,green:.*udp.* mcsre:,cyan:.*icmp.* mcsre:,,inverse:.*man.* # # ii - irc client colorscheme:ii:ii IRC client http://www.suckless.org/wiki/tools/irc cs_re_s:cyan:(^....-..-.. ..:..) cs_re_s:magenta:^....-..-.. ..:.. <([[:alnum:]_\^\|`-]+)> cs_re_s:green:^....-..-.. ..:.. <([[:alnum:]_\^\|`-]+)> (nion[ ,:].*)$ cs_re_s:yellow:(((http|https|ftp|gopher)|mailto):(//)?[^ <>\"[:blank:]]*|(www|ftp)[0-9]?\.[-a-z0-9.]+) cs_re:blue:....-..-.. ..:...*has joined \#.* cs_re:blue:....-..-.. ..:.. .*changed mode.* # # Snort colorscheme:snort:Intrusion detector cs_re:green:.*Priority: 3.* cs_re:yellow:.*Priority: 2.* cs_re:red:.*Priority: 1.* # # Motion colorscheme:motion:Security camera software cs_re:red:Unable to start external command cs_re:red:[^ ]* query failed cs_re:yellow:Failed to put image into video pipe cs_re:red:Could not create symbolic link cs_re:red:ffopen_open error creating [^ ]* file cs_re:red:Memory error while allocating output media context cs_re:red:av_new_stream - could not alloc stream cs_re:red:avcodec_alloc_frame - could not alloc frame cs_re:red:url_fopen - error opening file %s cs_re:red:Error opening file %s cs_re:red:Error while writing video frame cs_re:red:Could not alloc frame cs_re:green:Motion detected - starting event [0-9]* cs_re:red:Could not fetch initial image from network camera cs_re:red:Error capturing first image cs_re:red:Failed to open video loopback cs_re:red:MySQL error was cs_re:red,,bold:Video device fatal error - terminating camera thread cs_re:red:Video signal lost - Adding grey image cs_re:red,,bold:Somebody stole the video device, lets hope we got his picture cs_re:red,,blink:Could not allocate [0-9]* bytes of memory! cs_re:red:Problem creating directory cs_re:red:Error opening file [^ ]* with mode cs_re:yellow:No response from camera cs_re:red:Can't write picture to file cs_re:red,,bold:Thread is going to finish due to this fatal error cs_re:magenta:[^ ]* error in proc %d cs_re:magenta:mmap failed cs_re:yellow,,bold:Motion Exits. cs_re:yellow,,bold:httpd quitting # # errpt - IBM AIX error report # example usage: multitail -R 10 -cS errpt -l errpt colorscheme:errpt:AIX error reporting tool cs_re_s:blue|blue,,bold:^[^ ]* *(....)....(..) cs_re_s:blue,,bold|blue:^[^ ]* *....(....).. cs_re:magenta: I [A-Z] .* cs_re:yellow: T [A-Z] .* cs_re:red: [A-Z] H .* # # MySQL error log colorscheme:mysql:MySQL error log cs_re:magenta:: cs_re_s:blue|blue,,bold:^..(..).. ..:..:.. cs_re_s:blue,,bold|blue:^(..)..(..) ..:..:.. cs_re:blue:^...... ..:..:.. cs_re:red,,bold:^...... ..:..:.. \[ERROR\] cs_re:red:^...... ..:..:.. \[ERROR\].* cs_re:yellow,,bold:^...... ..:..:.. \[Warning\] cs_re:yellow:^...... ..:..:.. \[Warning\].* cs_re:green,,bold:^...... ..:..:.. \[Note\] cs_re:green:^...... ..:..:.. \[Note\].* cs_re:magenta:mysqld ended cs_re:red:Can't start server.* # # BOINC # execute boinc_client with -redirectio, it'll then create # stdoutdae.txt and stderrdae.txt files colorscheme:boinc:BOINC http://boinc.berkeley.edu/ cs_re:magenta:: cs_re:magenta:- cs_re:blue|blue,,bold:....-..-.. ..:..:.. cs_re_s:green:^....-..-.. ..:..:.. ([^]]*]) cs_re:yellow:Received signal cs_re:yellow,,blink:This computer is not attached to any projects cs_re:red:gethostbyname failed cs_re:green,,bold:Exit requested by user cs_re:cyan:Rescheduling CPU: application exited cs_re:cyan,,bold:Scheduler list download succeeded cs_re:yellow,,bold:Throughput [0-9]* bytes/sec cs_re:yellow,,underline:Finished upload of file.* cs_re:yellow,,bold/underline:Computation for task.*finished cs_re:red:Project communication failed: cs_re:yellow:Access to reference site succeeded - project servers may be temporarily down. cs_re_s:green,,bold:Requesting ([0-9]*) seconds of new work cs_re:green:Requesting [0-9]* seconds of new work cs_re:white,,bold:Deferring scheduler requests for.* cs_re:white,,bold:Deferring communication for.* cs_re:blue,,bold: [^ ]* download of file.* cs_re:red:Project is down # # acpitail # http://www.vanheusden.com/acpitail/ colorscheme:acpitail:Show temperature/battery/etc info cs_re:magenta:: cs_re:magenta:- cs_re:blue|blue,,bold:... ... [0-9]* ..:..:.. 2... cs_re_val_less:red,,bold:5:^.* remaining capacity: .* .([0-9]*) minutes cs_re_val_less:yellow:10:^.*remaining capacity: .* .([0-9]*) minutes cs_re:red:error.* cs_re_val_bigger:yellow:55:temperature.*: ([0-9]*) cs_re_val_bigger:red,,bold:65:temperature.*: ([0-9]*) # # QMT: clamd colorscheme:qmt-clamd cs_re:blue,,bold:^....-..-.. ..:..:.. cs_re:red:ERROR: cs_re_s:green,,bold:Protecting against ([0-9]*) viruses. cs_re:green:Protecting against [0-9]* viruses. cs_re:red,black,inverse:Exiting.* cs_re_s:red,,bold:^(.*): Unable to open file or directory ERROR cs_re:red:^.*: Unable to open file or directory ERROR cs_re:red,black,inverse:LOGGING DISABLED.* #cs_re:cyan:/var/qmail/simscan/.*(: OK) cs_re:cyan:(: OK) cs_re:magenta:/var/qmail/simscan/.*(: [^ ]* FOUND) cs_re:yellow: LibClamAV Warning.* # # QMT: qmail-smtp colorscheme:qmt-smtp cs_re:blue,,bold:^....-..-.. ..:..:.. cs_re:red:verify failed for SMTP recipient.* cs_re:red: *[^ ]* \[[0-9\.:]*\]: Connection refused cs_re:red,,bold: *[^ ]*policy_check: policy_load failed cs_re:white,,bold: *[^ ]*CLEAN ([^ ]*).* cs_re:white,,bold: *[^ ]*policy_check: policy allows([^ ]*).* cs_re:white,,bold: *[^ ]*RELAYCLIENT:([^ ]*).* cs_re:magenta: *[^ ]*policy_check: policy forbid([^ ]*).* cs_re:magenta:rblsmtpd: ([0-9\.]*).* cs_re:magenta:qmail-smtpd.* cs_re:magenta:spf-reject.* cs_re:magenta: *[^ ]*CHKUSER rejected intrusion: ([^ ]*).* cs_re:magenta: *[^ ]*CHKUSER rejected relaying: ([^ ]*).* cs_re:magenta: *[^ ]*CHKUSER rejected rcpt: ([^ ]*).* cs_re:magenta,,bold: *[^ ]*SPAM REJECT ([^ ]*).* cs_re:magenta,,bold: *[^ ]*:VIRUS:.* cs_re:magenta,,bold: *[^ ]*:ATTACH:.* # # QMT: qmail-send colorscheme:qmt-send cs_re:blue:^....-..-.. ..:..:.. cs_re:white,,bold:starting delivery ([0-9]*) cs_re:white,,bold:delivery *[^ ]*: success:.* cs_re:red,,bold:delivery *[^ ]*: failure:.* cs_re:yellow,,bold:delivery *[^ ]*: deferral:.* # # QMT: SpamAssassin colorscheme:qmt-spamassassin cs_re:blue,,bold:^....-..-.. ..:..:.. #cs_re:cyan,,bold:info: spamd: clean message .* cs_re_s:cyan,,bold:clean message ([^ ]*) for ([^ ]*) in ([^ ]*) seconds, ([^ ]*) bytes. cs_re:magenta,,bold:info: spamd: identified spam.* #cs_re:magenta,,bold:identified spam ([^ ]*) for ([^ ]*) in ([^ ]*) seconds, ([^ ]*) bytes. cs_re_s:magenta,,bold:info: spamd: result: Y ([^ ]*) -.* cs_re:yellow:autolearn=failed cs_re:red,,blink:server killed by [^,]*, shutting down cs_re_s:green,,bold:server successfully spawned child process, pid (.*) cs_re:green:server successfully spawned child process, pid cs_re:yellow: warn.* cs_re:white,,bold:info: spamd: processing message.* # # QMT: sophie colorscheme:qmt-sophie cs_re:blue,,bold:^....-..-.. ..:..:.. cs_re:white,,bold:NOTICE *[^ ]*:.* cs_re:yellow,,bold:Virus present.* cs_re:magenta,,bold:WARNING *[^ ]*: Scan result =.* cs_re:red,,bold:WARNING *[^ ]*: error:.* # Honeypots https://github.com/qeeqbox/honeypots colorscheme:honeypots:honeypots logs https://github.com/qeeqbox/honeypots cs_re_s:green:"action": "(process)" cs_re_s:yellow,,bold:"action": "([^"]+)" cs_re_s:blue,,bold:"dest_port": "([0-9]+)" cs_re_s:blue,,bold:"server": "([^"]+)" cs_re_s:magenta,,bold:"src_ip": "([^"]+)" cs_re:red:error cs_re_s:red,,bold:"error": "([^"]+)" cs_re_s:white,cyan,bold:"uri": "([^"]+)" # Fail2ban colorscheme colorscheme:f2b:Fail2ban logs cs_re_s:magenta,,bold:\[([A-Za-z\-]+)\] cs_re:yellow,,bold:[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3} cs_re:white,,underline/bold:[0-9]{2}:[0-9]{2}:[0-9]{2} cs_re_s:blue:(\[)[A-Za-z] cs_re_s:blue:[A-Za-z](\]) cs_re:red:already banned cs_re:cyan:INFO|NOTICE cs_re:yellow:WARNING cs_re:red:ERROR|FAIL[^ ]+ cs_re_s:magenta:ail[[:space:]]+'(.+)' cs_re:green:started|reloaded cs_re:red:stopped cs_re:cyan:(log(P|p)ath|log(e|E)ncoding|journal(m|M)atch|ignore(i|I)p|ignore(c|C)ommand|fail(r|R)egex|ignore(r|R)egex|find(t|T)ime|ban(t|T)ime|date(p|P)attern|use(d|D)ns|max(r|R)etry|max(l|L)ines|actions|encoding): cs_re:green: Unban cs_re:red: Ban cs_re_s:blue:'(\/[^']+)' # openHAB colorscheme:openhab cs_re:red,,bold:(ERROR|FATAL) cs_re:yellow,,bold:WARN cs_re:green,,bold:INFO cs_re:black,green,bold:TRACE cs_re:black,yellow,bold:DEBUG # Date cs_re_s:,,bold:([0-9]{4}-[0-9]{2}-[0-9]{2}\ [0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}) # Thread cs_re_s:cyan: \[.*\.(\w*?)\s?\] cs_re_s:cyan,,bold: \[(\w*?)\s*\] cs_re_s:blue,,bold: (\[.*?\]). #Class cs_re:cyan:[INFO|WARNING|ERROR|DEBUG|FATAL]\ ([a-zA-Z0-9\.]*)\.. # IP address (with port) cs_re:yellow:[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}(:[0-9]{1,5})+ # Numbers cs_re:magenta:(\b|-)([0-9]{1,})(\.[0-9]*)?\b # States cs_re:red,,bold: (UNDEF|NULL) cs_re:green,,bold: (ON|OPEN|ONLINE|PLAY) cs_re:yellow,,bold: (OFF|CLOSED|OFFLINE|PAUSE) cs_re_s:magenta,,bold: received command (.*?).? cs_re_s:magenta,,bold: changed from (.*) to ([^ ]*?) # Items cs_re_s:yellow: - (.*) changed from cs_re_s:yellow: through (.*) cs_re_s:yellow: Item '(.*)' # Filenames and quotes cs_re:yellow,,bold:\w*?\.(cfg|items|persist|rules|script|sitemap|things|map) cs_re_s:yellow:.*'(.*?)' # others cs_re:red: (failed|Failed|error|Error|Stopped) cs_re:green: (Loading model|Started|Restore\ Values|has\ been\ started|Added\ new\ thing|Connected) # ProFTPD colorscheme:proftpd # Date (default color) cs_re_s:white:^(\w{3}\ \w{3}\ [0-9]{2}\ [0-9]{2}:[0-9]{2}:[0-9]{2}\ [0-9]{4}) # transfer time in seconds cs_re_val_less:green:300:^\S*\ \S*\ \S*\ \S*\ \S*\ ([0-9]*) cs_re_val_equal:yellow:300:^\S*\ \S*\ \S*\ \S*\ \S*\ ([0-9]*) cs_re_val_bigger:red:300:^\S*\ \S*\ \S*\ \S*\ \S*\ ([0-9]*) # ip address cs_re_s:magenta:([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}) # transfered bytes cs_re_val_less:green,,bold:100000000:([0-9]*)\ \S*\ \S*\ \S*\ \S*\ \S*\ \S*\ \S*\ \S*\ \S*\ \S*$ cs_re_val_equal:yellow,,bold:100000000:([0-9]*)\ \S*\ \S*\ \S*\ \S*\ \S*\ \S*\ \S*\ \S*\ \S*\ \S*$ cs_re_val_bigger:red,,bold:100000000:([0-9]*)\ \S*\ \S*\ \S*\ \S*\ \S*\ \S*\ \S*\ \S*\ \S*\ \S*$ # file cs_re_s:white,,bold:(\S*)\ \S*\ \S*\ \S*\ \S*\ \S*\ \S*\ \S*\ \S*\ \S*$ # transfer type cs_re_s:blue,,bold:(a|b)\ \S*\ \S*\ \S*\ \S*\ \S*\ \S*\ \S*\ \S*$ # special action flag cs_re_s:yellow,,bold:(_|C|U)\ \S*\ \S*\ \S*\ \S*\ \S*\ \S*\ \S*$ # direction cs_re_s:red:(i)\ \S*\ \S*\ \S*\ \S*\ \S*\ \S*$ cs_re_s:green:(o)\ \S*\ \S*\ \S*\ \S*\ \S*\ \S*$ # access mode (a=anonymous, r=user) cs_re_s:black,red,bold:(a)\ \S*\ \S*\ \S*\ \S*\ \S*$ cs_re_s:cyan:(r)\ \S*\ \S*\ \S*\ \S*\ \S*$ # user name cs_re_s:cyan,,bold:^\S*\ \S*\ \S*\ \S*\ \S*\ \S*\ \S*\ \S*\ \S*\ \S*\ \S*\ \S*\ \S*\ (\S*)\ \S*\ \S*\ \S*\ \S* # service name cs_re_s:yellow,,bold:(\S*)\ \S*\ \S*\ \S*$ # auth mode cs_re_s:blue,,bold:\ (0|1)\ \S*\ \S*$ # user ID cs_re_s:cyan:\ (\S*)\ \S*$ # status (in/complete) cs_re_s:green,,bold:(c)$ cs_re_s:red,,bold:(i)$ # Kodi colorscheme:kodi cs_re:white,,bold:^....-..-..\ ..:..:..\..... cs_re:blue:T:[0-9]* cs_re:red,,bold:\s(ERROR|error) cs_re:yellow,,bold:\s(WARNING|warning) cs_re:green,,bold:\s(NOTICE|notice) cs_re:black,yellow,bold:(DEBUG|debug) cs_re:white,,bold:(INFO|info) cs_re:green:Opening: cs_re_s:yellow,,underline:.*Opening: (.*) cs_re:red:(CloseFile\(\)|:OnExit\(\)) cs_re_s:cyan:: ([0-9a-zA-Z\ ]*?): # MQTT colorscheme:mqtt # Topics cs_re_s:white,,bold:\/?([^\/\ ]*)\/ cs_re_s:cyan,,bold:/(\w*). cs_re_s:cyan,,bold:/(\$\w*). cs_re_s:magenta,,bold:/(set). cs_re_s:cyan,,bold:/(\w*)/set cs_re:magenta,,bold:/ cs_re_s:white,,bold:/(\$\w*) cs_re:white:^(\w|/|\$)* cs_re_s:white: \[.*\.(\w*?)\s?\] # Values cs_re:green,,bold:(on|ON|OPEN|ONLINE|Online|true) cs_re:yellow,,bold:(off|OFF|CLOSE|CLOSED) cs_re:red,,bold:(OFFLINE|Offline|false) cs_re_s:red,,bold: ([0-9]{1,3}),[0-9]{1,3},[0-9]{1,3} cs_re_s:green,,bold: [0-9]{1,3},([0-9]{1,3}),[0-9]{1,3} cs_re_s:blue,,bold: [0-9]{1,3},[0-9]{1,3},([0-9]{1,3}) cs_re_s:white,,bold: [0-9]{1,3},[0-9]{1,3},[0-9]{1,3},([0-9]{1,3}) cs_re_s:magenta: ([a-zA-Z0-9_,.]*) # JSON Payload cs_re_s:yellow:,("\w*") cs_re_s:yellow: ("\w*") cs_re_s:yellow:\{("\w*") cs_re_s:magenta:\:("\w*") cs_re_s:magenta:\:(\w*) cs_re_s:magenta:\:(".*") # Cisco IOS and IOS-XE colorscheme:cisco_ios # IP address and port cs_re:yellow,,bold:[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3} cs_re_s:yellow:[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\(([0-9]*?)\) cs_re_s:yellow:localport\: ([0-9]*?) # interfaces cs_re:magenta:\s(\w?+Ethernet|GigE|Fa|Gi|Te|Twe|Fo|Fi|Hu|TH) cs_re_s:magenta,,bold:(Ethernet|GigE|Fa|Gi|Te|Twe|Fo|Fi|Hu|TH)([0-9]+/[0-9]+(/[0-9]+|)) cs_re:green,,bold:\b(up|on)\b # User cs_re_s:magenta,,bold:User (\w+?) cs_re_s:magenta,,bold:user: (\w+?) # log levels cs_re:black,red:%\S*-0-\S* cs_re:black,red:%\S*-1-\S* cs_re:red:%\S*-2-\S* cs_re:red:%\S*-3-\S* cs_re:yellow:%\S*-4-\S* cs_re:yellow:%\S*-5-\S* cs_re:green:%\S*-6-\S* cs_re:black,white:%\S*-7-\S* # access lists cs_re_s:,,bold: list ([0-9]+) cs_re_s:blue,,bold: list [0-9]+ denied ([a-z0-9]*+). cs_re_s:blue,,bold: list [0-9]+ permitted ([a-z0-9]*+). cs_re:green,,bold:\bpermitted\b cs_re:red,,bold:\bdenied\b # misc cs_re:red,,bold:\b(down|off)\b cs_re_s:,,bold:[Pp]ower\ [Ss]upply\ ([0-9]) cs_re:red,,bold:\bfaulty\b cs_re:red,,bold:error cs_re:red,,bold:high alarm cs_re:yellow,,bold:low alarm # Cisco ASA # This and is mainly for VPN logs. # The colorscheme cisco_ios should be used after this one. colorscheme:cisco_asa # User cs_re_s:magenta,,bold:User <(\w+?)> cs_re_s:magenta,,bold:Username: (\w+?) cs_re_s:magenta,,bold:User=(\w+?) cs_re_s:magenta,,bold:Username = (\w+?) cs_re_s:magenta,,bold:LOCAL\\(\w+?) # GroupPolicy cs_re_s:blue,,bold: Group <(\S+?)> cs_re_s:blue,,bold: Group = (\S+?) cs_re_s:blue,,bold:GroupPolicy <(\S+?)> # TunnelGroup cs_re_s:cyan,,bold:TunnelGroup <(\S+?)> # Assigned IP cs_re_s:green,,bold:IPv4 Address <([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})> cs_re_s:green,,bold: Assigned IP=([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}) # dovecot colorscheme:dovecot cs_re:cyan:: cs_re:blue|blue,,bold:^... .. ..:..:.. cs_re:yellow,,bold:imap-login cs_re:yellow,,bold:pop3-login cs_re:green:<[^>]*> cs_re:red:auth failed cs_re:magenta:TLS # # colorscript: colorscripts are external scripts that decide what colors to use # for input they receive the line that needs colors # as a result they emit: start,end,foreground color,background color,attributes\n # ...\n # \n <- an empty line (only a linefeed) indicates # end of parameters for this line # start offset: what position these colors/attributes start # end offset: the position AFTER the last character for which the attributes are # valid # do NOT use spaces in each line! colorscript:cscriptexample:/etc/multitail/colors-example.pl:this is a barely functional example script # # # default colorschemes: scheme:uucp:/var/log/uucp/ scheme:zarafa:/var/log/zarafa/ scheme:postfix:/var/log/mail/ scheme:sendmail:/var/log/mail/ scheme:exim:/var/log/mail/ scheme:apache:/var/log/apache/.*access scheme:apache:/var/log/lighttpd/.*access scheme:apache_error:/var/log/apache/.*error scheme:asterisk:/var/log/asterisk/messages scheme:samba:/var/log/samba/ scheme:squid:/var/log/squid/ scheme:squid:/var/log/squid3/ scheme:openhab:/var/log/openhab2/ scheme:proftpd:xferlog scheme:syslog,ssh:/var/log/ scheme:vnetbr:bplog.rest scheme:procmail:procmail.log scheme:inn:/var/log/news/ scheme:snort:/var/log/snort/alert scheme:boinc:/var/lib/boinc-client/std...dae.txt scheme:qmt-send:/var/log/qmail/send/current scheme:qmt-smtp:/var/log/qmail/smtp/current scheme:qmt-smtp:/var/log/qmail/submission/current scheme:qmt-clamd:/var/log/qmail/clamd/current scheme:qmt-spamassassin:/var/log/qmail/spam/current scheme:qmt-sophie:/var/log/qmail/sophie/current scheme:honeypots:/var/log/honeypots/ scheme:f2b:/var/log/fail2ban.log scheme:kodi:kodi.log # the following line is to show you that colorscripts can be used the same way as colorschemes scheme:cscriptexample:/dev/null # # default number of lines to buffer FOR THESE PATTERNS: #default_nlines:500:/var/log/apache/*.access #default_bytes:100kb:/var/log/ # # default number of lines to buffer globally # one can set only 1 of these two #global_default_nlines:500 global_default_nkb:1MB # # how often to check for mail # set to 0 to disable # default is every 5 seconds check_mail:0 # # where to find the 'xclip' binary - used to send a buffer # to the X clipboard #xclip:/usr/bin/xclip # # where to find the 'pbcopy' binary - used to send a buffer # to the clipboard (OSX-only) #pbcopy:/usr/bin/pbcopy # # width of a TAB-character. in the VI editor this is, for # example, 8. default in multitail is 4 tab_stop:8 # # what program to start when the user presses a key # ...:x:... just that key # ...:^x:... that key with control bind:g:/usr/bin/pine bind:^k:/bin/bash bind:^l:/usr/bin/telnet bind:^s:/bin/su # # enable things with the xterm title bar # %f changed file # %h hostname # %l system load # %m "New mail" or nothing # %u username # %t date + time titlebar:%m %u@%h %f (%t) [%l] # # how timestrings will look when using '-ts' line_ts_format:%Y/%m/%d %H:%M:%S # # where to find tail. this is used when you don't use the default tail (using # turbotail for example) # or your tail is located somewhere else #tail:/usr/bin/tail # wether this tail only understands posix commands (=yes) posix_tail:no # # use bright colors #bright:1 # # wether to abbreviate filesizes to xKB/MB/GB abbreviate_filesize:on # # show number of subwindow? show_subwindow_id:off # # attributes for the marker-line markerline_color:red,black,reverse # # show timestamp in markerline? markerline_timestamp:on # whot character to print in the markerline markerline_char:- # # attributes for the line that is printed when multitail switches subwindow changeline_color:blue,white,bold/reverse changeline_char:- # # line printed when nothing happens in a window for a while idleline_color:yellow,black,bold/reverse idleline_char:- # # line printed for multitail messages (regexp errors, file truncated, etc) msgline_color:magenta,black,bold/reverse msgline_char:- # # when converting to a timestring, use this format: # (also for markerline) ts_format:%b %d %H:%M:%S # # timestring format for conversions cnv_ts_format:%b %d %H:%M:%S %Y # # timestring format for statusline statusline_ts_format:%Y/%m/%d %H:%M:%S # # put statusline above the data instead below it? statusline_above_data:no # # statusline attributes statusline_attrs:white,black,reverse # # conversions # note: the part you want to have replaced must be between '(' and ')' # possible conversions: # ip4tohost: an ip-address in 4 byte dotted format to a hostname # epochtodate: a value representing the number of seconds since 1970 to # a time-string. format of the string can be set with # "ts_format". # errnotostr: an errno-value to the error message it represents # hextodec: a hex-value into its decimal version # dectohex: a decimal value into hexadecimal # tai64todate: converts a TAI64 string to a datestring (see http://cr.yp.to/libtai/tai64.html#tai64n for details) # script this is a special case: it requires an extra parameter (before the regular expression): this parameter # selects a script (perl, bash, as long as it is executable) which loops forever and then reads a line # ending with \n, processes it and then emits the converted output also ending with \n (in 1 write!) # Please note: when using perl, disable i/o buffering! # abbrtok: abbreviates a value to KB/MB/GB # signrtostring: signal number to descriptive name convert:apache:ip4tohost:^([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}) convert:squid:epochtodate:^([0-9]*) convert:squid:ip4tohost:^[^ ]* *[0-9]* *([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}) convert:asterisk:epochtodate:^([0-9]*).([0-9]*) convert:nagios.log:epochtodate:^.([0-9]*). convert:qmailtimestr:tai64todate:^(@[^ ]*) convert:geoip:script:/etc/multitail/convert-geoip.pl:([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}) # # shell to invoke shell:/bin/sh # # default scrollback linewrap mode # default is yes (do wrap) dsblwm:yes # # should a popup box be displayed when a window closes by itself? warn_closed:yes # # allow 8 bit ascii? (e.g. umlauts etc.) allow_8bit:yes # # beep, flash, popup or none when error? beep_method:flash # in case of beep_method:popup; how long to display the popup before it # automatically disappears (in seconds) beep_popup_length:0.1 # # ^x instead of an inverse . caret_notation:yes # # what umask to use when creating files umask:0022 # # how often to refresh popups (in case applicable) popup_refresh_interval:5 # # print a markerline when one merges multiple logfiles and tail changes # from one logfile to an other global_mark_change:no # default settings for files selected with a regular expression(!) default_mark_change:yes:/var/log/apache/.* # replace_by_markerline:-- MARK -- # # what to buffer by default 'a'll or what went through the 'f'ilter default_bufferwhat:f # # should searches be case insensitive? press 'I' in the main menu to toggle at run-time searches_case_insensitive:no # # default linewrap mode # a: all # l: left # r: right # s: syslog # S: syslog w/o procname # o: offset # w: wordwrap default_linewrap:a # # follow filename instead of descriptor? follow_filename:yes # # filters (complete lines) filterscheme:syslog:removes '----mark----' and such rule:ev:---- MARK ---- # on what file(s) to use the filter by default usefilterscheme:syslog:/var/log/messages # # edits (part of lines) # the type (e.g. 'ke') is like the command line switches -ke/-kS/-kr etc. editscheme:syslog:removes '----' editrule:ke:---- editrule:ke:make # on what file(s) to use the filter by default useeditscheme:syslog:/var/log/messages # # close windows when the end-of-file was reached? close_closed_windows:yes # # should we skip empty lines? (the scrollback window always displays them) suppress_empty_lines:yes # # how the splitline should look like splitline_attrs:white,black,reverse # can be attr (use attributes defined with splitline_attrs), regular (use statusline attributes), none (display none) splitline:attributes # # what attributes to use when displaying things in reverse (-eC/-ec, highlight in main menu) inverse:bold/reverse # # what key must the user press to abort an action? e.g. a menu or so # this parameter expects an ascii-value # e.g. control + a = 1 # escape = 27 - please note that you would have to press it twice # ^g = 7 abort_key:7 # what key to press to exit multitail # ^c = 3 exit_key:3 # default_convert:apache:/var/log/apache/.*access default_convert:apache:/var/log/lighttpd/.*access default_convert:asterisk:/var/log/asterisk/messages default_convert:squid:/var/log/squid/ default_convert:squid:/var/log/squid3/ default_convert:qmailtimestr:/var/log/qmail/qmail.smtpd.log # # when a buffer (for scrollback) gets too full, some lines must be freed (unless one sets the buffersize to unlimited) # with this parameter one can set the minimum lines to free. this parameter is implemented for efficiency as with a # 1MB buffer buffer management starts to use quit a bit of processortime min_shrink:10 # # when scrolling or searching through the logging, show (sub-)window-nrs? (toggle with ^t) # press 't' to see a list of window-nrs and what's displayed in them scrollback_show_winnrs:no # # for word-wrap: what is the max length of a word which should still be wrapped wordwrapmaxlength:31 # # for searches, multitail can remember a history which can be retrieved with ^r or cursorkey down in places where # you can enter searchstrings # set 'history_size' to 0 to disable this feature searchhistory_file:~/.multitail.searchhistory # how many strings to remember searchhistory_size:15 # # like searchhistory only for filenames, pathnames and commands cmdfile_history_file:~/.multitail.cmdfilehistory cmdfile_history_size:30 # # default background color # comment this line to use the default terminal color #default_background_color:black # # in search-fields: give an empty edit-field or the previously used search string reuse_searchstring:no # # how many initial lines to tail at least initially (if available) min_n_bufferlines:50 # # what characters to use for the borders around popups and such # comment-out to use defaults #box_bottom_left_hand_corner:+ #box_bottom_right_hand_corner:+ #box_bottom_side:- #box_left_side:| #box_right_side:| #box_top_left_hand_corner:+ #box_top_right_hand_corner:+ #box_top_side:- # # text to put in front of line with window-number window_number:[%02d] subwindow_number:[%02d] # # parameters for --limit / --Limit # format of timestamp in logging syslog_ts_format:%Y/%m/%d %H:%M:%S # show ip addresses or hostnames? resolv_ip_addresses:yes # show severity/facility? not shown in regular syslogd show_severity_facility:yes # # should scrollback default to fullscreen (default is no) # scrollback_fullscreen_default:yes # # suppress colors in the scollback window? this speeds up scrolling a little scrollback_no_colors:no # # when you search in the scrollback: open new window with found strings (= on) # or jump to the next found (= off) scrollback_search_new_window:yes # # set to (yes) to map delete key as backspace key. # this is useful if you are using mac map_delete_as_backspace:no # ---- # # uwsgi log https://gist.github.com/karolyi/3032020b4d62c6fd0b9148db1a242548 colorscheme:uwsgi:https://uwsgi-docs.readthedocs.io/en/latest/ # HTTP method highlighting cs_re_s:cyan:\{[0-9]+ vars in [0-9]+ bytes\} \[.*\] (GET) cs_re_s:magenta:\{[0-9]+ vars in [0-9]+ bytes\} \[.*\] (POST|PUT|DELETE|OPTIONS) # Response time highlighting: >500: yellow, >750ms: magenta-bold, >1000ms: red-bold cs_re_val_bigger:red,,bold:1000:generated [0-9]+ bytes in ([0-9]+) msecs cs_re_val_bigger:magenta,,bold:750:generated [0-9]+ bytes in ([0-9]+) msecs cs_re_val_bigger:yellow:500:generated [0-9]+ bytes in ([0-9]+) msecs cs_re_s:green:generated [0-9]+ bytes in ([0-9]+) msecs # Request path highlighting with underline cs_re_s:white,,underline:\[pid: [0-9]+\|app: [0-9]+\|req: [0-9]+\/[0-9]+\].*[A-Z]+ ([^ ]+) # HTTP statuscode highlighting cs_re_val_bigger:red,,bold:499:generated [0-9]+ bytes in [0-9]+ msecs \(HTTP\/[0-9]+\.[0-9]+ ([0-9]+)\) cs_re_val_bigger:red:399:generated [0-9]+ bytes in [0-9]+ msecs \(HTTP\/[0-9]+\.[0-9]+ ([0-9]+)\) cs_re_val_bigger:yellow:299:generated [0-9]+ bytes in [0-9]+ msecs \(HTTP\/[0-9]+\.[0-9]+ ([0-9]+)\) cs_re_s:white,,bold:generated [0-9]+ bytes in [0-9]+ msecs \(HTTP\/[0-9]+\.[0-9]+ ([0-9]+)\) # IP address highlighting cs_re_s:blue,,bold:\[pid: [0-9]+\|app: [0-9]+\|req: [0-9]+\/[0-9]+\] ([0-9a-f:\.]+) # Time highlighting cs_re_s:white,,bold:\{[0-9]+ vars in [0-9]+ bytes\} (\[).* ([0-9]+:[0-9]+:[0-9]+) .*(\]) scheme:uwsgi:/var/log/uwsgi # ---- multitail-7.1.5/my_pty.c000066400000000000000000000110301465663511400152230ustar00rootroot00000000000000#define _LARGEFILE64_SOURCE /* required for GLIBC to enable stat64 and friends */ /* I read somewhere that this is needed on HP-UX */ #define _INCLUDE_HPUX_SOURCE #define _INCLUDE_POSIX_SOURCE #define _INCLUDE_XOPEN_SOURCE #define _INCLUDE_XOPEN_SOURCE_EXTENDED #define _INCLUDE_AES_SOURCE #include #include #include #include #include #include #include #include #include #include #include "mt.h" #ifdef __APPLE__ #include #endif #ifdef __OpenBSD__ #include #endif #if defined(linux) || defined(__CYGWIN__) || defined(__GNU__) || defined(__GLIBC__) #include #endif #if defined(__FreeBSD__) || defined(__minix) #include #endif #if defined(sun) || defined(__sun) #include #include #endif #if defined(IRIX) || defined(IRIX64) #endif #if defined(AIX) #endif #if defined(_HPUX_SOURCE) #include #include #include #include #include #include #include #include #endif #include #include "error.h" /* the following code was mostly taken from: */ /* $NetBSD: sshpty.c,v 1.8 2002/10/15 15:33:04 manu Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * Allocating a pseudo-terminal, and making it the controlling tty. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ /* additional code for *BSD/Linux/Apple, AIX and IRIX by folkert@vanheusden.com */ int get_pty_and_fork(int *fd_master, int *fd_slave) { #if defined(__FreeBSD__) || defined(linux) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) || defined(__CYGWIN__) || defined(__GNU__) || defined(__GLIBC__) || defined(__minix) if (openpty(fd_master, fd_slave, NULL, NULL, NULL) == -1) { error_exit(TRUE, FALSE, "openpty() failed.\n"); return -1; } return fork(); #elif defined(sun) || defined(__sun) || defined(AIX) || defined(_HPUX_SOURCE) || defined(OSF1) || defined(scoos) /* * This code is used e.g. on Solaris 2.x. (Note that Solaris 2.3 * also has bsd-style ptys, but they simply do not work.) */ int ptm; char *pts; pid_t pid; #if defined(AIX) char *multiplexer = "/dev/ptc"; #else char *multiplexer = "/dev/ptmx"; #endif ptm = myopen(multiplexer, O_RDWR | O_NOCTTY); if (ptm < 0) { error_exit(TRUE, FALSE, "Error opening %s.\n", multiplexer); return -1; } *fd_master = ptm; pid = fork(); if (pid == 0) { if (grantpt(ptm) < 0) error_exit(TRUE, FALSE, "grantpt() failed.\n"); if (unlockpt(ptm) < 0) error_exit(TRUE, FALSE, "unlockpt() failed.\n"); setsid(); /* lose old controlling terminal (FvH) */ pts = ptsname(ptm); if (pts == NULL) error_exit(TRUE, FALSE, "Slave pty side name could not be obtained.\n"); /* Open the slave side. */ *fd_slave = myopen(pts, O_RDWR | O_NOCTTY); if (*fd_slave < 0) error_exit(TRUE, FALSE, "Problem opening slave-side of pseudo tty (file '%s').\n", pts); #if !defined(AIX) && !defined(scoos) /* Push the appropriate streams modules, as described in Solaris pts(7). */ if (ioctl(*fd_slave, I_PUSH, "ptem") < 0) error_exit(TRUE, FALSE, "ioctl I_PUSH ptem failed.\n"); if (ioctl(*fd_slave, I_PUSH, "ldterm") < 0) error_exit(TRUE, FALSE, "ioctl I_PUSH ldterm failed.\n"); (void)ioctl(*fd_slave, I_PUSH, "ttcompat"); /* not on HP-UX? */ #endif } return pid; #elif defined(IRIX) || defined(IRIX64) char *line = _getpty(fd_master, O_RDWR | O_NDELAY, 0600, 0); if (line == NULL) error_exit(TRUE, FALSE, "_getpy() failed.\n"); *fd_slave = myopen(line, O_RDWR); if (*fd_slave < 0) error_exit(TRUE, FALSE, "Error while openening file %s.\n", line); return fork(); #else #error I'm sorry, but I don't know what kind of system this is. #error Because of that I do not know how to do openpty() on your #error system. Please contact me at folkert@vanheusden.com and #error tell me what kind of system you have and please give me #error the output of #error man openpty #error strings `which gcc` #error strings `which cc` #error ls -lR /usr/include #error Thank you in advance and sorry for all the hassle! #endif } multitail-7.1.5/my_pty.h000066400000000000000000000000651465663511400152360ustar00rootroot00000000000000int get_pty_and_fork(int *fd_master, int *fd_slave); multitail-7.1.5/scrollback.c000066400000000000000000000514301465663511400160310ustar00rootroot00000000000000#define _LARGEFILE64_SOURCE /* required for GLIBC to enable stat64 and friends */ #include #include #include #include #include #include #include #include #include #include "mt.h" #include "error.h" #include "my_pty.h" #include "utils.h" #include "term.h" #include "help.h" #include "mem.h" #include "ui.h" #include "misc.h" #include "globals.h" #include "clipboard.h" int scrollback_search_to_new_window(buffer *pbuf, char *org_title, char *find_str, mybool_t case_insensitive); int find_string(buffer *pbuf, char *find, int offset, char direction, mybool_t case_insensitive) { int loop, index = -1, rc; regex_t regex; /* compile the searchstring (which can be a regular expression) */ if ((rc = regcomp(®ex, find, REG_EXTENDED | (case_insensitive == MY_TRUE?REG_ICASE:0)))) { regexp_error_popup(rc, ®ex); return -1; /* failed -> not found */ } if (direction == 1) { for(loop=offset; loop curpos; loop++) { if ((pbuf -> be)[loop].Bline == NULL) continue; if (regexec(®ex, (pbuf -> be)[loop].Bline, 0, NULL, 0) == 0) { index = loop; break; } } } else if (direction == (char)-1) { for(loop=offset; loop>=0; loop--) { if ((pbuf -> be)[loop].Bline == NULL) continue; if (regexec(®ex, (pbuf -> be)[loop].Bline, 0, NULL, 0) == 0) { index = loop; break; } } } regfree(®ex); return index; } void scrollback_find_popup(char **find_str, mybool_t *pcase_insensitive) { char *dummy; NEWWIN *mywin = create_popup(5, 40); win_header(mywin, "Find"); dummy = edit_string(mywin, 3, 2, 40, 80, 0, reuse_searchstring?*find_str:NULL, HELP_SCROLLBACK_EDIT_SEARCH_STRING, -1, &search_h, pcase_insensitive); myfree(*find_str); *find_str = dummy; delete_popup(mywin); } void scrollback_savefile(buffer *pbuf) { char *file = NULL; NEWWIN *mywin = create_popup(8, 40); win_header(mywin, "Save buffer to file"); mvwprintw(mywin -> win, 4, 2, "Select file"); file = edit_string(mywin, 5, 2, 40, find_path_max(), 0, NULL, HELP_SCROLLBACK_SAVEFILE_ENTER_FILENAME, -1, &cmdfile_h, NULL); if (file) { FILE *fh = fopen(file, "w"); if (fh) { int loop; for(loop=0; loop curpos; loop++) { if ((pbuf -> be)[loop].Bline) { char display = 1; char *error; int dummy = -1; regmatch_t *pmatch = NULL; /* check filter */ if (!IS_MARKERLINE((pbuf -> be)[loop].pi)) { (void)check_filter((pbuf -> be)[loop].pi, (pbuf -> be)[loop].Bline, &pmatch, &error, &dummy, 0, &display); if (error) { fprintf(fh, "%s\n", error); myfree(error); } } if (display) { fprintf(fh, "%s\n", USE_IF_SET((pbuf -> be)[loop].Bline, "")); } if (pmatch) myfree(pmatch); } } fclose(fh); } else { error_popup("Save scrollback buffer", -1, "Cannot write to file, reason: %s", strerror(errno)); } } delete_popup(mywin); } int get_lines_needed(char *string, int terminal_width) { if (string) return (strlen(string) + terminal_width - 1) / terminal_width; else return 1; } void scrollback_displayline(int window_nr, NEWWIN *win, buffer *pbuf, int buffer_offset, int terminal_offset, int offset_in_line, mybool_t force_to_winwidth, char show_winnr) { char *cur_line = (pbuf -> be)[buffer_offset].Bline; wmove(win -> win, terminal_offset, 0); if (cur_line) { proginfo *cur_line_meta = (pbuf -> be)[buffer_offset].pi; double ts = (pbuf -> be)[buffer_offset].ts; char old_color_settings = 0; if (scrollback_no_colors && cur_line_meta != NULL) { old_color_settings = cur_line_meta -> cdef.colorize; cur_line_meta -> cdef.colorize = 0; } if (IS_MARKERLINE(cur_line_meta)) { color_print(window_nr, win, cur_line_meta, cur_line, NULL, -1, MY_FALSE, 0, 0, ts, show_winnr); } else /* just a buffered line */ { char *error = NULL; regmatch_t *pmatch = NULL; int matching_regex = -1; char display; (void)check_filter(cur_line_meta, cur_line, &pmatch, &error, &matching_regex, 0, &display); if (error) { color_print(window_nr, win, cur_line_meta, error, NULL, -1, MY_FALSE, 0, 0, ts, show_winnr); myfree(error); } if (display) { if (offset_in_line) { int line_len = strlen(cur_line); int new_size = 0; if (offset_in_line < line_len) new_size = min(win -> width, line_len - offset_in_line); color_print(window_nr, win, cur_line_meta, cur_line, pmatch, matching_regex, force_to_winwidth, offset_in_line, offset_in_line + new_size, ts, show_winnr); } else { color_print(window_nr, win, cur_line_meta, cur_line, pmatch, matching_regex, force_to_winwidth, 0, 0, ts, show_winnr); } } myfree(pmatch); } if (scrollback_no_colors && cur_line_meta != NULL) { cur_line_meta -> cdef.colorize = old_color_settings; } } else /* an empty line */ { /* do nothing */ } } void compute_text_dimensions(int *nlines, int *ncols, char fullscreen) { if (fullscreen) { *nlines = max_y; *ncols = max_x; } else { *nlines = max_y - 6; *ncols = max_x - 6; } } void create_scrollback_windows(NEWWIN **mywin1, NEWWIN **mywin2, int nlines, int ncols, char fullscreen) { /* Delete existing windows, if any. */ if (*mywin1) { delete_popup(*mywin1); } if (*mywin2) { delete_popup(*mywin2); } /* Re-create windows, according to fullscreen flag */ if (fullscreen) { *mywin1 = NULL; *mywin2 = create_popup(max_y, max_x); scrollok((*mywin2) -> win, FALSE); /* supposed to always return OK, according to the manpage */ } else { *mywin1 = create_popup(max_y - 4, max_x - 4); *mywin2 = create_popup(nlines, ncols); scrollok((*mywin2) -> win, FALSE); /* supposed to always return OK, according to the manpage */ } } int scrollback_do(int window_nr, buffer *pbuf, int *winnrs, char *header) { int rc = 0; char *find = NULL; char fullscreen = scrollback_fullscreen_default; NEWWIN *mywin1 = NULL, *mywin2 = NULL; int nlines, ncols; compute_text_dimensions(&nlines, &ncols, fullscreen); int offset = max(0, pbuf -> curpos - nlines); /* FIXME: aantal regels laten afhangen van lengte */ char redraw = 2; int line_offset = 0; char show_winnr = default_sb_showwinnr; mybool_t case_insensitive = re_case_insensitive; buffer cur_lb; int loop = 0; memset(&cur_lb, 0x00, sizeof(cur_lb)); for(loop=0; loop curpos; loop++) { if ((pbuf -> be)[loop].Bline == NULL) continue; cur_lb.be = myrealloc(cur_lb.be, (cur_lb.curpos + 1) * sizeof(buffered_entry)); cur_lb.be[cur_lb.curpos].pi = (pbuf -> be)[loop].pi; if ((pbuf -> be)[loop].pi != NULL && (!IS_MARKERLINE((pbuf -> be)[loop].pi)) && (pbuf -> be)[loop].pi -> cdef.term_emul != TERM_IGNORE) { color_offset_in_line *cmatches; int n_cmatches; cur_lb.be[cur_lb.curpos].Bline = emulate_terminal((pbuf -> be)[loop].Bline, &cmatches, &n_cmatches); myfree(cmatches); } else cur_lb.be[cur_lb.curpos].Bline = strdup((pbuf -> be)[loop].Bline); cur_lb.be[cur_lb.curpos].ts = (pbuf -> be)[loop].ts; cur_lb.curpos++; } LOG("---\n"); if (global_highlight_str) { find = mystrdup(global_highlight_str); } create_scrollback_windows(&mywin1, &mywin2, nlines, ncols, fullscreen); for(;;) { int c, uc; if (redraw == 2) { int index = 0; int lines_used = 0; if (mywin1) { ui_inverse_on(mywin1); mvwprintw(mywin1 -> win, nlines + 1, 1, "%s - %d buffered lines", shorten_filename(header, max(24, ncols - 24)), cur_lb.curpos); ui_inverse_off(mywin1); if (!no_linewrap) ui_inverse_on(mywin1); mvwprintw(mywin1 -> win, nlines + 1, ncols - 8, "LINEWRAP"); if (!no_linewrap) ui_inverse_off(mywin1); } werase(mywin2 -> win); if (!no_linewrap && line_offset > 0) { int temp_line_offset = line_offset; int n_chars_to_display_left = strlen((cur_lb.be)[offset].Bline) - temp_line_offset; while(lines_used < nlines && n_chars_to_display_left > 0) { scrollback_displayline(winnrs?winnrs[offset]:window_nr, mywin2, &cur_lb, offset, lines_used, temp_line_offset, 1, show_winnr); temp_line_offset += ncols; n_chars_to_display_left -= ncols; lines_used++; } index++; } for(;(offset + index) < cur_lb.curpos && lines_used < nlines;) { int lines_needed = get_lines_needed((cur_lb.be)[offset + index].Bline, ncols); if (no_linewrap || lines_needed == 1) { scrollback_displayline(winnrs?winnrs[offset + index]:window_nr, mywin2, &cur_lb, offset + index, lines_used, no_linewrap?line_offset:0, no_linewrap, show_winnr); lines_used++; } else { int cur_line_offset = 0; while(lines_used < nlines && lines_needed > 0) { scrollback_displayline(winnrs?winnrs[offset + index]:window_nr, mywin2, &cur_lb, offset + index, lines_used, cur_line_offset, 1, show_winnr); cur_line_offset += ncols; lines_used++; lines_needed--; } } index++; } redraw = 1; } if (redraw == 1) { mydoupdate(); redraw = 0; } c = wait_for_keypress(HELP_SCROLLBACK_HELP, 0, NULL, 1); uc = toupper(c); if (c == 'q' || c == abort_key || c == KEY_CLOSE || c == KEY_EXIT) { break; } else if (c == 'Q' || c == -1) /* Q: close whole stack of scrollbackwindows, -1: something got closed */ { rc = -1; break; } else if (c == 20 && winnrs != NULL) /* ^t */ { show_winnr = 1 - show_winnr; redraw = 2; } else if (c == 'x') { send_to_clipboard(pbuf); } else if (c == 'Y') { no_linewrap = !no_linewrap; redraw = 2; line_offset = 0; } else if (c == KEY_F(9) || c == 23) /* ^w */ { fullscreen = ! fullscreen; compute_text_dimensions(&nlines, &ncols, fullscreen); create_scrollback_windows(&mywin1, &mywin2, nlines, ncols, fullscreen); redraw = 2; } else if (c == 't') { statistics_menu(); } else if ((c == KEY_LEFT || c == KEY_BACKSPACE) && no_linewrap) { if (line_offset > 0) line_offset--; redraw = 2; } else if (c == KEY_SLEFT && no_linewrap) { if (line_offset >= (ncols / 2)) line_offset -= (ncols / 2); else line_offset = 0; redraw = 2; } else if (c == KEY_SRIGHT && no_linewrap) { line_offset += (ncols / 2); redraw = 2; } else if (c == KEY_BEG && no_linewrap) { if (line_offset) { line_offset = 0; redraw = 2; } } else if (c == KEY_BTAB) { if (line_offset >= 4) line_offset -= 4; else line_offset = 0; redraw = 2; } else if (c == KEY_RIGHT && no_linewrap) { line_offset++; redraw = 2; } else if ((c == KEY_UP || c == 'y' || c == 25 || /* ^y */ c == 'k' || /* c == 11 || */ /* ^k */ c == 16) /* ^p */ && (offset > 0 || (!no_linewrap && line_offset > 0))) { if (no_linewrap) { offset--; } else if (line_offset > 0) { line_offset = max(0, line_offset - ncols); } else { offset--; line_offset = (get_lines_needed((cur_lb.be)[offset].Bline, ncols) - 1) * ncols; } wmove(mywin2 -> win, 0, 0); winsdelln(mywin2 -> win, 1); scrollback_displayline(winnrs?winnrs[offset]:window_nr, mywin2, &cur_lb, offset, 0, line_offset, no_linewrap, show_winnr); redraw = 1; } else if ((c == KEY_DOWN || c == 'e' || c == 5 || /* ^e */ c == 'j' || c == 14 || /* ^n */ c == 13 || c == KEY_ENTER) && offset < (cur_lb.curpos - 1)) { if (no_linewrap) { offset++; } else if (strlen((cur_lb.be)[offset].Bline) > (line_offset + ncols)) { line_offset += ncols; } else if (offset < (cur_lb.curpos - 1)) { if (strlen((cur_lb.be)[offset].Bline) > (line_offset + ncols)) line_offset += ncols; else { line_offset = 0; offset++; } } redraw = 2; } else if ((c == KEY_NPAGE || c == 'f' || c == 6 || /* ^f */ c == ('V' - 65 + 1) || /* ^v */ c == ' ' || c == 'z' || c == 'u' || c == ('U' - 65 + 1)) /* ^u */ && offset < (cur_lb.curpos - 1)) { if (no_linewrap) { offset += nlines; if (offset >= cur_lb.curpos) offset = cur_lb.curpos - 1; } else { int n_lines_to_move = nlines; while(n_lines_to_move > 0 && offset < (cur_lb.curpos)) { if (line_offset > 0) { if (line_offset + ncols >= strlen((cur_lb.be)[offset].Bline)) { line_offset = 0; offset++; n_lines_to_move--; } else { line_offset += ncols; n_lines_to_move--; } } else { n_lines_to_move -= get_lines_needed((cur_lb.be)[offset].Bline, ncols); offset++; } } if (n_lines_to_move < 0) line_offset = (-n_lines_to_move) * ncols; } redraw = 2; } else if ((c == KEY_PPAGE || c == 'b' || c == 2 || /* ^b */ c == 'w' || c == 'd' || c == 4) /* ^d */ && offset > 0) { if (no_linewrap) { offset -= nlines; if (offset < 0) offset = 0; } else { int n_lines_to_move = nlines; if (line_offset) n_lines_to_move -= line_offset / ncols; while(n_lines_to_move > 0 && offset > 0) { offset--; n_lines_to_move -= get_lines_needed((cur_lb.be)[offset].Bline, ncols); if (n_lines_to_move < 0) { line_offset = (get_lines_needed((cur_lb.be)[offset].Bline, ncols) + n_lines_to_move) * ncols; } } } redraw = 2; } else if ((c == KEY_HOME || c == 'g' || c == '<' || c == KEY_SBEG) && offset > 0) { line_offset = offset = 0; redraw = 2; } else if ((c == KEY_END || c == 'G' || c == '>' || c == KEY_SEND) && offset < (cur_lb. curpos - 1)) { offset = cur_lb. curpos - 1; redraw = 2; } else if (uc == 'R' || c == ('R' - 65 + 1) || c == ('L' - 65 + 1) || c == KEY_REFRESH) { redraw = 2; } else if (c == ('K' - 65 + 1) || c == KEY_MARK) { scrollback_find_popup(&find, &case_insensitive); if (find) { int rc; regfree(&global_highlight_re); myfree(global_highlight_str); global_highlight_str = NULL; if ((rc = regcomp(&global_highlight_re, find, (case_insensitive == MY_TRUE?REG_ICASE:0) | REG_EXTENDED))) { regexp_error_popup(rc, &global_highlight_re); myfree(find); } else { global_highlight_str = find; } redraw = 2; /* force redraw */ } } else if (c == 'f' || c == '/' || c == '?' || c == KEY_FIND || c == KEY_SFIND) { char direction = (c == '?' || c == KEY_SFIND) ? -1 : 1; scrollback_find_popup(&find, &case_insensitive); if (find) { if (scrollback_search_new_window) { if (scrollback_search_to_new_window(&cur_lb, header, find, case_insensitive) == -1) { /* cascaded close */ rc = -1; break; } } else { int new_f_index; redraw = 2; /* force redraw */ regfree(&global_highlight_re); myfree(global_highlight_str); global_highlight_str = NULL; new_f_index = find_string(&cur_lb, find, 0, direction, case_insensitive); if (new_f_index == -1) { wrong_key(); } else { offset = new_f_index; line_offset = 0; } } } } else if (uc == 'N' || c == KEY_NEXT || c == KEY_PREVIOUS || c == KEY_SNEXT) { if (find != NULL) { char direction = (c == 'n' || c == KEY_NEXT) ? 1 : -1; int start_offset = offset + direction; int new_f_index = find_string(&cur_lb, find, start_offset, direction, case_insensitive); if (new_f_index == -1) { wrong_key(); } else { redraw = 2; /* force redraw */ offset = new_f_index; line_offset = 0; } } else { wrong_key(); } } else if (c == 's' || c == KEY_SAVE) { scrollback_savefile(&cur_lb); redraw = 2; /* force redraw */ } else if (c == 'h') { show_help(HELP_SCROLLBACK_HELP); } else if (c == 'c') { toggle_colors(); redraw = 2; /* force redraw */ } else if (c == 'i') { info(); } else if (c == 'T') { statistics_menu(); } else if (c == 20) { toggle_subwindow_nr(); redraw = 2; /* force redraw */ } else { wrong_key(); } } delete_popup(mywin2); if (mywin1) delete_popup(mywin1); myfree(find); delete_be_in_buffer(&cur_lb); return rc; } void scrollback(void) { int window = 0; if (nfd > 1) { window = select_window(HELP_SCROLLBACK_SELECT_WINDOW, NULL); } if (window != -1) { if (lb[window].bufferwhat == 0) error_popup("Scrollback", HELP_SCROLLBACK_NO_MARK, "Cannot scrollback: buffering is disabled."); } if (window != -1 && lb[window].bufferwhat != 0) { int header_size = strlen(pi[window].filename) + 4; char *header = (char *)mymalloc(header_size + 1); snprintf(header, header_size, "%02d] %s", window, pi[window].filename); scrollback_do(window, &lb[window], NULL, header); myfree(header); } } void merged_scrollback_with_search(char *search_for, mybool_t case_insensitive) { int lc_size = nfd * sizeof(int); int *last_check = (int *)mymalloc(lc_size); int *winnr = NULL; buffer buf; regex_t reg; int rc; memset(last_check, 0x00, lc_size); memset(&buf, 0x00, sizeof(buf)); /* compile the search string which is supposed to be a valid regular * expression */ if (search_for) { if ((rc=regcomp(®, search_for, REG_EXTENDED | (case_insensitive == MY_TRUE?REG_ICASE:0)))) { regexp_error_popup(rc, ®); free(last_check); return; } } /* merge all windows into one */ for(;;) { int loop; double chosen_ts = (double)(((long long int)1) << 62); int chosen_win = -1; int curline; char *string; int checked_all = 0; for(loop=0; loop last_check[loop]) last_check[loop]++; } continue; } if (!IS_MARKERLINE(lb[chosen_win].be[last_check[chosen_win]].pi)) { /*** ADD LINE TO BUFFER ***/ buf.be = (buffered_entry *)myrealloc(buf.be, sizeof(buffered_entry) * (buf.curpos + 1)); winnr = (int *)myrealloc(winnr, sizeof(int) * (buf.curpos + 1)); curline = buf.curpos++; /* add the logline itself */ string = lb[chosen_win].be[last_check[chosen_win]].Bline; if (string) buf.be[curline].Bline = mystrdup(string); else buf.be[curline].Bline = NULL; /* remember pointer to subwindow (required for setting colors etc.) */ buf.be[curline].pi = lb[chosen_win].be[last_check[chosen_win]].pi; buf.be[curline].ts = lb[chosen_win].be[last_check[chosen_win]].ts; /* remember window nr. */ winnr[curline] = chosen_win; } last_check[chosen_win]++; } if (buf.curpos == 0) error_popup("Search in all windows", -1, "Nothing found."); else { char *header; if (search_for) { char *help = "Searched for: "; int len = strlen(help) + strlen(search_for) + 1; header = mymalloc(len); snprintf(header, len, "%s%s", help, search_for); } else { char *help = "Merge view"; header = mymalloc(strlen(help) + 1); sprintf(header, "%s", help); } scrollback_do(-1, &buf, winnr, header); myfree(header); } delete_be_in_buffer(&buf); myfree(winnr); myfree(last_check); } int scrollback_search_to_new_window(buffer *pbuf, char *org_title, char *find_str, mybool_t case_insensitive) { int loop, rc; regex_t regex; buffer cur_lb; char *new_header; /* compile the searchstring (which can be a regular expression) */ if ((rc = regcomp(®ex, find_str, REG_EXTENDED | (case_insensitive == MY_TRUE?REG_ICASE:0)))) { regexp_error_popup(rc, ®ex); return 0; } memset(&cur_lb, 0x00, sizeof(buffer)); for(loop=0; loop curpos; loop++) { if ((pbuf -> be)[loop].Bline == NULL) continue; if (regexec(®ex, (pbuf -> be)[loop].Bline, 0, NULL, 0) == 0) { cur_lb.be = myrealloc(cur_lb.be, (cur_lb.curpos + 1) * sizeof(buffered_entry)); cur_lb.be[cur_lb.curpos].Bline = (pbuf -> be)[loop].Bline; cur_lb.be[cur_lb.curpos].pi = (pbuf -> be)[loop].pi; cur_lb.be[cur_lb.curpos].ts = (pbuf -> be)[loop].ts; cur_lb.curpos++; } } new_header = (char *)mymalloc(strlen(org_title) + 1 + strlen(find_str) + 1); sprintf(new_header, "%s %s", org_title, find_str); rc = scrollback_do(-1, &cur_lb, NULL, new_header); myfree(new_header); myfree(cur_lb.be); regfree(®ex); return rc; } multitail-7.1.5/scrollback.h000066400000000000000000000006061465663511400160350ustar00rootroot00000000000000int find_string(int window, char *find, int offset); void scrollback_help(void); void scrollback_savefile(int window); int get_lines_needed(char *string, int terminal_width); void scrollback_displayline(NEWWIN *win, int window, int offset, int terminal_offset); void scrollback(void); void delete_mark(void); void merged_scrollback_with_search(char *search_for, mybool_t case_insensitive); multitail-7.1.5/selbox.c000066400000000000000000000207061465663511400152100ustar00rootroot00000000000000#define _LARGEFILE64_SOURCE /* required for GLIBC to enable stat64 and friends */ #include #include #include #include #include #include #include #include #include "mt.h" #include "term.h" #include "mem.h" #include "utils.h" #include "globals.h" /** create_subwindow_list * - in: int f_index window number * char ***swlist pointer to an array of strings * - returns: int number of elements in the array of strings * this function creates for a given window (f_index) a list of subwindows: * subwindows are created when you're merging the output of several files/ * commands */ int create_subwindow_list(int f_index, char ***swlist) { char **list = NULL; int n = 0; proginfo *cur = &pi[f_index]; int subwin = 0; do { list = (char **)myrealloc(list, (n + 1) * sizeof(char *)); if (show_subwindow_id) { int len = strlen(cur -> filename); list[n] = (char *)mymalloc(len + 1); memcpy(list[n], cur -> filename, len + 1); } else list[n] = mystrdup(cur -> filename); n++; cur = cur -> next; subwin++; } while(cur); *swlist = list; return n; } char generate_string(char *whereto, void **list, selbox_type_t type, int wcols, int index) { char invert = 0; if (type == SEL_WIN) { snprintf(whereto, wcols + 1, "%02d %s", index, shorten_filename(((proginfo *)list)[index].filename, wcols)); invert = ((proginfo *)list)[index].hidden | ((proginfo *)list)[index].paused; } else if (type == SEL_SUBWIN) snprintf(whereto, wcols + 1, "%02d %s", index, shorten_filename(((char **)list)[index], wcols)); else if (type == SEL_FILES) strncpy(whereto, ((char **)list)[index], min(strlen(((char **)list)[index]), wcols) + 1); else if (type == SEL_CSCHEME) strncpy(whereto, ((color_scheme *)list)[index].name, min(strlen(((color_scheme *)list)[index].name), wcols) + 1); else if (type == SEL_HISTORY) strncpy(whereto, ((char **)list)[index], min(strlen(((char **)list)[index]), wcols) + 1); whereto[min(strlen(whereto), wcols - 1)] = 0x00; return invert; } int find_sb_string(void **list, selbox_type_t type, int nentries, char *compare_string) { int index, len = strlen(compare_string); for(index=0; index win); if (heading) win_header(mywin, heading); else if (type == SEL_WIN) win_header(mywin, "Select window"); else if (type == SEL_SUBWIN) win_header(mywin, "Select subwindow"); else if (type == SEL_FILES) win_header(mywin, "Select file"); else if (type == SEL_CSCHEME) win_header(mywin, "Select color scheme"); else if (type == SEL_HISTORY) win_header(mywin, "Select string from history"); for(loop=0; loop win, loop + 2, 1, "*"); mvwprintw(mywin -> win, loop + 2, 2, "%s", dummy); if (invert) color_off(mywin, find_colorpair(COLOR_YELLOW, -1, 0)); if (loop == offs) ui_inverse_off(mywin); } draw_border(mywin); ppos = pos; poffs = offs; } else if (poffs != offs) { int yellow_cp = find_colorpair(COLOR_YELLOW, -1, 0); char invert = generate_string(dummy, list, type, wcols, poffs + pos); if (invert) color_on(mywin, yellow_cp); mvwprintw(mywin -> win, poffs + 2, 2, "%s", dummy); if (invert) color_off(mywin, yellow_cp); invert = generate_string(dummy, list, type, wcols, offs + pos); ui_inverse_on(mywin); if (invert) color_on(mywin, yellow_cp); if (needs_mark && needs_mark[offs + pos]) mvwprintw(mywin -> win, loop + 2, 1, "*"); mvwprintw(mywin -> win, offs + 2, 2, "%s", dummy); if (invert) color_off(mywin, yellow_cp); ui_inverse_off(mywin); poffs = offs; } if (first) { first = 0; color_on(mywin, find_colorpair(COLOR_GREEN, -1, 0)); mvwprintw(mywin -> win, total_win_size - 2, 2, "Press ^G to abort"); color_off(mywin, find_colorpair(COLOR_GREEN, -1, 0)); } else { int loop, len = strlen(selstr); for(loop=0; loop win, total_win_size - 2, 1 + loop, " "); if (!selfound) color_on(mywin, find_colorpair(COLOR_RED, -1, 0)); mvwprintw(mywin -> win, total_win_size - 2, 1, "%s", &selstr[max(0, len - wcols)]); if (!selfound) color_off(mywin, find_colorpair(COLOR_RED, -1, 0)); } mydoupdate(); c = wait_for_keypress(what_help, 0, mywin, 1); if (c == KEY_UP) { if ((offs + pos) > 0) { if (offs) offs--; else pos--; } else { wrong_key(); } } else if (c == KEY_DOWN) { if ((pos + offs) < (nlines-1)) { if (offs < (wlines-1)) offs++; else pos++; } else { wrong_key(); } } else if (c == KEY_NPAGE) { if ((pos + offs) < (nlines - 1)) { pos += min(wlines, (nlines - 1) - (pos + offs)); } else { wrong_key(); } } else if (c == KEY_PPAGE) { if ((pos + offs - wlines) >= 0) { if (pos > wlines) { pos -= wlines; } else { pos -= (wlines - offs); offs = 0; } } else if (offs > 0) { offs = 0; } else if (pos > 0) { pos = 0; } else { wrong_key(); } } else if (c == KEY_ENTER || c == 13 || c == 10) { sel = pos + offs; break; } else if (c == abort_key || c == -1) { break; } else if ((c > 31 && c != 127) || (c == KEY_BACKSPACE)) { int index, curlen; curlen = strlen(selstr); if (c == KEY_BACKSPACE) { if (curlen > 0) selstr[curlen - 1] = 0x00; else wrong_key(); } else if (curlen < path_max) { selstr[curlen] = c; selstr[curlen + 1] = 0x00; } else wrong_key(); curlen = strlen(selstr); if (curlen > 0) { index = find_sb_string(list, type, nlines, selstr); if (index != -1) { ppos = -1; sel = pos = index; selfound = 1; } else { selfound = 0; } } } else { wrong_key(); } } delete_popup(mywin); myfree(dummy); myfree(selstr); return sel; } int select_window(int what_help, char *heading) { return selection_box((void **)pi, NULL, nfd, SEL_WIN, what_help, heading); } proginfo * select_subwindow(int f_index, int what_help, char *heading) { proginfo *cur = NULL; char **list; int list_n, index, loop; if (f_index == -1) return NULL; list_n = create_subwindow_list(f_index, &list); index = selection_box((void **)list, NULL, list_n, SEL_SUBWIN, what_help, heading); if (index != -1) { cur = &pi[f_index]; for(loop=0; loop next; } } delete_array(list, list_n); return cur; } char * select_file(char *input, int what_help) { char **list = NULL, *isdir = NULL, *path = NULL; char *new_fname = NULL; struct stat64 statbuf; int list_n, index; int strbufsize = find_path_max(); list_n = match_files(input, &path, &list, &isdir); if (list_n == 0) { myfree(path); flash(); return NULL; } index = selection_box((void **)list, isdir, list_n, SEL_FILES, what_help, NULL); if (index != -1) { new_fname = (char *)mymalloc(strbufsize + 1); snprintf(new_fname, strbufsize, "%s%s", path, list[index]); if (stat64(new_fname, &statbuf) == -1) { myfree(new_fname); new_fname = NULL; flash(); } else { if (S_ISDIR(statbuf.st_mode)) { strncat(new_fname, "/", strbufsize); } } } delete_array(list, list_n); myfree(isdir); myfree(path); return new_fname; } multitail-7.1.5/selbox.h000066400000000000000000000004521465663511400152110ustar00rootroot00000000000000#include "mt.h" int selection_box(void **list, char *needs_mark, int nlines, selbox_type_t type, int what_help, char *heading); int select_window(int what_help, char *heading); proginfo * select_subwindow(int f_index, int what_help, char *heading); char * select_file(char *input, int what_help); multitail-7.1.5/stripstring.c000066400000000000000000000314341465663511400163040ustar00rootroot00000000000000#define _LARGEFILE64_SOURCE /* required for GLIBC to enable stat64 and friends */ #include #include #include #include #include #include #include #include "mt.h" #include "help.h" #include "mem.h" #include "error.h" #include "term.h" #include "utils.h" #include "selbox.h" #include "globals.h" int edit_stripper_edit_regexp(NEWWIN *win, char **old_re, mybool_t *case_insensitive) { int changed = 0; char *new_re; /* ask new reg exp */ mvwprintw(win -> win, 15, 2, "Edit regular expression: "); new_re = edit_string(win, 16, 2, 58, 128, 0, *old_re, HELP_ENTER_REGEXP, -1, &search_h, case_insensitive); if (new_re) { myfree(*old_re); *old_re = new_re; changed = 1; } return changed; } int edit_stripper_edit_offsets(NEWWIN *win, int *offset_start, int *offset_end) { char *str_start = NULL, *str_end = NULL; char buffer[32] = { 0 }; char linebuf[68 + 1]; int changed = 0; if (*offset_start != -1) snprintf(buffer, sizeof(buffer), "%d", *offset_start); mvwprintw(win -> win, 15, 2, "Enter start offset: "); str_start = edit_string(win, 16, 2, 5, 5, 1, buffer, HELP_STRIPPER_START_OFFSET, -1, NULL, NULL); if (str_start) { if (*offset_end != -1) snprintf(buffer, sizeof(buffer), "%d", *offset_end); else buffer[0] = 0x00; mvwprintw(win -> win, 15, 2, "Enter end offset: "); memset(linebuf, ' ', sizeof(linebuf)); linebuf[sizeof(linebuf) - 1] = 0x00; mvwprintw(win -> win, 16, 1, "%s", linebuf); str_end = edit_string(win, 16, 2, 5, 5, 1, buffer, HELP_STRIPPER_END_OFFSET, -1, NULL, NULL); if (str_end) { *offset_start = atoi(str_start); *offset_end = atoi(str_end); changed = 1; } } myfree(str_start); myfree(str_end); return changed; } int edit_stripper_edit_field(NEWWIN *win, char **del, int *col_nr) { int changed = 0; char *new_del = NULL; char *new_col_nr = NULL; char buffer[32] = { 0 }; char linebuf[58 + 1]; mvwprintw(win -> win, 15, 2, "Enter delimiter: "); new_del = edit_string(win, 16, 2, 58, 128, 0, *del, HELP_STRIPPER_DELIMITER, -1, NULL, NULL); if (new_del) { mvwprintw(win -> win, 15, 2, "Enter column number: "); memset(linebuf, ' ', sizeof(linebuf)); linebuf[sizeof(linebuf) - 1] = 0x00; mvwprintw(win -> win, 16, 1, "%s", linebuf); if (*col_nr != -1) snprintf(buffer, sizeof(buffer), "%d", *col_nr); new_col_nr = edit_string(win, 16, 2, 5, 5, 1, buffer, HELP_STRIPPER_COL_NR, -1, NULL, NULL); } if (new_del != NULL && new_col_nr != NULL) { *del = mystrdup(new_del); *col_nr = atoi(new_col_nr); changed = 1; } myfree(new_col_nr); myfree(new_del); return changed; } int edit_strippers(void) { int changed = 0; NEWWIN *mywin; proginfo *cur; int f_index = 0; int cur_strip = 0; mybool_t case_insensitive = re_case_insensitive; /* select window */ if (nfd > 1) f_index = select_window(HELP_ENTER_STRIPPER_SELECT_WINDOW, "Select window (stripper editing)"); if (f_index == -1) /* user pressed Q/X */ return 0; /* select subwindow */ cur = &pi[f_index]; if (cur -> next) cur = select_subwindow(f_index, HELP_ENTER_STRIPPER_SELECT_SUBWINDOW, "Select subwindow (stripper editing)"); if (!cur) return 0; /* create window */ mywin = create_popup(23, 70); for(;;) { int c, key; int loop; char linebuf[68 + 1]; werase(mywin -> win); draw_border(mywin); win_header(mywin, "Edit strip regular expressions"); mvwprintw(mywin -> win, 2, 2, "%s", cur -> filename); escape_print(mywin, 3, 2, "^a^dd, ^e^dit, ^d^elete, ^q^uit"); /* clear */ memset(linebuf, ' ', sizeof(linebuf) - 1); linebuf[sizeof(linebuf) - 1] = 0x00; for(loop=4; loop<22; loop++) mvwprintw(mywin -> win, loop, 1, "%s", linebuf); /* display them lines */ for(loop=0; loop n_strip; loop++) { if (loop == cur_strip) ui_inverse_on(mywin); switch((cur -> pstrip)[loop].type) { case STRIP_KEEP_SUBSTR: case STRIP_TYPE_REGEXP: strncpy(linebuf, (cur -> pstrip)[loop].regex_str, 55); linebuf[55] = 0x00; mvwprintw(mywin -> win, 4 + loop, 1, "RE: %s", linebuf); break; case STRIP_TYPE_RANGE: mvwprintw(mywin -> win, 4 + loop, 1, "Range: %d - %d", (cur -> pstrip)[loop].start, (cur -> pstrip)[loop].end); break; case STRIP_TYPE_COLUMN: mvwprintw(mywin -> win, 4 + loop, 1, "Column: %d (delimiter: '%s')", (cur -> pstrip)[loop].col_nr, (cur -> pstrip)[loop].del); break; } if (loop == cur_strip) ui_inverse_off(mywin); mvwprintw(mywin -> win, 4 + loop, 60, "%d", (cur -> pstrip)[loop].match_count); } mydoupdate(); /* wait for key */ for(;;) { key = wait_for_keypress(HELP_REGEXP_MENU, 2, mywin, 1); c = toupper(key); /* convert return to 'E'dit */ if (key == 13) key = c = 'E'; /* any valid keys? */ if (c == 'Q' || c == abort_key || c == 'X' || c == 'A' || c == 'E' || c == 'D' || key == KEY_DOWN || key == 13 || key == KEY_UP) break; if (key == -1) /* T/O waiting for key? then update counters */ { for(loop=0; loop n_strip; loop++) mvwprintw(mywin -> win, 4 + loop, 60, "%d", (cur -> pstrip)[loop].match_count); mydoupdate(); } else wrong_key(); } /* exit this screen? */ if (c == 'Q' || c == abort_key || c == 'X') break; if (key == KEY_UP) { if (cur_strip > 0) cur_strip--; else wrong_key(); } else if (key == KEY_DOWN || key == 13) { if (cur_strip < (cur -> n_strip -1)) cur_strip++; else wrong_key(); } /* add or edit */ if (c == 'A') { int rc; char *regex_str = NULL; int offset_start = -1, offset_end = -1; char *del = NULL; int col_nr = -1; int c; int cur_changed = 0; /* max. 10 regular expressions */ if (cur -> n_strip == 10) { wrong_key(); continue; } /* ask type */ escape_print(mywin, 15, 2, "reg.^e^xp., ^r^ange, ^c^olumn, ^S^ubstrings"); mydoupdate(); for(;;) { c = toupper(wait_for_keypress(HELP_STRIPPER_TYPE, 0, mywin, 0)); if (c == 'E' || c == 'R' || c == 'C' || c == 'S' || c == abort_key || c == 'Q' || c == 'X') break; wrong_key(); } if (c == abort_key || c == 'Q' || c == 'X') continue; if (c == 'E' || c == 'S') { /* user did not abort edit? */ if (edit_stripper_edit_regexp(mywin, ®ex_str, &case_insensitive)) cur_changed = 1; } else if (c == 'R') { if (edit_stripper_edit_offsets(mywin, &offset_start, &offset_end)) cur_changed = 1; } else if (c == 'C') { if (edit_stripper_edit_field(mywin, &del, &col_nr)) cur_changed = 1; } if (!cur_changed) continue; changed = 1; cur_strip = cur -> n_strip++; cur -> pstrip = (strip_t *)myrealloc(cur -> pstrip, cur -> n_strip * sizeof(strip_t)); memset(&(cur -> pstrip)[cur_strip], 0x00, sizeof(strip_t)); /* compile */ if (c == 'E' || c == 'S') { if ((rc = regcomp(&(cur -> pstrip)[cur_strip].regex, regex_str, REG_EXTENDED))) { regexp_error_popup(rc, &(cur -> pre)[cur_strip].regex); cur -> n_strip--; if (cur -> n_strip == cur_strip) cur_strip--; myfree(regex_str); } else { /* compilation went well, remember everything */ (cur -> pstrip)[cur_strip].regex_str = regex_str; (cur -> pstrip)[cur_strip].type = (c == 'E' ? STRIP_TYPE_REGEXP : STRIP_KEEP_SUBSTR); } } else if (c == 'R') { (cur -> pstrip)[cur_strip].type = STRIP_TYPE_RANGE; (cur -> pstrip)[cur_strip].start = offset_start; (cur -> pstrip)[cur_strip].end = offset_end; } else if (c == 'C') { (cur -> pstrip)[cur_strip].type = STRIP_TYPE_COLUMN; (cur -> pstrip)[cur_strip].col_nr = col_nr; (cur -> pstrip)[cur_strip].del = del; } /* reset counter (as it is a different regexp which has not matched anything at all) */ (cur -> pstrip)[cur_strip].match_count = 0; } /* delete entry */ else if (c == 'D') { if (cur -> n_strip > 0) { changed = 1; /* delete entry */ myfree((cur -> pstrip)[cur_strip].regex_str); /* update administration */ if (cur -> n_strip == 1) { myfree(cur -> pstrip); cur -> pstrip = NULL; } else { int n_to_move = (cur -> n_strip - cur_strip) - 1; if (n_to_move > 0) memmove(&(cur -> pstrip)[cur_strip], &(cur -> pstrip)[cur_strip+1], sizeof(strip_t) * n_to_move); } cur -> n_strip--; /* move cursor */ if (cur_strip > 0 && cur_strip == cur -> n_strip) { cur_strip--; } } else wrong_key(); } else if (c == 'E') { if (cur -> n_strip > 0) { if ((cur -> pstrip)[cur_strip].type == STRIP_TYPE_REGEXP || (cur -> pstrip)[cur_strip].type == STRIP_KEEP_SUBSTR) { if (edit_stripper_edit_regexp(mywin, &(cur -> pstrip)[cur_strip].regex_str, &case_insensitive)) changed = 1; } else if ((cur -> pstrip)[cur_strip].type == STRIP_TYPE_RANGE) { if (edit_stripper_edit_offsets(mywin, &(cur -> pstrip)[cur_strip].start, &(cur -> pstrip)[cur_strip].end)) changed = 1; } else if ((cur -> pstrip)[cur_strip].type == STRIP_TYPE_COLUMN) { if (edit_stripper_edit_field(mywin, &(cur -> pstrip)[cur_strip].del, &(cur -> pstrip)[cur_strip].col_nr)) changed = 1; } } else wrong_key(); } } delete_popup(mywin); return changed; } void zero_str(char *what, int start, int end) { memset(&what[start], 0x00, end-start); } char do_strip_re(regex_t *pre, int *match_count, char *in, char *strip_what) { char changed = 0; int search_offset = 0; regmatch_t matches[MAX_N_RE_MATCHES]; do { int new_offset = -1; if (regexec(pre, &in[search_offset], MAX_N_RE_MATCHES, matches, 0) != REG_NOMATCH) { int match_i; for(match_i=0; match_i n_strip == 0) return NULL; len = strlen(in); if (len == 0) return NULL; strip_what = (char *)mymalloc(len); new_string = (char *)mymalloc(len + 1); memset(strip_what, 0x01, len); for(loop=0; loop n_strip; loop++) { if ((cur -> pstrip)[loop].type == STRIP_TYPE_RANGE) { memset(&strip_what[(cur -> pstrip)[loop].start], 0x00, (cur -> pstrip)[loop].end - (cur -> pstrip)[loop].start); changed = 1; } else if ((cur -> pstrip)[loop].type == STRIP_TYPE_REGEXP) { changed |= do_strip_re(&(cur -> pstrip)[loop].regex, &(cur -> pstrip)[loop].match_count, in, strip_what); } else if ((cur -> pstrip)[loop].type == STRIP_TYPE_COLUMN) { changed |= do_strip_column((cur -> pstrip)[loop].del, (cur -> pstrip)[loop].col_nr, in, strip_what); } else if ((cur -> pstrip)[loop].type == STRIP_KEEP_SUBSTR) { changed |= do_keep_re(&(cur -> pstrip)[loop].regex, &(cur -> pstrip)[loop].match_count, in, strip_what); } } if (changed) { for(loop=0; loop #include #include #include #include #include #include #include #include #include #include #include #include #if defined(sun) || defined(__sun) #include #endif #ifndef AIX #if defined(__FreeBSD__) #include #else #include /* needed on Solaris 8 */ #endif #endif #include #include #include "mt.h" #include "error.h" #include "mem.h" #include "term.h" #include "color.h" #include "utils.h" #include "help.h" #include "globals.h" #include "history.h" #include "ui.h" void wrong_key(void) { if (beep_method == BEEP_FLASH) flash(); else if (beep_method == BEEP_BEEP) beep(); else if (beep_method == BEEP_POPUP) { NEWWIN *beep_win = create_popup(5, 9); color_on(beep_win, find_colorpair(COLOR_GREEN, -1, 0)); mvwprintw(beep_win -> win, 3, 2, "Beep!"); color_off(beep_win, find_colorpair(COLOR_GREEN, -1, 0)); (void)wait_for_keypress(-1, beep_popup_length, beep_win, 0); delete_popup(beep_win); } else if (beep_method == BEEP_NONE) { /* do nothing */ } flushinp(); } void draw_border(NEWWIN *mywin) { wborder(mywin -> win, box_left_side, box_right_side, box_top_side, box_bottom_side, box_top_left_hand_corner, box_top_right_hand_corner, box_bottom_left_hand_corner, box_bottom_right_hand_corner); } int ask_yes_no(int what_help, NEWWIN *popup) { for(;;) { int c = toupper(wait_for_keypress(what_help, 0, popup, 0)); if (c == abort_key) return -1; switch(c) { case 'Y': case 'J': return 1; case 'N': return 0; case 'Q': return -1; } wrong_key(); } } void color_on(NEWWIN *win, int colorpair_index) { if (use_colors && colorpair_index != -1) wattron(win -> win, COLOR_PAIR(colorpair_index)); } void color_off(NEWWIN *win, int colorpair_index) { if (use_colors && colorpair_index != -1) wattroff(win -> win, COLOR_PAIR(colorpair_index)); } void myattr_on(NEWWIN *win, myattr_t attrs) { color_on(win, attrs.colorpair_index); if (attrs.attrs != -1) wattron(win -> win, attrs.attrs); } void myattr_off(NEWWIN *win, myattr_t attrs) { color_off(win, attrs.colorpair_index); if (attrs.attrs != -1) wattroff(win -> win, attrs.attrs); } void ui_inverse_on(NEWWIN *win) { wattron(win -> win, A_REVERSE); } void ui_inverse_off(NEWWIN *win) { wattroff(win -> win, A_REVERSE); } void draw_line(NEWWIN *win, linepos_t where) { int mx = getmaxx(win -> win), my = getmaxy(win -> win); if (where == LINE_LEFT) mvwvline(win -> win, 0, 0, ' ', my); else if (where == LINE_RIGHT) mvwvline(win -> win, 0, mx-1, ' ', my); else if (where == LINE_TOP) mvwhline(win -> win, 0, 0, ' ', mx); else if (where == LINE_BOTTOM) mvwhline(win -> win, my-1, 0, ' ', mx); } char * edit_string(NEWWIN *win, int win_y, int win_x, int win_width, int max_width, char numbers_only, char *input_string, int what_help, char first_char, history_t *ph, mybool_t *pcase_insensitive) { char *string = (char *)mymalloc(max_width + 1); int str_pos = 0, x = 0; int line_width = win_width; if (pcase_insensitive) mvwprintw(win -> win, win_y + 1, win_x - 1, "[%c] case insensitive (press TAB)", *pcase_insensitive?'X':' '); if (input_string) { int input_string_len = strlen(input_string), copy_len = min(min(win_width, max_width), input_string_len); int dummy = max(0, str_pos - line_width); memcpy(string, input_string, copy_len); string[copy_len] = 0x00; str_pos = dummy; mvwprintw(win -> win, win_y, win_x, "%s", &string[dummy]); x = strlen(string) - dummy; } else { string[0] = 0x00; } wmove(win -> win, win_y, win_x + x); mydoupdate(); for(;;) { char force_redraw = 0; int prev_str_pos = str_pos; int c; if (first_char != (char)-1) { c = first_char; first_char = -1; } else { c = wait_for_keypress(what_help, 0, NULL, 1); } /* confirm */ if (c == KEY_ENTER || c == 13 || c == 10 ) break; /* abort */ if (c == abort_key || c == 17 || c == 24) /* ^g / ^q / ^x */ { string[0] = 0x00; break; } /* key modifier */ if (c == 127 && map_delete_as_backspace == MY_TRUE) { c = KEY_BACKSPACE; } switch(c) { case 1: /* ^A */ str_pos = x = 0; break; case 5: /* ^E */ { int dummy = strlen(string); if (dummy > line_width) { str_pos = dummy - (line_width / 2); x = (line_width / 2); } else { str_pos = 0; x = dummy; } } break; case 9: /* tab (filename completion/switch to case insensitve field) */ if (pcase_insensitive) { ask_case_insensitive(pcase_insensitive); force_redraw = 1; } else if (numbers_only) { wrong_key(); } else { int dummy; char *file = select_file(string, -1); if (file) { strncpy(string, file, max_width); string[max_width] = 0x00; myfree(file); } dummy = strlen(string); if (dummy > line_width) { str_pos = dummy - (line_width / 2); x = (line_width / 2); } else { str_pos = 0; x = dummy; } force_redraw = 1; } break; case 21: /* ^U */ string[0] = 0x00; str_pos = x = 0; force_redraw = 1; break; case 23: /* ^W delete word */ { int spos = str_pos + x; int dpos = spos; /* remove spaces upto the first word */ while(dpos > 0 && string[dpos] == ' ') dpos --; /* remove that word we found */ while(dpos > 0 && string[dpos] != ' ') dpos--; memmove(&string[dpos], &string[spos], (max_width - spos) + 1); str_pos = max(0, dpos - (line_width / 2)); x = dpos - str_pos; force_redraw = 1; } break; case 127: /* DEL */ case 4: /* ^D */ { int spos = str_pos + x; int n_after = strlen(&string[spos]); if (n_after > 0) { memmove(&string[spos], &string[spos + 1], n_after); force_redraw = 1; } } break; case KEY_DOWN: /* cursor down */ case 18: /* ^R */ if (ph == NULL || ph -> history_size <= 0 || ph -> history_file == NULL) { wrong_key(); } else { int dummy; char *hs = search_history(ph, string); if (hs) { strncpy(string, hs, max_width); string[max_width] = 0x00; myfree(hs); } dummy = strlen(string); if (dummy > line_width) { str_pos = dummy - (line_width / 2); x = (line_width / 2); } else { str_pos = 0; x = dummy; } force_redraw = 1; } break; case KEY_BACKSPACE: { int spos = str_pos + x; if (spos > 0) { memmove(&string[spos - 1], &string[spos], (max_width - spos) + 1); if (x > 0) { x--; } else { str_pos--; } force_redraw = 1; } } break; case KEY_LEFT: if (x > 0) { x--; } else if (str_pos > 0) { str_pos--; } break; case KEY_RIGHT: if ((x + str_pos) < strlen(string)) { if (x < line_width) x++; else str_pos++; } else { wrong_key(); } break; default: { int len = strlen(string); /* only allow valid ASCII */ if (c < 32) { wrong_key(); break; } if (numbers_only && (c < '0' || c > '9')) { wrong_key(); break; } if (len == max_width) { wrong_key(); break; } /* cursor at end of string? */ if (str_pos == len) { string[str_pos + x] = c; string[str_pos + x + 1] = 0x00; waddch(win -> win, c); } else /* add character to somewhere IN the string */ { memmove(&string[str_pos + x + 1], &string[str_pos + x], strlen(&string[str_pos + x]) + 1); string[str_pos + x] = c; force_redraw = 1; } if ((x + str_pos) < max_width) { if (x < line_width) x++; else str_pos++; } else { wrong_key(); } } break; } if (str_pos != prev_str_pos || force_redraw) { int loop; char *dummy = mystrdup(&string[str_pos]); dummy[min(strlen(dummy), line_width)] = 0x00; for(loop=strlen(dummy); loop win, win_y, win_x + loop, " "); mvwprintw(win -> win, win_y, win_x, "%s", dummy); myfree(dummy); if (pcase_insensitive) mvwprintw(win -> win, win_y + 1, win_x, "%c", *pcase_insensitive?'X':' '); force_redraw = 0; } wmove(win -> win, win_y, win_x + x); mydoupdate(); } if (string[0] == 0x00) { myfree(string); string = NULL; } else if (ph != NULL) { history_add(ph, string); } return string; } NEWWIN * create_popup(int n_lines, int n_colls) { NEWWIN *newwin; int ocols = (max_x/2) - (n_colls/2); int olines = (max_y/2) - (n_lines/2); /* create new window */ newwin = mynewwin(n_lines, n_colls, olines, ocols); werase(newwin -> win); draw_border(newwin); show_panel(newwin -> pwin); return newwin; } void delete_popup(NEWWIN *mywin) { if (mywin) { mydelwin(mywin); update_panels(); doupdate(); myfree(mywin); } } void mydelwin(NEWWIN *win) { bottom_panel(win -> pwin); if (ERR == del_panel(win -> pwin)) error_exit(FALSE, FALSE, "del_panel() failed\n"); if (ERR == delwin(win -> win)) error_exit(FALSE, FALSE, "delwin() failed\n"); } void mydoupdate() { update_panels(); doupdate(); } NEWWIN * mynewwin(int nlines, int ncols, int begin_y, int begin_x) { NEWWIN *nwin = (NEWWIN *)mymalloc(sizeof(NEWWIN)); /* nwin -> win = subwin(stdscr, nlines, ncols, begin_y, begin_x); */ nwin -> win = newwin(nlines, ncols, begin_y, begin_x); if (!nwin -> win) error_exit(FALSE, FALSE, "Failed to create window with dimensions %dx%d at offset %d,%d (terminal size: %d,%d)\n", ncols, nlines, begin_x, begin_y, COLS, LINES); nwin -> pwin = new_panel(nwin -> win); if (!nwin -> pwin) error_exit(FALSE, FALSE, "Failed to create panel.\n"); nwin -> x_off = begin_x; nwin -> y_off = begin_y; nwin -> width = ncols; nwin -> height = nlines; if (bright_colors) wattron(nwin -> win, A_BOLD); if (default_bg_color != -1) wbkgdset(nwin -> win, COLOR_PAIR(find_or_init_colorpair(-1, default_bg_color, 1))); return nwin; } void escape_print(NEWWIN *win, int y, int x, char *str) { int loop, index = 0, len = strlen(str); char inv = 0, ul = 0, bold = 0; for(loop=0; loop win, y, x + index++, "^"); loop++; } else { if (!inv) ui_inverse_on(win); else ui_inverse_off(win); inv = 1 - inv; } } else if (str[loop] == '_') { if (str[loop + 1] == '_') /* __ is _ */ { /* just print a _ */ mvwprintw(win -> win, y, x + index++, "_"); loop++; } else { if (!ul) wattron(win -> win, A_UNDERLINE); else wattroff(win -> win, A_UNDERLINE); ul = 1 - ul; } } else if (str[loop] == '*') { if (str[loop + 1] == '*') { /* just print a * */ mvwprintw(win -> win, y, x + index++, "*"); loop++; } else { if (!bold) wattron(win -> win, A_BOLD); else wattroff(win -> win, A_BOLD); bold = 1 - bold; } } else { mvwprintw(win -> win, y, x + index++, "%c", str[loop]); } } if (inv) ui_inverse_off(win); if (ul) wattroff(win -> win, A_UNDERLINE); if (bold) wattroff(win -> win, A_BOLD); } void win_header(NEWWIN *win, char *str) { wattron(win -> win, A_BOLD); mvwprintw(win -> win, 1, 2, "%s", str); wattroff(win -> win, A_BOLD); } void gui_window_header(char *string) { if (term_type == TERM_XTERM) { fprintf(stderr, "\033]2;%s\007", string); #if 0 /* this code gives problems */ /* \033]0;%s\007 */ putp("\033]0;"); putp(string); putp("\007"); #endif } } int find_colorpair(int fgcolor, int bgcolor, char fuzzy) { int loop; for(loop=0; loop=0; loop++) { if (cp.fg_color[loop] == fgcolor && cp.bg_color[loop] == -1) return loop; else if (cp.fg_color[loop] == -1 && cp.bg_color[loop] == bgcolor) return loop; } } return -1; } myattr_t find_attr(int fgcolor, int bgcolor, int attrs) { myattr_t cdev; if (attrs == -1) attrs = A_NORMAL; cdev.attrs = attrs; cdev.colorpair_index = find_colorpair(fgcolor, bgcolor, 0); return cdev; } /* ignore errors when doing TERM-emulation */ int find_or_init_colorpair(int fgcolor, int bgcolor, char ignore_errors) { int index; if (use_colors) { index = find_colorpair(fgcolor, bgcolor, 0); if (index != -1) return index; if (cp.n_def == cp.size && cp.size > 0) { if (ignore_errors) { index = find_colorpair(fgcolor, bgcolor, 1); if (index != -1) return index; return 0; } error_exit(FALSE, FALSE, "Too many (%d) colorpairs defined.\n", cp.n_def); } cp.fg_color[cp.n_def] = fgcolor; cp.bg_color[cp.n_def] = bgcolor; init_pair(cp.n_def, cp.fg_color[cp.n_def], cp.bg_color[cp.n_def]); cp.n_def++; return cp.n_def - 1; } return 0; } int colorstr_to_nr(char *str) { int loop; int err; regex_t regex_is_color; if (str[0] == 0x00) return -1; for(loop=0; loop= COLORS) return "???"; return color_names[nr]; } void attr_to_str_helper(char *to, char *what) { int len = strlen(to); if (len) sprintf(&to[len], "/%s", what); else sprintf(&to[len], "%s", what); } char *attr_to_str(int attr) { char buffer[128] = { 0 }; if (attr & A_BOLD) attr_to_str_helper(buffer, "bold"); if (attr & A_BLINK) attr_to_str_helper(buffer, "blink"); if (attr & A_REVERSE) attr_to_str_helper(buffer, "inverse"); if (attr & A_UNDERLINE) attr_to_str_helper(buffer, "underline"); if (attr & A_DIM) attr_to_str_helper(buffer, "dim"); return mystrdup(buffer); } void determine_terminal_size(int *max_y, int *max_x) { struct winsize size; *max_x = 80; *max_y = 25; if (ioctl(1, TIOCGWINSZ, &size) == 0) { *max_y = size.ws_row; *max_x = size.ws_col; } else { char *dummy = getenv("COLUMNS"); if (dummy) *max_x = atoi(dummy); dummy = getenv("LINES"); if (dummy) *max_x = atoi(dummy); } } int ansi_code_to_color(int value) { switch(value) { case 30: return COLOR_BLACK; case 31: return COLOR_RED; case 32: return COLOR_GREEN; case 33: return COLOR_YELLOW; case 34: return COLOR_BLUE; case 35: return COLOR_MAGENTA; case 36: return COLOR_CYAN; case 37: return COLOR_WHITE; } return -1; } char * emulate_terminal(char *string, color_offset_in_line **cmatches, int *n_cmatches) { int len = strlen(string), new_offset = 0, loop; char *new_string = (char *)mymalloc(len + 1); int cur_n_cmatches = 0; *n_cmatches = 0; *cmatches = NULL; /* FIXME: this is ANSI/vt100-only */ for(loop=0; loop= 'a' && cur_char <= 'z') break; } if (string[find_cmd] == 'm') { int fg_i = -1, bg_i = -1, attr = 0; char *p = &string[loop + 2]; string[find_cmd] = 0x00; while(p) { int dummy; char *newp = strchr(p, ';'); if (newp) *newp = 0x00; dummy = atoi(p); if (dummy >= 40 && dummy <= 47) bg_i = ansi_code_to_color(dummy - 10); else if (dummy >= 30 && dummy <= 37) fg_i = ansi_code_to_color(dummy); else if (dummy == 1) attr = A_BOLD; else if (dummy == 4) attr = A_UNDERLINE; else if (dummy == 5) attr = A_BLINK; else if (dummy == 7) attr = A_REVERSE; p = newp; if (p) p++; } ci.colorpair_index = find_or_init_colorpair(fg_i, bg_i, 1); ci.attrs = attr; string[find_cmd] = 'm'; } loop = find_cmd; } if (ci.colorpair_index != -1) { if (cur_n_cmatches == *n_cmatches) { cur_n_cmatches = (cur_n_cmatches + 8) * 2; *cmatches = realloc_color_offset_in_line(*cmatches, cur_n_cmatches); } memset(&(*cmatches)[*n_cmatches], 0x00, sizeof(color_offset_in_line)); (*cmatches)[*n_cmatches].start = new_offset; (*cmatches)[*n_cmatches].end = -1; (*cmatches)[*n_cmatches].attrs = ci; (*n_cmatches)++; } } else { new_string[new_offset++] = string[loop]; } } for(loop=0; loop<(*n_cmatches - 1); loop++) (*cmatches)[loop].end = (*cmatches)[loop + 1].start; if (*n_cmatches >= 1) (*cmatches)[*n_cmatches - 1].end = new_offset; new_string[new_offset] = 0x00; return new_string; } void get_terminal_type(void) { char *dummy = getenv("TERM"); if (dummy && strstr(dummy, "xterm") != NULL) { term_type = TERM_XTERM; } } void init_ncurses(void) { initscr(); if (use_colors) start_color(); /* don't care if this one failes */ keypad(stdscr, TRUE); cbreak(); intrflush(stdscr, FALSE); noecho(); nonl(); refresh(); nodelay(stdscr, TRUE); meta(stdscr, TRUE); /* enable 8-bit input */ idlok(stdscr, TRUE); /* may give a little clunky screenredraw */ idcok(stdscr, TRUE); /* may give a little clunky screenredraw */ leaveok(stdscr, FALSE); max_y = LINES; max_x = COLS; } void init_colornames(void) { int loop, dummy; dummy = min(256, COLORS); if (use_colors) { color_names = (char **)mymalloc(dummy * sizeof(char *)); memset(color_names, 0x00, dummy * sizeof(char *)); color_names[COLOR_RED] = "red"; color_names[COLOR_GREEN] = "green"; color_names[COLOR_YELLOW] = "yellow"; color_names[COLOR_BLUE] = "blue"; color_names[COLOR_MAGENTA]= "magenta"; color_names[COLOR_CYAN] = "cyan"; color_names[COLOR_WHITE] = "white"; color_names[COLOR_BLACK] = "black"; } /* FIXME: is this needed? or are COLOR_* always at position 0...7? */ for(loop=dummy - 1; loop>=0; loop--) { if (color_names[loop]) { n_colors_defined = loop + 1; break; } } } multitail-7.1.5/term.h000066400000000000000000000030111465663511400146560ustar00rootroot00000000000000void wrong_key(void); int ask_yes_no(int what_help, NEWWIN *popup); void color_on(NEWWIN *win, int colorpair_index); void color_off(NEWWIN *win, int colorpair_index); void myattr_on(NEWWIN *win, myattr_t attr); void myattr_off(NEWWIN *win, myattr_t attr); void ui_inverse_on(NEWWIN *win); void ui_inverse_off(NEWWIN *win); void draw_line(NEWWIN *win, linepos_t where); char * edit_string(NEWWIN *win, int win_y, int win_x, int win_width, int max_width, char numbers_only, char *input_string, int what_help, char first_char, history_t *ph, mybool_t *pcase_insensitive); NEWWIN * create_popup(int n_lines, int n_colls); void delete_popup(NEWWIN *mywin); void mydelwin(NEWWIN *win); void mydoupdate(); NEWWIN * mynewwin(int nlines, int ncols, int begin_y, int begin_x); void escape_print(NEWWIN *win, int y, int x, char *str); void win_header(NEWWIN *win, char *str); void gui_window_header(char *string); int find_colorpair(int fgcolor, int bgcolor, char fuzzy); int find_or_init_colorpair(int fgcolor, int bgcolor, char ignore_errors); int colorstr_to_nr(char *str); int attrstr_to_nr(char *str); myattr_t parse_attributes(char *str); void set_ncurses_colorpairs(void); myattr_t find_attr(int fgcolor, int bgcolor, int attrs); char *color_to_string(int nr); char *attr_to_str(int attr); void determine_terminal_size(int *max_y, int *max_x); char * emulate_terminal(char *string, color_offset_in_line **cmatches, int *n_cmatches); void get_terminal_type(void); void init_ncurses(void); void init_colornames(void); void draw_border(NEWWIN *mywin); multitail-7.1.5/thanks.txt000066400000000000000000000027711465663511400156030ustar00rootroot00000000000000In alphabetic order. Bas Aartsen - Spanish translation Bernd Ahlers - OpenBSD build fixes René Berber - stripline bugfix / horizontal scroll in main menu enhancement David Blank-Edelman - Solaris 9 fixes Oliver Braun - FreeBSD packaging Lenny Cartier - Mandrake package Steve Christensen - SUN Solaris packages http://www.sunfreeware.com/ Rene Engelhard - Debian package, testing Doruk Fisek - packaging for Pardus distribution Udo van den Heuvel - lots of feedback and first draft of the wikipedia article Mads Martin Joergensen - SuSE package Peterpaul Kloosterman - Logo! Adam Lewandowski - CygWin build fixes Dean Moore - MacOS X fixes Peter Neilson - wikipedia article Kevin Piche - ArchLinux package Computer Services, www.smartcsc.com - build on SCO Bennett Todd - uClibc port Dries Verachtert - RedHat Enterprise Linux packages Aaron Walker - GenToo package Dag Wieers - RedHat Enterprise Linux packages Christian Weisgerber - several bugreports and OpenBSD port issues Dr. Volker Zell - CygWin packaging multitail-7.1.5/todo000066400000000000000000000000551465663511400144330ustar00rootroot00000000000000- macos: libintl - main: nl/ directory mkdir multitail-7.1.5/ui.c000066400000000000000000001763721465663511400143440ustar00rootroot00000000000000#define _LARGEFILE64_SOURCE /* required for GLIBC to enable stat64 and friends */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mt.h" #include "help.h" #include "error.h" #include "term.h" #include "mem.h" #include "utils.h" #include "selbox.h" #include "color.h" #include "exec.h" #include "globals.h" #include "scrollback.h" #include "cv.h" void error_popup(char *title, int help, char *message, ...) { va_list ap; NEWWIN *mywin; char buffer[4096]; myattr_t cdev; cdev.colorpair_index = find_colorpair(COLOR_RED, -1, 1); cdev.attrs = A_BLINK; va_start(ap, message); vsnprintf(buffer, sizeof(buffer), message, ap); va_end(ap); mywin = create_popup(9, max(40, find_char_offset(buffer, '\n')) + 4); win_header(mywin, title); myattr_on(mywin, cdev); mvwprintw(mywin -> win, 3, 2, "%s", buffer); myattr_off(mywin, cdev); escape_print(mywin, 7, 2, "_Press any key to exit this screen_"); mydoupdate(); wrong_key(); wait_for_keypress(help, 0, mywin, 0); delete_popup(mywin); } void edit_color(int index) { int example_color = COLOR_RED; NEWWIN *mywin = create_popup(15, 40); int colorbar = find_or_init_colorpair(example_color, example_color, 1); short old_r, old_g, old_b; double scale = 255.0 / 1000.0; int cursor_y = 0; short cur_r, cur_g, cur_b; color_content(example_color, &old_r, &old_g, &old_b); color_content(index, &cur_r, &cur_g, &cur_b); init_color(example_color, cur_r, cur_g, cur_b); for(;;) { int c; color_content(example_color, &cur_r, &cur_g, &cur_b); werase(mywin -> win); win_header(mywin, "Edit color"); mvwprintw(mywin -> win, 3, 2, "Color to edit:"); wattron(mywin -> win, COLOR_PAIR(colorbar)); mvwprintw(mywin -> win, 3, 17, " "); wattroff(mywin -> win, COLOR_PAIR(colorbar)); mvwprintw(mywin -> win, 3, 23, "%s", color_names[index]); mvwprintw(mywin -> win, 5, 17, " Red Green Blue"); mvwprintw(mywin -> win, 6, 15, "%c %4d %4d %4d", cursor_y == 0?'>':' ', cur_r, cur_g, cur_b); mvwprintw(mywin -> win, 7, 15, "%c %3d %3d %3d", cursor_y == 1?'>':' ', (int)((double)cur_r * scale), (int)((double)cur_g * scale), (int)((double)cur_b * scale)); mvwprintw(mywin -> win, 8, 15, "%c %2x %2x %2x ", cursor_y == 2?'>':' ', (int)((double)cur_r * scale), (int)((double)cur_g * scale), (int)((double)cur_b * scale)); escape_print(mywin, 10, 2, "^r^ edit red"); escape_print(mywin, 11, 2, "^g^ edit green"); escape_print(mywin, 12, 2, "^b^ edit blue"); escape_print(mywin, 13, 2, "^c^ change colorname"); mvwprintw(mywin -> win, 14, 2, "Press ^g to exit this screen"); draw_border(mywin); mydoupdate(); c = wait_for_keypress(HELP_EDIT_COLOR, 0, mywin, 1); if (c == abort_key) break; else if (c == KEY_UP) { if (cursor_y) cursor_y--; else wrong_key(); } else if (c == KEY_DOWN) { if (cursor_y < 2) cursor_y++; else wrong_key(); } else if (c == 'c') { char *newname = edit_string(mywin, 13, 23, 10, 10, 0, USE_IF_SET(color_names[index], ""), HELP_EDIT_COLOR_CHANGE_NAME, 0, NULL, NULL); if (newname) { myfree(color_names[index]); color_names[index] = newname; } } else if (c == 'r' || c == 'g' || c == 'b') { int y = -1; char val_str[5] = { 0 }; int val = -1; char *newval = NULL; if (c == 'r') { y = 0; val = cur_r; } else if (c == 'g') { y = 1; val = cur_g; } else if (c == 'b') { y = 2; val = cur_b; } mvwprintw(mywin -> win, 10 + y, 19, ">"); if (cursor_y == 0) snprintf(val_str, sizeof(val_str), "%d", val); else if (cursor_y == 1) snprintf(val_str, sizeof(val_str), "%d", (int)((double)val * scale)); else if (cursor_y == 2) snprintf(val_str, sizeof(val_str), "%2x", (int)((double)val * scale)); newval = edit_string(mywin, 10 + y , 20, 5, 5, (cursor_y < 2)?1:0, val_str, HELP_EDIT_COLOR_EDIT, -1, NULL, NULL); if (newval) { if (cursor_y == 0) val = max(0, min(atoi(newval), 1000)); else if (cursor_y == 1) val = max(0, (double)min(atoi(newval) / scale, 255)); else if (cursor_y == 2) val = max(0, (double)min(255, strtol(newval, NULL, 16) / scale)); if (c == 'r') cur_r = val; else if (c == 'b') cur_g = val; else if (c == 'g') cur_b = val; myfree(newval); } init_color(example_color, cur_r, cur_g, cur_b); } } init_color(example_color, old_r, old_g, old_b); delete_popup(mywin); } int color_management(myattr_t *org, myattr_t *new) { NEWWIN *mywin = create_popup(21, 64); short cur_fg = -1, cur_bg = -1; int cur_attr = 0; int offset = 0, cursor_y = 0, cursor_x = 0; int changed = 0; char *attrs[] = { "bold", "blink", "reverse", "underline", "normal", "dim" }; int attr_x[] = { A_BOLD, A_BLINK, A_REVERSE, A_UNDERLINE, A_NORMAL, A_DIM }; const int n_attrs = sizeof(attr_x) / sizeof(int); if (new != NULL && org != NULL) { if (org -> attrs != -1) cur_attr = org -> attrs; if (org -> colorpair_index != -1) pair_content(org -> colorpair_index, &cur_fg, &cur_bg); } for(;;) { int loop, c; werase(mywin -> win); if (new) win_header(mywin, "Select colors and attributes"); else win_header(mywin, "Select color to edit"); if (can_change_color()) mvwprintw(mywin -> win, 20, 2, "Press ^g to abort, 'e' to edit and 'a' to add a color"); else mvwprintw(mywin -> win, 20, 2, "Press ^g to abort"); wattron(mywin -> win, A_UNDERLINE); mvwprintw(mywin -> win, 2, 2, "foreground"); if (new) { mvwprintw(mywin -> win, 2, 22, "background"); mvwprintw(mywin -> win, 2, 42, "attributes"); } wattroff(mywin -> win, A_UNDERLINE); for(loop=0; loop<15; loop++) { int cur_offset = offset + loop; if (cur_offset >= COLORS) break; if (new) { mvwprintw(mywin -> win, 3 + loop, 2, "[%c] %s", cur_fg == cur_offset?'X':' ', color_names[cur_offset]?color_names[cur_offset]:""); mvwprintw(mywin -> win, 3 + loop, 22, "[%c] %s", cur_bg == cur_offset?'X':' ', color_names[cur_offset]?color_names[cur_offset]:""); } else mvwprintw(mywin -> win, 3 + loop, 2, "%s", USE_IF_SET(color_names[cur_offset], "")); } if (new) { for(loop=0; loop= n_attrs) break; mvwprintw(mywin -> win, 3 + loop, 42, "[%c] %s", cur_attr & attr_x[cur_offset]?'X':' ', attrs[loop]); } } draw_border(mywin); wmove(mywin -> win, 3 + cursor_y, 3 + cursor_x * 20); mydoupdate(); c = wait_for_keypress(HELP_SELECT_COLOR_AND_ATTRIBUTES, 0, mywin, 1); if (c == abort_key || c == 'Q' || c == 'X') { break; } else if (c == 'a' && can_change_color()) { /* find empty slot */ for(loop=0; loop win, 19, 2, "Enter color name: "); name = edit_string(mywin, 19, 20, 10, 10, 0, "", -1, -1, NULL, NULL); if (name) { color_names[loop] = name; edit_color(loop); /* no free! */ } } else error_popup("Create new color", -1, "Too many defined colors (%d max)", COLORS); } else if (c == KEY_UP) { if (cursor_y) cursor_y--; else if (offset) offset--; else wrong_key(); } else if (c == KEY_DOWN) { if (cursor_x == 2) { if ((offset + cursor_y) < (n_attrs - 1)) { if (cursor_y < (n_attrs - offset - 1)) cursor_y++; else offset++; } else wrong_key(); } else { if ((offset + cursor_y) < (n_colors_defined - 1)) { if (cursor_y < (n_colors_defined - offset - 1)) cursor_y++; else offset++; } else wrong_key(); } } else if (new != NULL && c == KEY_LEFT) { if (cursor_x) cursor_x--; else wrong_key(); } else if (new != NULL && c == KEY_RIGHT) { if (cursor_x == 0) cursor_x++; else if ((offset + cursor_y) < n_attrs && cursor_x == 1) cursor_x++; else wrong_key(); } else if (c == 13) break; else if (c == 'e' && (cursor_x == 0 || cursor_x == 1)) { edit_color(offset + cursor_y); } else if (new != NULL && c == ' ') { int sel = offset + cursor_y; changed = 1; if (cursor_x == 0) cur_fg = sel; else if (cursor_x == 1) cur_bg = sel; else if (cursor_x == 2) { if (sel >= n_attrs) error_exit(FALSE, FALSE, "Internal error.\n"); if (cur_attr & attr_x[sel]) cur_attr -= attr_x[sel]; else cur_attr |= attr_x[sel]; } else error_exit(FALSE, FALSE, "Internal error.\n"); } } delete_popup(mywin); if (new != NULL && changed) { new -> colorpair_index = find_or_init_colorpair(cur_fg, cur_bg, 1); new -> attrs = cur_attr; } return changed; } int swap_window(void) { int swapped = 0; if (nfd > 1) { int f1_index, f2_index; f1_index = select_window(HELP_SWAP_WIN1, "Swap windows, select window 1"); if (f1_index != -1) { f2_index = select_window(HELP_SWAP_WIN2, "Swap windows, select window 2"); if (f2_index != -1) { proginfo dummy; buffer dummy2; buffer_replace_pi_pointers(f1_index, &pi[f1_index], &pi[f2_index]); buffer_replace_pi_pointers(f2_index, &pi[f2_index], &pi[f1_index]); memmove(&dummy, &pi[f1_index], sizeof(proginfo)); memmove(&pi[f1_index], &pi[f2_index], sizeof(proginfo)); memmove(&pi[f2_index], &dummy, sizeof(proginfo)); memmove(&dummy2, &lb[f1_index], sizeof(buffer)); memmove(&lb[f1_index], &lb[f2_index], sizeof(buffer)); memmove(&lb[f2_index], &dummy2, sizeof(buffer)); swapped = 1; } } } else { error_popup("Swap windows", -1, "There's only 1 window!"); } return swapped; } void restart_window(void) { int f_index = 0; if (nfd > 1) f_index = select_window(HELP_SELECT_RESTART_WINDOW, "Select window to restart"); if (f_index != -1) { char all = 1; if (pi[f_index].next) { NEWWIN *mywin = create_popup(13, 24); win_header(mywin, "Restart window"); escape_print(mywin, 3, 2, "Restart all? (^y^/^n^)"); mydoupdate(); all = ask_yes_no(HELP_SELECT_RESTART_WINDOW_ALL, mywin); delete_popup(mywin); } if (all == 1) /* pressed 'Y' */ { proginfo *cur = &pi[f_index]; do { do_restart_window(cur); cur = cur -> next; } while(cur); } else { do_restart_window(&pi[f_index]); } } } int delete_window(void) { int deleted = 0; int f_index = 0; /* only popup selectionbox when more then one window */ if (nfd > 1) f_index = select_window(HELP_DELETE_SELECT_WINDOW, "Select window to delete"); /* user did not press q/x? */ if (f_index != -1) { char all = 1; /* multiple files for this window? then ask if delete all * or just one */ if (pi[f_index].next) { NEWWIN *mywin = create_popup(13, 24); win_header(mywin, "Delete window"); escape_print(mywin, 3, 2, "Delete all? (^y^/^n^)"); mydoupdate(); all = ask_yes_no(HELP_DELETE_WINDOW_DELETE_ALL_SUBWIN, mywin); delete_popup(mywin); } if (all == 1) /* pressed 'Y' */ { delete_entry(f_index, NULL); deleted = 1; } else if (all == 0) /* pressed 'N' */ { proginfo *cur = select_subwindow(f_index, HELP_DELETE_SELECT_SUBWINDOW, "Select subwindow"); if (cur) { delete_entry(f_index, cur); deleted = 1; } } /* otherwhise: pressed 'Q' */ } return deleted; } int hide_window(void) { int f_index = select_window(HELP_HIDE_WINDOW, "Select window to hide"); if (f_index != -1) pi[f_index].hidden = 1 - pi[f_index].hidden; return f_index != -1 ? 1 : 0; } int hide_all_but(void) { int f_index = select_window(HELP_HIDE_BUT_WINDOW, "Select window to keep open"); int loop; if (f_index != -1) { for(loop=0; loop win); win_header(win, "Select scheme(s)"); mvwprintw(win -> win, 2, 2, "Space to toggle, enter to proceed, ^g to abort"); for(loop=0; loop<16; loop++) { char *descr = "", *name; if (cur_offset + loop >= n_schemes_in) break; if (tscheme == SCHEME_COLOR) { name = ((color_scheme *)schemes_in)[cur_offset + loop].name; descr = ((color_scheme *)schemes_in)[cur_offset + loop].descr; } else { name = ((conversion *)schemes_in)[cur_offset + loop].name; } mvwprintw(win -> win, 3 + loop, 26, "%s", descr); if (loop == cur_line) ui_inverse_on(win); mvwprintw(win -> win, 3 + loop, 2, "[%c] %s", selected_schemes[cur_offset + loop]?'X':' ', name); if (loop == cur_line) ui_inverse_off(win); } draw_border(win); mydoupdate(); c = toupper(wait_for_keypress(tscheme == SCHEME_COLOR ? HELP_SELECT_COLORSCHEMES : HELP_SELECT_CONVERSIONSCHEMES, 0, win, 1)); if (c == abort_key || c == 'Q' || c == 'X') { c = -1; break; } else if (c == KEY_HOME) { cur_line = cur_offset = 0; } else if (c == KEY_END) { cur_line = 0; cur_offset = n_schemes_in - 1; } else if (c == KEY_PPAGE) { int todo = 16 - cur_line; cur_line = 0; if (cur_offset > todo) cur_offset -= todo; else cur_offset = 0; } else if (c == KEY_NPAGE) { int todo = cur_line; cur_line = 15; if ((cur_offset + todo) < (n_schemes_in - 1)) cur_offset += todo; else cur_offset = n_schemes_in - 1; } else if (c == KEY_UP) { if (cur_line > 0) cur_line--; else if (cur_offset > 0) cur_offset--; else wrong_key(); } else if (c == KEY_DOWN) { if ((cur_line + cur_offset) < (n_schemes_in - 1)) { if (cur_line < (16 - 1)) cur_line++; else cur_offset++; } else wrong_key(); } else if (c == ' ') { selected_schemes[cur_line + cur_offset] = 1 - selected_schemes[cur_line + cur_offset]; } else if (c == 13) { break; } else wrong_key(); } if (c != -1) { /* return result */ free_iat(schemes_out); for(loop=0; loop win, 2, 2, "1. ANSI/VT100"); mvwprintw(popup -> win, 3, 2, "2. no emulation"); mvwprintw(popup -> win, 4, 2, "0. abort"); mvwprintw(popup -> win, 6, 2, "Please contact folkert@vanheusden.com if"); mvwprintw(popup -> win, 7, 2, "you require other emulations as well."); mydoupdate(); for(;;) { int c = wait_for_keypress(HELP_SELECT_TERMINAL_EMULATION, 0, popup, 1); if (c == '1') { *term = TERM_ANSI; break; } else if (c == '2') { *term = TERM_IGNORE; break; } else if (c == '0' || toupper(c) == 'Q' || c == abort_key) { rc = -1; break; } wrong_key(); } delete_popup(popup); return rc; } char ask_colors(NEWWIN *win, int line, char cur, char *fieldnr, char **fielddel, int_array_t *color_schemes, myattr_t *attrs, term_t *term_emul) { char ok = 0; escape_print(win, line, 2, "Colors? (^s^yslog/^m^isc/^f^ield/^n^one/^S^cheme/^l^ist/^t^erm)"); mydoupdate(); for(;;) { int c = wait_for_keypress(HELP_ASK_COLORS, 0, win, 0); if (c == abort_key) return -1; switch(c) { case 't': if (ask_terminal_emulation(term_emul) == 0) return 0; return -1; case 'q': return -1; case 'S': if (select_schemes(cschemes, n_cschemes, SCHEME_COLOR, color_schemes) == -1) return -1; return 'S'; case 's': case 'm': return 'm'; case 'n': return 0; case 'l': { myattr_t new_attrs = { -1, -1 }; if (color_management(attrs, &new_attrs)) { *attrs = new_attrs; return 'i'; } } break; case 'f': { int loop, fld_dummy; char *dummy, nr[5]; /* xxx\0 and a little :-) */ snprintf(nr, sizeof(nr), "%d", *fieldnr); mvwprintw(win -> win, line + 2, 1, "Enter field number: "); dummy = edit_string(win, line + 3, 2, 42, 39, 1, nr, HELP_COLORS_FIELD_NR, -1, NULL, NULL); if (dummy) { fld_dummy = atoi(dummy); myfree(dummy); mvwprintw(win -> win, line + 4, 1, "Enter delimiter (eg a space):"); dummy = edit_string(win, line + 5, 2, 42, 39, 0, *fielddel, HELP_COLORS_FIELD_DELIMITER, -1, NULL, NULL); if (dummy) { myfree(*fielddel); *fielddel = dummy; *fieldnr = fld_dummy; ok = 1; } } for(loop=0; loop<4; loop++) mvwprintw(win -> win, line + 1 + loop, 1, " "); } if (ok) return 'f'; break; case 13: if (cur != (char)-1) return cur; default: wrong_key(); } } } char ask_regex_type(NEWWIN *win, int line) { escape_print(win, line+0, 2, "Usage of regexp? (^m^atch, ^v^ do not match"); escape_print(win, line+1, 2, "^C^olor, ^B^ell, ^b^ell + colorize, e^x^ecute)"); mydoupdate(); for(;;) { int c = wait_for_keypress(HELP_REGEXP_USAGE, 0, win, 0); if (c == 'm' || toupper(c) == 'C' || toupper(c) == 'B' || toupper(c) == 'X' || c == 'v') return c; if (toupper(c) == 'Q' || c == abort_key) return -1; wrong_key(); } } int add_window(void) { int added = 0; char *fname; NEWWIN *mywin = create_popup(12, 53); int col, fc; proginfo *cur = NULL; proginfo *set_next_pointer = NULL; char ask_add_to; int what_help = -1; if (nfd) ask_add_to = 1; else ask_add_to = 0; for(;;) { int_array_t color_schemes = { NULL, 0, 0 }; char field_nr = 0; char *field_del = NULL; char follow_filename = 0; char see_difference = 0; myattr_t cdev = { -1, -1 }; term_t term_emul = TERM_IGNORE; win_header(mywin, "Add window"); if (ask_add_to) { int rc; ask_add_to = 0; mvwprintw(mywin -> win, 2, 2, "Add (merge) to existing window?"); mydoupdate(); rc = ask_yes_no(HELP_ADD_WINDOW_MERGE_SUBWIN, mywin); if (rc == 1) /* pressed 'Y' */ { int index = 0; if (nfd > 1) index = select_window(HELP_ADD_WINDOW_SELECT_MERGE_WINDOW, NULL); if (index == -1) break; cur = &pi[index]; } else if (rc == -1) /* pressed 'Q' */ { break; } } /* check if this extra window will still fit */ if (!cur) { if ((max_y / (nfd + 1)) < 3) { error_popup("Add window", -1, "More windows would not fit."); break; } } escape_print(mywin, 2, 2, "File or command? (^f^/^c^) "); mydoupdate(); for(;;) { fc = toupper(wait_for_keypress(HELP_ADD_FILE_OR_CMD, 0, mywin, 0)); if (fc == 'F' || fc == 'C' || fc == 'Q' || fc == abort_key) break; wrong_key(); } if (fc == 'Q' || fc == abort_key) break; else if (fc == 'F') { mvwprintw(mywin -> win, 2, 2, "Enter filename: "); what_help = HELP_ENTER_FILENAME_TO_MONITOR; } else { mvwprintw(mywin -> win, 2, 2, "Enter command: "); what_help = HELP_ENTER_CMD_TO_MONITOR; } fname = edit_string(mywin, 3, 2, 41, find_path_max(), 0, NULL, what_help, -1, &cmdfile_h, NULL); if (!fname) break; if (fc == 'F') { escape_print(mywin, 4, 2, "Follow filename? (^y^/^n^)"); mydoupdate(); follow_filename = ask_yes_no(HELP_ADD_FILE_FOLLOW_FILENAME, mywin); } col = ask_colors(mywin, 5, -1, &field_nr, &field_del, &color_schemes, &cdev, &term_emul); if (col == -1) break; if (cur == NULL) { create_new_win(&cur, NULL); added = 1; } else { /* skip to end of chain */ while (cur -> next) cur = cur -> next; /* the last entry in the chain will point to this newly created structure * don't set this pointer yet as this will give problems as soon as we * call wait_for_keypress() */ set_next_pointer = cur; /* allocate new entry */ cur = (proginfo *)mymalloc(sizeof(proginfo)); } memset(cur, 0x00, sizeof(proginfo)); cur -> restart.restart = -1; if (fc == 'C') { char *dummy; mvwprintw(mywin -> win, 6, 2, "Repeat command interval: (empty for don't)"); dummy = edit_string(mywin, 7, 2, 10, 10, 1, NULL, HELP_ADD_WINDOW_REPEAT_INTERVAL, -1, NULL, NULL); if (dummy) { cur -> restart.restart = atoi(dummy); myfree(dummy); mvwprintw(mywin -> win, 8, 2, "Do you whish to only see the difference?"); mydoupdate(); see_difference = ask_yes_no(HELP_ADD_FILE_DISPLAY_DIFF, mywin); } } cur -> filename = fname; cur -> status = cur -> data = NULL; cur -> wt = (fc == 'F' ? WT_FILE : WT_COMMAND); cur -> retry_open = 0; cur -> follow_filename = follow_filename; cur -> restart.do_diff = see_difference; cur -> next = NULL; cur -> restart.first = 1; cur -> line_wrap = 'a'; cur -> win_height = -1; cur -> statistics.sccfirst = 1; cur -> cdef.colorize = col; cur -> cdef.field_nr = field_nr; cur -> cdef.field_del = field_del; cur -> cdef.color_schemes = color_schemes; cur -> cdef.attributes = cdev; cur -> cdef.term_emul = term_emul; init_iat(&cur -> conversions); cur -> statistics.start_ts = time(NULL); /* start tail-process */ if (start_proc(cur, max_y / (nfd + 1)) == -1) { mvwprintw(mywin -> win, 8, 2, "error opening file!"); mydoupdate(); wait_for_keypress(HELP_FAILED_TO_START_TAIL, 0, mywin, 0); nfd--; break; } /* and set pointer to the newly allocated entry */ if (set_next_pointer) set_next_pointer -> next = cur; mvwprintw(mywin -> win, 10, 2, "Add another to this new window?"); mydoupdate(); if (ask_yes_no(HELP_MERGE_ANOTHER_WINDOW, mywin) <= 0) break; werase(mywin -> win); draw_border(mywin); } delete_popup(mywin); return added; } int toggle_colors(void) { int changed = 0; int f_index = 0; if (nfd > 1) f_index = select_window(HELP_TOGGLE_COLORS_SELECT_WINDOW, "Toggle colors: select window"); if (f_index != -1) { char col; proginfo *cur = &pi[f_index]; NEWWIN *mywin = NULL; char *dummy = NULL; if (cur -> next) cur = select_subwindow(f_index, HELP_TOGGLE_COLORS_SELECT_SUBWINDOW, "Toggle colors: select subwindow"); if (cur) { mywin = create_popup(11, 53); win_header(mywin, "Toggle colors"); dummy = mystrdup(cur -> filename); dummy[min(strlen(dummy), 40)] = 0x00; mvwprintw(mywin -> win, 3, 1, "%s", dummy); col = ask_colors(mywin, 4, cur -> cdef.colorize, &cur -> cdef.field_nr, &cur -> cdef.field_del, &cur -> cdef.color_schemes, &cur -> cdef.attributes, &cur -> cdef.term_emul); if (col != (char)-1) { changed = 1; cur -> cdef.colorize = col; } delete_popup(mywin); myfree(dummy); } } return changed; } void swap_re(re *a, re *b) { re temp; memcpy(&temp, a, sizeof(temp)); memcpy(a, b, sizeof(*a)); memcpy(b, &temp, sizeof(*b)); } int edit_regexp(void) { int changed = 0; NEWWIN *mywin; proginfo *cur; int f_index = 0; int cur_re = 0; mybool_t case_insensitive = re_case_insensitive; /* select window */ if (nfd > 1) f_index = select_window(HELP_ENTER_REGEXP_SELECT_WINDOW, "Select window (reg.exp. editing)"); if (f_index == -1) /* user pressed Q/X */ return 0; /* select subwindow */ cur = &pi[f_index]; if (cur -> next) cur = select_subwindow(f_index, HELP_ENTER_REGEXP_SELECT_SUBWINDOW, "Select subwindow (reg.exp. editing)"); if (!cur) return 0; /* create window */ mywin = create_popup(23, 70); for(;;) { int c, key; int loop; char buffer[58 + 1]; buffer[58] = 0x00; werase(mywin -> win); win_header(mywin, "Edit reg.exp."); mvwprintw(mywin -> win, 2, 2, "%s", cur -> filename); escape_print(mywin, 3, 2, "^a^dd, ^e^dit, ^d^elete, ^q^uit, move ^D^own, move ^U^p, ^r^eset counter"); /* display them lines */ for(loop=0; loop n_re; loop++) { strncpy(buffer, (cur -> pre)[loop].regex_str, 34); if (loop == cur_re) ui_inverse_on(mywin); mvwprintw(mywin -> win, 4 + loop, 1, "%c%c %s", zerotomin((cur -> pre)[loop].invert_regex), zerotomin((cur -> pre)[loop].use_regex), buffer); if (toupper((cur -> pre)[loop].use_regex) == 'X') { char dummy[18]; strncpy(dummy, (cur -> pre)[loop].cmd, min(17, strlen((cur -> pre)[loop].cmd))); dummy[17]=0x00; mvwprintw(mywin -> win, 4 + loop, 42, "%s", dummy); wmove(mywin -> win, 4 + loop, 41); } if (loop == cur_re) ui_inverse_off(mywin); mvwprintw(mywin -> win, 4 + loop, 60, "%d", (cur -> pre)[loop].match_count); } draw_border(mywin); mydoupdate(); /* wait for key */ for(;;) { key = wait_for_keypress(HELP_REGEXP_MENU, 2, mywin, 1); c = key; /* convert return to 'E'dit */ if (key == 13) key = c = 'E'; /* any valid keys? */ if (toupper(c) == 'Q' || c == abort_key || toupper(c) == 'X' || c == 'a' || c == 'e' || toupper(c) == 'D' || c == 'U' || key == KEY_DOWN || key == 13 || key == KEY_UP || c == 'r') break; if (key == -1) /* T/O waiting for key? then update counters */ { for(loop=0; loop n_re; loop++) mvwprintw(mywin -> win, 4 + loop, 60, "%d", (cur -> pre)[loop].match_count); mydoupdate(); } else wrong_key(); } /* exit this screen? */ if (toupper(c) == 'Q' || c == abort_key || toupper(c) == 'X') break; if (key == KEY_UP) { if (cur_re > 0) cur_re--; else wrong_key(); } else if (key == KEY_DOWN || key == 13) { if (cur_re < (cur -> n_re -1)) cur_re++; else wrong_key(); } /* add or edit */ else if (c == 'a' || c == 'e') { int invert_regex = 0, regex_type; int rc; char *regex_str, *cmd_str = NULL; /* max. 10 regular expressions */ if (c == 'a' && cur -> n_re == 10) { wrong_key(); continue; } if (c == 'e' && cur -> n_re == 0) { wrong_key(); continue; } /* ask new reg exp */ mvwprintw(mywin -> win, 15, 2, "Edit regular expression:"); if (c == 'e') regex_str = edit_string(mywin, 16, 2, 58, 128, 0, (cur -> pre)[cur_re].regex_str, HELP_ENTER_REGEXP, -1, &search_h, &case_insensitive); else regex_str = edit_string(mywin, 16, 2, 58, 128, 0, NULL, HELP_ENTER_REGEXP, -1, &search_h, &case_insensitive); /* user did not abort edit? */ if (regex_str == NULL) continue; regex_type = ask_regex_type(mywin, 18); if (regex_type == -1) continue; if (regex_type != 'm' && regex_type != 'v') { invert_regex = ask_negate_regexp(mywin, 17); if (invert_regex == -1) /* pressed 'Q' */ continue; } if (toupper(regex_type) == 'X') { mvwprintw(mywin -> win, 20, 2, "Edit command:"); if (c == 'e') cmd_str = edit_string(mywin, 21, 2, 58, 128, 0, (cur -> pre)[cur_re].cmd, HELP_ENTER_CMD, -1, &cmdfile_h, NULL); else cmd_str = edit_string(mywin, 21, 2, 58, 128, 0, NULL, HELP_ENTER_CMD, -1, &cmdfile_h, NULL); /* did the user cancel things? */ if (!cmd_str) { /* then free the memory for the new regexp * and continue with the menu */ myfree(regex_str); continue; } } changed = 1; /* edit: free previous */ if (c == 'e') { regfree(&(cur -> pre)[cur_re].regex); myfree((cur -> pre)[cur_re].regex_str); } /* add: allocate new */ else { cur_re = cur -> n_re++; cur -> pre = (re *)myrealloc(cur -> pre, cur -> n_re * sizeof(re)); memset(&(cur -> pre)[cur_re], 0x00, sizeof(re)); } /* wether to negate this expression or not */ (cur -> pre)[cur_re].invert_regex = invert_regex; /* compile */ if ((rc = regcomp(&(cur -> pre)[cur_re].regex, regex_str, REG_EXTENDED | (case_insensitive == MY_TRUE?REG_ICASE:0)))) { regexp_error_popup(rc, &(cur -> pre)[cur_re].regex); if (c == 'a') { cur -> n_re--; if (cur -> n_re == cur_re) cur_re--; } myfree(regex_str); } else { /* compilation went well, remember everything */ (cur -> pre)[cur_re].regex_str = regex_str; (cur -> pre)[cur_re].use_regex = regex_type; (cur -> pre)[cur_re].cmd = cmd_str; } /* reset counter (as it is a different regexp which has not matched anything at all) */ (cur -> pre)[cur_re].match_count = 0; } /* delete entry */ else if (c == 'd') { changed = 1; /* delete entry */ free_re(&(cur -> pre)[cur_re]); /* update administration */ if (cur -> n_re == 1) { myfree(cur -> pre); cur -> pre = NULL; } else { int n_to_move = (cur -> n_re - cur_re) - 1; if (n_to_move > 0) memmove(&(cur -> pre)[cur_re], &(cur -> pre)[cur_re+1], sizeof(re) * n_to_move); } cur -> n_re--; /* move cursor */ if (cur_re > 0 && cur_re == cur -> n_re) { cur_re--; } } else if (c == 'D') { if (cur_re < (cur -> n_re - 1)) { swap_re(&(cur -> pre)[cur_re], &(cur -> pre)[cur_re + 1]); } else wrong_key(); } else if (c == 'U') { if (cur_re > 0) { swap_re(&(cur -> pre)[cur_re], &(cur -> pre)[cur_re - 1]); } else wrong_key(); } else if (c == 'r') { (cur -> pre)[cur_re].match_count = 0; } } delete_popup(mywin); return changed; } int toggle_vertical_split(void) { int changed = 0; if (split) { if ((max_y / nfd) < 3) { error_popup("Toggle vertical split", -1, "That is not possible: the new configuration won't fit."); } else { split = 0; changed = 1; myfree(vertical_split); vertical_split = NULL; myfree(n_win_per_col); n_win_per_col = NULL; } } else { if (nfd == 1) error_popup("Toggle vertical split", -1, "There's only 1 window!"); else { char *str; NEWWIN *mywin = create_popup(7, 35); escape_print(mywin, 2, 2, "Enter number of columns"); str = edit_string(mywin, 4, 2, 20, 3, 1, NULL, HELP_SET_VERTICAL_SPLIT_N_WIN, -1, NULL, NULL); if (str) { split = atoi(str); if (split > 1) { changed = 1; } else { split = 0; } myfree(str); } delete_popup(mywin); } } return changed; } void list_keybindings(void) { NEWWIN *mywin; int prevpos = -1, curpos = 0; mywin = create_popup(20, 40); for(;;) { int c; if (curpos != prevpos) { int loop; werase(mywin -> win); win_header(mywin, "Keybindings"); for(loop=0; loop<17; loop++) { if ((loop + curpos) >= n_keybindings) break; ui_inverse_on(mywin); mvwprintw(mywin -> win, 2 + loop, 2, "%2s", key_to_keybinding(keybindings[loop + curpos].key)); ui_inverse_off(mywin); mvwprintw(mywin -> win, 2 + loop, 5, "%s", keybindings[loop + curpos].command); } draw_border(mywin); mydoupdate(); prevpos = curpos; } c = wait_for_keypress(HELP_LIST_KEYBINDINGS, 0, mywin, 0); if (toupper(c) == 'Q' || c == abort_key) break; else if (c == KEY_UP && curpos > 0) curpos--; else if ((c == KEY_DOWN || c == 13) && curpos < (n_keybindings - 1)) curpos++; else wrong_key(); } delete_popup(mywin); } void write_script(void) { FILE *fh = NULL; char *fname; NEWWIN *mywin = create_popup(12, 42); win_header(mywin, "Write script"); mvwprintw(mywin -> win, 2, 2, "Enter filename:"); fname = edit_string(mywin, 3, 2, 41, find_path_max(), 0, NULL, HELP_WRITE_SCRIPT, -1, &cmdfile_h, NULL); if (fname) fh = fopen(fname, "w"); if (fh) { int loop; fprintf(fh, "#!/bin/sh\n\n"); fprintf(fh, "multitail"); if (heartbeat_interval != 0.0) fprintf(fh, " -H %f", heartbeat_interval); if (config_file) { fprintf(fh, " -F "); write_escape_str(fh, config_file); } /* markerline color */ if (markerline_attrs.colorpair_index != -1 || markerline_attrs.attrs != -1) { fprintf(fh, " -Z "); emit_myattr_t(fh, markerline_attrs); } if (timestamp_in_markerline) fprintf(fh, " -T"); if (show_subwindow_id) fprintf(fh, " -S"); if (set_title) { fprintf(fh, " -x "); write_escape_str(fh, set_title); } if (tab_width != DEFAULT_TAB_WIDTH) fprintf(fh, " -b %d", tab_width); if (update_interval) fprintf(fh, " -u %d", update_interval); if (vertical_split) { fprintf(fh, " -sw "); for(loop=0; loop beep.beep_interval > 0) fprintf(fh, " --bi %d", cur -> beep.beep_interval); if (cur -> check_interval) fprintf(fh, " -iw %d", cur -> check_interval); if (cur -> cont) fprintf(fh, " --cont"); /* terminal emulation */ if (cur -> cdef.term_emul == TERM_ANSI) fprintf(fh, " -cT ANSI"); /* vt100 */ /* add timestamp to lines */ if (cur -> add_timestamp) fprintf(fh, " -ts"); if (cur -> label) fprintf(fh, " --label %s", cur -> label); /* title in statusline */ if (cur -> win_title) { fprintf(fh, " -t "); write_escape_str(fh, cur -> win_title); } if (cur -> mark_interval) fprintf(fh, " --mark-interval %d", cur -> mark_interval); if (cur -> repeat.suppress_repeating_lines) fprintf(fh, " --no-repeat"); /* height of window */ if (cur -> win_height != -1) fprintf(fh, " -wh %d", cur -> win_height); /* redirect output to a file or other command */ for(lloop=0; lloop < cur -> n_redirect; lloop++) { if ((cur -> predir)[lloop].type == REDIRECTTO_PIPE || (cur -> predir)[lloop].type == REDIRECTTO_PIPE_FILTERED) fprintf(fh, " -g "); else if ((cur -> predir)[lloop].type == REDIRECTTO_FILE || (cur -> predir)[lloop].type == REDIRECTTO_FILE_FILTERED) fprintf(fh, " -a "); write_escape_str(fh, (cur -> predir)[lloop].redirect); } /* conversion scheme to use */ for(conv_index=0; conv_index conversions); conv_index++) fprintf(fh, " -cv %s", conversions[get_iat_element(&cur -> conversions, conv_index)].name); /* linewrap */ if (cur -> line_wrap != 'a') { fprintf(fh, " -p %c", cur -> line_wrap); if (cur -> line_wrap == 'o') fprintf(fh, " %d", cur -> line_wrap_offset); } /* for commands: restart interval and do diff or not */ if (cur -> restart.restart != -1) { char mode = 'r'; if (cur -> restart.do_diff) mode = 'R'; if (cur -> restart.restart_clear) fprintf(fh, " -%cc", mode); else fprintf(fh, " -%c", mode); fprintf(fh, " %d", cur -> restart.restart); } /* initial number of lines to tail */ if (cur -> initial_n_lines_tail != -1) fprintf(fh, " -n %d", cur -> initial_n_lines_tail); /* folow filename or filedescriptor */ if (cur -> follow_filename) fprintf(fh, " -f"); /* if the file could not be openend, retry open? */ if (cur -> retry_open) fprintf(fh, " --retry"); /* colors and colorschemes */ if (cur -> cdef.colorize != 'n' && cur -> cdef.colorize != 0) { switch(cur -> cdef.colorize) { case 'a': fprintf(fh, " -ca "); emit_myattr_t(fh, cur -> cdef.alt_col_cdev1); fprintf(fh, " "); emit_myattr_t(fh, cur -> cdef.alt_col_cdev2); break; case 'i': fprintf(fh, " -ci "); emit_myattr_t(fh, cur -> cdef.attributes); break; case 's': fprintf(fh, " -cs"); break; case 'm': fprintf(fh, " -cm"); break; case 'f': fprintf(fh, " -cf %d ", cur -> cdef.field_nr); write_escape_str(fh, cur -> cdef.field_del); break; case 'S': { int cs_index; for(cs_index=0; cs_index cdef.color_schemes); cs_index++) fprintf(fh, " -cS %s", cschemes[get_iat_element(&cur -> cdef.color_schemes, cs_index)].name); } break; } } /* regular expressions */ for(lloop=0; lloop n_re; lloop++) { if ((cur -> pre)[lloop].invert_regex) fprintf(fh, " -v"); fprintf(fh, " -e%c ", (cur -> pre)[lloop].use_regex); write_escape_str(fh, (cur -> pre)[lloop].regex_str); if ((cur -> pre)[lloop].use_regex == 'x') { fprintf(fh, " "); write_escape_str(fh, (cur -> pre)[lloop].cmd); } } /* what to strip */ for(lloop=0; lloop n_strip; lloop++) { switch((cur -> pstrip)[lloop].type) { case STRIP_TYPE_REGEXP: fprintf(fh, " -ke "); write_escape_str(fh, (cur -> pstrip)[lloop].regex_str); break; case STRIP_KEEP_SUBSTR: fprintf(fh, " -kS "); write_escape_str(fh, (cur -> pstrip)[lloop].regex_str); break; case STRIP_TYPE_RANGE: fprintf(fh, " -kr %d %d", (cur -> pstrip)[lloop].start, (cur -> pstrip)[lloop].end); break; case STRIP_TYPE_COLUMN: fprintf(fh, " -kc "); write_escape_str(fh, (cur -> pstrip)[lloop].del); fprintf(fh, " %d", (cur -> pstrip)[lloop].col_nr); break; } } /* add filename/command */ if (cur == &pi[loop]) { if (cur -> wt == WT_COMMAND) fprintf(fh, " -l"); else if (cur -> wt == WT_STDIN) fprintf(fh, " -j"); else if (cur -> wt == WT_SOCKET) fprintf(fh, " --listen"); else if (cur -> wt == WT_FILE) { if ((cur -> filename)[0] == '-') fprintf(fh, " -i"); } } else { if (cur -> wt == WT_COMMAND) fprintf(fh, " -L"); else if (cur -> wt == WT_STDIN) fprintf(fh, " -J"); else if (cur -> wt == WT_SOCKET) fprintf(fh, " --Listen"); else if (cur -> wt == WT_FILE) fprintf(fh, " -I"); } fprintf(fh, " "); write_escape_str(fh, cur -> filename); cur = cur -> next; } while(cur); } if (fchmod(fileno(fh), S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1) { error_exit(TRUE, FALSE, "write_script: error setting mode-bits on output file.\n"); } fprintf(fh, "\n"); fclose(fh); } else if (fname != NULL) { error_popup("Write script", HELP_ERROR_WRITE_SCRIPT_CREATE_FILE, "Error creating file: %s", strerror(errno)); } myfree(fname); delete_popup(mywin); } void selective_pause() { int selected_window = select_window(HELP_PAUSE_A_WINDOW, "Pause a window"); if (selected_window != -1) { pi[selected_window].paused = 1 - pi[selected_window].paused; update_statusline(pi[selected_window].status, selected_window, &pi[selected_window]); mydoupdate(); } } void do_pause(void) { NEWWIN *mywin = create_popup(3, 8); color_on(mywin, 1); mvwprintw(mywin -> win, 1, 1, "Paused"); draw_border(mywin); color_off(mywin, 1); wmove(mywin -> win, 1, 2); mydoupdate(); (void)getch(); delete_popup(mywin); } void set_buffering(void) { char winchoice, whatlines = 'a', maxnlines = 0; NEWWIN *mywin = create_popup(13, 40); win_header(mywin, "Set buffer size"); if (nfd > 1) { escape_print(mywin, 3, 2, "Set on ^a^ll or ^o^ne"); mvwprintw(mywin -> win, 4, 2, "window(s)?"); mydoupdate(); for(;;) { winchoice = toupper(wait_for_keypress(HELP_SET_BUFFERING, 0, mywin, 0)); if (winchoice == 'Q' || winchoice == 'X' || winchoice == abort_key) { winchoice = 'Q'; break; } if (winchoice == 'A' || winchoice == 'O') break; wrong_key(); } } else { winchoice = 'A'; } /* ask wether to store all lines or just the ones matchine to the * regular expression (if any) */ if (winchoice != 'Q' && winchoice != abort_key) { escape_print(mywin, 5, 2, "Store ^a^ll or ^m^atching regular"); mvwprintw(mywin -> win, 6, 2, "expressions (if any)?"); mydoupdate(); for(;;) { whatlines = toupper(wait_for_keypress(HELP_SET_BUFFERING_STORE_WHAT, 0, mywin, 0)); if (whatlines == 'Q' || whatlines == 'X' || whatlines == abort_key) { winchoice = 'Q'; break; } if (whatlines == 'A' || whatlines == 'M') break; wrong_key(); } } /* get number of lines to store */ if (winchoice != 'Q' && winchoice != abort_key) { char *dummy; mvwprintw(mywin -> win, 7, 2, "Store how many lines? (0 for all)"); dummy = edit_string(mywin, 8, 2, 40, 7, 1, NULL, HELP_ENTER_NUMBER_OF_LINES_TO_STORE, -1, NULL, NULL); if (!dummy) { winchoice = 'Q'; } else { maxnlines = atoi(dummy); myfree(dummy); } } /* do set mark */ if (winchoice != 'Q' && winchoice != abort_key) { if (winchoice == 'A') { int loop; for(loop=0; loop win); escape_print(mywin, 1, 2, "^Manage columns^"); escape_print(mywin, 2, 2, "^a^dd, ^d^elete, ^e^dit, ^q^uit"); escape_print(mywin, 3, 2, "width # windows"); if (split < 2) { color_on(mywin, find_colorpair(COLOR_RED, -1, 0)); escape_print(mywin, 5, 2, "No columns (no vertical split)"); color_off(mywin, find_colorpair(COLOR_RED, -1, 0)); } else { for(loop=0; loop win, 4 + loop, 2, "%2d %2d", vs, nw); if (loop == y) ui_inverse_off(mywin); } } draw_border(mywin); mydoupdate(); key = toupper(wait_for_keypress(HELP_MANAGE_COLS, 0, mywin, 1)); if (key == 'Q' || key == abort_key) break; if (key == KEY_UP) { if (y) y--; else wrong_key(); } else if (key == KEY_DOWN) { if (y < (split - 1)) y++; else wrong_key(); } else if (key == 'A') { if (split == MAX_N_COLUMNS) { error_popup("Add column", -1, "Maximum number of columns (%d) reached", MAX_N_COLUMNS); } else { int is_new = 0; if (split == 0) { is_new = 1; split = 2; } else split++; if (!vertical_split) is_new = 1; vertical_split = (int *)myrealloc(vertical_split, sizeof(int) * split); n_win_per_col = (int *)myrealloc(n_win_per_col, sizeof(int) * split); if (is_new) { memset(vertical_split, 0x00, sizeof(int) * split); memset(n_win_per_col , 0x00, sizeof(int) * split); } else { vertical_split[split - 1] = 0; n_win_per_col[split - 1] = 0; } resized = 1; } } else if (key == 'D') { if (split > 0) { resized = 1; if (split == 2) { split = 0; myfree(vertical_split); vertical_split = NULL; myfree(n_win_per_col); n_win_per_col = NULL; } else { int n_to_move = (split - y) - 1; if (vertical_split) memmove(&vertical_split[y], &vertical_split[y+1], sizeof(int) * n_to_move); if (n_win_per_col ) memmove(&n_win_per_col [y], &n_win_per_col [y+1], sizeof(int) * n_to_move); split--; } if (y >= split && split > 0) y--; resized = 1; } else wrong_key(); } else if (key == 'E') { if (split > 0) { char oldval[64], *result; NEWWIN *editwin = create_popup(6, 33); escape_print(editwin, 1, 2, "Column width:"); if (!vertical_split) { vertical_split = mymalloc(split * sizeof(int)); memset(vertical_split, 0x00, split * sizeof(int)); } if (!n_win_per_col) { n_win_per_col = mymalloc(split * sizeof(int)); memset(n_win_per_col, 0x00, split * sizeof(int)); } snprintf(oldval, sizeof(oldval), "%d", vertical_split[y]); result = edit_string(editwin, 2, 2, 20, 3, 1, oldval, HELP_COLUMN_WIDTH, -1, NULL, NULL); if (result) { int vs = atoi(result); myfree(result); escape_print(editwin, 3, 2, "Number of windows per column:"); snprintf(oldval, sizeof(oldval), "%d", n_win_per_col[y]); result = edit_string(editwin, 4, 2, 20, 3, 1, oldval, HELP_N_WIN_PER_COL, -1, NULL, NULL); if (result) { vertical_split[y] = vs; n_win_per_col [y] = atoi(result); myfree(result); resized = 1; } } delete_popup(editwin); } else wrong_key(); } else wrong_key(); } delete_popup(mywin); return resized; } int set_window_sizes(void) { int resized = 0; NEWWIN *mywin = create_popup(8, 35); for(;;) { int key; werase(mywin -> win); escape_print(mywin, 1, 2, "^m^ manage columns"); escape_print(mywin, 2, 2, "^h^ set height of a window"); escape_print(mywin, 3, 2, "^q^ quit this menu"); draw_border(mywin); mydoupdate(); key = toupper(wait_for_keypress(HELP_SET_WINDOWSIZES, 0, mywin, 0)); if (key == 'Q' || key == abort_key) break; if (key == 'M') { resized |= set_window_widths(); } else if (key == 'H') { if (nfd > 1) { int window = select_window(HELP_SET_WINDOW_HEIGHT_SELECT_WINDOW, NULL); if (window != -1) { char *str; char oldval[5] = { 0 }; if (pi[window].win_height > -1) snprintf(oldval, sizeof(oldval), "%d", pi[window].win_height); else if (pi[window].data) snprintf(oldval, sizeof(oldval), "%d", pi[window].data -> height); mvwprintw(mywin -> win, 4, 2, "Enter number of lines:"); mvwprintw(mywin -> win, 5, 2, "(0 = automatic)"); mydoupdate(); str = edit_string(mywin, 6, 2, 20, 3, 1, oldval, HELP_SET_WINDOW_HEIGHT, -1, NULL, NULL); if (str) { int dummy = atoi(str); myfree(str); if (dummy == 0) dummy = -1; if (dummy == -1 || dummy >= 2) { pi[window].win_height = dummy; resized = 1; break; } error_popup("Set window size", HELP_SET_WINDOW_HEIGHT, "Invalid height (%d) entered.", dummy); } } } else { mvwprintw(mywin -> win, 5, 2, "You need at least 2 windows."); } mydoupdate(); } else { wrong_key(); } } delete_popup(mywin); return resized; } void terminal_mode(void) { proginfo *cur = NULL; int f_index = -1; if (nfd > 0) { NEWWIN *mywin = create_popup(8 + nfd, 40); win_header(mywin, "Enter terminal mode"); mydoupdate(); f_index = select_window(HELP_TERMINAL_MODE_SELECT_WINDOW, NULL); delete_popup(mywin); } if (f_index != -1) { cur = &pi[f_index]; if (cur -> next) cur = select_subwindow(f_index, HELP_TOGGLE_COLORS_SELECT_SUBWINDOW, "Enter terminalmode: select subwindow"); } if (cur) { if (cur -> wt != WT_COMMAND) { error_popup("Enter terminal mode", -1, "Terminal mode is only possible for commands."); } else { terminal_index = cur; terminal_main_index = f_index; prev_term_char = -1; redraw_statuslines(); } } } void wipe_window(void) { NEWWIN *mywin = create_popup(5, 35); escape_print(mywin, 1, 2, "^Clear window^"); escape_print(mywin, 3, 2, "Press 0...9"); mydoupdate(); for(;;) { int c = wait_for_keypress(HELP_WIPE_WINDOW, 0, mywin, 0); if (c == abort_key || toupper(c) == 'Q') break; if (c < '0' || c > '9') { wrong_key(); continue; } c -= '0'; if (c >= nfd) { wrong_key(); continue; } if (pi[c].hidden) { wrong_key(); continue; } werase(pi[c].data -> win); mydoupdate(); break; } delete_popup(mywin); } void wipe_all_windows(void) { int loop; for(loop=0; loop win); } } void add_markerline(int index, proginfo *cur, proginfo *type, char *text) { char buffer[TIMESTAMP_EXTEND_BUFFER] = { 0 }; char *ml; char *curfname = cur ? cur -> filename : ""; char *curtext = USE_IF_SET(text, ""); double now = get_ts(); int str_len; if (timestamp_in_markerline) get_now_ts(statusline_ts_format, buffer, sizeof(buffer)); str_len = strlen(curfname) + 1 + strlen(buffer) + 1 + strlen(curtext) + 1 + 1; ml = (char *)mymalloc(str_len); snprintf(ml, str_len, "%s%s%s%s%s", curtext, curtext[0]?" ":"", curfname, (curtext[0] || curfname[0])?" ":"", buffer); do_print(index, type, ml, NULL, -1, now); do_buffer(index, type, ml, 1, now); myfree(ml); update_panels(); } void do_terminal(char c) { if (prev_term_char == 1) /* ^A */ { if (c == 'd') { terminal_main_index = -1; terminal_index = NULL; redraw_statuslines(); mydoupdate(); } else { WRITE(terminal_index -> wfd, &prev_term_char, 1, "do_terminal"); WRITE(terminal_index -> wfd, &c, 1, "do_terminal"); } } else { if (c != 1) /* ^A */ WRITE(terminal_index -> wfd, &c, 1, "do_terminal"); } prev_term_char = c; } void set_linewrap(void) { NEWWIN *mywin = create_popup(6, 64); int f_index = 0; proginfo *cur = NULL; escape_print(mywin, 1, 2, "^Set linewrap^"); escape_print(mywin, 3, 2, "^l^eft/^a^ll/^r^ight/^s^yslog/^S^yslog: no procname/^o^ffset/^w^ordwrap"); mydoupdate(); /* only popup selectionbox when more then one window */ if (nfd > 1) f_index = select_window(HELP_DELETE_SELECT_WINDOW, "Select window"); if (f_index != -1) { if (pi[f_index].next) cur = select_subwindow(f_index, HELP_DELETE_SELECT_SUBWINDOW, "Select subwindow"); else cur = &pi[f_index]; } if (cur) { for(;;) { int c = wait_for_keypress(HELP_SET_LINEWRAP, 0, mywin, 0); if (c == abort_key || toupper(c) == 'Q') break; if (c == 'l' || c == 'a' || c == 'r' || c == 's' || c == 'S' || c == 'o' || c == 'w') { if (c == 'o') { char offset[5], *new_offset; snprintf(offset, sizeof(offset), "%d", cur -> line_wrap_offset); mvwprintw(mywin -> win, 4, 2, "Offset:"); new_offset = edit_string(mywin, 4, 10, 4, 4, 1, offset, -1, -1, NULL, NULL); if (!new_offset) break; cur -> line_wrap_offset = atoi(new_offset); myfree(new_offset); } cur -> line_wrap = c; break; } wrong_key(); } } delete_popup(mywin); } void horizontal_scroll(int direction) { proginfo *cur = &pi[0]; do { if (cur->line_wrap == 'l') { if (direction == KEY_RIGHT) { cur->line_wrap_offset++; } else { if (cur->line_wrap_offset > 0) cur->line_wrap_offset--; } } cur= cur->next; } while(cur); } void regexp_error_popup(int rc, regex_t *pre) { char popup_buffer[4096] = { 0 }; char *error = convert_regexp_error(rc, pre); if (error) { int len = strlen(popup_buffer); if (strlen(error) > 58) { memcpy(&popup_buffer[len], error, 58); len += 58; popup_buffer[len++] = '\n'; strncat(popup_buffer, &error[58], max(1, sizeof(popup_buffer) - len)); popup_buffer[sizeof(popup_buffer) - 1] = 0x00; } else { strncat(popup_buffer, error, sizeof(popup_buffer)-strlen(popup_buffer)-1); } myfree(error); } error_popup("regexp_error_popup: failed to compile regular expression!", HELP_COMPILE_REGEXP_FAILED, popup_buffer); } void draw_gui_window_header(proginfo *last_changed_window) { char *out = NULL; int out_size = 0; int out_fill_size = 0; int pos = 0; int str_len; static char loadstr[16]; struct utsname uinfo; if (!set_title) return; str_len = strlen(set_title); for(;pos < str_len;) { if (set_title[pos] == '%') { char *repl_str = NULL; switch(set_title[++pos]) { case 'f': /* changed file */ repl_str = last_changed_window?last_changed_window -> filename:""; break; case 'h': /* hostname */ if (uname(&uinfo) == -1) error_exit(TRUE, FALSE, "uname() failed\n"); repl_str = uinfo.nodename; break; case 'l': /* current load */ { double v1, v2, v3; get_load_values(&v1, &v2, &v3); snprintf(loadstr, sizeof(loadstr), "%f", v1); repl_str = loadstr; } break; case 'm': repl_str = mail?"New mail!":""; break; case 'u': /* username */ repl_str = getusername(); break; case 't': /* atime */ if (last_changed_window) { char *dummy; time_t lastevent = last_changed_window -> statistics.lastevent; repl_str = ctime(&lastevent); dummy = strchr(repl_str, '\n'); if (dummy) *dummy = 0x00; } else { repl_str = ""; } break; case '%': /* % */ repl_str = "%"; break; } if (repl_str) { int repl_str_len = strlen(repl_str); /* +1: 0x00 */ int req_size = out_fill_size + repl_str_len; if (req_size > 0) { grow_mem_if_needed(&out, &out_size, req_size); memcpy(&out[out_fill_size], repl_str, repl_str_len); out_fill_size += repl_str_len; } } } else { grow_mem_if_needed(&out, &out_size, out_fill_size + 1); out[out_fill_size++] = set_title[pos]; } pos++; } /* terminate string */ grow_mem_if_needed(&out, &out_size, out_fill_size + 1); out[out_fill_size] = 0x00; gui_window_header(out); myfree(out); } void send_signal_failed(proginfo *cur) { NEWWIN *mywin = create_popup(6, 60); color_on(mywin, find_colorpair(COLOR_RED, -1, 0)); mvwprintw(mywin -> win, 2, 2, "Error sending signal to %s", cur -> filename); mvwprintw(mywin -> win, 3, 2, "%s", strerror(errno)); color_off(mywin, find_colorpair(COLOR_RED, -1, 0)); (void)wait_for_keypress(HELP_SEND_SIGNAL_FAILED, 0, mywin, 0); delete_popup(mywin); } int send_signal_select_signal(void) { int sig = -1; NEWWIN *mywin = create_popup(22, 40); int cursor_index = 0; for(;;) { int loop, c; werase(mywin -> win); win_header(mywin, "Select signal to send"); mvwprintw(mywin -> win, 20, 2, "Press ^g to abort"); for(loop=1; loop<=31; loop++) { if (loop == (cursor_index + 1)) ui_inverse_on(mywin); if (loop > 16) mvwprintw(mywin -> win, 2 + loop - 16, 18, "%s", sigs[loop]); else mvwprintw(mywin -> win, 2 + loop , 2, "%s", sigs[loop]); if (loop == (cursor_index + 1)) ui_inverse_off(mywin); } draw_border(mywin); mydoupdate(); c = toupper(wait_for_keypress(HELP_SELECT_SIGNAL, 0, mywin, 1)); if (c == KEY_UP) { if (cursor_index) cursor_index--; else wrong_key(); } else if (c == KEY_DOWN) { if (cursor_index < 30) cursor_index++; else wrong_key(); } else if (c == KEY_LEFT) { if (cursor_index > 16) cursor_index -= 16; else wrong_key(); } else if (c == KEY_RIGHT) { if (cursor_index < 16) { cursor_index += 16; if (cursor_index > 30) cursor_index = 30; } else wrong_key(); } else if (c == 'Q' || c == 'X' || c == abort_key) { sig = -1; break; } else if (c == ' ' || c == 13) { sig = cursor_index + 1; break; } else wrong_key(); } delete_popup(mywin); return sig; } void send_signal(void) { int f_index = 0; proginfo *cur = NULL; if (nfd > 1) f_index = select_window(HELP_SEND_SIGNAL_SELECT_WINDOW, "Select window to send signal to"); if (f_index != -1) { char all = 1; if (pi[f_index].next) { NEWWIN *mywin = create_popup(13, 24); win_header(mywin, "Send signal to window"); escape_print(mywin, 3, 2, "To all? (^y^/^n^)"); mydoupdate(); all = ask_yes_no(HELP_SEND_SIGNAL_WINDOW_SEND_TO_ALL_SUBWIN, mywin); delete_popup(mywin); } if (all == 1) /* pressed 'Y' */ { int sig = send_signal_select_signal(); cur = &pi[f_index]; while(cur && sig != -1) { if (kill(cur -> pid, sig) == -1) { send_signal_failed(cur); break; } cur = cur -> next; } } else if (all == 0) /* pressed 'N' */ { proginfo *cur = select_subwindow(f_index, HELP_SEND_SIGNAL_SELECT_SUBWINDOW, "Select subwindow"); if (cur) { int sig = send_signal_select_signal(); if (sig != -1 && kill(cur -> pid, sig) == -1) send_signal_failed(cur); } } } } void screendump_do(WINDOW *win) { char *filename; NEWWIN *mywin = create_popup(7, 30); win_header(mywin, "Screendump"); mvwprintw(mywin -> win, 3, 2, "Select file to write to:"); filename = edit_string(mywin, 4, 2, 38, find_path_max(), 0, "screendump.txt", HELP_SCREENDUMP_SELECT_FILE, -1, &cmdfile_h, NULL); if (filename) { FILE *fh = fopen(filename, "wb"); int maxy = 0, maxx = 0; int y, x; getmaxyx(win, maxy, maxx); for(y=0; y=0; curmaxx--) { if ((mvwinch(win, y, curmaxx) & A_CHARTEXT) != 32) break; } curmaxx++; for(x=0; x win); } } void truncate_file(void) { int f_index = 0; proginfo *cur = NULL; if (nfd > 1) f_index = select_window(HELP_TRUNCATE_FILE_SELECT_WINDOW, "Select window to truncate"); if (f_index != -1) { if (pi[f_index].next) cur = select_subwindow(f_index, HELP_TRUNCATE_FILE_SELECT_SUBWINDOW, "Select subwindow"); else cur = &pi[f_index]; if (cur) { if (cur -> wt == WT_FILE) { NEWWIN *mywin = create_popup(8, 50); color_on(mywin, find_colorpair(COLOR_RED, -1, 0)); win_header(mywin, "Truncate file"); mvwprintw(mywin -> win, 3, 2, "Truncate file %s", shorten_filename(cur -> filename, 31)); mvwprintw(mywin -> win, 4, 2, "Are you sure? (y/n)"); color_off(mywin, find_colorpair(COLOR_RED, -1, 0)); mydoupdate(); if (wait_for_keypress(HELP_TRUNCATE_AREYOUSURE, 0, mywin, 0) == 'y') { if (truncate(cur -> filename, 0) == -1) error_popup("Truncate failed", HELP_TRUNCATE_FAILED, "Error: %s", strerror(errno)); else { color_on(mywin, find_colorpair(COLOR_YELLOW, -1, 0)); escape_print(mywin, 5, 2, "_File truncated. Press any key._"); color_off(mywin, find_colorpair(COLOR_YELLOW, -1, 0)); mydoupdate(); (void)wait_for_keypress(-1, 0, mywin, 0); } } delete_popup(mywin); } else error_popup("Truncate failed", HELP_TRUNCATE_ONLY_LOGFILES, "Only logFILES can be truncated."); } } } void draw_marker_line(NEWWIN *win, char *string, proginfo *marker_type) { int mx = getmaxx(win -> win); int len = strlen(string); int left_dot = (mx / 2) - (len / 2); int loop; myattr_t attrs = { -1, -1 }; char dot = '-'; if (marker_type == MARKER_REGULAR) { attrs = markerline_attrs; dot = markerline_char; } else if (marker_type == MARKER_CHANGE) { attrs = changeline_attrs; dot = changeline_char; } else if (marker_type == MARKER_IDLE) { attrs = idleline_attrs; dot = idleline_char; } else if (marker_type == MARKER_MSG) { attrs = msgline_attrs; dot = msgline_char; } myattr_on(win, attrs); for(loop=0; loop win, dot); if (len < mx) wprintw(win -> win, "%s", string); else wprintw(win -> win, "%s", shorten_filename(string, mx)); for(loop=0; loop win, dot); myattr_off(win, attrs); } void search_in_all_windows(void) { char *find; NEWWIN *mywin = create_popup(8, 44); mybool_t case_insensitive = re_case_insensitive; win_header(mywin, "Find"); mvwprintw(mywin -> win, 3, 2, "^u empty line, ^g abort"); find = edit_string(mywin, 5, 2, 40, 80, 0, reuse_searchstring?global_find:NULL, HELP_SEARCH_IN_ALL_WINDOWS, -1, &search_h, &case_insensitive); delete_popup(mywin); myfree(global_find); global_find = find; if (find) merged_scrollback_with_search(find, case_insensitive); } void highlight_in_all_windows(void) { char *find; NEWWIN *mywin = create_popup(5, 44); mybool_t case_insensitive = re_case_insensitive; win_header(mywin, "Global highlight"); find = edit_string(mywin, 3, 2, 40, 80, 0, reuse_searchstring?global_highlight_str:NULL, HELP_HIGHLIGHT_IN_ALL_WINDOWS, -1, &search_h, &case_insensitive); delete_popup(mywin); if (global_highlight_str) { regfree(&global_highlight_re); myfree(global_highlight_str); global_highlight_str = NULL; } if (find) { int rc; if ((rc = regcomp(&global_highlight_re, find, REG_EXTENDED | (case_insensitive == MY_TRUE?REG_ICASE:0)))) { regexp_error_popup(rc, &global_highlight_re); myfree(find); } else { global_highlight_str = find; } } } void toggle_regexp_case_insensitive(void) { NEWWIN *mywin = create_popup(6, 40); re_case_insensitive = !re_case_insensitive; mvwprintw(mywin -> win, 2, 2, "Default searche case now is %s.", re_case_insensitive == MY_TRUE?"insensitive":"sensitive"); escape_print(mywin, 4, 2, "_Press any key to exit this screen_"); mydoupdate(); (void)wait_for_keypress(-1, 0, mywin, 0); delete_popup(mywin); } void inverse_on(NEWWIN *win) { wattron(win -> win, inverse_attrs); } void inverse_off(NEWWIN *win) { wattroff(win -> win, inverse_attrs); } void select_conversionschemes(void) { int f_index = 0; proginfo *cur = NULL; if (nfd > 1) f_index = select_window(HELP_TRUNCATE_FILE_SELECT_WINDOW, "Select window"); if (f_index != -1) { if (pi[f_index].next) cur = select_subwindow(f_index, HELP_TRUNCATE_FILE_SELECT_SUBWINDOW, "Select subwindow"); else cur = &pi[f_index]; if (cur) { (void)select_schemes(conversions, n_conversions, SCHEME_CONVERSION, &cur -> conversions); } } } void toggle_subwindow_nr(void) { show_subwindow_id = 1 - show_subwindow_id; } void clear_a_buffer(void) { int f_index = 0; if (nfd > 1) f_index = select_window(HELP_CLEAR_BUFFER, "Select window"); if (f_index != -1) { delete_be_in_buffer(&lb[f_index]); werase(pi[f_index].data -> win); mydoupdate(); } } void clear_all_buffers(void) { int loop; for(loop=0; loop win); mydoupdate(); } } void ask_case_insensitive(mybool_t *pcase_insensitive) { NEWWIN *mywin = create_popup(6, 40); mvwprintw(mywin -> win, 2, 2, "Search case insensitive?"); mvwprintw(mywin -> win, 3, 12, "(Y/N)"); mydoupdate(); for(;;) { int c = toupper(wait_for_keypress(-1, 0, mywin, 0)); if (c == 'Q' || c == abort_key) break; if (c == 'Y') { *pcase_insensitive = MY_TRUE; break; } else if (c == 'N') { *pcase_insensitive = MY_FALSE; break; } wrong_key(); } delete_popup(mywin); } multitail-7.1.5/ui.h000066400000000000000000000033601465663511400143330ustar00rootroot00000000000000int swap_window(void); int delete_window(void); int hide_window(void); char ask_negate_regexp(NEWWIN *win, int line); int select_schemes(void *schemes_in, int n_schemes_in, scheme_t tscheme, int_array_t *schemes_out); char ask_colors(NEWWIN *win, int line, char cur, char *fieldnr, char **fielddel, int_array_t *color_schemes, myattr_t *attrs); char ask_regex_type(NEWWIN *win, int line); int add_window(void); int toggle_colors(void); int edit_regexp(void); int toggle_vertical_split(void); void do_pause(void); void set_buffering(void); void list_keybindings(void); void write_script(void); int set_window_widths(void); int set_window_sizes(void); void selective_pause(); void wipe_window(void); void wipe_all_windows(void); void do_terminal(char c); void set_linewrap(void); void horizontal_scroll(int d); void terminal_mode(void); void regexp_error_popup(int rc, regex_t *pre); void draw_gui_window_header(proginfo *last_changed_window); void send_signal(void); void screendump_do(WINDOW *win); void screendump(void); void truncate_file(void); void edit_colors(int index); int color_management(myattr_t *org, myattr_t *new); int hide_all_but(void); int unhide_all_windows(void); void error_popup(char *title, int help, char *message, ...); void add_markerline(int index, proginfo *cur, proginfo *type, char *text); void draw_marker_line(NEWWIN *win, char *string, proginfo *marker_type); void search_in_all_windows(void); void highlight_in_all_windows(void); void toggle_regexp_case_insensitive(void); void restart_window(void); void inverse_on(NEWWIN *win); void inverse_off(NEWWIN *win); void select_conversionschemes(void); void toggle_subwindow_nr(void); void clear_a_buffer(void); void clear_all_buffers(void); void ask_case_insensitive(mybool_t *pcase_insensitive); multitail-7.1.5/utils.c000066400000000000000000000521271465663511400150560ustar00rootroot00000000000000#define _LARGEFILE64_SOURCE /* required for GLIBC to enable stat64 and friends */ #include "doassert.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef _POSIX_PRIORITY_SCHEDULING #include #endif #include #include #include #if defined(sun) || defined(__sun) #include #endif #include #include #include #include #include #include "version.h" #include "error.h" #include "mem.h" #include "mt.h" #include "term.h" #include "globals.h" #include "utils.h" int find_path_max(void) { #ifdef PATH_MAX int path_max = PATH_MAX; #else int path_max = pathconf("/", _PC_PATH_MAX); if (path_max <= 0) { if (errno) error_exit(TRUE, FALSE, "pathconf() failed\n"); path_max = 4096; } else path_max++; /* since its relative to root */ #endif if (path_max > 4096) path_max = 4096; return path_max; } int myrand(int max) { return (int)(((double)max * (double)rand()) / (double)RAND_MAX); } ssize_t WRITE(int fd, char *whereto, size_t len, char *for_whom) { ssize_t cnt=0; while(len>0) { ssize_t rc; rc = write(fd, whereto, len); if (rc == -1) { if (errno != EINTR && errno != EAGAIN) error_exit(TRUE, FALSE, "Problem writing to file descriptor while processing %s.\n", for_whom); } else if (rc == 0) { break; } else { whereto += rc; len -= rc; cnt += rc; } } return cnt; } void get_load_values(double *v1, double *v2, double *v3) { #if !defined(__UCLIBC__) && (defined(__FreeBSD__) || defined(linux) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) || defined(__GNU__) || defined(__sun) || defined(sun)) #if defined(__GLIBC__) && ( __GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 2)) /* Older glibc doesn't have getloadavg() - use sysinfo() */ /* thanks to Ville Herva for this code! */ double scale = 1 << SI_LOAD_SHIFT; struct sysinfo si; if (sysinfo(&si) == -1) { /* let's exit: if these kind of system- * calls start to fail, something must be * really wrong */ error_exit(TRUE, FALSE, "sysinfo() failed\n"); } *v1 = (double)si.loads[0] / scale; *v2 = (double)si.loads[1] / scale; *v3 = (double)si.loads[2] / scale; #else double loadavg[3]; if (getloadavg(loadavg, 3) == -1) { /* see comment on sysinfo() */ error_exit(TRUE, FALSE, "getloadavg() failed\n"); } *v1 = loadavg[0]; *v2 = loadavg[1]; *v3 = loadavg[2]; #endif #else *v1 = *v2 = *v3 = -1.0; #endif } int get_vmsize(pid_t pid) { int vmsize = -1; #if defined(linux) FILE *fh; int path_max = find_path_max(); char *path = mymalloc(path_max); assert(pid > 1); snprintf(path, path_max, "/proc/%d/stat", pid); fh = fopen(path, "r"); if (fh) { char *dummystr = mymalloc(path_max); char dummychar; int dummy; if (fscanf(fh, "%d %s %c %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d", &dummy, dummystr, &dummychar, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &vmsize) != 23) vmsize = -1; fclose(fh); myfree(dummystr); } myfree(path); #endif return vmsize; } int mykillpg(pid_t pid, int sigtype) { #ifdef AIX return kill(pid, sigtype); #else return killpg(pid, sigtype); #endif } /** stop_process * - in: int pid pid of process * - returns: nothing * this function sends a TERM-signal to the given process, sleeps for 1000 microseconds * and then sends a KILL-signal to the given process if it still exists. the TERM signal * is sent so the process gets the possibility to gracefully exit. if it doesn't do that * in 1000 microseconds, it is terminated */ void stop_process(pid_t pid) { assert(pid > 1); #ifndef __APPLE__ if (mykillpg(pid, SIGTERM) == -1) { if (errno != ESRCH) error_exit(TRUE, FALSE, "Problem stopping child process with PID %d (SIGTERM).\n", pid); } #endif usleep(1000); /* process still exists? */ if (mykillpg(pid, SIGTERM) == 0) { /* sleep for a millisecond... */ usleep(1000); /* ...and then really terminate the process */ if (mykillpg(pid, SIGKILL) == -1) { #ifdef __APPLE__ /* don't exit if the error is EPERM: macOS doesn't allow you to kill a process that has been already killed (i.e. zombies), which is what we did with the second mykillpg above. */ if (errno != ESRCH && errno != EPERM) #else if (errno != ESRCH) #endif error_exit(TRUE, FALSE, "Problem stopping child process with PID %d (SIGKILL).\n", pid); } } #ifdef __APPLE__ else if (errno != ESRCH && errno != EPERM) #else else if (errno != ESRCH) #endif { error_exit(TRUE, FALSE, "Problem stopping child process with PID %d (SIGTERM).\n", pid); } /* wait for the last remainder of the died process to go away, * otherwhise we'll find zombies on our way */ if (waitpid(pid, NULL, WNOHANG | WUNTRACED) == -1) { if (errno != ECHILD) error_exit(TRUE, FALSE, "waitpid() failed\n"); } #ifdef __APPLE__ /* since we ignored the case of a EPERM error above, check if the process got stopped regardless or if we actually failed to stop it */ BOOL process_gone = FALSE; for (int i = 0; i < 10; i++) { if (i != 0) usleep(1000); if (mykillpg(pid, 0) == -1) { if (errno == ESRCH) { process_gone = TRUE; break; } } } if (!process_gone) error_exit(TRUE, FALSE, "Could not confirm that child process with PID %d has been stopped.\n", pid); #endif } /** delete_array * - in: char **list array of strings to free * int n number of strings in this array * - returns: nothing * this function frees an array of strings: all strings are freed and * also the pointer-list itself is freed */ void delete_array(char **list, int n) { int loop; assert(n >= 0); for(loop=n-1; loop>=0; loop--) myfree(list[loop]); myfree(list); } int find_char_offset(char *str, char what) { char *pnt; assert(what > 0); pnt = strchr(str, what); if (!pnt) return -1; return (int)(pnt - str); } int file_info(char *filename, off64_t *file_size, time_field_t tft, time_t *ts, mode_t *mode) { struct stat64 buf; if (stat64(filename, &buf) == -1) { if (errno != ENOENT) error_exit(TRUE, FALSE, "Error while obtaining details of file %s.\n", filename); return -1; } if (file_size) *file_size = buf.st_size; if (ts) { if (tft == TT_ATIME) *ts = buf.st_atime; else if (tft == TT_MTIME) *ts = buf.st_mtime; else if (tft == TT_CTIME) *ts = buf.st_ctime; else { assert(tft == 0); *ts = 0; } } if (mode) *mode = buf.st_mode; return 0; } int file_exist(char *filename) { struct stat buf; int rc = stat(filename, &buf); if (rc == -1 && errno != ENOENT) error_exit(TRUE, FALSE, "stat() on file %s failed.\n", filename); return rc; } char * convert_regexp_error(int error, const regex_t *preg) { /* errors are specified not to be longer then 256 characters */ char *multitail_string = "MultiTail warning: regular expression failed, reason: "; int len = strlen(multitail_string); char *error_out = NULL; const int max_err_len = 256; assert(error != 0); if (error != REG_NOMATCH) { error_out = (char *)mymalloc(max_err_len + len + 1); memcpy(error_out, multitail_string, len); /* convert regexp error */ regerror(error, preg, &error_out[len], max_err_len); } return error_out; } char * amount_to_str(long long int amount) { char *out = mymalloc(AMOUNT_STR_LEN); /* ...XB\0 */ if (amount >= M_GB) /* GB */ snprintf(out, AMOUNT_STR_LEN, "%dGB", (int)((amount + M_GB - 1) / M_GB)); else if (amount >= M_MB) /* MB */ snprintf(out, AMOUNT_STR_LEN, "%dMB", (int)((amount + M_MB - 1) / M_MB)); else if (amount >= M_KB) /* KB */ snprintf(out, AMOUNT_STR_LEN, "%dKB", (int)((amount + M_KB - 1) / M_KB)); else snprintf(out, AMOUNT_STR_LEN, "%d", (int)(amount)); return out; } struct passwd *getuserinfo(void) { return getpwuid(geteuid()); } char * getusername(void) { static char username[128] = "???"; if (username[0] == '?') { char *logname = getenv("LOGNAME"); if (logname) { strncpy(username, logname, 128); username[127] = 0x00; } } if (username[0] == '?') { struct passwd *pw = getuserinfo(); if (pw) { strncpy(username, getuserinfo() -> pw_name, sizeof(username)); username[sizeof(username) - 1] = 0x00; } } return username; } /* these are there because AIX/IRIX can return EINTR for those */ int myopen(char *path, int mode) { int fd; for(;;) { fd = open64(path, mode); if (fd == -1) { if (errno == EINTR || errno == EAGAIN) /* for AIX */ continue; return fd; } break; } return fd; } int myclose(int fd) { for(;;) { if (close(fd) == -1) { if (errno == EINTR || errno == EAGAIN) /* for AIX */ continue; return -1; } return 0; } } char * shorten_filename(char *in, int max_len) { static char buffer[4096]; int len = strlen(in); int cutlen, dummy; assert(max_len >= 0); if (len <= max_len) return in; cutlen = (max_len - 3) / 2; memcpy(buffer, in, cutlen); memcpy(&buffer[cutlen], "...", 3); dummy = max_len - (cutlen + 3); memcpy(&buffer[cutlen + 3], &in[len - dummy], dummy + 1); return buffer; } double get_ts(void) { struct timeval ts; if (gettimeofday(&ts, NULL) == -1) error_exit(TRUE, FALSE, "gettimeofday() failed"); return (((double)ts.tv_sec) + ((double)ts.tv_usec)/1000000.0); } int match_files(char *search_for, char **path, char ***found, char **isdir) { DIR *dir; struct dirent *entry; char *cur_dir = mymalloc(find_path_max() + 1); char *fname; char **list = NULL; int nfound = 0; size_t fname_size; int path_len; int s1, s2; char *slash = strrchr(search_for, '/'); if (slash) { fname = mystrdup(slash + 1); *(slash + 1) = 0x00; *path = mystrdup(search_for); } else { *path = mystrdup("./"); fname = mystrdup(search_for); } fname_size = strlen(fname); path_len = strlen(*path); dir = opendir(*path); if (!dir) { free(cur_dir); return 0; } memcpy(cur_dir, *path, path_len + 1); while((entry = readdir(dir)) != NULL) { if ((fname_size == 0 || strncmp(entry -> d_name, fname, fname_size) == 0) && strcmp(entry -> d_name, ".") != 0 && strcmp(entry -> d_name, "..") != 0) { struct stat finfo; /* get filename */ list = (char **)myrealloc(list, (nfound + 1) * sizeof(char *)); list[nfound] = mystrdup(entry -> d_name); /* check if the file is a directory */ *isdir = (char *)myrealloc(*isdir, (nfound + 1) * sizeof(char)); strncpy(&cur_dir[path_len], entry -> d_name, max(0,find_path_max() - path_len)); if (stat(cur_dir, &finfo) == -1) { if (errno != ENOENT) /* file did not disappear? then something is very wrong */ error_exit(TRUE, FALSE, "Error while invoking stat() %s.\n", cur_dir); } (*isdir)[nfound] = S_ISDIR(finfo.st_mode)?1:0; nfound++; } } if (closedir(dir) == -1) error_exit(TRUE, FALSE, "closedir() failed\n"); /* qsort( (void *)list, (size_t)nfound, sizeof(char *), compare_filenames); */ for(s1=0; s1<(nfound - 1); s1++) { for(s2=s1+1; s2 regex_str); if (cur_re -> use_regex) regfree(&cur_re -> regex); myfree(cur_re -> cmd); } } char * find_most_recent_file(char *filespec, char *cur_file) { glob_t files; char *selected_file = NULL; unsigned int loop; time_t prev_ts = (time_t)0; /* get timestamp of previous file */ if (cur_file) { if (file_info(cur_file, NULL, TT_MTIME, &prev_ts, NULL) == -1) { prev_ts = (time_t)0; } } /* get list of files that match */ #if defined(__APPLE__) || defined(__CYGWIN__) if (glob(filespec, GLOB_ERR | GLOB_NOSORT, NULL, &files) != 0) #else if (glob(filespec, GLOB_ERR | GLOB_NOSORT | GLOB_NOESCAPE, NULL, &files) != 0) #endif { return NULL; } /* see if any of them is more recent than the current one */ for(loop=0; loop prev_ts) { selected_file = files.gl_pathv[loop]; prev_ts = new_ts; } } /* found a file? then remember the filename */ if (selected_file != NULL) { selected_file = mystrdup(selected_file); } globfree(&files); return selected_file; } char zerotomin(char c) { if (c == 0) return 'n'; if (c == 1) return 'y'; return c; } char *find_next_par(char *start) { char *dummy = strchr(start, ':'); if (dummy) { *dummy = 0x00; dummy++; } return dummy; } int mydup(int old_fd) { int new_fd = -1; for(;;) { new_fd = dup(old_fd); if (new_fd == -1) { if (errno == EINTR) continue; error_exit(TRUE, FALSE, "dup() failed\n"); } break; } return new_fd; } char * term_t_to_string(term_t term) { switch(term) { case TERM_ANSI: return "ansi"; case TERM_XTERM: return "xterm"; case TERM_IGNORE: return "dumb"; } return "dumb"; } void double_ts_to_str(dtime_t ts, char *format_str, char *dest, int dest_size) { time_t now = (time_t)ts; struct tm *ptm = localtime(&now); if (!ptm) error_exit(TRUE, FALSE, "localtime() failed\n"); assert(ts > 0); assert(dest_size > 0); (void)strftime(dest, dest_size, format_str, ptm); } void get_now_ts(char *format_str, char *dest, int dest_size) { double_ts_to_str(get_ts(), format_str, dest, dest_size); } void duplicate_re_array(re *pre_in, int n_rein, re **pre_out, int *n_reout) { int loop, old_n = *n_reout; assert(n_rein >= 0); *n_reout += n_rein; if (*n_reout) { *pre_out = (re *)myrealloc(*pre_out, (*n_reout) * sizeof(re)); for(loop=0; loop= 0); *n_esout += n_esin; *pes_out = (strip_t *)myrealloc(*pes_out, (*n_esout) * sizeof(strip_t)); for(loop=0; loop 0); while(*cur_len < requested_len) { changed = 1; if (*cur_len) (*cur_len) *= 2; else *cur_len = 128; } if (changed) *what = myrealloc(*what, *cur_len); } int READ(int fd, char *where_to, int max_len, char *for_whom) { assert(max_len > 0); for(;;) { int rc = read(fd, where_to, max_len); if (rc >= 0) return rc; if (errno != EINTR && errno != EAGAIN) error_exit(TRUE, FALSE, "Error writing to fd for %s.\n", for_whom); } } int get_value_arg(char *par, char *string, valcheck_t check) { long int result; int len, loop; int multiplier = 1; if (!string) error_exit(FALSE, FALSE, "%s needs a parameter.\n", par); len = strlen(string); for(loop=0; loop INT_MAX || result == LONG_MIN || result == LONG_MAX) error_exit(FALSE, FALSE, "Value %s for parameter %s is too large.\n", string, par); result *= multiplier; if (check == VAL_ZERO_POSITIVE) { if (result < 0) error_exit(FALSE, FALSE, "Value for %s must be >= 0.\n", par); } else if (check == VAL_POSITIVE_NOT_1) { if (result < 0 || result == 1) error_exit(FALSE, FALSE, "Value for %s must be >= 0 and must not be 1.\n", par); } else if (check == VAL_POSITIVE) { if (result <= 0) error_exit(FALSE, FALSE, "Value for %s must be > 0.\n", par); } else { assert(0); } return (int)result; } void add_to_iat(int_array_t *piat, int element) { if (piat -> n == piat -> size) { piat -> size = USE_IF_SET(piat -> size, 8) * 2; piat -> elements = (int *)myrealloc(piat -> elements, piat -> size * sizeof(int)); } (piat -> elements)[piat -> n] = element; (piat -> n)++; } int get_iat_size(int_array_t *piat) { return piat -> n; } void init_iat(int_array_t *piat) { piat -> elements = NULL; piat -> size = piat -> n = 0; } void free_iat(int_array_t *piat) { myfree(piat -> elements); init_iat(piat); } int get_iat_element(int_array_t *piat, int index) { assert(index < piat -> n); return (piat -> elements)[index]; } char *gethome(char *user) { struct passwd *pw; if (user) pw = getpwnam(user); else pw = getpwuid(getuid()); return mystrdup(pw->pw_dir); } char *myrealpath(char *in) { char *home, *pin; int home_len; char *pout; if (in[0] != '~') return mystrdup(in); if (in[1] != '/') { char *user; char *slash = strchr(in, '/'); int len; if (slash) { len = (int)((slash - in) - 1); pin = slash + 1; } else { len = strlen(in) - 1; pin = in + len + 1; } user = (char *)mymalloc(len + 1); memcpy(user, &in[1], len); user[len] = 0x00; home = gethome(user); myfree(user); } else { home = gethome(NULL); pin = in + 1; } if (!home) error_exit(FALSE, FALSE, "Cannot expand path %s:\nhome directory not found.\n", in); home_len = strlen(home); pout = (char *)mymalloc(home_len + strlen(in) + 1); sprintf(pout, "%s/%s", home, pin); myfree(home); return pout; } void str_add(char **to, const char *what, ...) { int len_to = *to ? strlen(*to) : 0; char buffer[4096] = { 0 }; int len_what = 0; va_list ap; /* FIXME: should vasprintf here: does other unixes have it? */ va_start(ap, what); len_what = vsnprintf(buffer, sizeof buffer, what, ap); va_end(ap); *to = (char *)realloc(*to, len_to + len_what + 1); memcpy(&(*to)[len_to], buffer, len_what + 1); } multitail-7.1.5/utils.h000066400000000000000000000042631465663511400150610ustar00rootroot00000000000000#define max(x, y) ((x)>(y)?(x):(y)) #define min(x, y) ((x)<(y)?(x):(y)) char *abbreviate_filesize(off64_t fsize); void add_to_iat(int_array_t *piat, int element); char * amount_to_str(long long int amount); void check_died_children(void); void compile_re(regex_t *whereto, char *what); char * convert_regexp_error(int error, const regex_t *preg); void delete_array(char **list, int n); void double_ts_to_str(double ts, char *format_str, char *dest, int dest_size); void duplicate_es_array(strip_t *pes_in, int n_esin, strip_t **pes_out, int *n_esout); void duplicate_re_array(re *pre_in, int n_rein, re **pre_out, int *n_reout); int file_exist(char *filename); int file_info(char *filename, off64_t *file_size, time_field_t tft, time_t *ts, mode_t *mode); int find_char_offset(char *str, char what); int find_editscheme(char *name); int find_filterscheme(char *name); char * find_most_recent_file(char *filespec, char *cur_file); char *find_next_par(char *start); int find_path_max(void); void free_iat(int_array_t *piat); void free_re(re *cur_re); char *gethome(char *user); int get_iat_element(int_array_t *piat, int index); int get_iat_size(int_array_t *piat); void get_load_values(double *v1, double *v2, double *v3); void get_now_ts(char *format_str, char *dest, int dest_size); double get_ts(void); char * getusername(void); int get_value_arg(char *par, char *string, valcheck_t check); int get_vmsize(pid_t pid); void grow_mem_if_needed(char **what, int *cur_len, int requested_len); void init_children_reaper(void); void init_iat(int_array_t *piat); int match_files(char *search_for, char **path, char ***found, char **isdir); int myclose(int fd); int mydup(int old_fd); int myopen(char *path, int mode); int myrand(int max); char *myrealpath(char *in); int open_null(void); struct passwd *getuserinfo(void); int READ(int fd, char *where_to, int max_len, char *for_whom); char * read_line_fd(int fd); void setup_for_childproc(int fd, char close_fd_0, char *term); char * shorten_filename(char *in, int max_len); void stop_process(pid_t pid); char * term_t_to_string(term_t term); ssize_t WRITE(int fd, char *whereto, size_t len, char *for_whom); char zerotomin(char c); void str_add(char **to, const char *what, ...); multitail-7.1.5/version000066400000000000000000000000161465663511400151500ustar00rootroot00000000000000VERSION=7.1.5 multitail-7.1.5/version.h000066400000000000000000000000401465663511400153730ustar00rootroot00000000000000extern const char *version_str;