├── .gitignore ├── .travis.yml ├── CMakeLists.txt ├── LICENSE ├── README.md ├── cmake ├── FindLibTermkey.cmake ├── FindLibUV.cmake ├── FindNcursesw.cmake └── FindPCRE.cmake ├── nav.1 ├── navrc └── src └── nav ├── ascii.h ├── cmd.c ├── cmd.h ├── cmdline.c ├── cmdline.h ├── compl.c ├── compl.h ├── config.c ├── config.h ├── event ├── event.c ├── event.h ├── file.c ├── file.h ├── fs.c ├── fs.h ├── ftw.c ├── ftw.h ├── hook.c ├── hook.h ├── input.c ├── input.h ├── process.c ├── process.h ├── rstream.c ├── rstream.h ├── shell.c ├── shell.h ├── stream.c ├── stream.h ├── uv_process.c ├── uv_process.h ├── wstream.c └── wstream.h ├── expand.c ├── expand.h ├── filter.c ├── filter.h ├── info.c ├── info.h ├── lib ├── bsearch.h ├── map.c ├── map.h ├── queue.h ├── sys_queue.h ├── utarray.h └── uthash.h ├── log.c ├── log.h ├── macros.h ├── model.c ├── model.h ├── nav.c ├── nav.h ├── option.c ├── option.h ├── plugins ├── dt │ ├── dt.c │ └── dt.h ├── ed │ ├── ed.c │ └── ed.h ├── fm │ ├── fm.c │ └── fm.h ├── img │ ├── img.c │ └── img.h ├── op │ ├── op.c │ └── op.h ├── out │ ├── out.c │ └── out.h ├── plugin.c ├── plugin.h └── term │ ├── term.c │ └── term.h ├── rbuffer.c ├── rbuffer.h ├── regex.c ├── regex.h ├── table.c ├── table.h ├── tui ├── buffer.c ├── buffer.h ├── ex_cmd.c ├── ex_cmd.h ├── history.c ├── history.h ├── layout.c ├── layout.h ├── menu.c ├── menu.h ├── message.c ├── message.h ├── overlay.c ├── overlay.h ├── screen.c ├── screen.h ├── select.c ├── select.h ├── window.c └── window.h ├── util.c ├── util.h └── vt ├── vt.c └── vt.h /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | bin/ 3 | build/ 4 | obj/ 5 | fifo 6 | 7 | *.so 8 | *.o 9 | 10 | dummy 11 | tags 12 | 13 | CMakeCache.txt 14 | CMakeFiles 15 | CMakeScripts 16 | Makefile 17 | cmake_install.cmake 18 | install_manifest.txt 19 | 20 | .ycm_extra_conf.py 21 | .ycm_extra_conf.pyc 22 | compile_commands.json 23 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | 3 | language: c 4 | 5 | compiler: 6 | - gcc 7 | - clang 8 | 9 | arch: 10 | packages: 11 | - cmake 12 | - ncurses 13 | - pcre 14 | - libtermkey 15 | - libuv 16 | script: 17 | - "cmake ." 18 | - "make" 19 | 20 | notifications: 21 | email: false 22 | 23 | script: 24 | - "curl -s https://raw.githubusercontent.com/mikkeloscar/arch-travis/master/arch-travis.sh | bash" 25 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.3.1) 2 | project(nav) 3 | set(CMAKE_C_FLAGS "-g") 4 | set(CMAKE_C_STANDARD 99) 5 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") 6 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wextra") 7 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-parameter") 8 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-sign-compare") 9 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-missing-field-initializers") 10 | add_definitions(-D_GNU_SOURCE) 11 | 12 | if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git) 13 | execute_process( 14 | COMMAND git describe --always 15 | OUTPUT_VARIABLE GIT_COMMIT_HASH 16 | OUTPUT_STRIP_TRAILING_WHITESPACE 17 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 18 | ) 19 | endif() 20 | add_definitions(-DNAV_VERSION=\"${GIT_COMMIT_HASH}\") 21 | 22 | string(TIMESTAMP CURRENT_DATE "%Y-%m-%d" UTC) 23 | add_definitions(-DNAV_DATE=\"${CURRENT_DATE}\") 24 | 25 | set( CMAKE_EXPORT_COMPILE_COMMANDS ON ) 26 | list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") 27 | 28 | include(CheckIncludeFiles) 29 | include(CheckFunctionExists) 30 | include(CheckSymbolExists) 31 | 32 | include_directories("${PROJECT_SOURCE_DIR}/src") 33 | 34 | file(GLOB_RECURSE SOURCES "${PROJECT_SOURCE_DIR}/src/nav/*.c") 35 | 36 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/build) 37 | 38 | add_executable(nav ${SOURCES}) 39 | 40 | set(CURSES_NEED_WIDE true) 41 | find_package(Ncursesw REQUIRED) 42 | find_package(PCRE REQUIRED) 43 | find_package(LibUV REQUIRED) 44 | find_package(LibTermkey REQUIRED) 45 | target_include_directories(nav PUBLIC ${CURSES_INCLUDE_PATH}) 46 | target_link_libraries(nav ${CURSES_LIBRARY}) 47 | target_link_libraries(nav ${PCRE_LIBRARY}) 48 | target_link_libraries(nav ${LIBUV_LIBRARIES}) 49 | target_link_libraries(nav ${LIBTERMKEY_LIBRARIES}) 50 | target_link_libraries(nav util) 51 | 52 | find_program(HAS_W3M w3m) 53 | if (HAS_W3M) 54 | add_definitions(-DW3M_SUPPORTED=1) 55 | endif() 56 | 57 | 58 | install( 59 | TARGETS nav 60 | RUNTIME 61 | DESTINATION bin 62 | COMPONENT runtime 63 | ) 64 | install( 65 | FILES ${PROJECT_SOURCE_DIR}/navrc 66 | DESTINATION /etc/nav/ 67 | COMPONENT configuration 68 | ) 69 | install( 70 | FILES ${PROJECT_SOURCE_DIR}/nav.1 71 | DESTINATION share/man/man1 72 | COMPONENT documentation 73 | ) 74 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any person obtaining a copy of 2 | this software and associated documentation files (the "Software"), to deal in 3 | the Software without restriction, including without limitation the rights to 4 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 5 | of the Software, and to permit persons to whom the Software is furnished to do 6 | so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all 9 | copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 14 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 17 | SOFTWARE. 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nav [![](https://api.travis-ci.org/jollywho/nav.svg)](https://travis-ci.org/jollywho/nav) 2 | nav is a hackable ncurses file manager, inspired by vim, [ranger](http://ranger.nongnu.org/), and [dvtm](http://www.brain-dump.org/projects/dvtm/). 3 | 4 | nav is a **work in progress**. 5 | 6 | ![](http://sicp.me/u/armtv.png) 7 | 8 | ----------------- 9 | - [Installation](#installation) 10 | - [Image Display](#image-display) 11 | - [Clipboard](#clipboard) 12 | - [Default Bindings](#default-bindings) 13 | - [Expansion Symbols](#expansion-symbols) 14 | - [Non Goals](#non-goals) 15 | - [Future](#future) 16 | - [Contributing](#contributing) 17 | 18 | ### What it has 19 | 20 | * filemanager buffers 21 | * w3mimgdisplay buffers 22 | * embedded dvtm terminal 23 | * async event-driven core 24 | * fast and simple metadata ([expansions](#expansion-symbols)) 25 | * simple script language (Navscript) 26 | * vim-style autocmds, marks, maps, syntax, colors 27 | * multi-selection: move, remove, copy 28 | * bulkrename 29 | * directory jumplist 30 | * file executor/opener with 'before/after' cmds. 31 | * strong [ctrlp](http://kien.github.io/ctrlp.vim/)-inspired autocomplete menu 32 | * menu hint keys 33 | * datatables (adhoc buffer content) 34 | * buffer signaling with :pipe 35 | 36 | ## Installation 37 | 38 | ### Compiling from Source 39 | 40 | Install dependencies: 41 | 42 | * cmake 43 | * ncurses 44 | * pcre 45 | * [libtermkey](http://www.leonerd.org.uk/code/libtermkey/) 46 | * [libuv](http://github.com/libuv/libuv) 47 | * [w3m](http://w3m.sourceforge.net/) (optional: image support) 48 | 49 | ```bash 50 | $ cmake . 51 | $ make 52 | $ sudo make install 53 | ``` 54 | 55 | ## Image Display 56 | 57 | With w3mimgdisplay installed 58 | ```viml 59 | :new fm 60 | :vnew img 61 | :pipe #BUFFER_ID 62 | ``` 63 | ```viml 64 | :vnew img #BUFFER_ID 65 | ``` 66 | 67 | ## Clipboard 68 | 69 | ``` 70 | :set copy-pipe #SELECT_PROGRAM 71 | ``` 72 | example: 73 | ``` 74 | :set copy-pipe xclip -i 75 | ``` 76 | 77 | ## Default Bindings 78 | 79 | See [Wiki](https://github.com/jollywho/nav/wiki#default-mappings) 80 | 81 | ## Expansion Symbols 82 | 83 | See [Wiki](https://github.com/jollywho/nav/wiki#expansion-symbols) 84 | 85 | ## Non Goals 86 | 87 | * text editor 88 | * Navscript as a full-featured language 89 | 90 | ## Future 91 | 92 | * plugins 93 | * dictionaries 94 | 95 | ## Contributing 96 | 97 | ...is much welcome. 98 | -------------------------------------------------------------------------------- /cmake/FindLibTermkey.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find libtermkey 2 | # Once done this will define 3 | # LIBTERMKEY_FOUND - System has libtermkey 4 | # LIBTERMKEY_INCLUDE_DIRS - The libtermkey include directories 5 | # LIBTERMKEY_LIBRARIES - The libraries needed to use libtermkey 6 | 7 | if(NOT LIBTERMKEY_USE_BUNDLED) 8 | find_package(PkgConfig) 9 | if (PKG_CONFIG_FOUND) 10 | pkg_check_modules(PC_LIBTERMKEY QUIET termkey) 11 | endif() 12 | else() 13 | set(PC_LIBTERMKEY_INCLUDEDIR) 14 | set(PC_LIBTERMKEY_INCLUDE_DIRS) 15 | set(PC_LIBTERMKEY_LIBDIR) 16 | set(PC_LIBTERMKEY_LIBRARY_DIRS) 17 | set(LIMIT_SEARCH NO_DEFAULT_PATH) 18 | endif() 19 | 20 | set(LIBTERMKEY_DEFINITIONS ${PC_LIBTERMKEY_CFLAGS_OTHER}) 21 | 22 | find_path(LIBTERMKEY_INCLUDE_DIR termkey.h 23 | PATHS ${PC_LIBTERMKEY_INCLUDEDIR} ${PC_LIBTERMKEY_INCLUDE_DIRS} 24 | ${LIMIT_SEARCH}) 25 | 26 | # If we're asked to use static linkage, add libuv.a as a preferred library name. 27 | if(LIBTERMKEY_USE_STATIC) 28 | list(APPEND LIBTERMKEY_NAMES 29 | "${CMAKE_STATIC_LIBRARY_PREFIX}termkey${CMAKE_STATIC_LIBRARY_SUFFIX}") 30 | endif() 31 | 32 | list(APPEND LIBTERMKEY_NAMES termkey) 33 | 34 | find_library(LIBTERMKEY_LIBRARY NAMES ${LIBTERMKEY_NAMES} 35 | HINTS ${PC_LIBTERMKEY_LIBDIR} ${PC_LIBTERMKEY_LIBRARY_DIRS} 36 | ${LIMIT_SEARCH}) 37 | 38 | set(LIBTERMKEY_LIBRARIES ${LIBTERMKEY_LIBRARY}) 39 | set(LIBTERMKEY_INCLUDE_DIRS ${LIBTERMKEY_INCLUDE_DIR}) 40 | 41 | include(FindPackageHandleStandardArgs) 42 | # handle the QUIETLY and REQUIRED arguments and set LIBTERMKEY_FOUND to TRUE 43 | # if all listed variables are TRUE 44 | find_package_handle_standard_args(LibTermkey DEFAULT_MSG 45 | LIBTERMKEY_LIBRARY LIBTERMKEY_INCLUDE_DIR) 46 | 47 | mark_as_advanced(LIBTERMKEY_INCLUDE_DIR LIBTERMKEY_LIBRARY) 48 | -------------------------------------------------------------------------------- /cmake/FindLibUV.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find libuv 2 | # Once done, this will define 3 | # 4 | # LIBUV_FOUND - system has libuv 5 | # LIBUV_INCLUDE_DIRS - the libuv include directories 6 | # LIBUV_LIBRARIES - link these to use libuv 7 | # 8 | # Set the LIBUV_USE_STATIC variable to specify if static libraries should 9 | # be preferred to shared ones. 10 | 11 | if(NOT LIBUV_USE_BUNDLED) 12 | find_package(PkgConfig) 13 | if (PKG_CONFIG_FOUND) 14 | pkg_check_modules(PC_LIBUV QUIET libuv) 15 | endif() 16 | else() 17 | set(PC_LIBUV_INCLUDEDIR) 18 | set(PC_LIBUV_INCLUDE_DIRS) 19 | set(PC_LIBUV_LIBDIR) 20 | set(PC_LIBUV_LIBRARY_DIRS) 21 | set(LIMIT_SEARCH NO_DEFAULT_PATH) 22 | endif() 23 | 24 | find_path(LIBUV_INCLUDE_DIR uv.h 25 | HINTS ${PC_LIBUV_INCLUDEDIR} ${PC_LIBUV_INCLUDE_DIRS} 26 | ${LIMIT_SEARCH}) 27 | 28 | # If we're asked to use static linkage, add libuv.a as a preferred library name. 29 | if(LIBUV_USE_STATIC) 30 | list(APPEND LIBUV_NAMES 31 | "${CMAKE_STATIC_LIBRARY_PREFIX}uv${CMAKE_STATIC_LIBRARY_SUFFIX}") 32 | endif(LIBUV_USE_STATIC) 33 | 34 | list(APPEND LIBUV_NAMES uv) 35 | 36 | find_library(LIBUV_LIBRARY NAMES ${LIBUV_NAMES} 37 | HINTS ${PC_LIBUV_LIBDIR} ${PC_LIBUV_LIBRARY_DIRS} 38 | ${LIMIT_SEARCH}) 39 | 40 | mark_as_advanced(LIBUV_INCLUDE_DIR LIBUV_LIBRARY) 41 | 42 | if(PC_LIBUV_LIBRARIES) 43 | list(REMOVE_ITEM PC_LIBUV_LIBRARIES uv) 44 | endif() 45 | 46 | set(LIBUV_LIBRARIES ${LIBUV_LIBRARY} ${PC_LIBUV_LIBRARIES}) 47 | set(LIBUV_INCLUDE_DIRS ${LIBUV_INCLUDE_DIR}) 48 | 49 | # Deal with the fact that libuv.pc is missing important dependency information. 50 | 51 | include(CheckLibraryExists) 52 | 53 | check_library_exists(dl dlopen "dlfcn.h" HAVE_LIBDL) 54 | if(HAVE_LIBDL) 55 | list(APPEND LIBUV_LIBRARIES dl) 56 | endif() 57 | 58 | check_library_exists(kstat kstat_lookup "kstat.h" HAVE_LIBKSTAT) 59 | if(HAVE_LIBKSTAT) 60 | list(APPEND LIBUV_LIBRARIES kstat) 61 | endif() 62 | 63 | check_library_exists(kvm kvm_open "kvm.h" HAVE_LIBKVM) 64 | if(HAVE_LIBKVM) 65 | list(APPEND LIBUV_LIBRARIES kvm) 66 | endif() 67 | 68 | check_library_exists(nsl gethostbyname "nsl.h" HAVE_LIBNSL) 69 | if(HAVE_LIBNSL) 70 | list(APPEND LIBUV_LIBRARIES nsl) 71 | endif() 72 | 73 | check_library_exists(perfstat perfstat_cpu "libperfstat.h" HAVE_LIBPERFSTAT) 74 | if(HAVE_LIBPERFSTAT) 75 | list(APPEND LIBUV_LIBRARIES perfstat) 76 | endif() 77 | 78 | check_library_exists(rt clock_gettime "time.h" HAVE_LIBRT) 79 | if(HAVE_LIBRT) 80 | list(APPEND LIBUV_LIBRARIES rt) 81 | endif() 82 | 83 | check_library_exists(sendfile sendfile "" HAVE_LIBSENDFILE) 84 | if(HAVE_LIBSENDFILE) 85 | list(APPEND LIBUV_LIBRARIES sendfile) 86 | endif() 87 | 88 | include(FindPackageHandleStandardArgs) 89 | 90 | # handle the QUIETLY and REQUIRED arguments and set LIBUV_FOUND to TRUE 91 | # if all listed variables are TRUE 92 | find_package_handle_standard_args(LibUV DEFAULT_MSG 93 | LIBUV_LIBRARY LIBUV_INCLUDE_DIR) 94 | 95 | mark_as_advanced(LIBUV_INCLUDE_DIR LIBUV_LIBRARY) 96 | -------------------------------------------------------------------------------- /cmake/FindNcursesw.cmake: -------------------------------------------------------------------------------- 1 | #.rst: 2 | # FindNcursesw 3 | # ------------ 4 | # 5 | # Find the ncursesw (wide ncurses) include file and library. 6 | # 7 | # Based on FindCurses.cmake which comes with CMake. 8 | # 9 | # Checks for ncursesw first. If not found, it then executes the 10 | # regular old FindCurses.cmake to look for for ncurses (or curses). 11 | # 12 | # 13 | # Result Variables 14 | # ^^^^^^^^^^^^^^^^ 15 | # 16 | # This module defines the following variables: 17 | # 18 | # ``CURSES_FOUND`` 19 | # True if curses is found. 20 | # ``NCURSESW_FOUND`` 21 | # True if ncursesw is found. 22 | # ``CURSES_INCLUDE_DIRS`` 23 | # The include directories needed to use Curses. 24 | # ``CURSES_LIBRARIES`` 25 | # The libraries needed to use Curses. 26 | # ``CURSES_HAVE_CURSES_H`` 27 | # True if curses.h is available. 28 | # ``CURSES_HAVE_NCURSES_H`` 29 | # True if ncurses.h is available. 30 | # ``CURSES_HAVE_NCURSES_NCURSES_H`` 31 | # True if ``ncurses/ncurses.h`` is available. 32 | # ``CURSES_HAVE_NCURSES_CURSES_H`` 33 | # True if ``ncurses/curses.h`` is available. 34 | # ``CURSES_HAVE_NCURSESW_NCURSES_H`` 35 | # True if ``ncursesw/ncurses.h`` is available. 36 | # ``CURSES_HAVE_NCURSESW_CURSES_H`` 37 | # True if ``ncursesw/curses.h`` is available. 38 | # 39 | # Set ``CURSES_NEED_NCURSES`` to ``TRUE`` before the 40 | # ``find_package(Ncursesw)`` call if NCurses functionality is required. 41 | # 42 | #============================================================================= 43 | # Copyright 2001-2014 Kitware, Inc. 44 | # modifications: Copyright 2015 kahrl 45 | # 46 | # Redistribution and use in source and binary forms, with or without 47 | # modification, are permitted provided that the following conditions 48 | # are met: 49 | # 50 | # * Redistributions of source code must retain the above copyright 51 | # notice, this list of conditions and the following disclaimer. 52 | # 53 | # * Redistributions in binary form must reproduce the above copyright 54 | # notice, this list of conditions and the following disclaimer in the 55 | # documentation and/or other materials provided with the distribution. 56 | # 57 | # * Neither the names of Kitware, Inc., the Insight Software Consortium, 58 | # nor the names of their contributors may be used to endorse or promote 59 | # products derived from this software without specific prior written 60 | # permission. 61 | # 62 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 63 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 64 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 65 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 66 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 67 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 68 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 69 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 70 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 71 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 72 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 73 | # 74 | # ------------------------------------------------------------------------------ 75 | # 76 | # The above copyright and license notice applies to distributions of 77 | # CMake in source and binary form. Some source files contain additional 78 | # notices of original copyright by their contributors; see each source 79 | # for details. Third-party software packages supplied with CMake under 80 | # compatible licenses provide their own copyright notices documented in 81 | # corresponding subdirectories. 82 | # 83 | # ------------------------------------------------------------------------------ 84 | # 85 | # CMake was initially developed by Kitware with the following sponsorship: 86 | # 87 | # * National Library of Medicine at the National Institutes of Health 88 | # as part of the Insight Segmentation and Registration Toolkit (ITK). 89 | # 90 | # * US National Labs (Los Alamos, Livermore, Sandia) ASC Parallel 91 | # Visualization Initiative. 92 | # 93 | # * National Alliance for Medical Image Computing (NAMIC) is funded by the 94 | # National Institutes of Health through the NIH Roadmap for Medical Research, 95 | # Grant U54 EB005149. 96 | # 97 | # * Kitware, Inc. 98 | #============================================================================= 99 | 100 | include(CheckLibraryExists) 101 | 102 | find_library(CURSES_NCURSESW_LIBRARY NAMES ncursesw 103 | DOC "Path to libncursesw.so or .lib or .a") 104 | 105 | set(CURSES_USE_NCURSES FALSE) 106 | set(CURSES_USE_NCURSESW FALSE) 107 | 108 | if(CURSES_NCURSESW_LIBRARY) 109 | set(CURSES_USE_NCURSES TRUE) 110 | set(CURSES_USE_NCURSESW TRUE) 111 | endif() 112 | 113 | if(CURSES_USE_NCURSESW) 114 | get_filename_component(_cursesLibDir "${CURSES_NCURSESW_LIBRARY}" PATH) 115 | get_filename_component(_cursesParentDir "${_cursesLibDir}" PATH) 116 | 117 | find_path(CURSES_INCLUDE_PATH 118 | NAMES ncursesw/ncurses.h ncursesw/curses.h 119 | HINTS "${_cursesParentDir}/include" 120 | ) 121 | 122 | # Previous versions of FindCurses provided these values. 123 | if(NOT DEFINED CURSES_LIBRARY) 124 | set(CURSES_LIBRARY "${CURSES_NCURSESW_LIBRARY}") 125 | endif() 126 | 127 | CHECK_LIBRARY_EXISTS("${CURSES_NCURSESW_LIBRARY}" 128 | cbreak "" CURSES_NCURSESW_HAS_CBREAK) 129 | if(NOT CURSES_NCURSESW_HAS_CBREAK) 130 | find_library(CURSES_EXTRA_LIBRARY tinfo HINTS "${_cursesLibDir}" 131 | DOC "Path to libtinfo.so or .lib or .a") 132 | find_library(CURSES_EXTRA_LIBRARY tinfo ) 133 | endif() 134 | 135 | # Report whether each possible header name exists in the include directory. 136 | if(NOT DEFINED CURSES_HAVE_NCURSESW_NCURSES_H) 137 | if(EXISTS "${CURSES_INCLUDE_PATH}/ncursesw/ncurses.h") 138 | set(CURSES_HAVE_NCURSESW_NCURSES_H "${CURSES_INCLUDE_PATH}/ncursesw/ncurses.h") 139 | else() 140 | set(CURSES_HAVE_NCURSESW_NCURSES_H "CURSES_HAVE_NCURSESW_NCURSES_H-NOTFOUND") 141 | endif() 142 | endif() 143 | if(NOT DEFINED CURSES_HAVE_NCURSESW_CURSES_H) 144 | if(EXISTS "${CURSES_INCLUDE_PATH}/ncursesw/curses.h") 145 | set(CURSES_HAVE_NCURSESW_CURSES_H "${CURSES_INCLUDE_PATH}/ncursesw/curses.h") 146 | else() 147 | set(CURSES_HAVE_NCURSESW_CURSES_H "CURSES_HAVE_NCURSESW_CURSES_H-NOTFOUND") 148 | endif() 149 | endif() 150 | if(NOT DEFINED CURSES_HAVE_NCURSES_H) 151 | if(EXISTS "${CURSES_INCLUDE_PATH}/ncurses.h") 152 | set(CURSES_HAVE_NCURSES_H "${CURSES_INCLUDE_PATH}/ncurses.h") 153 | else() 154 | set(CURSES_HAVE_NCURSES_H "CURSES_HAVE_NCURSES_H-NOTFOUND") 155 | endif() 156 | endif() 157 | if(NOT DEFINED CURSES_HAVE_CURSES_H) 158 | if(EXISTS "${CURSES_INCLUDE_PATH}/curses.h") 159 | set(CURSES_HAVE_CURSES_H "${CURSES_INCLUDE_PATH}/curses.h") 160 | else() 161 | set(CURSES_HAVE_CURSES_H "CURSES_HAVE_CURSES_H-NOTFOUND") 162 | endif() 163 | endif() 164 | 165 | 166 | find_library(CURSES_FORM_LIBRARY form HINTS "${_cursesLibDir}" 167 | DOC "Path to libform.so or .lib or .a") 168 | find_library(CURSES_FORM_LIBRARY form ) 169 | 170 | # Need to provide the *_LIBRARIES 171 | set(CURSES_LIBRARIES ${CURSES_LIBRARY}) 172 | 173 | if(CURSES_EXTRA_LIBRARY) 174 | set(CURSES_LIBRARIES ${CURSES_LIBRARIES} ${CURSES_EXTRA_LIBRARY}) 175 | endif() 176 | 177 | if(CURSES_FORM_LIBRARY) 178 | set(CURSES_LIBRARIES ${CURSES_LIBRARIES} ${CURSES_FORM_LIBRARY}) 179 | endif() 180 | 181 | # Provide the *_INCLUDE_DIRS result. 182 | if (DEFINED CURSES_HAVE_NCURSESW_NCURSES_H) 183 | set(CURSES_INCLUDE_PATH "${CURSES_INCLUDE_PATH}/ncursesw") 184 | endif() 185 | set(CURSES_INCLUDE_DIRS ${CURSES_INCLUDE_PATH}) 186 | set(CURSES_INCLUDE_DIR ${CURSES_INCLUDE_PATH}) # compatibility 187 | 188 | # handle the QUIETLY and REQUIRED arguments and set CURSES_FOUND to TRUE if 189 | # all listed variables are TRUE 190 | include(FindPackageHandleStandardArgs) 191 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(Ncursesw DEFAULT_MSG 192 | CURSES_LIBRARY CURSES_INCLUDE_PATH) 193 | set(CURSES_FOUND ${NCURSESW_FOUND}) 194 | 195 | else() 196 | find_package(Curses) 197 | set(NCURSESW_FOUND FALSE) 198 | endif() 199 | 200 | mark_as_advanced( 201 | CURSES_INCLUDE_PATH 202 | CURSES_CURSES_LIBRARY 203 | CURSES_NCURSES_LIBRARY 204 | CURSES_NCURSESW_LIBRARY 205 | CURSES_EXTRA_LIBRARY 206 | CURSES_FORM_LIBRARY 207 | ) 208 | -------------------------------------------------------------------------------- /cmake/FindPCRE.cmake: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2007-2009 LuaDist. 2 | # Created by Peter Kapec 3 | # Redistribution and use of this file is allowed according to the terms of the MIT license. 4 | # For details see the COPYRIGHT file distributed with LuaDist. 5 | # Note: 6 | # Searching headers and libraries is very simple and is NOT as powerful as scripts 7 | # distributed with CMake, because LuaDist defines directories to search for. 8 | # Everyone is encouraged to contact the author with improvements. Maybe this file 9 | # becomes part of CMake distribution sometimes. 10 | 11 | # - Find pcre 12 | # Find the native PCRE headers and libraries. 13 | # 14 | # PCRE_INCLUDE_DIRS - where to find pcre.h, etc. 15 | # PCRE_LIBRARIES - List of libraries when using pcre. 16 | # PCRE_FOUND - True if pcre found. 17 | 18 | # Look for the header file. 19 | FIND_PATH(PCRE_INCLUDE_DIR NAMES pcre.h) 20 | 21 | # Look for the library. 22 | FIND_LIBRARY(PCRE_LIBRARY NAMES pcre) 23 | 24 | # Handle the QUIETLY and REQUIRED arguments and set PCRE_FOUND to TRUE if all listed variables are TRUE. 25 | INCLUDE(FindPackageHandleStandardArgs) 26 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(PCRE DEFAULT_MSG PCRE_LIBRARY PCRE_INCLUDE_DIR) 27 | 28 | # Copy the results to the output variables. 29 | IF(PCRE_FOUND) 30 | SET(PCRE_LIBRARIES ${PCRE_LIBRARY}) 31 | SET(PCRE_INCLUDE_DIRS ${PCRE_INCLUDE_DIR}) 32 | ELSE(PCRE_FOUND) 33 | SET(PCRE_LIBRARIES) 34 | SET(PCRE_INCLUDE_DIRS) 35 | ENDIF(PCRE_FOUND) 36 | 37 | MARK_AS_ADVANCED(PCRE_INCLUDE_DIRS PCRE_LIBRARIES) 38 | -------------------------------------------------------------------------------- /nav.1: -------------------------------------------------------------------------------- 1 | .TH NAV 1 2 | .SH NAME 3 | nav \- a highly configurable, performant, async system tool 4 | .SH SYNOPSIS 5 | .IX Header "SYNOPSIS" 6 | \&\fBnav\fR [\fB\-\-version\fR] [\fB\-\-help\fR] [\fB\-\-debug\fR] 7 | .TP 8 | \&\fBnav\fR [\fB\-\-config\fR=\fIpath\fR] [\fB\-\-log-file\fR=\fIpath\fR] [\fB\-\-verbose\fR=\fIgroup\fR] 9 | .SH DESCRIPTION 10 | nav is a hackable ncurses file manager, inspired by vim, ranger, and dvtm. 11 | 12 | nav searches for a config file in the following order: 13 | \fB~/.navrc\fP 14 | \fB~/.nav/.navrc\fP 15 | \fB/etc/navrc\fP 16 | 17 | .SH OPTIONS 18 | .IP "\fB\-c\fR \fIpath\fR, \fB\-\-config\fR=\fIpath\fR" 14 19 | .IX Item "--config=path" 20 | Specify an alternative config file. 21 | .IP "\fB\-l\fR \fIpath\fR, \fB\-\-log-file\fR=\fIpath\fR" 14 22 | .IX Item "-l, --log-file" 23 | Specify an alternative log file 24 | (Sets \fB--debug\fP). 25 | .IP "\fB\-V\fR \fIgroup\fR, \fB\-\-verbose\fR=\fIgroup\fR" 14 26 | Verbose mode for a specific log group 27 | (Sets \fB--debug\fP). 28 | .IX Item "-V, --verbose" 29 | .IP "\fB\-d\fR, \fB\-\-debug\fR" 14 30 | .IX Item "--debug" 31 | Debug mode. 32 | .IP "\fB\-v\fR, \fB\-\-version\fR" 14 33 | .IX Item "--version" 34 | Print version information and exit. 35 | .IP "\fB\-h\fR, \fB\-\-help\fR" 14 36 | .IX Item "-h, --help" 37 | Print this help message and exit. 38 | 39 | .SH DEFAULT KEYS 40 | .SS "\s-1Buffer\s0" 41 | .IP "\fBh\fR" 42 | go left 43 | .IP "\fBj\fR" 44 | go down 45 | .IP "\fBk\fR" 46 | go up 47 | .IP "\fBl\fR" 48 | go right 49 | .IP "\fBC-j\fR" 50 | jump page down 51 | .IP "\fBC-k\fR" 52 | jump page up 53 | .IP "\fBn\fR" 54 | next search result 55 | .IP "\fBN\fR" 56 | previous search result 57 | .IP "\fBC-o\fR" 58 | jumplist cycle backward 59 | .IP "\fBC-i\fR" 60 | jumplist cycle forward 61 | .IP "\fBf\fR" 62 | toggle filter 63 | .IP "\fBg\fR" 64 | g operator 65 | .IP "\fBG\fR" 66 | goto bottom 67 | .IP "\fBV\fR" 68 | toggle visual select 69 | .IP "\fBy\fR" 70 | yank operator 71 | .IP "\fBd\fR" 72 | cut operator 73 | .IP "\fBm\fR" 74 | mark operator 75 | .IP "\fB’\fR" 76 | jump operator 77 | .IP "\fBp\fR" 78 | paste 79 | .IP "\fBX\fR" 80 | remove 81 | 82 | .RE 83 | .SS "\s-1Window\s0" 84 | .IP "\fBH\fR" 85 | move window focus left 86 | .IP "\fBJ\fR" 87 | move window focus down 88 | .IP "\fBK\fR" 89 | move window focus up 90 | .IP "\fBL\fR" 91 | move window focus down 92 | .IP "\fB:\fR" 93 | start command-line mode 94 | .IP "\fB/\fR" 95 | forward search 96 | .IP "\fB?\fR" 97 | backward search 98 | 99 | .RE 100 | .SS "\s-1Term\s0" 101 | .IP "\fBMeta-[\fR" 102 | escape terminal focus 103 | .IP "\fBMeta-k\fR" 104 | scroll buffer up 105 | .IP "\fBMeta-j\fR" 106 | scroll buffer down 107 | 108 | .RE 109 | .SS "\s-1Command-line\s0" 110 | .IP "\fBESC\fR" 111 | cancel/close ex_mode 112 | .IP "\fBTAB\fR" 113 | compl cycle forward 114 | .IP "\fBS-TAB\fR" 115 | compl cycle backward 116 | .IP "\fBCAR\fR" 117 | run cmd 118 | .IP "\fBBS\fR" 119 | (internal) 120 | .IP "\fBSpc\fR" 121 | (internal) 122 | .IP "\fBC-p\fR" 123 | history cycle backward 124 | .IP "\fBC-n\fR" 125 | history cycle forward 126 | .IP "\fBC-w\fR" 127 | delete word 128 | .IP "\fBC-b\fR" 129 | move cursor back word 130 | .IP "\fBC-e\fR" 131 | move cursor forward word 132 | .IP "\fBC-s\fR" 133 | move cursor start of line 134 | .IP "\fBC-f\fR" 135 | move cursor end of line 136 | .IP "\fBC-u\fR" 137 | delete line 138 | .IP "\fBC-l\fR" 139 | add invert symbol to cmd 140 | .IP "\fBC-g\fR" 141 | hint mode 142 | .IP "\fBC-j\fR" 143 | go down in menu 144 | .IP "\fBC-k\fR" 145 | go up in menu 146 | 147 | .RE 148 | .SS "\s-1Operator\s0" 149 | .IP "\fBm\fR {\fIa-zA-Z0-9\fR}" 150 | set mark {\fIa-zA-Z0-9\fR} for path. 151 | .IP "\fB’\fR {\fIa-zA-Z0-9\fR}" 152 | jump to the mark {\fIa-zA-Z0-9\fR} 153 | .IP "\fBC-w\fR {\fBHJKL\fR}" 154 | swap buffer in direction 155 | .IP "\fByy\fR 156 | yank visible field 157 | .IP "\fByf\fR 158 | yank fullpath 159 | .IP "\fByn\fR 160 | yank name 161 | .IP "\fByN\fR 162 | yank name (no extension) 163 | .IP "\fBye\fR" 164 | yank extension 165 | .IP "\fByd\fR" 166 | yank directory 167 | .IP "\fByt\fR" 168 | yank type (opgroup) 169 | .IP "\fByk\fR" 170 | yank kind (file type) 171 | .IP "\fBdd\fR" 172 | yank to delete register 173 | 174 | .RE 175 | .SS "\s-1Symbol Expansion\s0" 176 | .IP "\fB%b\fR" 177 | buffer number 178 | .IP "\fB%B\fR" 179 | buffer name 180 | .IP "\fB%f\fR" 181 | fullpath 182 | .IP "\fB%n\fR" 183 | name 184 | .IP "\fB%N\fR" 185 | name (no extension) 186 | .IP "\fB%e\fR" 187 | extension 188 | .IP "\fB%d\fR" 189 | directory 190 | .IP "\fB%t\fR" 191 | type (opgroup) 192 | .IP "\fB%k\fR" 193 | kind (file type) 194 | .IP "\fB%:%\fR" 195 | nav pid 196 | .IP "\fB%:!\fR" 197 | the pid of the last !cmd 198 | .IP "\fB%:?\fR" 199 | the exit status of the last !cmd 200 | .IP "\fB%_:\fR" 201 | selection field value 202 | .IP "\fB%o:\fR" 203 | opgroup variable 204 | 205 | .SH FILES 206 | User-local info file: \fI~/.navinfo\fR. 207 | .TP 208 | Global configuration file: \fI/etc/navrc\fR. 209 | .SH BUGS 210 | If you find a bug, please report it at 211 | .br 212 | <\fBhttp://github.com/jollywho/nav\fP>. 213 | .SH AUTHORS 214 | Kevin Vollmer 215 | -------------------------------------------------------------------------------- /navrc: -------------------------------------------------------------------------------- 1 | # ======================================== 2 | # nav syntax groups 3 | # ======================================== 4 | syn Video [mkv,avi,ogm,mp4,webm,div,flv,wmv,part,m4v] 5 | syn Music [flac,mp3,aac,m4a,ogg,wav,wma,m3u,opus] 6 | syn Image [jpg,jpeg,png,gif,bmp,tif] 7 | syn Archive [rar,zip,gz,7z,deb,dmg,xar,msi,pkg,bz,bz2,cab] 8 | syn Document [pdf,djvu,doc,rtf,xls,xslx,epub] 9 | 10 | # ======================================== 11 | # nav colors 12 | # ======================================== 13 | hi BufText 8 -1 14 | hi BufSelActive 178 1 15 | hi BufSelInactive 131 233 16 | hi BufDir 15 -1 17 | hi BufSz 14 -1 18 | hi BufStdout 8 -1 19 | hi BufStderr 1 -1 20 | hi MsgError 0 9 21 | hi MsgAsk 3 -1 22 | 23 | hi ComplSelected 16 3 24 | hi ComplText 7 -1 25 | hi ComplParam 9 0 26 | hi OverlaySep 8 -1 27 | hi OverlayLine 7 0 28 | hi OverlayBufNo 232 8 29 | hi OverlayActive 16 4 30 | hi OverlayArgs 195 235 31 | hi OverlayInactive 247 235 32 | hi OverlayTextInactive 247 0 33 | hi OverlayProgress 235 5 34 | hi OverlayFilter 7 13 35 | 36 | hi Video 8 -1 37 | hi Image 23 -1 38 | hi Archive 2 -1 39 | hi Music 3 -1 40 | hi Document 139 -1 41 | 42 | set sepchar = ╬ 43 | 44 | # ======================================== 45 | # file associations 46 | # ======================================== 47 | fu! op_before_vid() 48 | ret mpv %f 49 | en 50 | 51 | fu! op_before_mu() 52 | ret mpv %f 53 | en 54 | 55 | op Video "op_before_vid()" 56 | op Music "op_before_mu()" 57 | op Image "sxiv -m %f" 58 | op Document "mupdf %f" 59 | 60 | # ======================================== 61 | # nav keymaps 62 | # ======================================== 63 | map s :sort 64 | map S :sort 65 | map R :reload 66 | 67 | # ======================================== 68 | # auto 69 | # ======================================== 70 | new fm 71 | -------------------------------------------------------------------------------- /src/nav/ascii.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_ASCII_H 2 | #define NV_ASCII_H 3 | 4 | #include 5 | 6 | #define CharOrd(x) ((x) < 'a' ? (x) - 'A' : (x) - 'a') 7 | #define CharOrdLow(x) ((x) - 'a') 8 | #define CharOrdUp(x) ((x) - 'A') 9 | #define ROT13(c, a) (((((c) - (a)) + 13) % 26) + (a)) 10 | 11 | #define NUL '\000' 12 | #define BELL '\007' 13 | #define BS '\010' 14 | #define TAB '\011' 15 | #define NL '\012' 16 | #define NL_STR (char_u *)"\012" 17 | #define FF '\014' 18 | #define CAR '\015' /* CR is used by Mac OS X */ 19 | #define ESC '\033' 20 | #define ESC_STR (char_u *)"\033" 21 | #define ESC_STR_nc "\033" 22 | #define DEL 0x7f 23 | #define DEL_STR (char_u *)"\177" 24 | #define CSI 0x9b /* Control Sequence Introducer */ 25 | #define CSI_STR "\233" 26 | #define DCS 0x90 /* Device Control String */ 27 | #define STERM 0x9c /* String Terminator */ 28 | 29 | #define POUND 0xA3 30 | 31 | #define Ctrl_chr(x) (TOUPPER_ASC(x) ^ 0x40) /* '?' -> DEL, '@' -> ^@, etc. */ 32 | #define Meta(x) ((x) | 0x80) 33 | 34 | #define CTRL_F_STR "\006" 35 | #define CTRL_H_STR "\010" 36 | #define CTRL_V_STR "\026" 37 | 38 | #define Ctrl_AT 0 /* @ */ 39 | #define Ctrl_A 1 40 | #define Ctrl_B 2 41 | #define Ctrl_C 3 42 | #define Ctrl_D 4 43 | #define Ctrl_E 5 44 | #define Ctrl_F 6 45 | #define Ctrl_G 7 46 | #define Ctrl_H 8 47 | #define Ctrl_I 9 48 | #define Ctrl_J 10 49 | #define Ctrl_K 11 50 | #define Ctrl_L 12 51 | #define Ctrl_M 13 52 | #define Ctrl_N 14 53 | #define Ctrl_O 15 54 | #define Ctrl_P 16 55 | #define Ctrl_Q 17 56 | #define Ctrl_R 18 57 | #define Ctrl_S 19 58 | #define Ctrl_T 20 59 | #define Ctrl_U 21 60 | #define Ctrl_V 22 61 | #define Ctrl_W 23 62 | #define Ctrl_X 24 63 | #define Ctrl_Y 25 64 | #define Ctrl_Z 26 65 | /* CTRL- [ Left Square Bracket == ESC*/ 66 | #define Ctrl_BSL 28 /* \ BackSLash */ 67 | #define Ctrl_RSB 29 /* ] Right Square Bracket */ 68 | #define Ctrl_HAT 30 /* ^ */ 69 | #define Ctrl__ 31 70 | 71 | 72 | #define FORWARD 1 73 | #define BACKWARD (-1) 74 | 75 | #define NCH 0x01 /* need second char */ 76 | #define NCH_S (0x02|NCH) /* NCH is special */ 77 | #define NCH_A (0x04|NCH) /* NCH is any char */ 78 | 79 | /* Bits for modifier mask */ 80 | /* 0x01 cannot be used, because the modifier must be 0x02 or higher */ 81 | #define MOD_MASK_SHIFT 0x02 82 | #define MOD_MASK_CTRL 0x04 83 | #define MOD_MASK_ALT 0x08 /* aka META */ 84 | #define MOD_MASK_META 0x10 /* META when it's different from ALT */ 85 | 86 | #define KEY2TERMCAP0(x) ((-(x)) & 0xff) 87 | #define KEY2TERMCAP1(x) (((unsigned)(-(x)) >> 8) & 0xff) 88 | #define TERMCAP2KEY(a, b) (-((a) + ((int)(b) << 8))) 89 | #define IS_SPECIAL(c) ((c) < 0) 90 | 91 | #define KS_EXTRA 253 92 | #define KS_ZERO 255 93 | #define KE_FILLER ('X') 94 | #define K_S_TAB TERMCAP2KEY('k', 'B') 95 | #define K_ZERO TERMCAP2KEY(KS_ZERO, KE_FILLER) 96 | #define HC_S_TAB ("S-Tab") 97 | 98 | #define K_SPECIAL (0x80) 99 | #define K_UP TERMCAP2KEY('k', 'u') 100 | #define K_DOWN TERMCAP2KEY('k', 'd') 101 | #define K_LEFT TERMCAP2KEY('k', 'l') 102 | #define K_RIGHT TERMCAP2KEY('k', 'r') 103 | #define K_INS TERMCAP2KEY('k', 'I') 104 | #define K_DEL TERMCAP2KEY('k', 'D') 105 | #define K_HOME TERMCAP2KEY('k', 'h') 106 | #define K_END TERMCAP2KEY('@', '7') 107 | #define K_PAGEUP TERMCAP2KEY('k', 'P') 108 | #define K_PAGEDOWN TERMCAP2KEY('k', 'N') 109 | 110 | # define ASCII_ISLOWER(c) ((unsigned)(c) >= 'a' && (unsigned)(c) <= 'z') 111 | # define ASCII_ISUPPER(c) ((unsigned)(c) >= 'A' && (unsigned)(c) <= 'Z') 112 | # define ASCII_ISALPHA(c) (ASCII_ISUPPER(c) || ASCII_ISLOWER(c)) 113 | # define ASCII_ISALNUM(c) (ASCII_ISALPHA(c) || ascii_isdigit(c)) 114 | 115 | /// Check whether character is a decimal digit. 116 | /// 117 | /// Library isdigit() function is officially locale-dependent and, for 118 | /// example, returns true for superscript 1 (¹) in locales where encoding 119 | /// contains it in lower 8 bits. Also avoids crashes in case c is below 120 | /// 0 or above 255: library functions are officially defined as accepting 121 | /// only EOF and unsigned char values (otherwise it is undefined behaviour) 122 | /// what may be used for some optimizations (e.g. simple `return 123 | /// isdigit_table[c];`). 124 | static inline bool ascii_isdigit(int c) 125 | { 126 | return c >= '0' && c <= '9'; 127 | } 128 | 129 | #endif 130 | -------------------------------------------------------------------------------- /src/nav/cmd.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_CMD_H 2 | #define NV_CMD_H 3 | 4 | #include "nav/lib/uthash.h" 5 | #include "nav/cmdline.h" 6 | #include "nav/option.h" 7 | 8 | #define OUTPUT 0x01 9 | #define BUFFER 0x02 10 | #define RET_INT 0x04 11 | #define PLUGIN 0x32 12 | #define STRING 0x64 13 | #define NORET (Cmdret){} 14 | 15 | typedef struct Cmd_T Cmd_T; 16 | typedef struct Cmdarg Cmdarg; 17 | typedef Cmdret (*Cmd_Func_T)(List *, Cmdarg *); 18 | 19 | struct Cmd_T { 20 | char *name; /* cmd name */ 21 | char *alt; /* alt cmd name */ 22 | char *msg; 23 | Cmd_Func_T cmd_func; 24 | int flags; /* cmd flags */ 25 | int bflags; /* builtin flags */ 26 | }; 27 | 28 | struct Cmdarg { 29 | int flags; /* cmd flags */ 30 | int bflag; /* builtin flags */ 31 | Cmdstr *cmdstr; 32 | Cmdline *cmdline; 33 | }; 34 | 35 | void cmd_init(); 36 | void cmd_sort_cmds(); 37 | void cmd_cleanup(); 38 | void cmd_add(Cmd_T *); 39 | void cmd_remove(const char *); 40 | void cmd_clearall(); 41 | void cmd_flush(); 42 | void cmd_run(Cmdstr *cmdstr, Cmdline *cmdline); 43 | Cmd_T* cmd_find(const char *); 44 | void cmd_setline(int); 45 | void cmd_eval(Cmdstr *caller, char *line); 46 | nv_block* cmd_callstack(); 47 | void cmd_load(const char *); 48 | void cmd_unload(bool); 49 | void cmd_pushfile(ConfFile *); 50 | void cmd_popfile(); 51 | ConfFile* cmd_curfile(); 52 | 53 | bool cmd_conditional(); 54 | void cmd_list(); 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /src/nav/cmdline.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_CMDLINE_H 2 | #define NV_CMDLINE_H 3 | 4 | #include "nav/plugins/plugin.h" 5 | #include "nav/lib/queue.h" 6 | #include "nav/lib/utarray.h" 7 | 8 | typedef struct { 9 | char v_type; /* see below: VAR_NUMBER, VAR_STRING, etc. */ 10 | union { 11 | char *v_string; /* string value (can be NULL!) */ 12 | Pair *v_pair; /* pair value (can be NULL!) */ 13 | List *v_list; /* list value (can be NULL!) */ 14 | Dict *v_dict; /* dict value (can be NULL!) */ 15 | } vval; 16 | } typ_T; 17 | 18 | #define TOKENCHARS "~=%$!/;:|<>,[]{}() " 19 | #define EXPANCHARS "%!?$" 20 | 21 | #define VAR_NUMBER 1 /* "v_number" is used */ 22 | #define VAR_STRING 2 /* "v_string" is used */ 23 | #define VAR_PAIR 3 /* "v_pair" is used */ 24 | #define VAR_LIST 4 /* "v_list" is used */ 25 | 26 | struct Token { 27 | bool quoted; 28 | char symb; 29 | typ_T var; 30 | int start; /* start pos of token in line */ 31 | int end; /* start pos of token in line */ 32 | }; 33 | 34 | struct Pair { 35 | Token key; 36 | Token value; 37 | bool scope; 38 | }; 39 | 40 | struct List { 41 | UT_array *items; 42 | }; 43 | 44 | struct Cmdret { 45 | int type; //cmd type: OUTPUT, BUFFER, STRING, etc 46 | union { 47 | int v_int; //integer value 48 | char *v_str; //string value 49 | } val; 50 | }; 51 | 52 | struct Cmdstr { 53 | QUEUE stack; 54 | Cmdret ret; //return value 55 | Token args; 56 | bool bar; /* '|' cmd separator */ 57 | bool rev; /* reverse flag */ 58 | bool exec; //exec flag 59 | int st; //start pos of cmdstr 60 | int ed; //end pos of cmdstr 61 | int idx; //index in cmdline 62 | Cmdstr *caller; //used for returning values 63 | UT_array *chlds; //list of cmdstr subexpressions 64 | }; 65 | 66 | struct Cmdline { 67 | QUEUE refs; //queue of token refs to avoid recursive cleanup 68 | bool cont; //line continuation 69 | bool err; 70 | int len; 71 | int lvl; //subexpression level 72 | UT_array *cmds; //list of cmdstr 73 | UT_array *tokens; //list of tokens 74 | UT_array *vars; //list of tokens (variables) 75 | UT_array *arys; //list of ints (array indices) 76 | char *line; //the raw string being built upon 77 | }; 78 | 79 | void cmdline_build(Cmdline *cmdline, char *line); 80 | void cmdline_build_tokens(Cmdline *cmdline, char *line); 81 | void cmdline_req_run(Cmdstr *caller, Cmdline *cmdline); 82 | void cmdline_cleanup(Cmdline *cmdline); 83 | 84 | Token* cmdline_tokbtwn(Cmdline *cmdline, int st, int ed); 85 | Token* cmdline_tokindex(Cmdline *cmdline, int idx); 86 | Token* cmdline_last(Cmdline *cmdline); 87 | char* cmdline_line_from(Cmdline *cmdline, int idx); 88 | char* cmdline_line_after(Cmdline *cmdline, int idx); 89 | char* cmdline_cont_line(Cmdline *cmdline); 90 | Cmdstr* cmdline_getcmd(Cmdline *cmdline); 91 | List* cmdline_lst(Cmdline *cmd); 92 | int cmdline_can_exec(Cmdstr *cmd, char *line); 93 | 94 | void* token_val(Token *token, char v_type); 95 | void* list_arg(List *lst, int argc, char v_type); 96 | void* tok_arg(List *lst, int argc); 97 | int str_num(const char *, int *); 98 | int str_tfmt(const char *str, char *fmt, void *tmp); 99 | Token* container_elem(Token *ary, List *args, int idx, int n); 100 | char* container_str(char *line, Token *elem); 101 | char* repl_elem(char *line, char *expr, List *args, int fst, int lst); 102 | 103 | #endif 104 | -------------------------------------------------------------------------------- /src/nav/compl.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_COMPL_H 2 | #define NV_COMPL_H 3 | 4 | #include "nav/cmd.h" 5 | #include "nav/lib/uthash.h" 6 | 7 | typedef void (*compl_genfn)(void); 8 | typedef struct { 9 | char flag; 10 | char *label; 11 | compl_genfn gen; 12 | } compl_param; 13 | 14 | typedef struct { 15 | char *key; 16 | int argc; 17 | compl_param **params; 18 | UT_hash_handle hh; 19 | } compl_context; 20 | 21 | typedef struct { 22 | int argc; //arg position 23 | char *key; 24 | int colcount; //count of columns; (supports 0 or 1) 25 | char *columns; //column string 26 | } compl_item; 27 | 28 | typedef struct { 29 | int matchcount; 30 | UT_array *rows; //compl_item 31 | UT_array *matches; //compl_item 32 | int invalid_pos; 33 | } compl_list; 34 | 35 | void compl_init(); 36 | void compl_cleanup(); 37 | 38 | void compl_begin(int); 39 | void compl_end(); 40 | bool compl_dead(); 41 | 42 | compl_list* compl_complist(); 43 | 44 | void compl_backward(); 45 | void compl_update(const char *, int, char); 46 | void compl_build(); 47 | void compl_filter(const char *); 48 | 49 | void compl_list_add(const char *fmt, ...); 50 | void compl_set_col(int idx, char *fmt, ...); 51 | void compl_set_escapes(char ch[3]); 52 | void compl_set_exec(int); 53 | 54 | compl_item* compl_idx_match(int idx); 55 | void compl_walk_params(int (*param_cb)(char *,char,int,bool)); 56 | 57 | void compl_invalidate(int pos); 58 | bool compl_validate(int pos); 59 | bool compl_isexec(); 60 | bool compl_isroot(); 61 | int compl_next_pos(); 62 | int compl_last_pos(); 63 | int compl_cur_pos(); 64 | int compl_arg_pos(); 65 | int compl_root_pos(); 66 | 67 | #endif 68 | -------------------------------------------------------------------------------- /src/nav/config.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_CONFIG_H 2 | #define NV_CONFIG_H 3 | 4 | #include 5 | #include 6 | 7 | void config_init(); 8 | void config_start(char *); 9 | bool config_read(FILE *); 10 | void config_load(const char *); 11 | bool config_load_info(); 12 | void config_write_info(); 13 | 14 | typedef struct ConfFile ConfFile; 15 | struct ConfFile { 16 | ConfFile *parent; 17 | char *path; 18 | }; 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /src/nav/event/event.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "nav/event/event.h" 4 | #include "nav/event/input.h" 5 | #include "nav/log.h" 6 | 7 | typedef struct queue_item queue_item; 8 | struct queue_item { 9 | Event item; 10 | QUEUE node; 11 | }; 12 | 13 | static Loop main_event_loop; 14 | 15 | void event_init(void) 16 | { 17 | log_msg("INIT", "event"); 18 | uv_loop_init(eventloop()); 19 | uv_prepare_init(eventloop(), &mainloop()->event_prepare); 20 | uv_timer_init(eventloop(), &mainloop()->children_kill_timer); 21 | uv_signal_init(eventloop(), &mainloop()->children_watcher); 22 | SLIST_INIT(&mainloop()->children); 23 | eventloop()->data = eventloop(); 24 | QUEUE_INIT(&eventq()->headtail); 25 | QUEUE_INIT(&drawq()->headtail); 26 | mainloop()->running = false; 27 | } 28 | 29 | void event_cleanup(void) 30 | { 31 | uv_loop_close(eventloop()); 32 | } 33 | 34 | uint64_t os_hrtime(void) 35 | { 36 | return uv_hrtime(); 37 | } 38 | 39 | Loop* mainloop(void) 40 | { 41 | return &main_event_loop; 42 | } 43 | 44 | uv_loop_t *eventloop(void) 45 | { 46 | return &mainloop()->uv; 47 | } 48 | 49 | Queue* drawq(void) 50 | { 51 | return &mainloop()->drawq; 52 | } 53 | 54 | Queue* eventq(void) 55 | { 56 | return &mainloop()->eventq; 57 | } 58 | 59 | void event_input(void) 60 | { 61 | input_check(); 62 | } 63 | 64 | void start_event_loop(void) 65 | { 66 | log_msg("EVENT", "::::started::::"); 67 | uv_run(eventloop(), UV_RUN_DEFAULT); 68 | log_msg("EVENT", "::::exited:::::"); 69 | } 70 | 71 | void stop_event_loop(void) 72 | { 73 | uv_stop(&main_event_loop.uv); 74 | } 75 | 76 | void queue_push(Queue *queue, Event event) 77 | { 78 | queue_item *item = malloc(sizeof(queue_item)); 79 | item->item = event; 80 | QUEUE_INIT(&item->node); 81 | QUEUE_INSERT_TAIL(&queue->headtail, &item->node); 82 | } 83 | 84 | bool queue_empty(Queue *queue) 85 | { 86 | return QUEUE_EMPTY(&queue->headtail); 87 | } 88 | 89 | static queue_item *queue_node_data(QUEUE *queue) 90 | { 91 | return QUEUE_DATA(queue, queue_item, node); 92 | } 93 | 94 | static Event queue_pop(Queue *queue) 95 | { 96 | QUEUE *h = QUEUE_HEAD(&queue->headtail); 97 | queue_item *item = queue_node_data(h); 98 | QUEUE_REMOVE(&item->node); 99 | Event e; 100 | 101 | e = item->item; 102 | free(item); 103 | return e; 104 | } 105 | 106 | void queue_process_events(Queue *queue, int ms) 107 | { 108 | int remaining = ms; 109 | uint64_t before = (remaining > 0) ? os_hrtime() : 0; 110 | while (!queue_empty(queue)) { 111 | if (remaining > 0) { 112 | Event e = queue_pop(queue); 113 | if (e.handler) { 114 | e.handler(e.argv); 115 | } 116 | uint64_t now = os_hrtime(); 117 | remaining -= (int) ((now - before) / 1000000); 118 | if (remaining <= 0) { 119 | break; 120 | } 121 | } 122 | } 123 | } 124 | 125 | bool mainloop_busy() 126 | { 127 | return !(queue_empty(eventq()) && queue_empty(drawq())); 128 | } 129 | 130 | static void prepare_events(uv_prepare_t *handle) 131 | { 132 | log_msg("", "--event--"); 133 | if (mainloop()->running) 134 | return; 135 | 136 | mainloop()->running = true; 137 | while (1) { 138 | queue_process_events(eventq(), TIMEOUT); 139 | queue_process_events(drawq(), TIMEOUT); 140 | 141 | if (!mainloop_busy()) { 142 | uv_prepare_stop(handle); 143 | break; 144 | } 145 | else { 146 | uv_run(eventloop(), UV_RUN_NOWAIT); 147 | } 148 | } 149 | mainloop()->running = false; 150 | } 151 | 152 | void queue_put_event(Queue *queue, Event event) 153 | { 154 | queue_push(queue, event); 155 | if (!mainloop()->running) { 156 | uv_prepare_start(&mainloop()->event_prepare, prepare_events); 157 | } 158 | } 159 | 160 | void event_cycle_once() 161 | { 162 | bool pstate = mainloop()->running; 163 | mainloop()->running = false; 164 | queue_process_events(eventq(), TIMEOUT); 165 | queue_process_events(drawq(), TIMEOUT); 166 | uv_run(eventloop(), UV_RUN_NOWAIT); 167 | uv_run(eventloop(), UV_RUN_ONCE); 168 | mainloop()->running = pstate; 169 | } 170 | -------------------------------------------------------------------------------- /src/nav/event/event.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_EVENT_EVENT_H 2 | #define NV_EVENT_EVENT_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "nav/lib/queue.h" 9 | 10 | typedef void (*argv_callback)(void **argv); 11 | typedef struct queue Queue; 12 | struct queue { 13 | QUEUE headtail; 14 | }; 15 | 16 | typedef struct loop Loop; 17 | struct loop { 18 | uv_loop_t uv; 19 | Queue eventq; 20 | Queue drawq; 21 | SLIST_HEAD(WatcherPtr, process) children; 22 | SLIST_HEAD(WatcherTerm, term) subterms; 23 | uv_signal_t children_watcher; 24 | uv_prepare_t event_prepare; 25 | uv_timer_t children_kill_timer; 26 | size_t children_stop_requests; 27 | bool running; 28 | }; 29 | 30 | #define EVENT_HANDLER_MAX_ARGC 4 31 | typedef struct message { 32 | argv_callback handler; 33 | void *argv[EVENT_HANDLER_MAX_ARGC]; 34 | } Event; 35 | 36 | #define queue_put(q, h, ...) \ 37 | queue_put_event(q, event_create(h, __VA_ARGS__)); 38 | 39 | #define CREATE_EVENT(queue, handler, argc, ...) \ 40 | queue_put((queue), (handler), argc, __VA_ARGS__); \ 41 | 42 | #define VA_EVENT_INIT(event, h, a) \ 43 | do { \ 44 | (event)->handler = h; \ 45 | if (a) { \ 46 | va_list args; \ 47 | va_start(args, a); \ 48 | for (int i = 0; i < a; i++) { \ 49 | (event)->argv[i] = va_arg(args, void *); \ 50 | } \ 51 | va_end(args); \ 52 | } \ 53 | } while (0) 54 | 55 | static inline Event event_create(argv_callback cb, int argc, ...) 56 | { 57 | Event event; 58 | VA_EVENT_INIT(&event, cb, argc); 59 | return event; 60 | } 61 | 62 | #define TIMEOUT 10 63 | 64 | #define DO_EVENTS_UNTIL(cond) \ 65 | while (!(cond)) { \ 66 | queue_process_events(eventq(), TIMEOUT); \ 67 | queue_process_events(drawq(), TIMEOUT); \ 68 | uv_run(eventloop(), UV_RUN_NOWAIT); \ 69 | } \ 70 | 71 | bool mainloop_busy(); 72 | void event_cycle_once(); 73 | 74 | void queue_push(Queue *queue, Event event); 75 | void queue_put_event(Queue *queue, Event event); 76 | bool queue_empty(Queue *queue); 77 | void queue_process_events(Queue *queue, int ms); 78 | 79 | void event_init(void); 80 | void event_cleanup(void); 81 | void stop_event_loop(void); 82 | void start_event_loop(void); 83 | uv_loop_t *eventloop(void); 84 | Loop* mainloop(void); 85 | uint64_t os_hrtime(void); 86 | void event_input(void); 87 | Queue* drawq(void); 88 | Queue* eventq(void); 89 | 90 | #endif 91 | -------------------------------------------------------------------------------- /src/nav/event/file.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_FILE_H 2 | #define NV_FILE_H 3 | 4 | #include 5 | #include "nav/lib/sys_queue.h" 6 | #include "nav/plugins/plugin.h" 7 | 8 | #define F_COPY 1 9 | #define F_MOVE 2 10 | #define F_VERSIONED 4 11 | #define F_OVERWRITE 8 12 | #define F_UNLINK 16 13 | #define F_ERROR 32 14 | 15 | typedef struct FileItem FileItem; 16 | struct FileItem { 17 | TAILQ_ENTRY(FileItem) ent; 18 | char *src; 19 | char *dest; 20 | FileItem *parent; 21 | int refs; 22 | }; 23 | 24 | typedef struct FileGroup FileGroup; 25 | struct FileGroup { 26 | TAILQ_HEAD(cont, FileItem) p; 27 | TAILQ_ENTRY(FileGroup) ent; 28 | Buffer *owner; 29 | int flags; //F_MOVE, F_COPY etc 30 | uint64_t tsize; //size to write 31 | uint64_t wsize; //size written 32 | }; 33 | 34 | void file_init(); 35 | void file_cleanup(); 36 | void file_move_str(char *src, char *dst, Buffer *); 37 | void file_copy(varg_T, char *dest, Buffer *); 38 | void file_move(varg_T, char *dest, Buffer *); 39 | void file_remove(varg_T, Buffer *); 40 | void file_push(FileGroup *fg); 41 | void file_start(); 42 | void file_cancel(Buffer *); 43 | long file_progress(); 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /src/nav/event/fs.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_EVENT_FS_H 2 | #define NV_EVENT_FS_H 3 | 4 | #include "nav/event/event.h" 5 | #include "nav/table.h" 6 | 7 | typedef struct fentry fentry; 8 | typedef struct nv_fs nv_fs; 9 | 10 | struct nv_fs { 11 | char *path; 12 | 13 | Handle *hndl; 14 | uv_fs_t uv_fs; /* readonly stat */ 15 | char *readkey; 16 | fentry *ent; 17 | 18 | void *data; 19 | bool running; 20 | argv_callback open_cb; 21 | argv_callback stat_cb; 22 | UT_hash_handle hh; 23 | }; 24 | 25 | nv_fs* fs_init(Handle *hndl); 26 | void fs_cleanup(nv_fs *fs); 27 | void fs_open(nv_fs *fs, const char *); 28 | void fs_close(nv_fs *fs); 29 | void fs_read(nv_fs *fs, const char *); 30 | void fs_clr_cache(char *); 31 | void fs_clr_all_cache(); 32 | void fs_reload(char *); 33 | 34 | void fs_cancel(nv_fs *fs); 35 | void fs_fastreq(nv_fs *fs); 36 | bool fs_blocking(nv_fs *fs); 37 | 38 | const char* stat_kind(struct stat *); 39 | bool isrecdir(TblRec *); 40 | bool isreclnk(TblRec *rec); 41 | bool isrecreg(TblRec *rec); 42 | time_t rec_ctime(TblRec *rec); 43 | off_t rec_stsize(TblRec *rec); 44 | mode_t rec_stmode(TblRec *rec); 45 | char* fs_expand_path(const char *); 46 | char* fs_trypath(char *); 47 | char* fs_parent_dir(char *); 48 | char* conspath(const char *, const char *); 49 | const char* file_ext(const char *filename); 50 | const char* file_base(char *filename); 51 | 52 | char* fs_pwd(); 53 | void fs_cwd(char *); 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /src/nav/event/ftw.c: -------------------------------------------------------------------------------- 1 | //file tree walk 2 | #include 3 | #include 4 | #include 5 | #include "nav/event/ftw.h" 6 | #include "nav/log.h" 7 | #include "nav/util.h" 8 | #include "nav/event/event.h" 9 | 10 | typedef struct { 11 | bool running; 12 | bool cancel; 13 | uint64_t before; 14 | int count; 15 | int depth; 16 | FileItem *cur; 17 | FileGroup *fg; 18 | TAILQ_HEAD(Groups, FileGroup) p; 19 | } Ftw; 20 | static Ftw ftw; 21 | 22 | void ftw_init() 23 | { 24 | TAILQ_INIT(&ftw.p); 25 | ftw.running = false; 26 | } 27 | 28 | void ftw_cleanup() 29 | { 30 | //cancel items 31 | } 32 | 33 | static void add_ent(const char *str, struct stat *sb, int isdir) 34 | { 35 | log_msg("FTW", "add ent %s %d", str, ftw.depth); 36 | ftw.count++; 37 | ftw.fg->tsize += sb->st_size; 38 | 39 | if (ftw.depth == 0) 40 | return; 41 | 42 | FileItem *item = calloc(1, sizeof(FileItem)); 43 | item->src = strdup(str); 44 | item->parent = ftw.cur; 45 | ftw.cur->refs++; 46 | TAILQ_INSERT_TAIL(&ftw.fg->p, item, ent); 47 | 48 | if (isdir) 49 | ftw.cur = item; 50 | 51 | //TODO: onerror, pop item from ftw_queue 52 | } 53 | 54 | static void do_ftw(const char *path) 55 | { 56 | char buf[PATH_MAX], *p; 57 | struct dirent *d; 58 | struct stat st; 59 | DIR *dp; 60 | 61 | if (lstat(path, &st) == -1) 62 | return; 63 | 64 | int isdir = S_ISDIR(st.st_mode); 65 | 66 | add_ent(path, &st, isdir); 67 | 68 | if (!isdir) 69 | return; 70 | 71 | if (!(dp = opendir(path))) 72 | log_err("FTW", "opendir failed %s", path); 73 | 74 | ftw.depth++; 75 | 76 | while ((d = readdir(dp))) { 77 | if (ftw.cancel) { 78 | log_err("FTW", "<|_CANCEL_|>"); 79 | break; 80 | } 81 | 82 | uint64_t now = os_hrtime(); 83 | if ((now - ftw.before)/1000000 > MAX_WAIT) { 84 | event_cycle_once(); 85 | ftw.before = os_hrtime(); 86 | } 87 | 88 | if (strcmp(d->d_name, ".") == 0 || 89 | strcmp(d->d_name, "..") == 0) 90 | continue; 91 | 92 | strncpy(buf, path, sizeof(buf)); 93 | p = strrchr(buf, '\0'); 94 | 95 | /* remove all trailing slashes */ 96 | while (--p >= buf && *p == '/') 97 | *p ='\0'; 98 | 99 | strcat(buf, "/"); 100 | strcat(buf, d->d_name); 101 | 102 | do_ftw(buf); 103 | } 104 | 105 | ftw.depth--; 106 | ftw.cur = ftw.cur->parent; 107 | closedir(dp); 108 | } 109 | 110 | void ftw_cancel() 111 | { 112 | ftw.cancel = true; 113 | } 114 | 115 | static void ftw_start() 116 | { 117 | ftw.running = true; 118 | ftw.cancel = false; 119 | ftw.depth = 0; 120 | ftw.before = os_hrtime(); 121 | 122 | ftw.fg = TAILQ_FIRST(&ftw.p); 123 | ftw.cur = TAILQ_FIRST(&ftw.fg->p); 124 | do_ftw(ftw.cur->src); 125 | TAILQ_REMOVE(&ftw.p, ftw.fg, ent); 126 | 127 | log_msg("FTW", "Finished"); 128 | 129 | //check for more entries 130 | ftw.running = false; 131 | file_push(ftw.fg); 132 | } 133 | 134 | FileItem* ftw_new(char *src, char *dest) 135 | { 136 | FileItem *item = malloc(sizeof(FileItem)); 137 | item->src = strdup(src); 138 | item->dest = dest ? strdup(dest) : NULL; 139 | item->parent = NULL; 140 | item->refs = 0; 141 | return item; 142 | } 143 | 144 | void ftw_push_move(char *src, char *dest, FileGroup *fg) 145 | { 146 | log_msg("FTW", "pushed_move"); 147 | FileItem *item = ftw_new(src, dest); 148 | TAILQ_INSERT_TAIL(&fg->p, item, ent); 149 | fg->flags = F_MOVE|F_VERSIONED; 150 | 151 | //NOTE: copying across devices will fail and be readded as copy 152 | file_push(fg); 153 | } 154 | 155 | void ftw_push_copy(char *src, char *dest, FileGroup *fg) 156 | { 157 | log_msg("FTW", "pushed_copy"); 158 | FileItem *item = ftw_new(src, dest); 159 | TAILQ_INSERT_TAIL(&fg->p, item, ent); 160 | TAILQ_INSERT_TAIL(&ftw.p, fg, ent); 161 | 162 | fg->flags |= F_COPY|F_VERSIONED; 163 | if (!ftw.running) 164 | ftw_start(); 165 | } 166 | 167 | void ftw_push_remove(char *src, FileGroup *fg) 168 | { 169 | log_msg("FTW", "pushed_remove"); 170 | FileItem *item = ftw_new(src, NULL); 171 | TAILQ_INSERT_TAIL(&fg->p, item, ent); 172 | TAILQ_INSERT_TAIL(&ftw.p, fg, ent); 173 | 174 | fg->flags = F_UNLINK; 175 | if (!ftw.running) 176 | ftw_start(); 177 | } 178 | 179 | //enqueue but don't start 180 | void ftw_add_again(char *src, char *dst, FileGroup *fg) 181 | { 182 | FileItem *item = ftw_new(src, dst); 183 | TAILQ_INSERT_TAIL(&fg->p, item, ent); 184 | TAILQ_INSERT_HEAD(&ftw.p, fg, ent); 185 | fg->flags |= F_COPY; 186 | } 187 | 188 | void ftw_retry() 189 | { 190 | if (!ftw.running) 191 | ftw_start(); 192 | } 193 | 194 | void ftw_add(char *src, char *dst, FileGroup *fg) 195 | { 196 | log_msg("FILE", "ftw add |%s|%s|", src, dst); 197 | if (fg->flags == F_MOVE) 198 | ftw_push_move(src, dst, fg); 199 | else if (fg->flags == F_UNLINK) 200 | ftw_push_remove(src, fg); 201 | else 202 | ftw_push_copy(src, dst, fg); 203 | } 204 | -------------------------------------------------------------------------------- /src/nav/event/ftw.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_EVENT_FTW_H 2 | #define NV_EVENT_FTW_H 3 | 4 | #include "nav/event/file.h" 5 | #define MAX_WAIT 60 6 | 7 | void ftw_init(); 8 | void ftw_cleanup(); 9 | void ftw_add(char*, char *, FileGroup*); 10 | void ftw_add_again(char*, char *, FileGroup*); 11 | void ftw_cancel(); 12 | void ftw_retry(); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /src/nav/event/hook.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_EVENT_HOOK_H 2 | #define NV_EVENT_HOOK_H 3 | 4 | #include "nav/plugins/plugin.h" 5 | 6 | typedef struct { 7 | Keyarg *ka; 8 | void *arg; 9 | } HookArg; 10 | 11 | typedef void (*hook_cb)(Plugin *host, Plugin *caller, HookArg *hka); 12 | 13 | enum nv_event { 14 | EVENT_CURSOR_CHANGE, 15 | EVENT_DIROPEN, 16 | EVENT_EXEC_CLOSE, 17 | EVENT_EXEC_OPEN, 18 | EVENT_FILEOPEN, 19 | EVENT_JUMP, 20 | EVENT_LEFT, 21 | EVENT_OPEN, 22 | EVENT_OPTION_SET, 23 | EVENT_PASTE, 24 | EVENT_PIPE, 25 | EVENT_PIPE_REMOVE, 26 | EVENT_REMOVE, 27 | EVENT_RIGHT, 28 | EVENT_WINDOW_RESIZE, 29 | }; 30 | 31 | void send_hook_msg(int event, Plugin *host, Plugin *caller, HookArg *hka); 32 | void hook_init(); 33 | void hook_cleanup(); 34 | void hook_clear_host(int); 35 | 36 | int event_idx(char *key); 37 | void augroup_add(char *key); 38 | void augroup_remove(char *key); 39 | void hook_add(char *group, char *event, char *pattern, char *expr, int id); 40 | void hook_remove(char *group, char *event, char *pattern); 41 | char* hook_group_name(char *key); 42 | char* hook_event_name(char *key); 43 | 44 | void hook_add_intl(int id, Plugin *, Plugin *, hook_cb, int event); 45 | void hook_rm_intl(Plugin *, Plugin *, hook_cb, int event); 46 | void hook_set_tmp(int id); 47 | void hook_clear_msg(Plugin *, char *msg); 48 | 49 | void event_list(); 50 | void augs_list(); 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /src/nav/event/input.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_EVENT_INPUT_H 2 | #define NV_EVENT_INPUT_H 3 | 4 | #include "nav/plugins/plugin.h" 5 | #include "nav/ascii.h" 6 | 7 | #define OP_NOP 0 /* no pending operation */ 8 | #define OP_YANK 1 /* "y" yank operator */ 9 | #define OP_MARK 2 /* "m" mark operator */ 10 | #define OP_JUMP 3 /* "'" jump operator */ 11 | #define OP_G 4 /* "g" spcl operator */ 12 | #define OP_DELETE 5 /* "d" delete operator */ 13 | #define OP_WIN 6 /* "^w" window operator */ 14 | 15 | typedef void (*key_func)(void *obj, Keyarg *arg); 16 | 17 | typedef struct { 18 | int cmd_char; /* (first) command character */ 19 | key_func cmd_func; /* function for this command */ 20 | int cmd_flags; /* NCH* and EVENT */ 21 | short cmd_arg; /* value for ca.arg */ 22 | } nv_key; 23 | 24 | typedef struct { 25 | int op_char; 26 | int nchar; 27 | key_func cmd_func; 28 | } nv_oper; 29 | 30 | typedef struct { 31 | nv_key *tbl; 32 | short *cmd_idx; 33 | int maxsize; 34 | int maxlinear; 35 | } nv_keytbl; 36 | 37 | struct nv_reg { 38 | int key; 39 | varg_T value; 40 | }; 41 | 42 | typedef struct { 43 | int key; // current pending operator type 44 | int regname; // register to use for the operator 45 | int motion_type; // type of the current cursor motion 46 | int motion_force; // force motion type: 'v', 'V' or CTRL-V 47 | pos_T cursor_start; // cursor position before motion for "gw" 48 | 49 | long line_count; // number of lines from op_start to op_end 50 | // (inclusive) 51 | bool is_VIsual; // operator on Visual area 52 | bool is_unused; // op cmd rejected key 53 | } Oparg; 54 | 55 | struct Keyarg { 56 | nv_keytbl *optbl; /* table that set operator */ 57 | Oparg oap; /* Operator arguments */ 58 | long opcount; /* count before an operator */ 59 | int type; 60 | int key; 61 | int nkey; 62 | int mkey; 63 | short arg; 64 | char *utf8; 65 | }; 66 | 67 | void input_init(void); 68 | void input_cleanup(void); 69 | void input_setup_tbl(nv_keytbl *kt); 70 | 71 | void unset_map(char *from); 72 | void set_map(char *lhs, char *rhs); 73 | 74 | bool try_do_map(Keyarg *ca); 75 | int find_command(nv_keytbl *kt, int cmdchar); 76 | int find_do_key(nv_keytbl *kt, Keyarg *ca, void *obj); 77 | void input_check(); 78 | void oper(void *, Keyarg *ca); 79 | void clearop(Keyarg *ca); 80 | bool op_pending(Keyarg *arg); 81 | 82 | nv_reg* reg_get(int ch); 83 | nv_reg* reg_dcur(); 84 | void reg_clear_dcur(); 85 | void reg_set(int ch, varg_T); 86 | void reg_yank(char *); 87 | void maps_list(); 88 | 89 | #endif 90 | -------------------------------------------------------------------------------- /src/nav/event/process.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include "nav/event/event.h" 6 | #include "nav/event/process.h" 7 | #include "nav/event/uv_process.h" 8 | #include "nav/log.h" 9 | 10 | // {SIGNAL}_TIMEOUT is the time (in nanoseconds) that a process has to cleanly 11 | // exit before we send SIGNAL to it 12 | #define TERM_TIMEOUT 1000000000 13 | #define KILL_TIMEOUT (TERM_TIMEOUT * 2) 14 | 15 | #define CLOSE_PROC_STREAM(proc, stream) \ 16 | do { \ 17 | if (proc->stream && !proc->stream->closed) { \ 18 | stream_close(proc->stream, NULL); \ 19 | } \ 20 | } while (0) 21 | 22 | static bool process_is_tearing_down = false; 23 | 24 | static void on_process_stream_close(Stream *stream, void *data); 25 | static void on_process_exit(Process *proc); 26 | static void decref(Process *proc); 27 | static void process_close(Process *proc); 28 | static void children_kill_cb(uv_timer_t *handle); 29 | 30 | bool process_spawn(Process *proc) 31 | { 32 | log_msg("PROCESS", "spawn"); 33 | if (proc->in) { 34 | uv_pipe_init(&proc->loop->uv, &proc->in->uv.pipe, 0); 35 | } 36 | 37 | if (proc->out) { 38 | uv_pipe_init(&proc->loop->uv, &proc->out->uv.pipe, 0); 39 | } 40 | 41 | if (proc->err) { 42 | uv_pipe_init(&proc->loop->uv, &proc->err->uv.pipe, 0); 43 | } 44 | 45 | bool success = uv_process_spawn((UvProcess *)proc); 46 | 47 | if (!success) { 48 | if (proc->in) { 49 | uv_close((uv_handle_t *)&proc->in->uv.pipe, NULL); 50 | } 51 | if (proc->out) { 52 | uv_close((uv_handle_t *)&proc->out->uv.pipe, NULL); 53 | } 54 | if (proc->err) { 55 | uv_close((uv_handle_t *)&proc->err->uv.pipe, NULL); 56 | } 57 | 58 | uv_close((uv_handle_t *)&(((UvProcess *)proc)->uv), NULL); 59 | free(proc->argv); 60 | proc->status = -1; 61 | return false; 62 | } 63 | 64 | void *data = proc->data; 65 | 66 | if (proc->in) { 67 | stream_init(NULL, proc->in, -1, (uv_stream_t *)&proc->in->uv.pipe, data); 68 | proc->in->events = proc->events; 69 | proc->in->internal_data = proc; 70 | proc->in->internal_close_cb = on_process_stream_close; 71 | proc->refcount++; 72 | } 73 | 74 | if (proc->out) { 75 | stream_init(NULL, proc->out, -1, (uv_stream_t *)&proc->out->uv.pipe, data); 76 | proc->out->events = proc->events; 77 | proc->out->internal_data = proc; 78 | proc->out->internal_close_cb = on_process_stream_close; 79 | proc->refcount++; 80 | } 81 | 82 | if (proc->err) { 83 | stream_init(NULL, proc->err, -1, (uv_stream_t *)&proc->err->uv.pipe, data); 84 | proc->err->events = proc->events; 85 | proc->err->internal_data = proc; 86 | proc->err->internal_close_cb = on_process_stream_close; 87 | proc->refcount++; 88 | } 89 | 90 | proc->internal_exit_cb = on_process_exit; 91 | proc->internal_close_cb = decref; 92 | proc->refcount++; 93 | SLIST_INSERT_HEAD(&proc->loop->children, proc, ent); 94 | return true; 95 | } 96 | 97 | // Wrappers around `stream_close` that protect against double-closing. 98 | void process_close_streams(Process *proc) 99 | { 100 | log_msg("PROCESS", "process_close_streams"); 101 | process_close_in(proc); 102 | process_close_out(proc); 103 | process_close_err(proc); 104 | } 105 | 106 | void process_close_in(Process *proc) 107 | { 108 | log_msg("PROCESS", "process_close_in"); 109 | CLOSE_PROC_STREAM(proc, in); 110 | } 111 | 112 | void process_close_out(Process *proc) 113 | { 114 | log_msg("PROCESS", "process_close_out"); 115 | CLOSE_PROC_STREAM(proc, out); 116 | } 117 | 118 | void process_close_err(Process *proc) 119 | { 120 | log_msg("PROCESS", "process_close_err"); 121 | CLOSE_PROC_STREAM(proc, err); 122 | } 123 | 124 | /// Ask a process to terminate and eventually kill if it doesn't respond 125 | void process_stop(Process *proc) 126 | { 127 | log_msg("PROCESS", "process_stop"); 128 | if (proc->stopped_time) { 129 | return; 130 | } 131 | 132 | proc->stopped_time = os_hrtime(); 133 | // Close the process's stdin. If the process doesn't close its own 134 | // stdout/stderr, they will be closed when it exits(possibly due to being 135 | // terminated after a timeout) 136 | process_close_in(proc); 137 | 138 | Loop *loop = proc->loop; 139 | if (!loop->children_stop_requests++) { 140 | // When there's at least one stop request pending, start a timer that 141 | // will periodically check if a signal should be send to a to the job 142 | log_msg("PROCESS", "Starting job kill timer"); 143 | uv_timer_start(&loop->children_kill_timer, children_kill_cb, 100, 100); 144 | } 145 | } 146 | 147 | /// Iterates the process list sending SIGTERM to stopped processes and SIGKILL 148 | /// to those that didn't die from SIGTERM after a while(exit_timeout is 0). 149 | static void children_kill_cb(uv_timer_t *handle) 150 | { 151 | log_msg("PROCESS", "children_kill_cb"); 152 | Loop *loop = handle->loop->data; 153 | uint64_t now = os_hrtime(); 154 | 155 | Process *it; 156 | SLIST_FOREACH(it, &loop->children, ent) { 157 | Process *proc = it; 158 | if (!proc->stopped_time) 159 | continue; 160 | 161 | uint64_t elapsed = now - proc->stopped_time; 162 | 163 | if (!proc->term_sent && elapsed >= TERM_TIMEOUT) { 164 | log_msg("process", "Sending SIGTERM to pid %d", proc->pid); 165 | uv_kill(proc->pid, SIGTERM); 166 | proc->term_sent = true; 167 | } else if (elapsed >= KILL_TIMEOUT) { 168 | log_msg("process", "Sending SIGKILL to pid %d", proc->pid); 169 | uv_kill(proc->pid, SIGKILL); 170 | } 171 | } 172 | } 173 | 174 | static void process_close_event(void **args) 175 | { 176 | log_msg("PROCESS", "process_close_event"); 177 | Process *proc = args[0]; 178 | 179 | if (proc->cb) 180 | proc->cb(proc, proc->status, proc->data); 181 | } 182 | 183 | static void decref(Process *proc) 184 | { 185 | log_msg("PROCESS", "decref"); 186 | if (--proc->refcount != 0) 187 | return; 188 | 189 | Loop *loop = proc->loop; 190 | Process *node = NULL; 191 | Process *it; 192 | SLIST_FOREACH(it, &loop->children, ent) { 193 | if (it == proc) { 194 | node = it; 195 | break; 196 | } 197 | } 198 | SLIST_REMOVE(&loop->children, node, process, ent); 199 | CREATE_EVENT(eventq(), process_close_event, 1, proc); 200 | } 201 | 202 | static void process_close(Process *proc) 203 | { 204 | log_msg("PROCESS", "process_close"); 205 | if (process_is_tearing_down && proc->detach && proc->closed) { 206 | // If a detached process dies while tearing down it might get closed twice. 207 | return; 208 | } 209 | 210 | proc->closed = true; 211 | uv_process_close((UvProcess *)proc); 212 | } 213 | 214 | static void process_close_handles(void **argv) 215 | { 216 | log_msg("PROCESS", "process_close_handles"); 217 | Process *proc = argv[0]; 218 | process_close_streams(proc); 219 | process_close(proc); 220 | } 221 | 222 | static void on_process_exit(Process *proc) 223 | { 224 | log_msg("PROCESS", "on_process_exit"); 225 | Loop *loop = proc->loop; 226 | if (proc->stopped_time && loop->children_stop_requests 227 | && !--loop->children_stop_requests) { 228 | // Stop the timer if no more stop requests are pending 229 | log_msg("process", "Stopping process kill timer"); 230 | uv_timer_stop(&loop->children_kill_timer); 231 | } 232 | // Process handles are closed in the next event loop tick. This is done to 233 | // give libuv more time to read data from the OS after the process exits(If 234 | // process_close_streams is called with data still in the OS buffer, we lose 235 | // it) 236 | CREATE_EVENT(proc->events, process_close_handles, 1, proc); 237 | } 238 | 239 | static void on_process_stream_close(Stream *stream, void *data) 240 | { 241 | log_msg("PROCESS", "on_process_stream_close"); 242 | Process *proc = data; 243 | decref(proc); 244 | } 245 | -------------------------------------------------------------------------------- /src/nav/event/process.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_EVENT_PROCESS_H 2 | #define NV_EVENT_PROCESS_H 3 | 4 | #include "nav/event/event.h" 5 | #include "nav/event/stream.h" 6 | 7 | typedef struct process Process; 8 | typedef void (*process_exit_cb)(Process *proc, int status, void *data); 9 | typedef void (*internal_process_cb)(Process *proc); 10 | 11 | struct process { 12 | Loop *loop; 13 | void *data; 14 | int pid, status, refcount; 15 | // set to the hrtime of when process_stop was called for the process. 16 | uint64_t stopped_time; 17 | char **argv; 18 | char *cwd; 19 | Stream *in, *out, *err; 20 | process_exit_cb cb, fast_output; 21 | internal_process_cb internal_exit_cb, internal_close_cb; 22 | bool closed, term_sent, detach; 23 | Queue *events; 24 | SLIST_ENTRY(process) ent; 25 | }; 26 | 27 | static inline Process process_init(Loop *loop, void *data) 28 | { 29 | return (Process) { 30 | .data = data, 31 | .loop = loop, 32 | .pid = 0, 33 | .status = 0, 34 | .refcount = 0, 35 | .stopped_time = 0, 36 | .argv = NULL, 37 | .cwd = NULL, 38 | .in = NULL, 39 | .out = NULL, 40 | .err = NULL, 41 | .cb = NULL, 42 | .fast_output = NULL, 43 | .closed = false, 44 | .term_sent = false, 45 | .detach = false, 46 | .internal_close_cb = NULL, 47 | .internal_exit_cb = NULL, 48 | }; 49 | } 50 | bool process_spawn(Process *proc); 51 | void process_teardown(Loop *loop); 52 | void process_close_streams(Process *proc); 53 | void process_close_in(Process *proc); 54 | void process_close_out(Process *proc); 55 | void process_close_err(Process *proc); 56 | void process_stop(Process *proc); 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /src/nav/event/rstream.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include "nav/event/rstream.h" 9 | #include "nav/ascii.h" 10 | #include "nav/log.h" 11 | 12 | static void invoke_read_cb(Stream *stream, size_t count, bool eof); 13 | static void on_rbuffer_full(RBuffer *buf, void *data); 14 | static void on_rbuffer_nonfull(RBuffer *buf, void *data); 15 | static void alloc_cb(uv_handle_t *handle, size_t suggested, uv_buf_t *buf); 16 | static void read_cb(uv_stream_t *uvstream, ssize_t cnt, const uv_buf_t *buf); 17 | static void fread_idle_cb(uv_idle_t *handle); 18 | 19 | void rstream_init_fd(Loop *loop, Stream *stream, int fd, size_t bufsize, 20 | void *data) 21 | { 22 | log_msg("RSTREAM", "rstream_init_fd"); 23 | stream_init(loop, stream, fd, NULL, data); 24 | rstream_init(stream, bufsize); 25 | } 26 | 27 | void rstream_init_stream(Stream *stream, uv_stream_t *uvstream, size_t bufsize, 28 | void *data) 29 | { 30 | log_msg("RSTREAM", "rstream_init_stream"); 31 | stream_init(NULL, stream, -1, uvstream, data); 32 | rstream_init(stream, bufsize); 33 | } 34 | 35 | void rstream_init(Stream *stream, size_t bufsize) 36 | { 37 | log_msg("RSTREAM", "init"); 38 | stream->buffer = rbuffer_new(bufsize); 39 | stream->buffer->data = stream; 40 | stream->buffer->full_cb = on_rbuffer_full; 41 | stream->buffer->nonfull_cb = on_rbuffer_nonfull; 42 | } 43 | 44 | /// Starts watching for events from a `Stream` instance. 45 | /// 46 | /// @param stream The `Stream` instance 47 | void rstream_start(Stream *stream, stream_read_cb cb) 48 | { 49 | log_msg("RSTREAM", "start"); 50 | stream->read_cb = cb; 51 | if (stream->uvstream) { 52 | uv_read_start(stream->uvstream, alloc_cb, read_cb); 53 | } else { 54 | uv_idle_start(&stream->uv.idle, fread_idle_cb); 55 | } 56 | } 57 | 58 | /// Stops watching for events from a `Stream` instance. 59 | /// 60 | /// @param stream The `Stream` instance 61 | void rstream_stop(Stream *stream) 62 | { 63 | log_msg("RSTREAM", "stop"); 64 | if (stream->uvstream) { 65 | uv_read_stop(stream->uvstream); 66 | } else { 67 | uv_idle_stop(&stream->uv.idle); 68 | } 69 | } 70 | 71 | static void on_rbuffer_full(RBuffer *buf, void *data) 72 | { 73 | log_msg("RSTREAM", "full"); 74 | rstream_stop(data); 75 | } 76 | 77 | static void on_rbuffer_nonfull(RBuffer *buf, void *data) 78 | { 79 | log_msg("RSTREAM", "nonfull"); 80 | Stream *stream = data; 81 | assert(stream->read_cb); 82 | rstream_start(stream, stream->read_cb); 83 | } 84 | 85 | // Callbacks used by libuv 86 | 87 | // Called by libuv to allocate memory for reading. 88 | static void alloc_cb(uv_handle_t *handle, size_t suggested, uv_buf_t *buf) 89 | { 90 | log_msg("RSTREAM", "alloc"); 91 | Stream *stream = handle->data; 92 | buf->base = rbuffer_write_ptr(stream->buffer, &buf->len); 93 | } 94 | 95 | // Callback invoked by libuv after it copies the data into the buffer provided 96 | // by `alloc_cb`. This is also called on EOF or when `alloc_cb` returns a 97 | // 0-length buffer. 98 | static void read_cb(uv_stream_t *uvstream, ssize_t cnt, const uv_buf_t *buf) 99 | { 100 | log_msg("RSTREAM", "read"); 101 | Stream *stream = uvstream->data; 102 | 103 | if (cnt <= 0) { 104 | if (cnt != UV_ENOBUFS 105 | // cnt == 0 means libuv asked for a buffer and decided it wasn't needed: 106 | // http://docs.libuv.org/en/latest/stream.html#c.uv_read_start. 107 | // 108 | // We don't need to do anything with the RBuffer because the next call 109 | // to `alloc_cb` will return the same unused pointer(`rbuffer_produced` 110 | // won't be called) 111 | && cnt != 0) { 112 | log_msg("RSTREAM", "Closing Stream(%p) because of %s(%zd)", stream, 113 | uv_strerror((int)cnt), cnt); 114 | // Read error or EOF, either way stop the stream and invoke the callback 115 | // with eof == true 116 | uv_read_stop(uvstream); 117 | invoke_read_cb(stream, 0, true); 118 | } 119 | return; 120 | } 121 | 122 | // at this point we're sure that cnt is positive, no error occurred 123 | size_t nread = (size_t)cnt; 124 | // Data was already written, so all we need is to update 'wpos' to reflect 125 | // the space actually used in the buffer. 126 | rbuffer_produced(stream->buffer, nread); 127 | invoke_read_cb(stream, nread, false); 128 | } 129 | 130 | // Called by the by the 'idle' handle to emulate a reading event 131 | static void fread_idle_cb(uv_idle_t *handle) 132 | { 133 | log_msg("RSTREAM", "idle"); 134 | uv_fs_t req; 135 | Stream *stream = handle->data; 136 | 137 | stream->uvbuf.base = rbuffer_write_ptr(stream->buffer, &stream->uvbuf.len); 138 | 139 | // the offset argument to uv_fs_read is int64_t, could someone really try 140 | // to read more than 9 quintillion (9e18) bytes? 141 | // upcast is meant to avoid tautological condition warning on 32 bits 142 | uintmax_t fpos_intmax = stream->fpos; 143 | if (fpos_intmax > INT64_MAX) { 144 | log_msg("RSTREAM", "stream offset overflow"); 145 | //preserve_exit(); 146 | } 147 | 148 | // Synchronous read 149 | uv_fs_read( 150 | handle->loop, 151 | &req, 152 | stream->fd, 153 | &stream->uvbuf, 154 | 1, 155 | (int64_t) stream->fpos, 156 | NULL); 157 | 158 | uv_fs_req_cleanup(&req); 159 | 160 | if (req.result <= 0) { 161 | uv_idle_stop(&stream->uv.idle); 162 | invoke_read_cb(stream, 0, true); 163 | return; 164 | } 165 | 166 | // no errors (req.result (ssize_t) is positive), it's safe to cast. 167 | size_t nread = (size_t) req.result; 168 | rbuffer_produced(stream->buffer, nread); 169 | stream->fpos += nread; 170 | invoke_read_cb(stream, nread, false); 171 | } 172 | 173 | static void read_event(void **argv) 174 | { 175 | log_msg("RSTREAM", "read event"); 176 | Stream *stream = argv[0]; 177 | bool eof = argv[3]; 178 | if (eof) return; 179 | if (stream->read_cb) { 180 | size_t count = (uintptr_t)argv[1]; 181 | bool eof = (uintptr_t)argv[2]; 182 | stream->read_cb(stream, stream->buffer, count, stream->data, eof); 183 | } 184 | } 185 | 186 | static void invoke_read_cb(Stream *stream, size_t count, bool eof) 187 | { 188 | log_msg("RSTREAM", "invoke_read_cb"); 189 | CREATE_EVENT(stream->events, read_event, 4, stream, 190 | (void *)(uintptr_t *)count, (void *)stream->data, (void *)(uintptr_t)eof); 191 | } 192 | -------------------------------------------------------------------------------- /src/nav/event/rstream.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_EVENT_RSTREAM_H 2 | #define NV_EVENT_RSTREAM_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "nav/event/stream.h" 8 | 9 | void rstream_init_fd(Loop *loop, Stream *stream, int fd, size_t bufsize, 10 | void *data); 11 | void rstream_init_stream(Stream *stream, uv_stream_t *uvstream, size_t bufsize, 12 | void *data); 13 | void rstream_init(Stream *stream, size_t bufsize); 14 | void rstream_start(Stream *stream, stream_read_cb cb); 15 | void rstream_stop(Stream *stream); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /src/nav/event/shell.c: -------------------------------------------------------------------------------- 1 | #include "nav/event/shell.h" 2 | #include "nav/event/wstream.h" 3 | #include "nav/event/event.h" 4 | #include "nav/event/hook.h" 5 | #include "nav/log.h" 6 | #include "nav/option.h" 7 | #include "nav/plugins/out/out.h" 8 | #include "nav/plugins/op/op.h" 9 | 10 | static void out_data_cb(Stream *, RBuffer *, size_t, void *, bool); 11 | static void shell_write_cb(Stream *, void *, int); 12 | 13 | static char *args[4]; 14 | 15 | static void process_exit(Process *proc, int status, void *data) 16 | { 17 | log_msg("SHELL", "fin"); 18 | Shell *sh = data; 19 | int pid = sh->uvproc.process.pid; 20 | if (!sh) 21 | return; 22 | 23 | op_set_exit_status(status); 24 | 25 | sh->blocking = false; 26 | if (sh->again) { 27 | log_msg("SHELL", "again"); 28 | sh->again = false; 29 | shell_start(sh); 30 | } 31 | if (sh->reg) 32 | free(sh); 33 | send_hook_msg(EVENT_EXEC_CLOSE, NULL, NULL, &(HookArg){NULL,&pid}); 34 | } 35 | 36 | Shell* shell_new(Plugin *plugin) 37 | { 38 | log_msg("SHELL", "init"); 39 | Shell *sh = malloc(sizeof(Shell)); 40 | sh->msg = NULL; 41 | sh->readout = NULL; 42 | sh->blocking = false; 43 | sh->again = false; 44 | sh->reg = false; 45 | sh->data_cb = out_data_cb; 46 | 47 | sh->caller = plugin; 48 | sh->uvproc = uv_process_init(mainloop(), sh); 49 | Process *proc = &sh->uvproc.process; 50 | proc->events = eventq(); 51 | 52 | proc->in = &sh->in; 53 | proc->out = &sh->out; 54 | proc->err = &sh->err; 55 | proc->cb = process_exit; 56 | return sh; 57 | } 58 | 59 | void shell_args(Shell *sh, char **args, shell_stdout_cb readout) 60 | { 61 | Process *proc = &sh->uvproc.process; 62 | proc->argv = args; 63 | sh->readout = readout; 64 | } 65 | 66 | void shell_delete(Shell *sh) 67 | { 68 | log_msg("SHELL", "shell_free"); 69 | if (sh->msg) 70 | free(sh->msg); 71 | free(sh); 72 | } 73 | 74 | void shell_start(Shell *sh) 75 | { 76 | log_msg("SHELL", "start"); 77 | Process *proc = &sh->uvproc.process; 78 | if (sh->blocking) { 79 | log_msg("SHELL", "|***BLOCKED**|"); 80 | sh->again = true; 81 | return; 82 | } 83 | int succ = process_spawn(proc); 84 | if (!succ) { 85 | log_msg("PROC", "cannot execute %d", succ); 86 | if (sh->reg) 87 | process_exit(&sh->uvproc.process, 0, 0); 88 | return; 89 | } 90 | 91 | if (sh->reg) 92 | op_set_exec_line(proc->argv[2], NULL); 93 | 94 | sh->blocking = true; 95 | wstream_init(proc->in, 0); 96 | rstream_init(proc->out, 0); 97 | rstream_start(proc->out, sh->data_cb); 98 | rstream_init(proc->err, 0); 99 | rstream_start(proc->err, sh->data_cb); 100 | if (sh->msg) 101 | shell_write(sh, sh->msg); 102 | } 103 | 104 | void shell_stop(Shell *sh) 105 | { 106 | log_msg("SHELL", "stop"); 107 | Process *proc = &sh->uvproc.process; 108 | process_stop(proc); 109 | } 110 | 111 | void shell_set_in_buffer(Shell *sh, char *msg) 112 | { 113 | log_msg("SHELL", "shell_write"); 114 | if (sh->msg) 115 | free(sh->msg); 116 | sh->msg = strdup(msg); 117 | } 118 | 119 | void shell_write(Shell *sh, char *msg) 120 | { 121 | log_msg("SHELL", "write_to_stream"); 122 | WBuffer *input_buffer = wstream_new_buffer(msg, strlen(msg), 1, NULL); 123 | Process *proc = &sh->uvproc.process; 124 | 125 | if (!wstream_write(proc->in, input_buffer)) { 126 | // couldn't write, stop the process and tell the user about it 127 | log_msg("SHELL", "couldn't write"); 128 | process_stop(proc); 129 | return; 130 | } 131 | // close the input stream after everything is written 132 | wstream_set_write_cb(proc->in, shell_write_cb); 133 | } 134 | 135 | static void out_data_cb(Stream *stream, RBuffer *buf, size_t count, void *data, 136 | bool eof) 137 | { 138 | log_msg("SHELL", "out_data_cb"); 139 | size_t cnt; 140 | char *ptr = rbuffer_read_ptr(buf, &cnt); 141 | 142 | if (!cnt) 143 | return; 144 | 145 | ptr[count] = '\0'; 146 | Shell *sh = (Shell*)data; 147 | int pid = sh->uvproc.process.pid; 148 | int fd = &sh->out == stream ? 1 : 2; 149 | 150 | if (sh->readout) 151 | sh->readout(sh->caller, ptr); 152 | else 153 | out_recv(pid, fd, count, ptr); 154 | 155 | size_t written = count; 156 | // No output written, force emptying the Rbuffer if it is full. 157 | if (!written && rbuffer_size(buf) == rbuffer_capacity(buf)) 158 | written = cnt; 159 | if (written) 160 | rbuffer_consumed(buf, count); 161 | } 162 | 163 | static void shell_write_cb(Stream *stream, void *data, int status) 164 | { 165 | stream_close(stream, NULL); 166 | } 167 | 168 | int shell_exec(char *line, char *cwd) 169 | { 170 | log_msg("SHELL", "shell_exec"); 171 | log_msg("SHELL", "%s", line); 172 | 173 | Shell *sh = malloc(sizeof(Shell)); 174 | sh->msg = NULL; 175 | sh->readout = NULL; 176 | sh->blocking = false; 177 | sh->again = false; 178 | sh->reg = true; 179 | sh->data_cb = out_data_cb; 180 | sh->caller = NULL; 181 | 182 | sh->uvproc = uv_process_init(mainloop(), sh); 183 | Process *proc = &sh->uvproc.process; 184 | proc->events = eventq(); 185 | 186 | proc->in = &sh->in; 187 | proc->out = &sh->out; 188 | proc->err = &sh->err; 189 | proc->cb = process_exit; 190 | proc->cwd = cwd; 191 | 192 | char *rv = strdup(line); 193 | args[0] = get_opt_str("shell"); 194 | args[1] = "-c"; 195 | args[2] = rv; 196 | args[3] = NULL; 197 | shell_args(sh, args, NULL); 198 | shell_start(sh); 199 | free(rv); 200 | 201 | int pid = sh->uvproc.process.pid; 202 | send_hook_msg(EVENT_EXEC_OPEN, NULL, NULL, &(HookArg){NULL,&pid}); 203 | return pid; 204 | } 205 | -------------------------------------------------------------------------------- /src/nav/event/shell.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_EVENT_SHELL_H 2 | #define NV_EVENT_SHELL_H 3 | 4 | #include "nav/event/uv_process.h" 5 | #include "nav/event/rstream.h" 6 | #include "nav/plugins/plugin.h" 7 | 8 | typedef void (*shell_stdout_cb)(Plugin *c, char *out); 9 | 10 | typedef struct Shell Shell; 11 | struct Shell { 12 | stream_read_cb data_cb; 13 | UvProcess uvproc; 14 | Stream in; 15 | Stream out; 16 | Stream err; 17 | 18 | shell_stdout_cb readout; 19 | Plugin *caller; 20 | bool blocking; 21 | bool again; 22 | bool reg; 23 | char *msg; 24 | }; 25 | 26 | Shell* shell_new(Plugin *plugin); 27 | void shell_delete(Shell *sh); 28 | void shell_args(Shell *sh, char **args, shell_stdout_cb readout); 29 | void shell_start(Shell *sh); 30 | void shell_stop(Shell *sh); 31 | void shell_set_in_buffer(Shell *sh, char *msg); 32 | int shell_exec(char *line, char *cwd); 33 | void shell_write(Shell *sh, char *msg); 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /src/nav/event/stream.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #include "nav/event/stream.h" 8 | #include "nav/log.h" 9 | 10 | static void close_cb(uv_handle_t *handle); 11 | 12 | /// Sets the stream associated with `fd` to "blocking" mode. 13 | /// 14 | /// @return `0` on success, or libuv error code on failure. 15 | int stream_set_blocking(int fd, bool blocking) 16 | { 17 | // Private loop to avoid conflict with existing watcher(s): 18 | // uv__io_stop: Assertion `loop->watchers[w->fd] == w' failed. 19 | uv_loop_t loop; 20 | uv_pipe_t stream; 21 | uv_loop_init(&loop); 22 | uv_pipe_init(&loop, &stream, 0); 23 | uv_pipe_open(&stream, fd); 24 | int retval = uv_stream_set_blocking((uv_stream_t *)&stream, blocking); 25 | uv_close((uv_handle_t *)&stream, NULL); 26 | uv_run(&loop, UV_RUN_NOWAIT); // not necessary, but couldn't hurt. 27 | uv_loop_close(&loop); 28 | return retval; 29 | } 30 | 31 | void stream_init(Loop *loop, Stream *stream, int fd, uv_stream_t *uvstream, 32 | void *data) 33 | { 34 | stream->uvstream = uvstream; 35 | 36 | if (fd >= 0) { 37 | uv_handle_type type = uv_guess_handle(fd); 38 | stream->fd = fd; 39 | 40 | if (type == UV_FILE) { 41 | // Non-blocking file reads are simulated with an idle handle that reads in 42 | // chunks of the ring buffer size, giving time for other events to be 43 | // processed between reads. 44 | uv_idle_init(&loop->uv, &stream->uv.idle); 45 | stream->uv.idle.data = stream; 46 | } else { 47 | assert(type == UV_NAMED_PIPE || type == UV_TTY); 48 | uv_pipe_init(&loop->uv, &stream->uv.pipe, 0); 49 | uv_pipe_open(&stream->uv.pipe, fd); 50 | stream->uvstream = (uv_stream_t *)&stream->uv.pipe; 51 | } 52 | } 53 | 54 | if (stream->uvstream) { 55 | stream->uvstream->data = stream; 56 | } 57 | 58 | stream->data = data; 59 | stream->internal_data = NULL; 60 | stream->fpos = 0; 61 | stream->curmem = 0; 62 | stream->maxmem = 0; 63 | stream->pending_reqs = 0; 64 | stream->read_cb = NULL; 65 | stream->write_cb = NULL; 66 | stream->close_cb = NULL; 67 | stream->internal_close_cb = NULL; 68 | stream->closed = false; 69 | stream->buffer = NULL; 70 | stream->events = NULL; 71 | } 72 | 73 | void stream_close(Stream *stream, stream_close_cb on_stream_close) 74 | { 75 | assert(!stream->closed); 76 | stream->closed = true; 77 | stream->close_cb = on_stream_close; 78 | 79 | if (!stream->pending_reqs) { 80 | stream_close_handle(stream); 81 | } 82 | } 83 | 84 | void stream_close_handle(Stream *stream) 85 | { 86 | if (stream->uvstream) { 87 | uv_close((uv_handle_t *)stream->uvstream, close_cb); 88 | } else { 89 | uv_close((uv_handle_t *)&stream->uv.idle, close_cb); 90 | } 91 | } 92 | 93 | static void stream_cleanup_cb(void **argv) 94 | { 95 | Stream *stream = argv[0]; 96 | rbuffer_free(stream->buffer); 97 | } 98 | 99 | static void close_cb(uv_handle_t *handle) 100 | { 101 | Stream *stream = handle->data; 102 | if (stream->buffer) { 103 | CREATE_EVENT(eventq(), stream_cleanup_cb, 1, stream); 104 | } 105 | if (stream->close_cb) { 106 | stream->close_cb(stream, stream->data); 107 | } 108 | if (stream->internal_close_cb) { 109 | stream->internal_close_cb(stream, stream->internal_data); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/nav/event/stream.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_EVENT_STREAM_H 2 | #define NV_EVENT_STREAM_H 3 | 4 | #include "nav/event/event.h" 5 | #include "nav/rbuffer.h" 6 | 7 | typedef struct stream Stream; 8 | /// Type of function called when the Stream buffer is filled with data 9 | /// 10 | /// @param stream The Stream instance 11 | /// @param rbuffer The associated RBuffer instance 12 | /// @param count Number of bytes to read. This must be respected if keeping 13 | /// the order of events is a requirement. This is because events 14 | /// may be queued and only processed later when more data is copied 15 | /// into to the buffer, so one read may starve another. 16 | /// @param data User-defined data 17 | /// @param eof If the stream reached EOF. 18 | typedef void (*stream_read_cb)(Stream *stream, RBuffer *buf, size_t count, 19 | void *data, bool eof); 20 | 21 | /// Type of function called when the Stream has information about a write 22 | /// request. 23 | /// 24 | /// @param wstream The Stream instance 25 | /// @param data User-defined data 26 | /// @param status 0 on success, anything else indicates failure 27 | typedef void (*stream_write_cb)(Stream *stream, void *data, int status); 28 | typedef void (*stream_close_cb)(Stream *stream, void *data); 29 | 30 | struct stream { 31 | union { 32 | uv_pipe_t pipe; 33 | uv_tcp_t tcp; 34 | uv_idle_t idle; 35 | } uv; 36 | uv_stream_t *uvstream; 37 | uv_buf_t uvbuf; 38 | RBuffer *buffer; 39 | uv_file fd; 40 | stream_read_cb read_cb; 41 | stream_write_cb write_cb; 42 | stream_close_cb close_cb, internal_close_cb; 43 | size_t fpos; 44 | size_t curmem; 45 | size_t maxmem; 46 | size_t pending_reqs; 47 | void *data, *internal_data; 48 | bool closed; 49 | Queue *events; 50 | }; 51 | 52 | void stream_init(Loop *loop, Stream *stream, int fd, uv_stream_t *uvstream, 53 | void *data); 54 | void stream_close_handle(Stream *stream); 55 | void stream_close(Stream *stream, stream_close_cb on_stream_close); 56 | int stream_set_blocking(int fd, bool blocking); 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /src/nav/event/uv_process.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "nav/event/process.h" 5 | #include "nav/event/uv_process.h" 6 | #include "nav/log.h" 7 | 8 | static void close_cb(uv_handle_t *handle); 9 | static void exit_cb(uv_process_t *handle, int64_t status, int term_signal); 10 | 11 | bool uv_process_spawn(UvProcess *uvproc) 12 | { 13 | log_msg("UV_PROCESS", "spawn"); 14 | Process *proc = (Process *)uvproc; 15 | uvproc->uvopts.file = proc->argv[0]; 16 | uvproc->uvopts.args = proc->argv; 17 | uvproc->uvopts.flags = UV_PROCESS_DETACHED; 18 | uvproc->uvopts.exit_cb = exit_cb; 19 | uvproc->uvopts.cwd = proc->cwd; 20 | uvproc->uvopts.env = NULL; 21 | uvproc->uvopts.stdio = uvproc->uvstdio; 22 | uvproc->uvopts.stdio_count = 3; 23 | uvproc->uvstdio[0].flags = UV_IGNORE; 24 | uvproc->uvstdio[1].flags = UV_IGNORE; 25 | uvproc->uvstdio[2].flags = UV_IGNORE; 26 | uvproc->uv.data = proc; 27 | 28 | if (proc->in) { 29 | uvproc->uvstdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE; 30 | uvproc->uvstdio[0].data.stream = (uv_stream_t *)&proc->in->uv.pipe; 31 | } 32 | 33 | if (proc->out) { 34 | uvproc->uvstdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; 35 | uvproc->uvstdio[1].data.stream = (uv_stream_t *)&proc->out->uv.pipe; 36 | } 37 | 38 | if (proc->err) { 39 | uvproc->uvstdio[2].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; 40 | uvproc->uvstdio[2].data.stream = (uv_stream_t *)&proc->err->uv.pipe; 41 | } 42 | 43 | int status; 44 | if ((status = uv_spawn(&proc->loop->uv, &uvproc->uv, &uvproc->uvopts))) { 45 | log_msg("UV_PROCESS", "uv_spawn failed: %s", uv_strerror(status)); 46 | return false; 47 | } 48 | 49 | proc->pid = uvproc->uv.pid; 50 | return true; 51 | } 52 | 53 | void uv_process_close(UvProcess *uvproc) 54 | { 55 | log_msg("UV_PROCESS", "uv_process_close"); 56 | uv_close((uv_handle_t *)&uvproc->uv, close_cb); 57 | } 58 | 59 | static void close_cb(uv_handle_t *handle) 60 | { 61 | log_msg("UV_PROCESS", "close_cb"); 62 | Process *proc = handle->data; 63 | if (proc->internal_close_cb) { 64 | proc->internal_close_cb(proc); 65 | } 66 | } 67 | 68 | static void exit_cb(uv_process_t *handle, int64_t status, int term_signal) 69 | { 70 | log_msg("UV_PROCESS", "exit_cb"); 71 | Process *proc = handle->data; 72 | proc->status = (int)status; 73 | if (proc->fast_output) 74 | proc->fast_output(proc, status, proc->data); 75 | proc->internal_exit_cb(proc); 76 | } 77 | -------------------------------------------------------------------------------- /src/nav/event/uv_process.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_EVENT_UV_PROCESS_H 2 | #define NV_EVENT_UV_PROCESS_H 3 | 4 | #include 5 | #include "nav/event/process.h" 6 | 7 | typedef struct uv_process { 8 | Process process; 9 | uv_process_t uv; 10 | uv_process_options_t uvopts; 11 | uv_stdio_container_t uvstdio[3]; 12 | } UvProcess; 13 | 14 | static inline UvProcess uv_process_init(Loop *loop, void *data) 15 | { 16 | UvProcess rv; 17 | rv.process = process_init(loop, data); 18 | return rv; 19 | } 20 | 21 | bool uv_process_spawn(UvProcess *uvproc); 22 | void uv_process_close(UvProcess *uvproc); 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /src/nav/event/wstream.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "nav/event/wstream.h" 6 | #include "nav/log.h" 7 | 8 | #define DEFAULT_MAXMEM 1024 * 1024 * 10 9 | static void write_cb(uv_write_t *req, int status); 10 | 11 | typedef struct { 12 | Stream *stream; 13 | WBuffer *buffer; 14 | uv_write_t uv_req; 15 | } WRequest; 16 | 17 | void wstream_init_fd(Loop *loop, Stream *stream, int fd, size_t maxmem, 18 | void *data) 19 | { 20 | log_msg("WSTREAM", "rstream_init_fd"); 21 | stream_init(loop, stream, fd, NULL, data); 22 | wstream_init(stream, maxmem); 23 | } 24 | 25 | void wstream_init_stream(Stream *stream, uv_stream_t *uvstream, size_t maxmem, 26 | void *data) 27 | { 28 | log_msg("WSTREAM", "rstream_init_stream"); 29 | stream_init(NULL, stream, -1, uvstream, data); 30 | wstream_init(stream, maxmem); 31 | } 32 | 33 | void wstream_init(Stream *stream, size_t maxmem) 34 | { 35 | log_msg("WSTREAM", "init"); 36 | stream->maxmem = maxmem ? maxmem : DEFAULT_MAXMEM; 37 | } 38 | 39 | /// Sets a callback that will be called on completion of a write request, 40 | /// indicating failure/success. 41 | /// 42 | /// This affects all requests currently in-flight as well. Overwrites any 43 | /// possible earlier callback. 44 | /// 45 | /// @note This callback will not fire if the write request couldn't even be 46 | /// queued properly (i.e.: when `wstream_write() returns an error`). 47 | /// 48 | /// @param stream The `Stream` instance 49 | /// @param cb The callback 50 | void wstream_set_write_cb(Stream *stream, stream_write_cb cb) 51 | { 52 | log_msg("WSTREAM", "wstream_set_write_cb"); 53 | stream->write_cb = cb; 54 | } 55 | 56 | /// Queues data for writing to the backing file descriptor of a `Stream` 57 | /// instance. This will fail if the write would cause the Stream use more 58 | /// memory than specified by `maxmem`. 59 | /// 60 | /// @param stream The `Stream` instance 61 | /// @param buffer The buffer which contains data to be written 62 | /// @return false if the write failed 63 | bool wstream_write(Stream *stream, WBuffer *buffer) 64 | { 65 | log_msg("WSTREAM", "wstream_write"); 66 | if (stream->curmem > stream->maxmem) { 67 | goto err; 68 | } 69 | 70 | stream->curmem += buffer->size; 71 | 72 | WRequest *data = malloc(sizeof(WRequest)); 73 | data->stream = stream; 74 | data->buffer = buffer; 75 | data->uv_req.data = data; 76 | 77 | uv_buf_t uvbuf; 78 | uvbuf.base = buffer->data; 79 | uvbuf.len = buffer->size; 80 | 81 | if (uv_write(&data->uv_req, stream->uvstream, &uvbuf, 1, write_cb)) { 82 | free(data); 83 | goto err; 84 | } 85 | 86 | stream->pending_reqs++; 87 | return true; 88 | 89 | err: 90 | wstream_release_wbuffer(buffer); 91 | return false; 92 | } 93 | 94 | /// Creates a WBuffer object for holding output data. Instances of this 95 | /// object can be reused across Stream instances, and the memory is freed 96 | /// automatically when no longer needed(it tracks the number of references 97 | /// internally) 98 | /// 99 | /// @param data Data stored by the WBuffer 100 | /// @param size The size of the data array 101 | /// @param refcount The number of references for the WBuffer. This will be used 102 | /// by Stream instances to decide when a WBuffer should be freed. 103 | /// @param cb Pointer to function that will be responsible for freeing 104 | /// the buffer data(passing 'free' will work as expected). 105 | /// @return The allocated WBuffer instance 106 | WBuffer* wstream_new_buffer(char *data, size_t size, size_t refcount, 107 | wbuffer_data_finalizer cb) 108 | { 109 | log_msg("WSTREAM", "wstream_new_buffer"); 110 | WBuffer *rv = malloc(sizeof(WBuffer)); 111 | rv->size = size; 112 | rv->refcount = refcount; 113 | rv->cb = cb; 114 | rv->data = data; 115 | 116 | return rv; 117 | } 118 | 119 | static void write_cb(uv_write_t *req, int status) 120 | { 121 | log_msg("WSTREAM", "write_cb"); 122 | WRequest *data = req->data; 123 | 124 | data->stream->curmem -= data->buffer->size; 125 | 126 | wstream_release_wbuffer(data->buffer); 127 | 128 | if (data->stream->write_cb) { 129 | data->stream->write_cb(data->stream, data->stream->data, status); 130 | } 131 | 132 | data->stream->pending_reqs--; 133 | 134 | if (data->stream->closed && data->stream->pending_reqs == 0) { 135 | // Last pending write, free the stream; 136 | stream_close_handle(data->stream); 137 | } 138 | 139 | free(data); 140 | } 141 | 142 | void wstream_release_wbuffer(WBuffer *buffer) 143 | { 144 | if (!--buffer->refcount) { 145 | if (buffer->cb) { 146 | buffer->cb(buffer->data); 147 | } 148 | 149 | free(buffer); 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/nav/event/wstream.h: -------------------------------------------------------------------------------- 1 | #ifndef NVIM_EVENT_WSTREAM_H 2 | #define NVIM_EVENT_WSTREAM_H 3 | 4 | #include "nav/event/stream.h" 5 | 6 | typedef struct wbuffer WBuffer; 7 | typedef void (*wbuffer_data_finalizer)(void *data); 8 | 9 | struct wbuffer { 10 | size_t size, refcount; 11 | char *data; 12 | wbuffer_data_finalizer cb; 13 | }; 14 | 15 | void wstream_init_fd(Loop *loop, Stream *stream, int fd, size_t maxmem, 16 | void *data); 17 | void wstream_init_stream(Stream *stream, uv_stream_t *uvstream, size_t maxmem, 18 | void *data); 19 | void wstream_init(Stream *stream, size_t maxmem); 20 | void wstream_set_write_cb(Stream *stream, stream_write_cb cb); 21 | bool wstream_write(Stream *stream, WBuffer *buffer); 22 | WBuffer* wstream_new_buffer(char *data, size_t size, size_t refcount, 23 | wbuffer_data_finalizer cb); 24 | void wstream_release_wbuffer(WBuffer *buffer); 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /src/nav/expand.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "nav/tui/window.h" 3 | #include "nav/plugins/op/op.h" 4 | #include "nav/expand.h" 5 | #include "nav/log.h" 6 | #include "nav/util.h" 7 | #include "nav/option.h" 8 | #include "nav/table.h" 9 | #include "nav/model.h" 10 | #include "nav/event/fs.h" 11 | #include "nav/tui/select.h" 12 | 13 | enum fm_fmt { 14 | FM_NORM, 15 | FM_EXT, 16 | FM_NAME, 17 | FM_GROUP, 18 | FM_KIND, 19 | }; 20 | 21 | static varg_T bu_type(char ch) 22 | { 23 | varg_T arg = {}; 24 | if (ch == 'b') { 25 | arg.argc = 1; 26 | arg.argv = malloc(sizeof(char*)); 27 | asprintf(&arg.argv[0], "%d", buf_id(window_get_focus())); 28 | } 29 | else if (ch == 'B') { 30 | Plugin *plug = window_get_plugin(); 31 | arg.argc = 1; 32 | arg.argv = malloc(sizeof(char*)); 33 | arg.argv[0] = strdup(plug->name); 34 | } 35 | return arg; 36 | } 37 | 38 | static char* fm_ext(void *arg) 39 | { 40 | return strdup(file_ext(arg)); 41 | } 42 | 43 | static char* fm_name(void *arg) 44 | { 45 | return (char*)file_base(strdup(arg)); 46 | } 47 | 48 | static char* fm_group(void *arg) 49 | { 50 | nv_syn *syn = get_syn(file_ext(arg)); 51 | if (syn) 52 | return strdup(syn->group->key); 53 | else 54 | return strdup(""); 55 | } 56 | 57 | static char* fm_kind(void *arg) 58 | { 59 | struct stat *sb = arg; 60 | return strdup(stat_kind(sb)); 61 | } 62 | 63 | static varg_T fm_type(const char *name, enum fm_fmt fmt) 64 | { 65 | Buffer *buf = window_get_focus(); 66 | select_cb cb = NULL; 67 | 68 | switch (fmt) { 69 | case FM_EXT: cb = fm_ext; break; 70 | case FM_NAME: cb = fm_name; break; 71 | case FM_GROUP: cb = fm_group; break; 72 | case FM_KIND: cb = fm_kind; break; 73 | default: 74 | break; 75 | } 76 | return buf_select(buf, name, cb); 77 | } 78 | 79 | static varg_T tbl_type(const char *name) 80 | { 81 | Buffer *buf = window_get_focus(); 82 | return buf_select(buf, name, NULL); 83 | } 84 | 85 | static varg_T op_type(const char *name) 86 | { 87 | varg_T arg = {}; 88 | nv_group *grp = op_active_group(); 89 | if (!name || !grp) 90 | return arg; 91 | 92 | int isfld = !strcmp(name, "prev") || !strcmp(name, "next"); 93 | Ventry *vent = fnd_val("op_procs", "group", grp->key); 94 | 95 | if (isfld && vent) { 96 | Ventry *head = ent_head(vent); 97 | char *ret = rec_fld(head->rec, "pid"); 98 | arg.argc = 1; 99 | arg.argv = malloc(sizeof(char*)); 100 | arg.argv[0] = strdup(ret); 101 | } 102 | 103 | return arg; 104 | } 105 | 106 | static char* human_time(time_t const *t) 107 | { 108 | static char str[80]; 109 | struct tm *tm = localtime(t); 110 | if (!tm) 111 | return strdup("*** invalid date/time ***"); 112 | 113 | strftime(str, sizeof(str), "%Y-%m-%d %H:%M:%S", tm); 114 | return strdup(str); 115 | } 116 | 117 | //TODO: modify buf_select for extra data field 118 | static varg_T stat_type(const char *name) 119 | { 120 | varg_T arg = {}; 121 | Buffer *buf = window_get_focus(); 122 | if (!buf_attached(buf)) 123 | return arg; 124 | 125 | struct stat *sb = model_curs_value(buf->hndl->model, "stat"); 126 | if (!sb) 127 | return arg; 128 | 129 | arg.argc = 1; 130 | arg.argv = malloc(sizeof(char*)); 131 | 132 | if (!strcmp("mtime", name)) 133 | arg.argv[0] = human_time(&sb->st_mtim.tv_sec); 134 | else if (!strcmp("ctime", name)) 135 | arg.argv[0] = human_time(&sb->st_ctim.tv_sec); 136 | else if (!strcmp("atime", name)) 137 | arg.argv[0] = human_time(&sb->st_atim.tv_sec); 138 | else if (!strcmp("mode", name)) 139 | asprintf(&arg.argv[0], "%o", sb->st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)); 140 | else if (!strcmp("inode", name)) 141 | asprintf(&arg.argv[0], "%ld", sb->st_ino); 142 | else if (!strcmp("device", name)) 143 | asprintf(&arg.argv[0], "%ld", sb->st_dev); 144 | else if (!strcmp("nlink", name)) 145 | asprintf(&arg.argv[0], "%ld", sb->st_nlink); 146 | else if (!strcmp("size", name)) 147 | asprintf(&arg.argv[0], "%ld", sb->st_size); 148 | else 149 | arg.argv[0] = strdup(""); 150 | 151 | return arg; 152 | } 153 | 154 | static varg_T proc_type(const char *name) 155 | { 156 | varg_T arg = {}; 157 | if (name[1]) 158 | return arg; 159 | 160 | arg.argc = 1; 161 | arg.argv = malloc(sizeof(char*)); 162 | switch (*name) { 163 | case '%': 164 | asprintf(&arg.argv[0], "%d", getpid()); 165 | break; 166 | case '!': 167 | arg.argv[0] = op_pid_last(); 168 | break; 169 | case '?': 170 | arg.argv[0] = op_status_last(); 171 | break; 172 | } 173 | return arg; 174 | } 175 | 176 | static varg_T get_type(const char *key, const char *alt) 177 | { 178 | switch (*key) { 179 | case 'b': 180 | case 'B': 181 | return bu_type(*key); 182 | case 'f': 183 | return fm_type("fullpath", FM_NORM); 184 | case 'n': 185 | return fm_type("name", FM_NORM); 186 | case 'N': 187 | return fm_type("name", FM_NAME); 188 | case 'e': 189 | return fm_type("name", FM_EXT); 190 | case 'd': 191 | return fm_type("dir", FM_NORM); 192 | case 't': 193 | return fm_type("name", FM_GROUP); 194 | case 'k': 195 | return fm_type("stat", FM_KIND); 196 | case '_': 197 | return tbl_type(alt); 198 | case 'o': 199 | return op_type(alt); 200 | case 's': 201 | return stat_type(alt); 202 | case '!': 203 | case '?': 204 | case '%': 205 | return proc_type(key); 206 | default: 207 | return (varg_T){}; 208 | } 209 | } 210 | 211 | char* yank_symbol(const char *key, const char *alt) 212 | { 213 | varg_T ret = get_type(key, alt); 214 | if (!ret.argc) 215 | return strdup("''"); 216 | char *str = lines2yank(ret.argc, ret.argv); 217 | del_param_list(ret.argv, ret.argc); 218 | return str; 219 | } 220 | 221 | char* expand_symbol(const char *key, const char *alt) 222 | { 223 | log_msg("EXPAND", "expand symb {%s:%s}", key, alt); 224 | 225 | varg_T ret = get_type(key, alt); 226 | if (!ret.argc) 227 | return strdup("''"); 228 | char *str = lines2argv(ret.argc, ret.argv); 229 | del_param_list(ret.argv, ret.argc); 230 | return str; 231 | } 232 | -------------------------------------------------------------------------------- /src/nav/expand.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_EXPAND_H 2 | #define NV_EXPAND_H 3 | 4 | char* yank_symbol(const char *, const char *); 5 | char* expand_symbol(const char *, const char *); 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /src/nav/filter.c: -------------------------------------------------------------------------------- 1 | #include "nav/filter.h" 2 | #include "nav/table.h" 3 | #include "nav/model.h" 4 | #include "nav/tui/buffer.h" 5 | #include "nav/log.h" 6 | 7 | struct Filter { 8 | Handle *hndl; 9 | Pattern *pat; 10 | char *line; 11 | }; 12 | 13 | Filter* filter_new(Handle *hndl) 14 | { 15 | Filter *fil = malloc(sizeof(Filter)); 16 | fil->hndl = hndl; 17 | fil->pat = NULL; 18 | fil->line = strdup(""); 19 | return fil; 20 | } 21 | 22 | void filter_destroy(Handle *hndl) 23 | { 24 | Filter *fil = hndl->buf->filter; 25 | if (fil->pat) 26 | regex_pat_delete(fil->pat); 27 | free(fil->line); 28 | free(fil); 29 | } 30 | 31 | void filter_build(Filter *fil, const char *line) 32 | { 33 | log_msg("FILTER", "build"); 34 | if (fil->pat) 35 | regex_pat_delete(fil->pat); 36 | 37 | fil->pat = regex_pat_new(line); 38 | SWAP_ALLOC_PTR(fil->line, strdup(line)); 39 | 40 | Model *m = fil->hndl->model; 41 | 42 | model_clear_filter(m); 43 | 44 | int max = model_count(m); 45 | int count = 0; 46 | for (int i = max; i > 0; i--) { 47 | char *str = model_str_line(m, i-1); 48 | if (!regex_match(fil->pat, str)) { 49 | model_filter_line(m, i-1); 50 | count++; 51 | } 52 | } 53 | buf_signal_filter(fil->hndl->buf, count); 54 | } 55 | 56 | void filter_update(Filter *fil) 57 | { 58 | Model *m = fil->hndl->model; 59 | model_sort(m); 60 | } 61 | 62 | void filter_apply(Handle *hndl) 63 | { 64 | Filter *fil = hndl->buf->filter; 65 | if (strlen(fil->line) < 1) 66 | return; 67 | filter_build(fil, fil->line); 68 | } 69 | -------------------------------------------------------------------------------- /src/nav/filter.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_FILTER_H 2 | #define NV_FILTER_H 3 | 4 | #include "nav/plugins/plugin.h" 5 | 6 | typedef struct Filter Filter; 7 | 8 | Filter* filter_new(Handle *); 9 | void filter_destroy(Handle *); 10 | void filter_build(Filter *, const char *); 11 | void filter_update(Filter *); 12 | void filter_apply(Handle *); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /src/nav/info.c: -------------------------------------------------------------------------------- 1 | #include "nav/lib/uthash.h" 2 | 3 | #include "nav/info.h" 4 | #include "nav/ascii.h" 5 | #include "nav/log.h" 6 | #include "nav/compl.h" 7 | #include "nav/tui/history.h" 8 | #include "nav/tui/ex_cmd.h" 9 | 10 | typedef struct nv_mark nv_mark; 11 | struct nv_mark { 12 | char *key; 13 | char *path; 14 | UT_hash_handle hh; 15 | }; 16 | 17 | static nv_mark *lbl_marks; 18 | static nv_mark *chr_marks; 19 | 20 | void info_parse(char *line) 21 | { 22 | char *label; 23 | char *dir; 24 | switch (line[0]) { 25 | case '@': 26 | label = strtok(line, " "); 27 | dir = strtok(NULL, " "); 28 | mark_label_dir(label, dir); 29 | break; 30 | case '\'': 31 | label = strtok(line, " "); 32 | dir = strtok(NULL, " "); 33 | mark_strchr_str(label, dir); 34 | break; 35 | case ':': 36 | hist_insert(EX_CMD_STATE, &line[1]); 37 | break; 38 | } 39 | } 40 | 41 | static void mark_del(nv_mark **mrk, nv_mark **tbl) 42 | { 43 | log_msg("INFO", "MARKDEL"); 44 | HASH_DEL(*tbl, *mrk); 45 | free((*mrk)->key); 46 | free((*mrk)->path); 47 | free((*mrk)); 48 | } 49 | 50 | static void write_mark_info(FILE *f, nv_mark *tbl) 51 | { 52 | if (!tbl) 53 | return; 54 | 55 | nv_mark *it, *tmp; 56 | HASH_ITER(hh, tbl, it, tmp) { 57 | fprintf(f, "%s %s\n", it->key, it->path); 58 | mark_del(&it, &tbl); 59 | } 60 | } 61 | 62 | static void write_hist_info(FILE *f, int state) 63 | { 64 | hist_set_state(state); 65 | const char *line = hist_first(); 66 | while(line) { 67 | fprintf(f, "%s%s\n", ":", line); 68 | line = hist_next(); 69 | } 70 | } 71 | 72 | void info_write_file(FILE *file) 73 | { 74 | log_msg("INFO", "config_write_info"); 75 | write_mark_info(file, lbl_marks); 76 | write_mark_info(file, chr_marks); 77 | write_hist_info(file, EX_CMD_STATE); 78 | } 79 | 80 | void mark_list() 81 | { 82 | } 83 | 84 | void marklbl_list() 85 | { 86 | log_msg("INFO", "marklbl_list"); 87 | nv_mark *it; 88 | int i = 0; 89 | for (it = lbl_marks; it != NULL; it = it->hh.next) { 90 | compl_list_add("%s", it->key); 91 | compl_set_col(i, "%s", it->path); 92 | i++; 93 | } 94 | } 95 | 96 | char* mark_path(const char *key) 97 | { 98 | nv_mark *mrk; 99 | HASH_FIND_STR(lbl_marks, key, mrk); 100 | if (mrk) 101 | return mrk->path; 102 | 103 | return NULL; 104 | } 105 | 106 | char* mark_str(int chr) 107 | { 108 | nv_mark *mrk; 109 | char *key; 110 | asprintf(&key, "'%c", chr); 111 | 112 | HASH_FIND_STR(chr_marks, key, mrk); 113 | free(key); 114 | if (mrk) 115 | return mrk->path; 116 | 117 | return NULL; 118 | } 119 | 120 | void mark_label_dir(char *label, const char *dir) 121 | { 122 | log_msg("INFO", "mark_label_dir"); 123 | char *key; 124 | if (label[0] == '@') 125 | label = &label[1]; 126 | asprintf(&key, "@%s", label); 127 | 128 | char *tmp = strdup(dir); 129 | nv_mark *mrk; 130 | HASH_FIND_STR(lbl_marks, key, mrk); 131 | if (mrk) 132 | mark_del(&mrk, &lbl_marks); 133 | 134 | mrk = malloc(sizeof(nv_mark)); 135 | mrk->key = key; 136 | mrk->path = tmp; 137 | HASH_ADD_STR(lbl_marks, key, mrk); 138 | } 139 | 140 | void mark_strchr_str(const char *str, const char *dir) 141 | { 142 | if (strlen(str) == 2) 143 | mark_chr_str(str[1], dir); 144 | } 145 | 146 | void mark_chr_str(int chr, const char *dir) 147 | { 148 | log_msg("INFO", "mark_key_str"); 149 | char *key; 150 | asprintf(&key, "'%c", chr); 151 | 152 | char *tmp = strdup(dir); 153 | nv_mark *mrk; 154 | HASH_FIND_STR(chr_marks, key, mrk); 155 | if (mrk) 156 | mark_del(&mrk, &chr_marks); 157 | 158 | mrk = malloc(sizeof(nv_mark)); 159 | mrk->key = key; 160 | mrk->path = tmp; 161 | HASH_ADD_STR(chr_marks, key, mrk); 162 | } 163 | -------------------------------------------------------------------------------- /src/nav/info.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_INFO_H 2 | #define NV_INFO_H 3 | 4 | #include 5 | #include "nav/nav.h" 6 | 7 | void info_write_file(FILE *file); 8 | void info_parse(char *); 9 | 10 | char* mark_path(const char *); 11 | char* mark_str(int chr); 12 | 13 | void mark_label_dir(char *, const char *); 14 | void mark_strchr_str(const char *, const char *); 15 | void mark_chr_str(int chr, const char *); 16 | 17 | void mark_list(); 18 | void marklbl_list(); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /src/nav/lib/bsearch.h: -------------------------------------------------------------------------------- 1 | #ifndef BSEARCH_H 2 | #define BSEARCH_H 3 | 4 | #include 5 | 6 | static inline void* bsearch_r( 7 | register const void *key, 8 | const void *base0, 9 | size_t nmemb, 10 | register size_t size, 11 | register int (*compar)(const void *, const void *, void *), 12 | void *arg) 13 | { 14 | register const char *base = base0; 15 | register size_t lim; 16 | register int cmp; 17 | register const void *p; 18 | 19 | for (lim = nmemb; lim != 0; lim >>= 1) { 20 | p = base + (lim >> 1) * size; 21 | cmp = (*compar)(key, p, arg); 22 | if (cmp == 0) 23 | return ((void *)p); 24 | if (cmp > 0) { 25 | base = (char *)p + size; 26 | lim--; 27 | } 28 | } 29 | return (NULL); 30 | } 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /src/nav/lib/map.h: -------------------------------------------------------------------------------- 1 | #ifndef MAP_H 2 | #define MAP_H 3 | 4 | #include 5 | 6 | typedef struct Map Map; 7 | 8 | /* Allocate a new map. */ 9 | Map *map_new(void); 10 | /* Retrieves a value, or NULL if it isn't in the map */ 11 | void *map_get(const Map*, const char *key); 12 | /* Returns the corresponding value if the given prefix is unique. 13 | * Otherwise NULL, if no such prefix exists then errno is set to ENOENT. */ 14 | void *map_closest(const Map*, const char *prefix); 15 | /* check whether the map contains the given prefix, i.e. whether it can 16 | * be extended to match a key of an element stored in the map. */ 17 | bool map_contains(const Map*, const char *prefix); 18 | /* Place a member in the map. This returns false if we run out of memory 19 | * (errno = ENOMEM), or if that key already appears in the map (errno = EEXIST). */ 20 | bool map_put(Map*, const char *key, const void *value); 21 | /* Remove a member from the map. Returns the removed entry or NULL 22 | * if there was no entry found using the given key*/ 23 | void *map_delete(Map*, const char *key); 24 | /* Copy all entries from `src' into `dest', overwrites existing entries in `dest' */ 25 | bool map_copy(Map *dest, Map *src); 26 | /* Ordered iteration over a map, call handle for every entry. If handle 27 | * returns false, the iteration will stop. */ 28 | void map_iterate(const Map*, bool (*handle)(const char *key, void *value, void *data), const void *data); 29 | /* Return a submap matching a prefix. This returns a pointer into the 30 | * original map, so don't alter the map while using the return value. */ 31 | const Map *map_prefix(const Map*, const char *prefix); 32 | /* Test whether the map is empty i.e. contains no elements */ 33 | bool map_empty(const Map*); 34 | /* Remove every member from the map. The map will be empty after this. */ 35 | void map_clear(Map*); 36 | /* Release all memory associated with this map */ 37 | void map_free(Map*); 38 | /* Call free(3) for every pointer stored in the map, then free the map itself */ 39 | void map_free_full(Map*); 40 | bool map_multi_suffix(const Map *n); 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /src/nav/lib/queue.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2013, Ben Noordhuis 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef QUEUE_H_ 17 | #define QUEUE_H_ 18 | 19 | typedef void *QUEUE[2]; 20 | 21 | /* Private macros. */ 22 | #define QUEUE_NEXT(q) ((*(q))[0]) 23 | #define QUEUE_PREV(q) ((*(q))[1]) 24 | #define QUEUE_PREV_NEXT(q) (QUEUE_NEXT((QUEUE *) QUEUE_PREV(q))) 25 | #define QUEUE_NEXT_PREV(q) (QUEUE_PREV((QUEUE *) QUEUE_NEXT(q))) 26 | 27 | /* Public macros. */ 28 | #define QUEUE_DATA(ptr, type, field) \ 29 | ((type *) ((char *) (ptr) - ((long) &((type *) 0)->field))) 30 | 31 | #define QUEUE_FOREACH(q, h) \ 32 | for ((q) = (QUEUE *) (*(h))[0]; (q) != (h); (q) = (QUEUE *) (*(q))[0]) 33 | 34 | #define QUEUE_EMPTY(q) \ 35 | (QUEUE_NEXT(q) == (q)) 36 | 37 | #define QUEUE_HEAD(q) \ 38 | (QUEUE_NEXT(q)) 39 | 40 | #define QUEUE_INIT(q) \ 41 | do { \ 42 | QUEUE_NEXT(q) = (q); \ 43 | QUEUE_PREV(q) = (q); \ 44 | } \ 45 | while (0) 46 | 47 | #define QUEUE_ADD(h, n) \ 48 | do { \ 49 | QUEUE_PREV_NEXT(h) = QUEUE_NEXT(n); \ 50 | QUEUE_NEXT_PREV(n) = QUEUE_PREV(h); \ 51 | QUEUE_PREV(h) = QUEUE_PREV(n); \ 52 | QUEUE_PREV_NEXT(h) = (h); \ 53 | } \ 54 | while (0) 55 | 56 | #define QUEUE_SPLIT(h, q, n) \ 57 | do { \ 58 | QUEUE_PREV(n) = QUEUE_PREV(h); \ 59 | QUEUE_PREV_NEXT(n) = (n); \ 60 | QUEUE_NEXT(n) = (q); \ 61 | QUEUE_PREV(h) = QUEUE_PREV(q); \ 62 | QUEUE_PREV_NEXT(h) = (h); \ 63 | QUEUE_PREV(q) = (n); \ 64 | } \ 65 | while (0) 66 | 67 | #define QUEUE_INSERT_HEAD(h, q) \ 68 | do { \ 69 | QUEUE_NEXT(q) = QUEUE_NEXT(h); \ 70 | QUEUE_PREV(q) = (h); \ 71 | QUEUE_NEXT_PREV(q) = (q); \ 72 | QUEUE_NEXT(h) = (q); \ 73 | } \ 74 | while (0) 75 | 76 | #define QUEUE_INSERT_TAIL(h, q) \ 77 | do { \ 78 | QUEUE_NEXT(q) = (h); \ 79 | QUEUE_PREV(q) = QUEUE_PREV(h); \ 80 | QUEUE_PREV_NEXT(q) = (q); \ 81 | QUEUE_PREV(h) = (q); \ 82 | } \ 83 | while (0) 84 | 85 | #define QUEUE_REMOVE(q) \ 86 | do { \ 87 | QUEUE_PREV_NEXT(q) = QUEUE_NEXT(q); \ 88 | QUEUE_NEXT_PREV(q) = QUEUE_PREV(q); \ 89 | } \ 90 | while (0) 91 | 92 | #endif /* QUEUE_H_ */ 93 | -------------------------------------------------------------------------------- /src/nav/log.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "nav/log.h" 7 | 8 | #define _C "\033[33m" 9 | #define _R "\033[0m" 10 | #define _E "\033[31m" 11 | #define LOG_FILENAME ".navlog" 12 | 13 | static FILE *log_file; 14 | static int enabled; 15 | static char* listen_obj; 16 | 17 | void log_init() 18 | { 19 | enabled = 1; 20 | if (log_file) 21 | return; 22 | char *name = getenv("HOME"); 23 | char *path; 24 | asprintf(&path, "%s/%s", name, LOG_FILENAME); 25 | log_file = fopen(path, "w"); 26 | free(path); 27 | } 28 | 29 | void log_cleanup() 30 | { 31 | if (listen_obj) 32 | free(listen_obj); 33 | log_file = NULL; 34 | } 35 | 36 | void log_set_file(const char *path) 37 | { 38 | log_file = fopen(path, "w"); 39 | } 40 | 41 | void log_set_group(const char *obj) 42 | { 43 | listen_obj = strdup(obj); 44 | } 45 | 46 | void log_msg(const char *obj, const char *fmt, ...) 47 | { 48 | if (!enabled) return; 49 | if (listen_obj && strcasecmp(listen_obj, obj) != 0) 50 | return; 51 | 52 | fprintf(log_file, "[" _C "%s" _R "] ", obj); 53 | va_list args; 54 | va_start(args, fmt); 55 | vfprintf(log_file, fmt, args); 56 | va_end(args); 57 | fputc('\n', log_file); 58 | fflush(log_file); 59 | } 60 | 61 | void log_err(const char *obj, const char *fmt, ...) 62 | { 63 | if (!enabled) return; 64 | if (listen_obj && strcasecmp(listen_obj, obj) != 0) 65 | return; 66 | 67 | fprintf(log_file, "[" _E "%s" _R "] ", obj); 68 | va_list args; 69 | va_start(args, fmt); 70 | vfprintf(log_file, fmt, args); 71 | va_end(args); 72 | fputc('\n', log_file); 73 | fflush(log_file); 74 | } 75 | -------------------------------------------------------------------------------- /src/nav/log.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_LOG_H 2 | #define NV_LOG_H 3 | 4 | void log_init(); 5 | void log_cleanup(); 6 | void log_set_file(const char *); 7 | void log_set_group(const char *); 8 | void log_msg(const char *obj, const char *fmt, ...) \ 9 | __attribute__((format (printf, 2, 3))); 10 | void log_err(const char *obj, const char *fmt, ...) \ 11 | __attribute__((format (printf, 2, 3))); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /src/nav/macros.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_MACROS_H 2 | #define NV_MACROS_H 3 | 4 | /// Calculate the length of a C array. 5 | /// 6 | /// This should be called with a real array. Calling this with a pointer is an 7 | /// error. A mechanism to detect many (though not all) of those errors at compile 8 | /// time is implemented. It works by the second division producing a division by 9 | /// zero in those cases (-Wdiv-by-zero in GCC). 10 | #define LENGTH(arr) ((sizeof(arr)/sizeof((arr)[0])) / \ 11 | ((size_t)(!(sizeof(arr) % sizeof((arr)[0]))))) 12 | 13 | #define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) 14 | #define MAX(X, Y) ((X) > (Y) ? (X) : (Y)) 15 | 16 | #define SWAP(t, a, b) \ 17 | do { t __tmp = (a); (a) = (b); (b) = __tmp; } while (0) 18 | 19 | #define TOUPPER_ASC(c) (((c) < 'a' || (c) > 'z') ? (c) : (c) - ('a' - 'A')) 20 | #define TOLOWER_ASC(c) (((c) < 'A' || (c) > 'Z') ? (c) : (c) + ('a' - 'A')) 21 | 22 | #define DRAW_CH(obj,win,ln,col,str,color) \ 23 | do { \ 24 | wattron((obj)->win, COLOR_PAIR((obj)->color)); \ 25 | mvwaddch((obj)->win, ln, col, str); \ 26 | wattroff((obj)->win, COLOR_PAIR((obj)->color)); \ 27 | } while (0) 28 | 29 | #define SWAP_ALLOC_PTR(a,b) \ 30 | do { \ 31 | char *_tmp_str = (b); \ 32 | free(a); \ 33 | (a) = _tmp_str; \ 34 | } while (0) \ 35 | 36 | #define BITMASK_CHECK(x,y) (((x) & (y)) == (x)) 37 | #define ISDIGIT(c) ((unsigned) (c) - '0' <= 9) 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /src/nav/model.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_MODEL_H 2 | #define NV_MODEL_H 3 | 4 | #include "nav/table.h" 5 | 6 | typedef struct nv_line nv_line; 7 | 8 | void model_init(Handle *hndl); 9 | void model_cleanup(Handle *hndl); 10 | void model_open(Handle *hndl); 11 | void model_close(Handle *hndl); 12 | bool model_blocking(Handle *hndl); 13 | 14 | void model_ch_focus(Handle *); 15 | void model_set_sort(Model *m, char *, bool); 16 | void model_sort(Model *m); 17 | void model_flush(Handle *, bool); 18 | void model_recv(Model *m); 19 | void refind_line(Model *m); 20 | 21 | void model_read_entry(Model *m, TblLis *lis, Ventry *head); 22 | void model_null_entry(Model *m, TblLis *lis); 23 | void model_full_entry(Model *m, TblLis *lis); 24 | 25 | char* model_str_line(Model *m, int index); 26 | void* model_fld_line(Model *m, const char *fld, int index); 27 | void* model_curs_value(Model *m, const char *fld); 28 | TblRec* model_rec_line(Model *m, int index); 29 | 30 | void model_clear_filter(Model *m); 31 | void model_filter_line(Model *m, int); 32 | 33 | void model_set_curs(Model *m, int index); 34 | int model_count(Model *m); 35 | char* model_str_expansion(char* val, char *key); 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /src/nav/nav.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "nav/log.h" 8 | #include "nav/nav.h" 9 | #include "nav/option.h" 10 | #include "nav/config.h" 11 | #include "nav/tui/screen.h" 12 | #include "nav/tui/window.h" 13 | #include "nav/tui/ex_cmd.h" 14 | #include "nav/event/event.h" 15 | #include "nav/event/input.h" 16 | #include "nav/tui/history.h" 17 | #include "nav/table.h" 18 | #include "nav/compl.h" 19 | #include "nav/event/hook.h" 20 | #include "nav/event/shell.h" 21 | #include "nav/event/file.h" 22 | #include "nav/vt/vt.h" 23 | 24 | void init(int debug, char *config_path) 25 | { 26 | log_msg("INIT", "INIT_START"); 27 | if (debug) 28 | log_init(); 29 | 30 | setlocale(LC_CTYPE, ""); 31 | char *term = getenv("TERM"); 32 | 33 | log_msg("INIT", "initscr"); 34 | initscr(); 35 | start_color(); 36 | use_default_colors(); 37 | noecho(); 38 | nonl(); 39 | curs_set(0); 40 | //raw(); 41 | vt_init(term); 42 | 43 | cmd_init(); 44 | option_init(); 45 | tables_init(); 46 | event_init(); 47 | input_init(); 48 | compl_init(); 49 | hook_init(); 50 | ex_cmd_init(); 51 | window_init(); 52 | file_init(); 53 | 54 | config_init(); 55 | hist_init(); 56 | config_load_info(); 57 | config_load(config_path); 58 | 59 | screen_init(); 60 | cmd_sort_cmds(); 61 | log_msg("INIT", "INIT_END"); 62 | } 63 | 64 | void cleanup(void) 65 | { 66 | log_msg("CLEANUP", "CLEANUP_START"); 67 | ex_cmd_cleanup(); 68 | hist_cleanup(); 69 | file_cleanup(); 70 | window_cleanup(); 71 | hook_cleanup(); 72 | compl_cleanup(); 73 | input_cleanup(); 74 | event_cleanup(); 75 | tables_cleanup(); 76 | option_cleanup(); 77 | cmd_cleanup(); 78 | vt_shutdown(); 79 | endwin(); 80 | log_msg("CLEANUP", "CLEANUP_END"); 81 | log_cleanup(); 82 | } 83 | 84 | static const char* usage = 85 | "Usage: nav [options] [command]\n" 86 | "\n" 87 | " -c, --config= Specify an alternative config file.\n" 88 | " -l, --log-file= Specify an alternative log file (Requires -d).\n" 89 | " -V, --verbose= Verbose mode for a specific log group.\n" 90 | " -d, --debug Debug mode.\n" 91 | " -v, --version Print version information and exit.\n" 92 | " -h, --help Print this help message and exit.\n" 93 | "\n"; 94 | 95 | static struct option long_options[] = { 96 | {"config", required_argument, NULL, 'c'}, 97 | {"log-file", required_argument, NULL, 'l'}, 98 | {"verbose", required_argument, NULL, 'V'}, 99 | {"debug", no_argument, NULL, 'd'}, 100 | {"version", no_argument, NULL, 'v'}, 101 | {"help", no_argument, NULL, 'h'}, 102 | }; 103 | 104 | int main(int argc, char **argv) 105 | { 106 | int debug = 0; 107 | char *config_path = NULL; 108 | 109 | for (int i = 0; i < argc; i++) { 110 | int option_index = 0; 111 | int c = getopt_long(argc, argv, "c:l:V:dvh", long_options, &option_index); 112 | if (c < 0) 113 | continue; 114 | 115 | switch (c) { 116 | case 'h': 117 | fprintf(stdout, "%s", usage); 118 | exit(EXIT_SUCCESS); 119 | break; 120 | case 'c': 121 | config_path = optarg; 122 | break; 123 | case 'l': 124 | debug = 1; 125 | log_set_file(optarg); 126 | break; 127 | case 'd': 128 | debug = 1; 129 | break; 130 | case 'V': 131 | debug = 1; 132 | log_set_group(optarg); 133 | break; 134 | case 'v': 135 | fprintf(stdout, "%s\n", NAV_LONG_VERSION); 136 | exit(EXIT_SUCCESS); 137 | break; 138 | default: 139 | fprintf(stderr, "%s", usage); 140 | exit(EXIT_FAILURE); 141 | } 142 | } 143 | 144 | init(debug, config_path); 145 | start_event_loop(); 146 | DO_EVENTS_UNTIL(!mainloop_busy()); 147 | config_write_info(); 148 | cleanup(); 149 | } 150 | -------------------------------------------------------------------------------- /src/nav/nav.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_NAV_H 2 | #define NV_NAV_H 3 | 4 | #define STR_(x) #x 5 | #define STR(x) STR_(x) 6 | 7 | #ifndef NAV_VERSION 8 | #define NAV_VERSION "unknown" 9 | #define NAV_DATE "unknown" 10 | #endif 11 | #ifndef NAV_LONG_VERSION 12 | #define NAV_LONG_VERSION "NAV-"NAV_VERSION 13 | #endif 14 | 15 | typedef struct Token Token; 16 | typedef struct Pair Pair; 17 | typedef struct Dict Dict; 18 | typedef struct List List; 19 | typedef struct Cmdstr Cmdstr; 20 | typedef struct Cmdline Cmdline; 21 | typedef struct Cmdret Cmdret; 22 | 23 | #include 24 | #include "nav/macros.h" 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /src/nav/option.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_OPTION_H 2 | #define NV_OPTION_H 3 | 4 | #include "nav/nav.h" 5 | #include "nav/lib/uthash.h" 6 | #include "nav/lib/utarray.h" 7 | #include "nav/plugins/op/op.h" 8 | 9 | extern char *p_xc; /* 'clipboard cmd' */ 10 | 11 | typedef struct nv_group nv_group; 12 | typedef struct nv_module nv_module; 13 | typedef struct nv_syn nv_syn; 14 | typedef struct nv_block nv_block; 15 | 16 | struct nv_group { 17 | UT_hash_handle hh; 18 | char *key; 19 | short colorpair; 20 | Op_group *opgrp; /* make array if support > 1 binding per group */ 21 | }; 22 | 23 | struct nv_syn { 24 | UT_hash_handle hh; 25 | char *key; 26 | nv_group *group; 27 | }; 28 | 29 | typedef struct { 30 | UT_hash_handle hh; 31 | char *key; 32 | char *var; 33 | } nv_var; 34 | 35 | typedef struct { 36 | UT_hash_handle hh; 37 | char *key; 38 | int argc; 39 | char **argv; 40 | UT_array *lines; 41 | nv_module *module; 42 | } nv_func; 43 | 44 | struct nv_block { 45 | nv_var *vars; 46 | nv_func *fn; 47 | }; 48 | 49 | struct nv_module { 50 | UT_hash_handle hh; 51 | char *key; 52 | char *path; 53 | nv_block blk; 54 | }; 55 | 56 | enum nv_color_group { 57 | BUF_DIR, 58 | BUF_SEL_ACTIVE, 59 | BUF_SEL_INACTIVE, 60 | BUF_STDERR, 61 | BUF_STDOUT, 62 | BUF_SZ, 63 | BUF_TEXT, 64 | COMPL_PARAM, 65 | COMPL_SELECTED, 66 | COMPL_TEXT, 67 | MSG_ASK, 68 | MSG_ERROR, 69 | MSG_MESSAGE, 70 | OVERLAY_ACTIVE, 71 | OVERLAY_ARGS, 72 | OVERLAY_BUFNO, 73 | OVERLAY_FILTER, 74 | OVERLAY_INACTIVE, 75 | OVERLAY_LINE, 76 | OVERLAY_PROGRESS, 77 | OVERLAY_SEP, 78 | OVERLAY_TEXTINACTIVE, 79 | }; 80 | 81 | void option_init(); 82 | void option_cleanup(); 83 | void set_color(nv_group *, int, int); 84 | short opt_color(enum nv_color_group color); 85 | 86 | nv_group* set_group(const char *); 87 | nv_group* get_group(const char *); 88 | void set_syn(nv_syn *); 89 | nv_syn* get_syn(const char *); 90 | int get_syn_colpair(const char *); 91 | 92 | void set_module(nv_module *); 93 | nv_module* get_module(const char *); 94 | 95 | void set_var(nv_var *, nv_block *); 96 | char* opt_var(Token *, nv_block *); 97 | void set_func(nv_func *, nv_block *); 98 | void clear_block(nv_block *); 99 | nv_func* opt_func(const char *, nv_block *); 100 | 101 | void clear_opts(nv_option **); 102 | void add_opt(nv_option **, char *, enum opt_type); 103 | void set_opt(const char *, const char *, int); 104 | char* get_opt_str(const char *); 105 | uint get_opt_uint(const char *); 106 | int get_opt_int(const char *); 107 | void options_list(); 108 | void groups_list(); 109 | 110 | #endif 111 | -------------------------------------------------------------------------------- /src/nav/plugins/dt/dt.c: -------------------------------------------------------------------------------- 1 | #include "nav/plugins/dt/dt.h" 2 | #include "nav/tui/buffer.h" 3 | #include "nav/util.h" 4 | #include "nav/log.h" 5 | #include "nav/model.h" 6 | #include "nav/cmdline.h" 7 | #include "nav/event/fs.h" 8 | #include "nav/event/hook.h" 9 | #include "nav/option.h" 10 | 11 | static void dt_signal_model(void **data) 12 | { 13 | DT *dt = data[0]; 14 | Handle *h = dt->base->hndl; 15 | model_flush(h, true); 16 | model_recv(h->model); 17 | buf_move(h->buf, 0, 0); 18 | } 19 | 20 | static char* read_line(DT *dt, FILE *f) 21 | { 22 | int length = 0, size = 128; 23 | char *string = malloc(size); 24 | 25 | while (1) { 26 | int c = getc(f); 27 | if (c == EOF || c == '\n' || c == '\0') 28 | break; 29 | if (c == '\r') 30 | continue; 31 | 32 | string[length++] = c; 33 | if (length >= size) 34 | string = realloc(string, size *= 2); 35 | } 36 | string[length] = '\0'; 37 | return string; 38 | } 39 | 40 | static void dt_readfile(DT *dt) 41 | { 42 | FILE *f = fopen(dt->path, "rw"); 43 | if (!f) 44 | return; 45 | 46 | Handle *h = dt->base->hndl; 47 | int fcount = tbl_fld_count(h->tn); 48 | Table *t = get_tbl(h->tn); 49 | 50 | while (!feof(f)) { 51 | trans_rec *r = mk_trans_rec(fcount); 52 | char *line = read_line(dt, f); 53 | char *str = line; 54 | char *next = line; 55 | 56 | for (int i = 0; i < fcount; i++) { 57 | if (str && fcount - i > 1) { 58 | next = strchr(str, dt->delm); 59 | if (next) 60 | *next++ = '\0'; 61 | } 62 | char *val = str ? str : ""; 63 | edit_trans(r, tbl_fld(t, i), val, NULL); 64 | str = next; 65 | } 66 | CREATE_EVENT(eventq(), commit, 2, h->tn, r); 67 | free(line); 68 | } 69 | CREATE_EVENT(eventq(), dt_signal_model, 1, dt); 70 | fclose(f); 71 | } 72 | 73 | //new dt [table] [file] [delim:ch] #open table; open file into table 74 | //table[!] name [fields...] #mk or del table 75 | static bool dt_getopts(DT *dt, char *line) 76 | { 77 | bool succ = false; 78 | if (!line) 79 | return succ; 80 | 81 | log_err("DT", "%s", line); 82 | 83 | Handle *h = dt->base->hndl; 84 | dt->delm = ' '; 85 | 86 | Cmdline cmd; 87 | cmdline_build(&cmd, line); 88 | int argidx = 0; 89 | 90 | List *lst = cmdline_lst(&cmd); 91 | char *tn = list_arg(lst, argidx++, VAR_STRING); 92 | if (!get_tbl(tn)) 93 | goto cleanup; 94 | 95 | dt->path = list_arg(lst, argidx++, VAR_STRING); 96 | if (!dt->path) 97 | goto cleanup; 98 | 99 | dt->path = fs_trypath(dt->path); 100 | 101 | Pair *p = list_arg(lst, argidx, VAR_PAIR); 102 | if (p) { 103 | char *val = token_val(&p->value, VAR_STRING); 104 | if (val) 105 | dt->delm = *val; 106 | } 107 | 108 | h->fname = tbl_fld(get_tbl(tn), 0); 109 | h->kname = h->fname; 110 | h->tn = strdup(tn); 111 | h->key_fld = h->fname; 112 | h->key = ""; 113 | succ = true; 114 | cleanup: 115 | cmdline_cleanup(&cmd); 116 | return succ; 117 | } 118 | 119 | static void pipe_cb(Plugin *host, Plugin *caller, HookArg *hka) 120 | { 121 | log_msg("DT", "pipe_cb"); 122 | //TODO: 123 | //buf_select fields -> delimited string -> DT reader 124 | //pipe arg (dict fld:val) -> DT reader 125 | 126 | DT *dt = host->top; 127 | Handle *h = caller->hndl; 128 | Model *m = h->model;; 129 | 130 | int fcount = tbl_fld_count(h->tn); 131 | Table *t = get_tbl(h->tn); 132 | trans_rec *r = mk_trans_rec(fcount); 133 | 134 | for (int i = 0; i < fcount; i++) { 135 | char *fld = tbl_fld(t, i); 136 | char *val = model_curs_value(m, fld); 137 | edit_trans(r, fld, val ? val : "", NULL); 138 | } 139 | 140 | CREATE_EVENT(eventq(), commit, 2, h->tn, r); 141 | CREATE_EVENT(eventq(), dt_signal_model, 1, dt); 142 | } 143 | 144 | static void opt_cb(Plugin *host, Plugin *caller, HookArg *hka) 145 | { 146 | char *name = hka->arg; 147 | log_msg("DT", "opt_cb %s", name); 148 | if (!strcmp(name, "table")) { 149 | char *tbl = get_opt_str("table"); 150 | log_msg("DT", "new table name %s", tbl); 151 | //close table 152 | //reopen file + read 153 | } 154 | } 155 | 156 | void dt_new(Plugin *plugin, Buffer *buf, char *arg) 157 | { 158 | log_msg("DT", "init"); 159 | 160 | DT *dt = calloc(1, sizeof(DT)); 161 | dt->base = plugin; 162 | plugin->top = dt; 163 | plugin->fmt_name = "DT"; 164 | Handle *hndl = calloc(1, sizeof(Handle)); 165 | hndl->buf = buf; 166 | plugin->hndl = hndl; 167 | 168 | add_opt(&plugin->opts, "table", OPTION_STRING); 169 | hook_add_intl(buf->id, plugin, NULL, opt_cb, EVENT_OPTION_SET); 170 | 171 | if (!dt_getopts(dt, arg)) 172 | return buf_set_plugin(buf, plugin, SCR_NULL); 173 | 174 | buf_set_plugin(buf, plugin, SCR_SIMPLE); 175 | buf_set_status(buf, 0, hndl->tn, 0); 176 | 177 | model_init(hndl); 178 | model_open(hndl); 179 | dt_readfile(dt); 180 | hook_add_intl(buf->id, plugin, NULL, pipe_cb, EVENT_PIPE); 181 | } 182 | 183 | void dt_delete(Plugin *plugin) 184 | { 185 | log_msg("DT", "delete"); 186 | DT *dt = plugin->top; 187 | Handle *h = plugin->hndl; 188 | if (h->model) { 189 | model_close(h); 190 | model_cleanup(h); 191 | } 192 | free(dt->path); 193 | free(h->tn); 194 | free(h); 195 | free(dt); 196 | } 197 | -------------------------------------------------------------------------------- /src/nav/plugins/dt/dt.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_PLUGINS_DT_H 2 | #define NV_PLUGINS_DT_H 3 | 4 | #include "nav/plugins/plugin.h" 5 | 6 | typedef struct DT DT; 7 | 8 | struct DT { 9 | Plugin *base; 10 | char *path; 11 | char delm; 12 | }; 13 | 14 | void dt_new(Plugin *plugin, Buffer *buf, char *arg); 15 | void dt_delete(Plugin *plugin); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /src/nav/plugins/ed/ed.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_PLUGINS_ED_H 2 | #define NV_PLUGINS_ED_H 3 | 4 | #include 5 | #include "nav/plugins/plugin.h" 6 | 7 | typedef struct Ed Ed; 8 | 9 | struct Ed { 10 | Plugin *base; 11 | Buffer *buf; 12 | varg_T src; 13 | varg_T dest; 14 | char tmp_name[PATH_MAX]; 15 | int fd; 16 | int state; 17 | }; 18 | 19 | void ed_new(Plugin *plugin, Buffer *buf, char *arg); 20 | void ed_delete(Plugin *plugin); 21 | void ed_close_cb(Plugin *plugin, Ed *ed, bool closed); 22 | void ed_direct_rename(Plugin *caller, char *, char *); 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /src/nav/plugins/fm/fm.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_PLUGINS_FM_H 2 | #define NV_PLUGINS_FM_H 3 | 4 | #include "nav/plugins/plugin.h" 5 | #include "nav/event/fs.h" 6 | 7 | typedef struct jump_item jump_item; 8 | struct jump_item { 9 | char *path; 10 | TAILQ_ENTRY(jump_item) ent; 11 | }; 12 | 13 | typedef struct FM FM; 14 | struct FM { 15 | Plugin *base; 16 | char *cur_dir; 17 | nv_fs *fs; 18 | int jump_count; 19 | int jump_max; 20 | jump_item *jump_cur; 21 | TAILQ_HEAD(jlst, jump_item) p; 22 | }; 23 | 24 | void fm_init(); 25 | void fm_cleanup(); 26 | void fm_new(Plugin *plugin, Buffer *buf, char *arg); 27 | void fm_delete(Plugin *plugin); 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /src/nav/plugins/img/img.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "nav/plugins/img/img.h" 3 | #include "nav/log.h" 4 | #include "nav/table.h" 5 | #include "nav/model.h" 6 | #include "nav/cmdline.h" 7 | #include "nav/event/hook.h" 8 | #include "nav/tui/buffer.h" 9 | #include "nav/tui/layout.h" 10 | #include "nav/event/shell.h" 11 | #include "nav/event/fs.h" 12 | 13 | #define WM_IMG "/usr/lib/w3m/w3mimgdisplay" 14 | 15 | //FORMAT: 0;1;{x};{y};{w};{h};;;;;{filename}\n4;\n3;\n 16 | static const char *DR_ARG = "0;1;%d;%d;%d;%d;;;;;%s\n4;\n3;\n"; 17 | //FORMAT: 6;{x};{y};{w};{h}\n4;\n3;\n 18 | static const char *CL_ARG = "6;%d;%d;%d;%d\n4;\n3;\n"; 19 | //FORMAT: 5;filename 20 | static const char *SZ_ARG = "5;%s\n"; 21 | static const char *const args[] = {WM_IMG, NULL}; 22 | static const char *const t_args[] = {WM_IMG, "-test", NULL}; 23 | static const char *img_exts[] = {"png","jpeg","jpg","gif","bmp"}; 24 | 25 | static void img_draw(Plugin *plugin) 26 | { 27 | log_msg("IMG", "img_draw"); 28 | Img *img = (Img*)plugin->top; 29 | 30 | pos_T pos = buf_ofs(img->buf); 31 | pos_T size = buf_size(img->buf); 32 | 33 | int max_width_pixels = size.col * img->fontw; 34 | int max_height_pixels = size.lnum * img->fonth; 35 | 36 | if (img->width > max_width_pixels) { 37 | img->height = (img->height * max_width_pixels) / img->width; 38 | img->width = max_width_pixels; 39 | } 40 | if (img->height > max_height_pixels) { 41 | img->width = (img->width * max_height_pixels) / img->height; 42 | img->height = max_height_pixels; 43 | } 44 | 45 | //TODO: use buffer size * font, not given image 46 | free(img->cl_msg); 47 | free(img->img_msg); 48 | asprintf(&img->cl_msg, CL_ARG, 49 | pos.col * img->fontw + 1, pos.lnum * img->fonth, 50 | (size.col * img->fontw), (size.lnum * img->fonth)); 51 | asprintf(&img->img_msg, DR_ARG, 52 | pos.col * img->fontw, pos.lnum * img->fonth, 53 | img->width, img->height, img->path); 54 | 55 | shell_set_in_buffer(img->sh_clear, img->cl_msg); 56 | shell_start(img->sh_clear); 57 | 58 | shell_set_in_buffer(img->sh_draw, img->img_msg); 59 | shell_start(img->sh_draw); 60 | } 61 | 62 | static void shell_stdout_size_cb(Plugin *plugin, char *out) 63 | { 64 | log_msg("IMG", "shell_stdout_size_cb"); 65 | log_msg("IMG", "%s", out); 66 | Img *img = (Img*)plugin->top; 67 | if (!strchr(out, ' ')) 68 | return; 69 | 70 | char *w = strtok(out, " "); 71 | char *h = strtok(NULL, " "); 72 | sscanf(w, "%d", &img->width); 73 | sscanf(h, "%d", &img->height); 74 | img->img_set = true; 75 | img_draw(plugin); 76 | } 77 | 78 | static void shell_stdout_font_cb(Plugin *plugin, char *out) 79 | { 80 | log_msg("IMG", "shell_stdout_font_cb"); 81 | log_msg("IMG", "%s", out); 82 | Img *img = (Img*)plugin->top; 83 | 84 | char *w = strtok(out, " "); 85 | char *h = strtok(NULL, " "); 86 | sscanf(w, "%d", &img->fontw); 87 | sscanf(h, "%d", &img->fonth); 88 | 89 | pos_T lo = layout_size(); 90 | img->fontw = (img->fontw + 2) / lo.col; 91 | img->fonth = (img->fonth + 2) / lo.lnum; 92 | 93 | shell_args(img->sh_size, (char**)args, shell_stdout_size_cb); 94 | } 95 | 96 | static int valid_ext(const char *path) 97 | { 98 | for (int i = 0; i < LENGTH(img_exts); i++) { 99 | if (strcmp(file_ext(path), img_exts[i]) == 0) 100 | return 1; 101 | } 102 | return 0; 103 | } 104 | 105 | static int create_msg(Plugin *host, Plugin *caller, HookArg *hka) 106 | { 107 | Img *img = (Img*)caller->top; 108 | Handle *h = caller->hndl; 109 | 110 | char *path = model_curs_value(host->hndl->model, "fullpath"); 111 | char *name = model_curs_value(host->hndl->model, "name"); 112 | 113 | if (!valid_ext(path)) 114 | return 0; 115 | 116 | img->path = path; 117 | h->key = name; 118 | buf_set_status(h->buf, 0, h->key, 0); 119 | h->buf->attached = false; // override 120 | 121 | free(img->sz_msg); 122 | asprintf(&img->sz_msg, SZ_ARG, img->path); 123 | 124 | return 1; 125 | } 126 | 127 | static void cursor_change_cb(Plugin *host, Plugin *caller, HookArg *hka) 128 | { 129 | log_msg("IMG", "cursor_change_cb"); 130 | Img *img = (Img*)caller->top; 131 | 132 | if (img->disabled) 133 | return; 134 | 135 | if (create_msg(host, caller, NULL)) { 136 | shell_set_in_buffer(img->sh_size, img->sz_msg); 137 | shell_start(img->sh_size); 138 | } 139 | else if (img->img_set) { /* refresh */ 140 | shell_set_in_buffer(img->sh_clear, img->cl_msg); 141 | shell_start(img->sh_clear); 142 | } 143 | } 144 | 145 | //TODO: add to bottom of draw queue 146 | static void try_refresh(Plugin *host, Plugin *none, HookArg *hka) 147 | { 148 | Img *img = (Img*)host->top; 149 | if (!img->img_set) 150 | return; 151 | 152 | shell_set_in_buffer(img->sh_size, img->sz_msg); 153 | shell_start(img->sh_size); 154 | } 155 | 156 | static void pipe_cb(Plugin *host, Plugin *caller, HookArg *hka) 157 | { 158 | log_msg("IMG", "pipe_cb"); 159 | if (strcmp(caller->name, "fm")) 160 | return; 161 | int id = id_from_plugin(host); 162 | hook_add_intl(id, caller, host, cursor_change_cb, EVENT_CURSOR_CHANGE); 163 | cursor_change_cb(caller, host, NULL); 164 | } 165 | 166 | static void pipe_remove_cb(Plugin *host, Plugin *caller, HookArg *hka) 167 | { 168 | hook_rm_intl(caller, host, cursor_change_cb, EVENT_CURSOR_CHANGE); 169 | } 170 | 171 | void img_new(Plugin *plugin, Buffer *buf, char *arg) 172 | { 173 | log_msg("IMG", "INIT"); 174 | Img *img = malloc(sizeof(Img)); 175 | img->base = plugin; 176 | Handle *hndl = malloc(sizeof(Handle)); 177 | hndl->buf = buf; 178 | hndl->key = " "; 179 | plugin->hndl = hndl; 180 | 181 | plugin->fmt_name = "IMG"; 182 | plugin->top = img; 183 | img->buf = buf; 184 | img->disabled = false; 185 | img->img_set = false; 186 | img->sz_msg = malloc(1); 187 | img->cl_msg = malloc(1); 188 | img->img_msg = malloc(1); 189 | buf_set_plugin(buf, plugin, SCR_NULL); 190 | 191 | img->sh_size = shell_new(plugin); 192 | shell_args(img->sh_size, (char**)t_args, shell_stdout_font_cb); 193 | shell_start(img->sh_size); 194 | 195 | img->sh_draw = shell_new(plugin); 196 | shell_args(img->sh_draw, (char**)args, NULL); 197 | 198 | img->sh_clear = shell_new(plugin); 199 | shell_args(img->sh_clear, (char**)args, NULL); 200 | 201 | hook_add_intl(buf->id, plugin, NULL, pipe_cb, EVENT_PIPE); 202 | hook_add_intl(buf->id, plugin, NULL, pipe_remove_cb, EVENT_PIPE_REMOVE); 203 | hook_add_intl(buf->id, plugin, plugin, try_refresh, EVENT_WINDOW_RESIZE); 204 | 205 | int wnum; 206 | if (arg && str_num(arg, &wnum)) { 207 | Plugin *rhs = plugin_from_id(wnum); 208 | if (rhs && wnum != buf->id) 209 | send_hook_msg(EVENT_PIPE, plugin, rhs, NULL); 210 | } 211 | } 212 | 213 | static bool img_clean(Img *img) 214 | { 215 | return !(img->sh_clear->blocking || 216 | img->sh_size->blocking || 217 | img->sh_draw->blocking); 218 | } 219 | 220 | void img_delete(Plugin *plugin) 221 | { 222 | log_msg("IMG", "delete"); 223 | Img *img = plugin->top; 224 | Handle *h = img->base->hndl; 225 | 226 | if (img->img_set) { 227 | shell_set_in_buffer(img->sh_clear, img->cl_msg); 228 | shell_start(img->sh_clear); 229 | } 230 | img->disabled = true; 231 | 232 | DO_EVENTS_UNTIL(img_clean(img)); 233 | shell_delete(img->sh_clear); 234 | shell_delete(img->sh_size); 235 | shell_delete(img->sh_draw); 236 | free(h); 237 | free(img->cl_msg); 238 | free(img->sz_msg); 239 | free(img->img_msg); 240 | free(img); 241 | } 242 | -------------------------------------------------------------------------------- /src/nav/plugins/img/img.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_PLUGINS_IMG_H 2 | #define NV_PLUGINS_IMG_H 3 | 4 | #include "nav/event/shell.h" 5 | 6 | typedef struct Img Img; 7 | 8 | struct Img { 9 | Plugin *base; 10 | Shell *sh_draw; 11 | Shell *sh_size; 12 | Shell *sh_clear; 13 | Buffer *buf; 14 | bool disabled; 15 | bool img_set; 16 | char *sz_msg; 17 | char *cl_msg; 18 | char *img_msg; 19 | char *path; 20 | int fontw; int fonth; 21 | int maxw; int maxh; 22 | int width; int height; 23 | }; 24 | 25 | void img_new(Plugin *plugin, Buffer *buf, char *arg); 26 | void img_delete(Plugin *plugin); 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /src/nav/plugins/op/op.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "nav/lib/utarray.h" 3 | #include "nav/plugins/op/op.h" 4 | #include "nav/plugins/term/term.h" 5 | #include "nav/log.h" 6 | #include "nav/model.h" 7 | #include "nav/option.h" 8 | #include "nav/event/hook.h" 9 | #include "nav/event/event.h" 10 | #include "nav/event/shell.h" 11 | #include "nav/event/fs.h" 12 | #include "nav/cmdline.h" 13 | #include "nav/cmd.h" 14 | #include "nav/util.h" 15 | #include "nav/compl.h" 16 | 17 | static void create_proc(nv_group *, char *); 18 | 19 | static Op op; 20 | 21 | typedef struct { 22 | uv_process_t proc; 23 | uv_process_options_t opts; 24 | nv_group *grp; 25 | } Op_proc; 26 | 27 | void pid_list() 28 | { 29 | record_list("op_procs", "pid", "group"); 30 | } 31 | 32 | void op_set_exec_line(char *line, void *grp) 33 | { 34 | SWAP_ALLOC_PTR(op.last_line, strdup(line)); 35 | op.lastgrp = grp; 36 | } 37 | 38 | static void add_pid(char *name, int pid) 39 | { 40 | log_msg("OP", "add pid %d", pid); 41 | op.last_pid = pid; 42 | char *pidstr; 43 | asprintf(&pidstr, "%d", pid); 44 | trans_rec *r = mk_trans_rec(tbl_fld_count("op_procs")); 45 | edit_trans(r, "group", name, NULL); 46 | edit_trans(r, "pid", pidstr, NULL); 47 | CREATE_EVENT(eventq(), commit, 2, "op_procs", r); 48 | free(pidstr); 49 | } 50 | 51 | static void remove_pid(char *name, int pid) 52 | { 53 | log_msg("OP", "remove pid %d", pid); 54 | char *pidstr; 55 | asprintf(&pidstr, "%d", pid); 56 | tbl_del_val("op_procs", "pid", pidstr); 57 | free(pidstr); 58 | } 59 | 60 | static void del_proc(uv_handle_t *hndl) 61 | { 62 | free(hndl->data); 63 | } 64 | 65 | static void run_group(nv_group *grp, char *line, bool proc) 66 | { 67 | Cmdstr cmd; 68 | op.curgrp = grp; 69 | 70 | Cmdstr *cmdptr = proc ? &cmd : NULL; 71 | cmd_eval(cmdptr, line); 72 | 73 | if (proc && cmd.ret.type == STRING) { 74 | create_proc(grp, cmd.ret.val.v_str); 75 | free(cmd.ret.val.v_str); 76 | } 77 | 78 | op.curgrp = NULL; 79 | } 80 | 81 | static void exit_cb(uv_process_t *req, int64_t exit_status, int term_signal) 82 | { 83 | log_msg("OP", "exit_cb"); 84 | Op_proc *proc = req->data; 85 | nv_group *grp = proc->grp; 86 | remove_pid(grp->key, proc->proc.pid); 87 | uv_close((uv_handle_t*) req, del_proc); 88 | op.last_status = exit_status; 89 | 90 | if (!proc->proc.status) 91 | run_group(grp, grp->opgrp->after, false); 92 | } 93 | 94 | static void chld_handler(uv_signal_t *handle, int signum) 95 | { 96 | log_msg("OP", "chldhand"); 97 | int stat = 0; 98 | int pid; 99 | 100 | do 101 | pid = waitpid(-1, &stat, WNOHANG); 102 | while (pid < 0 && errno == EINTR); 103 | 104 | if (pid <= 0) 105 | return; 106 | 107 | Term *it; 108 | SLIST_FOREACH(it, &mainloop()->subterms, ent) { 109 | Term *term = it; 110 | if (term->pid == pid) { 111 | if (WIFEXITED(stat)) 112 | term->status = WEXITSTATUS(stat); 113 | else if (WIFSIGNALED(stat)) 114 | term->status = WTERMSIG(stat); 115 | 116 | term_close(it); 117 | break; 118 | } 119 | } 120 | } 121 | 122 | static void create_proc(nv_group *grp, char *line) 123 | { 124 | log_msg("OP", "create_proc %s", line); 125 | 126 | Op_proc *proc = malloc(sizeof(Op_proc)); 127 | memset(proc, 0, sizeof(Op_proc)); 128 | proc->opts.flags = UV_PROCESS_DETACHED; 129 | proc->opts.exit_cb = exit_cb; 130 | proc->grp = grp; 131 | 132 | char* args[4]; 133 | args[0] = get_opt_str("shell"); 134 | args[1] = "-c"; 135 | args[2] = line; 136 | args[3] = NULL; 137 | proc->opts.file = args[0]; 138 | proc->opts.args = args; 139 | proc->proc.data = proc; 140 | proc->opts.cwd = fs_pwd(); 141 | 142 | int ret = uv_spawn(eventloop(), &proc->proc, &proc->opts); 143 | 144 | if (ret < 0) { 145 | log_msg("?", "file: |%s|, %s", line, uv_strerror(ret)); 146 | free(proc); 147 | return; 148 | } 149 | 150 | op_set_exec_line(line, grp); 151 | uv_unref((uv_handle_t*)&proc->proc); 152 | add_pid(grp->key, proc->proc.pid); 153 | } 154 | 155 | //TODO: get_syn each item in buffer list. clump items together when opened. 156 | static void fileopen_cb(Plugin *host, Plugin *caller, HookArg *hka) 157 | { 158 | log_msg("OP", "fileopen_cb"); 159 | char *name = model_curs_value(host->hndl->model, "name"); 160 | char *path = model_curs_value(host->hndl->model, "fullpath"); 161 | log_msg("OP", "path %s %s", path, name); 162 | log_msg("OP", "ext %s ", file_ext(name)); 163 | nv_syn *syn = get_syn(file_ext(path)); 164 | if (!syn) 165 | return; 166 | 167 | nv_group *grp = syn->group; 168 | log_msg("OP", "%s", grp->key); 169 | if (!grp->opgrp) 170 | return; 171 | 172 | run_group(grp, grp->opgrp->before, true); 173 | } 174 | 175 | static void execopen_cb(Plugin *host, Plugin *caller, HookArg *hka) 176 | { 177 | log_msg("OP", "exec_cb"); 178 | if (!hka->arg) 179 | return; 180 | add_pid("", *(int*)hka->arg); 181 | } 182 | 183 | static void execclose_cb(Plugin *host, Plugin *caller, HookArg *hka) 184 | { 185 | log_msg("OP", "exec_cb"); 186 | if (!hka->arg) 187 | return; 188 | remove_pid("", *(int*)hka->arg); 189 | } 190 | 191 | Cmdret op_kill(List *args, Cmdarg *ca) 192 | { 193 | log_msg("OP", "kill"); 194 | int pid; 195 | char *pidstr = list_arg(args, 1, VAR_STRING); 196 | if (!pidstr || !str_num(pidstr, &pid)) 197 | return NORET; 198 | 199 | Ventry *vent = fnd_val("op_procs", "pid", pidstr); 200 | if (!vent) { 201 | log_msg("OP", "pid %s not found", pidstr); 202 | return NORET; 203 | } 204 | 205 | uv_kill(pid, SIGKILL); 206 | return NORET; 207 | } 208 | 209 | Op_group* op_newgrp(const char *before, const char *after) 210 | { 211 | Op_group *opgrp = malloc(sizeof(Op_group)); 212 | opgrp->before = strip_quotes(before); 213 | opgrp->after = strip_quotes(after); 214 | return opgrp; 215 | } 216 | 217 | void op_delgrp(Op_group *opgrp) 218 | { 219 | if (!opgrp) 220 | return; 221 | 222 | free(opgrp->before); 223 | free(opgrp->after); 224 | free(opgrp); 225 | } 226 | 227 | void* op_active_group() 228 | { 229 | return op.curgrp; 230 | } 231 | 232 | void op_set_exit_status(int status) 233 | { 234 | op.last_status = status; 235 | } 236 | 237 | char* op_pid_last() 238 | { 239 | char *str; 240 | asprintf(&str, "%d", op.last_pid); 241 | return str; 242 | } 243 | 244 | char* op_status_last() 245 | { 246 | char *str; 247 | asprintf(&str, "%d", op.last_status); 248 | return str; 249 | } 250 | 251 | int op_repeat_last_exec() 252 | { 253 | if (op.lastgrp) 254 | run_group(op.lastgrp, op.last_line, true); 255 | else 256 | return shell_exec(op.last_line, fs_pwd()); 257 | 258 | return op.last_pid; 259 | } 260 | 261 | void op_new(Plugin *plugin, Buffer *buf, char *arg) 262 | { 263 | log_msg("OP", "INIT"); 264 | op.base = plugin; 265 | op.curgrp = NULL; 266 | op.last_pid = 0; 267 | op.last_line = strdup(""); 268 | plugin->top = &op; 269 | hook_add_intl(-1, plugin, plugin, fileopen_cb, EVENT_FILEOPEN); 270 | hook_add_intl(-1, plugin, plugin, execopen_cb, EVENT_EXEC_OPEN); 271 | hook_add_intl(-1, plugin, plugin, execclose_cb, EVENT_EXEC_CLOSE); 272 | hook_set_tmp(EVENT_FILEOPEN); 273 | hook_set_tmp(EVENT_EXEC_OPEN); 274 | hook_set_tmp(EVENT_EXEC_CLOSE); 275 | if (tbl_mk("op_procs")) { 276 | tbl_mk_fld("op_procs", "group", TYP_STR); 277 | tbl_mk_fld("op_procs", "pid", TYP_STR); 278 | } 279 | uv_signal_start(&mainloop()->children_watcher, chld_handler, SIGCHLD); 280 | } 281 | 282 | void op_delete(Plugin *plugin) 283 | { 284 | } 285 | -------------------------------------------------------------------------------- /src/nav/plugins/op/op.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_PLUGINS_OP_H 2 | #define NV_PLUGINS_OP_H 3 | 4 | #include "nav/lib/utarray.h" 5 | #include "nav/plugins/plugin.h" 6 | 7 | typedef struct Op Op; 8 | typedef struct Op_group Op_group; 9 | 10 | struct Op_group { 11 | UT_array *locals; /* nv_var */ 12 | char *before; 13 | char *after; 14 | }; 15 | 16 | struct Op { 17 | Plugin *base; 18 | Handle *hndl; 19 | void *curgrp; /* nv_group* */ 20 | void *lastgrp; /* nv_group* */ 21 | int last_pid; 22 | int last_status; 23 | char *last_line; 24 | }; 25 | 26 | void op_new(Plugin *plugin, Buffer *buf, char *arg); 27 | void op_delete(Plugin *plugin); 28 | 29 | Op_group* op_newgrp(const char *before, const char *after); 30 | void op_delgrp(Op_group *); 31 | 32 | Cmdret op_kill(); 33 | void pid_list(); 34 | void op_set_exit_status(int); 35 | void op_set_exec_line(char *, void *); 36 | char* op_pid_last(); 37 | char* op_status_last(); 38 | void* op_active_group(); 39 | 40 | int op_repeat_last_exec(); 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /src/nav/plugins/out/out.c: -------------------------------------------------------------------------------- 1 | #include "nav/plugins/out/out.h" 2 | #include "nav/tui/buffer.h" 3 | #include "nav/event/event.h" 4 | #include "nav/event/hook.h" 5 | #include "nav/log.h" 6 | #include "nav/table.h" 7 | #include "nav/model.h" 8 | #include "nav/util.h" 9 | 10 | static Out out; 11 | static void out_signal_model(void **); 12 | 13 | void out_init() 14 | { 15 | log_msg("OUT", "init"); 16 | if (tbl_mk("out")) { 17 | tbl_mk_fld("out", "pid", TYP_STR); 18 | tbl_mk_fld("out", "fd", TYP_STR); 19 | tbl_mk_fld("out", "line", TYP_STR); 20 | } 21 | Handle *hndl = malloc(sizeof(Handle)); 22 | hndl->tn = "out"; 23 | hndl->key_fld = "line"; 24 | hndl->key = ""; 25 | hndl->fname = "line"; 26 | hndl->kname = "fd"; 27 | out.hndl = hndl; 28 | } 29 | 30 | void out_new(Plugin *plugin, Buffer *buf, char *arg) 31 | { 32 | log_msg("OUT", "new"); 33 | 34 | out.base = plugin; 35 | plugin->top = &out; 36 | plugin->fmt_name = "OUT"; 37 | 38 | Handle *hndl = out.hndl; 39 | hndl->buf = buf; 40 | plugin->hndl = hndl; 41 | model_init(hndl); 42 | model_open(hndl); 43 | 44 | buf_set_plugin(buf, plugin, SCR_OUT); 45 | buf_set_status(buf, 0, arg, 0); 46 | out.opened = true; 47 | out_signal_model(NULL); 48 | } 49 | 50 | void out_delete(Plugin *plugin) 51 | { 52 | out.opened = false; 53 | Handle *h = plugin->hndl; 54 | model_close(h); 55 | model_cleanup(h); 56 | } 57 | 58 | static void out_signal_model(void **data) 59 | { 60 | if (out.opened) { 61 | Handle *h = out.hndl; 62 | model_flush(h, true); 63 | model_recv(h->model); 64 | buf_move(h->buf, model_count(h->model), 0); 65 | } 66 | } 67 | 68 | void out_recv(int pid, int fd, size_t count, char *out) 69 | { 70 | if (!out) 71 | return; 72 | 73 | if (fd == 1) 74 | log_msg("OUT", "[%d|%d]: %s", pid, fd, out); 75 | else 76 | log_err("OUT", "[%d|%d]: %s", pid, fd, out); 77 | 78 | char *pidstr; 79 | asprintf(&pidstr, "%d", pid); 80 | char *fdstr; 81 | asprintf(&fdstr, "%d", fd); 82 | 83 | char c; 84 | int pos = 0; 85 | int prev = 0; 86 | while (pos < count && (c = out[pos++])) { 87 | if (c != '\n') 88 | continue; 89 | int len = (pos - prev) - 1; 90 | char buf[len]; 91 | strncpy(buf, &out[prev], len); 92 | buf[len] = '\0'; 93 | prev = pos; 94 | 95 | trans_rec *r = mk_trans_rec(tbl_fld_count("out")); 96 | edit_trans(r, "pid", pidstr, NULL); 97 | edit_trans(r, "fd", fdstr, NULL); 98 | edit_trans(r, "line", buf, NULL); 99 | CREATE_EVENT(eventq(), commit, 2, "out", r); 100 | } 101 | free(pidstr); 102 | free(fdstr); 103 | 104 | CREATE_EVENT(eventq(), out_signal_model, 0, NULL); 105 | } 106 | -------------------------------------------------------------------------------- /src/nav/plugins/out/out.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_PLUGINS_OUT_H 2 | #define NV_PLUGINS_OUT_H 3 | 4 | #include "nav/plugins/plugin.h" 5 | 6 | typedef struct Out Out; 7 | 8 | struct Out { 9 | Plugin *base; 10 | Handle *hndl; 11 | bool opened; 12 | }; 13 | 14 | void out_init(); 15 | void out_new(Plugin *plugin, Buffer *buf, char *arg); 16 | void out_delete(Plugin *plugin); 17 | 18 | void out_recv(int, int, size_t, char *); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /src/nav/plugins/plugin.c: -------------------------------------------------------------------------------- 1 | #include "nav/plugins/plugin.h" 2 | #include "nav/tui/window.h" 3 | #include "nav/plugins/fm/fm.h" 4 | #include "nav/plugins/op/op.h" 5 | #include "nav/plugins/img/img.h" 6 | #include "nav/plugins/term/term.h" 7 | #include "nav/plugins/out/out.h" 8 | #include "nav/plugins/dt/dt.h" 9 | #include "nav/plugins/ed/ed.h" 10 | #include "nav/compl.h" 11 | #include "nav/log.h" 12 | #include "nav/option.h" 13 | 14 | typedef struct plugin_ent plugin_ent; 15 | typedef struct { 16 | int key; 17 | Buffer *buf; 18 | UT_hash_handle hh; 19 | } Cid; 20 | 21 | typedef struct _Cid _Cid; 22 | struct _Cid { 23 | int key; 24 | LIST_ENTRY(_Cid) ent; 25 | }; 26 | 27 | static struct plugin_ent { 28 | char *name; 29 | plugin_init_cb init_cb; 30 | plugin_open_cb open_cb; 31 | plugin_close_cb close_cb; 32 | int type_bg; 33 | } plugin_table[] = { 34 | {"fm", fm_init, fm_new, fm_delete, 0}, 35 | {"op", NULL, op_new, op_delete, 1}, 36 | {"out", out_init, out_new, out_delete, 0}, 37 | #if W3M_SUPPORTED 38 | {"img", NULL, img_new, img_delete, 0}, 39 | #endif 40 | {"ed" , NULL, ed_new, ed_delete, 0}, 41 | {"term", NULL, term_new, term_delete, 0}, 42 | {"dt", NULL, dt_new, dt_delete, 0}, 43 | }; 44 | 45 | static int max_callable; 46 | static int max_id; 47 | static LIST_HEAD(ci, _Cid) id_pool; 48 | static Cid *id_table; 49 | 50 | void plugin_init() 51 | { 52 | max_callable = LENGTH(plugin_table); 53 | for (int i = 0; i < LENGTH(plugin_table); i++) { 54 | if (plugin_table[i].init_cb) 55 | plugin_table[i].init_cb(); 56 | if (plugin_table[i].type_bg) { 57 | plugin_open(plugin_table[i].name, 0, 0); 58 | max_callable--; 59 | } 60 | } 61 | } 62 | 63 | void plugin_cleanup() 64 | { 65 | fs_clr_all_cache(); 66 | } 67 | 68 | static int find_plugin(const char *name) 69 | { 70 | if (!name) 71 | return -1; 72 | 73 | for (int i = 0; i < LENGTH(plugin_table); i++) { 74 | if (strcmp(plugin_table[i].name, name) == 0) 75 | return i; 76 | } 77 | return -1; 78 | } 79 | 80 | void obtain_id(Buffer *buf) 81 | { 82 | int key; 83 | Cid *cid = malloc(sizeof(Cid)); 84 | 85 | if (!LIST_EMPTY(&id_pool)) { 86 | _Cid *ret = LIST_FIRST(&id_pool); 87 | LIST_REMOVE(ret, ent); 88 | key = ret->key; 89 | free(ret); 90 | } 91 | else 92 | key = ++max_id; 93 | 94 | cid->key = key; 95 | cid->buf = buf; 96 | buf->id = key; 97 | HASH_ADD_INT(id_table, key, cid); 98 | } 99 | 100 | void forefit_id(Buffer *buf) 101 | { 102 | Cid *cid; 103 | int key = buf->id; 104 | 105 | HASH_FIND_INT(id_table, &key, cid); 106 | HASH_DEL(id_table, cid); 107 | 108 | free(cid); 109 | _Cid *rem = malloc(sizeof(_Cid)); 110 | 111 | rem->key = key; 112 | LIST_INSERT_HEAD(&id_pool, rem, ent); 113 | } 114 | 115 | int plugin_requires_buf(const char *name) 116 | { 117 | int ret = find_plugin(name); 118 | if (ret == -1) 119 | return 0; 120 | return !plugin_table[ret].type_bg; 121 | } 122 | 123 | int plugin_open(const char *name, Buffer *buf, char *line) 124 | { 125 | int i = find_plugin(name); 126 | if (i == -1) 127 | return -1; 128 | 129 | log_msg("PLUG", "%s", plugin_table[i].name); 130 | Plugin *plugin = calloc(1, sizeof(Plugin)); 131 | plugin_table[i].open_cb(plugin, buf, line); 132 | plugin->name = plugin_table[i].name; 133 | int id = buf ? buf->id : -1; 134 | return id; 135 | } 136 | 137 | void plugin_close(Plugin *plugin) 138 | { 139 | if (!plugin) 140 | return; 141 | 142 | int i = find_plugin(plugin->name); 143 | if (i == -1) 144 | return; 145 | 146 | plugin_table[i].close_cb(plugin); 147 | clear_opts(&plugin->opts); 148 | free(plugin); 149 | plugin = NULL; 150 | } 151 | 152 | int plugin_isloaded(const char *name) 153 | { 154 | return find_plugin(name) + 1; 155 | } 156 | 157 | nv_option* local_opts() 158 | { 159 | Buffer *buf = window_get_focus(); 160 | return buf && buf->plugin ? buf->plugin->opts : NULL; 161 | } 162 | 163 | Plugin* focus_plugin() 164 | { 165 | Buffer *buf = window_get_focus(); 166 | if (!buf) 167 | return NULL; 168 | return buf->plugin; 169 | } 170 | 171 | Buffer* buf_from_id(int id) 172 | { 173 | Cid *cid; 174 | HASH_FIND_INT(id_table, &id, cid); 175 | if (cid) 176 | return cid->buf; 177 | return NULL; 178 | } 179 | 180 | Plugin* plugin_from_id(int id) 181 | { 182 | Buffer *buf = buf_from_id(id); 183 | if (buf) 184 | return buf->plugin; 185 | return NULL; 186 | } 187 | 188 | int id_from_plugin(Plugin *plug) 189 | { 190 | return plug->hndl->buf->id; 191 | } 192 | 193 | void plugin_list() 194 | { 195 | int k = 0; 196 | for (int i = 0; i < LENGTH(plugin_table); i++) { 197 | if (plugin_table[i].type_bg) 198 | continue; 199 | compl_list_add("%s", plugin_table[i].name); 200 | k++; 201 | } 202 | } 203 | 204 | void win_list() 205 | { 206 | int i = 0; 207 | Cid *it; 208 | for (it = id_table; it != NULL; it = it->hh.next) { 209 | compl_list_add("%d", it->key); 210 | char *name = ""; 211 | if (it->buf->plugin) 212 | name = it->buf->plugin->name; 213 | compl_set_col(i, "%s", name); 214 | i++; 215 | } 216 | } 217 | 218 | void type_list() 219 | { 220 | log_msg("PLUGIN", "type_list"); 221 | Plugin *plug = window_get_plugin(); 222 | if (!plug) 223 | return; 224 | 225 | int types = tbl_types(plug->hndl->tn); 226 | 227 | if (types & TYP_STR) 228 | compl_list_add("%s", "name"); //TODO: use field0 229 | if (types & TYP_STAT) { 230 | compl_list_add("%s", "size"); 231 | compl_list_add("%s", "type"); 232 | compl_list_add("%s", "ctime"); 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /src/nav/plugins/plugin.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_PLUGINS_PLUGIN_H 2 | #define NV_PLUGINS_PLUGIN_H 3 | 4 | #include "nav/nav.h" 5 | #include "nav/config.h" 6 | 7 | typedef struct nv_option nv_option; 8 | enum opt_type { OPTION_STRING, OPTION_INT, OPTION_UINT, OPTION_BOOLEAN }; 9 | 10 | typedef struct Window Window; 11 | typedef struct Plugin Plugin; 12 | typedef struct Table Table; 13 | typedef struct Buffer Buffer; 14 | typedef struct Handle Handle; 15 | typedef struct Model Model; 16 | typedef struct Overlay Overlay; 17 | typedef struct nv_reg nv_reg; 18 | typedef struct Keyarg Keyarg; 19 | 20 | typedef void (*plugin_init_cb)(void); 21 | typedef void (*plugin_open_cb)(Plugin *base, Buffer *b, char *arg); 22 | typedef void (*plugin_close_cb)(Plugin *plugin); 23 | 24 | struct Handle { 25 | Buffer *buf; 26 | Model *model; 27 | char *tn; // listening table name 28 | char *key; // listening value 29 | char *key_fld; // listening field 30 | char *fname; // filter field 31 | char *kname; // filter field 32 | }; 33 | 34 | struct Plugin { 35 | char *name; 36 | char *fmt_name; 37 | char *compl_key; 38 | Handle *hndl; 39 | nv_option *opts; 40 | void *top; 41 | void (*_cancel)(Plugin *plugin); 42 | void (*_focus)(Plugin *plugin); 43 | }; 44 | 45 | typedef struct { 46 | int lnum; /* line number */ 47 | int col; /* column number */ 48 | } pos_T; 49 | 50 | typedef struct { 51 | int argc; 52 | char **argv; 53 | } varg_T; 54 | 55 | void plugin_init(); 56 | void plugin_cleanup(); 57 | int plugin_isloaded(const char *); 58 | 59 | void obtain_id(Buffer *buf); 60 | void forefit_id(Buffer *buf); 61 | 62 | int plugin_open(const char *name, Buffer *buf, char *line); 63 | void plugin_close(Plugin *plugin); 64 | 65 | nv_option* local_opts(); 66 | Plugin* focus_plugin(); 67 | Buffer* buf_from_id(int id); 68 | Plugin* plugin_from_id(int id); 69 | int id_from_plugin(Plugin *plug); 70 | int plugin_requires_buf(const char *); 71 | 72 | void plugin_list(); 73 | void win_list(); 74 | void type_list(); 75 | 76 | #endif 77 | -------------------------------------------------------------------------------- /src/nav/plugins/term/term.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "nav/lib/utarray.h" 3 | 4 | #include "nav/plugins/term/term.h" 5 | #include "nav/event/rstream.h" 6 | #include "nav/event/wstream.h" 7 | #include "nav/event/event.h" 8 | #include "nav/event/hook.h" 9 | #include "nav/event/input.h" 10 | #include "nav/event/fs.h" 11 | #include "nav/tui/buffer.h" 12 | #include "nav/log.h" 13 | #include "nav/ascii.h" 14 | #include "nav/tui/window.h" 15 | #include "nav/option.h" 16 | 17 | #define SCROLL_HISTORY 500 18 | 19 | static void readfd_ready(uv_poll_t *, int, int); 20 | static void plugin_resize(Plugin *, Plugin *, HookArg *); 21 | static void plugin_focus(Plugin *); 22 | static void plugin_cancel(Plugin *plugin); 23 | static void pipe_cb(Plugin *, Plugin *, HookArg *); 24 | 25 | void term_new(Plugin *plugin, Buffer *buf, char *arg) 26 | { 27 | log_msg("TERM", "term_new"); 28 | Term *term = malloc(sizeof(Term)); 29 | term->base = plugin; 30 | term->closed = false; 31 | term->ed = NULL; 32 | plugin->top = term; 33 | plugin->fmt_name = "VT"; 34 | plugin->_focus = plugin_focus; 35 | plugin->_cancel = plugin_cancel; 36 | 37 | term->buf = buf; 38 | buf_set_plugin(buf, plugin, SCR_NULL); 39 | 40 | term->vt = vt_create(1, 1, SCROLL_HISTORY); 41 | const char *shell = getenv("SHELL"); 42 | const char *pargs[4] = { shell, NULL }; 43 | char *pwd = fs_pwd(); 44 | 45 | char *args = arg; 46 | if (args && args[0]) { 47 | pargs[1] = "-c"; 48 | pargs[2] = args; 49 | pargs[3] = NULL; 50 | } 51 | 52 | SLIST_INSERT_HEAD(&mainloop()->subterms, term, ent); 53 | 54 | term->pid = vt_forkpty(term->vt, shell, pargs, pwd, NULL, NULL, NULL); 55 | if (term->closed) { 56 | return; 57 | } 58 | 59 | uv_poll_init(eventloop(), &term->readfd, vt_pty_get(term->vt)); 60 | uv_poll_start(&term->readfd, UV_READABLE, readfd_ready); 61 | term->readfd.data = term; 62 | window_start_override(plugin); 63 | 64 | hook_add_intl(buf->id, plugin, plugin, plugin_resize, EVENT_WINDOW_RESIZE); 65 | hook_add_intl(buf->id, plugin, NULL, pipe_cb, EVENT_PIPE); 66 | } 67 | 68 | void term_set_editor(Plugin *plugin, Ed *ed) 69 | { 70 | Term *term = plugin->top; 71 | term->ed = ed; 72 | } 73 | 74 | static void term_cleanup(void **args) 75 | { 76 | log_msg("TERM", "term_cleanup"); 77 | Term *term = args[0]; 78 | vt_destroy(term->vt); 79 | free(term); 80 | term = NULL; 81 | } 82 | 83 | void term_delete(Plugin *plugin) 84 | { 85 | log_msg("TERM", "term_delete"); 86 | Term *term = plugin->top; 87 | SLIST_REMOVE(&mainloop()->subterms, term, term, ent); 88 | uv_poll_stop(&term->readfd); 89 | window_stop_override(); 90 | 91 | uv_handle_t *hp = (uv_handle_t*)&term->readfd; 92 | uv_cancel((uv_req_t*)&term->readfd); 93 | uv_close(hp, NULL); 94 | CREATE_EVENT(eventq(), term_cleanup, 1, term); 95 | 96 | if (term->ed && !term->closed) 97 | ed_close_cb(term->base, term->ed, true); 98 | } 99 | 100 | static void plugin_focus(Plugin *plugin) 101 | { 102 | Term *term = plugin->top; 103 | window_start_override(plugin); 104 | readfd_ready(&term->readfd, 0, 0); 105 | } 106 | 107 | void term_cursor(Plugin *plugin) 108 | { 109 | Term *term = plugin->top; 110 | curs_set(vt_cursor_visible(term->vt)); 111 | wnoutrefresh(buf_ncwin(term->buf)); 112 | doupdate(); 113 | } 114 | 115 | static void plugin_cancel(Plugin *plugin) 116 | { 117 | log_msg("TERM", "<|_CANCEL_|>"); 118 | Term *term = plugin->top; 119 | vt_keypress(term->vt, Ctrl_C); 120 | } 121 | 122 | static struct term_key_entry { 123 | int key; 124 | char *code; 125 | } termkeytable[] = { 126 | {K_UP , "\e[A"}, 127 | {K_DOWN , "\e[B"}, 128 | {K_RIGHT , "\e[C"}, 129 | {K_LEFT , "\e[D"}, 130 | {K_HOME , "\e[7~"}, 131 | {K_END , "\e[8~"}, 132 | {K_S_TAB , "\e[Z"}, 133 | {Ctrl_Z , "\x1A"}, 134 | }; 135 | 136 | static const char* convert_key(int key) 137 | { 138 | for (int i = 0; i < LENGTH(termkeytable); i++) { 139 | if (termkeytable[i].key == key) 140 | return termkeytable[i].code; 141 | } 142 | return ""; 143 | } 144 | 145 | void term_keypress(Plugin *plugin, Keyarg *ca) 146 | { 147 | Term *term = plugin->top; 148 | if (ca->key == Meta('[')) 149 | window_stop_override(); 150 | else if (ca->key == Meta('k')) { 151 | vt_scroll(term->vt, -1); 152 | readfd_ready(&term->readfd, 0, 0); 153 | } 154 | else if (ca->key == Meta('j')) { 155 | vt_scroll(term->vt, 1); 156 | readfd_ready(&term->readfd, 0, 0); 157 | } 158 | else { 159 | if (IS_SPECIAL(ca->key)) 160 | vt_write(term->vt, convert_key(ca->key), 3); 161 | else 162 | vt_keypress(term->vt, ca->key); 163 | } 164 | } 165 | 166 | static void term_draw(Term *term) 167 | { 168 | vt_draw(term->vt, buf_ncwin(term->buf), 0, 0); 169 | wnoutrefresh(buf_ncwin(term->buf)); 170 | window_update(); 171 | doupdate(); 172 | } 173 | 174 | static void plugin_resize(Plugin *host, Plugin *none, HookArg *hka) 175 | { 176 | log_msg("TERM", "plugin_resize"); 177 | Term *term = host->top; 178 | vt_dirty(term->vt); 179 | 180 | pos_T sz = buf_size(term->buf); 181 | pos_T ofs = buf_ofs(term->buf); 182 | wresize(buf_ncwin(term->buf), sz.lnum, sz.col); 183 | mvwin(buf_ncwin(term->buf), ofs.lnum, ofs.col); 184 | vt_resize(term->vt, sz.lnum, sz.col); 185 | term_draw(term); 186 | } 187 | 188 | static void pipe_cb(Plugin *host, Plugin *caller, HookArg *hka) 189 | { 190 | log_msg("TERM", "pipe_cb"); 191 | Term *term = host->top; 192 | //TODO: parse with convert_key for 193 | vt_write(term->vt, hka->arg, strlen(hka->arg)); 194 | } 195 | 196 | static void readfd_ready(uv_poll_t *handle, int status, int events) 197 | { 198 | Term *term = handle->data; 199 | vt_process(term->vt); 200 | term_draw(term); 201 | } 202 | 203 | void term_close(Term *term) 204 | { 205 | log_msg("TERM", "term_close"); 206 | term->closed = true; 207 | if (term->ed) 208 | ed_close_cb(term->base, term->ed, false); 209 | else 210 | window_close_focus(); 211 | } 212 | -------------------------------------------------------------------------------- /src/nav/plugins/term/term.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_PLUGINS_TERM_H 2 | #define NV_PLUGINS_TERM_H 3 | 4 | #include 5 | #include "nav/lib/sys_queue.h" 6 | #include "nav/plugins/plugin.h" 7 | #include "nav/plugins/ed/ed.h" 8 | #include "nav/vt/vt.h" 9 | 10 | typedef struct term Term; 11 | 12 | struct term { 13 | Plugin *base; 14 | Vt *vt; 15 | uv_poll_t readfd; 16 | Buffer *buf; 17 | SLIST_ENTRY(term) ent; 18 | Ed *ed; 19 | int pid; 20 | int status; 21 | bool closed; 22 | }; 23 | 24 | void term_new(Plugin *plugin, Buffer *buf, char *arg); 25 | void term_delete(Plugin *plugin); 26 | void term_keypress(Plugin *plugin, Keyarg *ca); 27 | void term_cursor(Plugin *plugin); 28 | void term_set_editor(Plugin *plugin, Ed *ed); 29 | void term_close(Term *term); 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /src/nav/rbuffer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "nav.h" 6 | #include "nav/rbuffer.h" 7 | #include "nav/log.h" 8 | 9 | /// Creates a new `RBuffer` instance. 10 | RBuffer* rbuffer_new(size_t capacity) 11 | { 12 | log_msg("RBUFFER", "new"); 13 | if (!capacity) { 14 | capacity = 0xffff; 15 | } 16 | 17 | RBuffer *rv = malloc(sizeof(RBuffer) + capacity); 18 | rv->full_cb = rv->nonfull_cb = NULL; 19 | rv->data = NULL; 20 | rv->size = 0; 21 | rv->write_ptr = rv->read_ptr = rv->start_ptr; 22 | rv->end_ptr = rv->start_ptr + capacity; 23 | return rv; 24 | } 25 | 26 | void rbuffer_free(RBuffer *buf) 27 | { 28 | log_msg("RBUFFER", "free"); 29 | free(buf); 30 | } 31 | 32 | size_t rbuffer_size(RBuffer *buf) 33 | { 34 | return buf->size; 35 | } 36 | 37 | size_t rbuffer_capacity(RBuffer *buf) 38 | { 39 | return (size_t)(buf->end_ptr - buf->start_ptr); 40 | } 41 | 42 | size_t rbuffer_space(RBuffer *buf) 43 | { 44 | return rbuffer_capacity(buf) - buf->size; 45 | } 46 | 47 | /// Return a pointer to a raw buffer containing the first empty slot available 48 | /// for writing. The second argument is a pointer to the maximum number of 49 | /// bytes that could be written. 50 | /// 51 | /// It is necessary to call this function twice to ensure all empty space was 52 | /// used. See RBUFFER_UNTIL_FULL for a macro that simplifies this task. 53 | char* rbuffer_write_ptr(RBuffer *buf, size_t *write_count) 54 | { 55 | log_msg("RBUFFER", "write_ptr"); 56 | if (buf->size == rbuffer_capacity(buf)) { 57 | *write_count = 0; 58 | return NULL; 59 | } 60 | 61 | if (buf->write_ptr >= buf->read_ptr) { 62 | *write_count = (size_t)(buf->end_ptr - buf->write_ptr); 63 | } else { 64 | *write_count = (size_t)(buf->read_ptr - buf->write_ptr); 65 | } 66 | 67 | log_msg("RBUFFER", "write_ptr end"); 68 | return buf->write_ptr; 69 | } 70 | 71 | // Set read and write pointer for an empty RBuffer to the beginning of the 72 | // buffer. 73 | void rbuffer_reset(RBuffer *buf) 74 | { 75 | if (buf->size == 0) { 76 | buf->write_ptr = buf->read_ptr = buf->start_ptr; 77 | } 78 | } 79 | 80 | /// Adjust `rbuffer` write pointer to reflect produced data. This is called 81 | /// automatically by `rbuffer_write`, but when using `rbuffer_write_ptr` 82 | /// directly, this needs to called after the data was copied to the internal 83 | /// buffer. The write pointer will be wrapped if required. 84 | void rbuffer_produced(RBuffer *buf, size_t count) 85 | { 86 | buf->write_ptr += count; 87 | if (buf->write_ptr >= buf->end_ptr) { 88 | // wrap around 89 | buf->write_ptr -= rbuffer_capacity(buf); 90 | } 91 | 92 | buf->size += count; 93 | if (buf->full_cb && !rbuffer_space(buf)) { 94 | buf->full_cb(buf, buf->data); 95 | } 96 | } 97 | 98 | /// Return a pointer to a raw buffer containing the first byte available 99 | /// for reading. The second argument is a pointer to the maximum number of 100 | /// bytes that could be read. 101 | /// 102 | /// It is necessary to call this function twice to ensure all available bytes 103 | /// were read. See RBUFFER_UNTIL_EMPTY for a macro that simplifies this task. 104 | char* rbuffer_read_ptr(RBuffer *buf, size_t *read_count) 105 | { 106 | log_msg("RBUFFER", "read_ptr"); 107 | if (!buf->size) { 108 | *read_count = 0; 109 | return NULL; 110 | } 111 | 112 | if (buf->read_ptr < buf->write_ptr) { 113 | *read_count = (size_t)(buf->write_ptr - buf->read_ptr); 114 | } else { 115 | *read_count = (size_t)(buf->end_ptr - buf->read_ptr); 116 | } 117 | 118 | return buf->read_ptr; 119 | } 120 | 121 | /// Adjust `rbuffer` read pointer to reflect consumed data. This is called 122 | /// automatically by `rbuffer_read`, but when using `rbuffer_read_ptr` 123 | /// directly, this needs to called after the data was copied from the internal 124 | /// buffer. The read pointer will be wrapped if required. 125 | void rbuffer_consumed(RBuffer *buf, size_t count) 126 | { 127 | log_msg("RBUFFER", "consumed"); 128 | buf->read_ptr += count; 129 | if (buf->read_ptr >= buf->end_ptr) { 130 | buf->read_ptr -= rbuffer_capacity(buf); 131 | } 132 | 133 | bool was_full = buf->size == rbuffer_capacity(buf); 134 | buf->size -= count; 135 | if (buf->nonfull_cb && was_full) { 136 | buf->nonfull_cb(buf, buf->data); 137 | } 138 | } 139 | 140 | // Higher level functions for copying from/to RBuffer instances and data 141 | // pointers 142 | size_t rbuffer_write(RBuffer *buf, char *src, size_t src_size) 143 | { 144 | log_msg("RBUFFER", "write"); 145 | size_t size = src_size; 146 | 147 | RBUFFER_UNTIL_FULL(buf, wptr, wcnt) { 148 | size_t copy_count = MIN(src_size, wcnt); 149 | memcpy(wptr, src, copy_count); 150 | rbuffer_produced(buf, copy_count); 151 | 152 | if (!(src_size -= copy_count)) { 153 | return size; 154 | } 155 | 156 | src += copy_count; 157 | } 158 | 159 | return size - src_size; 160 | } 161 | 162 | size_t rbuffer_read(RBuffer *buf, char *dst, size_t dst_size) 163 | { 164 | log_msg("RBUFFER", "read"); 165 | size_t size = dst_size; 166 | 167 | RBUFFER_UNTIL_EMPTY(buf, rptr, rcnt) { 168 | size_t copy_count = MIN(dst_size, rcnt); 169 | memcpy(dst, rptr, copy_count); 170 | rbuffer_consumed(buf, copy_count); 171 | 172 | if (!(dst_size -= copy_count)) { 173 | return size; 174 | } 175 | 176 | dst += copy_count; 177 | } 178 | 179 | return size - dst_size; 180 | } 181 | 182 | char* rbuffer_get(RBuffer *buf, size_t index) 183 | { 184 | log_msg("RBUFFER", "rbuffer_get"); 185 | char *rptr = buf->read_ptr + index; 186 | if (rptr >= buf->end_ptr) { 187 | rptr -= rbuffer_capacity(buf); 188 | } 189 | return rptr; 190 | } 191 | 192 | int rbuffer_cmp(RBuffer *buf, const char *str, size_t count) 193 | { 194 | size_t rcnt; 195 | (void)rbuffer_read_ptr(buf, &rcnt); 196 | size_t n = MIN(count, rcnt); 197 | int rv = memcmp(str, buf->read_ptr, n); 198 | count -= n; 199 | size_t remaining = buf->size - rcnt; 200 | 201 | if (rv || !count || !remaining) { 202 | return rv; 203 | } 204 | 205 | return memcmp(str + n, buf->start_ptr, count); 206 | } 207 | -------------------------------------------------------------------------------- /src/nav/rbuffer.h: -------------------------------------------------------------------------------- 1 | // Ring buffer implementation. This is basically an array that wraps read/write 2 | // pointers around the memory region. It should be more efficient than the old 3 | // RBuffer which required memmove() calls to relocate read/write positions. 4 | // 5 | // The main purpose of RBuffer is simplify memory management when reading from 6 | // uv_stream_t instances: 7 | // 8 | // - The event loop writes data to a RBuffer, advancing the write pointer 9 | // - The main loop reads data, advancing the read pointer 10 | // - If the buffer becomes full(size == capacity) the rstream is temporarily 11 | // stopped(automatic backpressure handling) 12 | // 13 | // Reference: http://en.wikipedia.org/wiki/Circular_buffer 14 | #ifndef NV_RBUFFER_H 15 | #define NV_RBUFFER_H 16 | 17 | #include 18 | #include 19 | 20 | // Macros that simplify working with the read/write pointers directly by hiding 21 | // ring buffer wrap logic. Some examples: 22 | // 23 | // - Pass the write pointer to a function(write_data) that incrementally 24 | // produces data, returning the number of bytes actually written to the 25 | // ring buffer: 26 | // 27 | // RBUFFER_UNTIL_FULL(rbuf, ptr, cnt) 28 | // rbuffer_produced(rbuf, write_data(state, ptr, cnt)); 29 | // 30 | // - Pass the read pointer to a function(read_data) that incrementally 31 | // consumes data, returning the number of bytes actually read from the 32 | // ring buffer: 33 | // 34 | // RBUFFER_UNTIL_EMPTY(rbuf, ptr, cnt) 35 | // rbuffer_consumed(rbuf, read_data(state, ptr, cnt)); 36 | // 37 | // Note that the rbuffer_{produced,consumed} calls are necessary or these macros 38 | // create infinite loops 39 | #define RBUFFER_UNTIL_EMPTY(buf, rptr, rcnt) \ 40 | for (size_t rcnt = 0, _r = 1; _r; _r = 0) \ 41 | for (char *rptr = rbuffer_read_ptr(buf, &rcnt); \ 42 | buf->size; \ 43 | rptr = rbuffer_read_ptr(buf, &rcnt)) 44 | 45 | #define RBUFFER_UNTIL_FULL(buf, wptr, wcnt) \ 46 | for (size_t wcnt = 0, _r = 1; _r; _r = 0) \ 47 | for (char *wptr = rbuffer_write_ptr(buf, &wcnt); \ 48 | rbuffer_space(buf); \ 49 | wptr = rbuffer_write_ptr(buf, &wcnt)) 50 | 51 | 52 | // Iteration 53 | #define RBUFFER_EACH(buf, c, i) \ 54 | for (size_t i = 0; i < buf->size; i = buf->size) \ 55 | for (char c = 0; \ 56 | i < buf->size ? ((int)(c = *rbuffer_get(buf, i))) || 1 : 0; \ 57 | i++) 58 | 59 | #define RBUFFER_EACH_REVERSE(buf, c, i) \ 60 | for (size_t i = buf->size; i != SIZE_MAX; i = SIZE_MAX) \ 61 | for (char c = 0; \ 62 | i-- > 0 ? ((int)(c = *rbuffer_get(buf, i))) || 1 : 0; \ 63 | ) 64 | 65 | typedef struct rbuffer RBuffer; 66 | /// Type of function invoked during certain events: 67 | /// - When the RBuffer switches to the full state 68 | /// - When the RBuffer switches to the non-full state 69 | typedef void(*rbuffer_callback)(RBuffer *buf, void *data); 70 | 71 | struct rbuffer { 72 | rbuffer_callback full_cb, nonfull_cb; 73 | void *data; 74 | size_t size; 75 | char *end_ptr, *read_ptr, *write_ptr; 76 | char start_ptr[]; 77 | }; 78 | 79 | void rbuffer_free(RBuffer *buf); 80 | RBuffer* rbuffer_new(size_t capacity); 81 | void rbuffer_free(RBuffer *buf); 82 | size_t rbuffer_size(RBuffer *buf); 83 | size_t rbuffer_capacity(RBuffer *buf); 84 | size_t rbuffer_space(RBuffer *buf); 85 | char* rbuffer_write_ptr(RBuffer *buf, size_t *write_count); 86 | void rbuffer_reset(RBuffer *buf); 87 | void rbuffer_produced(RBuffer *buf, size_t count); 88 | char* rbuffer_read_ptr(RBuffer *buf, size_t *read_count); 89 | void rbuffer_consumed(RBuffer *buf, size_t count); 90 | size_t rbuffer_write(RBuffer *buf, char *src, size_t src_size); 91 | size_t rbuffer_read(RBuffer *buf, char *dst, size_t dst_size); 92 | char* rbuffer_get(RBuffer *buf, size_t index); 93 | int rbuffer_cmp(RBuffer *buf, const char *str, size_t count); 94 | 95 | #endif 96 | -------------------------------------------------------------------------------- /src/nav/regex.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "nav/lib/utarray.h" 3 | 4 | #include "nav/tui/buffer.h" 5 | #include "nav/regex.h" 6 | #include "nav/log.h" 7 | #include "nav/model.h" 8 | 9 | #define NSUBEXP 5 10 | 11 | struct Pattern { 12 | pcre *pcre; 13 | pcre_extra *extra; 14 | }; 15 | 16 | struct LineMatch { 17 | UT_array *lines; 18 | Handle *hndl; 19 | int pivot_top; 20 | int pivot_lnum; 21 | char *gcomp; 22 | }; 23 | 24 | static char* gcomp; /* shared regex for all buffers */ 25 | static int gregsign; 26 | 27 | LineMatch* regex_new(Handle *hndl) 28 | { 29 | LineMatch *lm = malloc(sizeof(LineMatch)); 30 | memset(lm, 0, sizeof(LineMatch)); 31 | lm->hndl = hndl; 32 | utarray_new(lm->lines, &ut_int_icd); 33 | return lm; 34 | } 35 | 36 | void regex_destroy(Handle *hndl) 37 | { 38 | LineMatch *lm = hndl->buf->matches; 39 | if (lm->lines) 40 | utarray_free(lm->lines); 41 | free(lm); 42 | } 43 | 44 | void regex_setsign(int sign) 45 | { 46 | gregsign = sign; 47 | } 48 | 49 | static void regex_compile(const char *comp, pcre **pcre, pcre_extra **extra) 50 | { 51 | const char *pcreErrorStr; 52 | int pcreErrorOffset; 53 | 54 | *pcre = pcre_compile(comp, 55 | PCRE_CASELESS, 56 | &pcreErrorStr, 57 | &pcreErrorOffset, 58 | NULL); 59 | 60 | if (pcre == NULL) { 61 | log_msg("REGEX", "COMPILE ERROR: %s, %s", comp, pcreErrorStr); 62 | return; 63 | } 64 | 65 | *extra = pcre_study(*pcre, 0, &pcreErrorStr); 66 | 67 | if(pcreErrorStr != NULL) { 68 | log_msg("REGEX", "COULD NOT STUDY: %s, %s", comp, pcreErrorStr); 69 | return; 70 | } 71 | } 72 | 73 | char* regex_str(LineMatch *lm) 74 | { 75 | if (lm->gcomp) 76 | return lm->gcomp; 77 | return gcomp; 78 | } 79 | 80 | int regex_match_count(LineMatch *lm) 81 | { 82 | if (!lm->lines) 83 | return -1; 84 | return utarray_len(lm->lines); 85 | } 86 | 87 | void regex_build(LineMatch *lm, const char *line) 88 | { 89 | log_msg("REGEX", "build"); 90 | log_msg("REGEX", ":%s:", line); 91 | pcre *pcre = NULL; 92 | pcre_extra *extra = NULL; 93 | 94 | if (line) 95 | SWAP_ALLOC_PTR(gcomp, strdup(line)); 96 | if (!gcomp) 97 | return; 98 | lm->gcomp = gcomp; 99 | 100 | regex_compile(gcomp, &pcre, &extra); 101 | 102 | regex_del_matches(lm); 103 | utarray_new(lm->lines, &ut_int_icd); 104 | 105 | int max = model_count(lm->hndl->model); 106 | for (int i = 0; i < max; i++) { 107 | int substr[NSUBEXP]; 108 | const char *match; 109 | 110 | char* subject = model_str_line(lm->hndl->model, i); 111 | 112 | int ret = pcre_exec(pcre, 113 | extra, 114 | subject, 115 | strlen(subject), // length of string 116 | 0, // Start looking at this point 117 | 0, // OPTIONS 118 | substr, 119 | NSUBEXP); // Length of substr 120 | 121 | if (ret == 0) 122 | ret = NSUBEXP / 3; 123 | if (ret > -1) { 124 | pcre_get_substring(subject, substr, ret, 0, &(match)); 125 | 126 | for(int j = 0; j < ret; j++) { 127 | if (substr[j*2+1] > 0) { 128 | utarray_push_back(lm->lines, &i); 129 | break; 130 | } 131 | } 132 | pcre_free_substring(match); 133 | } 134 | } 135 | pcre_free(extra); 136 | pcre_free(pcre); 137 | } 138 | 139 | void regex_del_matches(LineMatch *lm) 140 | { 141 | log_msg("REGEX", "regex_del_matches"); 142 | if (lm->lines) 143 | utarray_free(lm->lines); 144 | lm->lines = NULL; 145 | } 146 | 147 | Pattern* regex_pat_new(const char *regex) 148 | { 149 | Pattern *pat = malloc(sizeof(Pattern)); 150 | regex_compile(regex, &pat->pcre, &pat->extra); 151 | return pat; 152 | } 153 | 154 | void regex_pat_delete(Pattern *pat) 155 | { 156 | pcre_free(pat->extra); 157 | pcre_free(pat->pcre); 158 | free(pat); 159 | } 160 | 161 | bool regex_match(Pattern *pat, const char *line) 162 | { 163 | if (!line) 164 | return false; 165 | 166 | int substr[NSUBEXP]; 167 | const char *match; 168 | bool succ = false; 169 | int ret = pcre_exec(pat->pcre, 170 | pat->extra, 171 | line, 172 | strlen(line), // length of string 173 | 0, // Start looking at this point 174 | 0, // OPTIONS 175 | substr, 176 | NSUBEXP); // Length of substr 177 | if (ret == 0) 178 | ret = NSUBEXP / 3; 179 | if (ret > -1) { 180 | pcre_get_substring(line, substr, ret, 0, &(match)); 181 | succ = true; 182 | pcre_free_substring(match); 183 | } 184 | return succ; 185 | } 186 | 187 | static int focus_cur_line(LineMatch *lm) 188 | { 189 | return lm->pivot_top + lm->pivot_lnum; 190 | } 191 | 192 | static void ensure_global_match(LineMatch *lm) 193 | { 194 | if (!lm->lines || lm->gcomp != gcomp) { 195 | regex_mk_pivot(lm); 196 | regex_build(lm, NULL); 197 | } 198 | } 199 | 200 | static int* nearest_next_match(UT_array *matches, int line) 201 | { 202 | log_msg("REGEX", "nearest_next_match"); 203 | int *it = NULL; 204 | while ((it = (int*)utarray_next(matches, it))) { 205 | if (*it == line || *it > line) 206 | break; 207 | } 208 | return it; 209 | } 210 | 211 | static void regex_focus(LineMatch *lm, int to) 212 | { 213 | log_msg("REGEX", "regex_focus"); 214 | Buffer *buf = lm->hndl->buf; 215 | int dif = to - buf_index(buf); 216 | if (dif != 0) 217 | buf_move(buf, dif, 0); 218 | } 219 | 220 | void regex_pivot(LineMatch *lm) 221 | { 222 | log_msg("REGEX", "regex_pivot"); 223 | Buffer *buf = lm->hndl->buf; 224 | buf_move_invalid(buf, lm->pivot_top, lm->pivot_lnum); 225 | } 226 | 227 | void regex_mk_pivot(LineMatch *lm) 228 | { 229 | log_msg("REGEX", "regex_mk_pivot"); 230 | lm->pivot_top = buf_top(lm->hndl->buf); 231 | lm->pivot_lnum = buf_line(lm->hndl->buf); 232 | } 233 | 234 | void regex_hover(LineMatch *lm) 235 | { 236 | int line = focus_cur_line(lm); 237 | 238 | if (!lm->lines || utarray_len(lm->lines) < 1) { 239 | regex_pivot(lm); 240 | return; 241 | } 242 | 243 | int *ret = nearest_next_match(lm->lines, line); 244 | if (ret) 245 | regex_focus(lm, *ret); 246 | else { 247 | ret = (int*)utarray_next(lm->lines, ret); \ 248 | ret = ret ? ret : (int*)utarray_next(lm->lines, ret); 249 | regex_focus(lm, *ret); 250 | } 251 | } 252 | 253 | // pivot buffernode focus to closest match. 254 | // varies by direction: '/', '?' 255 | // return next match index. 256 | int regex_next(LineMatch *lm, int line, int dir) 257 | { 258 | log_msg("REGEX", "regex_next"); 259 | ensure_global_match(lm); 260 | if (!lm->lines || utarray_len(lm->lines) < 1) 261 | return -1; 262 | 263 | int *ret = nearest_next_match(lm->lines, line); 264 | if (!ret) 265 | ret = (int*)utarray_front(lm->lines); 266 | 267 | if (ret && *ret == line) { 268 | if (gregsign * dir > 0) { 269 | ret = (int*)utarray_next(lm->lines, ret); \ 270 | ret = ret ? ret : (int*)utarray_next(lm->lines, ret); 271 | } 272 | else { 273 | ret = (int*)utarray_prev(lm->lines, ret); \ 274 | ret = ret ? ret : (int*)utarray_prev(lm->lines, ret); 275 | } 276 | } 277 | if (ret) { 278 | regex_focus(lm, *ret); 279 | return utarray_eltidx(lm->lines, ret); 280 | } 281 | return -1; 282 | } 283 | -------------------------------------------------------------------------------- /src/nav/regex.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_REGEX_H 2 | #define NV_REGEX_H 3 | 4 | #include "nav/plugins/plugin.h" 5 | 6 | typedef struct LineMatch LineMatch; 7 | typedef struct Pattern Pattern; 8 | 9 | LineMatch* regex_new(Handle *hndl); 10 | void regex_destroy(Handle *hndl); 11 | void regex_build(LineMatch *lm, const char *); 12 | void regex_del_matches(LineMatch *lm); 13 | void regex_setsign(int sign); 14 | 15 | Pattern* regex_pat_new(const char *); 16 | void regex_pat_delete(Pattern *pat); 17 | bool regex_match(Pattern *pat, const char *); 18 | 19 | void regex_mk_pivot(LineMatch *lm); 20 | void regex_pivot(LineMatch *lm); 21 | 22 | void regex_hover(LineMatch *lm); 23 | int regex_next(LineMatch *lm, int line, int dir); 24 | char* regex_str(LineMatch *lm); 25 | int regex_match_count(LineMatch *lm); 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /src/nav/table.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_TABLE_H 2 | #define NV_TABLE_H 3 | 4 | #include "nav/lib/uthash.h" 5 | #include "nav/plugins/plugin.h" 6 | 7 | typedef struct TblRec TblRec; 8 | typedef struct TblVal TblVal; 9 | typedef struct TblFld TblFld; 10 | typedef struct TblLis TblLis; 11 | typedef struct Tentry Tentry; 12 | typedef struct Ventry Ventry; 13 | 14 | #define TYP_STR 1 15 | #define TYP_INT 2 16 | #define TYP_STAT 4 17 | 18 | struct Ventry { 19 | Ventry *prev; 20 | Ventry *next; 21 | TblRec *rec; 22 | TblVal *val; 23 | }; 24 | 25 | struct TblLis { 26 | char *key; // listening value 27 | TblFld *key_fld; // listening field 28 | char *fname; // filter field 29 | char *fval; // filter val 30 | TblRec *rec; // record for both listening & filter 31 | int lnum; 32 | int index; 33 | UT_hash_handle hh; 34 | }; 35 | 36 | void tables_init(); 37 | void tables_cleanup(); 38 | bool tbl_mk(const char *); 39 | void tbl_del(const char *); 40 | void tbl_mk_fld(const char *, const char *, int); 41 | 42 | Table* get_tbl(const char *tn); 43 | void tbl_add_lis(const char *, const char *, const char *); 44 | void tbl_del_fld_lis(TblFld *); 45 | void commit(void **data); 46 | 47 | Ventry* fnd_val(const char *, const char *, const char *); 48 | TblLis* fnd_lis(const char *, const char *, const char *); 49 | Ventry* lis_get_val(TblLis *lis, const char *); 50 | void lis_save(TblLis *lis, int index, int lnum, const char *); 51 | void* rec_fld(TblRec *rec, const char *); 52 | char* ent_str(Ventry *ent); 53 | char* tbl_fld(Table*, int); 54 | Ventry* ent_head(Ventry *ent); 55 | Ventry* ent_rec(TblRec *rec, const char *); 56 | TblRec* tbl_iter(TblRec *next); 57 | int fld_type(const char *, const char *); 58 | 59 | void tbl_del_val(const char *, const char *, const char *); 60 | 61 | int tbl_types(const char *); 62 | int tbl_fld_count(const char *); 63 | int tbl_ent_count(Ventry *e); 64 | 65 | typedef struct { 66 | int count; 67 | int max; 68 | char **flds; 69 | int *type; 70 | void **data; 71 | } trans_rec; 72 | 73 | trans_rec* mk_trans_rec(int fld_count); 74 | void edit_trans(trans_rec *r, char *, char *, void *data); 75 | void clear_trans(trans_rec *r, int flush); 76 | 77 | void record_list(const char *tn, char *f1, char *f2); 78 | 79 | #endif 80 | -------------------------------------------------------------------------------- /src/nav/tui/buffer.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_TUI_BUFFER_H 2 | #define NV_TUI_BUFFER_H 3 | 4 | #include 5 | #include "nav/regex.h" 6 | #include "nav/filter.h" 7 | 8 | enum scr_type { 9 | SCR_NULL, 10 | SCR_SIMPLE, 11 | SCR_FILE, 12 | SCR_OUT, 13 | } type; 14 | 15 | struct Buffer { 16 | int id; 17 | WINDOW *nc_win; 18 | Plugin *plugin; 19 | Overlay *ov; 20 | enum scr_type scr; 21 | 22 | LineMatch *matches; 23 | Filter *filter; 24 | 25 | pos_T b_size; 26 | pos_T b_ofs; 27 | 28 | int lnum; // cursor 29 | int top; // index 30 | 31 | int ldif; 32 | 33 | Handle *hndl; 34 | bool dirty; 35 | bool queued; 36 | bool del; 37 | bool attached; 38 | bool focused; 39 | }; 40 | 41 | enum move_dir { MOVE_UP, MOVE_DOWN, MOVE_LEFT, MOVE_RIGHT }; 42 | enum dir_type { L_HORIZ, L_VERT }; 43 | 44 | void buf_init(); 45 | void buf_cleanup(); 46 | Buffer* buf_new(); 47 | void buf_delete(Buffer *buf); 48 | void buf_detach(Buffer *buf); 49 | 50 | Plugin* buf_plugin(Buffer *buf); 51 | WINDOW* buf_ncwin(Buffer *buf); 52 | 53 | void buf_set_plugin(Buffer *buf, Plugin *plugin, enum scr_type); 54 | 55 | void buf_set_size_ofs(Buffer *buf, pos_T size, pos_T ofs); 56 | void buf_set_linematch(Buffer *buf, LineMatch *match); 57 | void buf_set_status(Buffer *buf, char *, char *, char *); 58 | 59 | void buf_update_progress(Buffer *buf, long); 60 | void buf_full_invalidate(Buffer *buf, int index, int lnum); 61 | int buf_input(Buffer *bn, Keyarg *ca); 62 | 63 | void buf_refresh(Buffer *buf); 64 | void buf_toggle_focus(Buffer *buf, int focus); 65 | void buf_signal_filter(Buffer *buf, int count); 66 | 67 | void buf_move_invalid(Buffer *buf, int index, int lnum); 68 | void buf_move(Buffer *buf, int y, int x); 69 | void buf_scroll(Buffer *buf, int y, int max); 70 | 71 | void buf_end_sel(Buffer *buf); 72 | void buf_g(void *, Keyarg *); 73 | void buf_mark(void *, Keyarg *); 74 | void buf_gomark(void *, Keyarg *); 75 | void buf_yank(void *, Keyarg *); 76 | void buf_del(void *, Keyarg *); 77 | 78 | int buf_index(Buffer *buf); 79 | int buf_line(Buffer *buf); 80 | int buf_top(Buffer *buf); 81 | int buf_id(Buffer *buf); 82 | int buf_sel_count(Buffer *buf); 83 | pos_T buf_pos(Buffer *buf); 84 | pos_T buf_size(Buffer *buf); 85 | pos_T buf_ofs(Buffer *buf); 86 | 87 | int buf_attached(Buffer *buf); 88 | 89 | typedef char* (*select_cb)(void *); 90 | varg_T buf_select(Buffer *buf, const char *fld, select_cb cb); 91 | void buf_sort(Buffer *buf, char *fld, int flags); 92 | 93 | #endif 94 | -------------------------------------------------------------------------------- /src/nav/tui/ex_cmd.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_TUI_EX_CMD_H 2 | #define NV_TUI_EX_CMD_H 3 | 4 | #include "nav/tui/buffer.h" 5 | #include "nav/compl.h" 6 | 7 | // these need to align with state_symbol array 8 | #define EX_OFF_STATE 10 9 | #define EX_REG_STATE 0 10 | #define EX_CMD_STATE 1 11 | #define EX_FIL_STATE 2 12 | 13 | #define EX_EMPTY 1 14 | #define EX_FRESH 16 15 | #define EX_QUIT 32 16 | #define EX_CYCLE 64 17 | #define EX_HIST 128 18 | #define EX_EXEC 256 19 | #define EX_CLEAR (EX_EMPTY|EX_CYCLE|EX_HIST) 20 | 21 | void ex_cmd_init(); 22 | void ex_cmd_cleanup(); 23 | void start_ex_cmd(char, int); 24 | void stop_ex_cmd(); 25 | void ex_cmdinvert(); 26 | 27 | void ex_input(Keyarg *ca); 28 | void cmdline_resize(); 29 | void cmdline_refresh(); 30 | void ex_cmd_populate(const char *); 31 | 32 | char ex_cmd_curch(); 33 | int ex_cmd_curpos(); 34 | Token* ex_cmd_curtok(); 35 | char* ex_cmd_curstr(); 36 | int ex_cmd_state(); 37 | char* ex_cmd_line(); 38 | char* ex_cmd_curline(); 39 | 40 | int ex_cmd_height(); 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /src/nav/tui/history.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "nav/lib/sys_queue.h" 4 | #include "nav/tui/history.h" 5 | #include "nav/tui/ex_cmd.h" 6 | #include "nav/log.h" 7 | 8 | typedef struct hist_item hist_item; 9 | struct hist_item { 10 | char *line; 11 | TAILQ_ENTRY(hist_item) ent; 12 | }; 13 | 14 | struct nv_hist { 15 | TAILQ_HEAD(cont, hist_item) p; 16 | hist_item *cur; 17 | uint count; 18 | uint max; 19 | }; 20 | 21 | static nv_hist *hist_cmds; 22 | static nv_hist *hist_regs; 23 | static nv_hist *cur; 24 | 25 | static nv_hist* hist_new() 26 | { 27 | nv_hist *hst = malloc(sizeof(nv_hist)); 28 | memset(hst, 0, sizeof(nv_hist)); 29 | TAILQ_INIT(&hst->p); 30 | hst->max = get_opt_uint("history"); 31 | return hst; 32 | } 33 | 34 | static void hist_delete(nv_hist* hst) 35 | { 36 | log_msg("HIST", "hist_delete"); 37 | while (!TAILQ_EMPTY(&hst->p)) { 38 | hist_item *it = TAILQ_FIRST(&hst->p); 39 | TAILQ_REMOVE(&hst->p, it, ent); 40 | free(it->line); 41 | free(it); 42 | } 43 | free(hst); 44 | } 45 | 46 | void hist_init() 47 | { 48 | hist_cmds = hist_new(); 49 | hist_regs = hist_new(); 50 | } 51 | 52 | void hist_cleanup() 53 | { 54 | hist_delete(hist_cmds); 55 | hist_delete(hist_regs); 56 | } 57 | 58 | void hist_set_state(int state) 59 | { 60 | if (state == EX_CMD_STATE) 61 | cur = hist_cmds; 62 | else 63 | cur = hist_regs; 64 | cur->cur = TAILQ_LAST(&cur->p, cont); 65 | } 66 | 67 | const char* hist_first() 68 | { 69 | hist_item *first = TAILQ_FIRST(&cur->p); 70 | cur->cur = first; 71 | return first->line; 72 | } 73 | 74 | static int hist_try_resize() 75 | { 76 | cur->max = get_opt_uint("history"); 77 | return cur->max > cur->count; 78 | } 79 | 80 | static void hst_chk_limits() 81 | { 82 | if (cur->count < cur->max) 83 | return; 84 | if (!hist_try_resize()) { 85 | hist_item *first = TAILQ_FIRST(&cur->p); 86 | TAILQ_REMOVE(&cur->p, first, ent); 87 | free(first->line); 88 | free(first); 89 | cur->count--; 90 | } 91 | } 92 | 93 | void hist_pop() 94 | { 95 | log_msg("HIST", "hist_pop"); 96 | hist_item *last = TAILQ_LAST(&cur->p, cont); 97 | TAILQ_REMOVE(&cur->p, last, ent); 98 | free(last->line); 99 | free(last); 100 | cur->count--; 101 | } 102 | 103 | void hist_push(int state) 104 | { 105 | log_msg("HIST", "hist_push"); 106 | hist_set_state(state); 107 | hist_item *item = malloc(sizeof(hist_item)); 108 | cur->count++; 109 | item->line = strdup(""); 110 | cur->cur = item; 111 | TAILQ_INSERT_TAIL(&cur->p, item, ent); 112 | hst_chk_limits(); 113 | } 114 | 115 | void hist_save(char *line, int count) 116 | { 117 | log_msg("HIST", "hist_save"); 118 | hist_item *last = TAILQ_LAST(&cur->p, cont); 119 | hist_item *prev = TAILQ_PREV(last, cont, ent); 120 | 121 | if ((prev && !strcmp(prev->line, line)) || count < 1) 122 | return hist_pop(); 123 | 124 | free(last->line); 125 | last->line = strdup(line); 126 | } 127 | 128 | const char* hist_prev() 129 | { 130 | hist_item *item = TAILQ_PREV(cur->cur, cont, ent); 131 | if (!item) 132 | return NULL; 133 | cur->cur = item; 134 | return item->line; 135 | } 136 | 137 | const char* hist_next() 138 | { 139 | hist_item *item = TAILQ_NEXT(cur->cur, ent); 140 | if (!item) 141 | return NULL; 142 | cur->cur = item; 143 | return item->line; 144 | } 145 | 146 | void hist_insert(int state, char *line) 147 | { 148 | hist_set_state(state); 149 | hist_item *item = malloc(sizeof(hist_item)); 150 | cur->count++; 151 | item->line = strdup(line); 152 | cur->cur = item; 153 | TAILQ_INSERT_TAIL(&cur->p, item, ent); 154 | hst_chk_limits(); 155 | } 156 | -------------------------------------------------------------------------------- /src/nav/tui/history.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_HIST_MENU_H 2 | #define NV_HIST_MENU_H 3 | 4 | #include "nav/cmd.h" 5 | 6 | typedef struct nv_hist nv_hist; 7 | 8 | void hist_init(); 9 | void hist_cleanup(); 10 | void hist_set_state(int); 11 | 12 | void hist_push(int); 13 | void hist_pop(); 14 | void hist_save(char *, int); 15 | void hist_insert(int, char *); 16 | 17 | const char* hist_first(); 18 | const char* hist_prev(); 19 | const char* hist_next(); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /src/nav/tui/layout.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_TUI_LAYOUT_H 2 | #define NV_TUI_LAYOUT_H 3 | 4 | #include "nav/tui/buffer.h" 5 | 6 | typedef struct Container Container; 7 | typedef struct { 8 | Container *root; 9 | Container *focus; 10 | } Layout; 11 | 12 | void layout_init(Layout *layout); 13 | void layout_cleanup(Layout *layout); 14 | 15 | void layout_add_buffer(Layout *layout, Buffer *next, enum move_dir dir); 16 | void layout_remove_buffer(Layout *layout, Buffer *); 17 | 18 | void layout_movement(Layout *layout, enum move_dir dir); 19 | void layout_swap(Layout *layout, enum move_dir dir); 20 | Buffer* layout_buf(Layout *layout); 21 | 22 | void layout_set_status(Layout *layout, char *, char *, char *); 23 | void layout_refresh(Layout *layout, int); 24 | 25 | pos_T layout_size(); 26 | 27 | int layout_is_root(Layout *layout); 28 | int key2move_type(int); 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /src/nav/tui/menu.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_TUI_MENU_H 2 | #define NV_TUI_MENU_H 3 | 4 | #include "nav/cmd.h" 5 | 6 | typedef struct Menu Menu; 7 | 8 | Menu* menu_new(); 9 | void menu_delete(Menu *mnu); 10 | 11 | void menu_start(Menu *mnu); 12 | void menu_clear(Menu *mnu); 13 | void menu_restart(Menu *mnu); 14 | void menu_stop(Menu *mnu); 15 | void menu_toggle_hints(Menu *mnu); 16 | bool menu_hints_enabled(Menu *mnu); 17 | int menu_input(Menu *mnu, int key); 18 | void menu_killword(Menu *mnu); 19 | 20 | void menu_rebuild(Menu *mnu); 21 | void menu_update(Menu *mnu, Cmdline *cmd); 22 | 23 | void menu_mv(Menu *mnu, int y); 24 | void menu_resize(Menu *mnu); 25 | void menu_draw(Menu *mnu); 26 | char* menu_next(Menu *mnu, int dir); 27 | 28 | void path_list(); 29 | void menu_ch_dir(void **args); 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /src/nav/tui/message.c: -------------------------------------------------------------------------------- 1 | #include "nav/tui/message.h" 2 | #include "nav/tui/layout.h" 3 | #include "nav/event/input.h" 4 | #include "nav/event/event.h" 5 | #include "nav/option.h" 6 | #include "nav/util.h" 7 | 8 | int dialog_pending; 9 | int message_pending; 10 | static int gch; 11 | static WINDOW *win; 12 | 13 | static void message_draw(char *line, int color) 14 | { 15 | draw_wide(win, 0, 0, line, layout_size().col); 16 | mvwchgat(win, 0, 0, strlen(line), A_NORMAL, color, NULL); 17 | wnoutrefresh(win); 18 | doupdate(); 19 | } 20 | 21 | static void message_start() 22 | { 23 | pos_T max = layout_size(); 24 | win = newwin(1, 0, max.lnum - 1, 0); 25 | } 26 | 27 | static void message_stop() 28 | { 29 | gch = NUL; 30 | werase(win); 31 | wnoutrefresh(win); 32 | doupdate(); 33 | delwin(win); 34 | } 35 | 36 | int confirm(char *fmt, ...) 37 | { 38 | char *msg; 39 | va_list args; 40 | va_start(args, fmt); 41 | vasprintf(&msg, fmt, args); 42 | va_end(args); 43 | 44 | int color = opt_color(MSG_ASK); 45 | 46 | message_start(); 47 | dialog_pending = 1; 48 | 49 | message_draw(msg, color); 50 | while (gch == NUL) 51 | event_cycle_once(); 52 | free(msg); 53 | 54 | int ret = 0; 55 | if (gch == 'Y' || gch == 'y' || gch == CAR) 56 | ret = 1; 57 | 58 | dialog_pending = 0; 59 | message_stop(); 60 | return ret; 61 | } 62 | 63 | static void msg(int color, char *line) 64 | { 65 | message_start(); 66 | message_pending = 1; 67 | message_draw(line, color); 68 | free(line); 69 | } 70 | 71 | void nv_err(char *fmt, ...) 72 | { 73 | int color = opt_color(MSG_ERROR); 74 | char *line; 75 | va_list args; 76 | va_start(args, fmt); 77 | vasprintf(&line, fmt, args); 78 | va_end(args); 79 | msg(color, line); 80 | } 81 | 82 | void nv_msg(char *fmt, ...) 83 | { 84 | char *line; 85 | va_list args; 86 | va_start(args, fmt); 87 | vasprintf(&line, fmt, args); 88 | va_end(args); 89 | msg(0, line); 90 | } 91 | 92 | void msg_clear() 93 | { 94 | message_pending = 0; 95 | message_stop(); 96 | } 97 | 98 | void dialog_input(int key) 99 | { 100 | gch = key; 101 | } 102 | -------------------------------------------------------------------------------- /src/nav/tui/message.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_TUI_MESSAGE_H 2 | #define NV_TUI_MESSAGE_H 3 | 4 | extern int dialog_pending; 5 | extern int message_pending; 6 | 7 | void dialog_input(int); 8 | int confirm(char *, ...); 9 | void nv_err(char *fmt, ...); 10 | void nv_msg(char *fmt, ...); 11 | void nv_trace(char *f, int l, char *fmt, ...); 12 | void msg_clear(); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /src/nav/tui/overlay.c: -------------------------------------------------------------------------------- 1 | #include "nav/tui/overlay.h" 2 | #include "nav/log.h" 3 | #include "nav/tui/buffer.h" 4 | #include "nav/tui/window.h" 5 | #include "nav/util.h" 6 | 7 | #define SZ_BUF 3 8 | #define SZ_LBL 8 9 | #define SZ_ARGS 8 10 | #define SZ_LN 10 11 | #define ST_ARG() (SZ_LBL) 12 | #define ST_PRG() ((SZ_LBL)-1) 13 | #define ST_LN(col) ((col)-((SZ_ARGS)-1)) 14 | #define SZ_USR(col) ((col)-(SZ_BUF+SZ_LBL+SZ_LN)) 15 | #define SZ_PRG(col) (SZ_USR(col)+ST_PRG()-2) 16 | #define LBL_FMT " %-"STR(SZ_LBL)"s" 17 | 18 | struct Overlay { 19 | WINDOW *nc_sep; 20 | WINDOW *nc_st; 21 | pos_T ov_size; 22 | pos_T ov_ofs; 23 | int separator; 24 | int prog; 25 | int filter; 26 | bool queued; 27 | bool del; 28 | bool progfin; 29 | bool redraw; 30 | 31 | char *arg; 32 | char *pipe_in; 33 | char bufno[SZ_BUF]; 34 | char name[SZ_LBL]; 35 | char lineno[SZ_LN]; 36 | 37 | short col_lbl; 38 | short col_text; 39 | short col_ln; 40 | short col_arg; 41 | short col_sep; 42 | short col_bufno; 43 | short col_prog; 44 | short col_fil; 45 | }; 46 | 47 | static char *sep_char; 48 | 49 | Overlay* overlay_new() 50 | { 51 | Overlay *ov = malloc(sizeof(Overlay)); 52 | memset(ov, 0, sizeof(Overlay)); 53 | ov->nc_st = newwin(1,1,0,0); 54 | ov->nc_sep = newwin(1,1,0,0); 55 | leaveok(ov->nc_st, true); 56 | leaveok(ov->nc_sep, true); 57 | 58 | ov->col_lbl = opt_color(OVERLAY_ACTIVE); 59 | ov->col_text = opt_color(OVERLAY_LINE); 60 | ov->col_ln = opt_color(OVERLAY_LINE); 61 | ov->col_sep = opt_color(OVERLAY_SEP); 62 | ov->col_arg = opt_color(OVERLAY_ARGS); 63 | ov->col_bufno = opt_color(OVERLAY_BUFNO); 64 | ov->col_prog = opt_color(OVERLAY_PROGRESS); 65 | ov->col_fil = opt_color(OVERLAY_FILTER); 66 | 67 | sep_char = get_opt_str("sepchar"); 68 | 69 | ov->arg = strdup(" "); 70 | overlay_bufno(ov, 0); 71 | 72 | memset(ov->name, ' ', SZ_LBL); 73 | memset(ov->lineno, ' ', SZ_LN); 74 | return ov; 75 | } 76 | 77 | static int overlay_expire(Overlay *ov) 78 | { 79 | if (!ov->del) 80 | return 0; 81 | 82 | delwin(ov->nc_sep); 83 | delwin(ov->nc_st); 84 | free(ov); 85 | return 1; 86 | } 87 | 88 | void overlay_delete(Overlay *ov) 89 | { 90 | log_msg("overlay", "delete"); 91 | if (ov->del) 92 | return; 93 | 94 | if (ov->arg) 95 | free(ov->arg); 96 | if (ov->pipe_in) 97 | free(ov->pipe_in); 98 | 99 | ov->del = true; 100 | if (!ov->queued) 101 | overlay_expire(ov); 102 | } 103 | 104 | static void overlay_refresh(Overlay *ov) 105 | { 106 | if (ov->queued) 107 | return; 108 | ov->queued = true; 109 | window_req_draw(ov, overlay_draw); 110 | } 111 | 112 | static void set_string(char **from, char *to) 113 | { 114 | if (!to) 115 | return; 116 | free(*from); 117 | 118 | *from = strdup(to); 119 | } 120 | 121 | void overlay_erase(Overlay *ov) 122 | { 123 | if (ov->del) 124 | return; 125 | set_string(&ov->arg, ""); 126 | set_string(&ov->pipe_in, ""); 127 | memset(ov->name, ' ', SZ_LBL); 128 | memset(ov->lineno, ' ', SZ_LN); 129 | } 130 | 131 | void overlay_focus(Overlay *ov) 132 | { 133 | ov->col_lbl = opt_color(OVERLAY_ACTIVE); 134 | ov->col_text = opt_color(OVERLAY_LINE); 135 | overlay_refresh(ov); 136 | } 137 | 138 | void overlay_unfocus(Overlay *ov) 139 | { 140 | ov->col_lbl = opt_color(OVERLAY_INACTIVE); 141 | ov->col_text = opt_color(OVERLAY_TEXTINACTIVE); 142 | overlay_refresh(ov); 143 | } 144 | 145 | void overlay_set(Overlay *ov, pos_T size, pos_T ofs, int sep) 146 | { 147 | log_msg("OVERLAY", "overlay_set"); 148 | ov->separator = sep; 149 | ov->redraw = true; 150 | 151 | ov->ov_size = (pos_T){size.lnum, size.col}; 152 | ov->ov_ofs = (pos_T){ofs.lnum + size.lnum, ofs.col }; 153 | 154 | wresize(ov->nc_st, 1, ov->ov_size.col); 155 | mvwin(ov->nc_st, ov->ov_ofs.lnum, ov->ov_ofs.col); 156 | 157 | wresize(ov->nc_sep, size.lnum + 1, 1); 158 | mvwin(ov->nc_sep, ofs.lnum, ofs.col - 1); 159 | 160 | overlay_refresh(ov); 161 | } 162 | 163 | void overlay_bufno(Overlay *ov, int id) 164 | { 165 | snprintf(ov->bufno, SZ_BUF, "%02d", id); 166 | } 167 | 168 | void overlay_lnum(Overlay *ov, int lnum, int max) 169 | { 170 | snprintf(ov->lineno, SZ_LN, " %*d:%-*d ", 3, lnum+1, 3, max); 171 | int pos = ST_LN(ov->ov_size.col) - 2; 172 | draw_wide(ov->nc_st, 0, pos, ov->lineno, SZ_ARGS+1); 173 | mvwchgat (ov->nc_st, 0, pos, -1, A_NORMAL, ov->col_lbl, NULL); 174 | mvwchgat (ov->nc_st, 0, pos+5, ov->filter, A_NORMAL, ov->col_fil, NULL); 175 | wnoutrefresh(ov->nc_st); 176 | } 177 | 178 | void overlay_filter(Overlay *ov, int max, bool enable) 179 | { 180 | if (!enable) { 181 | ov->filter = 0; 182 | return; 183 | } 184 | char szbuf[4]; 185 | snprintf(szbuf, 4, "%d", max); 186 | ov->filter = strlen(szbuf); 187 | } 188 | 189 | void overlay_edit(Overlay *ov, char *name, char *usr, char *in) 190 | { 191 | log_msg("OVERLAY", "edit: %s ", name); 192 | set_string(&ov->arg, usr); 193 | set_string(&ov->pipe_in, in); 194 | if (name) 195 | snprintf(ov->name, SZ_LBL, LBL_FMT, name); 196 | overlay_refresh(ov); 197 | } 198 | 199 | void overlay_progress(Overlay *ov, long percent) 200 | { 201 | log_err("OVERLAY", "prog: %ld", percent); 202 | 203 | int prog = SZ_PRG(ov->ov_size.col) * percent * 0.01; 204 | if (prog == 0) 205 | ov->progfin = true; 206 | 207 | //TODO: queue refresh for next draw cycle. not this one. disabled for now. 208 | ov->progfin = false; 209 | ov->prog = prog; 210 | 211 | overlay_refresh(ov); 212 | } 213 | 214 | static void overlay_draw_separator(Overlay *ov) 215 | { 216 | ov->redraw = false; 217 | int y = ov->ov_size.lnum; 218 | wattron(ov->nc_sep, COLOR_PAIR(ov->col_sep)); 219 | 220 | int i; 221 | for (i = 0; i < y; i++) 222 | mvwaddstr(ov->nc_sep, i, 0, sep_char); 223 | 224 | wattroff(ov->nc_sep, COLOR_PAIR(ov->col_sep)); 225 | DRAW_CH(ov, nc_sep, i, 0, ' ', col_ln); 226 | wnoutrefresh(ov->nc_sep); 227 | } 228 | 229 | static void overlay_draw_progress(Overlay *ov) 230 | { 231 | int x = ov->ov_size.col; 232 | if (ov->progfin) { 233 | ov->progfin = false; 234 | mvwchgat(ov->nc_st, 0, ST_PRG(), SZ_PRG(x), A_REVERSE, ov->col_lbl, NULL); 235 | } 236 | else 237 | mvwchgat(ov->nc_st, 0, ST_PRG(), ov->prog, A_NORMAL, ov->col_prog, NULL); 238 | } 239 | 240 | static void overlay_draw_labels(Overlay *ov) 241 | { 242 | int x = ov->ov_size.col; 243 | draw_wide(ov->nc_st, 0, 0, ov->bufno, SZ_BUF+1); 244 | mvwchgat (ov->nc_st, 0, 0, SZ_BUF+1, A_NORMAL, ov->col_bufno, NULL); 245 | 246 | draw_wide(ov->nc_st, 0, SZ_BUF-1, ov->name, SZ_LBL+1); 247 | mvwchgat (ov->nc_st, 0, SZ_BUF-1, SZ_LBL+1, A_NORMAL, ov->col_lbl, NULL); 248 | 249 | mvwhline(ov->nc_st, 0, SZ_LBL-1, ' ', x); 250 | mvwchgat(ov->nc_st, 0, SZ_LBL-1, -1, A_NORMAL, ov->col_ln, NULL); 251 | 252 | //TODO: if arg exceeds SZ_USR() then compress /*/*/ to fit 253 | int pos = ST_LN(x) - 2; 254 | draw_wide(ov->nc_st, 0, ST_ARG(), ov->arg, SZ_USR(x)); 255 | mvwchgat (ov->nc_st, 0, ST_ARG(), pos, A_NORMAL, ov->col_text, NULL); 256 | 257 | draw_wide(ov->nc_st, 0, pos, ov->lineno, SZ_ARGS+1); 258 | mvwchgat (ov->nc_st, 0, pos, -1, A_NORMAL, ov->col_lbl, NULL); 259 | mvwchgat (ov->nc_st, 0, pos+5, ov->filter, A_NORMAL, ov->col_fil, NULL); 260 | } 261 | 262 | void overlay_draw(void **argv) 263 | { 264 | log_msg("OVERLAY", "draw"); 265 | Overlay *ov = argv[0]; 266 | 267 | if (!ov) 268 | return; 269 | if (overlay_expire(ov)) 270 | return; 271 | 272 | ov->queued = false; 273 | 274 | if (ov->redraw && ov->separator) 275 | overlay_draw_separator(ov); 276 | 277 | overlay_draw_labels(ov); 278 | overlay_draw_progress(ov); 279 | 280 | wnoutrefresh(ov->nc_st); 281 | } 282 | -------------------------------------------------------------------------------- /src/nav/tui/overlay.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_TUI_OVERLAY_H 2 | #define NV_TUI_OVERLAY_H 3 | 4 | #include 5 | #include "nav/plugins/plugin.h" 6 | #include "nav/option.h" 7 | 8 | Overlay* overlay_new(); 9 | void overlay_delete(Overlay *ov); 10 | void overlay_set(Overlay *ov, pos_T size, pos_T ofs, int sep); 11 | 12 | void overlay_bufno(Overlay *ov, int id); 13 | void overlay_lnum(Overlay *ov, int lnum, int max); 14 | void overlay_filter(Overlay *ov, int max, bool enable); 15 | void overlay_edit(Overlay *ov, char *, char *, char *); 16 | void overlay_progress(Overlay *ov, long); 17 | void overlay_draw(void **argv); 18 | void overlay_erase(Overlay *ov); 19 | void overlay_focus(Overlay *ov); 20 | void overlay_unfocus(Overlay *ov); 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /src/nav/tui/screen.c: -------------------------------------------------------------------------------- 1 | #include "nav/tui/screen.h" 2 | #include "nav/tui/select.h" 3 | #include "nav/model.h" 4 | #include "nav/util.h" 5 | #include "nav/event/fs.h" 6 | #include "nav/option.h" 7 | 8 | #define SZ_LEN 6 9 | #define MAX_POS(x) ((x)-(SZ_LEN+1)) 10 | static char szbuf[SZ_LEN*2]; 11 | static short col_focus; 12 | static short col_unfocus; 13 | static short col_text; 14 | static short col_dir; 15 | static short col_sz; 16 | 17 | void screen_init() 18 | { 19 | col_focus = opt_color(BUF_SEL_ACTIVE); 20 | col_unfocus = opt_color(BUF_SEL_INACTIVE); 21 | col_text = opt_color(BUF_TEXT); 22 | col_dir = opt_color(BUF_DIR); 23 | col_sz = opt_color(BUF_SZ); 24 | } 25 | 26 | static void draw_simple(Buffer *buf, Model *m) 27 | { 28 | for (int i = 0; i < buf->b_size.lnum; ++i) { 29 | char *it = model_str_line(m, buf->top + i); 30 | if (!it) 31 | break; 32 | 33 | attr_t attr = A_NORMAL; 34 | if (select_has_line(buf, buf->top + i)) 35 | attr = A_REVERSE; 36 | 37 | int max = MAX_POS(buf->b_size.col); 38 | draw_wide(buf->nc_win, i, 0, it, max - 1); 39 | mvwchgat(buf->nc_win, i, 0, -1, attr, col_text, NULL); 40 | } 41 | } 42 | 43 | static void draw_file(Buffer *buf, Model *m) 44 | { 45 | for (int i = 0; i < buf->b_size.lnum; ++i) { 46 | char *it = model_str_line(m, buf->top + i); 47 | if (!it) 48 | break; 49 | 50 | TblRec *rec = model_rec_line(m, buf->top + i); 51 | 52 | readable_fs(rec_stsize(rec), szbuf); 53 | 54 | attr_t attr = A_NORMAL; 55 | if (select_has_line(buf, buf->top + i)) 56 | attr = A_REVERSE; 57 | 58 | int max = MAX_POS(buf->b_size.col); 59 | draw_wide(buf->nc_win, i, 0, it, max - 1); 60 | 61 | if (isrecreg(rec)) { 62 | int col = get_syn_colpair(file_ext(it)); 63 | mvwchgat(buf->nc_win, i, 0, -1, attr, col, NULL); 64 | draw_wide(buf->nc_win, i, 2+max, szbuf, SZ_LEN); 65 | mvwchgat(buf->nc_win, i, 2+max, -1, attr, col_sz, NULL); 66 | mvwchgat(buf->nc_win, i, buf->b_size.col - 1, 1, attr, col_text,0); 67 | } 68 | else { 69 | char *symb = "?"; 70 | if (isreclnk(rec)) 71 | symb = ">"; 72 | else if (isrecdir(rec)) 73 | symb = "/"; 74 | 75 | mvwchgat(buf->nc_win, i, 0, -1, attr, col_dir, NULL); 76 | draw_wide(buf->nc_win, i, buf->b_size.col - 1, symb, SZ_LEN); 77 | mvwchgat(buf->nc_win, i, buf->b_size.col - 1, 1, attr, col_sz, NULL); 78 | } 79 | } 80 | } 81 | 82 | static void draw_out(Buffer *buf, Model *m) 83 | { 84 | char *prev = NULL; 85 | for (int i = 0; i < buf->b_size.lnum; ++i) { 86 | char *it = model_str_line(m, buf->top + i); 87 | if (!it) 88 | break; 89 | bool alt = false; 90 | 91 | TblRec *rec = model_rec_line(m, buf->top + i); 92 | char *fd = rec_fld(rec, "fd"); 93 | short col = *fd == '1' ? opt_color(BUF_STDOUT) : opt_color(BUF_STDERR); 94 | char *pidstr = rec_fld(rec, "pid"); 95 | sprintf(szbuf, "%s:", pidstr); 96 | 97 | if (prev != pidstr) { 98 | alt = true; 99 | prev = pidstr; 100 | } 101 | 102 | attr_t attr = A_NORMAL; 103 | if (select_has_line(buf, buf->top + i)) 104 | attr = A_REVERSE; 105 | 106 | int st = alt ? SZ_LEN : 0; 107 | int max = buf->b_size.col - st; 108 | 109 | if (alt) 110 | draw_wide(buf->nc_win, i, 0, szbuf, SZ_LEN); 111 | draw_wide(buf->nc_win, i, st+1, it, max - 2); 112 | 113 | mvwchgat(buf->nc_win, i, 0, -1, A_UNDERLINE, SZ_LEN, NULL); 114 | mvwchgat(buf->nc_win, i, st, -1, attr, col, NULL); 115 | } 116 | } 117 | 118 | void draw_curline(Buffer *buf) 119 | { 120 | attr_t attr = A_NORMAL; 121 | if (select_has_line(buf, buf->lnum + buf->top)) 122 | attr = A_REVERSE; 123 | 124 | short col = buf->focused ? col_focus : col_unfocus;; 125 | mvwchgat(buf->nc_win, buf->lnum, 0, -1, attr, col, NULL); 126 | } 127 | 128 | void draw_screen(Buffer *buf) 129 | { 130 | Model *m = buf->hndl->model; 131 | switch (buf->scr) { 132 | case SCR_SIMPLE: 133 | draw_simple(buf, m); 134 | draw_curline(buf); 135 | break; 136 | case SCR_FILE: 137 | draw_file(buf, m); 138 | draw_curline(buf); 139 | break; 140 | case SCR_OUT: 141 | draw_out(buf, m); 142 | break; 143 | case SCR_NULL: 144 | return; 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/nav/tui/screen.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_TUI_SCREEN_H 2 | #define NV_TUI_SCREEN_H 3 | 4 | #include "nav/tui/buffer.h" 5 | 6 | void screen_init(); 7 | void draw_screen(Buffer *); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /src/nav/tui/select.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "nav/tui/select.h" 3 | #include "nav/tui/buffer.h" 4 | #include "nav/log.h" 5 | #include "nav/macros.h" 6 | 7 | typedef struct { 8 | int orgn_lnum; 9 | int orgn_index; 10 | int head; 11 | int *lines; 12 | int max; 13 | int count; 14 | bool enabled; 15 | bool active; 16 | bool aditv; 17 | Buffer *owner; 18 | } Select; 19 | 20 | static Select sel; 21 | 22 | void select_toggle(Buffer *buf, int max) 23 | { 24 | log_msg("SELECT", "toggle"); 25 | if (max < 1) 26 | return; 27 | if (select_active() && !select_owner(buf)) { 28 | select_clear(sel.owner); 29 | buf_refresh(sel.owner); 30 | } 31 | 32 | sel.owner = buf; 33 | sel.enabled = !sel.enabled; 34 | if (!sel.enabled) 35 | return; 36 | 37 | if (!select_active()) 38 | sel.lines = calloc(max, sizeof(int)); 39 | 40 | int idx = buf_index(buf); 41 | sel.aditv = !sel.lines[idx]; 42 | 43 | sel.head = idx; 44 | sel.max = max; 45 | sel.active = true; 46 | sel.orgn_lnum = buf_line(buf); 47 | sel.orgn_index = buf_top(buf); 48 | sel.lines[idx] = 1; 49 | sel.count++; 50 | } 51 | 52 | void select_clear(Buffer *buf) 53 | { 54 | if (!select_owner(buf)) 55 | return; 56 | 57 | free(sel.lines); 58 | sel.lines = NULL; 59 | sel.enabled = false; 60 | sel.active = false; 61 | sel.count = 0; 62 | } 63 | 64 | bool select_active() 65 | { 66 | return sel.active; 67 | } 68 | 69 | bool select_owner(Buffer *buf) 70 | { 71 | return sel.owner == buf; 72 | } 73 | 74 | int select_count() 75 | { 76 | if (sel.enabled) 77 | return sel.count; 78 | return 0; 79 | } 80 | 81 | void select_enter(Buffer *buf, int idx) 82 | { 83 | if (!sel.enabled || !select_active() || !select_owner(buf)) 84 | return; 85 | 86 | bool enable = sel.aditv; 87 | int orgn = sel.orgn_lnum + sel.orgn_index; 88 | 89 | int st = MIN(sel.head, orgn); 90 | int ed = MAX(sel.head, orgn); 91 | 92 | for (int i = st; i <= ed; i++) 93 | sel.lines[i] = !enable; 94 | 95 | st = MIN(idx, orgn); 96 | ed = MAX(idx, orgn); 97 | sel.count = (ed - st) + 1; 98 | 99 | for (int i = st; i <= ed; i++) 100 | sel.lines[i] = enable; 101 | 102 | sel.head = idx; 103 | sel.lines[idx] = enable; 104 | } 105 | 106 | void select_min_origin(Buffer *buf, int *lnum, int *index) 107 | { 108 | if (sel.orgn_lnum + sel.orgn_index > *lnum + *index) 109 | return; 110 | select_alt_origin(buf, lnum, index); 111 | } 112 | 113 | bool select_alt_origin(Buffer *buf, int *lnum, int *index) 114 | { 115 | if (!select_owner(buf) || !select_active()) 116 | return false; 117 | 118 | SWAP(int, *lnum, sel.orgn_lnum); 119 | SWAP(int, *index, sel.orgn_index); 120 | return true; 121 | } 122 | 123 | bool select_has_line(Buffer *buf, int idx) 124 | { 125 | if (!select_owner(buf) || !select_active()) 126 | return false; 127 | return (sel.lines[idx]); 128 | } 129 | -------------------------------------------------------------------------------- /src/nav/tui/select.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_TUI_SELECT_H 2 | #define NV_TUI_SELECT_H 3 | 4 | #include "nav/plugins/plugin.h" 5 | 6 | void select_toggle(Buffer *, int max); 7 | void select_clear(Buffer *); 8 | bool select_active(); 9 | int select_count(); 10 | bool select_owner(Buffer *); 11 | void select_enter(Buffer *, int idx); 12 | void select_min_origin(Buffer *, int *lnum, int *index); 13 | bool select_alt_origin(Buffer *, int *lnum, int *index); 14 | bool select_has_line(Buffer *, int idx); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /src/nav/tui/window.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_TUI_WINDOW_H 2 | #define NV_TUI_WINDOW_H 3 | 4 | #include "nav/event/event.h" 5 | #include "nav/tui/buffer.h" 6 | 7 | void window_init(void); 8 | void window_cleanup(void); 9 | void window_req_draw(void *obj, argv_callback); 10 | void window_refresh(); 11 | void window_input(Keyarg *ca); 12 | 13 | void window_add_buffer(enum move_dir); 14 | void window_close_focus(); 15 | void window_remove_buffer(Buffer *); 16 | void window_ex_cmd_end(); 17 | void window_draw_all(); 18 | void window_update(); 19 | Buffer* window_get_focus(); 20 | Plugin* window_get_plugin(); 21 | int window_focus_attached(); 22 | void win_move(void *, Keyarg *); 23 | 24 | void window_start_override(Plugin *); 25 | void window_stop_override(); 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /src/nav/util.h: -------------------------------------------------------------------------------- 1 | #ifndef NV_UTIL_H 2 | #define NV_UTIL_H 3 | 4 | #include 5 | 6 | wchar_t* str2wide(char *src); 7 | char* wide2str(wchar_t *src); 8 | int cell_len(char *str); 9 | char* prev_widechar(const char *string_start, const char *string); 10 | char* next_widechar(const char *string); 11 | void draw_wide(WINDOW *win, int row, int col, char *src, int max); 12 | void readable_fs(double size/*in bytes*/, char buf[]); 13 | void conspath_buf(char *buf, char *base, char *name); 14 | char* escape_shell(char *src); 15 | char* strip_shell(const char *src); 16 | char* strip_whitespace(char *); 17 | int count_strstr(const char *str, char *fnd); 18 | int rev_strchr_pos(char *src, int n, char *accept); 19 | int count_lines(char *src); 20 | char* lines2argv(int, char **); 21 | char* lines2yank(int argc, char **argv); 22 | void del_param_list(char **params, int argc); 23 | char* strip_quotes(const char *); 24 | char* add_quotes(char *); 25 | int fuzzystrspn(const char *p, const char *s); 26 | bool fuzzy_match(char *, const char *); 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /src/nav/vt/vt.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2004 Bruno T. C. de Oliveira 3 | * Copyright © 2006 Pierre Habouzit 4 | * Copyright © 2008-2013 Marc André Tanner 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15 | * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16 | * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | #ifndef VT_H 19 | #define VT_H 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #ifndef NCURSES_MOUSE_VERSION 26 | #define mmask_t unsigned long 27 | #endif 28 | 29 | typedef struct Vt Vt; 30 | typedef void (*vt_title_handler_t)(Vt*, const char *title); 31 | typedef void (*vt_urgent_handler_t)(Vt*); 32 | 33 | void vt_init(char *); 34 | void vt_shutdown(void); 35 | 36 | void vt_keytable_set(char const * const keytable_overlay[], int count); 37 | void vt_default_colors_set(Vt*, attr_t attrs, short fg, short bg); 38 | void vt_title_handler_set(Vt*, vt_title_handler_t); 39 | void vt_urgent_handler_set(Vt*, vt_urgent_handler_t); 40 | void vt_data_set(Vt*, void *); 41 | void *vt_data_get(Vt*); 42 | 43 | Vt *vt_create(int rows, int cols, int scroll_buf_sz); 44 | void vt_resize(Vt*, int rows, int cols); 45 | void vt_destroy(Vt*); 46 | pid_t vt_forkpty(Vt*, const char *p, const char *argv[], const char *cwd, const char *env[], int *to, int *from); 47 | int vt_pty_get(Vt*); 48 | bool vt_cursor_visible(Vt*); 49 | 50 | int vt_process(Vt *); 51 | void vt_keypress(Vt *, int keycode); 52 | ssize_t vt_write(Vt*, const char *buf, size_t len); 53 | void vt_mouse(Vt*, int x, int y, mmask_t mask); 54 | void vt_dirty(Vt*); 55 | void vt_draw(Vt*, WINDOW *win, int startrow, int startcol); 56 | short vt_color_get(Vt*, short fg, short bg); 57 | short vt_color_reserve(short fg, short bg); 58 | 59 | void vt_scroll(Vt*, int rows); 60 | void vt_noscroll(Vt*); 61 | 62 | pid_t vt_pid_get(Vt*); 63 | size_t vt_content_get(Vt*, char **s, bool colored); 64 | int vt_content_start(Vt*); 65 | 66 | #endif /* VT_H */ 67 | --------------------------------------------------------------------------------